diff --git a/cmd/audit.go b/cmd/audit.go index 57cdc35..4c95e04 100644 --- a/cmd/audit.go +++ b/cmd/audit.go @@ -28,7 +28,6 @@ func newAuditCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.auditList() }, - PreRun: bindPFlags, } auditDescribeCmd := &cobra.Command{ Use: "describe ", @@ -36,7 +35,6 @@ func newAuditCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.auditDescribe(args) }, - PreRun: bindPFlags, } auditDescribeCmd.Flags().String("phase", "response", "phase of the audit trace. One of [request, response, single, error, opened, closed]") diff --git a/cmd/billing.go b/cmd/billing.go index 46c930d..da886f9 100644 --- a/cmd/billing.go +++ b/cmd/billing.go @@ -50,7 +50,6 @@ func newBillingCmd(c *config) *cobra.Command { } return c.projectsBilling() }, - PreRun: bindPFlags, } containerBillingCmd := &cobra.Command{ Use: "container", @@ -70,7 +69,6 @@ export CLOUDCTL_COSTS_MEMORY_GI_HOUR=0.01 # costs per memory hour } return c.containerUsage() }, - PreRun: bindPFlags, } clusterBillingCmd := &cobra.Command{ Use: "cluster", @@ -89,7 +87,6 @@ export CLOUDCTL_COSTS_HOUR=0.01 # costs per hour } return c.clusterUsage() }, - PreRun: bindPFlags, } ipBillingCmd := &cobra.Command{ Use: "ip", @@ -108,7 +105,6 @@ export CLOUDCTL_COSTS_HOUR=0.01 # costs per hour } return c.ipUsage() }, - PreRun: bindPFlags, } machineBillingCmd := &cobra.Command{ Use: "machine", @@ -127,7 +123,6 @@ export CLOUDCTL_COSTS_HOUR=0.01 # costs per hour } return c.machineUsage() }, - PreRun: bindPFlags, } productOptionBillingCmd := &cobra.Command{ Use: "product-option", @@ -146,7 +141,6 @@ export CLOUDCTL_COSTS_HOUR=0.01 # costs per hour } return c.productOptionUsage() }, - PreRun: bindPFlags, } networkTrafficBillingCmd := &cobra.Command{ Use: "network-traffic", @@ -167,7 +161,6 @@ export CLOUDCTL_COSTS_TOTAL_NETWORK_TRAFFIC_GI=0.01 # costs per gi } return c.networkTrafficUsage() }, - PreRun: bindPFlags, } s3BillingCmd := &cobra.Command{ Use: "s3", @@ -186,7 +179,6 @@ export CLOUDCTL_COSTS_STORAGE_GI_HOUR=0.01 # costs per storage hour } return c.s3Usage() }, - PreRun: bindPFlags, } volumeBillingCmd := &cobra.Command{ Use: "volume", @@ -205,7 +197,6 @@ export CLOUDCTL_COSTS_STORAGE_GI_HOUR=0.01 # costs per capacity hour } return c.volumeUsage() }, - PreRun: bindPFlags, } postgresBillingCmd := &cobra.Command{ Use: "postgres", @@ -226,7 +217,6 @@ export CLOUDCTL_COSTS_STORAGE_GI_HOUR=0.01 # Costs per capacity hour } return c.postgresUsage() }, - PreRun: bindPFlags, } billingCmd.AddCommand(projectBillingCmd) diff --git a/cmd/cluster.go b/cmd/cluster.go index 4ed4d43..07ba5e5 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -54,7 +54,6 @@ func newClusterCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.clusterCreate() }, - PreRun: bindPFlags, } clusterListCmd := &cobra.Command{ @@ -64,7 +63,6 @@ func newClusterCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.clusterList() }, - PreRun: bindPFlags, } clusterDeleteCmd := &cobra.Command{ Use: "delete ", @@ -74,7 +72,6 @@ func newClusterCmd(c *config) *cobra.Command { return c.clusterDelete(args) }, ValidArgsFunction: c.comp.ClusterListCompletion, - PreRun: bindPFlags, } clusterDescribeCmd := &cobra.Command{ Use: "describe ", @@ -83,7 +80,6 @@ func newClusterCmd(c *config) *cobra.Command { return c.clusterDescribe(args) }, ValidArgsFunction: c.comp.ClusterListCompletion, - PreRun: bindPFlags, } clusterKubeconfigCmd := &cobra.Command{ Use: "kubeconfig ", @@ -92,7 +88,6 @@ func newClusterCmd(c *config) *cobra.Command { return c.clusterKubeconfig(args) }, ValidArgsFunction: c.comp.ClusterListCompletion, - PreRun: bindPFlags, } clusterReconcileCmd := &cobra.Command{ @@ -102,7 +97,6 @@ func newClusterCmd(c *config) *cobra.Command { return c.reconcileCluster(args) }, ValidArgsFunction: c.comp.ClusterListCompletion, - PreRun: bindPFlags, } clusterUpdateCmd := &cobra.Command{ Use: "update ", @@ -111,7 +105,6 @@ func newClusterCmd(c *config) *cobra.Command { return c.updateCluster(args) }, ValidArgsFunction: c.comp.ClusterListCompletion, - PreRun: bindPFlags, } clusterInputsCmd := &cobra.Command{ Use: "inputs", @@ -119,7 +112,6 @@ func newClusterCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.clusterInputs() }, - PreRun: bindPFlags, } clusterMachineCmd := &cobra.Command{ Use: "machine", @@ -134,7 +126,6 @@ func newClusterCmd(c *config) *cobra.Command { return c.clusterMachines(args) }, ValidArgsFunction: c.comp.ClusterListCompletion, - PreRun: bindPFlags, } clusterIssuesCmd := &cobra.Command{ Use: "issues []", @@ -144,7 +135,6 @@ func newClusterCmd(c *config) *cobra.Command { return c.clusterIssues(args) }, ValidArgsFunction: c.comp.ClusterListCompletion, - PreRun: bindPFlags, } clusterMonitoringSecretCmd := &cobra.Command{ Use: "monitoring-secret ", @@ -153,7 +143,6 @@ func newClusterCmd(c *config) *cobra.Command { return c.clusterMonitoringSecret(args) }, ValidArgsFunction: c.comp.ClusterListCompletion, - PreRun: bindPFlags, } clusterMachineSSHCmd := &cobra.Command{ Use: "ssh ", @@ -162,7 +151,6 @@ func newClusterCmd(c *config) *cobra.Command { return c.clusterMachineSSH(args, false) }, ValidArgsFunction: c.comp.ClusterListCompletion, - PreRun: bindPFlags, } clusterMachineConsoleCmd := &cobra.Command{ Use: "console ", @@ -171,7 +159,6 @@ func newClusterCmd(c *config) *cobra.Command { return c.clusterMachineSSH(args, true) }, ValidArgsFunction: c.comp.ClusterListCompletion, - PreRun: bindPFlags, } clusterMachineResetCmd := &cobra.Command{ Use: "reset ", @@ -180,7 +167,6 @@ func newClusterCmd(c *config) *cobra.Command { return c.clusterMachineReset(args) }, ValidArgsFunction: c.comp.ClusterListCompletion, - PreRun: bindPFlags, } clusterMachineCycleCmd := &cobra.Command{ Use: "cycle ", @@ -189,7 +175,6 @@ func newClusterCmd(c *config) *cobra.Command { return c.clusterMachineCycle(args) }, ValidArgsFunction: c.comp.ClusterListCompletion, - PreRun: bindPFlags, } clusterMachineReinstallCmd := &cobra.Command{ Use: "reinstall ", @@ -198,7 +183,6 @@ func newClusterCmd(c *config) *cobra.Command { return c.clusterMachineReinstall(args) }, ValidArgsFunction: c.comp.ClusterListCompletion, - PreRun: bindPFlags, } clusterMachinePackagesCmd := &cobra.Command{ Use: "packages ", @@ -207,7 +191,6 @@ func newClusterCmd(c *config) *cobra.Command { return c.clusterMachinePackages(args) }, ValidArgsFunction: c.comp.ClusterListCompletion, - PreRun: bindPFlags, } clusterLogsCmd := &cobra.Command{ Use: "logs ", @@ -216,7 +199,6 @@ func newClusterCmd(c *config) *cobra.Command { return c.clusterLogs(args) }, ValidArgsFunction: c.comp.ClusterListCompletion, - PreRun: bindPFlags, } clusterDNSManifestCmd := &cobra.Command{ Use: "dns-manifest ", @@ -225,7 +207,6 @@ func newClusterCmd(c *config) *cobra.Command { return c.clusterDNSManifest(args) }, ValidArgsFunction: c.comp.ClusterListCompletion, - PreRun: bindPFlags, } clusterCreateCmd.Flags().String("name", "", "name of the cluster, max 10 characters. [required]") diff --git a/cmd/cluster_audit.go b/cmd/cluster_audit.go index ef7a5aa..16d5b4a 100644 --- a/cmd/cluster_audit.go +++ b/cmd/cluster_audit.go @@ -33,7 +33,6 @@ func newClusterAuditCmd(c *config) *cobra.Command { } return fmt.Errorf("no command specified") }, - PreRun: bindPFlags, } modeCmd := &cobra.Command{ Use: "mode --cluster-id=", @@ -51,7 +50,6 @@ func newClusterAuditCmd(c *config) *cobra.Command { "blocking\tBlock API server responses on processing each individual event.", "blocking-strict\tSame as blocking, but when there is a failure during audit logging at the RequestReceived stage, the whole request to the kube-apiserver fails. This is the default.", }, - PreRun: bindPFlags, } policyCmd := &cobra.Command{ Use: "policy --cluster-id=", @@ -60,7 +58,6 @@ func newClusterAuditCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return w.auditPolicy() }, - PreRun: bindPFlags, } splunkCmd := &cobra.Command{ Use: "splunk --cluster-id=", @@ -68,7 +65,6 @@ func newClusterAuditCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return w.splunk() }, - PreRun: bindPFlags, } clusterForwardingCmd := &cobra.Command{ Use: "cluster-forwarding --cluster-id=", @@ -77,7 +73,6 @@ func newClusterAuditCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return w.clusterForwarding() }, - PreRun: bindPFlags, } clusterAuditCmd.Flags().Bool("disabled", false, "disables the entire audit functionality, enable again with --disabled=false, requires --yes-i-really-mean-it flag") diff --git a/cmd/context.go b/cmd/context.go index a281e1a..674b2b2 100644 --- a/cmd/context.go +++ b/cmd/context.go @@ -42,7 +42,6 @@ contexts: } return nil }, - PreRun: bindPFlags, } contextShortCmd := &cobra.Command{ @@ -51,7 +50,6 @@ contexts: RunE: func(cmd *cobra.Command, args []string) error { return contextShort() }, - PreRun: bindPFlags, } contextCmd.AddCommand(contextShortCmd) diff --git a/cmd/dashboard.go b/cmd/dashboard.go index 2e5e1fd..98ffb6c 100644 --- a/cmd/dashboard.go +++ b/cmd/dashboard.go @@ -47,7 +47,6 @@ func newDashboardCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return runDashboard(c.cloud) }, - PreRun: bindPFlags, } tabs := dashboardTabs(nil, nil, nil) diff --git a/cmd/health.go b/cmd/health.go index b274c81..32cbfa4 100644 --- a/cmd/health.go +++ b/cmd/health.go @@ -32,7 +32,6 @@ func newHealthCmd(c *config) *cobra.Command { return output.New().Print(resp.Payload.Services) }, - PreRun: bindPFlags, } return healthCmd } diff --git a/cmd/helper/helper.go b/cmd/helper/helper.go index f0b1ca6..d90f991 100644 --- a/cmd/helper/helper.go +++ b/cmd/helper/helper.go @@ -8,6 +8,7 @@ import ( "math" "os" "os/exec" + "path/filepath" "strings" "time" @@ -184,3 +185,16 @@ func ClientNoAuth() runtime.ClientAuthInfoWriterFunc { noAuth := func(_ runtime.ClientRequest, _ strfmt.Registry) error { return nil } return runtime.ClientAuthInfoWriterFunc(noAuth) } + +func ExpandHomeDir(path string) (string, error) { + if !strings.HasPrefix(path, "~/") { + return path, nil + } + + homedir, err := os.UserHomeDir() + if err != nil { + return "", fmt.Errorf("unable to expand home dir: %w", err) + } + + return filepath.Join(homedir, strings.TrimLeft(path, "~/")), nil +} diff --git a/cmd/helper/helper_test.go b/cmd/helper/helper_test.go new file mode 100644 index 0000000..6ddc17f --- /dev/null +++ b/cmd/helper/helper_test.go @@ -0,0 +1,45 @@ +package helper + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestExpandHomeDir(t *testing.T) { + homedir, err := os.UserHomeDir() + require.NoError(t, err) + + tests := []struct { + name string + path string + want string + wantErr bool + }{ + { + name: "no home dir", + path: "/my/absolute/path", + want: "/my/absolute/path", + wantErr: false, + }, + { + name: "with home dir", + path: "~/file", + want: homedir + "/file", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ExpandHomeDir(tt.path) + if (err != nil) != tt.wantErr { + t.Errorf("ExpandHomeDir() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("ExpandHomeDir() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/cmd/ip.go b/cmd/ip.go index 5bcc45a..c1cfb7f 100644 --- a/cmd/ip.go +++ b/cmd/ip.go @@ -27,7 +27,6 @@ func newIPCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.ipList() }, - PreRun: bindPFlags, } ipStaticCmd := &cobra.Command{ Use: "static ", @@ -35,7 +34,6 @@ func newIPCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.ipStatic(args) }, - PreRun: bindPFlags, } ipAllocateCmd := &cobra.Command{ Use: "allocate ", @@ -43,7 +41,6 @@ func newIPCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.ipAllocate() }, - PreRun: bindPFlags, } ipFreeCmd := &cobra.Command{ Use: "delete ", @@ -52,7 +49,6 @@ func newIPCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.ipFree(args) }, - PreRun: bindPFlags, } ipCmd.AddCommand(ipListCmd) diff --git a/cmd/login.go b/cmd/login.go index 0057faf..03a8dff 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -104,7 +104,6 @@ func newLoginCmd(c *config) *cobra.Command { return nil }, - PreRun: bindPFlags, } loginCmd.Flags().Bool("print-only", false, "If true, the token is printed to stdout") return loginCmd diff --git a/cmd/logout.go b/cmd/logout.go index bf73413..5477da7 100644 --- a/cmd/logout.go +++ b/cmd/logout.go @@ -27,7 +27,6 @@ func newLogoutCmd(c *config) *cobra.Command { return nil }, - PreRun: bindPFlags, } return logoutCmd } diff --git a/cmd/postgres.go b/cmd/postgres.go index c7704ab..4ce3b20 100644 --- a/cmd/postgres.go +++ b/cmd/postgres.go @@ -73,7 +73,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresCreate() }, - PreRun: bindPFlags, } postgresCreateStandbyCmd := &cobra.Command{ Use: "create-standby", @@ -81,7 +80,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresCreateStandby() }, - PreRun: bindPFlags, } postgresPromoteToPrimaryCmd := &cobra.Command{ Use: "promote-to-primary", @@ -89,7 +87,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresPromoteToPrimary(args) }, - PreRun: bindPFlags, } postgresDemoteToStandbyCmd := &cobra.Command{ Use: "demote-to-standby", @@ -97,7 +94,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresDemoteToStandby(args) }, - PreRun: bindPFlags, } postgresRestoreCmd := &cobra.Command{ Use: "restore", @@ -105,7 +101,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresRestore() }, - PreRun: bindPFlags, } postgresApplyCmd := &cobra.Command{ Use: "apply", @@ -113,7 +108,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresApply() }, - PreRun: bindPFlags, } postgresEditCmd := &cobra.Command{ Use: "edit", @@ -121,7 +115,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresEdit(args) }, - PreRun: bindPFlags, } postgresUpdateCmd := &cobra.Command{ Use: "update", @@ -129,7 +122,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresUpdate(args) }, - PreRun: bindPFlags, } postgresAcceptRestoreCmd := &cobra.Command{ Use: "restore-accepted", @@ -137,7 +129,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresAcceptRestore(args) }, - PreRun: bindPFlags, } postgresListCmd := &cobra.Command{ Use: "list", @@ -146,7 +137,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresFind() }, - PreRun: bindPFlags, } postgresListBackupsCmd := &cobra.Command{ Use: "list-backups", @@ -154,7 +144,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresListBackups(args) }, - PreRun: bindPFlags, } postgresDeleteCmd := &cobra.Command{ Use: "delete ", @@ -163,7 +152,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresDelete(args) }, - PreRun: bindPFlags, } postgresDescribeCmd := &cobra.Command{ Use: "describe ", @@ -171,7 +159,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresDescribe(args) }, - PreRun: bindPFlags, } postgresConnectionStringCmd := &cobra.Command{ Use: "connectionstring ", @@ -179,7 +166,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresConnectionString(args) }, - PreRun: bindPFlags, } postgresVersionsCmd := &cobra.Command{ Use: "version", @@ -187,7 +173,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresVersions() }, - PreRun: bindPFlags, } postgresPartitionsCmd := &cobra.Command{ Use: "partition", @@ -195,7 +180,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresPartitions() }, - PreRun: bindPFlags, } postgresBackupCmd := &cobra.Command{ Use: "backup-config", @@ -208,7 +192,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresBackupCreate(false) }, - PreRun: bindPFlags, } postgresBackupAutoCreateCmd := &cobra.Command{ Use: "auto-create", @@ -216,7 +199,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresBackupCreate(true) }, - PreRun: bindPFlags, } postgresBackupUpdateCmd := &cobra.Command{ Use: "update", @@ -224,7 +206,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresBackupUpdate() }, - PreRun: bindPFlags, } postgresBackupListCmd := &cobra.Command{ Use: "list", @@ -233,7 +214,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresBackupList() }, - PreRun: bindPFlags, } postgresBackupDescribeCmd := &cobra.Command{ Use: "describe", @@ -241,7 +221,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresBackupDescribe(args) }, - PreRun: bindPFlags, } postgresBackupDeleteCmd := &cobra.Command{ Use: "delete ", @@ -250,7 +229,6 @@ postgres=# RunE: func(cmd *cobra.Command, args []string) error { return c.postgresBackupDelete(args) }, - PreRun: bindPFlags, } postgresCmd.AddCommand(postgresBackupCmd) diff --git a/cmd/project.go b/cmd/project.go index 163ca2c..453ed9e 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -29,7 +29,6 @@ func newProjectCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.projectCreate() }, - PreRun: bindPFlags, } projectDescribeCmd := &cobra.Command{ Use: "describe ", @@ -37,7 +36,6 @@ func newProjectCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.projectDescribe(args) }, - PreRun: bindPFlags, ValidArgsFunction: c.comp.ProjectListCompletion, } projectDeleteCmd := &cobra.Command{ @@ -47,7 +45,6 @@ func newProjectCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.projectDelete(args) }, - PreRun: bindPFlags, ValidArgsFunction: c.comp.ProjectListCompletion, } projectApplyCmd := &cobra.Command{ @@ -56,7 +53,6 @@ func newProjectCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.projectApply() }, - PreRun: bindPFlags, } projectEditCmd := &cobra.Command{ Use: "edit ", @@ -64,7 +60,6 @@ func newProjectCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.projectEdit(args) }, - PreRun: bindPFlags, ValidArgsFunction: c.comp.ProjectListCompletion, } projectListCmd := &cobra.Command{ @@ -74,7 +69,6 @@ func newProjectCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.projectList() }, - PreRun: bindPFlags, } projectCreateCmd.Flags().String("name", "", "name of the project, max 10 characters. [required]") diff --git a/cmd/root.go b/cmd/root.go index 6db7b0d..e80b9a0 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "io" "log" "log/slog" "net/url" @@ -11,32 +12,43 @@ import ( cloudgo "github.com/fi-ts/cloud-go" "github.com/fi-ts/cloud-go/api/client" "github.com/fi-ts/cloudctl/cmd/completion" + "github.com/fi-ts/cloudctl/cmd/helper" "github.com/fi-ts/cloudctl/pkg/api" "github.com/metal-stack/metal-lib/pkg/genericcli" + "github.com/spf13/afero" "github.com/spf13/cobra" "github.com/spf13/viper" ) -var ( - // will bind all viper flags to subcommands and - // prevent overwrite of identical flag names from other commands - // see https://github.com/spf13/viper/issues/233#issuecomment-386791444 - bindPFlags = func(cmd *cobra.Command, args []string) { - err := viper.BindPFlags(cmd.Flags()) - if err != nil { - fmt.Printf("error during setup:%v", err) - os.Exit(1) - } - } +const ( + binaryName = "cloudctl" ) -func newRootCmd() *cobra.Command { - name := "cloudctl" +type config struct { + fs afero.Fs + out io.Writer + cloud *client.CloudAPI + comp *completion.Completion + consoleHost string + log *slog.Logger +} + +func newRootCmd(cfg *config) *cobra.Command { rootCmd := &cobra.Command{ - Use: name, + Use: binaryName, Short: "a cli to manage cloud entities.", Long: "with cloudctl you can manage kubernetes cluster, view networks et.al.", SilenceUsage: true, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + viper.SetFs(cfg.fs) + genericcli.Must(viper.BindPFlags(cmd.Flags())) + genericcli.Must(viper.BindPFlags(cmd.PersistentFlags())) + // we cannot instantiate the config earlier because + // cobra flags do not work so early in the game + genericcli.Must(initConfigWithViperCtx(cfg)) + + return nil + }, } rootCmd.PersistentFlags().StringP("url", "u", "", "api server address. Can be specified with CLOUDCTL_URL environment variable.") @@ -56,15 +68,10 @@ func newRootCmd() *cobra.Command { `) rootCmd.PersistentFlags().BoolP("yes-i-really-mean-it", "", false, "skips security prompts (which can be dangerous to set blindly because actions can lead to data loss or additional costs)") - genericcli.Must(viper.BindPFlags(rootCmd.Flags())) - genericcli.Must(viper.BindPFlags(rootCmd.PersistentFlags())) - - cfg := getConfig(name) - rootCmd.AddCommand(newAuditCmd(cfg)) rootCmd.AddCommand(newClusterCmd(cfg)) rootCmd.AddCommand(newDashboardCmd(cfg)) - rootCmd.AddCommand(newUpdateCmd(cfg, name)) + rootCmd.AddCommand(newUpdateCmd(cfg, binaryName)) rootCmd.AddCommand(newLoginCmd(cfg)) rootCmd.AddCommand(newLogoutCmd(cfg)) rootCmd.AddCommand(newWhoamiCmd()) @@ -84,7 +91,15 @@ func newRootCmd() *cobra.Command { // Execute is the entrypoint of the cloudctl application func Execute() { - cmd := newRootCmd() + // the config will be provided with more values on cobra init + // cobra flags do not work so early in the game + c := &config{ + fs: afero.NewOsFs(), + out: os.Stdout, + comp: &completion.Completion{}, + } + + cmd := newRootCmd(c) err := cmd.Execute() if err != nil { if viper.GetBool("debug") { @@ -94,16 +109,8 @@ func Execute() { } } -type config struct { - name string - cloud *client.CloudAPI - comp *completion.Completion - consoleHost string - log *slog.Logger -} - -func getConfig(name string) *config { - viper.SetEnvPrefix(strings.ToUpper(name)) +func initConfigWithViperCtx(cfg *config) error { + viper.SetEnvPrefix(strings.ToUpper(binaryName)) viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) viper.AutomaticEnv() @@ -117,12 +124,12 @@ func getConfig(name string) *config { } } else { viper.SetConfigName("config") - viper.AddConfigPath(fmt.Sprintf("/etc/%s", name)) + viper.AddConfigPath(fmt.Sprintf("/etc/%s", binaryName)) h, err := os.UserHomeDir() if err != nil { log.Printf("unable to figure out user home directory, skipping config lookup path: %v", err) } else { - viper.AddConfigPath(fmt.Sprintf(h+"/.%s", name)) + viper.AddConfigPath(fmt.Sprintf(h+"/.%s", binaryName)) } viper.AddConfigPath(".") if err := viper.ReadInConfig(); err != nil { @@ -133,12 +140,22 @@ func getConfig(name string) *config { } } + if viper.IsSet("kubeconfig") { + kubeconfigPath, err := helper.ExpandHomeDir(viper.GetString("kubeconfig")) + if err != nil { + return fmt.Errorf("unable to get kubeconfig path: %w", err) + } + + viper.Set("kubeconfig", kubeconfigPath) + } + ctx := api.MustDefaultContext() opts := &slog.HandlerOptions{} if viper.GetBool("debug") { opts.Level = slog.LevelDebug } + cfg.log = slog.New(slog.NewJSONHandler(os.Stdout, opts)) driverURL := viper.GetString("url") if driverURL == "" && ctx.ApiURL != "" { @@ -166,19 +183,14 @@ func getConfig(name string) *config { log.Fatalf("error initializing cloud-api client: %v", err) } - comp := completion.NewCompletion(cloud) + cfg.cloud = cloud + cfg.comp = completion.NewCompletion(cloud) parsedURL, err := url.Parse(driverURL) if err != nil { log.Fatalf("could not parse driver url: %v", err) } - consoleHost := parsedURL.Host - - return &config{ - name: name, - cloud: cloud, - comp: comp, - consoleHost: consoleHost, - log: slog.New(slog.NewJSONHandler(os.Stdout, opts)), - } + cfg.consoleHost = parsedURL.Host + + return nil } diff --git a/cmd/s3.go b/cmd/s3.go index 6434dc4..9455814 100644 --- a/cmd/s3.go +++ b/cmd/s3.go @@ -24,7 +24,6 @@ func newS3Cmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.s3Describe() }, - PreRun: bindPFlags, } s3CreateCmd := &cobra.Command{ Use: "create", @@ -32,7 +31,6 @@ func newS3Cmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.s3Create() }, - PreRun: bindPFlags, } s3DeleteCmd := &cobra.Command{ Use: "delete", @@ -41,7 +39,6 @@ func newS3Cmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.s3Delete() }, - PreRun: bindPFlags, } s3ListCmd := &cobra.Command{ Use: "list", @@ -50,7 +47,6 @@ func newS3Cmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.s3List() }, - PreRun: bindPFlags, } s3PartitionListCmd := &cobra.Command{ Use: "partitions", @@ -59,7 +55,6 @@ func newS3Cmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.s3ListPartitions() }, - PreRun: bindPFlags, } s3AddKeyCmd := &cobra.Command{ Use: "add-key", @@ -67,7 +62,6 @@ func newS3Cmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.s3AddKey() }, - PreRun: bindPFlags, } s3RemoveKeyCmd := &cobra.Command{ Use: "remove-key", @@ -75,7 +69,6 @@ func newS3Cmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.s3RemoveKey() }, - PreRun: bindPFlags, } s3CreateCmd.Flags().StringP("id", "i", "", "id of the s3 user [required]") diff --git a/cmd/tenant.go b/cmd/tenant.go index a7edc07..79fa2ed 100644 --- a/cmd/tenant.go +++ b/cmd/tenant.go @@ -28,7 +28,6 @@ func newTenantCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.tenantList() }, - PreRun: bindPFlags, } tenantDescribeCmd := &cobra.Command{ Use: "describe ", @@ -36,7 +35,6 @@ func newTenantCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.tenantDescribe(args) }, - PreRun: bindPFlags, ValidArgsFunction: c.comp.TenantListCompletion, } tenantEditCmd := &cobra.Command{ @@ -45,7 +43,6 @@ func newTenantCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.tenantEdit(args) }, - PreRun: bindPFlags, ValidArgsFunction: c.comp.TenantListCompletion, } tenantApplyCmd := &cobra.Command{ @@ -54,7 +51,6 @@ func newTenantCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.tenantApply() }, - PreRun: bindPFlags, } tenantListCmd.Flags().String("id", "", "show projects of given id") diff --git a/cmd/version.go b/cmd/version.go index 097b1f4..87be17c 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -32,7 +32,6 @@ func newVersionCmd(c *config) *cobra.Command { } return nil }, - PreRun: bindPFlags, } return versionCmd } diff --git a/cmd/volume.go b/cmd/volume.go index 38eddaa..089757a 100644 --- a/cmd/volume.go +++ b/cmd/volume.go @@ -26,7 +26,6 @@ func newVolumeCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.volumeFind() }, - PreRun: bindPFlags, } volumeDescribeCmd := &cobra.Command{ Use: "describe ", @@ -35,7 +34,6 @@ func newVolumeCmd(c *config) *cobra.Command { return c.volumeDescribe(args) }, ValidArgsFunction: c.comp.VolumeListCompletion, - PreRun: bindPFlags, } volumeDeleteCmd := &cobra.Command{ Use: "delete ", @@ -45,7 +43,6 @@ func newVolumeCmd(c *config) *cobra.Command { return c.volumeDelete(args) }, ValidArgsFunction: c.comp.VolumeListCompletion, - PreRun: bindPFlags, } volumeManifestCmd := &cobra.Command{ Use: "manifest ", @@ -55,7 +52,6 @@ func newVolumeCmd(c *config) *cobra.Command { return c.volumeManifest(args) }, ValidArgsFunction: c.comp.VolumeListCompletion, - PreRun: bindPFlags, } volumeEncryptionSecretManifestCmd := &cobra.Command{ Use: "encryption-secret-manifest", @@ -64,7 +60,6 @@ func newVolumeCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.volumeEncryptionSecretManifest() }, - PreRun: bindPFlags, } volumeClusterInfoCmd := &cobra.Command{ Use: "clusterinfo", @@ -72,7 +67,6 @@ func newVolumeCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.volumeClusterInfo() }, - PreRun: bindPFlags, } snapshotCmd := &cobra.Command{ @@ -87,7 +81,6 @@ func newVolumeCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.snapshotFind() }, - PreRun: bindPFlags, } snapshotDescribeCmd := &cobra.Command{ Use: "describe ", @@ -95,7 +88,6 @@ func newVolumeCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.snapshotDescribe(args) }, - PreRun: bindPFlags, } snapshotDeleteCmd := &cobra.Command{ Use: "delete ", @@ -104,7 +96,6 @@ func newVolumeCmd(c *config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return c.snapshotDelete(args) }, - PreRun: bindPFlags, } snapshotListCmd.Flags().StringP("snapshotid", "", "", "snapshotid to filter [optional]") diff --git a/cmd/whoami.go b/cmd/whoami.go index be1ccb6..76fecf8 100644 --- a/cmd/whoami.go +++ b/cmd/whoami.go @@ -41,7 +41,6 @@ func newWhoamiCmd() *cobra.Command { return nil }, - PreRun: bindPFlags, } return whoamiCmd }