From e32d108f7213ee9305a4fa1d46aa3fcc46f478ad Mon Sep 17 00:00:00 2001 From: facchettos Date: Tue, 6 Aug 2024 09:30:39 +0200 Subject: [PATCH] [UX] moved login from root level to platform subcommand --- cmd/vclusterctl/cmd/login.go | 235 +----------------- cmd/vclusterctl/cmd/logout.go | 48 +--- cmd/vclusterctl/cmd/platform/login.go | 293 +++++++++++++++++++++++ cmd/vclusterctl/cmd/platform/logout.go | 83 +++++++ cmd/vclusterctl/cmd/platform/platform.go | 10 + 5 files changed, 392 insertions(+), 277 deletions(-) create mode 100644 cmd/vclusterctl/cmd/platform/login.go create mode 100644 cmd/vclusterctl/cmd/platform/logout.go diff --git a/cmd/vclusterctl/cmd/login.go b/cmd/vclusterctl/cmd/login.go index 36677658f1..f6cfa5de79 100644 --- a/cmd/vclusterctl/cmd/login.go +++ b/cmd/vclusterctl/cmd/login.go @@ -1,31 +1,12 @@ package cmd import ( - "bytes" - "context" - "fmt" - "os" - "strings" - - dockerconfig "github.com/docker/cli/cli/config" - "github.com/docker/cli/cli/config/configfile" - managementv1 "github.com/loft-sh/api/v4/pkg/apis/management/v1" - storagev1 "github.com/loft-sh/api/v4/pkg/apis/storage/v1" "github.com/loft-sh/api/v4/pkg/product" "github.com/loft-sh/log" - "github.com/loft-sh/vcluster/cmd/vclusterctl/cmd/use" - "github.com/loft-sh/vcluster/pkg/cli/config" + platformcli "github.com/loft-sh/vcluster/cmd/vclusterctl/cmd/platform" "github.com/loft-sh/vcluster/pkg/cli/flags" - "github.com/loft-sh/vcluster/pkg/docker" - "github.com/loft-sh/vcluster/pkg/platform" - "github.com/loft-sh/vcluster/pkg/platform/clihelper" - "github.com/loft-sh/vcluster/pkg/platform/kube" "github.com/loft-sh/vcluster/pkg/upgrade" - "github.com/mgutz/ansi" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const PlatformURL = "VCLUSTER_PLATFORM_URL" @@ -42,10 +23,7 @@ type LoginCmd struct { } func NewLoginCmd(globalFlags *flags.GlobalFlags) (*cobra.Command, error) { - cmd := LoginCmd{ - GlobalFlags: globalFlags, - Log: log.GetInstance(), - } + cmd := platformcli.NewLoginCmd(globalFlags) description := `######################################################## #################### vcluster login #################### @@ -64,6 +42,7 @@ vcluster login https://my-vcluster-platform.com --access-key myaccesskey Long: description, Args: cobra.MaximumNArgs(1), RunE: func(cobraCmd *cobra.Command, args []string) error { + log.GetInstance().Warnf("\"vcluster login\" is deprecated, please use \"vcluster platform login\" instead") // Check for newer version upgrade.PrintNewerVersionWarning() @@ -78,211 +57,3 @@ vcluster login https://my-vcluster-platform.com --access-key myaccesskey return loginCmd, nil } - -func (cmd *LoginCmd) Run(ctx context.Context, args []string) error { - cfg := cmd.LoadedConfig(cmd.Log) - - var url string - // Print login information - if len(args) == 0 { - url = os.Getenv(PlatformURL) - if url == "" { - insecureFlag := "" - if cfg.Platform.Insecure { - insecureFlag = "--insecure" - } - - err := cmd.printLoginDetails(ctx) - if err != nil { - cmd.Log.Fatalf("%s\n\nYou may need to log in again via: %s login %s %s\n", err.Error(), os.Args[0], cfg.Platform.Host, insecureFlag) - } - - domain := cfg.Platform.Host - if domain == "" { - domain = "my-vcluster-platform.com" - } - - cmd.Log.WriteString(logrus.InfoLevel, fmt.Sprintf("\nTo log in as a different user, run: %s login %s %s\n\n", os.Args[0], domain, insecureFlag)) - - return nil - } - } else { - url = args[0] - } - - if !strings.HasPrefix(url, "http") { - url = "https://" + url - } - - // log into platform - loginClient := platform.NewLoginClientFromConfig(cfg) - url = strings.TrimSuffix(url, "/") - var err error - if cmd.AccessKey != "" { - err = loginClient.LoginWithAccessKey(url, cmd.AccessKey, cmd.Insecure) - } else { - err = loginClient.Login(url, cmd.Insecure, cmd.Log) - } - if err != nil { - return err - } - cmd.Log.Donef(product.Replace("Successfully logged into Loft instance %s"), ansi.Color(url, "white+b")) - - // skip log into docker registries? - if !cmd.DockerLogin { - return nil - } - - err = dockerLogin(ctx, cmd.LoadedConfig(cmd.Log), cmd.Log) - if err != nil { - return err - } - - // should switch driver - if cmd.Driver != "" { - err := use.SwitchDriver(ctx, cfg, cmd.Driver, log.GetInstance()) - if err != nil { - return fmt.Errorf("driver switch failed: %w", err) - } - } - - return nil -} - -func (cmd *LoginCmd) printLoginDetails(ctx context.Context) error { - cfg := cmd.LoadedConfig(cmd.Log) - platformClient := platform.NewClientFromConfig(cfg) - - managementClient, err := platformClient.Management() - if err != nil { - return err - } - - userName, teamName, err := platform.GetCurrentUser(ctx, managementClient) - if err != nil { - return err - } - - if userName != nil { - cmd.Log.Infof("Logged into %s as user: %s", platformClient.Config().Platform.Host, clihelper.DisplayName(&userName.EntityInfo)) - } else { - cmd.Log.Infof("Logged into %s as team: %s", platformClient.Config().Platform.Host, clihelper.DisplayName(teamName)) - } - return nil -} - -func dockerLogin(ctx context.Context, config *config.CLI, log log.Logger) error { - platformClient := platform.NewClientFromConfig(config) - - managementClient, err := platformClient.Management() - if err != nil { - return err - } - - // get user name - userName, teamName, err := platform.GetCurrentUser(ctx, managementClient) - if err != nil { - return err - } - - // collect image pull secrets from team or user - dockerConfigs := []*configfile.ConfigFile{} - if userName != nil { - // get image pull secrets from user - user, err := managementClient.Loft().ManagementV1().Users().Get(ctx, userName.Name, metav1.GetOptions{}) - if err != nil { - return err - } - dockerConfigs = append(dockerConfigs, collectImagePullSecrets(ctx, managementClient, user.Spec.ImagePullSecrets, log)...) - - // get image pull secrets from teams - for _, teamName := range user.Status.Teams { - team, err := managementClient.Loft().ManagementV1().Teams().Get(ctx, teamName, metav1.GetOptions{}) - if err != nil { - return err - } - - dockerConfigs = append(dockerConfigs, collectImagePullSecrets(ctx, managementClient, team.Spec.ImagePullSecrets, log)...) - } - } else if teamName != nil { - // get image pull secrets from team - team, err := managementClient.Loft().ManagementV1().Teams().Get(ctx, teamName.Name, metav1.GetOptions{}) - if err != nil { - return err - } - dockerConfigs = append(dockerConfigs, collectImagePullSecrets(ctx, managementClient, team.Spec.ImagePullSecrets, log)...) - } - - // store docker configs - if len(dockerConfigs) > 0 { - dockerConfig, err := docker.NewDockerConfig() - if err != nil { - return err - } - - // log into registries locally - for _, config := range dockerConfigs { - for registry, authConfig := range config.AuthConfigs { - err = dockerConfig.Store(registry, authConfig) - if err != nil { - return err - } - - if registry == "https://index.docker.io/v1/" { - registry = "docker hub" - } - - log.Donef("Successfully logged into docker registry '%s'", registry) - } - } - - err = dockerConfig.Save() - if err != nil { - return errors.Wrap(err, "save docker config") - } - } - - return nil -} - -func collectImagePullSecrets(ctx context.Context, managementClient kube.Interface, imagePullSecrets []*storagev1.KindSecretRef, log log.Logger) []*configfile.ConfigFile { - retConfigFiles := []*configfile.ConfigFile{} - for _, imagePullSecret := range imagePullSecrets { - // unknown image pull secret type? - if imagePullSecret.Kind != "SharedSecret" || (imagePullSecret.APIGroup != storagev1.SchemeGroupVersion.Group && imagePullSecret.APIGroup != managementv1.SchemeGroupVersion.Group) { - continue - } else if imagePullSecret.SecretName == "" || imagePullSecret.SecretNamespace == "" { - continue - } - - sharedSecret, err := managementClient.Loft().ManagementV1().SharedSecrets(imagePullSecret.SecretNamespace).Get(ctx, imagePullSecret.SecretName, metav1.GetOptions{}) - if err != nil { - log.Warnf("Unable to retrieve image pull secret %s/%s: %v", imagePullSecret.SecretNamespace, imagePullSecret.SecretName, err) - continue - } else if len(sharedSecret.Spec.Data) == 0 { - log.Warnf("Unable to retrieve image pull secret %s/%s: secret is empty", imagePullSecret.SecretNamespace, imagePullSecret.SecretName) - continue - } else if imagePullSecret.Key == "" && len(sharedSecret.Spec.Data) > 1 { - log.Warnf("Unable to retrieve image pull secret %s/%s: secret has multiple keys, but none is specified for image pull secret", imagePullSecret.SecretNamespace, imagePullSecret.SecretName) - continue - } - - // determine shared secret key - key := imagePullSecret.Key - if key == "" { - for k := range sharedSecret.Spec.Data { - key = k - } - } - - configFile, err := dockerconfig.LoadFromReader(bytes.NewReader(sharedSecret.Spec.Data[key])) - if err != nil { - log.Warnf("Parsing image pull secret %s/%s.%s: %v", imagePullSecret.SecretNamespace, imagePullSecret.SecretName, key, err) - continue - } - - retConfigFiles = append(retConfigFiles, configFile) - } - - return retConfigFiles -} diff --git a/cmd/vclusterctl/cmd/logout.go b/cmd/vclusterctl/cmd/logout.go index 2b6e7de1e2..542e18fdd7 100644 --- a/cmd/vclusterctl/cmd/logout.go +++ b/cmd/vclusterctl/cmd/logout.go @@ -1,31 +1,14 @@ package cmd import ( - "context" - "fmt" - - "github.com/loft-sh/api/v4/pkg/product" "github.com/loft-sh/log" - "github.com/loft-sh/vcluster/cmd/vclusterctl/cmd/use" - "github.com/loft-sh/vcluster/pkg/cli/config" + platformcli "github.com/loft-sh/vcluster/cmd/vclusterctl/cmd/platform" "github.com/loft-sh/vcluster/pkg/cli/flags" - "github.com/loft-sh/vcluster/pkg/platform" - "github.com/mgutz/ansi" "github.com/spf13/cobra" ) -type LogoutCmd struct { - *flags.GlobalFlags - - Log log.Logger -} - func NewLogoutCmd(globalFlags *flags.GlobalFlags) (*cobra.Command, error) { - cmd := &LogoutCmd{ - GlobalFlags: globalFlags, - Log: log.GetInstance(), - } - + cmd := platformcli.NewLogoutCmd(globalFlags) description := `######################################################## ################### vcluster logout #################### ######################################################## @@ -42,35 +25,10 @@ vcluster logout Long: description, Args: cobra.NoArgs, RunE: func(cobraCmd *cobra.Command, _ []string) error { + log.GetInstance().Warnf("\"vcluster logout\" is deprecated, please use \"vcluster platform logout\" instead") return cmd.Run(cobraCmd.Context()) }, } return logoutCmd, nil } - -func (cmd *LogoutCmd) Run(ctx context.Context) error { - platformClient := platform.NewClientFromConfig(cmd.LoadedConfig(cmd.Log)) - - // delete old access key if were logged in before - cfg := platformClient.Config() - if cfg.Platform.AccessKey != "" { - if err := platformClient.Logout(ctx); err != nil { - cmd.Log.Errorf("failed to send logout request: %v", err) - } - - configHost := cfg.Platform.Host - cfg.Platform.Host = "" - cfg.Platform.AccessKey = "" - cfg.Platform.LastInstallContext = "" - cfg.Platform.Insecure = false - - if err := platformClient.Save(); err != nil { - return fmt.Errorf("save config: %w", err) - } - - cmd.Log.Donef(product.Replace("Successfully logged out of loft instance %s"), ansi.Color(configHost, "white+b")) - } - - return use.SwitchDriver(ctx, cfg, string(config.HelmDriver), cmd.Log) -} diff --git a/cmd/vclusterctl/cmd/platform/login.go b/cmd/vclusterctl/cmd/platform/login.go new file mode 100644 index 0000000000..3c035ede23 --- /dev/null +++ b/cmd/vclusterctl/cmd/platform/login.go @@ -0,0 +1,293 @@ +package platform + +import ( + "bytes" + "context" + "fmt" + "os" + "strings" + + dockerconfig "github.com/docker/cli/cli/config" + "github.com/docker/cli/cli/config/configfile" + managementv1 "github.com/loft-sh/api/v4/pkg/apis/management/v1" + storagev1 "github.com/loft-sh/api/v4/pkg/apis/storage/v1" + "github.com/loft-sh/api/v4/pkg/product" + "github.com/loft-sh/log" + "github.com/loft-sh/vcluster/cmd/vclusterctl/cmd/use" + "github.com/loft-sh/vcluster/pkg/cli/config" + "github.com/loft-sh/vcluster/pkg/cli/flags" + "github.com/loft-sh/vcluster/pkg/docker" + "github.com/loft-sh/vcluster/pkg/platform" + "github.com/loft-sh/vcluster/pkg/platform/clihelper" + "github.com/loft-sh/vcluster/pkg/platform/kube" + "github.com/loft-sh/vcluster/pkg/upgrade" + "github.com/mgutz/ansi" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const PlatformURL = "VCLUSTER_PLATFORM_URL" + +type LoginCmd struct { + *flags.GlobalFlags + + Log log.Logger + + Driver string + AccessKey string + Insecure bool + DockerLogin bool +} + +func NewLoginCmd(gf *flags.GlobalFlags) LoginCmd { + return LoginCmd{ + GlobalFlags: gf, + Log: log.GetInstance(), + } + +} + +func NewCobraLoginCmd(globalFlags *flags.GlobalFlags) (*cobra.Command, error) { + cmd := NewLoginCmd(globalFlags) + + description := `######################################################## +############### vcluster platform login ################ +######################################################## +Login into vCluster platform + +Example: +vcluster platform login https://my-vcluster-platform.com +vcluster platform login https://my-vcluster-platform.com --access-key myaccesskey +######################################################## + ` + + loginCmd := &cobra.Command{ + Use: "login [VCLUSTER_PLATFORM_HOST]", + Short: "Login to a vCluster platform instance", + Long: description, + Args: cobra.MaximumNArgs(1), + RunE: func(cobraCmd *cobra.Command, args []string) error { + // Check for newer version + upgrade.PrintNewerVersionWarning() + + return cmd.Run(cobraCmd.Context(), args) + }, + } + + loginCmd.Flags().StringVar(&cmd.Driver, "use-driver", "", "Switch vCluster driver between platform and helm") + loginCmd.Flags().StringVar(&cmd.AccessKey, "access-key", "", "The access key to use") + loginCmd.Flags().BoolVar(&cmd.Insecure, "insecure", true, product.Replace("Allow login into an insecure Loft instance")) + loginCmd.Flags().BoolVar(&cmd.DockerLogin, "docker-login", true, "If true, will log into the docker image registries the user has image pull secrets for") + + return loginCmd, nil +} + +func (cmd *LoginCmd) Run(ctx context.Context, args []string) error { + cfg := cmd.LoadedConfig(cmd.Log) + + var url string + // Print login information + if len(args) == 0 { + url = os.Getenv(PlatformURL) + if url == "" { + insecureFlag := "" + if cfg.Platform.Insecure { + insecureFlag = "--insecure" + } + + err := cmd.printLoginDetails(ctx) + if err != nil { + cmd.Log.Fatalf("%s\n\nYou may need to log in again via: %s platform login %s %s\n", err.Error(), os.Args[0], cfg.Platform.Host, insecureFlag) + } + + domain := cfg.Platform.Host + if domain == "" { + domain = "my-vcluster-platform.com" + } + + cmd.Log.WriteString(logrus.InfoLevel, fmt.Sprintf("\nTo log in as a different user, run: %s platform login %s %s\n\n", os.Args[0], domain, insecureFlag)) + + return nil + } + } else { + url = args[0] + } + + if !strings.HasPrefix(url, "http") { + url = "https://" + url + } + + // log into platform + loginClient := platform.NewLoginClientFromConfig(cfg) + url = strings.TrimSuffix(url, "/") + var err error + if cmd.AccessKey != "" { + err = loginClient.LoginWithAccessKey(url, cmd.AccessKey, cmd.Insecure) + } else { + err = loginClient.Login(url, cmd.Insecure, cmd.Log) + } + if err != nil { + return err + } + cmd.Log.Donef(product.Replace("Successfully logged into Loft instance %s"), ansi.Color(url, "white+b")) + + // skip log into docker registries? + if !cmd.DockerLogin { + return nil + } + + err = dockerLogin(ctx, cmd.LoadedConfig(cmd.Log), cmd.Log) + if err != nil { + return err + } + + // should switch driver + if cmd.Driver != "" { + err := use.SwitchDriver(ctx, cfg, cmd.Driver, log.GetInstance()) + if err != nil { + return fmt.Errorf("driver switch failed: %w", err) + } + } + + return nil +} + +func (cmd *LoginCmd) printLoginDetails(ctx context.Context) error { + cfg := cmd.LoadedConfig(cmd.Log) + platformClient := platform.NewClientFromConfig(cfg) + + managementClient, err := platformClient.Management() + if err != nil { + return err + } + + userName, teamName, err := platform.GetCurrentUser(ctx, managementClient) + if err != nil { + return err + } + + if userName != nil { + cmd.Log.Infof("Logged into %s as user: %s", platformClient.Config().Platform.Host, clihelper.DisplayName(&userName.EntityInfo)) + } else { + cmd.Log.Infof("Logged into %s as team: %s", platformClient.Config().Platform.Host, clihelper.DisplayName(teamName)) + } + return nil +} + +func dockerLogin(ctx context.Context, config *config.CLI, log log.Logger) error { + platformClient := platform.NewClientFromConfig(config) + + managementClient, err := platformClient.Management() + if err != nil { + return err + } + + // get user name + userName, teamName, err := platform.GetCurrentUser(ctx, managementClient) + if err != nil { + return err + } + + // collect image pull secrets from team or user + dockerConfigs := []*configfile.ConfigFile{} + if userName != nil { + // get image pull secrets from user + user, err := managementClient.Loft().ManagementV1().Users().Get(ctx, userName.Name, metav1.GetOptions{}) + if err != nil { + return err + } + dockerConfigs = append(dockerConfigs, collectImagePullSecrets(ctx, managementClient, user.Spec.ImagePullSecrets, log)...) + + // get image pull secrets from teams + for _, teamName := range user.Status.Teams { + team, err := managementClient.Loft().ManagementV1().Teams().Get(ctx, teamName, metav1.GetOptions{}) + if err != nil { + return err + } + + dockerConfigs = append(dockerConfigs, collectImagePullSecrets(ctx, managementClient, team.Spec.ImagePullSecrets, log)...) + } + } else if teamName != nil { + // get image pull secrets from team + team, err := managementClient.Loft().ManagementV1().Teams().Get(ctx, teamName.Name, metav1.GetOptions{}) + if err != nil { + return err + } + dockerConfigs = append(dockerConfigs, collectImagePullSecrets(ctx, managementClient, team.Spec.ImagePullSecrets, log)...) + } + + // store docker configs + if len(dockerConfigs) > 0 { + dockerConfig, err := docker.NewDockerConfig() + if err != nil { + return err + } + + // log into registries locally + for _, config := range dockerConfigs { + for registry, authConfig := range config.AuthConfigs { + err = dockerConfig.Store(registry, authConfig) + if err != nil { + return err + } + + if registry == "https://index.docker.io/v1/" { + registry = "docker hub" + } + + log.Donef("Successfully logged into docker registry '%s'", registry) + } + } + + err = dockerConfig.Save() + if err != nil { + return errors.Wrap(err, "save docker config") + } + } + + return nil +} + +func collectImagePullSecrets(ctx context.Context, managementClient kube.Interface, imagePullSecrets []*storagev1.KindSecretRef, log log.Logger) []*configfile.ConfigFile { + retConfigFiles := []*configfile.ConfigFile{} + for _, imagePullSecret := range imagePullSecrets { + // unknown image pull secret type? + if imagePullSecret.Kind != "SharedSecret" || (imagePullSecret.APIGroup != storagev1.SchemeGroupVersion.Group && imagePullSecret.APIGroup != managementv1.SchemeGroupVersion.Group) { + continue + } else if imagePullSecret.SecretName == "" || imagePullSecret.SecretNamespace == "" { + continue + } + + sharedSecret, err := managementClient.Loft().ManagementV1().SharedSecrets(imagePullSecret.SecretNamespace).Get(ctx, imagePullSecret.SecretName, metav1.GetOptions{}) + if err != nil { + log.Warnf("Unable to retrieve image pull secret %s/%s: %v", imagePullSecret.SecretNamespace, imagePullSecret.SecretName, err) + continue + } else if len(sharedSecret.Spec.Data) == 0 { + log.Warnf("Unable to retrieve image pull secret %s/%s: secret is empty", imagePullSecret.SecretNamespace, imagePullSecret.SecretName) + continue + } else if imagePullSecret.Key == "" && len(sharedSecret.Spec.Data) > 1 { + log.Warnf("Unable to retrieve image pull secret %s/%s: secret has multiple keys, but none is specified for image pull secret", imagePullSecret.SecretNamespace, imagePullSecret.SecretName) + continue + } + + // determine shared secret key + key := imagePullSecret.Key + if key == "" { + for k := range sharedSecret.Spec.Data { + key = k + } + } + + configFile, err := dockerconfig.LoadFromReader(bytes.NewReader(sharedSecret.Spec.Data[key])) + if err != nil { + log.Warnf("Parsing image pull secret %s/%s.%s: %v", imagePullSecret.SecretNamespace, imagePullSecret.SecretName, key, err) + continue + } + + retConfigFiles = append(retConfigFiles, configFile) + } + + return retConfigFiles +} diff --git a/cmd/vclusterctl/cmd/platform/logout.go b/cmd/vclusterctl/cmd/platform/logout.go new file mode 100644 index 0000000000..e74b79ef67 --- /dev/null +++ b/cmd/vclusterctl/cmd/platform/logout.go @@ -0,0 +1,83 @@ +package platform + +import ( + "context" + "fmt" + + "github.com/loft-sh/api/v4/pkg/product" + "github.com/loft-sh/log" + "github.com/loft-sh/vcluster/cmd/vclusterctl/cmd/use" + "github.com/loft-sh/vcluster/pkg/cli/config" + "github.com/loft-sh/vcluster/pkg/cli/flags" + "github.com/loft-sh/vcluster/pkg/platform" + "github.com/mgutz/ansi" + "github.com/spf13/cobra" +) + +type LogoutCmd struct { + *flags.GlobalFlags + + Log log.Logger +} + +func NewLogoutCmd(gf *flags.GlobalFlags) LogoutCmd { + return LogoutCmd{ + GlobalFlags: gf, + Log: log.GetInstance(), + } +} + +func NewLogoutCobraCmd(globalFlags *flags.GlobalFlags) (*cobra.Command, error) { + cmd := &LogoutCmd{ + GlobalFlags: globalFlags, + Log: log.GetInstance(), + } + + description := `######################################################## +############## vcluster platform logout ################ +######################################################## +Log out of vCluster platform + +Example: +vcluster platform logout +######################################################## + ` + + logoutCmd := &cobra.Command{ + Use: "logout", + Short: "Log out of a vCluster platform instance", + Long: description, + Args: cobra.NoArgs, + RunE: func(cobraCmd *cobra.Command, _ []string) error { + return cmd.Run(cobraCmd.Context()) + }, + } + + return logoutCmd, nil +} + +func (cmd *LogoutCmd) Run(ctx context.Context) error { + platformClient := platform.NewClientFromConfig(cmd.LoadedConfig(cmd.Log)) + + // delete old access key if were logged in before + cfg := platformClient.Config() + if cfg.Platform.AccessKey != "" { + if err := platformClient.Logout(ctx); err != nil { + cmd.Log.Errorf("failed to send logout request: %v", err) + } + + configHost := cfg.Platform.Host + cfg.Platform.Host = "" + cfg.Platform.AccessKey = "" + cfg.Platform.LastInstallContext = "" + cfg.Platform.Insecure = false + + if err := platformClient.Save(); err != nil { + return fmt.Errorf("save config: %w", err) + } + + cmd.Log.Donef(product.Replace("Successfully logged out of loft instance %s"), ansi.Color(configHost, "white+b")) + } + + return use.SwitchDriver(ctx, cfg, string(config.HelmDriver), cmd.Log) +} diff --git a/cmd/vclusterctl/cmd/platform/platform.go b/cmd/vclusterctl/cmd/platform/platform.go index 2eaa663cbd..49dac5a06f 100644 --- a/cmd/vclusterctl/cmd/platform/platform.go +++ b/cmd/vclusterctl/cmd/platform/platform.go @@ -48,6 +48,14 @@ func NewPlatformCmd(globalFlags *flags.GlobalFlags) (*cobra.Command, error) { } startCmd := NewStartCmd(globalFlags) + loginCmd, err := NewCobraLoginCmd(globalFlags) + if err != nil { + return nil, err + } + logoutCmd, err := NewLogoutCobraCmd(globalFlags) + if err != nil { + return nil, err + } platformCmd.AddCommand(startCmd) platformCmd.AddCommand(NewResetCmd(globalFlags)) @@ -63,6 +71,8 @@ func NewPlatformCmd(globalFlags *flags.GlobalFlags) (*cobra.Command, error) { platformCmd.AddCommand(share.NewShareCmd(globalFlags, defaults)) platformCmd.AddCommand(create.NewCreateCmd(globalFlags, defaults)) platformCmd.AddCommand(cmddelete.NewDeleteCmd(globalFlags, defaults)) + platformCmd.AddCommand(loginCmd) + platformCmd.AddCommand(logoutCmd) return platformCmd, nil }