From 79868ae374f4dde7a5d9a73977e3094ff1e1333b Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Tue, 5 Apr 2022 20:04:18 +0200 Subject: [PATCH] Add owner reference to the created secrets Signed-off-by: Xabier Larrakoetxea --- .../deployment/deployment_controller.go | 20 +++++- .../onepassworditem_controller.go | 16 ++++- .../kubernetes_secrets_builder.go | 20 +++--- .../kubernetes_secrets_builder_test.go | 61 +++++++++++++++++-- pkg/onepassword/secret_update_handler.go | 2 +- 5 files changed, 101 insertions(+), 18 deletions(-) diff --git a/pkg/controller/deployment/deployment_controller.go b/pkg/controller/deployment/deployment_controller.go index fc2183db..0e709a60 100644 --- a/pkg/controller/deployment/deployment_controller.go +++ b/pkg/controller/deployment/deployment_controller.go @@ -14,9 +14,11 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -114,7 +116,7 @@ func (r *ReconcileDeployment) Reconcile(request reconcile.Request) (reconcile.Re } } // Handles creation or updating secrets for deployment if needed - if err := r.HandleApplyingDeployment(deployment.Namespace, annotations, request); err != nil { + if err := r.HandleApplyingDeployment(deployment, deployment.Namespace, annotations, request); err != nil { return reconcile.Result{}, err } return reconcile.Result{}, nil @@ -187,7 +189,7 @@ func (r *ReconcileDeployment) removeOnePasswordFinalizerFromDeployment(deploymen return r.kubeClient.Update(context.Background(), deployment) } -func (r *ReconcileDeployment) HandleApplyingDeployment(namespace string, annotations map[string]string, request reconcile.Request) error { +func (r *ReconcileDeployment) HandleApplyingDeployment(deployment *appsv1.Deployment, namespace string, annotations map[string]string, request reconcile.Request) error { reqLog := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) secretName := annotations[op.NameAnnotation] @@ -204,5 +206,17 @@ func (r *ReconcileDeployment) HandleApplyingDeployment(namespace string, annotat return fmt.Errorf("Failed to retrieve item: %v", err) } - return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, namespace, item, annotations[op.RestartDeploymentsAnnotation], secretLabels, secretType, annotations) + // Create owner reference. + gvk, err := apiutil.GVKForObject(deployment, r.scheme) + if err != nil { + return fmt.Errorf("could not to retrieve group version kind: %v", err) + } + ownerRef := &metav1.OwnerReference{ + APIVersion: gvk.GroupVersion().String(), + Kind: gvk.Kind, + Name: deployment.GetName(), + UID: deployment.GetUID(), + } + + return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, namespace, item, annotations[op.RestartDeploymentsAnnotation], secretLabels, secretType, annotations, ownerRef) } diff --git a/pkg/controller/onepassworditem/onepassworditem_controller.go b/pkg/controller/onepassworditem/onepassworditem_controller.go index 0788f02e..4b2e6957 100644 --- a/pkg/controller/onepassworditem/onepassworditem_controller.go +++ b/pkg/controller/onepassworditem/onepassworditem_controller.go @@ -14,9 +14,11 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" kubeClient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -154,5 +156,17 @@ func (r *ReconcileOnePasswordItem) HandleOnePasswordItem(resource *onepasswordv1 return fmt.Errorf("Failed to retrieve item: %v", err) } - return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, resource.Namespace, item, autoRestart, labels, secretType, annotations) + // Create owner reference. + gvk, err := apiutil.GVKForObject(resource, r.scheme) + if err != nil { + return fmt.Errorf("could not to retrieve group version kind: %v", err) + } + ownerRef := &metav1.OwnerReference{ + APIVersion: gvk.GroupVersion().String(), + Kind: gvk.Kind, + Name: resource.GetName(), + UID: resource.GetUID(), + } + + return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, resource.Namespace, item, autoRestart, labels, secretType, annotations, ownerRef) } diff --git a/pkg/kubernetessecrets/kubernetes_secrets_builder.go b/pkg/kubernetessecrets/kubernetes_secrets_builder.go index 34157376..2cd3a052 100644 --- a/pkg/kubernetessecrets/kubernetes_secrets_builder.go +++ b/pkg/kubernetessecrets/kubernetes_secrets_builder.go @@ -35,7 +35,7 @@ var ErrCannotUpdateSecretType = errs.New("Cannot change secret type. Secret type var log = logf.Log -func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretName, namespace string, item *onepassword.Item, autoRestart string, labels map[string]string, secretType string, secretAnnotations map[string]string) error { +func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretName, namespace string, item *onepassword.Item, autoRestart string, labels map[string]string, secretType string, secretAnnotations map[string]string, ownerRef *metav1.OwnerReference) error { itemVersion := fmt.Sprint(item.Version) @@ -57,7 +57,7 @@ func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretNa } // "Opaque" and "" secret types are treated the same by Kubernetes. - secret := BuildKubernetesSecretFromOnePasswordItem(secretName, namespace, secretAnnotations, labels, secretType, *item) + secret := BuildKubernetesSecretFromOnePasswordItem(secretName, namespace, secretAnnotations, labels, secretType, *item, ownerRef) currentSecret := &corev1.Secret{} err := kubeClient.Get(context.Background(), types.NamespacedName{Name: secret.Name, Namespace: secret.Namespace}, currentSecret) @@ -87,13 +87,19 @@ func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretNa return nil } -func BuildKubernetesSecretFromOnePasswordItem(name, namespace string, annotations map[string]string, labels map[string]string, secretType string, item onepassword.Item) *corev1.Secret { +func BuildKubernetesSecretFromOnePasswordItem(name, namespace string, annotations map[string]string, labels map[string]string, secretType string, item onepassword.Item, ownerRef *metav1.OwnerReference) *corev1.Secret { + var ownerRefs []metav1.OwnerReference + if ownerRef != nil { + ownerRefs = []metav1.OwnerReference{*ownerRef} + } + return &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: formatSecretName(name), - Namespace: namespace, - Annotations: annotations, - Labels: labels, + Name: formatSecretName(name), + Namespace: namespace, + Annotations: annotations, + Labels: labels, + OwnerReferences: ownerRefs, }, Data: BuildKubernetesSecretData(item.Fields, item.Files), Type: corev1.SecretType(secretType), diff --git a/pkg/kubernetessecrets/kubernetes_secrets_builder_test.go b/pkg/kubernetessecrets/kubernetes_secrets_builder_test.go index 2bc9faf0..47475048 100644 --- a/pkg/kubernetessecrets/kubernetes_secrets_builder_test.go +++ b/pkg/kubernetessecrets/kubernetes_secrets_builder_test.go @@ -8,6 +8,7 @@ import ( "github.com/1Password/connect-sdk-go/onepassword" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" kubeValidate "k8s.io/apimachinery/pkg/util/validation" "k8s.io/client-go/kubernetes" @@ -37,7 +38,7 @@ func TestCreateKubernetesSecretFromOnePasswordItem(t *testing.T) { } secretType := "" - err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretType, secretAnnotations) + err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretType, secretAnnotations, nil) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -55,6 +56,54 @@ func TestCreateKubernetesSecretFromOnePasswordItem(t *testing.T) { } } +func TestKubernetesSecretFromOnePasswordItemOwnerReferences(t *testing.T) { + secretName := "test-secret-name" + namespace := "test" + + item := onepassword.Item{} + item.Fields = generateFields(5) + item.Version = 123 + item.Vault.ID = "hfnjvi6aymbsnfc2xeeoheizda" + item.ID = "h46bb3jddvay7nxopfhvlwg35q" + + kubeClient := fake.NewFakeClient() + secretLabels := map[string]string{} + secretAnnotations := map[string]string{ + "testAnnotation": "exists", + } + secretType := "" + + ownerRef := &metav1.OwnerReference{ + Kind: "Deployment", + APIVersion: "apps/v1", + Name: "test-deployment", + UID: types.UID("test-uid"), + } + err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretType, secretAnnotations, ownerRef) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + createdSecret := &corev1.Secret{} + err = kubeClient.Get(context.Background(), types.NamespacedName{Name: secretName, Namespace: namespace}, createdSecret) + + // Check owner references. + gotOwnerRefs := createdSecret.ObjectMeta.OwnerReferences + if len(gotOwnerRefs) != 1 { + t.Errorf("Expected owner references length: 1 but got: %d", len(gotOwnerRefs)) + } + + expOwnerRef := metav1.OwnerReference{ + Kind: "Deployment", + APIVersion: "apps/v1", + Name: "test-deployment", + UID: types.UID("test-uid"), + } + gotOwnerRef := gotOwnerRefs[0] + if gotOwnerRef != expOwnerRef { + t.Errorf("Expected owner reference value: %v but got: %v", expOwnerRef, gotOwnerRef) + } +} + func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) { secretName := "test-secret-update" namespace := "test" @@ -70,7 +119,7 @@ func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) { secretAnnotations := map[string]string{} secretType := "" - err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretType, secretAnnotations) + err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretType, secretAnnotations, nil) if err != nil { t.Errorf("Unexpected error: %v", err) @@ -82,7 +131,7 @@ func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) { newItem.Version = 456 newItem.Vault.ID = "hfnjvi6aymbsnfc2xeeoheizda" newItem.ID = "h46bb3jddvay7nxopfhvlwg35q" - err = CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &newItem, restartDeploymentAnnotation, secretLabels, secretType, secretAnnotations) + err = CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &newItem, restartDeploymentAnnotation, secretLabels, secretType, secretAnnotations, nil) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -118,7 +167,7 @@ func TestBuildKubernetesSecretFromOnePasswordItem(t *testing.T) { labels := map[string]string{} secretType := "" - kubeSecret := BuildKubernetesSecretFromOnePasswordItem(name, namespace, annotations, labels, secretType, item) + kubeSecret := BuildKubernetesSecretFromOnePasswordItem(name, namespace, annotations, labels, secretType, item, nil) if kubeSecret.Name != strings.ToLower(name) { t.Errorf("Expected name value: %v but got: %v", name, kubeSecret.Name) } @@ -153,7 +202,7 @@ func TestBuildKubernetesSecretFixesInvalidLabels(t *testing.T) { }, } - kubeSecret := BuildKubernetesSecretFromOnePasswordItem(name, namespace, annotations, labels, secretType, item) + kubeSecret := BuildKubernetesSecretFromOnePasswordItem(name, namespace, annotations, labels, secretType, item, nil) // Assert Secret's meta.name was fixed if kubeSecret.Name != expectedName { @@ -188,7 +237,7 @@ func TestCreateKubernetesTLSSecretFromOnePasswordItem(t *testing.T) { } secretType := "kubernetes.io/tls" - err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretType, secretAnnotations) + err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretType, secretAnnotations, nil) if err != nil { t.Errorf("Unexpected error: %v", err) } diff --git a/pkg/onepassword/secret_update_handler.go b/pkg/onepassword/secret_update_handler.go index f83a139b..a7a0c7c5 100644 --- a/pkg/onepassword/secret_update_handler.go +++ b/pkg/onepassword/secret_update_handler.go @@ -140,7 +140,7 @@ func (h *SecretUpdateHandler) updateKubernetesSecrets() (map[string]map[string]* log.Info(fmt.Sprintf("Updating kubernetes secret '%v'", secret.GetName())) secret.Annotations[VersionAnnotation] = itemVersion secret.Annotations[ItemPathAnnotation] = itemPathString - updatedSecret := kubeSecrets.BuildKubernetesSecretFromOnePasswordItem(secret.Name, secret.Namespace, secret.Annotations, secret.Labels, string(secret.Type), *item) + updatedSecret := kubeSecrets.BuildKubernetesSecretFromOnePasswordItem(secret.Name, secret.Namespace, secret.Annotations, secret.Labels, string(secret.Type), *item, nil) log.Info(fmt.Sprintf("New secret path: %v and version: %v", updatedSecret.Annotations[ItemPathAnnotation], updatedSecret.Annotations[VersionAnnotation])) h.client.Update(context.Background(), updatedSecret) if updatedSecrets[secret.Namespace] == nil {