From 36ed728a15abfa81634b256b899fba72ef7a5f1d Mon Sep 17 00:00:00 2001 From: Paul Laffitte Date: Wed, 22 Nov 2023 17:41:53 +0100 Subject: [PATCH] feat(controller): use imagePullSecrets from ServiceAccounts --- controllers/pod_controller.go | 26 +++++++++++++ controllers/pod_controller_test.go | 37 +++++++++++++++++++ controllers/suite_test.go | 1 + .../templates/clusterrole.yaml | 7 ++++ 4 files changed, 71 insertions(+) diff --git a/controllers/pod_controller.go b/controllers/pod_controller.go index 73186baa..11b85320 100644 --- a/controllers/pod_controller.go +++ b/controllers/pod_controller.go @@ -60,6 +60,10 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R log.Info("reconciling pod") cachedImages := desiredCachedImages(ctx, &pod) + serviceAccountImagePullSecrets, err := r.imagePullSecretNamesFromPodServiceAccount(ctx, &pod) + if err != nil { + return ctrl.Result{}, err + } // On pod deletion if !pod.DeletionTimestamp.IsZero() { @@ -81,6 +85,8 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R continue } + cachedImage.Spec.PullSecretNames = append(cachedImage.Spec.PullSecretNames, serviceAccountImagePullSecrets...) + // Create or update CachedImage depending on weather it already exists or not if apierrors.IsNotFound(err) { err = r.Create(ctx, &cachedImage) @@ -242,3 +248,23 @@ func cachedImageFromSourceImage(sourceImage string) (*kuikenixiov1alpha1.CachedI return &cachedImage, nil } + +func (r *PodReconciler) imagePullSecretNamesFromPodServiceAccount(ctx context.Context, pod *corev1.Pod) ([]string, error) { + if pod.Spec.ServiceAccountName == "" { + return []string{}, nil + } + + var serviceAccount corev1.ServiceAccount + serviceAccountNamespacedName := types.NamespacedName{Namespace: pod.Namespace, Name: pod.Spec.ServiceAccountName} + if err := r.Get(ctx, serviceAccountNamespacedName, &serviceAccount); err != nil { + return []string{}, err + } + + imagePullSecretNames := make([]string, len(serviceAccount.ImagePullSecrets)) + + for i, imagePullSecret := range serviceAccount.ImagePullSecrets { + imagePullSecretNames[i] = imagePullSecret.Name + } + + return imagePullSecretNames, nil +} diff --git a/controllers/pod_controller_test.go b/controllers/pod_controller_test.go index 74137600..fc5fe899 100644 --- a/controllers/pod_controller_test.go +++ b/controllers/pod_controller_test.go @@ -38,6 +38,17 @@ var podStub = corev1.Pod{ }, } +var serviceAccountStub = corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: podStub.Namespace, + }, + ImagePullSecrets: []corev1.LocalObjectReference{ + {Name: "service-account-pull-secret"}, + {Name: "service-account-pull-secret-2"}, + }, +} + var podStubNotRewritten = corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test-pod", @@ -156,6 +167,10 @@ var _ = Describe("Pod Controller", func() { Expect(k8sClient.Delete(context.Background(), &podStub)).Should(suceedOrNotFound) Expect(k8sClient.Delete(context.Background(), &podStubNotRewritten)).Should(suceedOrNotFound) + // allow to reuse the stub + podStub.ResourceVersion = "" + podStubNotRewritten.ResourceVersion = "" + By("Deleting all cached images") Expect(k8sClient.DeleteAllOf(context.Background(), &kuikenixiov1alpha1.CachedImage{})).Should(Succeed()) }) @@ -205,5 +220,27 @@ var _ = Describe("Pod Controller", func() { return fetched.Items }, timeout, interval).Should(HaveLen(0)) }) + It("Should create CachedImages with imagePullSecrets from Pod's ServiceAccount", func() { + By("Creating a Pod with a ServiceAccount") + Expect(k8sClient.Create(context.Background(), &serviceAccountStub)).Should(Succeed()) + + podStub.Spec.ServiceAccountName = serviceAccountStub.Name + Expect(k8sClient.Create(context.Background(), &podStub)).Should(Succeed()) + + fetched := &kuikenixiov1alpha1.CachedImageList{} + Eventually(func() []kuikenixiov1alpha1.CachedImage { + _ = k8sClient.List(context.Background(), fetched) + return fetched.Items + }, timeout, interval).Should(HaveLen(len(podStub.Spec.Containers) + len(podStub.Spec.InitContainers))) + + imagePullSecretNames := make([]string, len(serviceAccountStub.ImagePullSecrets)) + for i, imagePullSecretName := range serviceAccountStub.ImagePullSecrets { + imagePullSecretNames[i] = imagePullSecretName.Name + } + + for _, cachedImage := range fetched.Items { + Expect(cachedImage.Spec.PullSecretNames).Should(ContainElements(imagePullSecretNames)) + } + }) }) }) diff --git a/controllers/suite_test.go b/controllers/suite_test.go index a7df1298..444067c5 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -139,6 +139,7 @@ var _ = BeforeSuite(func() { err = (&CachedImageReconciler{ Client: k8sManager.GetClient(), + ApiReader: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: k8sManager.GetEventRecorderFor("cachedimage-controller"), ExpiryDelay: 1 * time.Hour, diff --git a/helm/kube-image-keeper/templates/clusterrole.yaml b/helm/kube-image-keeper/templates/clusterrole.yaml index f0e36137..084978b9 100644 --- a/helm/kube-image-keeper/templates/clusterrole.yaml +++ b/helm/kube-image-keeper/templates/clusterrole.yaml @@ -23,6 +23,13 @@ rules: - patch - update - watch + - apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - list + - watch - apiGroups: - "" resources: