diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 900bfcfbaa..bbd9055007 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -4044,6 +4044,17 @@ } } }, + "v1.LocalObjectReference": { + "description": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.", + "type": "object", + "properties": { + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + } + }, + "x-kubernetes-map-type": "atomic" + }, "v1.ManagedFieldsEntry": { "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.", "type": "object", @@ -4893,6 +4904,14 @@ "description": "FilesystemOverhead describes the space reserved for overhead when using Filesystem volumes. A value is between 0 and 1, if not defined it is 0.055 (5.5% overhead)", "$ref": "#/definitions/v1beta1.FilesystemOverhead" }, + "imagePullSecrets": { + "description": "The imagePullSecrets used to pull the container images", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.LocalObjectReference" + } + }, "importProxy": { "description": "ImportProxy contains importer pod proxy configuration.", "$ref": "#/definitions/v1beta1.ImportProxy" @@ -4939,6 +4958,14 @@ "description": "FilesystemOverhead describes the space reserved for overhead when using Filesystem volumes. A percentage value is between 0 and 1", "$ref": "#/definitions/v1beta1.FilesystemOverhead" }, + "imagePullSecrets": { + "description": "The imagePullSecrets used to pull the container images", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.LocalObjectReference" + } + }, "importProxy": { "description": "ImportProxy contains importer pod proxy configuration.", "$ref": "#/definitions/v1beta1.ImportProxy" diff --git a/pkg/apis/core/v1beta1/openapi_generated.go b/pkg/apis/core/v1beta1/openapi_generated.go index 379dbe3996..18f6615f3e 100644 --- a/pkg/apis/core/v1beta1/openapi_generated.go +++ b/pkg/apis/core/v1beta1/openapi_generated.go @@ -22986,11 +22986,25 @@ func schema_pkg_apis_core_v1beta1_CDIConfigSpec(ref common.ReferenceCallback) co Ref: ref("github.com/openshift/api/config/v1.TLSSecurityProfile"), }, }, + "imagePullSecrets": { + SchemaProps: spec.SchemaProps{ + Description: "The imagePullSecrets used to pull the container images", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "github.com/openshift/api/config/v1.TLSSecurityProfile", "k8s.io/api/core/v1.ResourceRequirements", "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1.FilesystemOverhead", "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1.ImportProxy"}, + "github.com/openshift/api/config/v1.TLSSecurityProfile", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.ResourceRequirements", "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1.FilesystemOverhead", "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1.ImportProxy"}, } } @@ -23040,11 +23054,25 @@ func schema_pkg_apis_core_v1beta1_CDIConfigStatus(ref common.ReferenceCallback) Format: "", }, }, + "imagePullSecrets": { + SchemaProps: spec.SchemaProps{ + Description: "The imagePullSecrets used to pull the container images", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "k8s.io/api/core/v1.ResourceRequirements", "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1.FilesystemOverhead", "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1.ImportProxy"}, + "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.ResourceRequirements", "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1.FilesystemOverhead", "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1.ImportProxy"}, } } diff --git a/pkg/controller/clone-controller.go b/pkg/controller/clone-controller.go index 66deb8e8a7..981409866b 100644 --- a/pkg/controller/clone-controller.go +++ b/pkg/controller/clone-controller.go @@ -497,6 +497,11 @@ func (r *CloneReconciler) CreateCloneSourcePod(image, pullPolicy string, pvc *co return nil, err } + imagePullSecrets, err := cc.GetImagePullSecrets(r.client) + if err != nil { + return nil, err + } + workloadNodePlacement, err := cc.GetWorkloadNodePlacement(r.client) if err != nil { return nil, err @@ -514,7 +519,7 @@ func (r *CloneReconciler) CreateCloneSourcePod(image, pullPolicy string, pvc *co sourceVolumeMode = corev1.PersistentVolumeFilesystem } - pod := MakeCloneSourcePodSpec(sourceVolumeMode, image, pullPolicy, sourcePvcName, sourcePvcNamespace, ownerKey, serverCABundle, pvc, podResourceRequirements, workloadNodePlacement) + pod := MakeCloneSourcePodSpec(sourceVolumeMode, image, pullPolicy, imagePullSecrets, sourcePvcName, sourcePvcNamespace, ownerKey, serverCABundle, pvc, podResourceRequirements, workloadNodePlacement) util.SetRecommendedLabels(pod, r.installerLabels, "cdi-controller") if err := r.client.Create(context.TODO(), pod); err != nil { @@ -527,7 +532,7 @@ func (r *CloneReconciler) CreateCloneSourcePod(image, pullPolicy string, pvc *co } // MakeCloneSourcePodSpec creates and returns the clone source pod spec based on the target pvc. -func MakeCloneSourcePodSpec(sourceVolumeMode corev1.PersistentVolumeMode, image, pullPolicy, sourcePvcName, sourcePvcNamespace, ownerRefAnno string, +func MakeCloneSourcePodSpec(sourceVolumeMode corev1.PersistentVolumeMode, image, pullPolicy string, imagePullSecrets []corev1.LocalObjectReference, sourcePvcName, sourcePvcNamespace, ownerRefAnno string, serverCACert []byte, targetPvc *corev1.PersistentVolumeClaim, resourceRequirements *corev1.ResourceRequirements, workloadNodePlacement *sdkapi.NodePlacement) *corev1.Pod { @@ -617,7 +622,8 @@ func MakeCloneSourcePodSpec(sourceVolumeMode corev1.PersistentVolumeMode, image, }, }, }, - RestartPolicy: corev1.RestartPolicyOnFailure, + ImagePullSecrets: imagePullSecrets, + RestartPolicy: corev1.RestartPolicyOnFailure, Volumes: []corev1.Volume{ { Name: cc.DataVolName, diff --git a/pkg/controller/common/util.go b/pkg/controller/common/util.go index 18ac585bdd..5e040fcd72 100644 --- a/pkg/controller/common/util.go +++ b/pkg/controller/common/util.go @@ -411,6 +411,17 @@ func GetDefaultPodResourceRequirements(client client.Client) (*v1.ResourceRequir return cdiconfig.Status.DefaultPodResourceRequirements, nil } +// GetImagePullSecrets gets the imagePullSecrets needed to pull images from the cdi config +func GetImagePullSecrets(client client.Client) ([]corev1.LocalObjectReference, error) { + cdiconfig := &cdiv1.CDIConfig{} + if err := client.Get(context.TODO(), types.NamespacedName{Name: common.ConfigName}, cdiconfig); err != nil { + klog.Errorf("Unable to find CDI configuration, %v\n", err) + return nil, err + } + + return cdiconfig.Status.ImagePullSecrets, nil +} + // AddVolumeDevices returns VolumeDevice slice with one block device for pods using PV with block volume mode func AddVolumeDevices() []v1.VolumeDevice { volumeDevices := []v1.VolumeDevice{ diff --git a/pkg/controller/config-controller.go b/pkg/controller/config-controller.go index ec62c8f1e8..668b85dbe2 100644 --- a/pkg/controller/config-controller.go +++ b/pkg/controller/config-controller.go @@ -97,6 +97,10 @@ func (r *CDIConfigReconciler) Reconcile(_ context.Context, req reconcile.Request return reconcile.Result{}, err } + if err := r.reconcileImagePullSecrets(config); err != nil { + return reconcile.Result{}, err + } + if err := r.reconcileFilesystemOverhead(config); err != nil { return reconcile.Result{}, err } @@ -232,6 +236,11 @@ func (r *CDIConfigReconciler) reconcileStorageClass(config *cdiv1.CDIConfig) err return nil } +func (r *CDIConfigReconciler) reconcileImagePullSecrets(config *cdiv1.CDIConfig) error { + config.Status.ImagePullSecrets = config.Spec.ImagePullSecrets + return nil +} + func (r *CDIConfigReconciler) reconcileDefaultPodResourceRequirements(config *cdiv1.CDIConfig) error { cpuLimit, _ := resource.ParseQuantity(defaultCPULimit) memLimit, _ := resource.ParseQuantity(defaultMemLimit) diff --git a/pkg/controller/dataimportcron-controller.go b/pkg/controller/dataimportcron-controller.go index 23aff531a2..454c8a91c6 100644 --- a/pkg/controller/dataimportcron-controller.go +++ b/pkg/controller/dataimportcron-controller.go @@ -860,6 +860,10 @@ func (r *DataImportCronReconciler) newCronJob(cron *cdiv1.DataImportCron) (*batc addEnvVarFromImportProxyConfig(common.ImportProxyHTTPS) addEnvVarFromImportProxyConfig(common.ImportProxyNoProxy) + imagePullSecrets, err := cc.GetImagePullSecrets(r.client) + if err != nil { + return nil, err + } cronJobName := GetCronJobName(cron) cronJob := &batchv1.CronJob{ ObjectMeta: metav1.ObjectMeta{ @@ -880,6 +884,7 @@ func (r *DataImportCronReconciler) newCronJob(cron *cdiv1.DataImportCron) (*batc Containers: []corev1.Container{container}, ServiceAccountName: common.CronJobServiceAccountName, Volumes: volumes, + ImagePullSecrets: imagePullSecrets, }, }, BackoffLimit: pointer.Int32(2), diff --git a/pkg/controller/datavolume/clone-controller-base.go b/pkg/controller/datavolume/clone-controller-base.go index c4d9e4ca08..225aa7443f 100644 --- a/pkg/controller/datavolume/clone-controller-base.go +++ b/pkg/controller/datavolume/clone-controller-base.go @@ -420,6 +420,11 @@ func (r *CloneReconcilerBase) createExpansionPod(pvc *corev1.PersistentVolumeCla return nil, err } + imagePullSecrets, err := cc.GetImagePullSecrets(r.client) + if err != nil { + return nil, err + } + workloadNodePlacement, err := cc.GetWorkloadNodePlacement(r.client) if err != nil { return nil, err @@ -447,7 +452,8 @@ func (r *CloneReconcilerBase) createExpansionPod(pvc *corev1.PersistentVolumeCla Args: []string{"-c", "echo", "'hello cdi'"}, }, }, - RestartPolicy: corev1.RestartPolicyOnFailure, + ImagePullSecrets: imagePullSecrets, + RestartPolicy: corev1.RestartPolicyOnFailure, Volumes: []corev1.Volume{ { Name: cc.DataVolName, diff --git a/pkg/controller/datavolume/pvc-clone-controller.go b/pkg/controller/datavolume/pvc-clone-controller.go index a5d3a8d8ad..0aa4e95394 100644 --- a/pkg/controller/datavolume/pvc-clone-controller.go +++ b/pkg/controller/datavolume/pvc-clone-controller.go @@ -1240,6 +1240,11 @@ func (r *PvcCloneReconciler) makeSizeDetectionPodSpec( if container == nil { return nil } + imagePullSecrets, err := cc.GetImagePullSecrets(r.client) + if err != nil { + return nil + } + // Assemble the pod pod := &corev1.Pod{ ObjectMeta: *objectMeta, @@ -1255,6 +1260,7 @@ func (r *PvcCloneReconciler) makeSizeDetectionPodSpec( Tolerations: workloadNodePlacement.Tolerations, Affinity: workloadNodePlacement.Affinity, PriorityClassName: cc.GetPriorityClass(sourcePvc), + ImagePullSecrets: imagePullSecrets, }, } diff --git a/pkg/controller/import-controller.go b/pkg/controller/import-controller.go index bd2be41a1b..a2d36cb0bc 100644 --- a/pkg/controller/import-controller.go +++ b/pkg/controller/import-controller.go @@ -111,6 +111,7 @@ type importerPodArgs struct { pvc *corev1.PersistentVolumeClaim scratchPvcName *string podResourceRequirements *corev1.ResourceRequirements + imagePullSecrets []corev1.LocalObjectReference workloadNodePlacement *sdkapi.NodePlacement vddkImageName *string priorityClassName string @@ -867,6 +868,11 @@ func createImporterPod(log logr.Logger, client client.Client, args *importerPodA return nil, err } + args.imagePullSecrets, err = cc.GetImagePullSecrets(client) + if err != nil { + return nil, err + } + args.workloadNodePlacement, err = cc.GetWorkloadNodePlacement(client) if err != nil { return nil, err @@ -971,6 +977,7 @@ func makeNodeImporterPodSpec(args *importerPodArgs) *corev1.Pod { Tolerations: args.workloadNodePlacement.Tolerations, Affinity: args.workloadNodePlacement.Affinity, PriorityClassName: args.priorityClassName, + ImagePullSecrets: args.imagePullSecrets, }, } @@ -1003,7 +1010,7 @@ func makeNodeImporterPodSpec(args *importerPodArgs) *corev1.Pod { args.podEnvVar.ep = "http://localhost:8100/disk.img" args.podEnvVar.readyFile = "/shared/ready" args.podEnvVar.doneFile = "/shared/done" - setImporterPodCommons(pod, args.podEnvVar, args.pvc, args.podResourceRequirements) + setImporterPodCommons(pod, args.podEnvVar, args.pvc, args.podResourceRequirements, args.imagePullSecrets) pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{ MountPath: "/shared", Name: "shared-volume", @@ -1085,10 +1092,11 @@ func makeImporterPodSpec(args *importerPodArgs) *corev1.Pod { Tolerations: args.workloadNodePlacement.Tolerations, Affinity: args.workloadNodePlacement.Affinity, PriorityClassName: args.priorityClassName, + ImagePullSecrets: args.imagePullSecrets, }, } - setImporterPodCommons(pod, args.podEnvVar, args.pvc, args.podResourceRequirements) + setImporterPodCommons(pod, args.podEnvVar, args.pvc, args.podResourceRequirements, args.imagePullSecrets) if args.scratchPvcName != nil { pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{ @@ -1162,12 +1170,13 @@ func makeImporterPodSpec(args *importerPodArgs) *corev1.Pod { return pod } -func setImporterPodCommons(pod *corev1.Pod, podEnvVar *importPodEnvVar, pvc *corev1.PersistentVolumeClaim, podResourceRequirements *corev1.ResourceRequirements) { +func setImporterPodCommons(pod *corev1.Pod, podEnvVar *importPodEnvVar, pvc *corev1.PersistentVolumeClaim, podResourceRequirements *corev1.ResourceRequirements, imagePullSecrets []corev1.LocalObjectReference) { if podResourceRequirements != nil { for i := range pod.Spec.Containers { pod.Spec.Containers[i].Resources = *podResourceRequirements } } + pod.Spec.ImagePullSecrets = imagePullSecrets ownerUID := pvc.UID if len(pvc.OwnerReferences) == 1 { diff --git a/pkg/controller/upload-controller.go b/pkg/controller/upload-controller.go index c590a5c026..15d7b10ca1 100644 --- a/pkg/controller/upload-controller.go +++ b/pkg/controller/upload-controller.go @@ -579,12 +579,17 @@ func (r *UploadReconciler) createUploadPod(args UploadPodArgs) (*v1.Pod, error) return nil, err } + imagePullSecrets, err := cc.GetImagePullSecrets(r.client) + if err != nil { + return nil, err + } + workloadNodePlacement, err := cc.GetWorkloadNodePlacement(r.client) if err != nil { return nil, err } - pod := r.makeUploadPodSpec(args, podResourceRequirements, workloadNodePlacement) + pod := r.makeUploadPodSpec(args, podResourceRequirements, imagePullSecrets, workloadNodePlacement) util.SetRecommendedLabels(pod, r.installerLabels, "cdi-controller") if err := r.client.Get(context.TODO(), types.NamespacedName{Name: args.Name, Namespace: ns}, pod); err != nil { @@ -730,7 +735,7 @@ func createUploadServiceNameFromPvcName(pvc string) string { return naming.GetServiceNameFromResourceName(createUploadResourceName(pvc)) } -func (r *UploadReconciler) makeUploadPodSpec(args UploadPodArgs, resourceRequirements *v1.ResourceRequirements, workloadNodePlacement *sdkapi.NodePlacement) *v1.Pod { +func (r *UploadReconciler) makeUploadPodSpec(args UploadPodArgs, resourceRequirements *v1.ResourceRequirements, imagePullSecrets []v1.LocalObjectReference, workloadNodePlacement *sdkapi.NodePlacement) *v1.Pod { requestImageSize, _ := cc.GetRequestedImageSize(args.PVC) serviceName := naming.GetServiceNameFromResourceName(args.Name) pod := &v1.Pod{ @@ -840,6 +845,7 @@ func (r *UploadReconciler) makeUploadPodSpec(args UploadPodArgs, resourceRequire Tolerations: workloadNodePlacement.Tolerations, Affinity: workloadNodePlacement.Affinity, PriorityClassName: cc.GetPriorityClass(args.PVC), + ImagePullSecrets: imagePullSecrets, }, } diff --git a/pkg/operator/controller/controller_test.go b/pkg/operator/controller/controller_test.go index 6e370b89f5..5d00e271b9 100644 --- a/pkg/operator/controller/controller_test.go +++ b/pkg/operator/controller/controller_test.go @@ -1244,7 +1244,9 @@ var _ = Describe("Controller", func() { Entry("verify - unused deployment deleted", func() (client.Object, error) { - deployment := utils.CreateDeployment("fake-cdi-deployment", "app", "containerized-data-importer", "fake-sa", int32(1), &sdkapi.NodePlacement{}) + const imagePullSecretName = "fake-registry-key" + var imagePullSecrets = []corev1.LocalObjectReference{{Name: imagePullSecretName}} + deployment := utils.CreateDeployment("fake-cdi-deployment", "app", "containerized-data-importer", "fake-sa", imagePullSecrets, int32(1), &sdkapi.NodePlacement{}) return deployment, nil }), Entry("verify - unused service deleted", diff --git a/pkg/operator/controller/cr-manager.go b/pkg/operator/controller/cr-manager.go index cbdfe9854a..b1666bcf28 100644 --- a/pkg/operator/controller/cr-manager.go +++ b/pkg/operator/controller/cr-manager.go @@ -83,6 +83,9 @@ func (r *ReconcileCDI) getNamespacedArgs(cr *cdiv1.CDI) *cdinamespaced.FactoryAr if cr.Spec.ImagePullPolicy != "" { result.PullPolicy = string(cr.Spec.ImagePullPolicy) } + if cr.Spec.Config != nil && len(cr.Spec.Config.ImagePullSecrets) > 0 { + result.ImagePullSecrets = cr.Spec.Config.ImagePullSecrets + } if cr.Spec.PriorityClass != nil && string(*cr.Spec.PriorityClass) != "" { result.PriorityClassName = string(*cr.Spec.PriorityClass) } else { diff --git a/pkg/operator/resources/crds_generated.go b/pkg/operator/resources/crds_generated.go index bee88f6ec6..8bee557847 100644 --- a/pkg/operator/resources/crds_generated.go +++ b/pkg/operator/resources/crds_generated.go @@ -125,6 +125,19 @@ spec: global value type: object type: object + imagePullSecrets: + description: The imagePullSecrets used to pull the container images + items: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + type: array importProxy: description: ImportProxy contains importer pod proxy configuration. properties: @@ -2341,6 +2354,19 @@ spec: global value type: object type: object + imagePullSecrets: + description: The imagePullSecrets used to pull the container images + items: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + type: array importProxy: description: ImportProxy contains importer pod proxy configuration. properties: @@ -4531,6 +4557,19 @@ spec: value type: object type: object + imagePullSecrets: + description: The imagePullSecrets used to pull the container images + items: + description: LocalObjectReference contains enough information to + let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + type: array importProxy: description: ImportProxy contains importer pod proxy configuration. properties: @@ -4748,6 +4787,19 @@ spec: value type: object type: object + imagePullSecrets: + description: The imagePullSecrets used to pull the container images + items: + description: LocalObjectReference contains enough information to + let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + type: array importProxy: description: ImportProxy contains importer pod proxy configuration. properties: diff --git a/pkg/operator/resources/namespaced/apiserver.go b/pkg/operator/resources/namespaced/apiserver.go index 9e6fbfc111..3fd2b5ccd9 100644 --- a/pkg/operator/resources/namespaced/apiserver.go +++ b/pkg/operator/resources/namespaced/apiserver.go @@ -22,6 +22,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/util/intstr" "sigs.k8s.io/controller-runtime/pkg/client" @@ -46,7 +47,7 @@ func createAPIServerResources(args *FactoryArgs) []client.Object { createAPIServerRoleBinding(), createAPIServerRole(), createAPIServerService(), - createAPIServerDeployment(args.APIServerImage, args.Verbosity, args.PullPolicy, args.PriorityClassName, args.InfraNodePlacement), + createAPIServerDeployment(args.APIServerImage, args.Verbosity, args.PullPolicy, args.ImagePullSecrets, args.PriorityClassName, args.InfraNodePlacement), } } @@ -91,9 +92,9 @@ func createAPIServerService() *corev1.Service { return service } -func createAPIServerDeployment(image, verbosity, pullPolicy, priorityClassName string, infraNodePlacement *sdkapi.NodePlacement) *appsv1.Deployment { +func createAPIServerDeployment(image, verbosity, pullPolicy string, imagePullSecrets []corev1.LocalObjectReference, priorityClassName string, infraNodePlacement *sdkapi.NodePlacement) *appsv1.Deployment { defaultMode := corev1.ConfigMapVolumeSourceDefaultMode - deployment := utils.CreateDeployment(apiServerRessouceName, cdiLabel, apiServerRessouceName, apiServerRessouceName, 1, infraNodePlacement) + deployment := utils.CreateDeployment(apiServerRessouceName, cdiLabel, apiServerRessouceName, apiServerRessouceName, imagePullSecrets, 1, infraNodePlacement) if priorityClassName != "" { deployment.Spec.Template.Spec.PriorityClassName = priorityClassName } diff --git a/pkg/operator/resources/namespaced/controller.go b/pkg/operator/resources/namespaced/controller.go index aa084f3a0d..23eb52a97c 100644 --- a/pkg/operator/resources/namespaced/controller.go +++ b/pkg/operator/resources/namespaced/controller.go @@ -50,6 +50,7 @@ func createControllerResources(args *FactoryArgs) []client.Object { args.UploadServerImage, args.Verbosity, args.PullPolicy, + args.ImagePullSecrets, args.PriorityClassName, args.InfraNodePlacement), createInsecureRegConfigMap(), @@ -161,9 +162,9 @@ func createControllerServiceAccount() *corev1.ServiceAccount { return utils.ResourceBuilder.CreateServiceAccount(common.ControllerServiceAccountName) } -func createControllerDeployment(controllerImage, importerImage, clonerImage, uploadServerImage, verbosity, pullPolicy, priorityClassName string, infraNodePlacement *sdkapi.NodePlacement) *appsv1.Deployment { +func createControllerDeployment(controllerImage, importerImage, clonerImage, uploadServerImage, verbosity, pullPolicy string, imagePullSecrets []corev1.LocalObjectReference, priorityClassName string, infraNodePlacement *sdkapi.NodePlacement) *appsv1.Deployment { defaultMode := corev1.ConfigMapVolumeSourceDefaultMode - deployment := utils.CreateDeployment(controllerResourceName, "app", "containerized-data-importer", common.ControllerServiceAccountName, int32(1), infraNodePlacement) + deployment := utils.CreateDeployment(controllerResourceName, "app", "containerized-data-importer", common.ControllerServiceAccountName, imagePullSecrets, int32(1), infraNodePlacement) if priorityClassName != "" { deployment.Spec.Template.Spec.PriorityClassName = priorityClassName } diff --git a/pkg/operator/resources/namespaced/factory.go b/pkg/operator/resources/namespaced/factory.go index ff59509d97..1dedd7777d 100644 --- a/pkg/operator/resources/namespaced/factory.go +++ b/pkg/operator/resources/namespaced/factory.go @@ -19,6 +19,7 @@ package namespaced import ( "fmt" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -39,6 +40,7 @@ type FactoryArgs struct { UploadServerImage string `required:"true" split_words:"true"` Verbosity string `required:"true"` PullPolicy string `required:"true" split_words:"true"` + ImagePullSecrets []corev1.LocalObjectReference PriorityClassName string Namespace string InfraNodePlacement *sdkapi.NodePlacement diff --git a/pkg/operator/resources/namespaced/uploadproxy.go b/pkg/operator/resources/namespaced/uploadproxy.go index df26afb813..212a19ea52 100644 --- a/pkg/operator/resources/namespaced/uploadproxy.go +++ b/pkg/operator/resources/namespaced/uploadproxy.go @@ -39,7 +39,7 @@ func createUploadProxyResources(args *FactoryArgs) []client.Object { createUploadProxyService(), createUploadProxyRoleBinding(), createUploadProxyRole(), - createUploadProxyDeployment(args.UploadProxyImage, args.Verbosity, args.PullPolicy, args.PriorityClassName, args.InfraNodePlacement), + createUploadProxyDeployment(args.UploadProxyImage, args.Verbosity, args.PullPolicy, args.ImagePullSecrets, args.PriorityClassName, args.InfraNodePlacement), } } @@ -84,9 +84,9 @@ func createUploadProxyRole() *rbacv1.Role { return utils.ResourceBuilder.CreateRole(uploadProxyResourceName, rules) } -func createUploadProxyDeployment(image, verbosity, pullPolicy, priorityClassName string, infraNodePlacement *sdkapi.NodePlacement) *appsv1.Deployment { +func createUploadProxyDeployment(image, verbosity, pullPolicy string, imagePullSecrets []corev1.LocalObjectReference, priorityClassName string, infraNodePlacement *sdkapi.NodePlacement) *appsv1.Deployment { defaultMode := corev1.ConfigMapVolumeSourceDefaultMode - deployment := utils.CreateDeployment(uploadProxyResourceName, cdiLabel, uploadProxyResourceName, uploadProxyResourceName, int32(1), infraNodePlacement) + deployment := utils.CreateDeployment(uploadProxyResourceName, cdiLabel, uploadProxyResourceName, uploadProxyResourceName, imagePullSecrets, int32(1), infraNodePlacement) if priorityClassName != "" { deployment.Spec.Template.Spec.PriorityClassName = priorityClassName } diff --git a/pkg/operator/resources/operator/factory.go b/pkg/operator/resources/operator/factory.go index 877fc93488..abb4ffd4be 100644 --- a/pkg/operator/resources/operator/factory.go +++ b/pkg/operator/resources/operator/factory.go @@ -20,6 +20,7 @@ import ( "fmt" csvv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" + corev1 "k8s.io/api/core/v1" extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -60,6 +61,7 @@ type ClusterServiceVersionData struct { ReplacesCsvVersion string Namespace string ImagePullPolicy string + ImagePullSecrets []corev1.LocalObjectReference IconBase64 string Verbosity string diff --git a/pkg/operator/resources/operator/operator.go b/pkg/operator/resources/operator/operator.go index 982ca04e90..47627c1171 100644 --- a/pkg/operator/resources/operator/operator.go +++ b/pkg/operator/resources/operator/operator.go @@ -290,7 +290,8 @@ func createDeployment(args *FactoryArgs) []client.Object { args.NamespacedArgs.UploadProxyImage, args.NamespacedArgs.UploadServerImage, args.NamespacedArgs.Verbosity, - args.NamespacedArgs.PullPolicy), + args.NamespacedArgs.PullPolicy, + args.NamespacedArgs.ImagePullSecrets), createOperatorLeaderElectionConfigMap(args.NamespacedArgs.Namespace), } } @@ -324,6 +325,7 @@ func createCDIListCRD() *extv1.CustomResourceDefinition { } func createOperatorEnvVar(operatorVersion, deployClusterResources, operatorImage, controllerImage, importerImage, clonerImage, apiServerImage, uploadProxyImage, uploadServerImage, verbosity, pullPolicy string) []corev1.EnvVar { + return []corev1.EnvVar{ { Name: "DEPLOY_CLUSTER_RESOURCES", @@ -372,8 +374,8 @@ func createOperatorEnvVar(operatorVersion, deployClusterResources, operatorImage } } -func createOperatorDeployment(operatorVersion, namespace, deployClusterResources, operatorImage, controllerImage, importerImage, clonerImage, apiServerImage, uploadProxyImage, uploadServerImage, verbosity, pullPolicy string) *appsv1.Deployment { - deployment := utils.CreateOperatorDeployment("cdi-operator", namespace, "name", "cdi-operator", serviceAccountName, int32(1)) +func createOperatorDeployment(operatorVersion, namespace, deployClusterResources, operatorImage, controllerImage, importerImage, clonerImage, apiServerImage, uploadProxyImage, uploadServerImage, verbosity, pullPolicy string, imagePullSecrets []corev1.LocalObjectReference) *appsv1.Deployment { + deployment := utils.CreateOperatorDeployment("cdi-operator", namespace, "name", "cdi-operator", serviceAccountName, imagePullSecrets, int32(1)) container := utils.CreatePortsContainer("cdi-operator", operatorImage, pullPolicy, createPrometheusPorts()) container.Resources = corev1.ResourceRequirements{ Requests: corev1.ResourceList{ @@ -431,7 +433,8 @@ _The CDI Operator does not support updates yet._ data.UplodaProxyImage, data.UplodaServerImage, data.Verbosity, - data.ImagePullPolicy) + data.ImagePullPolicy, + data.ImagePullSecrets) deployment.Spec.Template.Spec.PriorityClassName = utils.CDIPriorityClass diff --git a/pkg/operator/resources/utils/common.go b/pkg/operator/resources/utils/common.go index 0fe71d1b91..4e89691906 100644 --- a/pkg/operator/resources/utils/common.go +++ b/pkg/operator/resources/utils/common.go @@ -87,23 +87,26 @@ func CreatePortsContainer(name, image, pullPolicy string, ports []corev1.Contain } // CreateDeployment creates deployment -func CreateDeployment(name, matchKey, matchValue, serviceAccountName string, replicas int32, infraNodePlacement *sdkapi.NodePlacement) *appsv1.Deployment { +func CreateDeployment(name, matchKey, matchValue, serviceAccountName string, imagePullSecrets []corev1.LocalObjectReference, replicas int32, infraNodePlacement *sdkapi.NodePlacement) *appsv1.Deployment { podSpec := corev1.PodSpec{ SecurityContext: &corev1.PodSecurityContext{ RunAsNonRoot: &[]bool{true}[0], }, + ImagePullSecrets: imagePullSecrets, } deployment := ResourceBuilder.CreateDeployment(name, "", matchKey, matchValue, serviceAccountName, replicas, podSpec, infraNodePlacement) return deployment } // CreateOperatorDeployment creates operator deployment -func CreateOperatorDeployment(name, namespace, matchKey, matchValue, serviceAccount string, numReplicas int32) *appsv1.Deployment { +func CreateOperatorDeployment(name, namespace, matchKey, matchValue, serviceAccount string, imagePullSecrets []corev1.LocalObjectReference, numReplicas int32) *appsv1.Deployment { + podSpec := corev1.PodSpec{ SecurityContext: &corev1.PodSecurityContext{ RunAsNonRoot: &[]bool{true}[0], }, - NodeSelector: map[string]string{"kubernetes.io/os": "linux"}, + ImagePullSecrets: imagePullSecrets, + NodeSelector: map[string]string{"kubernetes.io/os": "linux"}, Tolerations: []corev1.Toleration{ { Key: "CriticalAddonsOnly", diff --git a/staging/src/kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1/types.go b/staging/src/kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1/types.go index 7194d40142..94af9ae759 100644 --- a/staging/src/kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1/types.go +++ b/staging/src/kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1/types.go @@ -773,6 +773,8 @@ type CDIConfigSpec struct { DataVolumeTTLSeconds *int32 `json:"dataVolumeTTLSeconds,omitempty"` // TLSSecurityProfile is used by operators to apply cluster-wide TLS security settings to operands. TLSSecurityProfile *ocpconfigv1.TLSSecurityProfile `json:"tlsSecurityProfile,omitempty"` + // The imagePullSecrets used to pull the container images + ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` } // CDIConfigStatus provides the most recently observed status of the CDI Config resource @@ -790,6 +792,8 @@ type CDIConfigStatus struct { FilesystemOverhead *FilesystemOverhead `json:"filesystemOverhead,omitempty"` // Preallocation controls whether storage for DataVolumes should be allocated in advance. Preallocation bool `json:"preallocation,omitempty"` + // The imagePullSecrets used to pull the container images + ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` } // CDIConfigList provides the needed parameters to do request a list of CDIConfigs from the system diff --git a/staging/src/kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1/types_swagger_generated.go b/staging/src/kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1/types_swagger_generated.go index 9736d946fc..b3f77cd660 100644 --- a/staging/src/kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1/types_swagger_generated.go +++ b/staging/src/kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1/types_swagger_generated.go @@ -375,6 +375,7 @@ func (CDIConfigSpec) SwaggerDoc() map[string]string { "insecureRegistries": "InsecureRegistries is a list of TLS disabled registries", "dataVolumeTTLSeconds": "DataVolumeTTLSeconds is the time in seconds after DataVolume completion it can be garbage collected. The default is 0 sec. To disable GC use -1.\n+optional", "tlsSecurityProfile": "TLSSecurityProfile is used by operators to apply cluster-wide TLS security settings to operands.", + "imagePullSecrets": "The imagePullSecrets used to pull the container images", } } @@ -387,6 +388,7 @@ func (CDIConfigStatus) SwaggerDoc() map[string]string { "defaultPodResourceRequirements": "ResourceRequirements describes the compute resource requirements.", "filesystemOverhead": "FilesystemOverhead describes the space reserved for overhead when using Filesystem volumes. A percentage value is between 0 and 1", "preallocation": "Preallocation controls whether storage for DataVolumes should be allocated in advance.", + "imagePullSecrets": "The imagePullSecrets used to pull the container images", } } diff --git a/staging/src/kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1/zz_generated.deepcopy.go b/staging/src/kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1/zz_generated.deepcopy.go index 8913ef5145..69d7adcb90 100644 --- a/staging/src/kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1/zz_generated.deepcopy.go +++ b/staging/src/kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1/zz_generated.deepcopy.go @@ -196,6 +196,11 @@ func (in *CDIConfigSpec) DeepCopyInto(out *CDIConfigSpec) { *out = new(configv1.TLSSecurityProfile) (*in).DeepCopyInto(*out) } + if in.ImagePullSecrets != nil { + in, out := &in.ImagePullSecrets, &out.ImagePullSecrets + *out = make([]v1.LocalObjectReference, len(*in)) + copy(*out, *in) + } return } @@ -232,6 +237,11 @@ func (in *CDIConfigStatus) DeepCopyInto(out *CDIConfigStatus) { *out = new(FilesystemOverhead) (*in).DeepCopyInto(*out) } + if in.ImagePullSecrets != nil { + in, out := &in.ImagePullSecrets, &out.ImagePullSecrets + *out = make([]v1.LocalObjectReference, len(*in)) + copy(*out, *in) + } return }