mirror of
				https://github.com/1Password/onepassword-operator.git
				synced 2025-10-31 03:39:39 +00:00 
			
		
		
		
	Compare commits
	
		
			12 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 58b4ff8ecf | ||
|   | d93fecdc76 | ||
|   | 486465247d | ||
|   | 79868ae374 | ||
|   | 6286f7e306 | ||
|   | 0b5efc8690 | ||
|   | a760e524ea | ||
|   | 19f774bb2d | ||
|   | 32643651d9 | ||
|   | ba8d3fa698 | ||
|   | c57aa22a9c | ||
|   | 48944b0d56 | 
							
								
								
									
										104
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,96 +1,128 @@ | |||||||
| [//]: # (START/LATEST) | [//]: # "START/LATEST" | ||||||
|  |  | ||||||
| # Latest | # Latest | ||||||
|  |  | ||||||
| ## Features | ## Features | ||||||
|   * A user-friendly description of a new feature. {issue-number} |  | ||||||
|  | - A user-friendly description of a new feature. {issue-number} | ||||||
|  |  | ||||||
| ## Fixes | ## Fixes | ||||||
|  * A user-friendly description of a fix. {issue-number} |  | ||||||
|  | - A user-friendly description of a fix. {issue-number} | ||||||
|  |  | ||||||
| ## Security | ## Security | ||||||
|  * A user-friendly description of a security fix. {issue-number} |  | ||||||
|  | - A user-friendly description of a security fix. {issue-number} | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| [//]: # (START/v1.3.0) | [//]: # "START/v1.4.0" | ||||||
|  |  | ||||||
|  | # v1.4.0 | ||||||
|  |  | ||||||
|  | ## Features | ||||||
|  |  | ||||||
|  | - The operator now declares the an OwnerReference for the secrets it manages. This should stop secrets from getting pruned by tools like Argo CD. {#51,#84,#96} | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | [//]: # "START/v1.3.0" | ||||||
|  |  | ||||||
| # v1.3.0 | # v1.3.0 | ||||||
|  |  | ||||||
| ## Features | ## Features | ||||||
|   * Added support for loading secrets from files stored in 1Password. {#47} |  | ||||||
|  | - Added support for loading secrets from files stored in 1Password. {#47} | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| [//]: # (START/v1.2.0) | [//]: # "START/v1.2.0" | ||||||
|  |  | ||||||
| # v1.2.0 | # v1.2.0 | ||||||
|  |  | ||||||
| ## Features | ## Features | ||||||
|   * Support secrets provisioned through FromEnv. {#74} |  | ||||||
|   * Support configuration of Kubernetes Secret type. {#87} | - Support secrets provisioned through FromEnv. {#74} | ||||||
|   * Improved logging. (#72) | - Support configuration of Kubernetes Secret type. {#87} | ||||||
|  | - Improved logging. (#72) | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| [//]: # (START/v1.1.0) | [//]: # "START/v1.1.0" | ||||||
|  |  | ||||||
| # v1.1.0 | # v1.1.0 | ||||||
|  |  | ||||||
| ## Fixes | ## Fixes | ||||||
|  * Fix normalization for keys in a Secret's `data` section to allow upper- and lower-case alphanumeric characters. {#66} |  | ||||||
|  | - Fix normalization for keys in a Secret's `data` section to allow upper- and lower-case alphanumeric characters. {#66} | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| [//]: # (START/v1.0.2) | [//]: # "START/v1.0.2" | ||||||
|  |  | ||||||
| # v1.0.2 | # v1.0.2 | ||||||
|  |  | ||||||
| ## Fixes | ## Fixes | ||||||
|  * Name normalizer added to handle non-conforming item names. |  | ||||||
|  | - Name normalizer added to handle non-conforming item names. | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| [//]: # (START/v1.0.1) | [//]: # "START/v1.0.1" | ||||||
|  |  | ||||||
| # v1.0.1 | # v1.0.1 | ||||||
|  |  | ||||||
| ## Features | ## Features | ||||||
| * This release also contains an arm64 Docker image. {#20} |  | ||||||
| * Docker images are also pushed to the :latest and :<major>.<minor> tags. | - This release also contains an arm64 Docker image. {#20} | ||||||
|  | - Docker images are also pushed to the :latest and :<major>.<minor> tags. | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| [//]: # (START/v1.0.0) | [//]: # "START/v1.0.0" | ||||||
|  |  | ||||||
| # v1.0.0 | # v1.0.0 | ||||||
|  |  | ||||||
| ## Features: | ## Features: | ||||||
| * Option to automatically deploy 1Password Connect via the operator |  | ||||||
| * Ignore restart annotation when looking for 1Password annotations | - Option to automatically deploy 1Password Connect via the operator | ||||||
| * Release Automation | - Ignore restart annotation when looking for 1Password annotations | ||||||
| * Upgrading apiextensions.k8s.io/v1beta apiversion from the operator custom resource | - Release Automation | ||||||
| * Adding configuration for auto rolling restart on deployments | - Upgrading apiextensions.k8s.io/v1beta apiversion from the operator custom resource | ||||||
| * Configure Auto Restarts for a OnePasswordItem Custom Resource | - Adding configuration for auto rolling restart on deployments | ||||||
| * Update Connect Dependencies to latest | - Configure Auto Restarts for a OnePasswordItem Custom Resource | ||||||
| * Add Github action for building and testing operator | - Update Connect Dependencies to latest | ||||||
|  | - Add Github action for building and testing operator | ||||||
|  |  | ||||||
| ## Fixes: | ## Fixes: | ||||||
| * Fix spec field example for OnePasswordItem in readme |  | ||||||
| * Casing of annotations are now consistent | - Fix spec field example for OnePasswordItem in readme | ||||||
|  | - Casing of annotations are now consistent | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| [//]: # (START/v0.0.2) | [//]: # "START/v0.0.2" | ||||||
|  |  | ||||||
| # v0.0.2 | # v0.0.2 | ||||||
|  |  | ||||||
| ## Features: | ## Features: | ||||||
| * Items can now be accessed by either `vaults/<vault_id>/items/<item_id>` or `vaults/<vault_title>/items/<item_title>` |  | ||||||
|  | - Items can now be accessed by either `vaults/<vault_id>/items/<item_id>` or `vaults/<vault_title>/items/<item_title>` | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| [//]: # (START/v0.0.1) | [//]: # "START/v0.0.1" | ||||||
|  |  | ||||||
| # v0.0.1 | # v0.0.1 | ||||||
|  |  | ||||||
| Initial 1Password Operator release | Initial 1Password Operator release | ||||||
|  |  | ||||||
| ## Features | ## Features | ||||||
| * watches for deployment creations with `onepassword` annotations and creates an associated kubernetes secret |  | ||||||
| * watches for `onepasswordsecret` crd creations and creates an associated kubernetes secrets | - watches for deployment creations with `onepassword` annotations and creates an associated kubernetes secret | ||||||
| * watches for changes to 1Password secrets associated with kubernetes secrets and updates the kubernetes secret with changes | - watches for `onepasswordsecret` crd creations and creates an associated kubernetes secrets | ||||||
| * restart pods when secret has been updated | - watches for changes to 1Password secrets associated with kubernetes secrets and updates the kubernetes secret with changes | ||||||
| * cleanup of kubernetes secrets when deployment or `onepasswordsecret` is deleted | - restart pods when secret has been updated | ||||||
|  | - cleanup of kubernetes secrets when deployment or `onepasswordsecret` is deleted | ||||||
|  |  | ||||||
| --- | --- | ||||||
|   | |||||||
| @@ -14,9 +14,11 @@ import ( | |||||||
| 	appsv1 "k8s.io/api/apps/v1" | 	appsv1 "k8s.io/api/apps/v1" | ||||||
| 	corev1 "k8s.io/api/core/v1" | 	corev1 "k8s.io/api/core/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/api/errors" | 	"k8s.io/apimachinery/pkg/api/errors" | ||||||
|  | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
| 	ctrl "sigs.k8s.io/controller-runtime" | 	ctrl "sigs.k8s.io/controller-runtime" | ||||||
| 	"sigs.k8s.io/controller-runtime/pkg/client" | 	"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/controller" | ||||||
| 	"sigs.k8s.io/controller-runtime/pkg/handler" | 	"sigs.k8s.io/controller-runtime/pkg/handler" | ||||||
| 	logf "sigs.k8s.io/controller-runtime/pkg/log" | 	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 | 		// 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{}, err | ||||||
| 		} | 		} | ||||||
| 		return reconcile.Result{}, nil | 		return reconcile.Result{}, nil | ||||||
| @@ -187,7 +189,7 @@ func (r *ReconcileDeployment) removeOnePasswordFinalizerFromDeployment(deploymen | |||||||
| 	return r.kubeClient.Update(context.Background(), deployment) | 	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) | 	reqLog := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) | ||||||
|  |  | ||||||
| 	secretName := annotations[op.NameAnnotation] | 	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 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) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -14,9 +14,11 @@ import ( | |||||||
|  |  | ||||||
| 	corev1 "k8s.io/api/core/v1" | 	corev1 "k8s.io/api/core/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/api/errors" | 	"k8s.io/apimachinery/pkg/api/errors" | ||||||
|  | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
| 	ctrl "sigs.k8s.io/controller-runtime" | 	ctrl "sigs.k8s.io/controller-runtime" | ||||||
| 	kubeClient "sigs.k8s.io/controller-runtime/pkg/client" | 	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/controller" | ||||||
| 	"sigs.k8s.io/controller-runtime/pkg/handler" | 	"sigs.k8s.io/controller-runtime/pkg/handler" | ||||||
| 	logf "sigs.k8s.io/controller-runtime/pkg/log" | 	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 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) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -101,7 +101,7 @@ var tests = []testReconcileItem{ | |||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		testName: "Test Do not update if OnePassword Version has not changed", | 		testName: "Test Do not update if OnePassword Version or VaultPath has not changed", | ||||||
| 		customResource: &onepasswordv1.OnePasswordItem{ | 		customResource: &onepasswordv1.OnePasswordItem{ | ||||||
| 			TypeMeta: metav1.TypeMeta{ | 			TypeMeta: metav1.TypeMeta{ | ||||||
| 				Kind:       onePasswordItemKind, | 				Kind:       onePasswordItemKind, | ||||||
|   | |||||||
| @@ -35,7 +35,7 @@ var ErrCannotUpdateSecretType = errs.New("Cannot change secret type. Secret type | |||||||
|  |  | ||||||
| var log = logf.Log | 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) | 	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. | 	// "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{} | 	currentSecret := &corev1.Secret{} | ||||||
| 	err := kubeClient.Get(context.Background(), types.NamespacedName{Name: secret.Name, Namespace: secret.Namespace}, currentSecret) | 	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 | 	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{ | 	return &corev1.Secret{ | ||||||
| 		ObjectMeta: metav1.ObjectMeta{ | 		ObjectMeta: metav1.ObjectMeta{ | ||||||
| 			Name:        formatSecretName(name), | 			Name:            formatSecretName(name), | ||||||
| 			Namespace:   namespace, | 			Namespace:       namespace, | ||||||
| 			Annotations: annotations, | 			Annotations:     annotations, | ||||||
| 			Labels:      labels, | 			Labels:          labels, | ||||||
|  | 			OwnerReferences: ownerRefs, | ||||||
| 		}, | 		}, | ||||||
| 		Data: BuildKubernetesSecretData(item.Fields, item.Files), | 		Data: BuildKubernetesSecretData(item.Fields, item.Files), | ||||||
| 		Type: corev1.SecretType(secretType), | 		Type: corev1.SecretType(secretType), | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/1Password/connect-sdk-go/onepassword" | 	"github.com/1Password/connect-sdk-go/onepassword" | ||||||
| 	corev1 "k8s.io/api/core/v1" | 	corev1 "k8s.io/api/core/v1" | ||||||
|  | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/types" | 	"k8s.io/apimachinery/pkg/types" | ||||||
| 	kubeValidate "k8s.io/apimachinery/pkg/util/validation" | 	kubeValidate "k8s.io/apimachinery/pkg/util/validation" | ||||||
| 	"k8s.io/client-go/kubernetes" | 	"k8s.io/client-go/kubernetes" | ||||||
| @@ -37,7 +38,7 @@ func TestCreateKubernetesSecretFromOnePasswordItem(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 	secretType := "" | 	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 { | 	if err != nil { | ||||||
| 		t.Errorf("Unexpected error: %v", err) | 		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) { | func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) { | ||||||
| 	secretName := "test-secret-update" | 	secretName := "test-secret-update" | ||||||
| 	namespace := "test" | 	namespace := "test" | ||||||
| @@ -70,7 +119,7 @@ func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) { | |||||||
| 	secretAnnotations := map[string]string{} | 	secretAnnotations := map[string]string{} | ||||||
| 	secretType := "" | 	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 { | 	if err != nil { | ||||||
| 		t.Errorf("Unexpected error: %v", err) | 		t.Errorf("Unexpected error: %v", err) | ||||||
| @@ -82,7 +131,7 @@ func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) { | |||||||
| 	newItem.Version = 456 | 	newItem.Version = 456 | ||||||
| 	newItem.Vault.ID = "hfnjvi6aymbsnfc2xeeoheizda" | 	newItem.Vault.ID = "hfnjvi6aymbsnfc2xeeoheizda" | ||||||
| 	newItem.ID = "h46bb3jddvay7nxopfhvlwg35q" | 	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 { | 	if err != nil { | ||||||
| 		t.Errorf("Unexpected error: %v", err) | 		t.Errorf("Unexpected error: %v", err) | ||||||
| 	} | 	} | ||||||
| @@ -118,7 +167,7 @@ func TestBuildKubernetesSecretFromOnePasswordItem(t *testing.T) { | |||||||
| 	labels := map[string]string{} | 	labels := map[string]string{} | ||||||
| 	secretType := "" | 	secretType := "" | ||||||
|  |  | ||||||
| 	kubeSecret := BuildKubernetesSecretFromOnePasswordItem(name, namespace, annotations, labels, secretType, item) | 	kubeSecret := BuildKubernetesSecretFromOnePasswordItem(name, namespace, annotations, labels, secretType, item, nil) | ||||||
| 	if kubeSecret.Name != strings.ToLower(name) { | 	if kubeSecret.Name != strings.ToLower(name) { | ||||||
| 		t.Errorf("Expected name value: %v but got: %v", name, kubeSecret.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 | 	// Assert Secret's meta.name was fixed | ||||||
| 	if kubeSecret.Name != expectedName { | 	if kubeSecret.Name != expectedName { | ||||||
| @@ -188,7 +237,7 @@ func TestCreateKubernetesTLSSecretFromOnePasswordItem(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 	secretType := "kubernetes.io/tls" | 	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 { | 	if err != nil { | ||||||
| 		t.Errorf("Unexpected error: %v", err) | 		t.Errorf("Unexpected error: %v", err) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -5,6 +5,8 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	v1 "github.com/1Password/onepassword-operator/pkg/apis/onepassword/v1" | ||||||
|  |  | ||||||
| 	kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets" | 	kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets" | ||||||
| 	"github.com/1Password/onepassword-operator/pkg/utils" | 	"github.com/1Password/onepassword-operator/pkg/utils" | ||||||
|  |  | ||||||
| @@ -116,23 +118,30 @@ func (h *SecretUpdateHandler) updateKubernetesSecrets() (map[string]map[string]* | |||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		item, err := GetOnePasswordItemByPath(h.opConnectClient, secret.Annotations[ItemPathAnnotation]) | 		OnePasswordItemPath := h.getPathFromOnePasswordItem(secret) | ||||||
|  |  | ||||||
|  | 		item, err := GetOnePasswordItemByPath(h.opConnectClient, OnePasswordItemPath) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Error(err, "failed to retrieve 1Password item at path \"%s\" for secret \"%s\"", secret.Annotations[ItemPathAnnotation], secret.Name) | 			log.Error(err, "failed to retrieve 1Password item at path \"%s\" for secret \"%s\"", secret.Annotations[ItemPathAnnotation], secret.Name) | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		itemVersion := fmt.Sprint(item.Version) | 		itemVersion := fmt.Sprint(item.Version) | ||||||
| 		if currentVersion != itemVersion { | 		itemPathString := fmt.Sprintf("vaults/%v/items/%v", item.Vault.ID, item.ID) | ||||||
|  |  | ||||||
|  | 		if currentVersion != itemVersion || secret.Annotations[ItemPathAnnotation] != itemPathString { | ||||||
| 			if isItemLockedForForcedRestarts(item) { | 			if isItemLockedForForcedRestarts(item) { | ||||||
| 				log.Info(fmt.Sprintf("Secret '%v' has been updated in 1Password but is set to be ignored. Updates to an ignored secret will not trigger an update to a kubernetes secret or a rolling restart.", secret.GetName())) | 				log.Info(fmt.Sprintf("Secret '%v' has been updated in 1Password but is set to be ignored. Updates to an ignored secret will not trigger an update to a kubernetes secret or a rolling restart.", secret.GetName())) | ||||||
| 				secret.Annotations[VersionAnnotation] = itemVersion | 				secret.Annotations[VersionAnnotation] = itemVersion | ||||||
|  | 				secret.Annotations[ItemPathAnnotation] = itemPathString | ||||||
| 				h.client.Update(context.Background(), &secret) | 				h.client.Update(context.Background(), &secret) | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
| 			log.Info(fmt.Sprintf("Updating kubernetes secret '%v'", secret.GetName())) | 			log.Info(fmt.Sprintf("Updating kubernetes secret '%v'", secret.GetName())) | ||||||
| 			secret.Annotations[VersionAnnotation] = itemVersion | 			secret.Annotations[VersionAnnotation] = itemVersion | ||||||
| 			updatedSecret := kubeSecrets.BuildKubernetesSecretFromOnePasswordItem(secret.Name, secret.Namespace, secret.Annotations, secret.Labels, string(secret.Type), *item) | 			secret.Annotations[ItemPathAnnotation] = itemPathString | ||||||
|  | 			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) | 			h.client.Update(context.Background(), updatedSecret) | ||||||
| 			if updatedSecrets[secret.Namespace] == nil { | 			if updatedSecrets[secret.Namespace] == nil { | ||||||
| 				updatedSecrets[secret.Namespace] = make(map[string]*corev1.Secret) | 				updatedSecrets[secret.Namespace] = make(map[string]*corev1.Secret) | ||||||
| @@ -177,6 +186,22 @@ func (h *SecretUpdateHandler) getIsSetForAutoRestartByNamespaceMap() (map[string | |||||||
| 	return namespacesMap, nil | 	return namespacesMap, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (h *SecretUpdateHandler) getPathFromOnePasswordItem(secret corev1.Secret) string { | ||||||
|  | 	onePasswordItem := &v1.OnePasswordItem{} | ||||||
|  |  | ||||||
|  | 	// Search for our original OnePasswordItem if it exists | ||||||
|  | 	err := h.client.Get(context.TODO(), client.ObjectKey{ | ||||||
|  | 		Namespace: secret.Namespace, | ||||||
|  | 		Name:      secret.Name}, onePasswordItem) | ||||||
|  |  | ||||||
|  | 	if err == nil { | ||||||
|  | 		return onePasswordItem.Spec.ItemPath | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// If we can't find the OnePassword Item we'll just return the annotation from the secret item. | ||||||
|  | 	return secret.Annotations[ItemPathAnnotation] | ||||||
|  | } | ||||||
|  |  | ||||||
| func isSecretSetForAutoRestart(secret *corev1.Secret, deployment *appsv1.Deployment, setForAutoRestartByNamespace map[string]bool) bool { | func isSecretSetForAutoRestart(secret *corev1.Secret, deployment *appsv1.Deployment, setForAutoRestartByNamespace map[string]bool) bool { | ||||||
| 	restartDeployment := secret.Annotations[RestartDeploymentsAnnotation] | 	restartDeployment := secret.Annotations[RestartDeploymentsAnnotation] | ||||||
| 	//If annotation for auto restarts for deployment is not set. Check for the annotation on its namepsace | 	//If annotation for auto restarts for deployment is not set. Check for the annotation on its namepsace | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user