mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-27 18:34:18 +00:00
Customize the tolerations of maintenance job. (#10553)
Run the E2E test on kind / setup-test-matrix (push) Failing after 3s
Scorecard supply-chain security / Scorecard analysis (push) Skipped
e2e-test-kind.yaml / extract (push) Failing after 8s
Run the E2E test on kind / get-go-version (push) Failing after 9s
Run the E2E test on kind / build (push) Skipped
Run the E2E test on kind / run-e2e-test (push) Skipped
push.yml / extract (push) Failing after 6s
Main CI / get-go-version (push) Failing after 7s
Main CI / Build (push) Skipped
Run the E2E test on kind / setup-test-matrix (push) Failing after 3s
Scorecard supply-chain security / Scorecard analysis (push) Skipped
e2e-test-kind.yaml / extract (push) Failing after 8s
Run the E2E test on kind / get-go-version (push) Failing after 9s
Run the E2E test on kind / build (push) Skipped
Run the E2E test on kind / run-e2e-test (push) Skipped
push.yml / extract (push) Failing after 6s
Main CI / get-go-version (push) Failing after 7s
Main CI / Build (push) Skipped
Signed-off-by: Xun Jiang <xun.jiang@broadcom.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
Add configurable tolerations to the repository maintenance job ConfigMap (global section); deployment tolerations matching the third-party allowlist (kubernetes.azure.com/scalesetpriority, CriticalAddonsOnly) and Windows toleration continue to be inherited and merged with configured tolerations.
|
||||
@@ -257,6 +257,9 @@ func getJobConfig(
|
||||
repoMaintenanceJobConfig,
|
||||
repoJobConfigKey)
|
||||
}
|
||||
|
||||
// Tolerations are only read from global config, not per-repository
|
||||
result.Tolerations = nil
|
||||
}
|
||||
|
||||
if _, ok := cm.Data[GlobalKeyForRepoMaintenanceJobCM]; ok {
|
||||
@@ -302,6 +305,11 @@ func getJobConfig(
|
||||
if len(globalResult.PodAnnotations) > 0 {
|
||||
result.PodAnnotations = globalResult.PodAnnotations
|
||||
}
|
||||
|
||||
// Tolerations are only read from global config, not per-repository
|
||||
if len(globalResult.Tolerations) > 0 {
|
||||
result.Tolerations = globalResult.Tolerations
|
||||
}
|
||||
}
|
||||
|
||||
logger.Debugf("Configuration content for repository %s is %+v", repo.Name, result)
|
||||
@@ -481,24 +489,45 @@ func StartNewJob(
|
||||
return maintenanceJob.Name, nil
|
||||
}
|
||||
|
||||
// buildTolerationsForMaintenanceJob builds the tolerations for maintenance jobs.
|
||||
// It includes the required Windows toleration for backward compatibility and
|
||||
// inherits all tolerations from the Velero deployment.
|
||||
func buildTolerationsForMaintenanceJob(deployment *appsv1api.Deployment) []corev1api.Toleration {
|
||||
// Start with the Windows toleration for backward compatibility
|
||||
// buildTolerationsForMaintenanceJob builds the tolerations for maintenance jobs:
|
||||
// the explicitly configured tolerations (sourced from the maintenance job ConfigMap),
|
||||
// plus the required Windows toleration for backward compatibility, plus any toleration
|
||||
// on the Velero deployment whose key is in util.ThirdPartyTolerations. The combined
|
||||
// list is deduplicated by kube.DeduplicateTolerations.
|
||||
//
|
||||
// configuredTolerations is appended first so it wins: DeduplicateTolerations
|
||||
// keeps only the first occurrence of each exact (Key, Operator, Value, Effect) combination,
|
||||
// so an allowlisted deployment toleration or default Windows toleration identical to one
|
||||
// already set in the ConfigMap is dropped as a duplicate rather than overriding it.
|
||||
func buildTolerationsForMaintenanceJob(deployment *appsv1api.Deployment, configuredTolerations []corev1api.Toleration) []corev1api.Toleration {
|
||||
windowsToleration := corev1api.Toleration{
|
||||
Key: "os",
|
||||
Operator: "Equal",
|
||||
Effect: "NoSchedule",
|
||||
Value: "windows",
|
||||
}
|
||||
result := []corev1api.Toleration{windowsToleration}
|
||||
|
||||
// Inherit all tolerations from the Velero deployment
|
||||
deploymentTolerations := veleroutil.GetTolerationsFromVeleroServer(deployment)
|
||||
result = append(result, deploymentTolerations...)
|
||||
var deploymentTolerations []corev1api.Toleration
|
||||
if deployment != nil {
|
||||
deploymentTolerations = veleroutil.GetTolerationsFromVeleroServer(deployment)
|
||||
}
|
||||
|
||||
return result
|
||||
merged := make([]corev1api.Toleration, 0, len(configuredTolerations)+1+len(deploymentTolerations))
|
||||
merged = append(merged, configuredTolerations...)
|
||||
merged = append(merged, windowsToleration)
|
||||
|
||||
allowedTolerations := make(map[string]struct{}, len(util.ThirdPartyTolerations))
|
||||
for _, allowed := range util.ThirdPartyTolerations {
|
||||
allowedTolerations[allowed] = struct{}{}
|
||||
}
|
||||
|
||||
for _, t := range deploymentTolerations {
|
||||
if _, ok := allowedTolerations[t.Key]; ok {
|
||||
merged = append(merged, t)
|
||||
}
|
||||
}
|
||||
|
||||
return kube.DeduplicateTolerations(merged)
|
||||
}
|
||||
|
||||
func getPriorityClassName(ctx context.Context, cli client.Client, config *velerotypes.JobConfigs, logger logrus.FieldLogger) string {
|
||||
@@ -649,6 +678,11 @@ func buildJob(
|
||||
args = append(args, fmt.Sprintf("--log-level=%s", logLevel.String()))
|
||||
args = append(args, fmt.Sprintf("--log-format=%s", logFormat.String()))
|
||||
|
||||
var configuredTolerations []corev1api.Toleration
|
||||
if config != nil && len(config.Tolerations) > 0 {
|
||||
configuredTolerations = config.Tolerations
|
||||
}
|
||||
|
||||
// build the maintenance job
|
||||
job := &batchv1api.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -689,7 +723,7 @@ func buildJob(
|
||||
SecurityContext: podSecurityContext,
|
||||
Volumes: volumes,
|
||||
ServiceAccountName: serviceAccount,
|
||||
Tolerations: buildTolerationsForMaintenanceJob(deployment),
|
||||
Tolerations: buildTolerationsForMaintenanceJob(deployment, configuredTolerations),
|
||||
ImagePullSecrets: imagePullSecrets,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -546,8 +546,8 @@ func TestGetJobConfig(t *testing.T) {
|
||||
Name: repoMaintenanceJobConfig,
|
||||
},
|
||||
Data: map[string]string{
|
||||
GlobalKeyForRepoMaintenanceJobCM: "{\"keepLatestMaintenanceJobs\":1,\"podResources\":{\"cpuRequest\":\"50m\",\"cpuLimit\":\"100m\",\"memoryRequest\":\"50Mi\",\"memoryLimit\":\"100Mi\"},\"loadAffinity\":[{\"nodeSelector\":{\"matchExpressions\":[{\"key\":\"cloud.google.com/machine-family\",\"operator\":\"In\",\"values\":[\"n2\"]}]}}],\"priorityClassName\":\"global-priority\",\"podAnnotations\":{\"global-key\":\"global-value\"},\"podLabels\":{\"global-key\":\"global-value\"}}",
|
||||
"test-default-kopia": "{\"podResources\":{\"cpuRequest\":\"100m\",\"cpuLimit\":\"200m\",\"memoryRequest\":\"100Mi\",\"memoryLimit\":\"200Mi\"},\"loadAffinity\":[{\"nodeSelector\":{\"matchExpressions\":[{\"key\":\"cloud.google.com/machine-family\",\"operator\":\"In\",\"values\":[\"e2\"]}]}}],\"priorityClassName\":\"specific-priority\",\"podAnnotations\":{\"specific-key\":\"specific-value\"},\"podLabels\":{\"specific-key\":\"specific-value\"}}",
|
||||
GlobalKeyForRepoMaintenanceJobCM: "{\"keepLatestMaintenanceJobs\":1,\"podResources\":{\"cpuRequest\":\"50m\",\"cpuLimit\":\"100m\",\"memoryRequest\":\"50Mi\",\"memoryLimit\":\"100Mi\"},\"loadAffinity\":[{\"nodeSelector\":{\"matchExpressions\":[{\"key\":\"cloud.google.com/machine-family\",\"operator\":\"In\",\"values\":[\"n2\"]}]}}],\"priorityClassName\":\"global-priority\",\"podAnnotations\":{\"global-key\":\"global-value\"},\"podLabels\":{\"global-key\":\"global-value\"},\"tolerations\":[{\"key\":\"global-taint\",\"operator\":\"Exists\",\"effect\":\"NoSchedule\"}]}",
|
||||
"test-default-kopia": "{\"podResources\":{\"cpuRequest\":\"100m\",\"cpuLimit\":\"200m\",\"memoryRequest\":\"100Mi\",\"memoryLimit\":\"200Mi\"},\"loadAffinity\":[{\"nodeSelector\":{\"matchExpressions\":[{\"key\":\"cloud.google.com/machine-family\",\"operator\":\"In\",\"values\":[\"e2\"]}]}}],\"priorityClassName\":\"specific-priority\",\"podAnnotations\":{\"specific-key\":\"specific-value\"},\"podLabels\":{\"specific-key\":\"specific-value\"},\"tolerations\":[{\"key\":\"specific-taint\",\"operator\":\"Equal\",\"value\":\"dedicated\",\"effect\":\"NoSchedule\"}]}",
|
||||
},
|
||||
},
|
||||
expectedConfig: &velerotypes.JobConfigs{
|
||||
@@ -574,6 +574,79 @@ func TestGetJobConfig(t *testing.T) {
|
||||
PriorityClassName: "global-priority",
|
||||
PodAnnotations: map[string]string{"global-key": "global-value"},
|
||||
PodLabels: map[string]string{"global-key": "global-value"},
|
||||
Tolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "global-taint",
|
||||
Operator: corev1api.TolerationOpExists,
|
||||
Effect: corev1api.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Find config with tolerations in global section",
|
||||
repoJobConfig: &corev1api.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: veleroNamespace,
|
||||
Name: repoMaintenanceJobConfig,
|
||||
},
|
||||
Data: map[string]string{
|
||||
GlobalKeyForRepoMaintenanceJobCM: "{\"tolerations\":[{\"key\":\"global-taint\",\"operator\":\"Exists\",\"effect\":\"NoSchedule\"}]}",
|
||||
},
|
||||
},
|
||||
expectedConfig: &velerotypes.JobConfigs{
|
||||
Tolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "global-taint",
|
||||
Operator: corev1api.TolerationOpExists,
|
||||
Effect: corev1api.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Tolerations in specific config should be ignored",
|
||||
repoJobConfig: &corev1api.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: veleroNamespace,
|
||||
Name: repoMaintenanceJobConfig,
|
||||
},
|
||||
Data: map[string]string{
|
||||
"test-default-kopia": "{\"podResources\":{\"cpuRequest\":\"100m\"},\"tolerations\":[{\"key\":\"specific-taint\",\"operator\":\"Equal\",\"value\":\"dedicated\",\"effect\":\"NoSchedule\"}]}",
|
||||
},
|
||||
},
|
||||
expectedConfig: &velerotypes.JobConfigs{
|
||||
PodResources: &kube.PodResources{
|
||||
CPURequest: "100m",
|
||||
},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Global tolerations apply when specific config does not specify global configs",
|
||||
repoJobConfig: &corev1api.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: veleroNamespace,
|
||||
Name: repoMaintenanceJobConfig,
|
||||
},
|
||||
Data: map[string]string{
|
||||
GlobalKeyForRepoMaintenanceJobCM: "{\"tolerations\":[{\"key\":\"global-taint\",\"operator\":\"Exists\",\"effect\":\"NoSchedule\"}]}",
|
||||
"test-default-kopia": "{\"podResources\":{\"cpuRequest\":\"100m\"}}",
|
||||
},
|
||||
},
|
||||
expectedConfig: &velerotypes.JobConfigs{
|
||||
PodResources: &kube.PodResources{
|
||||
CPURequest: "100m",
|
||||
},
|
||||
Tolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "global-taint",
|
||||
Operator: corev1api.TolerationOpExists,
|
||||
Effect: corev1api.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
@@ -1937,11 +2010,13 @@ func TestBuildTolerationsForMaintenanceJob(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
deploymentTolerations []corev1api.Toleration
|
||||
configuredTolerations []corev1api.Toleration
|
||||
expectedTolerations []corev1api.Toleration
|
||||
}{
|
||||
{
|
||||
name: "no tolerations should only include Windows toleration",
|
||||
deploymentTolerations: nil,
|
||||
configuredTolerations: nil,
|
||||
expectedTolerations: []corev1api.Toleration{
|
||||
windowsToleration,
|
||||
},
|
||||
@@ -1949,12 +2024,59 @@ func TestBuildTolerationsForMaintenanceJob(t *testing.T) {
|
||||
{
|
||||
name: "empty tolerations should only include Windows toleration",
|
||||
deploymentTolerations: []corev1api.Toleration{},
|
||||
configuredTolerations: []corev1api.Toleration{},
|
||||
expectedTolerations: []corev1api.Toleration{
|
||||
windowsToleration,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "all tolerations should be inherited",
|
||||
name: "non-allowed deployment toleration should not be inherited",
|
||||
deploymentTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "vng-ondemand",
|
||||
Operator: "Equal",
|
||||
Effect: "NoSchedule",
|
||||
Value: "amd64",
|
||||
},
|
||||
},
|
||||
configuredTolerations: nil,
|
||||
expectedTolerations: []corev1api.Toleration{
|
||||
windowsToleration,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "allowed deployment tolerations should be inherited",
|
||||
deploymentTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "kubernetes.azure.com/scalesetpriority",
|
||||
Operator: "Equal",
|
||||
Effect: "NoSchedule",
|
||||
Value: "spot",
|
||||
},
|
||||
{
|
||||
Key: "CriticalAddonsOnly",
|
||||
Operator: "Exists",
|
||||
Effect: "NoSchedule",
|
||||
},
|
||||
},
|
||||
configuredTolerations: nil,
|
||||
expectedTolerations: []corev1api.Toleration{
|
||||
windowsToleration,
|
||||
{
|
||||
Key: "kubernetes.azure.com/scalesetpriority",
|
||||
Operator: "Equal",
|
||||
Effect: "NoSchedule",
|
||||
Value: "spot",
|
||||
},
|
||||
{
|
||||
Key: "CriticalAddonsOnly",
|
||||
Operator: "Exists",
|
||||
Effect: "NoSchedule",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mixed allowed and non-allowed deployment tolerations should only inherit allowed",
|
||||
deploymentTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "vng-ondemand",
|
||||
@@ -1974,43 +2096,155 @@ func TestBuildTolerationsForMaintenanceJob(t *testing.T) {
|
||||
Value: "custom-value",
|
||||
},
|
||||
},
|
||||
configuredTolerations: nil,
|
||||
expectedTolerations: []corev1api.Toleration{
|
||||
windowsToleration,
|
||||
{
|
||||
Key: "vng-ondemand",
|
||||
Key: "CriticalAddonsOnly",
|
||||
Operator: "Exists",
|
||||
Effect: "NoSchedule",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "configured tolerations should be included along with Windows toleration",
|
||||
deploymentTolerations: nil,
|
||||
configuredTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "dedicated",
|
||||
Operator: "Equal",
|
||||
Effect: "NoSchedule",
|
||||
Value: "amd64",
|
||||
Value: "backup",
|
||||
},
|
||||
},
|
||||
expectedTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "dedicated",
|
||||
Operator: "Equal",
|
||||
Effect: "NoSchedule",
|
||||
Value: "backup",
|
||||
},
|
||||
windowsToleration,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "configured tolerations merged with allowed deployment tolerations and Windows toleration",
|
||||
deploymentTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "CriticalAddonsOnly",
|
||||
Operator: "Exists",
|
||||
Effect: "NoSchedule",
|
||||
},
|
||||
{
|
||||
Key: "custom-key",
|
||||
Operator: "Equal",
|
||||
Effect: "NoExecute",
|
||||
Value: "custom-value",
|
||||
Key: "unallowed-key",
|
||||
Operator: "Exists",
|
||||
Effect: "NoSchedule",
|
||||
},
|
||||
},
|
||||
configuredTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "karpenter.sh/nodepool",
|
||||
Operator: "Equal",
|
||||
Effect: "NoSchedule",
|
||||
Value: "maintenance",
|
||||
},
|
||||
},
|
||||
expectedTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "karpenter.sh/nodepool",
|
||||
Operator: "Equal",
|
||||
Effect: "NoSchedule",
|
||||
Value: "maintenance",
|
||||
},
|
||||
windowsToleration,
|
||||
{
|
||||
Key: "CriticalAddonsOnly",
|
||||
Operator: "Exists",
|
||||
Effect: "NoSchedule",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "duplicate tolerations in configured list should be deduplicated",
|
||||
deploymentTolerations: nil,
|
||||
configuredTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "dedicated",
|
||||
Operator: "Equal",
|
||||
Effect: "NoSchedule",
|
||||
Value: "backup",
|
||||
},
|
||||
{
|
||||
Key: "dedicated",
|
||||
Operator: "Equal",
|
||||
Effect: "NoSchedule",
|
||||
Value: "backup",
|
||||
},
|
||||
},
|
||||
expectedTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "dedicated",
|
||||
Operator: "Equal",
|
||||
Effect: "NoSchedule",
|
||||
Value: "backup",
|
||||
},
|
||||
windowsToleration,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "configured toleration duplicate of allowed deployment toleration keeps configured",
|
||||
deploymentTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "CriticalAddonsOnly",
|
||||
Operator: "Exists",
|
||||
Effect: "NoSchedule",
|
||||
},
|
||||
},
|
||||
configuredTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "CriticalAddonsOnly",
|
||||
Operator: "Exists",
|
||||
Effect: "NoSchedule",
|
||||
},
|
||||
},
|
||||
expectedTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "CriticalAddonsOnly",
|
||||
Operator: "Exists",
|
||||
Effect: "NoSchedule",
|
||||
},
|
||||
windowsToleration,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "configured toleration duplicate of Windows toleration keeps configured",
|
||||
deploymentTolerations: nil,
|
||||
configuredTolerations: []corev1api.Toleration{
|
||||
windowsToleration,
|
||||
},
|
||||
expectedTolerations: []corev1api.Toleration{
|
||||
windowsToleration,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Create a deployment with the specified tolerations
|
||||
deployment := &appsv1api.Deployment{
|
||||
Spec: appsv1api.DeploymentSpec{
|
||||
Template: corev1api.PodTemplateSpec{
|
||||
Spec: corev1api.PodSpec{
|
||||
Tolerations: tc.deploymentTolerations,
|
||||
var deployment *appsv1api.Deployment
|
||||
if tc.deploymentTolerations != nil {
|
||||
deployment = &appsv1api.Deployment{
|
||||
Spec: appsv1api.DeploymentSpec{
|
||||
Template: corev1api.PodTemplateSpec{
|
||||
Spec: corev1api.PodSpec{
|
||||
Tolerations: tc.deploymentTolerations,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
result := buildTolerationsForMaintenanceJob(deployment)
|
||||
result := buildTolerationsForMaintenanceJob(deployment, tc.configuredTolerations)
|
||||
assert.Equal(t, tc.expectedTolerations, result)
|
||||
})
|
||||
}
|
||||
@@ -2028,6 +2262,7 @@ func TestBuildJobWithTolerationsInheritance(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
deploymentTolerations []corev1api.Toleration
|
||||
jobConfig *velerotypes.JobConfigs
|
||||
expectedTolerations []corev1api.Toleration
|
||||
}{
|
||||
{
|
||||
@@ -2038,7 +2273,7 @@ func TestBuildJobWithTolerationsInheritance(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "all tolerations should be inherited along with Windows toleration",
|
||||
name: "allowed tolerations should be inherited along with Windows toleration",
|
||||
deploymentTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "kubernetes.azure.com/scalesetpriority",
|
||||
@@ -2061,13 +2296,68 @@ func TestBuildJobWithTolerationsInheritance(t *testing.T) {
|
||||
Effect: "NoSchedule",
|
||||
Value: "spot",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "configured tolerations should be applied to job",
|
||||
deploymentTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "custom-taint",
|
||||
Operator: "Equal",
|
||||
Effect: "NoExecute",
|
||||
Value: "dedicated",
|
||||
Key: "CriticalAddonsOnly",
|
||||
Operator: "Exists",
|
||||
Effect: "NoSchedule",
|
||||
},
|
||||
},
|
||||
jobConfig: &velerotypes.JobConfigs{
|
||||
Tolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "karpenter.sh/nodepool",
|
||||
Operator: "Equal",
|
||||
Effect: "NoSchedule",
|
||||
Value: "maintenance",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "karpenter.sh/nodepool",
|
||||
Operator: "Equal",
|
||||
Effect: "NoSchedule",
|
||||
Value: "maintenance",
|
||||
},
|
||||
windowsToleration,
|
||||
{
|
||||
Key: "CriticalAddonsOnly",
|
||||
Operator: "Exists",
|
||||
Effect: "NoSchedule",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "duplicate tolerations between configured and deployment should be deduplicated",
|
||||
deploymentTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "CriticalAddonsOnly",
|
||||
Operator: "Exists",
|
||||
Effect: "NoSchedule",
|
||||
},
|
||||
},
|
||||
jobConfig: &velerotypes.JobConfigs{
|
||||
Tolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "CriticalAddonsOnly",
|
||||
Operator: "Exists",
|
||||
Effect: "NoSchedule",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedTolerations: []corev1api.Toleration{
|
||||
{
|
||||
Key: "CriticalAddonsOnly",
|
||||
Operator: "Exists",
|
||||
Effect: "NoSchedule",
|
||||
},
|
||||
windowsToleration,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2119,7 +2409,10 @@ func TestBuildJobWithTolerationsInheritance(t *testing.T) {
|
||||
client := fake.NewClientBuilder().WithScheme(localScheme).WithObjects(deployment).Build()
|
||||
|
||||
// Create minimal job configs and resources
|
||||
jobConfig := &velerotypes.JobConfigs{}
|
||||
jobConfig := tc.jobConfig
|
||||
if jobConfig == nil {
|
||||
jobConfig = &velerotypes.JobConfigs{}
|
||||
}
|
||||
logLevel := logrus.InfoLevel
|
||||
logFormat := logging.NewFormatFlag()
|
||||
logFormat.Set("text")
|
||||
|
||||
@@ -16,7 +16,11 @@ limitations under the License.
|
||||
|
||||
package types
|
||||
|
||||
import "github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
import (
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
)
|
||||
|
||||
type JobConfigs struct {
|
||||
// LoadAffinities is the config for repository maintenance job load affinity.
|
||||
@@ -39,4 +43,10 @@ type JobConfigs struct {
|
||||
// PodLabels are labels to be added to maintenance job pods.
|
||||
// Note: This is only read from the global configuration, not per-repository
|
||||
PodLabels map[string]string `json:"podLabels,omitempty"`
|
||||
|
||||
// Tolerations are tolerations to be added to repository maintenance job pods.
|
||||
// Note: This is only read from the global configuration, not per-repository
|
||||
// These are merged with (and deduplicated against) any Velero deployment tolerations
|
||||
// whose key is in the in-tree third-party toleration allowlist, and the default Windows toleration.
|
||||
Tolerations []corev1api.Toleration `json:"tolerations,omitempty"`
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ Status:
|
||||
- `Recent Maintenance` keeps the status of the recent 3 maintenance jobs, including its start time, result (succeeded/failed), completion time (if the maintenance job succeeded), or error message (if the maintenance failed)
|
||||
|
||||
### Others
|
||||
Maintenance jobs will inherit toleration, nodeSelector, service account, image, environment variables, cloud-credentials, priorityClassName etc. from Velero deployment.
|
||||
Maintenance jobs will inherit nodeSelector, service account, image, environment variables, cloud-credentials, priorityClassName etc. from Velero deployment.
|
||||
|
||||
For labels and annotations, maintenance jobs do NOT inherit all labels and annotations from the Velero deployment. Instead, they include:
|
||||
|
||||
@@ -170,13 +170,25 @@ For labels and annotations, maintenance jobs do NOT inherit all labels and annot
|
||||
* `velero.io/repo-name: <repository-name>` - automatically added to identify which repository they are maintaining
|
||||
* Only specific [third-party labels][4] from the Velero server deployment that are in the predefined list, currently limited to:
|
||||
* `azure.workload.identity/use`
|
||||
* Any custom labels explicitly configured in the repository maintenance job ConfigMap
|
||||
|
||||
**Annotations:**
|
||||
|
||||
* Only specific [third-party annotations][5] from the Velero server deployment that are in the predefined list, currently limited to:
|
||||
* `iam.amazonaws.com/role`
|
||||
* Any custom annotations explicitly configured in the repository maintenance job ConfigMap
|
||||
|
||||
**Important:** Other labels and annotations from the Velero deployment are NOT inherited by maintenance jobs. This is by design to ensure only specific labels and annotations required for cloud provider identity systems are propagated.
|
||||
|
||||
**Tolerations:**
|
||||
|
||||
Similarly, maintenance jobs do NOT inherit all tolerations from the Velero deployment. Instead, they include:
|
||||
* The required Windows toleration (`os=windows:NoSchedule`) for backward compatibility
|
||||
* Only specific [third-party tolerations][6] from the Velero server deployment that are in the predefined list, currently limited to:
|
||||
* `kubernetes.azure.com/scalesetpriority`
|
||||
* `CriticalAddonsOnly`
|
||||
* Any custom tolerations explicitly configured in the repository maintenance job ConfigMap
|
||||
|
||||
Maintenance jobs will not run for backup repositories whose backup storage location is set as readOnly.
|
||||
|
||||
#### Priority Class Configuration
|
||||
@@ -196,8 +208,63 @@ Maintenance jobs can be configured with a specific priority class through the re
|
||||
|
||||
Note that priority class configuration is only read from the global configuration section, ensuring all maintenance jobs use the same priority class regardless of which repository they are maintaining.
|
||||
|
||||
#### Tolerations Configuration
|
||||
Maintenance jobs can be configured with customized tolerations through the repository maintenance job ConfigMap to allow scheduling on nodes with custom taints (such as dedicated node groups managed by Karpenter). The tolerations should be specified in the global configuration section:
|
||||
|
||||
```json
|
||||
{
|
||||
"global": {
|
||||
"tolerations": [
|
||||
{
|
||||
"key": "dedicated",
|
||||
"operator": "Equal",
|
||||
"value": "maintenance",
|
||||
"effect": "NoSchedule"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that tolerations configuration is only read from the global configuration section, ensuring all maintenance jobs use the same tolerations regardless of which repository they are maintaining.
|
||||
|
||||
Configured tolerations are merged with (and deduplicated against) the Windows toleration (`os=windows:NoSchedule`) and in-tree third-party tolerations inherited from the Velero deployment (`kubernetes.azure.com/scalesetpriority` and `CriticalAddonsOnly`).
|
||||
Deduplication applies only to exact duplicate tolerations with the same `key`, `operator`, `value`, and `effect`; configured tolerations do not override different inherited tolerations based only on matching keys or partial field matches.
|
||||
|
||||
#### Pod Labels and Annotations Configuration
|
||||
Maintenance jobs can be configured with customized labels (`podLabels`) and annotations (`podAnnotations`) through the repository maintenance job ConfigMap to support third-party integrations, monitoring, and environment-specific requirements.
|
||||
|
||||
Both labels and annotations must be specified in the `global` configuration section:
|
||||
|
||||
```json
|
||||
{
|
||||
"global": {
|
||||
"podLabels": {
|
||||
"environment": "production",
|
||||
"team": "storage"
|
||||
},
|
||||
"podAnnotations": {
|
||||
"vault.hashicorp.com/agent-inject": "true",
|
||||
"prometheus.io/scrape": "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that `podLabels` and `podAnnotations` are only read from the global configuration section, ensuring all maintenance jobs use the same labels and annotations regardless of which repository they are maintaining.
|
||||
|
||||
**Label Handling Rules:**
|
||||
* Velero automatically adds the `velero.io/repo-name: <repository-name>` label to identify the repository being maintained; this reserved label cannot be overridden by user configuration.
|
||||
* If `podLabels` is configured in the ConfigMap, user-provided labels are applied. Note that user-provided `podLabels` supersede the in-tree third-party labels from the Velero deployment. If you need both custom labels and in-tree third-party labels (e.g. `azure.workload.identity/use`), you must explicitly include them in the `podLabels` configuration.
|
||||
* If `podLabels` is not configured, Velero automatically inherits the allowed [third-party labels][4] from the Velero deployment.
|
||||
|
||||
**Annotation Handling Rules:**
|
||||
* If `podAnnotations` is configured in the ConfigMap, user-provided annotations are applied. Note that user-provided `podAnnotations` supersede the in-tree third-party annotations from the Velero deployment. If you need both custom annotations and in-tree third-party annotations (e.g. `iam.amazonaws.com/role`), you must explicitly include them in the `podAnnotations` configuration.
|
||||
* If `podAnnotations` is not configured, Velero automatically inherits the allowed [third-party annotations][5] from the Velero deployment.
|
||||
|
||||
[1]: velero-install.md#usage
|
||||
[2]: node-agent-concurrency.md
|
||||
[3]: backup-repository-configuration.md#full-maintenance-interval-customization
|
||||
[4]: https://github.com/velero-io/velero/blob/d5a2e7e6b9512e8ba52ec269ed5ce9a0fa23548c/pkg/util/third_party.go#L19-L21
|
||||
[5]: https://github.com/velero-io/velero/blob/d5a2e7e6b9512e8ba52ec269ed5ce9a0fa23548c/pkg/util/third_party.go#L23-L25
|
||||
[6]: https://github.com/velero-io/velero/blob/d5a2e7e6b9512e8ba52ec269ed5ce9a0fa23548c/pkg/util/third_party.go#L27-L30
|
||||
|
||||
@@ -504,7 +504,7 @@ Add customized tolerations for data mover pods to allow scheduling on nodes with
|
||||
|
||||
Unlike `podLabels`/`podAnnotations`, `tolerations` does **not** replace Velero's [in-tree third-party toleration allowlist](https://github.com/vmware-tanzu/velero/blob/main/pkg/util/third_party.go). Any toleration on the node-agent DaemonSet whose key is in that allowlist (currently `kubernetes.azure.com/scalesetpriority` and `CriticalAddonsOnly`) is always merged in alongside the tolerations configured here, with duplicates removed.
|
||||
|
||||
The configurations work for DataUpload, DataDownload, PodVolumeBackup, and PodVolumeRestore pods. This does not affect repository maintenance jobs, which inherit tolerations from the Velero Deployment directly.
|
||||
The configurations work for DataUpload, DataDownload, PodVolumeBackup, and PodVolumeRestore pods. This does not affect repository maintenance jobs; tolerations for maintenance jobs are configured separately via the repository maintenance job ConfigMap (see [Repository Maintenance](../repository-maintenance.md#tolerations-configuration)).
|
||||
|
||||
#### Configuration Example
|
||||
```json
|
||||
|
||||
Reference in New Issue
Block a user