diff --git a/api/v1alpha1/oadp_types.go b/api/v1alpha1/oadp_types.go index c5015d1182..e629ef1c47 100644 --- a/api/v1alpha1/oadp_types.go +++ b/api/v1alpha1/oadp_types.go @@ -109,6 +109,12 @@ type VeleroConfig struct { // Default is 10m // +optional ResourceTimeout string `json:"resourceTimeout,omitempty"` + // maximum number of requests by the server to the Kubernetes API in a short period of time. (default 100) + // +optional + ClientBurst *int `json:"client-burst,omitempty"` + // maximum number of requests per second by the server to the Kubernetes API once the burst limit has been reached. (default 100) + // +optional + ClientQPS *int `json:"client-qps,omitempty"` // Velero args are settings to customize velero server arguments. Overrides values in other fields. // +optional Args *server.Args `json:"args,omitempty"` diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index d7bce3d2f2..4eea30b28e 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -663,6 +663,16 @@ func (in *VeleroConfig) DeepCopyInto(out *VeleroConfig) { *out = new(bool) **out = **in } + if in.ClientBurst != nil { + in, out := &in.ClientBurst, &out.ClientBurst + *out = new(int) + **out = **in + } + if in.ClientQPS != nil { + in, out := &in.ClientQPS, &out.ClientQPS + *out = new(int) + **out = **in + } if in.Args != nil { in, out := &in.Args, &out.Args *out = new(server.Args) diff --git a/bundle/manifests/oadp.openshift.io_dataprotectionapplications.yaml b/bundle/manifests/oadp.openshift.io_dataprotectionapplications.yaml index c955481608..b61c9d4d7a 100644 --- a/bundle/manifests/oadp.openshift.io_dataprotectionapplications.yaml +++ b/bundle/manifests/oadp.openshift.io_dataprotectionapplications.yaml @@ -798,6 +798,12 @@ spec: description: comma-separated list of pattern=N settings for file-filtered logging type: string type: object + client-burst: + description: maximum number of requests by the server to the Kubernetes API in a short period of time. (default 100) + type: integer + client-qps: + description: maximum number of requests per second by the server to the Kubernetes API once the burst limit has been reached. (default 100) + type: integer customPlugins: description: customPlugins defines the custom plugin to be installed with Velero items: diff --git a/config/crd/bases/oadp.openshift.io_dataprotectionapplications.yaml b/config/crd/bases/oadp.openshift.io_dataprotectionapplications.yaml index 6eb9918bd2..c86b4f5ab2 100644 --- a/config/crd/bases/oadp.openshift.io_dataprotectionapplications.yaml +++ b/config/crd/bases/oadp.openshift.io_dataprotectionapplications.yaml @@ -798,6 +798,12 @@ spec: description: comma-separated list of pattern=N settings for file-filtered logging type: string type: object + client-burst: + description: maximum number of requests by the server to the Kubernetes API in a short period of time. (default 100) + type: integer + client-qps: + description: maximum number of requests per second by the server to the Kubernetes API once the burst limit has been reached. (default 100) + type: integer customPlugins: description: customPlugins defines the custom plugin to be installed with Velero items: diff --git a/controllers/velero.go b/controllers/velero.go index 363d6c74f5..d31c1a2424 100644 --- a/controllers/velero.go +++ b/controllers/velero.go @@ -556,6 +556,13 @@ func (r *DPAReconciler) customizeVeleroContainer(dpa *oadpv1alpha1.DataProtectio veleroContainer.Args = append(veleroContainer.Args, fmt.Sprintf("--fs-backup-timeout=%s", getFsBackupTimeout(dpa))) // Overriding velero restore resource priorities to OpenShift default (ie. SecurityContextConstraints needs to be restored before pod/SA) veleroContainer.Args = append(veleroContainer.Args, fmt.Sprintf("--restore-resource-priorities=%s", common.DefaultRestoreResourcePriorities.String())) + + if dpa.Spec.Configuration.Velero != nil && dpa.Spec.Configuration.Velero.ClientBurst != nil { + veleroContainer.Args = append(veleroContainer.Args, fmt.Sprintf("--client-burst=%v", *dpa.Spec.Configuration.Velero.ClientBurst)) + } + if dpa.Spec.Configuration.Velero != nil && dpa.Spec.Configuration.Velero.ClientQPS != nil { + veleroContainer.Args = append(veleroContainer.Args, fmt.Sprintf("--client-qps=%v", *dpa.Spec.Configuration.Velero.ClientQPS)) + } setContainerDefaults(veleroContainer) // if server args is set, override the default server args if dpa.Spec.Configuration.Velero.Args != nil { diff --git a/controllers/velero_test.go b/controllers/velero_test.go index ff47049744..9c06850c8e 100644 --- a/controllers/velero_test.go +++ b/controllers/velero_test.go @@ -4559,6 +4559,160 @@ func TestDPAReconciler_buildVeleroDeployment(t *testing.T) { }, }, }, + { + name: "Override burst and qps", + dpa: &oadpv1alpha1.DataProtectionApplication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-Velero-CR", + Namespace: "test-ns", + }, + Spec: oadpv1alpha1.DataProtectionApplicationSpec{ + Configuration: &oadpv1alpha1.ApplicationConfig{ + Velero: &oadpv1alpha1.VeleroConfig{ + ClientBurst: ptr.To(123), + ClientQPS: ptr.To(123), + }, + NodeAgent: &oadpv1alpha1.NodeAgentConfig{ + UploaderType: "kopia", + }, + }, + }, + }, + veleroDeployment: &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-velero-deployment", + Namespace: "test-ns", + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{MatchLabels: veleroDeploymentMatchLabels}, + }, + }, + wantVeleroDeployment: &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-velero-deployment", + Namespace: "test-ns", + Labels: veleroDeploymentLabel, + }, + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + APIVersion: appsv1.SchemeGroupVersion.String(), + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{MatchLabels: veleroDeploymentMatchLabels}, + Replicas: ptr.To(int32(1)), + Template: corev1.PodTemplateSpec{ + ObjectMeta: veleroPodObjectMeta, + Spec: corev1.PodSpec{ + RestartPolicy: corev1.RestartPolicyAlways, + ServiceAccountName: common.Velero, + Containers: []corev1.Container{ + { + Name: common.Velero, + Image: common.VeleroImage, + ImagePullPolicy: corev1.PullAlways, + Ports: []corev1.ContainerPort{{Name: "metrics", ContainerPort: 8085}}, + Resources: corev1.ResourceRequirements{Requests: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("500m"), corev1.ResourceMemory: resource.MustParse("128Mi")}}, + Command: []string{"/velero"}, + Args: []string{ + "server", + "--uploader-type=kopia", + defaultFileSystemBackupTimeout, + defaultRestoreResourcePriorities, + "--client-burst=123", + "--client-qps=123", + defaultDisableInformerCache, + }, + VolumeMounts: baseVolumeMounts, + Env: baseEnvVars, + }, + }, + Volumes: baseVolumes, + InitContainers: []corev1.Container{}, + }, + }, + }, + }, + }, + { + name: "Conflicting burst and qps", + dpa: &oadpv1alpha1.DataProtectionApplication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-Velero-CR", + Namespace: "test-ns", + }, + Spec: oadpv1alpha1.DataProtectionApplicationSpec{ + Configuration: &oadpv1alpha1.ApplicationConfig{ + Velero: &oadpv1alpha1.VeleroConfig{ + ClientBurst: ptr.To(123), + ClientQPS: ptr.To(123), + Args: &server.Args{ + ServerConfig: server.ServerConfig{ + ClientBurst: ptr.To(321), + ClientQPS: ptr.To("321"), + }, + }, + }, + NodeAgent: &oadpv1alpha1.NodeAgentConfig{ + UploaderType: "kopia", + }, + }, + }, + }, + veleroDeployment: &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-velero-deployment", + Namespace: "test-ns", + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{MatchLabels: veleroDeploymentMatchLabels}, + }, + }, + wantVeleroDeployment: &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-velero-deployment", + Namespace: "test-ns", + Labels: veleroDeploymentLabel, + }, + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + APIVersion: appsv1.SchemeGroupVersion.String(), + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{MatchLabels: veleroDeploymentMatchLabels}, + Replicas: ptr.To(int32(1)), + Template: corev1.PodTemplateSpec{ + ObjectMeta: veleroPodObjectMeta, + Spec: corev1.PodSpec{ + RestartPolicy: corev1.RestartPolicyAlways, + ServiceAccountName: common.Velero, + Containers: []corev1.Container{ + { + Name: common.Velero, + Image: common.VeleroImage, + ImagePullPolicy: corev1.PullAlways, + Ports: []corev1.ContainerPort{{Name: "metrics", ContainerPort: 8085}}, + Resources: corev1.ResourceRequirements{Requests: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("500m"), corev1.ResourceMemory: resource.MustParse("128Mi")}}, + Command: []string{"/velero"}, + Args: []string{ + "server", + // should be present... "--uploader-type=kopia", + "--client-burst=321", + "--client-qps=321", + "--fs-backup-timeout=4h0m0s", + defaultRestoreResourcePriorities, + defaultDisableInformerCache, + }, + VolumeMounts: baseVolumeMounts, + Env: baseEnvVars, + }, + }, + Volumes: baseVolumes, + InitContainers: []corev1.Container{}, + }, + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {