diff --git a/commands/provision.go b/commands/provision.go index 27ff5086..13e6f779 100644 --- a/commands/provision.go +++ b/commands/provision.go @@ -23,7 +23,7 @@ import ( "github.com/percona/percona-everest-cli/commands/provision" ) -func newProvisionCmd(l *zap.SugaredLogger) *cobra.Command { +func newProvisionCmd(l *zap.SugaredLogger) *cobra.Command { //nolint:deadcode,unused cmd := &cobra.Command{ Use: "provision", } diff --git a/pkg/install/operators.go b/pkg/install/operators.go index 831781d1..a84318bb 100644 --- a/pkg/install/operators.go +++ b/pkg/install/operators.go @@ -245,6 +245,10 @@ func (o *Operators) checkEverestConnection(ctx context.Context) error { } func (o *Operators) performProvisioning(ctx context.Context) error { + if err := o.checkPreviousInstallations(ctx); err != nil { + return err + } + if err := o.provisionNamespace(); err != nil { return err } @@ -661,6 +665,7 @@ func (o *Operators) runOperatorsWizard() error { // provisionNamespace provisions a namespace for Everest. func (o *Operators) provisionNamespace() error { o.l.Infof("Creating namespace %s", o.config.Namespace) + err := o.kubeClient.CreateNamespace(o.config.Namespace) if err != nil { return errors.Join(err, errors.New("could not provision namespace")) @@ -670,6 +675,24 @@ func (o *Operators) provisionNamespace() error { return nil } +func (o *Operators) checkPreviousInstallations(ctx context.Context) error { + // check if the current cluster already registered in Everest (by its uid) + // if it's NOT registred, run the below check: + + o.l.Info("Checking existing Everest components") + namespaces, err := o.kubeClient.ListNamespaces(ctx) + if err != nil { + return errors.Join(err, errors.New("could not list namespaces")) + } + for _, ns := range namespaces { + if ns == o.config.Namespace { + return errors.New("please clean up the old installation of Everest first") + } + } + + return nil +} + // provisionAllOperators provisions all configured operators to a k8s cluster. func (o *Operators) provisionAllOperators(ctx context.Context) error { o.l.Info("Started provisioning the cluster") @@ -792,6 +815,9 @@ func (o *Operators) connectToEverest(ctx context.Context) (*client.KubernetesClu o.l.Info("Connecting your Kubernetes cluster to Everest") + // list clusters + // check the current uid is there, if so, skip the registration + k, err := o.everestClient.RegisterKubernetesCluster(ctx, client.CreateKubernetesClusterParams{ Kubeconfig: base64.StdEncoding.EncodeToString([]byte(kubeconfig)), Name: o.config.Name, diff --git a/pkg/kubernetes/client/client.go b/pkg/kubernetes/client/client.go index 0b6ff5ad..2a751693 100644 --- a/pkg/kubernetes/client/client.go +++ b/pkg/kubernetes/client/client.go @@ -999,6 +999,21 @@ func (c *Client) CreateNamespace(name string) error { return c.ApplyObject(n) } +// ListNamespaces lists the existing namespaces. +func (c *Client) ListNamespaces(ctx context.Context) ([]string, error) { + list, err := c.clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, err + } + + namespaces := make([]string, 0, len(list.Items)) + for _, ns := range list.Items { + namespaces = append(namespaces, ns.Name) + } + + return namespaces, nil +} + // GetOperatorGroup retrieves an operator group details by namespace and name. func (c *Client) GetOperatorGroup(ctx context.Context, namespace, name string) (*v1.OperatorGroup, error) { operatorClient, err := versioned.NewForConfig(c.restConfig) diff --git a/pkg/kubernetes/client/kubeclient_interface.go b/pkg/kubernetes/client/kubeclient_interface.go index 3a50d264..c6e0844c 100644 --- a/pkg/kubernetes/client/kubeclient_interface.go +++ b/pkg/kubernetes/client/kubeclient_interface.go @@ -69,6 +69,8 @@ type KubeClientConnector interface { DoRolloutWait(ctx context.Context, key types.NamespacedName) error // CreateNamespace creates a new namespace. CreateNamespace(name string) error + // ListNamespaces lists the existing namespaces + ListNamespaces(ctx context.Context) ([]string, error) // GetOperatorGroup retrieves an operator group details by namespace and name. GetOperatorGroup(ctx context.Context, namespace, name string) (*v1.OperatorGroup, error) // CreateOperatorGroup creates an operator group to be used as part of a subscription. diff --git a/pkg/kubernetes/client/mock_kube_client_connector.go b/pkg/kubernetes/client/mock_kube_client_connector.go index c3988f68..328c403c 100644 --- a/pkg/kubernetes/client/mock_kube_client_connector.go +++ b/pkg/kubernetes/client/mock_kube_client_connector.go @@ -811,6 +811,32 @@ func (_m *MockKubeClientConnector) ListDatabaseClusters(ctx context.Context) (*a return r0, r1 } +// ListNamespaces provides a mock function with given fields: ctx +func (_m *MockKubeClientConnector) ListNamespaces(ctx context.Context) ([]string, error) { + ret := _m.Called(ctx) + + var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]string, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) []string); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // ListSecrets provides a mock function with given fields: ctx func (_m *MockKubeClientConnector) ListSecrets(ctx context.Context) (*corev1.SecretList, error) { ret := _m.Called(ctx) diff --git a/pkg/kubernetes/kubernetes.go b/pkg/kubernetes/kubernetes.go index 7dfb1173..01a2b177 100644 --- a/pkg/kubernetes/kubernetes.go +++ b/pkg/kubernetes/kubernetes.go @@ -574,6 +574,11 @@ func (k *Kubernetes) CreateNamespace(name string) error { return k.client.CreateNamespace(name) } +// ListNamespaces creates a new namespace. +func (k *Kubernetes) ListNamespaces(ctx context.Context) ([]string, error) { + return k.client.ListNamespaces(ctx) +} + // InstallOperatorRequest holds the fields to make an operator install request. type InstallOperatorRequest struct { Namespace string