From be46e11f5bb34000f49f7475dfb62a52e01f2310 Mon Sep 17 00:00:00 2001 From: Johannes Frey Date: Thu, 16 May 2024 18:14:57 +0200 Subject: [PATCH] chore(vclusterctl): consolidate vcluster cli and platform config --- cmd/vclusterctl/cmd/connect.go | 18 +-- cmd/vclusterctl/cmd/create.go | 19 +-- cmd/vclusterctl/cmd/delete.go | 21 ++-- cmd/vclusterctl/cmd/info.go | 10 +- cmd/vclusterctl/cmd/list.go | 15 +-- cmd/vclusterctl/cmd/login.go | 5 +- cmd/vclusterctl/cmd/logout.go | 10 +- cmd/vclusterctl/cmd/pause.go | 15 ++- cmd/vclusterctl/cmd/platform/access_key.go | 2 +- cmd/vclusterctl/cmd/platform/import.go | 26 ++-- cmd/vclusterctl/cmd/platform/platform.go | 3 +- cmd/vclusterctl/cmd/platform/pro.go | 3 +- cmd/vclusterctl/cmd/resume.go | 16 +-- cmd/vclusterctl/cmd/root.go | 23 ++-- cmd/vclusterctl/cmd/telemetry/disable.go | 13 +- cmd/vclusterctl/cmd/telemetry/enable.go | 13 +- cmd/vclusterctl/cmd/telemetry/telemetry.go | 7 +- cmd/vclusterctl/cmd/ui.go | 3 +- cmd/vclusterctl/cmd/use/manager.go | 30 +++-- pkg/cli/activate_helm.go | 7 +- pkg/cli/activate_platform.go | 8 +- pkg/cli/config/config.go | 140 +++++++++++++++++++++ pkg/cli/config/types.go | 22 ++++ pkg/cli/connect_platform.go | 7 +- pkg/cli/create_helm.go | 18 +-- pkg/cli/create_platform.go | 46 ++++--- pkg/cli/delete_helm.go | 9 +- pkg/cli/delete_platform.go | 7 +- pkg/cli/flags/flags.go | 35 +++++- pkg/cli/list_helm.go | 8 +- pkg/cli/list_platform.go | 6 +- pkg/cli/pause_platform.go | 7 +- pkg/cli/resume_platform.go | 7 +- pkg/manager/types.go | 8 ++ pkg/platform/client.go | 39 +++--- pkg/platform/config.go | 121 ------------------ pkg/platform/flags.go | 29 ----- pkg/platform/secret.go | 22 ++-- pkg/platform/types.go | 64 +++++++++- pkg/telemetry/collect_cli.go | 22 ++-- pkg/telemetry/helpers.go | 14 +-- pkg/telemetry/noop.go | 2 +- pkg/util/cliconfig/config.go | 107 ---------------- 43 files changed, 514 insertions(+), 493 deletions(-) create mode 100644 pkg/cli/config/config.go create mode 100644 pkg/cli/config/types.go create mode 100644 pkg/manager/types.go delete mode 100644 pkg/platform/config.go delete mode 100644 pkg/platform/flags.go delete mode 100644 pkg/util/cliconfig/config.go diff --git a/cmd/vclusterctl/cmd/connect.go b/cmd/vclusterctl/cmd/connect.go index d1d09fcee0..64e430e474 100644 --- a/cmd/vclusterctl/cmd/connect.go +++ b/cmd/vclusterctl/cmd/connect.go @@ -7,7 +7,9 @@ import ( loftctlUtil "github.com/loft-sh/loftctl/v4/pkg/util" "github.com/loft-sh/log" "github.com/loft-sh/vcluster/pkg/cli" + "github.com/loft-sh/vcluster/pkg/cli/config" "github.com/loft-sh/vcluster/pkg/cli/flags" + "github.com/loft-sh/vcluster/pkg/manager" "github.com/loft-sh/vcluster/pkg/platform" "github.com/loft-sh/vcluster/pkg/upgrade" "github.com/spf13/cobra" @@ -91,15 +93,13 @@ func (cmd *ConnectCmd) Run(ctx context.Context, args []string) error { return err } - // get manager - manager, err := platform.GetManager(cmd.Manager) - if err != nil { - return err - } - - // is platform manager? - if manager == platform.ManagerPlatform { - return cli.ConnectPlatform(ctx, &cmd.ConnectOptions, cmd.GlobalFlags, vClusterName, args[1:], cmd.Log) + c := config.Read(cmd.Config, cmd.Log) + if c.Manager.Type == manager.Platform { + platformClient, err := platform.CreatePlatformClient(ctx, c.Platform.Config) + if err != nil { + return err + } + return cli.ConnectPlatform(ctx, &cmd.ConnectOptions, platformClient, cmd.GlobalFlags, vClusterName, args[1:], cmd.Log) } return cli.ConnectHelm(ctx, &cmd.ConnectOptions, cmd.GlobalFlags, vClusterName, args[1:], cmd.Log) diff --git a/cmd/vclusterctl/cmd/create.go b/cmd/vclusterctl/cmd/create.go index e349f6bf6c..3e54311ba6 100644 --- a/cmd/vclusterctl/cmd/create.go +++ b/cmd/vclusterctl/cmd/create.go @@ -8,8 +8,10 @@ import ( loftctlUtil "github.com/loft-sh/loftctl/v4/pkg/util" "github.com/loft-sh/log" "github.com/loft-sh/vcluster/pkg/cli" + "github.com/loft-sh/vcluster/pkg/cli/config" "github.com/loft-sh/vcluster/pkg/cli/flags" "github.com/loft-sh/vcluster/pkg/constants" + "github.com/loft-sh/vcluster/pkg/manager" "github.com/loft-sh/vcluster/pkg/platform" "github.com/loft-sh/vcluster/pkg/upgrade" "github.com/spf13/cobra" @@ -94,16 +96,17 @@ vcluster create test --namespace test // Run executes the functionality func (cmd *CreateCmd) Run(ctx context.Context, args []string) error { - manager, err := platform.GetManager(cmd.Manager) - if err != nil { - return err - } + c := config.Read(cmd.Config, cmd.log) // check if we should create a platform vCluster - platform.PrintManagerInfo("create", manager, cmd.log) - if manager == platform.ManagerPlatform { - return cli.CreatePlatform(ctx, &cmd.CreateOptions, cmd.GlobalFlags, args[0], cmd.log) + config.PrintManagerInfo("create", c.Manager.Type, cmd.log) + if c.Manager.Type == manager.Platform { + platformClient, err := platform.CreatePlatformClient(ctx, c.Platform.Config) + if err != nil { + return err + } + return cli.CreatePlatform(ctx, &cmd.CreateOptions, platformClient, cmd.GlobalFlags, args[0], cmd.log) } - return cli.CreateHelm(ctx, &cmd.CreateOptions, cmd.GlobalFlags, args[0], cmd.log) + return cli.CreateHelm(ctx, &cmd.CreateOptions, &c.Platform.Config, cmd.GlobalFlags, args[0], cmd.log) } diff --git a/cmd/vclusterctl/cmd/delete.go b/cmd/vclusterctl/cmd/delete.go index 4e62024e48..c772e554fd 100644 --- a/cmd/vclusterctl/cmd/delete.go +++ b/cmd/vclusterctl/cmd/delete.go @@ -4,7 +4,9 @@ import ( loftctlUtil "github.com/loft-sh/loftctl/v4/pkg/util" "github.com/loft-sh/log" "github.com/loft-sh/vcluster/pkg/cli" + "github.com/loft-sh/vcluster/pkg/cli/config" "github.com/loft-sh/vcluster/pkg/cli/flags" + "github.com/loft-sh/vcluster/pkg/manager" "github.com/loft-sh/vcluster/pkg/platform" "github.com/spf13/cobra" ) @@ -62,17 +64,16 @@ vcluster delete test --namespace test // Run executes the functionality func (cmd *DeleteCmd) Run(cobraCmd *cobra.Command, args []string) error { - manager, err := platform.GetManager(cmd.Manager) - if err != nil { - return err - } + c := config.Read(cmd.Config, cmd.log) + config.PrintManagerInfo("delete", c.Manager.Type, cmd.log) - // check if we should delete a platform vCluster - platform.PrintManagerInfo("delete", manager, cmd.log) - if manager == platform.ManagerPlatform { - // deploy platform cluster - return cli.DeletePlatform(cobraCmd.Context(), &cmd.DeleteOptions, args[0], cmd.log) + if c.Manager.Type == manager.Platform { + platformClient, err := platform.CreatePlatformClient(cobraCmd.Context(), c.Platform.Config) + if err != nil { + return err + } + return cli.DeletePlatform(cobraCmd.Context(), &cmd.DeleteOptions, platformClient, args[0], cmd.log) } - return cli.DeleteHelm(cobraCmd.Context(), &cmd.DeleteOptions, cmd.GlobalFlags, args[0], cmd.log) + return cli.DeleteHelm(cobraCmd.Context(), &cmd.DeleteOptions, c.Platform.Config, cmd.GlobalFlags, args[0], cmd.log) } diff --git a/cmd/vclusterctl/cmd/info.go b/cmd/vclusterctl/cmd/info.go index 799f108948..061ad72255 100644 --- a/cmd/vclusterctl/cmd/info.go +++ b/cmd/vclusterctl/cmd/info.go @@ -5,6 +5,8 @@ import ( "runtime" "github.com/loft-sh/log" + "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/loft-sh/vcluster/pkg/telemetry" "github.com/spf13/cobra" @@ -20,7 +22,7 @@ type cliInfo struct { } // NewInfoCmd creates a new info command -func NewInfoCmd() *cobra.Command { +func NewInfoCmd(globalFlags *flags.GlobalFlags) *cobra.Command { cobraCmd := &cobra.Command{ Use: "info", Short: "Displays informations about the cli and platform", @@ -37,14 +39,16 @@ vcluster info Args: cobra.NoArgs, Hidden: true, RunE: func(cobraCmd *cobra.Command, _ []string) error { + logger := log.GetInstance() infos := cliInfo{ Version: cobraCmd.Root().Version, OS: runtime.GOOS, Arch: runtime.GOARCH, - MachineID: telemetry.GetMachineID(log.GetInstance()), + MachineID: telemetry.GetMachineID(globalFlags.Config, logger), } - platformClient, err := platform.CreatePlatformClient() + c := config.Read(globalFlags.Config, logger) + platformClient, err := platform.CreatePlatformClient(cobraCmd.Context(), c.Platform.Config) if err == nil { infos.InstanceID = platformClient.Self().Status.InstanceID } diff --git a/cmd/vclusterctl/cmd/list.go b/cmd/vclusterctl/cmd/list.go index 8749adc26c..4ba86f865f 100644 --- a/cmd/vclusterctl/cmd/list.go +++ b/cmd/vclusterctl/cmd/list.go @@ -3,8 +3,9 @@ package cmd import ( "github.com/loft-sh/log" "github.com/loft-sh/vcluster/pkg/cli" + "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/loft-sh/vcluster/pkg/manager" "github.com/spf13/cobra" ) @@ -53,15 +54,11 @@ vcluster list --namespace test // Run executes the functionality func (cmd *ListCmd) Run(cobraCmd *cobra.Command, _ []string) error { - manager, err := platform.GetManager(cmd.Manager) - if err != nil { - return err - } - + c := config.Read(cmd.Config, cmd.log) // check if we should create a platform vCluster - if manager == platform.ManagerPlatform { - return cli.ListPlatform(cobraCmd.Context(), &cmd.ListOptions, cmd.GlobalFlags, cmd.log) + if c.Manager.Type == manager.Platform { + return cli.ListPlatform(cobraCmd.Context(), &cmd.ListOptions, c.Platform.Config, cmd.GlobalFlags, cmd.log) } - return cli.ListHelm(cobraCmd.Context(), &cmd.ListOptions, cmd.GlobalFlags, cmd.log) + return cli.ListHelm(cobraCmd.Context(), &cmd.ListOptions, c.Platform.Config, cmd.GlobalFlags, cmd.log) } diff --git a/cmd/vclusterctl/cmd/login.go b/cmd/vclusterctl/cmd/login.go index d640d03b88..f99e8ae0e9 100644 --- a/cmd/vclusterctl/cmd/login.go +++ b/cmd/vclusterctl/cmd/login.go @@ -8,7 +8,6 @@ import ( "github.com/loft-sh/log" "github.com/loft-sh/vcluster/cmd/vclusterctl/cmd/use" "github.com/loft-sh/vcluster/pkg/cli/flags" - "github.com/loft-sh/vcluster/pkg/platform" "github.com/spf13/cobra" ) @@ -21,7 +20,7 @@ type LoginOptions struct { } func NewLoginCmd(globalFlags *flags.GlobalFlags) (*cobra.Command, error) { - loftGlobalFlags, err := platform.GlobalFlags(globalFlags) + loftGlobalFlags, err := flags.LoftctlGlobalFlags(globalFlags) if err != nil { return nil, fmt.Errorf("failed to parse pro flags: %w", err) } @@ -61,7 +60,7 @@ vcluster login https://my-vcluster-platform.com --access-key myaccesskey // should switch manager if options.Manager != "" { - err = use.SwitchManager(options.Manager, log.GetInstance()) + err = use.SwitchManager(cobraCmd.Context(), globalFlags.Config, options.Manager, log.GetInstance()) if err != nil { return fmt.Errorf("switch manager failed: %w", err) } diff --git a/cmd/vclusterctl/cmd/logout.go b/cmd/vclusterctl/cmd/logout.go index e4a42b9221..6493a93d78 100644 --- a/cmd/vclusterctl/cmd/logout.go +++ b/cmd/vclusterctl/cmd/logout.go @@ -6,13 +6,15 @@ import ( loftctl "github.com/loft-sh/loftctl/v4/cmd/loftctl/cmd" "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/manager" "github.com/loft-sh/vcluster/pkg/platform" "github.com/spf13/cobra" ) func NewLogoutCmd(globalFlags *flags.GlobalFlags) (*cobra.Command, error) { - loftctlGlobalFlags, err := platform.GlobalFlags(globalFlags) + loftctlGlobalFlags, err := flags.LoftctlGlobalFlags(globalFlags) if err != nil { return nil, fmt.Errorf("failed to parse pro flags: %w", err) } @@ -38,7 +40,9 @@ vcluster logout Long: description, Args: cobra.NoArgs, RunE: func(cobraCmd *cobra.Command, args []string) error { - _, err := platform.CreatePlatformClient() + logger := log.GetInstance() + c := config.Read(cmd.Config, logger) + _, err := platform.CreatePlatformClient(cobraCmd.Context(), c.Platform.Config) if err != nil { return err } @@ -48,7 +52,7 @@ vcluster logout return err } - err = use.SwitchManager(string(platform.ManagerHelm), log.GetInstance()) + err = use.SwitchManager(cobraCmd.Context(), globalFlags.Config, string(manager.Helm), logger) if err != nil { return err } diff --git a/cmd/vclusterctl/cmd/pause.go b/cmd/vclusterctl/cmd/pause.go index 010db7224e..2c783aa0e0 100644 --- a/cmd/vclusterctl/cmd/pause.go +++ b/cmd/vclusterctl/cmd/pause.go @@ -7,7 +7,9 @@ import ( loftctlUtil "github.com/loft-sh/loftctl/v4/pkg/util" "github.com/loft-sh/log" "github.com/loft-sh/vcluster/pkg/cli" + "github.com/loft-sh/vcluster/pkg/cli/config" "github.com/loft-sh/vcluster/pkg/cli/flags" + "github.com/loft-sh/vcluster/pkg/manager" "github.com/loft-sh/vcluster/pkg/platform" "github.com/spf13/cobra" ) @@ -65,14 +67,15 @@ vcluster pause test --namespace test // Run executes the functionality func (cmd *PauseCmd) Run(ctx context.Context, args []string) error { - manager, err := platform.GetManager(cmd.Manager) - if err != nil { - return err - } + c := config.Read(cmd.Config, cmd.Log) // check if we should create a platform vCluster - if manager == platform.ManagerPlatform { - return cli.PausePlatform(ctx, &cmd.PauseOptions, args[0], cmd.Log) + if c.Manager.Type == manager.Platform { + platformClient, err := platform.CreatePlatformClient(ctx, c.Platform.Config) + if err != nil { + return err + } + return cli.PausePlatform(ctx, &cmd.PauseOptions, platformClient, args[0], cmd.Log) } return cli.PauseHelm(ctx, cmd.GlobalFlags, args[0], cmd.Log) diff --git a/cmd/vclusterctl/cmd/platform/access_key.go b/cmd/vclusterctl/cmd/platform/access_key.go index 406cd1cdbd..4a57340111 100644 --- a/cmd/vclusterctl/cmd/platform/access_key.go +++ b/cmd/vclusterctl/cmd/platform/access_key.go @@ -88,7 +88,7 @@ func getToken(cmd *AccessKeyCmd, baseClient client.Client) error { if config == nil { return ErrNoConfigLoaded } else if config.Host == "" || config.AccessKey == "" { - return fmt.Errorf("%w: please make sure you have run '%s [%s]'", ErrNotLoggedIn, product.LoginCmd(), product.Url()) + return fmt.Errorf("%w: please make sure you have run '%s' to create one or '%s [%s]' if one already exists", ErrNotLoggedIn, product.StartCmd(), product.LoginCmd(), product.Url()) } // by default we print the access key as token diff --git a/cmd/vclusterctl/cmd/platform/import.go b/cmd/vclusterctl/cmd/platform/import.go index 1d2a6d4b89..e9b6aabb53 100644 --- a/cmd/vclusterctl/cmd/platform/import.go +++ b/cmd/vclusterctl/cmd/platform/import.go @@ -2,11 +2,14 @@ package platform import ( "context" + "fmt" loftctlUtil "github.com/loft-sh/loftctl/v4/pkg/util" "github.com/loft-sh/log" "github.com/loft-sh/vcluster/pkg/cli" + "github.com/loft-sh/vcluster/pkg/cli/config" "github.com/loft-sh/vcluster/pkg/cli/flags" + "github.com/loft-sh/vcluster/pkg/manager" "github.com/loft-sh/vcluster/pkg/platform" "github.com/spf13/cobra" ) @@ -56,15 +59,24 @@ vcluster platform import my-vcluster --cluster connected-cluster \ // Run executes the functionality func (cmd *ImportCmd) Run(ctx context.Context, args []string) error { - manager, err := platform.GetManager(cmd.Manager) - if err != nil { - return err - } + c := config.Read(cmd.Config, cmd.Log) // check if we should create a platform vCluster - if manager == platform.ManagerPlatform { - return cli.ActivatePlatform(ctx, &cmd.ActivateOptions, cmd.GlobalFlags, args[0], cmd.Log) + if c.Manager.Type == manager.Platform { + platformClient, err := platform.CreatePlatformClient(ctx, c.Platform.Config) + if err != nil { + return err + } + + return cli.ActivatePlatform(ctx, &cmd.ActivateOptions, platformClient, cmd.GlobalFlags, args[0], cmd.Log) + } + + if err := cli.ActivateHelm(ctx, &cmd.ActivateOptions, &c.Platform.Config, cmd.GlobalFlags, args[0], cmd.Log); err != nil { + return err + } + if err := config.Write(cmd.Config, c); err != nil { + return fmt.Errorf("save vCluster config: %w", err) } - return cli.ActivateHelm(ctx, &cmd.ActivateOptions, cmd.GlobalFlags, args[0], cmd.Log) + return nil } diff --git a/cmd/vclusterctl/cmd/platform/platform.go b/cmd/vclusterctl/cmd/platform/platform.go index fad705bddf..e935476841 100644 --- a/cmd/vclusterctl/cmd/platform/platform.go +++ b/cmd/vclusterctl/cmd/platform/platform.go @@ -5,7 +5,6 @@ import ( "github.com/loft-sh/vcluster/cmd/vclusterctl/cmd/platform/connect" "github.com/loft-sh/vcluster/pkg/cli/flags" - "github.com/loft-sh/vcluster/pkg/platform" "github.com/spf13/cobra" ) @@ -20,7 +19,7 @@ func NewPlatformCmd(globalFlags *flags.GlobalFlags) (*cobra.Command, error) { Args: cobra.NoArgs, } - loftctlGlobalFlags, err := platform.GlobalFlags(globalFlags) + loftctlGlobalFlags, err := flags.LoftctlGlobalFlags(globalFlags) if err != nil { return nil, fmt.Errorf("failed to parse pro flags: %w", err) } diff --git a/cmd/vclusterctl/cmd/platform/pro.go b/cmd/vclusterctl/cmd/platform/pro.go index c366f6535f..49493f914d 100644 --- a/cmd/vclusterctl/cmd/platform/pro.go +++ b/cmd/vclusterctl/cmd/platform/pro.go @@ -5,7 +5,6 @@ import ( "github.com/loft-sh/vcluster/cmd/vclusterctl/cmd/platform/connect" "github.com/loft-sh/vcluster/pkg/cli/flags" - "github.com/loft-sh/vcluster/pkg/platform" "github.com/spf13/cobra" ) @@ -22,7 +21,7 @@ Deprecated, please use vcluster platform instead Args: cobra.NoArgs, } - loftctlGlobalFlags, err := platform.GlobalFlags(globalFlags) + loftctlGlobalFlags, err := flags.LoftctlGlobalFlags(globalFlags) if err != nil { return nil, fmt.Errorf("failed to parse pro flags: %w", err) } diff --git a/cmd/vclusterctl/cmd/resume.go b/cmd/vclusterctl/cmd/resume.go index 370d2d1959..f68d89b925 100644 --- a/cmd/vclusterctl/cmd/resume.go +++ b/cmd/vclusterctl/cmd/resume.go @@ -6,7 +6,9 @@ import ( loftctlUtil "github.com/loft-sh/loftctl/v4/pkg/util" "github.com/loft-sh/log" "github.com/loft-sh/vcluster/pkg/cli" + "github.com/loft-sh/vcluster/pkg/cli/config" "github.com/loft-sh/vcluster/pkg/cli/flags" + "github.com/loft-sh/vcluster/pkg/manager" "github.com/loft-sh/vcluster/pkg/platform" "github.com/spf13/cobra" ) @@ -60,14 +62,14 @@ vcluster resume test --namespace test // Run executes the functionality func (cmd *ResumeCmd) Run(ctx context.Context, args []string) error { - manager, err := platform.GetManager(cmd.Manager) - if err != nil { - return err - } - + c := config.Read(cmd.Config, cmd.Log) // check if we should resume a platform backed virtual cluster - if manager == platform.ManagerPlatform { - return cli.ResumePlatform(ctx, &cmd.ResumeOptions, args[0], cmd.Log) + if c.Manager.Type == manager.Platform { + platformClient, err := platform.CreatePlatformClient(ctx, c.Platform.Config) + if err != nil { + return err + } + return cli.ResumePlatform(ctx, &cmd.ResumeOptions, platformClient, args[0], cmd.Log) } return cli.ResumeHelm(ctx, cmd.GlobalFlags, args[0], cmd.Log) diff --git a/cmd/vclusterctl/cmd/root.go b/cmd/vclusterctl/cmd/root.go index b04dfecb8c..ac6dfac764 100644 --- a/cmd/vclusterctl/cmd/root.go +++ b/cmd/vclusterctl/cmd/root.go @@ -11,6 +11,7 @@ import ( cmdpro "github.com/loft-sh/vcluster/cmd/vclusterctl/cmd/platform" cmdtelemetry "github.com/loft-sh/vcluster/cmd/vclusterctl/cmd/telemetry" "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/loft-sh/vcluster/pkg/telemetry" @@ -27,6 +28,14 @@ func NewRootCmd(log log.Logger) *cobra.Command { SilenceErrors: true, Short: "Welcome to vcluster!", PersistentPreRun: func(_ *cobra.Command, _ []string) { + if globalFlags.Config == "" { + var err error + globalFlags.Config, err = config.DefaultConfigFilePath() + if err != nil { + log.Fatalf("failed to get vcluster configuration file path: %w", err) + } + } + if globalFlags.Silent { log.SetLevel(logrus.FatalLevel) } else if globalFlags.Debug { @@ -49,9 +58,6 @@ func Execute() { panic(err) } - // start telemetry - telemetry.StartCLI() - // start command log := log.GetInstance() rootCmd, err := BuildRoot(log) @@ -60,6 +66,9 @@ func Execute() { log.Fatalf("error building root: %+v\n", err) } + // start telemetry + telemetry.StartCLI(globalFlags.Config) + // Execute command err = rootCmd.ExecuteContext(context.Background()) recordAndFlush(err) @@ -76,7 +85,7 @@ func Execute() { func BuildRoot(log log.Logger) (*cobra.Command, error) { rootCmd := NewRootCmd(log) persistentFlags := rootCmd.PersistentFlags() - globalFlags = flags.SetGlobalFlags(persistentFlags) + globalFlags = flags.SetGlobalFlags(persistentFlags, log) // Set version for --version flag rootCmd.Version = upgrade.GetVersion() @@ -93,9 +102,9 @@ func BuildRoot(log log.Logger) (*cobra.Command, error) { rootCmd.AddCommand(get.NewGetCmd(globalFlags)) rootCmd.AddCommand(use.NewUseCmd(globalFlags)) rootCmd.AddCommand(convert.NewConvertCmd(globalFlags)) - rootCmd.AddCommand(cmdtelemetry.NewTelemetryCmd()) + rootCmd.AddCommand(cmdtelemetry.NewTelemetryCmd(globalFlags)) rootCmd.AddCommand(versionCmd) - rootCmd.AddCommand(NewInfoCmd()) + rootCmd.AddCommand(NewInfoCmd(globalFlags)) // add pro commands proCmd, err := cmdpro.NewProCmd(globalFlags) @@ -143,6 +152,6 @@ func BuildRoot(log log.Logger) (*cobra.Command, error) { } func recordAndFlush(err error) { - telemetry.CollectorCLI.RecordCLI(platform.Self, err) + telemetry.CollectorCLI.RecordCLI(globalFlags.Config, platform.Self, err) telemetry.CollectorCLI.Flush() } diff --git a/cmd/vclusterctl/cmd/telemetry/disable.go b/cmd/vclusterctl/cmd/telemetry/disable.go index 8bb79fdf38..1466775856 100644 --- a/cmd/vclusterctl/cmd/telemetry/disable.go +++ b/cmd/vclusterctl/cmd/telemetry/disable.go @@ -2,17 +2,20 @@ package telemetry import ( "github.com/loft-sh/log" - "github.com/loft-sh/vcluster/pkg/util/cliconfig" + "github.com/loft-sh/vcluster/pkg/cli/config" + "github.com/loft-sh/vcluster/pkg/cli/flags" "github.com/spf13/cobra" ) type DisableCmd struct { + *flags.GlobalFlags log log.Logger } -func disable() *cobra.Command { +func disable(globalFlags *flags.GlobalFlags) *cobra.Command { cmd := &DisableCmd{ - log: log.GetInstance(), + GlobalFlags: globalFlags, + log: log.GetInstance(), } cobraCmd := &cobra.Command{ @@ -37,7 +40,7 @@ docs: https://www.vcluster.com/docs/advanced-topics/telemetry } func (cmd *DisableCmd) Run(*cobra.Command) error { - c := cliconfig.GetConfig(cmd.log) + c := config.Read(cmd.Config, cmd.log) c.TelemetryDisabled = true - return cliconfig.WriteConfig(c) + return config.Write(cmd.Config, c) } diff --git a/cmd/vclusterctl/cmd/telemetry/enable.go b/cmd/vclusterctl/cmd/telemetry/enable.go index 316760d50a..4d64d5f14f 100644 --- a/cmd/vclusterctl/cmd/telemetry/enable.go +++ b/cmd/vclusterctl/cmd/telemetry/enable.go @@ -2,17 +2,20 @@ package telemetry import ( "github.com/loft-sh/log" - "github.com/loft-sh/vcluster/pkg/util/cliconfig" + "github.com/loft-sh/vcluster/pkg/cli/config" + "github.com/loft-sh/vcluster/pkg/cli/flags" "github.com/spf13/cobra" ) type EnableCmd struct { + *flags.GlobalFlags log log.Logger } -func enable() *cobra.Command { +func enable(globalFlags *flags.GlobalFlags) *cobra.Command { cmd := &EnableCmd{ - log: log.GetInstance(), + GlobalFlags: globalFlags, + log: log.GetInstance(), } cobraCmd := &cobra.Command{ @@ -37,7 +40,7 @@ docs: https://www.vcluster.com/docs/advanced-topics/telemetry } func (cmd *EnableCmd) Run(*cobra.Command) error { - c := cliconfig.GetConfig(cmd.log) + c := config.Read(cmd.Config, cmd.log) c.TelemetryDisabled = false - return cliconfig.WriteConfig(c) + return config.Write(cmd.Config, c) } diff --git a/cmd/vclusterctl/cmd/telemetry/telemetry.go b/cmd/vclusterctl/cmd/telemetry/telemetry.go index 98350cefde..ac32320130 100644 --- a/cmd/vclusterctl/cmd/telemetry/telemetry.go +++ b/cmd/vclusterctl/cmd/telemetry/telemetry.go @@ -1,10 +1,11 @@ package telemetry import ( + "github.com/loft-sh/vcluster/pkg/cli/flags" "github.com/spf13/cobra" ) -func NewTelemetryCmd() *cobra.Command { +func NewTelemetryCmd(globalFlags *flags.GlobalFlags) *cobra.Command { telemetryCmd := &cobra.Command{ Use: "telemetry", Short: "Sets your vcluster telemetry preferences", @@ -23,7 +24,7 @@ docs: https://www.vcluster.com/docs/advanced-topics/telemetry //TODO: hide global flags on this command and all sub-commands, same for the top-level upgrade command - telemetryCmd.AddCommand(disable()) - telemetryCmd.AddCommand(enable()) + telemetryCmd.AddCommand(disable(globalFlags)) + telemetryCmd.AddCommand(enable(globalFlags)) return telemetryCmd } diff --git a/cmd/vclusterctl/cmd/ui.go b/cmd/vclusterctl/cmd/ui.go index 939daa22dd..797217839e 100644 --- a/cmd/vclusterctl/cmd/ui.go +++ b/cmd/vclusterctl/cmd/ui.go @@ -7,12 +7,11 @@ import ( loftctl "github.com/loft-sh/loftctl/v4/cmd/loftctl/cmd" "github.com/loft-sh/log" "github.com/loft-sh/vcluster/pkg/cli/flags" - "github.com/loft-sh/vcluster/pkg/platform" "github.com/spf13/cobra" ) func NewUICmd(globalFlags *flags.GlobalFlags) (*cobra.Command, error) { - loftctlGlobalFlags, err := platform.GlobalFlags(globalFlags) + loftctlGlobalFlags, err := flags.LoftctlGlobalFlags(globalFlags) if err != nil { return nil, fmt.Errorf("failed to parse pro flags: %w", err) } diff --git a/cmd/vclusterctl/cmd/use/manager.go b/cmd/vclusterctl/cmd/use/manager.go index 80bf72ed3a..dfab4d3712 100644 --- a/cmd/vclusterctl/cmd/use/manager.go +++ b/cmd/vclusterctl/cmd/use/manager.go @@ -5,7 +5,9 @@ import ( "fmt" "github.com/loft-sh/log" + "github.com/loft-sh/vcluster/pkg/cli/config" "github.com/loft-sh/vcluster/pkg/cli/flags" + "github.com/loft-sh/vcluster/pkg/manager" "github.com/loft-sh/vcluster/pkg/platform" "github.com/spf13/cobra" ) @@ -35,7 +37,7 @@ Either use helm or vCluster platform as the deployment method for managing virtu Long: description, Args: cobra.ExactArgs(1), RunE: func(cobraCmd *cobra.Command, args []string) error { - if args[0] != string(platform.ManagerHelm) && args[0] != string(platform.ManagerPlatform) { + if args[0] != string(manager.Helm) && args[0] != string(manager.Platform) { return fmt.Errorf("you can only use helm or platform to use") } @@ -46,29 +48,25 @@ Either use helm or vCluster platform as the deployment method for managing virtu return managerCmd } -func (cmd *ManagerCmd) Run(_ context.Context, args []string) error { - return SwitchManager(args[0], cmd.Log) +func (cmd *ManagerCmd) Run(ctx context.Context, args []string) error { + return SwitchManager(ctx, cmd.Config, args[0], cmd.Log) } -func SwitchManager(manager string, log log.Logger) error { - if manager == string(platform.ManagerPlatform) { - _, err := platform.CreatePlatformClient() +func SwitchManager(ctx context.Context, configPath, mngr string, log log.Logger) error { + c := config.Read(configPath, log) + mngrType := manager.Type(mngr) + + if mngrType == manager.Platform { + _, err := platform.CreatePlatformClient(ctx, c.Platform.Config) if err != nil { return fmt.Errorf("cannot switch to platform manager, because seems like you are not logged into a vCluster platform (%w)", err) } } - - managerFile, err := platform.LoadManagerFile() - if err != nil { - return err - } - - managerFile.Manager = platform.ManagerType(manager) - err = platform.SaveManagerFile(managerFile) - if err != nil { + c.Manager.Type = mngrType + if err := config.Write(configPath, c); err != nil { return err } - log.Donef("Successfully switched manager to %s", manager) + log.Donef("Successfully switched manager to %s", mngr) return nil } diff --git a/pkg/cli/activate_helm.go b/pkg/cli/activate_helm.go index bd3977a996..20f497b44d 100644 --- a/pkg/cli/activate_helm.go +++ b/pkg/cli/activate_helm.go @@ -20,12 +20,11 @@ type ActivateOptions struct { ImportName string } -func ActivateHelm(ctx context.Context, options *ActivateOptions, globalFlags *flags.GlobalFlags, vClusterName string, log log.Logger) error { - platformClient, err := platform.CreatePlatformClient() +func ActivateHelm(ctx context.Context, options *ActivateOptions, platformConfig *platform.Config, globalFlags *flags.GlobalFlags, vClusterName string, log log.Logger) error { + platformClient, err := platform.CreatePlatformClient(ctx, *platformConfig) if err != nil { return err } - // check if vCluster exists vCluster, err := find.GetVCluster(ctx, globalFlags.Context, vClusterName, globalFlags.Namespace, log) if err != nil { @@ -43,7 +42,7 @@ func ActivateHelm(ctx context.Context, options *ActivateOptions, globalFlags *fl } // apply platform secret - err = platformClient.ApplyPlatformSecret(ctx, kubeClient, options.ImportName, vCluster.Namespace, options.Project) + err = platformClient.ApplyPlatformSecret(ctx, platformConfig, kubeClient, options.ImportName, vCluster.Namespace, options.Project) if err != nil { return err } diff --git a/pkg/cli/activate_platform.go b/pkg/cli/activate_platform.go index 70102f5361..684fa54c01 100644 --- a/pkg/cli/activate_platform.go +++ b/pkg/cli/activate_platform.go @@ -21,12 +21,8 @@ import ( "k8s.io/client-go/tools/clientcmd" ) -func ActivatePlatform(ctx context.Context, options *ActivateOptions, globalFlags *flags.GlobalFlags, vClusterName string, log log.Logger) error { - platformClient, err := platform.CreatePlatformClient() - if err != nil { - return err - } - +func ActivatePlatform(ctx context.Context, options *ActivateOptions, platformClient platform.Client, globalFlags *flags.GlobalFlags, vClusterName string, log log.Logger) error { + var err error if options.Project == "" { options.Project, err = getProjectName(ctx, platformClient) if err != nil { diff --git a/pkg/cli/config/config.go b/pkg/cli/config/config.go new file mode 100644 index 0000000000..d79407f983 --- /dev/null +++ b/pkg/cli/config/config.go @@ -0,0 +1,140 @@ +package config + +import ( + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "sync" + + "github.com/loft-sh/log" + "github.com/loft-sh/vcluster/pkg/manager" + "github.com/loft-sh/vcluster/pkg/platform" + homedir "github.com/mitchellh/go-homedir" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + DirName = ".vcluster" + FileName = "config.json" + ManagerFileName = "manager.json" +) + +var ( + singleConfig *Config + singleConfigOnce sync.Once + singleConfigMutex sync.RWMutex +) + +// New creates a new default config +func New() *Config { + return &Config{ + TelemetryDisabled: false, + Platform: PlatformConfig{ + platform.Config{ + TypeMeta: metav1.TypeMeta{ + Kind: "Config", + APIVersion: "storage.loft.sh/v1", + }, + VirtualClusterAccessPointCertificates: make(map[string]platform.VirtualClusterCertificatesEntry), + }, + }, + Manager: ManagerConfig{ + Type: manager.Helm, + }, + } +} + +// Read returns the current config by trying to read it from the given config path. +// It returns a new default config if there have been any errors during the read. +func Read(path string, log log.Logger) *Config { + singleConfigMutex.RLock() + defer singleConfigMutex.RUnlock() + + singleConfigOnce.Do(func() { + if singleConfig == nil { + cfg, err := readOrNew(path) + if err != nil { + log.Debugf("Failed to load local configuration file: %v", err) + } + singleConfig = cfg + } + }) + + return singleConfig +} + +// Write updates the current in-memory config and writes its content to the provided path. +func Write(path string, c *Config) error { + singleConfigMutex.Lock() + defer singleConfigMutex.Unlock() + + singleConfig = c + + err := os.MkdirAll(filepath.Dir(path), 0755) + if err != nil { + return fmt.Errorf("failed to create directory for configuration file, following error occurred: %w", err) + } + + data, err := json.Marshal(c) + if err != nil { + return fmt.Errorf("failed to transform config into JSON format, following error occurred: %w", err) + } + + err = os.WriteFile(path, data, 0644) + if err != nil { + return fmt.Errorf("failed to write configuration file, following error occurred: %w", err) + } + + return nil +} + +func PrintManagerInfo(verb string, mngr manager.Type, log log.Logger) { + // only print this to stderr + log = log.ErrorStreamOnly() + + if mngr == manager.Helm { + log.Infof("Using vCluster manager 'helm' to %s your virtual clusters, which means the vCluster CLI is running helm commands directly", verb) + log.Info("If you prefer to use the vCluster platform API instead, use the flag '--manager platform' or run 'vcluster use manager platform' to change the default") + } else { + log.Infof("Using vCluster manager 'platform' to %s your virtual clusters, which means the CLI is using the vCluster platform API instead of helm", verb) + log.Info("If you prefer to use helm instead, use the flag '--manager helm' or run 'vcluster use manager helm' to change the default") + } +} + +func DefaultConfigFilePath() (string, error) { + home, err := homedir.Dir() + if err != nil { + return "", err + } + return filepath.Join(home, DirName, FileName), nil +} + +func readOrNew(path string) (*Config, error) { + file, err := os.Open(path) + if err != nil { + return New(), fmt.Errorf("failed to load vcluster configuration file from %s, falling back to default configuration, following error occurred: %w", path, err) + } + stat, err := file.Stat() + if err != nil { + return New(), fmt.Errorf("failed to load vcluster configuration file from %s, falling back to default configuration, following error occurred: %w", path, err) + } + if stat.IsDir() { + return New(), fmt.Errorf("failed to load vcluster configuration file %s, falling back to default configuration, this path is a directory", path) + } + defer file.Close() + + bytes, err := io.ReadAll(file) + if err != nil { + return New(), err + } + + c := &Config{} + err = json.Unmarshal(bytes, &c) + if err != nil { + return New(), fmt.Errorf("failed to unmarshall vcluster configuration from %s file, falling back to default configuration, following error occurred: %w", path, err) + } + + return c, nil +} diff --git a/pkg/cli/config/types.go b/pkg/cli/config/types.go new file mode 100644 index 0000000000..025890d7b9 --- /dev/null +++ b/pkg/cli/config/types.go @@ -0,0 +1,22 @@ +package config + +import ( + "github.com/loft-sh/vcluster/pkg/manager" + "github.com/loft-sh/vcluster/pkg/platform" +) + +type Config struct { + TelemetryDisabled bool `json:"telemetryDisabled,omitempty"` + Platform PlatformConfig `json:"platform,omitempty"` + Manager ManagerConfig `json:"manager,omitempty"` +} + +// PlatformConfig defines the platform client config structure +type PlatformConfig struct { + platform.Config `json:",inline"` +} + +type ManagerConfig struct { + // Type is the current manager type that is used, either helm or platform + Type manager.Type `json:"type,omitempty"` +} diff --git a/pkg/cli/connect_platform.go b/pkg/cli/connect_platform.go index 3c6454ddbc..fea9a8103b 100644 --- a/pkg/cli/connect_platform.go +++ b/pkg/cli/connect_platform.go @@ -23,12 +23,7 @@ type connectPlatform struct { log log.Logger } -func ConnectPlatform(ctx context.Context, options *ConnectOptions, globalFlags *flags.GlobalFlags, vClusterName string, command []string, log log.Logger) error { - platformClient, err := platform.CreatePlatformClient() - if err != nil { - return err - } - +func ConnectPlatform(ctx context.Context, options *ConnectOptions, platformClient platform.Client, globalFlags *flags.GlobalFlags, vClusterName string, command []string, log log.Logger) error { // retrieve the vcluster vCluster, err := find.GetPlatformVCluster(ctx, platformClient, vClusterName, options.Project, log) if err != nil { diff --git a/pkg/cli/create_helm.go b/pkg/cli/create_helm.go index a6bd3ca671..f678df4065 100644 --- a/pkg/cli/create_helm.go +++ b/pkg/cli/create_helm.go @@ -18,6 +18,7 @@ import ( "github.com/loft-sh/log/terminal" "github.com/loft-sh/vcluster/config" "github.com/loft-sh/vcluster/config/legacyconfig" + cliconfig "github.com/loft-sh/vcluster/pkg/cli/config" "github.com/loft-sh/vcluster/pkg/cli/find" "github.com/loft-sh/vcluster/pkg/cli/flags" "github.com/loft-sh/vcluster/pkg/cli/localkubernetes" @@ -28,7 +29,6 @@ import ( "github.com/loft-sh/vcluster/pkg/telemetry" "github.com/loft-sh/vcluster/pkg/upgrade" "github.com/loft-sh/vcluster/pkg/util" - "github.com/loft-sh/vcluster/pkg/util/cliconfig" "github.com/loft-sh/vcluster/pkg/util/clihelper" "github.com/loft-sh/vcluster/pkg/util/helmdownloader" "golang.org/x/mod/semver" @@ -93,7 +93,7 @@ type createHelm struct { localCluster bool } -func CreateHelm(ctx context.Context, options *CreateOptions, globalFlags *flags.GlobalFlags, vClusterName string, log log.Logger) error { +func CreateHelm(ctx context.Context, options *CreateOptions, platformConfig *platform.Config, globalFlags *flags.GlobalFlags, vClusterName string, log log.Logger) error { cmd := &createHelm{ GlobalFlags: globalFlags, CreateOptions: options, @@ -263,7 +263,7 @@ func CreateHelm(ctx context.Context, options *CreateOptions, globalFlags *flags. // create platform secret if cmd.Activate { - err = cmd.activateVCluster(ctx, vClusterConfig) + err = cmd.activateVCluster(ctx, platformConfig, vClusterConfig) if err != nil { return err } @@ -323,22 +323,22 @@ func (cmd *createHelm) parseVClusterYAML(chartValues string) (*config.Config, er return vClusterConfig, nil } -func (cmd *createHelm) activateVCluster(ctx context.Context, vClusterConfig *config.Config) error { +func (cmd *createHelm) activateVCluster(ctx context.Context, platformConfig *platform.Config, vClusterConfig *config.Config) error { if vClusterConfig.Platform.API.AccessKey != "" || vClusterConfig.Platform.API.SecretRef.Name != "" { return nil } - platformClient, err := platform.CreatePlatformClient() + platformClient, err := platform.CreatePlatformClient(ctx, *platformConfig) if err != nil { if vClusterConfig.IsProFeatureEnabled() { - return fmt.Errorf("you have vCluster pro features activated, but seems like you are not logged in (%w). Please make sure to log into vCluster Platform to use vCluster pro features or run this command with --activate=false", err) + return fmt.Errorf("using vCluster Pro features requires the CLI to be logged into a vCluster Platform. If your CLI for some reason can't login but you already have a Platform set up, add the flag --activate=false to bypass this check: %w", err) } cmd.log.Debugf("create platform client: %v", err) return nil } - err = platformClient.ApplyPlatformSecret(ctx, cmd.kubeClient, "", cmd.Namespace, cmd.Project) + err = platformClient.ApplyPlatformSecret(ctx, platformConfig, cmd.kubeClient, "", cmd.Namespace, cmd.Project) if err != nil { return fmt.Errorf("apply platform secret: %w", err) } @@ -492,9 +492,9 @@ func (cmd *createHelm) ToChartOptions(kubernetesVersion *version.Info, log log.L Major: kubernetesVersion.Major, Minor: kubernetesVersion.Minor, }, - DisableTelemetry: cliconfig.GetConfig(log).TelemetryDisabled, + DisableTelemetry: cliconfig.Read(cmd.Config, log).TelemetryDisabled, InstanceCreatorType: "vclusterctl", - MachineID: telemetry.GetMachineID(log), + MachineID: telemetry.GetMachineID(cmd.Config, log), }, nil } diff --git a/pkg/cli/create_platform.go b/pkg/cli/create_platform.go index 5b484083ac..a160abb027 100644 --- a/pkg/cli/create_platform.go +++ b/pkg/cli/create_platform.go @@ -15,11 +15,12 @@ import ( storagev1 "github.com/loft-sh/api/v4/pkg/apis/storage/v1" "github.com/loft-sh/loftctl/v4/cmd/loftctl/cmd/create" "github.com/loft-sh/loftctl/v4/pkg/client/helper" - "github.com/loft-sh/loftctl/v4/pkg/client/naming" "github.com/loft-sh/loftctl/v4/pkg/config" + "github.com/loft-sh/loftctl/v4/pkg/projectutil" "github.com/loft-sh/loftctl/v4/pkg/vcluster" "github.com/loft-sh/log" vclusterconfig "github.com/loft-sh/vcluster/config" + cliconfig "github.com/loft-sh/vcluster/pkg/cli/config" "github.com/loft-sh/vcluster/pkg/cli/flags" "github.com/loft-sh/vcluster/pkg/constants" "github.com/loft-sh/vcluster/pkg/platform" @@ -27,7 +28,6 @@ import ( "github.com/loft-sh/vcluster/pkg/telemetry" "github.com/loft-sh/vcluster/pkg/upgrade" "github.com/loft-sh/vcluster/pkg/util" - "github.com/loft-sh/vcluster/pkg/util/cliconfig" "golang.org/x/mod/semver" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -35,19 +35,15 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -func CreatePlatform(ctx context.Context, options *CreateOptions, globalFlags *flags.GlobalFlags, virtualClusterName string, log log.Logger) error { - platformClient, err := platform.CreatePlatformClient() - if err != nil { - return err - } - +func CreatePlatform(ctx context.Context, options *CreateOptions, platformClient platform.Client, globalFlags *flags.GlobalFlags, virtualClusterName string, log log.Logger) error { // determine project & cluster name + var err error options.Cluster, options.Project, err = helper.SelectProjectOrCluster(ctx, platformClient, options.Cluster, options.Project, false, log) if err != nil { return err } - virtualClusterNamespace := naming.ProjectNamespace(options.Project) + virtualClusterNamespace := projectutil.ProjectNamespace((options.Project)) managementClient, err := platformClient.Management() if err != nil { return err @@ -112,13 +108,13 @@ func CreatePlatform(ctx context.Context, options *CreateOptions, globalFlags *fl } else { if virtualClusterInstance == nil { // create without template - virtualClusterInstance, err = createWithoutTemplate(ctx, platformClient, options, virtualClusterName, globalFlags.Namespace, log) + virtualClusterInstance, err = createWithoutTemplate(ctx, globalFlags.Config, platformClient, options, virtualClusterName, globalFlags.Namespace, log) if err != nil { return err } } else { // upgrade via template - virtualClusterInstance, err = upgradeWithoutTemplate(ctx, platformClient, options, virtualClusterInstance, log) + virtualClusterInstance, err = upgradeWithoutTemplate(ctx, globalFlags.Config, platformClient, options, virtualClusterInstance, log) if err != nil { return err } @@ -139,21 +135,21 @@ func CreatePlatform(ctx context.Context, options *CreateOptions, globalFlags *fl KubeConfigContextName: options.KubeConfigContextName, KubeConfig: "./kubeconfig.yaml", Project: options.Project, - }, globalFlags, virtualClusterName, nil, log) + }, platformClient, globalFlags, virtualClusterName, nil, log) } log.Donef("Successfully created virtual cluster %s in project %s. \n- Use 'vcluster connect %s --project %s' to access the virtual cluster\n- Use `vcluster connect %s --project %s -- kubectl get ns` to run a command directly within the vcluster", virtualClusterName, options.Project, virtualClusterName, options.Project, virtualClusterName, options.Project) return nil } -func createWithoutTemplate(ctx context.Context, platformClient platform.Client, options *CreateOptions, virtualClusterName, targetNamespace string, log log.Logger) (*managementv1.VirtualClusterInstance, error) { +func createWithoutTemplate(ctx context.Context, configPath string, platformClient platform.Client, options *CreateOptions, virtualClusterName, targetNamespace string, log log.Logger) (*managementv1.VirtualClusterInstance, error) { err := validateNoTemplateOptions(options) if err != nil { return nil, err } // merge values - helmValues, err := mergeValues(platformClient, options, log) + helmValues, err := mergeValues(configPath, platformClient, options, log) if err != nil { return nil, err } @@ -162,7 +158,7 @@ func createWithoutTemplate(ctx context.Context, platformClient platform.Client, zone, offset := time.Now().Zone() virtualClusterInstance := &managementv1.VirtualClusterInstance{ ObjectMeta: metav1.ObjectMeta{ - Namespace: naming.ProjectNamespace(options.Project), + Namespace: projectutil.ProjectNamespace((options.Project)), Name: virtualClusterName, Annotations: map[string]string{ clusterv1.SleepModeTimezoneAnnotation: zone + "#" + strconv.Itoa(offset), @@ -227,14 +223,14 @@ func createWithoutTemplate(ctx context.Context, platformClient platform.Client, return virtualClusterInstance, nil } -func upgradeWithoutTemplate(ctx context.Context, platformClient platform.Client, options *CreateOptions, virtualClusterInstance *managementv1.VirtualClusterInstance, log log.Logger) (*managementv1.VirtualClusterInstance, error) { +func upgradeWithoutTemplate(ctx context.Context, configPath string, platformClient platform.Client, options *CreateOptions, virtualClusterInstance *managementv1.VirtualClusterInstance, log log.Logger) (*managementv1.VirtualClusterInstance, error) { err := validateNoTemplateOptions(options) if err != nil { return nil, err } // merge values - helmValues, err := mergeValues(platformClient, options, log) + helmValues, err := mergeValues(configPath, platformClient, options, log) if err != nil { return nil, err } @@ -357,7 +353,7 @@ func createWithTemplate(ctx context.Context, platformClient platform.Client, opt zone, offset := time.Now().Zone() virtualClusterInstance := &managementv1.VirtualClusterInstance{ ObjectMeta: metav1.ObjectMeta{ - Namespace: naming.ProjectNamespace(options.Project), + Namespace: projectutil.ProjectNamespace((options.Project)), Name: virtualClusterName, Annotations: map[string]string{ clusterv1.SleepModeTimezoneAnnotation: zone + "#" + strconv.Itoa(offset), @@ -526,9 +522,9 @@ func validateTemplateOptions(options *CreateOptions) error { return nil } -func mergeValues(platformClient platform.Client, options *CreateOptions, log log.Logger) (string, error) { +func mergeValues(configPath string, platformClient platform.Client, options *CreateOptions, log log.Logger) (string, error) { // merge values - chartOptions, err := toChartOptions(platformClient, options, log) + chartOptions, err := toChartOptions(configPath, platformClient, options, log) if err != nil { return "", err } @@ -589,7 +585,7 @@ func parseString(str string) (map[string]interface{}, error) { return out, nil } -func toChartOptions(platformClient platform.Client, options *CreateOptions, log log.Logger) (*vclusterconfig.ExtraValuesOptions, error) { +func toChartOptions(configPath string, platformClient platform.Client, options *CreateOptions, log log.Logger) (*vclusterconfig.ExtraValuesOptions, error) { if !util.Contains(options.Distro, AllowedDistros) { return nil, fmt.Errorf("unsupported distro %s, please select one of: %s", options.Distro, strings.Join(AllowedDistros, ", ")) } @@ -627,10 +623,10 @@ func toChartOptions(platformClient platform.Client, options *CreateOptions, log Distro: options.Distro, Expose: options.Expose, KubernetesVersion: kubernetesVersion, - DisableTelemetry: cliconfig.GetConfig(log).TelemetryDisabled, + DisableTelemetry: cliconfig.Read(configPath, log).TelemetryDisabled, InstanceCreatorType: "vclusterctl", - PlatformInstanceID: telemetry.GetPlatformInstanceID(platformClient.Self()), - PlatformUserID: telemetry.GetPlatformUserID(platformClient.Self()), - MachineID: telemetry.GetMachineID(log), + PlatformInstanceID: telemetry.GetPlatformInstanceID(configPath, platformClient.Self()), + PlatformUserID: telemetry.GetPlatformUserID(configPath, platformClient.Self()), + MachineID: telemetry.GetMachineID(configPath, log), }, nil } diff --git a/pkg/cli/delete_helm.go b/pkg/cli/delete_helm.go index 08eefc5f08..bcc2ecbf11 100644 --- a/pkg/cli/delete_helm.go +++ b/pkg/cli/delete_helm.go @@ -51,7 +51,7 @@ type deleteHelm struct { log log.Logger } -func DeleteHelm(ctx context.Context, options *DeleteOptions, globalFlags *flags.GlobalFlags, vClusterName string, log log.Logger) error { +func DeleteHelm(ctx context.Context, options *DeleteOptions, platformConfig platform.Config, globalFlags *flags.GlobalFlags, vClusterName string, log log.Logger) error { cmd := deleteHelm{ GlobalFlags: globalFlags, DeleteOptions: options, @@ -121,7 +121,7 @@ func DeleteHelm(ctx context.Context, options *DeleteOptions, globalFlags *flags. // try to delete the vCluster in the platform if vClusterService != nil { - err = cmd.deleteVClusterInPlatform(ctx, vClusterService) + err = cmd.deleteVClusterInPlatform(ctx, platformConfig, vClusterService) if err != nil { return err } @@ -228,13 +228,12 @@ func DeleteHelm(ctx context.Context, options *DeleteOptions, globalFlags *flags. return nil } -func (cmd *deleteHelm) deleteVClusterInPlatform(ctx context.Context, vClusterService *corev1.Service) error { - platformClient, err := platform.CreatePlatformClient() +func (cmd *deleteHelm) deleteVClusterInPlatform(ctx context.Context, platformConfig platform.Config, vClusterService *corev1.Service) error { + platformClient, err := platform.CreatePlatformClient(ctx, platformConfig) if err != nil { cmd.log.Debugf("Error creating platform client: %v", err) return nil } - managementClient, err := platformClient.Management() if err != nil { cmd.log.Debugf("Error creating management client: %v", err) diff --git a/pkg/cli/delete_platform.go b/pkg/cli/delete_platform.go index a52dd04777..802b66bcff 100644 --- a/pkg/cli/delete_platform.go +++ b/pkg/cli/delete_platform.go @@ -13,12 +13,7 @@ import ( "k8s.io/client-go/tools/clientcmd" ) -func DeletePlatform(ctx context.Context, options *DeleteOptions, vClusterName string, log log.Logger) error { - platformClient, err := platform.CreatePlatformClient() - if err != nil { - return err - } - +func DeletePlatform(ctx context.Context, options *DeleteOptions, platformClient platform.Client, vClusterName string, log log.Logger) error { // retrieve the vcluster vCluster, err := find.GetPlatformVCluster(ctx, platformClient, vClusterName, options.Project, log) if err != nil { diff --git a/pkg/cli/flags/flags.go b/pkg/cli/flags/flags.go index 0c218f8d66..8adde6ff39 100644 --- a/pkg/cli/flags/flags.go +++ b/pkg/cli/flags/flags.go @@ -1,6 +1,12 @@ package flags import ( + "fmt" + + loftctlflags "github.com/loft-sh/loftctl/v4/cmd/loftctl/flags" + "github.com/loft-sh/log" + "github.com/loft-sh/vcluster/pkg/cli/config" + flag "github.com/spf13/pflag" ) @@ -15,10 +21,16 @@ type GlobalFlags struct { } // SetGlobalFlags applies the global flags -func SetGlobalFlags(flags *flag.FlagSet) *GlobalFlags { +func SetGlobalFlags(flags *flag.FlagSet, log log.Logger) *GlobalFlags { globalFlags := &GlobalFlags{} + defaultConfigPath, err := config.DefaultConfigFilePath() + if err != nil { + log.Fatalf("failed to get vcluster configuration file path: %w", err) + } + flags.BoolVar(&globalFlags.Debug, "debug", false, "Prints the stack trace if an error occurs") + flags.StringVar(&globalFlags.Config, "config", defaultConfigPath, "The vcluster CLI config to use (will be created if it does not exist)") flags.StringVar(&globalFlags.Context, "context", "", "The kubernetes config context to use") flags.StringVarP(&globalFlags.Namespace, "namespace", "n", "", "The kubernetes namespace to use") flags.BoolVarP(&globalFlags.Silent, "silent", "s", false, "Run in silent mode and prevents any vcluster log output except panics & fatals") @@ -26,3 +38,24 @@ func SetGlobalFlags(flags *flag.FlagSet) *GlobalFlags { return globalFlags } + +// LoftctlGlobalFlags converts vcluster global flags to vcluster pro global flags +func LoftctlGlobalFlags(globalFlags *GlobalFlags) (*loftctlflags.GlobalFlags, error) { + loftctlGlobalFlags := &loftctlflags.GlobalFlags{ + Silent: globalFlags.Silent, + Debug: globalFlags.Debug, + LogOutput: globalFlags.LogOutput, + } + + if globalFlags.Config != "" { + loftctlGlobalFlags.Config = globalFlags.Config + } else { + var err error + loftctlGlobalFlags.Config, err = config.DefaultConfigFilePath() + if err != nil { + return nil, fmt.Errorf("failed to get vcluster pro configuration file path: %w", err) + } + } + + return loftctlGlobalFlags, nil +} diff --git a/pkg/cli/list_helm.go b/pkg/cli/list_helm.go index 975d29b510..83470266dd 100644 --- a/pkg/cli/list_helm.go +++ b/pkg/cli/list_helm.go @@ -34,7 +34,7 @@ type ListOptions struct { Output string } -func ListHelm(ctx context.Context, options *ListOptions, globalFlags *flags.GlobalFlags, log log.Logger) error { +func ListHelm(ctx context.Context, options *ListOptions, platformConfig platform.Config, globalFlags *flags.GlobalFlags, log log.Logger) error { rawConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{}).RawConfig() if err != nil { return err @@ -55,7 +55,7 @@ func ListHelm(ctx context.Context, options *ListOptions, globalFlags *flags.Glob return err } - err = printVClusters(ctx, options, ossToVClusters(vClusters, currentContext), globalFlags, true, log) + err = printVClusters(ctx, options, platformConfig, ossToVClusters(vClusters, currentContext), globalFlags, true, log) if err != nil { return err } @@ -63,7 +63,7 @@ func ListHelm(ctx context.Context, options *ListOptions, globalFlags *flags.Glob return nil } -func printVClusters(ctx context.Context, options *ListOptions, output []ListVCluster, globalFlags *flags.GlobalFlags, showPlatform bool, logger log.Logger) error { +func printVClusters(ctx context.Context, options *ListOptions, platformConfig platform.Config, output []ListVCluster, globalFlags *flags.GlobalFlags, showPlatform bool, logger log.Logger) error { if options.Output == "json" { bytes, err := json.MarshalIndent(output, "", " ") if err != nil { @@ -78,7 +78,7 @@ func printVClusters(ctx context.Context, options *ListOptions, output []ListVClu // show use manager command if showPlatform { - platformClient, err := platform.CreatePlatformClient() + platformClient, err := platform.CreatePlatformClient(ctx, platformConfig) if err == nil { ctx, cancel := context.WithTimeout(ctx, 2*time.Second) defer cancel() diff --git a/pkg/cli/list_platform.go b/pkg/cli/list_platform.go index b5b5ce7560..c45b9f4f73 100644 --- a/pkg/cli/list_platform.go +++ b/pkg/cli/list_platform.go @@ -11,7 +11,7 @@ import ( "k8s.io/client-go/tools/clientcmd" ) -func ListPlatform(ctx context.Context, options *ListOptions, globalFlags *flags.GlobalFlags, logger log.Logger) error { +func ListPlatform(ctx context.Context, options *ListOptions, platformConfig platform.Config, globalFlags *flags.GlobalFlags, logger log.Logger) error { rawConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{}).RawConfig() if err != nil { return err @@ -22,7 +22,7 @@ func ListPlatform(ctx context.Context, options *ListOptions, globalFlags *flags. globalFlags.Context = currentContext } - platformClient, err := platform.CreatePlatformClient() + platformClient, err := platform.CreatePlatformClient(ctx, platformConfig) if err != nil { return err } @@ -32,7 +32,7 @@ func ListPlatform(ctx context.Context, options *ListOptions, globalFlags *flags. return err } - err = printVClusters(ctx, options, proToVClusters(proVClusters, currentContext), globalFlags, false, logger) + err = printVClusters(ctx, options, platformConfig, proToVClusters(proVClusters, currentContext), globalFlags, false, logger) if err != nil { return err } diff --git a/pkg/cli/pause_platform.go b/pkg/cli/pause_platform.go index e356c6b43a..4475ee3d5c 100644 --- a/pkg/cli/pause_platform.go +++ b/pkg/cli/pause_platform.go @@ -16,12 +16,7 @@ import ( "k8s.io/apimachinery/pkg/util/wait" ) -func PausePlatform(ctx context.Context, options *PauseOptions, vClusterName string, log log.Logger) error { - platformClient, err := platform.CreatePlatformClient() - if err != nil { - return err - } - +func PausePlatform(ctx context.Context, options *PauseOptions, platformClient platform.Client, vClusterName string, log log.Logger) error { vCluster, err := find.GetPlatformVCluster(ctx, platformClient, vClusterName, options.Project, log) if err != nil { return err diff --git a/pkg/cli/resume_platform.go b/pkg/cli/resume_platform.go index a3852d9b84..b88351c8c5 100644 --- a/pkg/cli/resume_platform.go +++ b/pkg/cli/resume_platform.go @@ -10,12 +10,7 @@ import ( "github.com/loft-sh/vcluster/pkg/platform" ) -func ResumePlatform(ctx context.Context, options *ResumeOptions, vClusterName string, log log.Logger) error { - platformClient, err := platform.CreatePlatformClient() - if err != nil { - return err - } - +func ResumePlatform(ctx context.Context, options *ResumeOptions, platformClient platform.Client, vClusterName string, log log.Logger) error { vCluster, err := find.GetPlatformVCluster(ctx, platformClient, vClusterName, options.Project, log) if err != nil { return err diff --git a/pkg/manager/types.go b/pkg/manager/types.go new file mode 100644 index 0000000000..97bd24a0c0 --- /dev/null +++ b/pkg/manager/types.go @@ -0,0 +1,8 @@ +package manager + +const ( + Helm Type = "helm" + Platform Type = "platform" +) + +type Type string diff --git a/pkg/platform/client.go b/pkg/platform/client.go index b690652be5..9d656639b0 100644 --- a/pkg/platform/client.go +++ b/pkg/platform/client.go @@ -4,15 +4,14 @@ import ( "context" "errors" "fmt" - "os" "strings" "sync" managementv1 "github.com/loft-sh/api/v4/pkg/apis/management/v1" "github.com/loft-sh/api/v4/pkg/client/clientset_generated/clientset/scheme" loftclient "github.com/loft-sh/loftctl/v4/pkg/client" - "github.com/loft-sh/loftctl/v4/pkg/client/naming" "github.com/loft-sh/loftctl/v4/pkg/kube" + "github.com/loft-sh/loftctl/v4/pkg/projectutil" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -26,7 +25,7 @@ type Client interface { loftclient.Client Self() *managementv1.Self - ApplyPlatformSecret(ctx context.Context, kubeClient kubernetes.Interface, name, namespace, project string) error + ApplyPlatformSecret(ctx context.Context, config *Config, kubeClient kubernetes.Interface, name, namespace, project string) error ListVClusters(ctx context.Context, virtualClusterName, projectName string) ([]VirtualClusterInstanceProject, error) } @@ -37,19 +36,31 @@ type VirtualClusterInstanceProject struct { var ErrConfigNotFound = errors.New("couldn't find vCluster platform config") -func CreatePlatformClient() (Client, error) { - configPath, err := ConfigFilePath() - if err != nil { - return nil, err +func CreatePlatformClient(ctx context.Context, c Config) (Client, error) { + // TODO: as long as we have the loftctctl dependency, translate the platform config to a loftclient config. + cfg := &loftclient.Config{ + TypeMeta: c.TypeMeta, + Host: c.Host, + LastInstallContext: c.LastInstallContext, + Insecure: c.Insecure, + AccessKey: c.AccessKey, + VirtualClusterAccessKey: c.VirtualClusterAccessKey, + DirectClusterEndpointToken: c.DirectClusterEndpointToken, + DirectClusterEndpointTokenRequested: c.DirectClusterEndpointTokenRequested, + VirtualClusterAccessPointCertificates: make(map[string]loftclient.VirtualClusterCertificatesEntry), } - _, err = os.Stat(configPath) - if err != nil { - return nil, fmt.Errorf("%w: please make sure to run 'vcluster login' to connect to an existing instance or 'vcluster platform start' to deploy a new instance", ErrConfigNotFound) + for k, v := range c.VirtualClusterAccessPointCertificates { + cfg.VirtualClusterAccessPointCertificates[k] = loftclient.VirtualClusterCertificatesEntry{ + CertificateData: v.CertificateData, + KeyData: v.KeyData, + LastRequested: v.LastRequested, + ExpirationTime: v.ExpirationTime, + } } - platformClient, err := loftclient.NewClientFromPath(configPath) - if err != nil { + platformClient := loftclient.NewClientFromConfig(cfg) + if err := platformClient.RefreshSelf(ctx); err != nil { return nil, err } @@ -148,7 +159,7 @@ func getProjectVirtualClusterInstance(ctx context.Context, managementClient kube err := managementClient.Loft().ManagementV1().RESTClient(). Get(). Resource("virtualclusterinstances"). - Namespace(naming.ProjectNamespace(project.Name)). + Namespace(projectutil.ProjectNamespace(project.Name)). Name(virtualClusterName). VersionedParams(&metav1.GetOptions{}, scheme.ParameterCodec). Param("extended", "true"). @@ -173,7 +184,7 @@ func getProjectVirtualClusterInstances(ctx context.Context, managementClient kub err := managementClient.Loft().ManagementV1().RESTClient(). Get(). Resource("virtualclusterinstances"). - Namespace(naming.ProjectNamespace(project.Name)). + Namespace(projectutil.ProjectNamespace(project.Name)). VersionedParams(&metav1.ListOptions{}, scheme.ParameterCodec). Param("extended", "true"). Do(ctx). diff --git a/pkg/platform/config.go b/pkg/platform/config.go deleted file mode 100644 index 27a665fb0d..0000000000 --- a/pkg/platform/config.go +++ /dev/null @@ -1,121 +0,0 @@ -package platform - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - - "github.com/loft-sh/log" - "github.com/loft-sh/vcluster/pkg/constants" - homedir "github.com/mitchellh/go-homedir" -) - -const ( - VClusterProFolder = "pro" -) - -// ConfigFilePath returns the path to the loft config file -func ConfigFilePath() (string, error) { - home, err := homedir.Dir() - if err != nil { - return "", fmt.Errorf("failed to open vCluster platform configuration file, unable to detect $HOME directory, falling back to default configuration, following error occurred: %w", err) - } - - return filepath.Join(home, constants.VClusterFolder, VClusterProFolder, constants.ConfigFileName), nil -} - -func managerFilePath() (string, error) { - home, err := homedir.Dir() - if err != nil { - return "", fmt.Errorf("failed to open vCluster platform manager file, unable to detect $HOME directory, falling back to default configuration, following error occurred: %w", err) - } - - return filepath.Join(home, constants.VClusterFolder, VClusterProFolder, constants.ManagerFileName), nil -} - -func PrintManagerInfo(verb string, manager ManagerType, log log.Logger) { - // only print this to stderr - log = log.ErrorStreamOnly() - - // check if there is a platform client or we skip the info message - _, err := CreatePlatformClient() - if err == nil { - if manager == ManagerHelm { - log.Infof("Using vCluster manager 'helm' to %s your virtual clusters, which means the vCluster CLI is running helm commands directly", verb) - log.Info("If you prefer to use the vCluster platform API instead, use the flag '--manager platform' or run 'vcluster use manager platform' to change the default") - } else { - log.Infof("Using vCluster manager 'platform' to %s your virtual clusters, which means the CLI is using the vCluster platform API instead of helm", verb) - log.Info("If you prefer to use helm instead, use the flag '--manager helm' or run 'vcluster use manager helm' to change the default") - } - } -} - -func GetManager(manager string) (ManagerType, error) { - if manager != "" { - if manager != string(ManagerPlatform) && manager != string(ManagerHelm) { - return "", fmt.Errorf("unknown manager: %s, please choose either helm or platform", manager) - } - - return ManagerType(manager), nil - } - - managerFile, err := LoadManagerFile() - if err != nil { - return "", err - } else if managerFile.Manager == "" { - return ManagerHelm, nil - } - - return managerFile.Manager, nil -} - -func LoadManagerFile() (*ManagerConfig, error) { - managerFile, err := managerFilePath() - if err != nil { - return nil, fmt.Errorf("get manager file path: %w", err) - } - - _, err = os.Stat(managerFile) - if err != nil { - // couldn't find manager file, so just return an empty manager config - return &ManagerConfig{}, nil - } - - rawBytes, err := os.ReadFile(managerFile) - if err != nil { - return nil, fmt.Errorf("error reading vCluster platform manager file: %w", err) - } - - managerConfig := &ManagerConfig{} - err = json.Unmarshal(rawBytes, managerConfig) - if err != nil { - return nil, fmt.Errorf("error parsing vCluster platform manager file: %w", err) - } - - return managerConfig, nil -} - -func SaveManagerFile(managerConfig *ManagerConfig) error { - managerFile, err := managerFilePath() - if err != nil { - return fmt.Errorf("get manager file path: %w", err) - } - - rawBytes, err := json.Marshal(managerConfig) - if err != nil { - return fmt.Errorf("marshal manager config: %w", err) - } - - err = os.MkdirAll(filepath.Dir(managerFile), 0755) - if err != nil { - return fmt.Errorf("create manager dir: %w", err) - } - - err = os.WriteFile(managerFile, rawBytes, 0644) - if err != nil { - return fmt.Errorf("error saving manager config: %w", err) - } - - return nil -} diff --git a/pkg/platform/flags.go b/pkg/platform/flags.go deleted file mode 100644 index 1fd5b990f2..0000000000 --- a/pkg/platform/flags.go +++ /dev/null @@ -1,29 +0,0 @@ -package platform - -import ( - "fmt" - - loftctlflags "github.com/loft-sh/loftctl/v4/cmd/loftctl/flags" - "github.com/loft-sh/vcluster/pkg/cli/flags" -) - -// GlobalFlags converts vcluster global flags to vcluster pro global flags -func GlobalFlags(globalFlags *flags.GlobalFlags) (*loftctlflags.GlobalFlags, error) { - loftctlGlobalFlags := &loftctlflags.GlobalFlags{ - Silent: globalFlags.Silent, - Debug: globalFlags.Debug, - LogOutput: globalFlags.LogOutput, - } - - if globalFlags.Config != "" { - loftctlGlobalFlags.Config = globalFlags.Config - } else { - var err error - loftctlGlobalFlags.Config, err = ConfigFilePath() - if err != nil { - return nil, fmt.Errorf("failed to get vcluster pro configuration file path: %w", err) - } - } - - return loftctlGlobalFlags, nil -} diff --git a/pkg/platform/secret.go b/pkg/platform/secret.go index 4fa339c2ce..95d6da0598 100644 --- a/pkg/platform/secret.go +++ b/pkg/platform/secret.go @@ -19,28 +19,28 @@ import ( const DefaultPlatformSecretName = "vcluster-platform-api-key" -func (c *client) ApplyPlatformSecret(ctx context.Context, kubeClient kubernetes.Interface, importName, namespace, project string) error { +func (c *client) ApplyPlatformSecret(ctx context.Context, config *Config, kubeClient kubernetes.Interface, importName, namespace, project string) error { managementClient, err := c.Management() if err != nil { return fmt.Errorf("create management client: %w", err) } // is the access key still valid? - if c.Config().VirtualClusterAccessKey != "" { + if config.VirtualClusterAccessKey != "" { selfCtx, cancel := context.WithTimeout(ctx, 1*time.Minute) self, err := managementClient.Loft().ManagementV1().Selves().Create(selfCtx, &managementv1.Self{ Spec: managementv1.SelfSpec{ - AccessKey: c.Config().VirtualClusterAccessKey, + AccessKey: config.VirtualClusterAccessKey, }, }, metav1.CreateOptions{}) cancel() if err != nil || self.Status.Subject != c.self.Status.Subject { - c.Config().VirtualClusterAccessKey = "" + config.VirtualClusterAccessKey = "" } } // check if we need to create a virtual cluster access key - if c.Config().VirtualClusterAccessKey == "" { + if config.VirtualClusterAccessKey == "" { user := "" team := "" if c.self.Status.User != nil { @@ -70,18 +70,14 @@ func (c *client) ApplyPlatformSecret(ctx context.Context, kubeClient kubernetes. return fmt.Errorf("create owned access key: %w", err) } - c.Config().VirtualClusterAccessKey = accessKey.Spec.Key - err = c.Save() - if err != nil { - return fmt.Errorf("save vCluster platform config: %w", err) - } + config.VirtualClusterAccessKey = accessKey.Spec.Key } // build secret payload payload := map[string][]byte{ - "accessKey": []byte(c.Config().VirtualClusterAccessKey), - "host": []byte(strings.TrimPrefix(c.Config().Host, "https://")), - "insecure": []byte(strconv.FormatBool(c.Config().Insecure)), + "accessKey": []byte(config.VirtualClusterAccessKey), + "host": []byte(strings.TrimPrefix(config.Host, "https://")), + "insecure": []byte(strconv.FormatBool(config.Insecure)), } if project != "" { payload["project"] = []byte(project) diff --git a/pkg/platform/types.go b/pkg/platform/types.go index 1996cf768c..d7865b6628 100644 --- a/pkg/platform/types.go +++ b/pkg/platform/types.go @@ -1,12 +1,74 @@ package platform -type ManagerType string +import ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) const ( ManagerHelm ManagerType = "helm" ManagerPlatform ManagerType = "platform" ) +// Config defines the client config structure +type Config struct { + metav1.TypeMeta `json:",inline"` + + // host is the http endpoint of how to access loft + // +optional + Host string `json:"host,omitempty"` + + // LastInstallContext is the last install context + // +optional + LastInstallContext string `json:"lastInstallContext,omitempty"` + + // insecure specifies if the loft instance is insecure + // +optional + Insecure bool `json:"insecure,omitempty"` + + // access key is the access key for the given loft host + // +optional + AccessKey string `json:"accesskey,omitempty"` + + // virtual cluster access key is the access key for the given loft host to create virtual clusters + // +optional + VirtualClusterAccessKey string `json:"virtualClusterAccessKey,omitempty"` + + // DEPRECATED: do not use anymore + // the direct cluster endpoint token + // +optional + DirectClusterEndpointToken string `json:"directClusterEndpointToken,omitempty"` + + // DEPRECATED: do not use anymore + // last time the direct cluster endpoint token was requested + // +optional + DirectClusterEndpointTokenRequested *metav1.Time `json:"directClusterEndpointTokenRequested,omitempty"` + + // map of cached certificates for "access point" mode virtual clusters + // +optional + VirtualClusterAccessPointCertificates map[string]VirtualClusterCertificatesEntry +} + +type VirtualClusterCertificatesEntry struct { + CertificateData string + KeyData string + LastRequested metav1.Time + ExpirationTime time.Time +} + +// NewConfig creates a new config +func NewConfig() *Config { + return &Config{ + TypeMeta: metav1.TypeMeta{ + Kind: "Config", + APIVersion: "storage.loft.sh/v1", + }, + } +} + +type ManagerType string + type ManagerConfig struct { // Manager is the current manager that is used, either helm or platform Manager ManagerType `json:"manager,omitempty"` diff --git a/pkg/telemetry/collect_cli.go b/pkg/telemetry/collect_cli.go index 42840d4f90..4ce208446b 100644 --- a/pkg/telemetry/collect_cli.go +++ b/pkg/telemetry/collect_cli.go @@ -9,8 +9,8 @@ import ( "github.com/loft-sh/analytics-client/client" managementv1 "github.com/loft-sh/api/v4/pkg/apis/management/v1" "github.com/loft-sh/log" + "github.com/loft-sh/vcluster/pkg/cli/config" "github.com/loft-sh/vcluster/pkg/upgrade" - "github.com/loft-sh/vcluster/pkg/util/cliconfig" "github.com/loft-sh/vcluster/pkg/util/loghelper" ) @@ -26,15 +26,15 @@ const ( var CollectorCLI CLICollector = &noopCollector{} type CLICollector interface { - RecordCLI(self *managementv1.Self, err error) + RecordCLI(configPath string, self *managementv1.Self, err error) // Flush makes sure all events are sent to the backend Flush() } // StartCLI starts collecting events and sending them to the backend from the CLI -func StartCLI() { - cliConfig := cliconfig.GetConfig(log.Discard) +func StartCLI(configPath string) { + cliConfig := config.Read(configPath, log.Discard) // if disabled, we return noop collector if cliConfig.TelemetryDisabled || upgrade.GetVersion() == upgrade.DevelopmentVersion { @@ -70,7 +70,7 @@ func (d *cliCollector) Flush() { d.analyticsClient.Flush() } -func (d *cliCollector) RecordCLI(self *managementv1.Self, err error) { +func (d *cliCollector) RecordCLI(configPath string, self *managementv1.Self, err error) { timezone, _ := time.Now().Zone() eventProperties := map[string]interface{}{ "command": os.Args, @@ -91,16 +91,16 @@ func (d *cliCollector) RecordCLI(self *managementv1.Self, err error) { d.analyticsClient.RecordEvent(client.Event{ "event": { "type": "vcluster_cli", - "platform_user_id": GetPlatformUserID(self), - "platform_instance_id": GetPlatformInstanceID(self), - "machine_id": GetMachineID(log.Discard), + "platform_user_id": GetPlatformUserID(configPath, self), + "platform_instance_id": GetPlatformInstanceID(configPath, self), + "machine_id": GetMachineID(configPath, log.Discard), "properties": string(eventPropertiesRaw), "timestamp": time.Now().Unix(), }, "user": { - "platform_user_id": GetPlatformUserID(self), - "platform_instance_id": GetPlatformInstanceID(self), - "machine_id": GetMachineID(log.Discard), + "platform_user_id": GetPlatformUserID(configPath, self), + "platform_instance_id": GetPlatformInstanceID(configPath, self), + "machine_id": GetMachineID(configPath, log.Discard), "properties": string(userPropertiesRaw), "timestamp": time.Now().Unix(), }, diff --git a/pkg/telemetry/helpers.go b/pkg/telemetry/helpers.go index d082648556..fa67e021ab 100644 --- a/pkg/telemetry/helpers.go +++ b/pkg/telemetry/helpers.go @@ -9,7 +9,7 @@ import ( "github.com/denisbrodbeck/machineid" managementv1 "github.com/loft-sh/api/v4/pkg/apis/management/v1" "github.com/loft-sh/log" - "github.com/loft-sh/vcluster/pkg/util/cliconfig" + "github.com/loft-sh/vcluster/pkg/cli/config" homedir "github.com/mitchellh/go-homedir" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/version" @@ -75,8 +75,8 @@ func toKubernetesVersion(vi *version.Info) *KubernetesVersion { } // GetPlatformUserID returns the loft instance id -func GetPlatformUserID(self *managementv1.Self) string { - if cliconfig.GetConfig(log.Discard).TelemetryDisabled || self == nil { +func GetPlatformUserID(configPath string, self *managementv1.Self) string { + if config.Read(configPath, log.Discard).TelemetryDisabled || self == nil { return "" } platformID := self.Status.Subject @@ -87,8 +87,8 @@ func GetPlatformUserID(self *managementv1.Self) string { } // GetPlatformInstanceID returns the loft instance id -func GetPlatformInstanceID(self *managementv1.Self) string { - if cliconfig.GetConfig(log.Discard).TelemetryDisabled || self == nil { +func GetPlatformInstanceID(configPath string, self *managementv1.Self) string { + if config.Read(configPath, log.Discard).TelemetryDisabled || self == nil { return "" } @@ -97,8 +97,8 @@ func GetPlatformInstanceID(self *managementv1.Self) string { // GetMachineID retrieves machine ID and encodes it together with users $HOME path and // extra key to protect privacy. Returns a hex-encoded string. -func GetMachineID(log log.Logger) string { - if cliconfig.GetConfig(log).TelemetryDisabled { +func GetMachineID(configPath string, log log.Logger) string { + if config.Read(configPath, log).TelemetryDisabled { return "" } diff --git a/pkg/telemetry/noop.go b/pkg/telemetry/noop.go index 207108345e..d99335e70a 100644 --- a/pkg/telemetry/noop.go +++ b/pkg/telemetry/noop.go @@ -19,4 +19,4 @@ func (n *noopCollector) Flush() {} func (n *noopCollector) SetVirtualClient(_ kubernetes.Interface) {} -func (n *noopCollector) RecordCLI(_ *managementv1.Self, _ error) {} +func (n *noopCollector) RecordCLI(_ string, _ *managementv1.Self, _ error) {} diff --git a/pkg/util/cliconfig/config.go b/pkg/util/cliconfig/config.go deleted file mode 100644 index 9e83c6fbd2..0000000000 --- a/pkg/util/cliconfig/config.go +++ /dev/null @@ -1,107 +0,0 @@ -package cliconfig - -import ( - "encoding/json" - "fmt" - "io" - "os" - "path/filepath" - "sync" - - "github.com/loft-sh/log" - "github.com/loft-sh/vcluster/pkg/constants" - homedir "github.com/mitchellh/go-homedir" -) - -var ( - singleConfig *CLIConfig - singleConfigOnce sync.Once -) - -type CLIConfig struct { - TelemetryDisabled bool `json:"telemetryDisabled,omitempty"` -} - -func getDefaultCLIConfig() *CLIConfig { - return &CLIConfig{ - TelemetryDisabled: false, - } -} - -func getConfigFilePath(home string) string { - return filepath.Join(home, constants.VClusterFolder, constants.ConfigFileName) -} - -func GetConfig(log log.Logger) *CLIConfig { - singleConfigOnce.Do(func() { - var err error - singleConfig, err = getConfig() - if err != nil && log != nil { - log.Debugf("Failed to load local configuration file: %v", err.Error()) - } - - // set default if nil - if singleConfig == nil { - singleConfig = getDefaultCLIConfig() - } - }) - - return singleConfig -} - -func getConfig() (*CLIConfig, error) { - home, err := homedir.Dir() - if err != nil { - return getDefaultCLIConfig(), fmt.Errorf("failed to open vcluster configuration file from, unable to detect $HOME directory, falling back to default configuration, following error occurred: %w", err) - } - - path := getConfigFilePath(home) - // check if the file exists - fi, err := os.Stat(path) - if err != nil { - if os.IsNotExist(err) { - return getDefaultCLIConfig(), nil - } - return getDefaultCLIConfig(), fmt.Errorf("failed to load vcluster configuration file from %s, falling back to default configuration, following error occurred: %w", path, err) - } - if fi.IsDir() { - return getDefaultCLIConfig(), fmt.Errorf("failed to load vcluster configuration file %s, falling back to default configuration, this path is a directory", path) - } - file, err := os.Open(path) - if err != nil { - return getDefaultCLIConfig(), fmt.Errorf("failed to open vcluster configuration file from %s, falling back to default configuration, following error occurred: %w", path, err) - } - defer file.Close() - bytes, _ := io.ReadAll(file) - c := &CLIConfig{} - err = json.Unmarshal(bytes, &c) - if err != nil { - return getDefaultCLIConfig(), fmt.Errorf("failed to unmarshall vcluster configuration from %s file, falling back to default configuration, following error occurred: %w", path, err) - } - return c, nil -} - -func WriteConfig(c *CLIConfig) error { - home, err := homedir.Dir() - if err != nil { - return fmt.Errorf("failed to write vcluster configuration file from, unable to detect $HOME directory, falling back to default configuration, following error occurred: %w", err) - } - path := getConfigFilePath(home) - - err = os.MkdirAll(filepath.Dir(path), 0755) - if err != nil { - return fmt.Errorf("failed to create directory for configuration file, following error occurred: %w", err) - } - - data, err := json.Marshal(c) - if err != nil { - return fmt.Errorf("failed to transform config into JSON format, following error occurred: %w", err) - } - - err = os.WriteFile(path, data, 0644) - if err != nil { - return fmt.Errorf("failed to write configuration file, following error occurred: %w", err) - } - - return nil -}