diff --git a/api/raczylo.com/v1/clusterimage_types.go b/api/raczylo.com/v1/clusterimage_types.go index d901400..c31e30e 100644 --- a/api/raczylo.com/v1/clusterimage_types.go +++ b/api/raczylo.com/v1/clusterimage_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1 import ( + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -28,15 +29,18 @@ import ( // +kubebuilder:printcolumn:name="Path",type="string",JSONPath=".spec.exportPath" // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" type ClusterImageSpec struct { - Image string `json:"image,omitempty"` - Tag string `json:"tag,omitempty"` - Sha string `json:"sha,omitempty"` - FullName string `json:"fullName,omitempty"` // Because I'm lazy and it's easier to pull that way - Storage string `json:"storage,omitempty"` - ExportName string `json:"exportName"` - ExportPath string `json:"exportPath,omitempty"` - ImageNamespace string `json:"imageNamespace,omitempty"` + Image string `json:"image,omitempty"` + Tag string `json:"tag,omitempty"` + Sha string `json:"sha,omitempty"` + FullName string `json:"fullName,omitempty"` // Because I'm lazy and it's easier to pull that way + Storage string `json:"storage,omitempty"` + ExportName string `json:"exportName"` + ExportPath string `json:"exportPath,omitempty"` + ImageNamespace string `json:"imageNamespace,omitempty"` + // +kubebuilder:validation:Optional JobAnnotations map[string]string `json:"jobAnnotations,omitempty"` + // +kubebuilder:validation:Optional + ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` } // ClusterImageStatus defines the observed state of ClusterImage diff --git a/api/raczylo.com/v1/clusterimageexport_types.go b/api/raczylo.com/v1/clusterimageexport_types.go index 9596827..b6380b0 100644 --- a/api/raczylo.com/v1/clusterimageexport_types.go +++ b/api/raczylo.com/v1/clusterimageexport_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1 import ( + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -60,9 +61,12 @@ type ClusterImageExportSpec struct { // Base path for the export - both file and S3 // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=255 - BasePath string `json:"basePath"` - Storage ClusterImageStorageSpec `json:"storage"` - JobAnnotations map[string]string `json:"jobAnnotations,omitempty"` + BasePath string `json:"basePath"` + Storage ClusterImageStorageSpec `json:"storage"` + // +kubebuilder:validation:Optional + JobAnnotations map[string]string `json:"jobAnnotations,omitempty"` + // +kubebuilder:validation:Optional + ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` // +kubebuilder:validation.Minimum=1 // +kubebuilder:validation.Maximum=100 MaxConcurrentJobs int `json:"maxConcurrentJobs"` diff --git a/chart/Chart.yaml b/chart/Chart.yaml index dbdc4fb..2e520cb 100644 --- a/chart/Chart.yaml +++ b/chart/Chart.yaml @@ -10,9 +10,9 @@ description: | type: application -version: 0.1.20 +version: 0.1.23 -appVersion: "0.1.20" +appVersion: "0.1.23" home: https://github.com/lukaszraczylo/kubernetes-images-sync-operator diff --git a/chart/templates/clusterimage-crd.yaml b/chart/templates/clusterimage-crd.yaml index 1c4d732..b8237dd 100644 --- a/chart/templates/clusterimage-crd.yaml +++ b/chart/templates/clusterimage-crd.yaml @@ -78,6 +78,24 @@ spec: type: string imageNamespace: type: string + imagePullSecrets: + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array jobAnnotations: additionalProperties: type: string diff --git a/chart/templates/clusterimageexport-crd.yaml b/chart/templates/clusterimageexport-crd.yaml index 31fcc53..193464a 100644 --- a/chart/templates/clusterimageexport-crd.yaml +++ b/chart/templates/clusterimageexport-crd.yaml @@ -70,6 +70,24 @@ spec: items: type: string type: array + imagePullSecrets: + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array includes: description: Include only images which contain these strings items: diff --git a/chart/values.yaml b/chart/values.yaml index 3f5df68..909a6b8 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -12,7 +12,7 @@ sa: - ALL image: repository: ghcr.io/lukaszraczylo/kubernetes-images-sync-operator - tag: 0.1.20 + tag: 0.1.23 resources: limits: cpu: 500m diff --git a/config/crd/bases/raczylo.com_clusterimageexports.yaml b/config/crd/bases/raczylo.com_clusterimageexports.yaml index af007d3..443f135 100644 --- a/config/crd/bases/raczylo.com_clusterimageexports.yaml +++ b/config/crd/bases/raczylo.com_clusterimageexports.yaml @@ -70,6 +70,24 @@ spec: items: type: string type: array + imagePullSecrets: + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array includes: description: Include only images which contain these strings items: diff --git a/config/crd/bases/raczylo.com_clusterimages.yaml b/config/crd/bases/raczylo.com_clusterimages.yaml index 504670e..304467f 100644 --- a/config/crd/bases/raczylo.com_clusterimages.yaml +++ b/config/crd/bases/raczylo.com_clusterimages.yaml @@ -77,6 +77,24 @@ spec: type: string imageNamespace: type: string + imagePullSecrets: + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array jobAnnotations: additionalProperties: type: string diff --git a/internal/controller/raczylo.com/clusterimage_controller.go b/internal/controller/raczylo.com/clusterimage_controller.go index 56f4e3b..8a8d87e 100644 --- a/internal/controller/raczylo.com/clusterimage_controller.go +++ b/internal/controller/raczylo.com/clusterimage_controller.go @@ -36,6 +36,8 @@ type ClusterImageReconciler struct { // # additional RBAC rules - create and manage jobs // +kubebuilder:rbac:groups=batch,resources=jobs,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch;create;update;patch;delete +// add access to secrets +// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch func (r *ClusterImageReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { l := log.FromContext(ctx) @@ -229,12 +231,13 @@ func (r *ClusterImageReconciler) createBackupJob(ctx context.Context, clusterIma defaultCommands = append(defaultCommands, "rm -f /tmp/"+normalisedImageName+".tar") jobParams := shared.JobParams{ - Name: fmt.Sprintf("img-export-%s", clusterImage.Name), - Namespace: clusterImage.Namespace, - Image: shared.BACKUP_JOB_IMAGE, - Annotations: clusterImage.Spec.JobAnnotations, - Commands: defaultCommands, - ServiceAccount: os.Getenv("POD_SERVICE_ACCOUNT"), + Name: fmt.Sprintf("img-export-%s", clusterImage.Name), + Namespace: clusterImage.Namespace, + Image: shared.BACKUP_JOB_IMAGE, + Annotations: clusterImage.Spec.JobAnnotations, + Commands: defaultCommands, + ServiceAccount: os.Getenv("POD_SERVICE_ACCOUNT"), + ImagePullSecrets: clusterImage.Spec.ImagePullSecrets, OwnerReferences: []metav1.OwnerReference{ { APIVersion: clusterImage.APIVersion, diff --git a/internal/controller/raczylo.com/clusterimageexport_controller.go b/internal/controller/raczylo.com/clusterimageexport_controller.go index 96a716e..f2db199 100644 --- a/internal/controller/raczylo.com/clusterimageexport_controller.go +++ b/internal/controller/raczylo.com/clusterimageexport_controller.go @@ -131,15 +131,16 @@ func (r *ClusterImageExportReconciler) Reconcile(ctx context.Context, req ctrl.R }, }, Spec: raczylocomv1.ClusterImageSpec{ - Image: image.Image, - Tag: image.Tag, - Sha: image.Sha, - FullName: image.FullName, - ImageNamespace: image.ImageNamespace, - Storage: clusterImageExport.Spec.Storage.StorageTarget, - ExportName: clusterImageExport.Name, - ExportPath: clusterImageExport.Spec.BasePath, - JobAnnotations: clusterImageExport.Spec.JobAnnotations, + Image: image.Image, + Tag: image.Tag, + Sha: image.Sha, + FullName: image.FullName, + ImageNamespace: image.ImageNamespace, + Storage: clusterImageExport.Spec.Storage.StorageTarget, + ExportName: clusterImageExport.Name, + ExportPath: clusterImageExport.Spec.BasePath, + JobAnnotations: clusterImageExport.Spec.JobAnnotations, + ImagePullSecrets: clusterImageExport.Spec.ImagePullSecrets, }, } diff --git a/internal/shared/jobs.go b/internal/shared/jobs.go index 1219248..f397c71 100644 --- a/internal/shared/jobs.go +++ b/internal/shared/jobs.go @@ -12,14 +12,15 @@ import ( ) type JobParams struct { - Name string - Namespace string - Annotations map[string]string - Image string - Commands []string - EnvVars []corev1.EnvVar - OwnerReferences []metav1.OwnerReference - ServiceAccount string + Name string + Namespace string + Annotations map[string]string + Image string + Commands []string + EnvVars []corev1.EnvVar + OwnerReferences []metav1.OwnerReference + ServiceAccount string + ImagePullSecrets []corev1.LocalObjectReference } func CreateJob[T any](params JobParams, setupFunc func(T) []string) *batchv1.Job { @@ -44,6 +45,7 @@ func CreateJob[T any](params JobParams, setupFunc func(T) []string) *batchv1.Job Spec: corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyOnFailure, ServiceAccountName: params.ServiceAccount, + ImagePullSecrets: params.ImagePullSecrets, Containers: []corev1.Container{ { Name: "export",