diff --git a/cli-tests/Makefile b/cli-tests/Makefile index ccbb94b4..dc60c0e1 100644 --- a/cli-tests/Makefile +++ b/cli-tests/Makefile @@ -4,7 +4,7 @@ init: ## Install dependencies install-operators: ## Install operators to k8s ../bin/everest install \ - --namespace percona-everest-operators \ + --namespaces percona-everest-operators \ --skip-wizard \ test-cli: ## Run all tests diff --git a/cli-tests/tests/flow/all-operators.spec.ts b/cli-tests/tests/flow/all-operators.spec.ts index 54ef12d4..b0cc5de8 100644 --- a/cli-tests/tests/flow/all-operators.spec.ts +++ b/cli-tests/tests/flow/all-operators.spec.ts @@ -46,7 +46,7 @@ test.describe('Everest CLI install', async () => { await test.step('run everest install command', async () => { const out = await cli.everestExecSkipWizard( - `install --namespace=everest-all`, + `install --namespaces=everest-all`, ); await out.assertSuccess(); @@ -73,7 +73,7 @@ test.describe('Everest CLI install', async () => { await out.assertSuccess(); - out = await cli.everestExecSkipWizardWithEnv('upgrade --namespace=everest-all', 'DISABLE_TELEMETRY=true'); + out = await cli.everestExecSkipWizardWithEnv('upgrade --namespaces=everest-all', 'DISABLE_TELEMETRY=true'); await out.assertSuccess(); await out.outErrContainsNormalizedMany([ 'Subscriptions have been patched\t{"component": "upgrade"}', diff --git a/cli-tests/tests/flow/mongodb-operator.spec.ts b/cli-tests/tests/flow/mongodb-operator.spec.ts index d217bc97..3f63bd13 100644 --- a/cli-tests/tests/flow/mongodb-operator.spec.ts +++ b/cli-tests/tests/flow/mongodb-operator.spec.ts @@ -49,7 +49,7 @@ test.describe('Everest CLI install', async () => { await test.step('run everest install command', async () => { const out = await cli.everestExecSkipWizard( - `install --operator.mongodb=true --operator.postgresql=false --operator.xtradb-cluster=false --namespace=everest-operators`, + `install --operator.mongodb=true --operator.postgresql=false --operator.xtradb-cluster=false --namespaces=everest-operators`, ); await out.assertSuccess(); diff --git a/cli-tests/tests/flow/pg-operator.spec.ts b/cli-tests/tests/flow/pg-operator.spec.ts index 6f7188b2..a86935a9 100644 --- a/cli-tests/tests/flow/pg-operator.spec.ts +++ b/cli-tests/tests/flow/pg-operator.spec.ts @@ -48,7 +48,7 @@ test.describe('Everest CLI install', async () => { await test.step('run everest install command', async () => { const out = await cli.everestExecSkipWizard( - `install --operator.mongodb=false --operator.postgresql=true --operator.xtradb-cluster=false --namespace=everest-operators`, + `install --operator.mongodb=false --operator.postgresql=true --operator.xtradb-cluster=false --namespaces=everest-operators`, ); await out.assertSuccess(); @@ -68,7 +68,7 @@ test.describe('Everest CLI install', async () => { await operator.assertSuccess(); const out = await cli.everestExecSkipWizard( - `install --operator.mongodb=false --operator.postgresql=true --operator.xtradb-cluster=true --namespace=everest-operators`, + `install --operator.mongodb=false --operator.postgresql=true --operator.xtradb-cluster=true --namespaces=everest-operators`, ); const restartedOperator = await cli.exec(`kubectl -n everest-system get po | grep everest|awk {'print $1'}`); await restartedOperator.assertSuccess(); diff --git a/cli-tests/tests/flow/pxc-operator.spec.ts b/cli-tests/tests/flow/pxc-operator.spec.ts index c8e2ea24..980c27f5 100644 --- a/cli-tests/tests/flow/pxc-operator.spec.ts +++ b/cli-tests/tests/flow/pxc-operator.spec.ts @@ -49,7 +49,7 @@ test.describe('Everest CLI install', async () => { await test.step('run everest install command', async () => { const out = await cli.everestExecSkipWizard( - `install --operator.mongodb=false --operator.postgresql=false --operator.xtradb-cluster=true --namespace=everest-operators`, + `install --operator.mongodb=false --operator.postgresql=false --operator.xtradb-cluster=true --namespaces=everest-operators`, ); await out.assertSuccess(); diff --git a/commands/install.go b/commands/install.go index cdc59bba..7c15ca0e 100644 --- a/commands/install.go +++ b/commands/install.go @@ -30,7 +30,7 @@ import ( func newInstallCmd(l *zap.SugaredLogger) *cobra.Command { cmd := &cobra.Command{ Use: "install", - Example: "everestctl install --namespace dev --namespace staging --namespace prod --operator.mongodb=true --operator.postgresql=true --operator.xtradb-cluster=true --skip-wizard", + Example: "everestctl install --namespaces dev,staging,prod --operator.mongodb=true --operator.postgresql=true --operator.xtradb-cluster=true --skip-wizard", Run: func(cmd *cobra.Command, args []string) { initInstallViperFlags(cmd) c := &install.Config{} @@ -58,7 +58,7 @@ func newInstallCmd(l *zap.SugaredLogger) *cobra.Command { func initInstallFlags(cmd *cobra.Command) { cmd.Flags().StringP("kubeconfig", "k", "~/.kube/config", "Path to a kubeconfig") - cmd.Flags().StringArray("namespace", []string{}, "Namespaces list Percona Everest can manage") + cmd.Flags().String("namespaces", "", "Comma-separated namespaces list Percona Everest can manage") cmd.Flags().Bool("skip-wizard", false, "Skip installation wizard") cmd.Flags().Bool("operator.mongodb", true, "Install MongoDB operator") @@ -71,7 +71,7 @@ func initInstallViperFlags(cmd *cobra.Command) { viper.BindEnv("kubeconfig") //nolint:errcheck,gosec viper.BindPFlag("kubeconfig", cmd.Flags().Lookup("kubeconfig")) //nolint:errcheck,gosec - viper.BindPFlag("namespace", cmd.Flags().Lookup("namespace")) //nolint:errcheck,gosec + viper.BindPFlag("namespaces", cmd.Flags().Lookup("namespaces")) //nolint:errcheck,gosec viper.BindPFlag("operator.mongodb", cmd.Flags().Lookup("operator.mongodb")) //nolint:errcheck,gosec viper.BindPFlag("operator.postgresql", cmd.Flags().Lookup("operator.postgresql")) //nolint:errcheck,gosec diff --git a/commands/upgrade.go b/commands/upgrade.go index 3c6f5f54..7380ddb1 100644 --- a/commands/upgrade.go +++ b/commands/upgrade.go @@ -59,7 +59,7 @@ func newUpgradeCmd(l *zap.SugaredLogger) *cobra.Command { func initUpgradeFlags(cmd *cobra.Command) { cmd.Flags().StringP("kubeconfig", "k", "~/.kube/config", "Path to a kubeconfig") - cmd.Flags().StringArray("namespace", []string{}, "Namespaces list Percona Everest can manage") + cmd.Flags().String("namespaces", "", "Comma-separated namespaces list Percona Everest can manage") cmd.Flags().Bool("upgrade-olm", false, "Upgrade OLM distribution") cmd.Flags().Bool("skip-wizard", false, "Skip installation wizard") } @@ -67,7 +67,7 @@ func initUpgradeFlags(cmd *cobra.Command) { func initUpgradeViperFlags(cmd *cobra.Command) { viper.BindEnv("kubeconfig") //nolint:errcheck,gosec viper.BindPFlag("kubeconfig", cmd.Flags().Lookup("kubeconfig")) //nolint:errcheck,gosec - viper.BindPFlag("namespace", cmd.Flags().Lookup("namespace")) //nolint:errcheck,gosec + viper.BindPFlag("namespaces", cmd.Flags().Lookup("namespaces")) //nolint:errcheck,gosec viper.BindPFlag("upgrade-olm", cmd.Flags().Lookup("upgrade-olm")) //nolint:errcheck,gosec viper.BindPFlag("skip-wizard", cmd.Flags().Lookup("skip-wizard")) //nolint:errcheck,gosec } diff --git a/install.sh b/install.sh index 9e5b24c8..cab215c4 100755 --- a/install.sh +++ b/install.sh @@ -35,7 +35,7 @@ fi echo "Provisioning Everest with monitoring disabled" echo "If you want to enable monitoring please refer to the everest installation documentation." echo "" -./everestctl install --namespace everest --operator.mongodb=true --operator.postgresql=true --operator.xtradb-cluster=true --skip-wizard +./everestctl install --namespaces everest --operator.mongodb=true --operator.postgresql=true --operator.xtradb-cluster=true --skip-wizard echo "Your provisioned Everest instance will be available at http://127.0.0.1:8080" echo "Exposing Everest using kubectl port-forwarding. You can expose it manually" diff --git a/pkg/install/install.go b/pkg/install/install.go index 1af3335a..28fb1d5e 100644 --- a/pkg/install/install.go +++ b/pkg/install/install.go @@ -88,8 +88,8 @@ const ( type ( // Config stores configuration for the operators. Config struct { - // Namespaces defines namespaces that everest can operate in. - Namespaces []string `mapstructure:"namespace"` + // Namespaces defines comma-separated list of namespaces that everest can operate in. + Namespaces string `mapstructure:"namespaces"` // SkipWizard skips wizard during installation. SkipWizard bool `mapstructure:"skip-wizard"` // KubeconfigPath is a path to a kubeconfig @@ -109,6 +109,11 @@ type ( } ) +// NamespacesList returns list of the namespaces that everest can operate in. +func (c Config) NamespacesList() []string { + return strings.Split(c.Namespaces, ",") +} + // NewInstall returns a new Install struct. func NewInstall(c Config, l *zap.SugaredLogger) (*Install, error) { cli := &Install{ @@ -176,11 +181,13 @@ func (o *Install) populateConfig() error { } } - if len(o.config.Namespaces) == 0 { - return errors.New("namespace list is empty. Specify at least one namespace using the --namespace flag") + if len(o.config.NamespacesList()) == 0 { + return errors.New("namespace list is empty. Specify the comma-separated list of namespaces using the --namespaces flag, at least one namespace is required") } - for _, ns := range o.config.Namespaces { + + for _, ns := range o.config.NamespacesList() { if ns == SystemNamespace || ns == MonitoringNamespace { + return fmt.Errorf("'%s' namespace is reserved for Everest internals. Please specify another namespace", ns) } } @@ -237,7 +244,7 @@ func (o *Install) provisionEverestOperator(ctx context.Context) error { } o.l.Info("Creating operator group for everest") - if err := o.kubeClient.CreateOperatorGroup(ctx, systemOperatorGroup, SystemNamespace, o.config.Namespaces); err != nil { + if err := o.kubeClient.CreateOperatorGroup(ctx, systemOperatorGroup, SystemNamespace, o.config.NamespacesList()); err != nil { return err } @@ -275,7 +282,7 @@ func (o *Install) provisionEverest(ctx context.Context) error { } o.l.Info("Updating cluster role bindings for everest-admin") - if err := o.kubeClient.UpdateClusterRoleBinding(ctx, everestServiceAccountClusterRoleBinding, o.config.Namespaces); err != nil { + if err := o.kubeClient.UpdateClusterRoleBinding(ctx, everestServiceAccountClusterRoleBinding, o.config.NamespacesList()); err != nil { return err } @@ -283,7 +290,7 @@ func (o *Install) provisionEverest(ctx context.Context) error { } func (o *Install) provisionDBNamespaces(ctx context.Context) error { - for _, namespace := range o.config.Namespaces { + for _, namespace := range o.config.NamespacesList() { namespace := namespace if err := o.createNamespace(namespace); err != nil { return err @@ -347,10 +354,14 @@ func (o *Install) runEverestWizard() error { return fmt.Errorf("'%s' namespace is reserved for Everest internals. Please specify another namespace", ns) } - o.config.Namespaces = append(o.config.Namespaces, ns) + if o.config.Namespaces != "" { + o.config.Namespaces += "," + } + + o.config.Namespaces += ns } - if len(o.config.Namespaces) == 0 { + if len(o.config.NamespacesList()) == 0 { return errors.New("namespace list is empty. Specify at least one namespace") } @@ -495,7 +506,7 @@ func (o *Install) installOperator(ctx context.Context, channel, operatorName, na }, } if operatorName == everestOperatorName { - params.TargetNamespaces = o.config.Namespaces + params.TargetNamespaces = o.config.NamespacesList() params.SubscriptionConfig.Env = append(params.SubscriptionConfig.Env, []corev1.EnvVar{ { Name: EverestMonitoringNamespaceEnvVar, @@ -503,7 +514,7 @@ func (o *Install) installOperator(ctx context.Context, channel, operatorName, na }, { Name: kubernetes.EverestDBNamespacesEnvVar, - Value: strings.Join(o.config.Namespaces, ","), + Value: o.config.Namespaces, }, }...) } diff --git a/pkg/kubernetes/kubernetes.go b/pkg/kubernetes/kubernetes.go index 553ec250..3b50730f 100644 --- a/pkg/kubernetes/kubernetes.go +++ b/pkg/kubernetes/kubernetes.go @@ -583,7 +583,7 @@ type InstallOperatorRequest struct { func mergeNamespacesEnvVar(str1, str2 string) string { ns1 := strings.Split(str1, ",") ns2 := strings.Split(str2, ",") - nsMap := map[string]struct{}{} + nsMap := make(map[string]struct{}) for _, ns := range ns1 { if ns == "" { diff --git a/pkg/upgrade/upgrade.go b/pkg/upgrade/upgrade.go index 39ca637d..d2d95797 100644 --- a/pkg/upgrade/upgrade.go +++ b/pkg/upgrade/upgrade.go @@ -22,6 +22,7 @@ import ( "fmt" "net/url" "os" + "strings" "github.com/AlecAivazis/survey/v2" goversion "github.com/hashicorp/go-version" @@ -36,8 +37,8 @@ import ( type ( // Config defines configuration required for upgrade command. Config struct { - // Namespaces defines namespaces that everest can operate in. - Namespaces []string `mapstructure:"namespace"` + // Namespaces defines comma-separated list of namespaces that everest can operate in. + Namespaces string `mapstructure:"namespaces"` // KubeconfigPath is a path to a kubeconfig KubeconfigPath string `mapstructure:"kubeconfig"` // UpgradeOLM defines do we need to upgrade OLM or not. @@ -54,6 +55,11 @@ type ( } ) +// NamespacesList returns list of the namespaces that everest can operate in. +func (c Config) NamespacesList() []string { + return strings.Split(c.Namespaces, ",") +} + // NewUpgrade returns a new Upgrade struct. func NewUpgrade(c Config, l *zap.SugaredLogger) (*Upgrade, error) { cli := &Upgrade{ @@ -79,7 +85,7 @@ func (u *Upgrade) Run(ctx context.Context) error { if err := u.runEverestWizard(ctx); err != nil { return err } - if len(u.config.Namespaces) == 0 { + if len(u.config.NamespacesList()) == 0 { return errors.New("namespace list is empty. Specify at least one namespace") } if err := u.upgradeOLM(ctx); err != nil { @@ -126,7 +132,7 @@ func (u *Upgrade) runEverestWizard(ctx context.Context) error { } func (u *Upgrade) patchSubscriptions(ctx context.Context) error { - for _, namespace := range u.config.Namespaces { + for _, namespace := range u.config.NamespacesList() { namespace := namespace subList, err := u.kubeClient.ListSubscriptions(ctx, namespace) if err != nil {