From b934931957b21153549f517582ba391633c2c17f Mon Sep 17 00:00:00 2001 From: Shubham Bajpai Date: Tue, 7 Jul 2020 13:04:22 +0530 Subject: [PATCH] feat(migrate): add support to rename spc while migrating to cspc (#13) - add support to rename spc while migrating to cspc - skip snapshot checks if no snapshots exist - bump VERSION file to 1.12.0 Signed-off-by: shubham --- VERSION | 2 +- cmd/migrate/executor/cstor_pool.go | 9 +++ cmd/migrate/executor/options.go | 1 + pkg/migrate/cstor/cspc_generator.go | 13 ++-- pkg/migrate/cstor/pool.go | 107 +++++++++++++++++++++++++--- pkg/migrate/cstor/snapshot.go | 13 ++-- 6 files changed, 122 insertions(+), 23 deletions(-) diff --git a/VERSION b/VERSION index 1cac385c..0eed1a29 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.11.0 +1.12.0 diff --git a/cmd/migrate/executor/cstor_pool.go b/cmd/migrate/executor/cstor_pool.go index 0e848a37..97d1d0cf 100644 --- a/cmd/migrate/executor/cstor_pool.go +++ b/cmd/migrate/executor/cstor_pool.go @@ -55,6 +55,11 @@ func NewMigratePoolJob() *cobra.Command { options.spcName, "cstor SPC name to be migrated. Run \"kubectl get spc\", to get spc-name") + cmd.Flags().StringVarP(&options.cspcName, + "cspc-name", "", + options.cspcName, + "[optional] custom cspc name. By default cspc is created with same name as spc") + return cmd } @@ -72,6 +77,10 @@ func (m *MigrateOptions) RunCStorSPCMigrate() error { klog.Infof("Migrating spc %s to cspc", m.spcName) migrator := cstor.CSPCMigrator{} + if m.cspcName != "" { + klog.Infof("using custom cspc name as %s", m.cspcName) + migrator.SetCSPCName(m.cspcName) + } err := migrator.Migrate(m.spcName, m.openebsNamespace) if err != nil { klog.Error(err) diff --git a/cmd/migrate/executor/options.go b/cmd/migrate/executor/options.go index 39de1e11..a34395b2 100644 --- a/cmd/migrate/executor/options.go +++ b/cmd/migrate/executor/options.go @@ -27,6 +27,7 @@ import ( type MigrateOptions struct { openebsNamespace string spcName string + cspcName string pvName string } diff --git a/pkg/migrate/cstor/cspc_generator.go b/pkg/migrate/cstor/cspc_generator.go index a4d72bda..7980c0b7 100644 --- a/pkg/migrate/cstor/cspc_generator.go +++ b/pkg/migrate/cstor/cspc_generator.go @@ -65,7 +65,7 @@ func getBDList(rg apis.BlockDeviceGroup) []cstor.CStorPoolInstanceBlockDevice { return list } -func (c *CSPCMigrator) getCSPCSpecForSPC() (*cstor.CStorPoolCluster, error) { +func (c *CSPCMigrator) getCSPCSpecForSPC(spcName string) (*cstor.CStorPoolCluster, error) { cspClient := csp.KubeClient() cspList, err := cspClient.List(metav1.ListOptions{ LabelSelector: string(apis.StoragePoolClaimCPK) + "=" + c.SPCObj.Name, @@ -74,11 +74,12 @@ func (c *CSPCMigrator) getCSPCSpecForSPC() (*cstor.CStorPoolCluster, error) { return nil, err } cspcObj := &cstor.CStorPoolCluster{} - cspcObj.Name = c.SPCObj.Name + cspcObj.Name = c.CSPCName cspcObj.Annotations = map[string]string{ // This annotation will be used to disable reconciliation on the dependants. // In this case that will be CSPI types.OpenEBSDisableDependantsReconcileKey: "true", + "openebs.io/migrated-from": spcName, } for _, cspObj := range cspList.Items { cspDeployList, err := deploy.NewKubeClient().WithNamespace(c.OpenebsNamespace). @@ -130,15 +131,15 @@ func getCSPAuxResources(cspDeploy appsv1.Deployment) *corev1.ResourceRequirement } // generateCSPC creates an equivalent cspc for the given spc object -func (c *CSPCMigrator) generateCSPC() ( +func (c *CSPCMigrator) generateCSPC(spcName string) ( *cstor.CStorPoolCluster, error) { cspcObj, err := c.OpenebsClientset.CstorV1(). - CStorPoolClusters(c.OpenebsNamespace).Get(c.SPCObj.Name, metav1.GetOptions{}) + CStorPoolClusters(c.OpenebsNamespace).Get(c.CSPCName, metav1.GetOptions{}) if !k8serrors.IsNotFound(err) && err != nil { return nil, err } if err != nil { - cspcObj, err = c.getCSPCSpecForSPC() + cspcObj, err = c.getCSPCSpecForSPC(spcName) if err != nil { return nil, err } @@ -176,7 +177,7 @@ func (c *CSPCMigrator) generateCSPC() ( return nil, err } cspcObj, err = c.OpenebsClientset.CstorV1(). - CStorPoolClusters(c.OpenebsNamespace).Get(c.SPCObj.Name, metav1.GetOptions{}) + CStorPoolClusters(c.OpenebsNamespace).Get(c.CSPCName, metav1.GetOptions{}) if err != nil { return nil, err } diff --git a/pkg/migrate/cstor/pool.go b/pkg/migrate/cstor/pool.go index 9c7e9b67..4a03ecf9 100644 --- a/pkg/migrate/cstor/pool.go +++ b/pkg/migrate/cstor/pool.go @@ -65,6 +65,12 @@ type CSPCMigrator struct { CSPCObj *cstor.CStorPoolCluster SPCObj *apis.StoragePoolClaim OpenebsNamespace string + CSPCName string +} + +// SetCSPCName is used to initialize custom name if provided +func (c *CSPCMigrator) SetCSPCName(name string) { + c.CSPCName = name } // Migrate ... @@ -86,6 +92,13 @@ func (c *CSPCMigrator) Migrate(name, namespace string) error { if err != nil { return err } + if c.CSPCName == "" { + c.CSPCName = name + } + err = c.checkForExistingCSPC(name) + if err != nil { + return err + } err = c.migrate(name) return err } @@ -133,12 +146,12 @@ func (c *CSPCMigrator) migrate(spcName string) error { if err != nil { return errors.Wrapf(err, "failed to validate spc %s", spcName) } - err = c.updateBDCLabels(spcName) + err = c.updateBDCLabels() if err != nil { return errors.Wrapf(err, "failed to update bdc labels for spc %s", spcName) } - klog.Infof("Creating equivalent cspc for spc %s", spcName) - c.CSPCObj, err = c.generateCSPC() + klog.Infof("Creating equivalent cspc %s for spc %s", c.CSPCName, spcName) + c.CSPCObj, err = c.generateCSPC(spcName) if err != nil { return err } @@ -178,6 +191,63 @@ func (c *CSPCMigrator) migrate(spcName string) error { return nil } +// checkForExistingCSPC verifies the migration as follows: +// spc = getSPC +// 1. if spc does not exist +// return nil (getSPCWithMigrationStatus will handle it) +// cspc = getCSPC(from flag or same as spc) +// 2. if cspc exist && +// a. spc has anno with cspc-name && cspc has migration anno with spc-name +// return nil +// b. else +// return err cspc already exists +// 3. if cspc does not exist && +// a. spc has no anno +// patch spc with anno +// b. spc has diff anno than current cspc-name +// return err +func (c *CSPCMigrator) checkForExistingCSPC(spcName string) error { + spcObj, err := spc.NewKubeClient().Get(spcName, metav1.GetOptions{}) + if err != nil && !k8serrors.IsNotFound(err) { + return err + } + if k8serrors.IsNotFound(err) { + return nil + } + cspc, err := c.OpenebsClientset.CstorV1(). + CStorPoolClusters(c.OpenebsNamespace). + Get(c.CSPCName, metav1.GetOptions{}) + if err != nil && !k8serrors.IsNotFound(err) { + return err + } + if err == nil { + if spcObj.Annotations[types.CStorPoolClusterLabelKey] == c.CSPCName && + cspc.Annotations["openebs.io/migrated-from"] == spcName { + return nil + } + return errors.Errorf( + "failed to validate migration: the spc %s is set to be renamed as %s, but got cspc-name %s instead", + spcName, + spcObj.Annotations[types.CStorPoolClusterLabelKey], + c.CSPCName, + ) + } + if k8serrors.IsNotFound(err) { + if spcObj.Annotations == nil || spcObj.Annotations[types.CStorPoolClusterLabelKey] == "" { + return addCSPCAnnotationToSPC(spcObj, c.CSPCName) + } + if spcObj.Annotations[types.CStorPoolClusterLabelKey] != c.CSPCName { + return errors.Errorf( + "failed to validate migration: the spc %s is set to be renamed as %s, but got cspc-name %s instead", + spcName, + spcObj.Annotations[types.CStorPoolClusterLabelKey], + c.CSPCName, + ) + } + } + return nil +} + // validateSPC determines that if the spc is allowed to migrate or not. // If the max pool count does not match the number of csp in case auto spc provisioning, // or the blocldevice list in spc does not match bds from the csp, in case of manual provisioning @@ -232,9 +302,9 @@ func (c *CSPCMigrator) getSPCWithMigrationStatus(spcName string) (*apis.StorageP if k8serrors.IsNotFound(err) { klog.Infof("spc %s not found.", spcName) _, err = c.OpenebsClientset.CstorV1(). - CStorPoolClusters(c.OpenebsNamespace).Get(spcName, metav1.GetOptions{}) + CStorPoolClusters(c.OpenebsNamespace).Get(c.CSPCName, metav1.GetOptions{}) if err != nil { - return nil, false, errors.Wrapf(err, "failed to get equivalent cspc for spc %s", spcName) + return nil, false, errors.Wrapf(err, "failed to get equivalent cspc %s for spc %s", c.CSPCName, spcName) } return nil, true, nil } @@ -248,7 +318,7 @@ func (c *CSPCMigrator) getSPCWithMigrationStatus(spcName string) (*apis.StorageP func (c *CSPCMigrator) cspTocspi(cspiObj *cstor.CStorPoolInstance) error { var err1 error hostnameLabel := types.HostNameLabelKey + "=" + cspiObj.Labels[types.HostNameLabelKey] - spcLabel := string(apis.StoragePoolClaimCPK) + "=" + c.CSPCObj.Name + spcLabel := string(apis.StoragePoolClaimCPK) + "=" + c.SPCObj.Name cspLabel := hostnameLabel + "," + spcLabel cspObj, err := getCSP(cspLabel) if err != nil { @@ -374,9 +444,9 @@ func (c *CSPCMigrator) scaleDownDeployment(cspObj *apis.CStorPool, openebsNamesp // Update the bdc with the cspc labels instead of spc labels to allow // filtering of bds claimed by the migrated cspc. -func (c *CSPCMigrator) updateBDCLabels(cspcName string) error { +func (c *CSPCMigrator) updateBDCLabels() error { bdcList, err := c.OpenebsClientset.OpenebsV1alpha1().BlockDeviceClaims(c.OpenebsNamespace).List(metav1.ListOptions{ - LabelSelector: string(apis.StoragePoolClaimCPK) + "=" + cspcName, + LabelSelector: string(apis.StoragePoolClaimCPK) + "=" + c.SPCObj.Name, }) if err != nil { return err @@ -387,7 +457,7 @@ func (c *CSPCMigrator) updateBDCLabels(cspcName string) error { bdcObj := &bdcItem klog.Infof("Updating bdc %s with cspc labels & finalizer.", bdcObj.Name) delete(bdcObj.Labels, string(apis.StoragePoolClaimCPK)) - bdcObj.Labels[types.CStorPoolClusterLabelKey] = cspcName + bdcObj.Labels[types.CStorPoolClusterLabelKey] = c.CSPCName for i, finalizer := range bdcObj.Finalizers { if finalizer == spcFinalizer { bdcObj.Finalizers[i] = cspcFinalizer @@ -466,9 +536,10 @@ func (c *CSPCMigrator) updateCVRsLabels(cspObj *apis.CStorPool, cspiObj *cstor.C func addSkipAnnotationToSPC(spcObj *apis.StoragePoolClaim) error { retry: - spcObj.Annotations = map[string]string{ - "openebs.io/skip-validations": "true", + if spcObj.Annotations == nil { + spcObj.Annotations = map[string]string{} } + spcObj.Annotations["openebs.io/skip-validations"] = "true" _, err := spc.NewKubeClient().Update(spcObj) if k8serrors.IsConflict(err) { klog.Errorf("failed to update spc with skip-validation annotation due to conflict error") @@ -476,3 +547,17 @@ retry: } return err } + +func addCSPCAnnotationToSPC(spcObj *apis.StoragePoolClaim, cspcName string) error { +retry: + if spcObj.Annotations == nil { + spcObj.Annotations = map[string]string{} + } + spcObj.Annotations[types.CStorPoolClusterLabelKey] = cspcName + _, err := spc.NewKubeClient().Update(spcObj) + if k8serrors.IsConflict(err) { + klog.Errorf("failed to update spc with cspc annotation due to conflict error") + goto retry + } + return err +} diff --git a/pkg/migrate/cstor/snapshot.go b/pkg/migrate/cstor/snapshot.go index a1915a95..d77ae2fd 100644 --- a/pkg/migrate/cstor/snapshot.go +++ b/pkg/migrate/cstor/snapshot.go @@ -57,11 +57,6 @@ func (s *SnapshotMigrator) migrate(pvName string) error { } func (s *SnapshotMigrator) migrateSnapshots() error { - _, err := s.snapClient.SnapshotV1beta1().VolumeSnapshotClasses(). - Get(snapClass, metav1.GetOptions{}) - if err != nil { - return errors.Wrapf(err, "failed to get snapshotclass %s", snapClass) - } snapshotList, err := snap.NewKubeClient(). WithNamespace(""). List(metav1.ListOptions{ @@ -70,6 +65,14 @@ func (s *SnapshotMigrator) migrateSnapshots() error { if err != nil { return err } + if len(snapshotList.Items) == 0 { + return nil + } + _, err = s.snapClient.SnapshotV1beta1().VolumeSnapshotClasses(). + Get(snapClass, metav1.GetOptions{}) + if err != nil { + return errors.Wrapf(err, "failed to get snapshotclass %s", snapClass) + } for _, snapshot := range snapshotList.Items { snapshot := snapshot // pin it if len(snapshot.Spec.SnapshotDataName) == 0 {