diff --git a/apis/apps/v1alpha1/backuppolicytemplate_types.go b/apis/apps/v1alpha1/backuppolicytemplate_types.go index acd19f3f105..200e9fb5428 100644 --- a/apis/apps/v1alpha1/backuppolicytemplate_types.go +++ b/apis/apps/v1alpha1/backuppolicytemplate_types.go @@ -210,6 +210,12 @@ type TargetInstance struct { // the `strategy` field below. Role string `json:"role"` + // Specifies the fallback role to select one replica for backup, this only takes effect when the + // `strategy` field below is set to `Any`. + // + // +optional + FallbackRole string `json:"fallbackRole,omitempty"` + // If `backupPolicy.componentDefs` is set, this field is required to specify the system account name. // This account must match one listed in `componentDefinition.spec.systemAccounts[*].name`. // The corresponding secret created by this account is used to connect to the database. diff --git a/apis/dataprotection/v1alpha1/backuppolicy_types.go b/apis/dataprotection/v1alpha1/backuppolicy_types.go index 92527c5c17e..31fe87f49e7 100644 --- a/apis/dataprotection/v1alpha1/backuppolicy_types.go +++ b/apis/dataprotection/v1alpha1/backuppolicy_types.go @@ -111,6 +111,12 @@ type PodSelector struct { // labelsSelector is the label selector to filter the target pods. *metav1.LabelSelector `json:",inline"` + // fallbackLabelSelector is used to filter available pods when the labelSelector fails. + // This only takes effect when the `strategy` field below is set to `Any`. + // + // +optional + FallbackLabelSelector *metav1.LabelSelector `json:"fallbackLabelSelector,omitempty"` + // Specifies the strategy to select the target pod when multiple pods are selected. // Valid values are: // diff --git a/apis/dataprotection/v1alpha1/zz_generated.deepcopy.go b/apis/dataprotection/v1alpha1/zz_generated.deepcopy.go index 0279a22601b..0fb63af2b21 100644 --- a/apis/dataprotection/v1alpha1/zz_generated.deepcopy.go +++ b/apis/dataprotection/v1alpha1/zz_generated.deepcopy.go @@ -1162,6 +1162,11 @@ func (in *PodSelector) DeepCopyInto(out *PodSelector) { *out = new(metav1.LabelSelector) (*in).DeepCopyInto(*out) } + if in.FallbackLabelSelector != nil { + in, out := &in.FallbackLabelSelector, &out.FallbackLabelSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSelector. diff --git a/config/crd/bases/apps.kubeblocks.io_backuppolicytemplates.yaml b/config/crd/bases/apps.kubeblocks.io_backuppolicytemplates.yaml index 4f0ae6e1b58..d9fee4b254a 100644 --- a/config/crd/bases/apps.kubeblocks.io_backuppolicytemplates.yaml +++ b/config/crd/bases/apps.kubeblocks.io_backuppolicytemplates.yaml @@ -416,6 +416,11 @@ spec: If not specified, the default key "username" is used. type: string type: object + fallbackRole: + description: |- + Specifies the fallback role to select one replica for backup, this only takes effect when the + `strategy` field below is set to `Any`. + type: string name: description: |- Specifies a mandatory and unique identifier for each target when using the "targets" field. @@ -425,6 +430,53 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are @@ -687,6 +739,53 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are @@ -916,6 +1015,11 @@ spec: If not specified, the default key "username" is used. type: string type: object + fallbackRole: + description: |- + Specifies the fallback role to select one replica for backup, this only takes effect when the + `strategy` field below is set to `Any`. + type: string role: description: |- Specifies the role to select one or more replicas for backup. diff --git a/config/crd/bases/dataprotection.kubeblocks.io_backuppolicies.yaml b/config/crd/bases/dataprotection.kubeblocks.io_backuppolicies.yaml index ee2934a16f3..d7fa06edd4c 100644 --- a/config/crd/bases/dataprotection.kubeblocks.io_backuppolicies.yaml +++ b/config/crd/bases/dataprotection.kubeblocks.io_backuppolicies.yaml @@ -307,6 +307,52 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. @@ -538,6 +584,53 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. @@ -774,6 +867,52 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. @@ -948,6 +1087,52 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. diff --git a/config/crd/bases/dataprotection.kubeblocks.io_backups.yaml b/config/crd/bases/dataprotection.kubeblocks.io_backups.yaml index 2e140a05cc8..76b17beecad 100644 --- a/config/crd/bases/dataprotection.kubeblocks.io_backups.yaml +++ b/config/crd/bases/dataprotection.kubeblocks.io_backups.yaml @@ -501,6 +501,52 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. @@ -731,6 +777,52 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. @@ -1013,6 +1105,52 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. @@ -1189,6 +1327,52 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. diff --git a/config/crd/bases/dataprotection.kubeblocks.io_restores.yaml b/config/crd/bases/dataprotection.kubeblocks.io_restores.yaml index defd85fe5bd..bcd3ca3b0af 100644 --- a/config/crd/bases/dataprotection.kubeblocks.io_restores.yaml +++ b/config/crd/bases/dataprotection.kubeblocks.io_restores.yaml @@ -2042,6 +2042,53 @@ spec: Selects one of the pods, identified by labels, to build the job spec. This includes mounting required volumes and injecting built-in environment variables of the selected pod. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. diff --git a/controllers/apps/transformer_cluster_backup_policy.go b/controllers/apps/transformer_cluster_backup_policy.go index fff26d0d612..49ad32b562a 100644 --- a/controllers/apps/transformer_cluster_backup_policy.go +++ b/controllers/apps/transformer_cluster_backup_policy.go @@ -362,7 +362,7 @@ func (r *clusterBackupPolicyTransformer) syncBackupPolicy(comp componentItem, ba r.syncBackupPolicyTargetSpec(backupPolicy, comp) } -func (r *clusterBackupPolicyTransformer) syncRoleLabelSelector(comp componentItem, target *dpv1alpha1.BackupTarget, role string) { +func (r *clusterBackupPolicyTransformer) syncRoleLabelSelector(comp componentItem, target *dpv1alpha1.BackupTarget, role, alternateRole string) { if len(role) == 0 || target == nil { return } @@ -372,8 +372,17 @@ func (r *clusterBackupPolicyTransformer) syncRoleLabelSelector(comp componentIte } if r.getCompReplicas(comp) == 1 { delete(podSelector.LabelSelector.MatchLabels, constant.RoleLabelKey) - } else if podSelector.LabelSelector.MatchLabels[constant.RoleLabelKey] == "" { + if podSelector.FallbackLabelSelector != nil && podSelector.FallbackLabelSelector.MatchLabels != nil { + delete(podSelector.FallbackLabelSelector.MatchLabels, constant.RoleLabelKey) + } + } else { podSelector.LabelSelector.MatchLabels[constant.RoleLabelKey] = role + if len(alternateRole) > 0 { + if podSelector.FallbackLabelSelector == nil || podSelector.FallbackLabelSelector.MatchLabels == nil { + podSelector.FallbackLabelSelector = &metav1.LabelSelector{MatchLabels: map[string]string{}} + } + podSelector.FallbackLabelSelector.MatchLabels[constant.RoleLabelKey] = alternateRole + } } } @@ -489,7 +498,7 @@ func (r *clusterBackupPolicyTransformer) buildBackupTarget( ) *dpv1alpha1.BackupTarget { if oldTarget != nil { // if the target already exists, only sync the role by component replicas automatically. - r.syncRoleLabelSelector(comp, oldTarget, targetTpl.Role) + r.syncRoleLabelSelector(comp, oldTarget, targetTpl.Role, targetTpl.FallbackRole) return oldTarget } clusterName := r.OrigCluster.Name @@ -500,12 +509,17 @@ func (r *clusterBackupPolicyTransformer) buildBackupTarget( PodSelector: &dpv1alpha1.PodSelector{ Strategy: targetTpl.Strategy, LabelSelector: &metav1.LabelSelector{ - MatchLabels: r.buildTargetPodLabels(targetTpl, comp), + MatchLabels: r.buildTargetPodLabels(targetTpl.Role, comp), }, }, // dataprotection will use its dedicated service account if this field is empty. ServiceAccountName: "", } + if len(targetTpl.Role) != 0 && len(targetTpl.FallbackRole) != 0 { + target.PodSelector.FallbackLabelSelector = &metav1.LabelSelector{ + MatchLabels: r.buildTargetPodLabels(targetTpl.FallbackRole, comp), + } + } if comp.isSharding { target.Name = comp.fullComponentName } @@ -724,16 +738,16 @@ func (r *clusterBackupPolicyTransformer) compDefNameFromPolicy(policy *dpv1alpha // buildTargetPodLabels builds the target labels for the backup policy that will be // used to select the target pod. -func (r *clusterBackupPolicyTransformer) buildTargetPodLabels(targetTpl appsv1alpha1.TargetInstance, comp componentItem) map[string]string { +func (r *clusterBackupPolicyTransformer) buildTargetPodLabels(role string, comp componentItem) map[string]string { labels := map[string]string{ constant.AppInstanceLabelKey: r.OrigCluster.Name, constant.AppManagedByLabelKey: constant.AppName, constant.KBAppComponentLabelKey: comp.fullComponentName, } // append label to filter specific role of the component. - if len(targetTpl.Role) > 0 && r.getCompReplicas(comp) > 1 { + if len(role) > 0 && r.getCompReplicas(comp) > 1 { // the role only works when the component has multiple replicas. - labels[constant.RoleLabelKey] = targetTpl.Role + labels[constant.RoleLabelKey] = role } if comp.isSharding { labels[constant.KBAppShardingNameLabelKey] = comp.componentName diff --git a/controllers/dataprotection/backup_controller_test.go b/controllers/dataprotection/backup_controller_test.go index 3ff97117a17..9084cc590d5 100644 --- a/controllers/dataprotection/backup_controller_test.go +++ b/controllers/dataprotection/backup_controller_test.go @@ -20,6 +20,7 @@ along with this program. If not, see . package dataprotection import ( + "context" "fmt" "slices" "time" @@ -362,6 +363,78 @@ var _ = Describe("Backup Controller test", func() { })).Should(Succeed()) }) + It("create an backup using fallbackLabelSelector", func() { + podFactory := func(name string) *testapps.MockPodFactory { + return testapps.NewPodFactory(testCtx.DefaultNamespace, name). + AddAppInstanceLabel(testdp.ClusterName). + AddAppComponentLabel(testdp.ComponentName). + AddContainer(corev1.Container{Name: testdp.ContainerName, Image: testapps.ApeCloudMySQLImage}) + } + podName := "fallback" + testdp.ClusterName + "-" + testdp.ComponentName + By("mock a primary pod that is available ") + pod0 := podFactory(podName + "-0"). + AddRoleLabel("primary"). + Create(&testCtx).GetObject() + Expect(testapps.ChangeObjStatus(&testCtx, pod0, func() { + pod0.Status.Phase = corev1.PodRunning + testk8s.MockPodAvailable(pod0, metav1.Now()) + })).Should(Succeed()) + By("mock a secondary pod that is unavailable") + pod1 := podFactory(podName + "-1"). + AddRoleLabel("secondary"). + Create(&testCtx).GetObject() + Expect(testapps.ChangeObjStatus(&testCtx, pod1, func() { + pod1.Status.Phase = corev1.PodFailed + testk8s.MockPodIsFailed(context.Background(), testCtx, pod1) + })).Should(Succeed()) + + By("Set backupPolicy's target with fallbackLabelSelector") + Expect(testapps.ChangeObj(&testCtx, backupPolicy, func(bp *dpv1alpha1.BackupPolicy) { + podSelector := &dpv1alpha1.PodSelector{ + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + constant.AppInstanceLabelKey: testdp.ClusterName, + constant.KBAppComponentLabelKey: testdp.ComponentName, + constant.RoleLabelKey: "secondary", + }, + }, + FallbackLabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + constant.AppInstanceLabelKey: testdp.ClusterName, + constant.KBAppComponentLabelKey: testdp.ComponentName, + constant.RoleLabelKey: "primary", + }, + }, + Strategy: dpv1alpha1.PodSelectionStrategyAny, + } + backupPolicy.Spec.Target = &dpv1alpha1.BackupTarget{ + Name: testdp.ComponentName + "-0", PodSelector: podSelector, + } + })).Should(Succeed()) + + By("check targets pod") + target := backupPolicy.Spec.Target + reqCtx := intctrlutil.RequestCtx{Ctx: ctx} + targetPods, err := GetTargetPods(reqCtx, k8sClient, nil, backupPolicy, target, dpv1alpha1.BackupTypeFull) + Expect(err).ShouldNot(HaveOccurred()) + Expect(targetPods).Should(HaveLen(1)) + Expect(targetPods[0].Name).Should(Equal(pod0.Name)) + + By("create a backup") + backup := testdp.NewFakeBackup(&testCtx, nil) + getJobKey := func(targetName string) client.ObjectKey { + return client.ObjectKey{ + Name: dpbackup.GenerateBackupJobName(backup, fmt.Sprintf("%s-%s-0", dpbackup.BackupDataJobNamePrefix, targetName)), + Namespace: backup.Namespace, + } + } + By("mock backup jobs to completed and backup should be completed") + testdp.PatchK8sJobStatus(&testCtx, getJobKey(target.Name), batchv1.JobComplete) + Eventually(testapps.CheckObj(&testCtx, client.ObjectKeyFromObject(backup), func(g Gomega, fetched *dpv1alpha1.Backup) { + g.Expect(fetched.Status.Phase).To(Equal(dpv1alpha1.BackupPhaseCompleted)) + })).Should(Succeed()) + }) + Context("creates a backup with encryption", func() { const ( encryptionKeySecretName = "backup-encryption" diff --git a/controllers/dataprotection/utils.go b/controllers/dataprotection/utils.go index 061c271f402..f31462452a2 100644 --- a/controllers/dataprotection/utils.go +++ b/controllers/dataprotection/utils.go @@ -24,7 +24,6 @@ import ( "encoding/json" "fmt" "reflect" - "sort" "strings" "sync" @@ -149,59 +148,63 @@ func GetTargetPods(reqCtx intctrlutil.RequestCtx, if !existPodSelector(selector) { return nil, nil } - labelSelector, err := metav1.LabelSelectorAsSelector(selector.LabelSelector) - if err != nil { - return nil, err - } - pods := &corev1.PodList{} - if err = cli.List(reqCtx.Ctx, pods, - client.InNamespace(reqCtx.Req.Namespace), - client.MatchingLabelsSelector{Selector: labelSelector}); err != nil { - return nil, err - } - if len(pods.Items) == 0 { - return nil, fmt.Errorf("failed to find target pods by backup policy %s/%s", - backupPolicy.Namespace, backupPolicy.Name) - } - sort.Sort(intctrlutil.ByPodName(pods.Items)) - var targetPods []*corev1.Pod - if len(selectedPodNames) == 0 || backupType == dpv1alpha1.BackupTypeContinuous { - switch selector.Strategy { + filterTargetPods := func(strategy dpv1alpha1.PodSelectionStrategy, + labelSelector *metav1.LabelSelector) ([]*corev1.Pod, error) { + var targetPods []*corev1.Pod + pods, err := dputils.GetPodListByLabelSelector(reqCtx, cli, labelSelector) + if err != nil { + return nil, err + } + switch strategy { case dpv1alpha1.PodSelectionStrategyAny: - // always selecting the first pod - pod := dputils.GetFirstIndexRunningPod(pods) + var pod *corev1.Pod + if len(selectedPodNames) == 0 || backupType == dpv1alpha1.BackupTypeContinuous { + pod = dputils.GetFirstIndexRunningPod(pods) + } else { + // if already selected target pods and backupType is not Continuous, we should re-use them. + pod = dputils.GetPodByName(pods, selectedPodNames[0]) + } if pod != nil { targetPods = append(targetPods, pod) } case dpv1alpha1.PodSelectionStrategyAll: + if len(selectedPodNames) == 0 || backupType == dpv1alpha1.BackupTypeContinuous { + for i := range pods.Items { + targetPods = append(targetPods, &pods.Items[i]) + } + return targetPods, nil + } + // if already selected target pods and backupType is not Continuous, we should re-use them. + if len(pods.Items) == 0 { + return nil, fmt.Errorf("failed to find target pods by backup policy %s/%s", + backupPolicy.Namespace, backupPolicy.Name) + } + podMap := map[string]*corev1.Pod{} for i := range pods.Items { - targetPods = append(targetPods, &pods.Items[i]) + podMap[pods.Items[i].Name] = &pods.Items[i] + } + for _, podName := range selectedPodNames { + pod, ok := podMap[podName] + if !ok { + return nil, intctrlutil.NewFatalError(fmt.Sprintf(`can not found the target pod "%s"`, podName)) + } + targetPods = append(targetPods, pod) } } return targetPods, nil } - // if already selected target pods and backupType is not Continuous, we should re-use them. - switch selector.Strategy { - case dpv1alpha1.PodSelectionStrategyAny: - for _, pod := range pods.Items { - if pod.Name == selectedPodNames[0] { - targetPods = append(targetPods, &pod) - break - } - } - case dpv1alpha1.PodSelectionStrategyAll: - podMap := map[string]corev1.Pod{} - for i := range pods.Items { - podMap[pods.Items[i].Name] = pods.Items[i] - } - for _, podName := range selectedPodNames { - pod, ok := podMap[podName] - if !ok { - return nil, intctrlutil.NewFatalError(fmt.Sprintf(`can not found the target pod "%s"`, podName)) - } - targetPods = append(targetPods, &pod) - } + + targetPods, err := filterTargetPods(selector.Strategy, selector.LabelSelector) + if err != nil { + return nil, err + } + if selector.Strategy == dpv1alpha1.PodSelectionStrategyAll || targetPods != nil || + selector.FallbackLabelSelector == nil { + return targetPods, nil + } + if targetPods, err = filterTargetPods(selector.Strategy, selector.FallbackLabelSelector); err != nil { + return nil, err } return targetPods, nil } diff --git a/deploy/helm/crds/apps.kubeblocks.io_backuppolicytemplates.yaml b/deploy/helm/crds/apps.kubeblocks.io_backuppolicytemplates.yaml index 4f0ae6e1b58..d9fee4b254a 100644 --- a/deploy/helm/crds/apps.kubeblocks.io_backuppolicytemplates.yaml +++ b/deploy/helm/crds/apps.kubeblocks.io_backuppolicytemplates.yaml @@ -416,6 +416,11 @@ spec: If not specified, the default key "username" is used. type: string type: object + fallbackRole: + description: |- + Specifies the fallback role to select one replica for backup, this only takes effect when the + `strategy` field below is set to `Any`. + type: string name: description: |- Specifies a mandatory and unique identifier for each target when using the "targets" field. @@ -425,6 +430,53 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are @@ -687,6 +739,53 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are @@ -916,6 +1015,11 @@ spec: If not specified, the default key "username" is used. type: string type: object + fallbackRole: + description: |- + Specifies the fallback role to select one replica for backup, this only takes effect when the + `strategy` field below is set to `Any`. + type: string role: description: |- Specifies the role to select one or more replicas for backup. diff --git a/deploy/helm/crds/dataprotection.kubeblocks.io_backuppolicies.yaml b/deploy/helm/crds/dataprotection.kubeblocks.io_backuppolicies.yaml index ee2934a16f3..d7fa06edd4c 100644 --- a/deploy/helm/crds/dataprotection.kubeblocks.io_backuppolicies.yaml +++ b/deploy/helm/crds/dataprotection.kubeblocks.io_backuppolicies.yaml @@ -307,6 +307,52 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. @@ -538,6 +584,53 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. @@ -774,6 +867,52 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. @@ -948,6 +1087,52 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. diff --git a/deploy/helm/crds/dataprotection.kubeblocks.io_backups.yaml b/deploy/helm/crds/dataprotection.kubeblocks.io_backups.yaml index 2e140a05cc8..76b17beecad 100644 --- a/deploy/helm/crds/dataprotection.kubeblocks.io_backups.yaml +++ b/deploy/helm/crds/dataprotection.kubeblocks.io_backups.yaml @@ -501,6 +501,52 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. @@ -731,6 +777,52 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. @@ -1013,6 +1105,52 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. @@ -1189,6 +1327,52 @@ spec: description: Used to find the target pod. The volumes of the target pod will be backed up. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. diff --git a/deploy/helm/crds/dataprotection.kubeblocks.io_restores.yaml b/deploy/helm/crds/dataprotection.kubeblocks.io_restores.yaml index defd85fe5bd..bcd3ca3b0af 100644 --- a/deploy/helm/crds/dataprotection.kubeblocks.io_restores.yaml +++ b/deploy/helm/crds/dataprotection.kubeblocks.io_restores.yaml @@ -2042,6 +2042,53 @@ spec: Selects one of the pods, identified by labels, to build the job spec. This includes mounting required volumes and injecting built-in environment variables of the selected pod. properties: + fallbackLabelSelector: + description: |- + fallbackLabelSelector is used to filter available pods when the labelSelector fails. + This only takes effect when the `strategy` field below is set to `Any`. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. diff --git a/docs/developer_docs/api-reference/backup.md b/docs/developer_docs/api-reference/backup.md index 3997e475cf8..6ddc639bcb5 100644 --- a/docs/developer_docs/api-reference/backup.md +++ b/docs/developer_docs/api-reference/backup.md @@ -3900,6 +3900,21 @@ Kubernetes meta/v1.LabelSelector +fallbackLabelSelector
+ + +Kubernetes meta/v1.LabelSelector + + + + +(Optional) +

fallbackLabelSelector is used to filter available pods when the labelSelector fails. +This only takes effect when the strategy field below is set to Any.

+ + + + strategy
diff --git a/docs/developer_docs/api-reference/cluster.md b/docs/developer_docs/api-reference/cluster.md index c228091ece4..8f12caf9e90 100644 --- a/docs/developer_docs/api-reference/cluster.md +++ b/docs/developer_docs/api-reference/cluster.md @@ -18879,6 +18879,19 @@ the strategy field below. +fallbackRole
+ +string + + + +(Optional) +

Specifies the fallback role to select one replica for backup, this only takes effect when the +strategy field below is set to Any.

+ + + + account
string diff --git a/pkg/dataprotection/restore/manager.go b/pkg/dataprotection/restore/manager.go index 931c4722f1f..c1c9aa58879 100644 --- a/pkg/dataprotection/restore/manager.go +++ b/pkg/dataprotection/restore/manager.go @@ -537,7 +537,7 @@ func (r *RestoreManager) BuildPostReadyActionJobs(reqCtx intctrlutil.RequestCtx, } actionSpec := backupSet.ActionSet.Spec.Restore.PostReady[step] getTargetPodList := func(labelSelector metav1.LabelSelector, msgKey string) (*corev1.PodList, error) { - targetPodList, err := utils.GetPodListByLabelSelector(reqCtx, cli, labelSelector) + targetPodList, err := utils.GetPodListByLabelSelector(reqCtx, cli, &labelSelector) if err != nil { return nil, err } diff --git a/pkg/dataprotection/utils/utils.go b/pkg/dataprotection/utils/utils.go index 667827eb378..2bbabdc3ba2 100644 --- a/pkg/dataprotection/utils/utils.go +++ b/pkg/dataprotection/utils/utils.go @@ -144,8 +144,8 @@ func GetBackupMethodByName(name string, backupPolicy *dpv1alpha1.BackupPolicy) * func GetPodListByLabelSelector(reqCtx intctrlutil.RequestCtx, cli client.Client, - labelSelector metav1.LabelSelector) (*corev1.PodList, error) { - selector, err := metav1.LabelSelectorAsSelector(&labelSelector) + labelSelector *metav1.LabelSelector) (*corev1.PodList, error) { + selector, err := metav1.LabelSelectorAsSelector(labelSelector) if err != nil { return nil, err } @@ -289,6 +289,18 @@ func GetFirstIndexRunningPod(podList *corev1.PodList) *corev1.Pod { return nil } +func GetPodByName(podList *corev1.PodList, name string) *corev1.Pod { + if podList == nil { + return nil + } + for i, v := range podList.Items { + if v.Name == name { + return &podList.Items[i] + } + } + return nil +} + // GetKubeVersion get the version of Kubernetes and return the gitVersion func GetKubeVersion() (string, error) { verInfo := viper.Get(constant.CfgKeyServerInfo)