func installIngress(cmd *cobra.Command, args []string) error {
	cmd.SilenceUsage = true
	ExitfIfError(checkDependencies(), "Cannot operate")
	installClient, installCleanup, err := admincmd.NewInstallClient(cmd.Context(), &traefikOptions)
	defer func() {
		if installCleanup != nil {
			installCleanup()
		}
	}()
	if err != nil {
		return errors.Wrap(err, "error initializing cli")
	}
	err = installClient.InstallIngress(cmd)
	if err != nil {
		return errors.Wrap(err, "error installing Epinio ingress")
	}
	return nil
}
installIngress is the backend for CmdInstallIngress.
It adds epinio's ingress controller to the targeted cluster
package cli
import (
	"fmt"
	"github.com/epinio/epinio/deployments"
	"github.com/epinio/epinio/helpers/kubernetes"
	"github.com/epinio/epinio/helpers/randstr"
	"github.com/epinio/epinio/helpers/termui"
	"github.com/epinio/epinio/internal/cli/admincmd"
	"github.com/epinio/epinio/internal/cli/usercmd"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"
)
var (
	skipLinkerdOption = kubernetes.InstallationOption{
		Name:        "skip-linkerd",
		Description: "Assert to epinio that Linkerd is already installed.",
		Type:        kubernetes.BooleanType,
		Default:     false,
		Value:       false,
	}
	emailOption = kubernetes.InstallationOption{
		Name:        "email_address",
		Description: "The email address you are planning to use for getting notifications about your certificates",
		Type:        kubernetes.StringType,
		Default:     "epinio@suse.com",
		Value:       "",
	}
	ingressServiceIPOption = kubernetes.InstallationOption{
		Name:        "loadbalancer-ip",
		Description: "IP address to be assigned to ingress loadbalancer service",
		Type:        kubernetes.StringType,
		Default:     "",
		Value:       "",
	}
)
var neededOptions = kubernetes.InstallationOptions{
	{
		Name:        "skip-traefik",
		Description: "Assert to epinio that there is a Traefik active, even if epinio cannot find it.",
		Type:        kubernetes.BooleanType,
		Default:     false,
		Value:       false,
	},
	skipLinkerdOption,
	{
		Name:        "skip-cert-manager",
		Description: "Assert to epinio that cert-manager is already installed.",
		Type:        kubernetes.BooleanType,
		Default:     false,
		Value:       false,
	},
	{
		Name:        "system_domain",
		Description: "The domain you are planning to use for Epinio. Should be pointing to the traefik public IP (Leave empty to use a omg.howdoi.website domain).",
		Type:        kubernetes.StringType,
		Default:     "",
		Value:       "",
	},
	emailOption,
	{
		Name:        "user",
		Description: "The user name for authenticating all API requests",
		Type:        kubernetes.StringType,
		Default:     "",
		Value:       "",
		DynDefaultFunc: func(o *kubernetes.InstallationOption) error {
			uid, err := randstr.Hex16()
			if err != nil {
				return err
			}
			o.Value = uid
			return nil
		},
	},
	{
		Name:        "password",
		Description: "The password for authenticating all API requests",
		Type:        kubernetes.StringType,
		Default:     "",
		Value:       "",
		DynDefaultFunc: func(o *kubernetes.InstallationOption) error {
			uid, err := randstr.Hex16()
			if err != nil {
				return err
			}
			o.Value = uid
			return nil
		},
	},
	{
		Name: "tls-issuer",
		Description: fmt.Sprintf("The name of the cluster issuer to use. Epinio creates three options: '%s', '%s', and '%s'.",
			deployments.EpinioCAIssuer,
			deployments.LetsencryptIssuer,
			deployments.SelfSignedIssuer),
		Type:    kubernetes.StringType,
		Default: deployments.EpinioCAIssuer,
		Value:   deployments.EpinioCAIssuer,
	},
	{
		Name:        "use-internal-registry-node-port",
		Description: "Make the internal registry accessible via a node port, so kubelet can access the registry without trusting its cert.",
		Type:        kubernetes.BooleanType,
		Default:     true,
		Value:       true,
	},
	ingressServiceIPOption,
	{
		Name:        "s3-access-key-id",
		Description: "If you are using your own S3 compatible object storage for blobs, set the access key id with this setting.",
		Type:        kubernetes.StringType,
		Default:     "",
		Value:       "",
	},
	{
		Name:        "s3-secret-access-key",
		Description: "If you are using your own S3 compatible object storage for blobs, set the secret access key with this setting.",
		Type:        kubernetes.StringType,
		Default:     "",
		Value:       "",
	},
	{
		Name:        "s3-bucket",
		Description: "If you are using your own S3 compatible object storage for blobs, set the bucket with this setting.",
		Type:        kubernetes.StringType,
		Default:     "",
		Value:       "",
	},
	{
		Name:        "s3-location",
		Description: "If you are using your own S3 compatible object storage for blobs, set the S3 bucket location with this setting.",
		Type:        kubernetes.StringType,
		Default:     "",
		Value:       "",
	},
	{
		Name:        "s3-endpoint",
		Description: "If you are using your own S3 compatible object storage for blobs, set the S3 endpoint with this setting.",
		Type:        kubernetes.StringType,
		Default:     "",
		Value:       "",
	},
	{
		Name:        "s3-use-ssl",
		Description: "If you are using your own S3 compatible object storage for blobs, toggle SSL on or off.",
		Type:        kubernetes.BooleanType,
		Default:     false,
		Value:       false,
	},
}
var traefikOptions = kubernetes.InstallationOptions{skipLinkerdOption, ingressServiceIPOption}
var certManagerOptions = kubernetes.InstallationOptions{emailOption}
const (
	DefaultOrganization = "workspace"
)
func init() {
	CmdInstall.Flags().BoolP("interactive", "i", false, "Whether to ask the user or not (default not)")
	CmdInstall.Flags().BoolP("skip-default-namespace", "s", false, "Set this to skip the creation of a default namespace")
	CmdInstallIngress.Flags().BoolP("interactive", "i", false, "Whether to ask the user or not (default not)")
	CmdInstallCertManager.Flags().BoolP("interactive", "i", false, "Whether to ask the user or not (default not)")
	neededOptions.AsCobraFlagsFor(CmdInstall.Flags())
	traefikOptions.AsCobraFlagsFor(CmdInstallIngress.Flags())
	certManagerOptions.AsCobraFlagsFor(CmdInstallCertManager.Flags())
}
var CmdInstall = &cobra.Command{
	Use:   "install",
	Short: "install Epinio in your configured kubernetes cluster",
	Long:  `install Epinio PaaS in your configured kubernetes cluster`,
	Args:  cobra.ExactArgs(0),
	RunE:  install,
}
var CmdInstallIngress = &cobra.Command{
	Use:   "install-ingress",
	Short: "install Epinio's Ingress in your configured kubernetes cluster",
	Long:  `install Epinio Ingress controller in your configured kubernetes cluster`,
	Args:  cobra.ExactArgs(0),
	RunE:  installIngress,
}
var CmdInstallCertManager = &cobra.Command{
	Use:   "install-cert-manager",
	Short: "install Epinio's cert-manager in your configured kubernetes cluster",
	Long:  `install Epinio cert-manager controller in your configured kubernetes cluster`,
	Args:  cobra.ExactArgs(0),
	RunE:  installCertManager,
}
func install(cmd *cobra.Command, args []string) error {
	cmd.SilenceUsage = true
	ExitfIfError(checkDependencies(), "Cannot operate")
	skipTraefik, err := cmd.Flags().GetBool("skip-traefik")
	if err != nil {
		return errors.Wrap(err, "could not read option --skip-traefik")
	}
	ingressIP, err := cmd.Flags().GetString("loadbalancer-ip")
	if err != nil {
		return errors.Wrap(err, "could not read option --loadbalancer-ip")
	}
	if ingressIP != "" && skipTraefik {
		return errors.New("cannot have --skip-traefik and --loadbalancer-ip together")
	}
	installClient, installCleanup, err := admincmd.NewInstallClient(cmd.Context(), &neededOptions)
	defer func() {
		if installCleanup != nil {
			installCleanup()
		}
	}()
	if err != nil {
		return errors.Wrap(err, "error initializing cli")
	}
	err = installClient.Install(cmd.Context(), cmd.Flags())
	if err != nil {
		return errors.Wrap(err, "error installing Epinio")
	}
	ui := termui.NewUI()
	ui.Success().Msg("Installation Complete.")
	cfgCmd, err := admincmd.New()
	if err != nil {
		return errors.Wrap(err, "error initializing config cli")
	}
	cfgCmd.Log.Info("Initial config", "value", cfgCmd.Config.String())
	err = cfgCmd.ConfigUpdate(cmd.Context())
	if err != nil {
		return errors.Wrap(err, "error updating config")
	}
	usercmd.ClearMemoization()
	userCmd, err := usercmd.New()
	if err != nil {
		return errors.Wrap(err, "error initializing user cli")
	}
	userCmd.Log.Info("Post update config", "value", userCmd.Config.String())
	skipDefaultOrg, err := cmd.Flags().GetBool("skip-default-namespace")
	if err != nil {
		return errors.Wrap(err, "error reading option --skip-default-namespace")
	}
	if !skipDefaultOrg {
		ui.Note().Msg("Now checking ability to use the API server. And setting up useful things at the same time")
		err := userCmd.CreateOrg(DefaultOrganization)
		if err != nil {
			return errors.Wrap(err, "error creating namespace")
		}
		err = userCmd.Target(DefaultOrganization)
		if err != nil {
			return errors.Wrap(err, "failed to set target")
		}
	}
	return nil
}