From 0e14d17a0e2ef74537d7fa38c2f88a6d9cec0562 Mon Sep 17 00:00:00 2001 From: shubham Date: Thu, 28 Nov 2019 16:27:57 +0530 Subject: [PATCH] feat(cspc): migrate pools from spc to cspc Signed-off-by: shubham --- GNUmakefile | 2 + buildscripts/migrate/Dockerfile | 20 ++ buildscripts/migrate/Makefile.mk | 32 +++ cmd/cspi-mgmt/app/handler.go | 7 +- cmd/cstor-pool-mgmt/pool/v1alpha2/import.go | 5 +- cmd/migrate/executor/env.go | 30 ++ cmd/migrate/executor/error.go | 34 +++ cmd/migrate/executor/options.go | 46 +++ cmd/migrate/executor/pool.go | 80 ++++++ cmd/migrate/executor/setup_job.go | 66 +++++ cmd/migrate/main.go | 31 ++ .../nodeselect/v1alpha2/build_csp.go | 22 +- pkg/cstor/migrate/cspc_generator.go | 64 ++++- pkg/cstor/migrate/migration.go | 270 ++++++++++++++++++ 14 files changed, 694 insertions(+), 15 deletions(-) create mode 100644 buildscripts/migrate/Dockerfile create mode 100644 buildscripts/migrate/Makefile.mk create mode 100644 cmd/migrate/executor/env.go create mode 100644 cmd/migrate/executor/error.go create mode 100644 cmd/migrate/executor/options.go create mode 100644 cmd/migrate/executor/pool.go create mode 100644 cmd/migrate/executor/setup_job.go create mode 100644 cmd/migrate/main.go create mode 100644 pkg/cstor/migrate/migration.go diff --git a/GNUmakefile b/GNUmakefile index b8c4d310b5..1e345be2b3 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -79,6 +79,7 @@ M_EXPORTER_REPO_NAME?=m-exporter ADMISSION_SERVER_REPO_NAME?=admission-server M_UPGRADE_REPO_NAME?=m-upgrade CSPC_OPERATOR_REPO_NAME?=cspc-operator +M_MIGRATE_REPO_NAME?=migrate ifeq (${IMAGE_TAG}, ) IMAGE_TAG = ci @@ -118,6 +119,7 @@ include ./buildscripts/apiserver/Makefile.mk include ./buildscripts/provisioner-localpv/Makefile.mk include ./buildscripts/upgrade/Makefile.mk include ./buildscripts/upgrade-082090/Makefile.mk +include ./buildscripts/migrate/Makefile.mk .PHONY: all all: compile-tests apiserver-image exporter-image pool-mgmt-image volume-mgmt-image admission-server-image cspc-operator-image cspc-operator-debug-image cspi-mgmt-image upgrade-image provisioner-localpv-image diff --git a/buildscripts/migrate/Dockerfile b/buildscripts/migrate/Dockerfile new file mode 100644 index 0000000000..bb75294e48 --- /dev/null +++ b/buildscripts/migrate/Dockerfile @@ -0,0 +1,20 @@ +# +# This builds openebs migratte image using +# its latest binary +# + +FROM alpine:3.9 + +# copy the latest binary +COPY migrate /usr/local/bin/migrate + +ARG BUILD_DATE + +LABEL org.label-schema.name="migrate" +LABEL org.label-schema.description="migrates openebs components" +LABEL org.label-schema.url="https://openebs.io/" +LABEL org.label-schema.vcs-url="https://github.com/openebs/maya" +LABEL org.label-schema.schema-version="1.0" +LABEL org.label-schema.build-date=$BUILD_DATE + +ENTRYPOINT ["migrate"] diff --git a/buildscripts/migrate/Makefile.mk b/buildscripts/migrate/Makefile.mk new file mode 100644 index 0000000000..1afafee4f1 --- /dev/null +++ b/buildscripts/migrate/Makefile.mk @@ -0,0 +1,32 @@ + +# Specify the name for the binaries +MIGRATE=migrate + +# build migrate binary +.PHONY: migrate +migrate: + @echo "----------------------------" + @echo "--> ${MIGRATE} " + @echo "----------------------------" + @# PNAME is the sub-folder in ./bin where binary will be placed. + @# CTLNAME indicates the folder/pkg under cmd that needs to be built. + @# The output binary will be: ./bin/${PNAME}//${CTLNAME} + @# A copy of the binary will also be placed under: ./bin/${PNAME}/${CTLNAME} + @PNAME=${MIGRATE} CTLNAME=${MIGRATE} CGO_ENABLED=0 sh -c "'$(PWD)/buildscripts/build.sh'" + +# build migrate image +.PHONY: migrate-image +migrate-image: migrate + @echo "-----------------------------------------------" + @echo "--> ${MIGRATE} image " + @echo "${HUB_USER}/${M_MIGRATE_REPO_NAME}:${IMAGE_TAG}" + @echo "-----------------------------------------------" + @cp bin/${MIGRATE}/${MIGRATE} buildscripts/${MIGRATE}/ + @cd buildscripts/${MIGRATE} && \ + sudo docker build -t "${HUB_USER}/${M_MIGRATE_REPO_NAME}:${IMAGE_TAG}" --build-arg BUILD_DATE=${BUILD_DATE} . + @rm buildscripts/${MIGRATE}/${MIGRATE} + +# cleanup migrate build +.PHONY: cleanup-migrate +cleanup-migrate: + rm -rf ${GOPATH}/bin/${MIGRATE} diff --git a/cmd/cspi-mgmt/app/handler.go b/cmd/cspi-mgmt/app/handler.go index d34ba4528b..8ef6d39832 100644 --- a/cmd/cspi-mgmt/app/handler.go +++ b/cmd/cspi-mgmt/app/handler.go @@ -59,7 +59,11 @@ func (c *CStorPoolInstanceController) reconcile(key string) error { common.SyncResources.Mux.Lock() // try to import pool - isImported, err = zpool.Import(cspi) + if cspi.Annotations["cspuid"] != "" { + isImported, err = zpool.Import(cspi, "cstor-"+cspi.Annotations["cspuid"]) + } else { + isImported, err = zpool.Import(cspi, "") + } if isImported { if err != nil { common.SyncResources.Mux.Unlock() @@ -71,6 +75,7 @@ func (c *CStorPoolInstanceController) reconcile(key string) error { } zpool.CheckImportedPoolVolume() common.SyncResources.Mux.Unlock() + delete(cspi.Annotations, "cspuid") err = c.update(cspi) if err != nil { c.recorder.Event(cspi, diff --git a/cmd/cstor-pool-mgmt/pool/v1alpha2/import.go b/cmd/cstor-pool-mgmt/pool/v1alpha2/import.go index eb4c257777..078689f956 100644 --- a/cmd/cstor-pool-mgmt/pool/v1alpha2/import.go +++ b/cmd/cstor-pool-mgmt/pool/v1alpha2/import.go @@ -31,7 +31,7 @@ import ( // It will return - // - If pool is imported or not // - If any error occurred during import operation -func Import(cspi *apis.CStorPoolInstance) (bool, error) { +func Import(cspi *apis.CStorPoolInstance, oldName string) (bool, error) { if poolExist := checkIfPoolPresent(PoolName(cspi)); poolExist { return true, nil } @@ -54,7 +54,8 @@ func Import(cspi *apis.CStorPoolInstance) (bool, error) { WithCachefile(cspi.Spec.PoolConfig.CacheFile). WithProperty("cachefile", cspi.Spec.PoolConfig.CacheFile). WithDirectory(devID). - WithPool(PoolName(cspi)). + WithPool(oldName). + WithNewPool(PoolName(cspi)). Execute() if err == nil { poolImported = true diff --git a/cmd/migrate/executor/env.go b/cmd/migrate/executor/env.go new file mode 100644 index 0000000000..5d5e3f91c8 --- /dev/null +++ b/cmd/migrate/executor/env.go @@ -0,0 +1,30 @@ +/* +Copyright 2019 The OpenEBS Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package executor + +import ( + menv "github.com/openebs/maya/pkg/env/v1alpha1" +) + +//This file defines the environement variable names that are specific +// to this provisioner. In addition to the variables defined in this file, +// provisioner also uses the following: +// OPENEBS_NAMESPACE + +func getOpenEBSNamespace() string { + return menv.Get(menv.OpenEBSNamespace) +} diff --git a/cmd/migrate/executor/error.go b/cmd/migrate/executor/error.go new file mode 100644 index 0000000000..ff6d566999 --- /dev/null +++ b/cmd/migrate/executor/error.go @@ -0,0 +1,34 @@ +/* +Copyright 2019 The OpenEBS Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package executor + +import ( + "context" + "fmt" + "os" +) + +// CheckError prints err to stderr and exits with code 1 if err is not nil. Otherwise, it is a +// no-op. +func CheckError(err error) { + if err != nil { + if err != context.Canceled { + fmt.Fprintf(os.Stderr, fmt.Sprintf("An error occurred: %v\n", err)) + } + os.Exit(1) + } +} diff --git a/cmd/migrate/executor/options.go b/cmd/migrate/executor/options.go new file mode 100644 index 0000000000..ef881202af --- /dev/null +++ b/cmd/migrate/executor/options.go @@ -0,0 +1,46 @@ +/* +Copyright 2019 The OpenEBS Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package executor + +import ( + "strings" + + errors "github.com/pkg/errors" + + "github.com/spf13/cobra" +) + +// MigrateOptions stores information required for migrate +type MigrateOptions struct { + openebsNamespace string + spcName string +} + +var ( + options = &MigrateOptions{ + openebsNamespace: "openebs", + } +) + +// RunPreFlightChecks will ensure the sanity of the common migrate options +func (u *MigrateOptions) RunPreFlightChecks(cmd *cobra.Command) error { + if len(strings.TrimSpace(u.openebsNamespace)) == 0 { + return errors.Errorf("Cannot execute migrate job: namespace is missing") + } + + return nil +} diff --git a/cmd/migrate/executor/pool.go b/cmd/migrate/executor/pool.go new file mode 100644 index 0000000000..707d48f333 --- /dev/null +++ b/cmd/migrate/executor/pool.go @@ -0,0 +1,80 @@ +/* +Copyright 2019 The OpenEBS Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package executor + +import ( + "strings" + + migrate "github.com/openebs/maya/pkg/cstor/migrate" + "github.com/openebs/maya/pkg/util" + "github.com/spf13/cobra" + "k8s.io/klog" + + errors "github.com/pkg/errors" +) + +var ( + cstorSPCMigrateCmdHelpText = ` +This command migrates the cStor SPC + +Usage: migrate pool --spc-name +` +) + +// NewMigratePoolJob migrates all the cStor Pools associated with +// a given Storage Pool Claim +func NewMigratePoolJob() *cobra.Command { + cmd := &cobra.Command{ + Use: "pool", + Short: "Migrate cStor SPC", + Long: cstorSPCMigrateCmdHelpText, + Example: `migrate cstor-spc --spc-name `, + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(options.RunPreFlightChecks(cmd), util.Fatal) + util.CheckErr(options.RunCStorSPCMigrateChecks(cmd), util.Fatal) + util.CheckErr(options.RunCStorSPCMigrate(cmd), util.Fatal) + }, + } + + cmd.Flags().StringVarP(&options.spcName, + "spc-name", "", + options.spcName, + "cstor SPC name to be migrated. Run \"kubectl get spc\", to get spc-name") + + return cmd +} + +// RunCStorSPCMigrateChecks will ensure the sanity of the cstor SPC migrate options +func (u *MigrateOptions) RunCStorSPCMigrateChecks(cmd *cobra.Command) error { + if len(strings.TrimSpace(u.spcName)) == 0 { + return errors.Errorf("Cannot execute migrate job: cstor spc name is missing") + } + + return nil +} + +// RunCStorSPCMigrate migrates the given spc. +func (u *MigrateOptions) RunCStorSPCMigrate(cmd *cobra.Command) error { + + err := migrate.Pool(u.spcName, u.openebsNamespace) + if err != nil { + klog.Error(err) + return errors.Errorf("Failed to migrate cStor SPC : %s", u.spcName) + } + + return nil +} diff --git a/cmd/migrate/executor/setup_job.go b/cmd/migrate/executor/setup_job.go new file mode 100644 index 0000000000..52a0edd1c6 --- /dev/null +++ b/cmd/migrate/executor/setup_job.go @@ -0,0 +1,66 @@ +/* +Copyright 2019 The OpenEBS Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package executor + +import ( + "flag" + //"fmt" + //"os" + "strings" + + //"k8s.io/klog" + + "github.com/spf13/cobra" +) + +var spcName, openebsNamespace string + +// NewJob will setup a new migrate job +func NewJob() *cobra.Command { + // Create a new command. + cmd := &cobra.Command{ + Use: "migrate", + Short: "OpenEBS Migrate Utility", + Long: `An utility to migrate OpenEBS SPC Pools to CSPC and Non-CSI Volumes to CSI Volumes, + run as a Kubernetes Job`, + PersistentPreRun: PreRun, + } + + cmd.AddCommand( + NewMigratePoolJob(), + ) + + cmd.PersistentFlags().StringVarP(&options.openebsNamespace, + "openebs-namespace", "", + options.openebsNamespace, + "namespace where openebs components are installed.") + + cmd.PersistentFlags().AddGoFlagSet(flag.CommandLine) + + // Hack: Without the following line, the logs will be prefixed with Error + _ = flag.CommandLine.Parse([]string{}) + + return cmd +} + +// PreRun will check for environement variables to be read and intialized. +func PreRun(cmd *cobra.Command, args []string) { + namespace := getOpenEBSNamespace() + if len(strings.TrimSpace(namespace)) != 0 { + openebsNamespace = namespace + } +} diff --git a/cmd/migrate/main.go b/cmd/migrate/main.go new file mode 100644 index 0000000000..e806b60638 --- /dev/null +++ b/cmd/migrate/main.go @@ -0,0 +1,31 @@ +/* +Copyright 2018 The OpenEBS Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "github.com/openebs/maya/cmd/migrate/executor" + mlogger "github.com/openebs/maya/pkg/logs" +) + +func main() { + // Init logging + mlogger.InitLogs() + defer mlogger.FlushLogs() + + err := executor.NewJob().Execute() + executor.CheckError(err) +} diff --git a/pkg/algorithm/nodeselect/v1alpha2/build_csp.go b/pkg/algorithm/nodeselect/v1alpha2/build_csp.go index 4ead6b95ca..cdbf774078 100644 --- a/pkg/algorithm/nodeselect/v1alpha2/build_csp.go +++ b/pkg/algorithm/nodeselect/v1alpha2/build_csp.go @@ -61,9 +61,25 @@ func (ac *Config) GetCSPSpec() (*apis.CStorPoolInstance, error) { return nil, errors.Wrapf(err, "failed to build CSP object for node selector {%v}", poolSpec.NodeSelector) } - err = ac.ClaimBDsForNode(ac.GetBDListForNode(poolSpec)) - if err != nil { - return nil, errors.Wrapf(err, "failed to claim block devices for node {%s}", nodeName) + annotations := map[string]string{} + + if ac.CSPC.GetAnnotations()["reconcile.openebs.io/dependants"] == "false" { + annotations[string(apis.OpenEBSDisableReconcileKey)] = "true" + } + + if poolSpec.OldCSPUID != "" { + annotations["cspuid"] = poolSpec.OldCSPUID + } + + if len(annotations) != 0 { + cspObj.Object.SetAnnotations(annotations) + } + + if poolSpec.OldCSPUID == "" { + err = ac.ClaimBDsForNode(ac.GetBDListForNode(poolSpec)) + if err != nil { + return nil, errors.Wrapf(err, "failed to claim block devices for node {%s}", nodeName) + } } return cspObj.Object, nil } diff --git a/pkg/cstor/migrate/cspc_generator.go b/pkg/cstor/migrate/cspc_generator.go index 1cd8b8b401..77173538a3 100644 --- a/pkg/cstor/migrate/cspc_generator.go +++ b/pkg/cstor/migrate/cspc_generator.go @@ -17,10 +17,15 @@ limitations under the License. package migrate import ( + "time" + apis "github.com/openebs/maya/pkg/apis/openebs.io/v1alpha1" csp "github.com/openebs/maya/pkg/cstor/pool/v1alpha3" cspc "github.com/openebs/maya/pkg/cstor/poolcluster/v1alpha1" - spc "github.com/openebs/maya/pkg/storagepoolclaim/v1alpha1" + cspi "github.com/openebs/maya/pkg/cstor/poolinstance/v1alpha3" + "github.com/openebs/maya/pkg/util/retry" + "github.com/pkg/errors" + k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -85,18 +90,59 @@ func getCSPCSpec(spc *apis.StoragePoolClaim) (*apis.CStorPoolCluster, error) { return cspcObj, nil } -func generateCSPC(spcName, openebsNamespace string) error { - spcObj, err := spc.NewKubeClient().Get(spcName, metav1.GetOptions{}) +func generateCSPC(spcObj *apis.StoragePoolClaim, openebsNamespace string) ( + *apis.CStorPoolCluster, error) { + cspcObj, err := cspc.NewKubeClient(). + WithNamespace(openebsNamespace).Get(spcObj.Name, metav1.GetOptions{}) + if err == nil { + return cspcObj, nil + } + if !k8serrors.IsNotFound(err) { + return nil, err + } + cspcObj, err = getCSPCSpec(spcObj) + if err != nil { + return nil, err + } + cspcObj, err = cspc.NewKubeClient(). + WithNamespace(openebsNamespace).Create(cspcObj) if err != nil { - return err + return nil, err } - cspcObj, err := getCSPCSpec(spcObj) + err = retry. + Times(60). + Wait(5 * time.Second). + Try(func(attempt uint) error { + cspiList, err1 := cspi.NewKubeClient(). + WithNamespace(openebsNamespace).List( + metav1.ListOptions{ + LabelSelector: string(apis.CStorPoolClusterCPK) + "=" + cspcObj.Name, + }) + if err1 != nil { + return err1 + } + if len(cspiList.Items) != len(cspcObj.Spec.Pools) { + return errors.Errorf("failed to verify cspi count expected: %d got: %d", + len(cspcObj.Spec.Pools), + len(cspiList.Items), + ) + } + return nil + }) if err != nil { - return err + return nil, err } - _, err = cspc.NewKubeClient().WithNamespace(openebsNamespace).Create(cspcObj) + cspcObj, err = cspc.NewKubeClient(). + WithNamespace(openebsNamespace).Get(spcObj.Name, metav1.GetOptions{}) if err != nil { - return err + return nil, err } - return nil + delete(cspcObj.Annotations, "reconcile.openebs.io/dependants") + cspcObj, err = cspc.NewKubeClient(). + WithNamespace(openebsNamespace). + Update(cspcObj) + if err != nil { + return nil, err + } + return cspcObj, nil } diff --git a/pkg/cstor/migrate/migration.go b/pkg/cstor/migrate/migration.go new file mode 100644 index 0000000000..ebdc74418e --- /dev/null +++ b/pkg/cstor/migrate/migration.go @@ -0,0 +1,270 @@ +/* +Copyright 2019 The OpenEBS Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package migrate + +import ( + "fmt" + "time" + + "k8s.io/klog" + + apis "github.com/openebs/maya/pkg/apis/openebs.io/v1alpha1" + bd "github.com/openebs/maya/pkg/blockdevice/v1alpha2" + bdc "github.com/openebs/maya/pkg/blockdeviceclaim/v1alpha1" + csp "github.com/openebs/maya/pkg/cstor/pool/v1alpha3" + cspc "github.com/openebs/maya/pkg/cstor/poolcluster/v1alpha1" + cspi "github.com/openebs/maya/pkg/cstor/poolinstance/v1alpha3" + cvr "github.com/openebs/maya/pkg/cstor/volumereplica/v1alpha1" + deploy "github.com/openebs/maya/pkg/kubernetes/deployment/appsv1/v1alpha1" + pod "github.com/openebs/maya/pkg/kubernetes/pod/v1alpha1" + spc "github.com/openebs/maya/pkg/storagepoolclaim/v1alpha1" + "github.com/openebs/maya/pkg/util/retry" + "github.com/pkg/errors" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" +) + +const replicaPatch = `{ + "spec": { + "replicas": 0 + } +}` + +// Pool ... +func Pool(spcName, openebsNamespace string) error { + klog.Infof("Migrating spc %s to cspc", spcName) + spcObj, err := spc.NewKubeClient(). + Get(spcName, metav1.GetOptions{}) + if k8serrors.IsNotFound(err) { + klog.Infof("spc %s not found.", spcName) + _, err := cspc.NewKubeClient(). + WithNamespace(openebsNamespace).Get(spcName, metav1.GetOptions{}) + if err != nil { + return errors.Wrapf(err, "failed to get equivalent cspc for spc %s", spcName) + } + klog.Infof("spc %s is already migrated to cspc", spcName) + return nil + } + if err != nil { + return err + } + klog.Infof("Creating equivalent cspc for spc %s", spcName) + cspcObj, err := generateCSPC(spcObj, openebsNamespace) + if err != nil { + return err + } + + cspiList, err := cspi.NewKubeClient(). + WithNamespace(openebsNamespace). + List(metav1.ListOptions{ + LabelSelector: string(apis.CStorPoolClusterCPK) + "=" + cspcObj.Name, + }) + if err != nil { + return err + } + + for _, cspiObj := range cspiList.Items { + if cspiObj.Status.Phase != "ONLINE" { + err = csptocspi(&cspiObj, cspcObj, openebsNamespace) + if err != nil { + return err + } + cspcObj, err = cspc.NewKubeClient(). + WithNamespace(openebsNamespace).Get(cspcObj.Name, metav1.GetOptions{}) + for i, poolspec := range cspcObj.Spec.Pools { + if poolspec.NodeSelector[string(apis.HostNameCPK)] == + cspiObj.Labels[string(apis.HostNameCPK)] { + cspcObj.Spec.Pools[i].OldCSPUID = "" + } + } + cspcObj, err = cspc.NewKubeClient(). + WithNamespace(openebsNamespace).Update(cspcObj) + if err != nil { + return err + } + } + } + err = spc.NewKubeClient(). + Delete(spcName, &metav1.DeleteOptions{}) + if err != nil { + return err + } + return nil +} + +func csptocspi(cspiObj *apis.CStorPoolInstance, cspcObj *apis.CStorPoolCluster, openebsNamespace string) error { + hostnameLabel := string(apis.HostNameCPK) + "=" + cspiObj.Labels[string(apis.HostNameCPK)] + spcLabel := string(apis.StoragePoolClaimCPK) + "=" + cspcObj.Name + cspLabel := hostnameLabel + "," + spcLabel + + cspObj, err := getCSP(cspLabel) + if err != nil { + return err + } + klog.Infof("Migrating csp %s to cspi %s", cspiObj.Name, cspObj.Name) + err = scaleDownDeployment(cspObj, openebsNamespace) + if err != nil { + return err + } + for _, bdName := range cspObj.Spec.Group[0].Item { + err = updateBDC(bdName, cspcObj, openebsNamespace) + if err != nil { + return err + } + } + delete(cspiObj.Annotations, string(apis.OpenEBSDisableReconcileKey)) + cspiObj, err = cspi.NewKubeClient(). + WithNamespace(openebsNamespace). + Update(cspiObj) + if err != nil { + return err + } + err = retry. + Times(60). + Wait(5 * time.Second). + Try(func(attempt uint) error { + cspiObj, err1 := cspi.NewKubeClient(). + WithNamespace(openebsNamespace). + Get(cspiObj.Name, metav1.GetOptions{}) + if err1 != nil { + return err1 + } + if cspiObj.Status.Phase != "ONLINE" { + return errors.Errorf("failed to verify cspi phase expected: Healthy got: %s", + cspiObj.Status.Phase) + } + return nil + }) + if err != nil { + return err + } + err = updateCVRsLabels(cspObj.Name, openebsNamespace, cspiObj) + if err != nil { + return err + } + return nil +} + +func getCSP(cspLabel string) (*apis.CStorPool, error) { + cspClient := csp.KubeClient() + cspList, err := cspClient.List(metav1.ListOptions{ + LabelSelector: cspLabel, + }) + if err != nil { + return nil, err + } + if len(cspList.Items) != 1 { + return nil, fmt.Errorf("Invalid number of pools on one node: %d", len(cspList.Items)) + } + cspObj := cspList.Items[0] + if err != nil { + return nil, err + } + return &cspObj, nil +} + +func scaleDownDeployment(cspObj *apis.CStorPool, openebsNamespace string) error { + klog.Infof("Scaling down deployemnt %s", cspObj.Name) + cspPod, err := pod.NewKubeClient(). + WithNamespace(openebsNamespace).List( + metav1.ListOptions{ + LabelSelector: "openebs.io/cstor-pool=" + cspObj.Name, + }) + if err != nil { + return err + } + _, err = deploy.NewKubeClient(). + WithNamespace(openebsNamespace).Patch( + cspObj.Name, + types.StrategicMergePatchType, + []byte(replicaPatch), + ) + err = retry. + Times(60). + Wait(5 * time.Second). + Try(func(attempt uint) error { + _, err1 := pod.NewKubeClient(). + WithNamespace(openebsNamespace). + Get(cspPod.Items[0].Name, metav1.GetOptions{}) + if !k8serrors.IsNotFound(err1) { + return errors.Errorf("failed to get csp pod because %s", err1) + } + return nil + }) + if err != nil { + return err + } + return nil +} + +func updateBDC(bdName apis.CspBlockDevice, cspcObj *apis.CStorPoolCluster, openebsNamespace string) error { + bdObj, err := bd.NewKubeClient(). + WithNamespace(openebsNamespace). + Get(bdName.Name, metav1.GetOptions{}) + if err != nil { + return err + } + bdcObj, err := bdc.NewKubeClient(). + WithNamespace(openebsNamespace). + Get(bdObj.Spec.ClaimRef.Name, metav1.GetOptions{}) + if err != nil { + return err + } + klog.Infof("Updating bdc %s with cspc %s info.", bdcObj.Name, cspcObj.Name) + delete(bdcObj.Labels, string(apis.StoragePoolClaimCPK)) + bdcObj.Labels[string(apis.CStorPoolClusterCPK)] = cspcObj.Name + for i, finalizer := range bdcObj.Finalizers { + if finalizer == "storagepoolclaim.openebs.io/finalizer" { + bdcObj.Finalizers[i] = "cstorpoolcluster.openebs.io/finalizer" + } + } + bdcObj.OwnerReferences[0].Kind = "CStorPoolCluster" + bdcObj.OwnerReferences[0].UID = cspcObj.UID + bdcObj, err = bdc.NewKubeClient(). + WithNamespace(openebsNamespace). + Update(bdcObj) + if err != nil { + return err + } + return nil +} + +func updateCVRsLabels(cspName, openebsNamespace string, cspiObj *apis.CStorPoolInstance) error { + cvrList, err := cvr.NewKubeclient(). + WithNamespace(openebsNamespace).List(metav1.ListOptions{ + LabelSelector: "cstorpool.openebs.io/name=" + cspName, + }) + if err != nil { + return err + } + for _, cvrObj := range cvrList.Items { + klog.Infof("Updating cvr %s with cspi %s info.", cvrObj.Name, cspiObj.Name) + delete(cvrObj.Labels, "cstorpool.openebs.io/name") + delete(cvrObj.Labels, "cstorpool.openebs.io/uid") + cvrObj.Labels["cstorpoolinstance.openebs.io/name"] = cspiObj.Name + cvrObj.Labels["cstorpoolinstance.openebs.io/uid"] = string(cspiObj.UID) + delete(cvrObj.Annotations, "cstorpool.openebs.io/hostname") + cvrObj.Annotations["cstorpoolinstance.openebs.io/hostname"] = cspiObj.Spec.HostName + _, err = cvr.NewKubeclient().WithNamespace(openebsNamespace). + Update(&cvrObj) + if err != nil { + return err + } + } + return nil +}