mirror of
https://github.com/1Password/onepassword-operator.git
synced 2025-10-22 15:38:06 +00:00
Merge branch 'main' into secret-path-updates
This commit is contained in:
@@ -26,6 +26,7 @@ type OnePasswordItemStatus struct {
|
||||
type OnePasswordItem struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
|
||||
Spec OnePasswordItemSpec `json:"spec,omitempty"`
|
||||
Status OnePasswordItemStatus `json:"status,omitempty"`
|
||||
|
@@ -192,6 +192,8 @@ func (r *ReconcileDeployment) HandleApplyingDeployment(namespace string, annotat
|
||||
|
||||
secretName := annotations[op.NameAnnotation]
|
||||
secretLabels := map[string]string(nil)
|
||||
secretType := ""
|
||||
|
||||
if len(secretName) == 0 {
|
||||
reqLog.Info("No 'item-name' annotation set. 'item-path' and 'item-name' must be set as annotations to add new secret.")
|
||||
return nil
|
||||
@@ -202,5 +204,5 @@ 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, annotations)
|
||||
return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, namespace, item, annotations[op.RestartDeploymentsAnnotation], secretLabels, secretType, annotations)
|
||||
}
|
||||
|
@@ -279,7 +279,7 @@ var tests = []testReconcileItem{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
op.NameAnnotation: name,
|
||||
},
|
||||
@@ -292,7 +292,7 @@ var tests = []testReconcileItem{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
op.NameAnnotation: name,
|
||||
},
|
||||
@@ -329,6 +329,7 @@ var tests = []testReconcileItem{
|
||||
op.VersionAnnotation: "456",
|
||||
},
|
||||
},
|
||||
Type: corev1.SecretType(""),
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
expectedError: nil,
|
||||
@@ -340,6 +341,7 @@ var tests = []testReconcileItem{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
},
|
||||
},
|
||||
Type: corev1.SecretType(""),
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
opItem: map[string]string{
|
||||
@@ -373,6 +375,7 @@ var tests = []testReconcileItem{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
},
|
||||
},
|
||||
Type: corev1.SecretType(""),
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
opItem: map[string]string{
|
||||
|
@@ -3,6 +3,7 @@ package onepassworditem
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
onepasswordv1 "github.com/1Password/onepassword-operator/pkg/apis/onepassword/v1"
|
||||
kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets"
|
||||
"github.com/1Password/onepassword-operator/pkg/onepassword"
|
||||
@@ -145,6 +146,7 @@ func (r *ReconcileOnePasswordItem) HandleOnePasswordItem(resource *onepasswordv1
|
||||
secretName := resource.GetName()
|
||||
labels := resource.Labels
|
||||
annotations := resource.Annotations
|
||||
secretType := resource.Type
|
||||
autoRestart := annotations[op.RestartDeploymentsAnnotation]
|
||||
|
||||
item, err := onepassword.GetOnePasswordItemByPath(r.opConnectClient, resource.Spec.ItemPath)
|
||||
@@ -152,5 +154,5 @@ 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, annotations)
|
||||
return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, resource.Namespace, item, autoRestart, labels, secretType, annotations)
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/1Password/onepassword-operator/pkg/kubernetessecrets"
|
||||
"github.com/1Password/onepassword-operator/pkg/mocks"
|
||||
op "github.com/1Password/onepassword-operator/pkg/onepassword"
|
||||
|
||||
@@ -119,7 +120,7 @@ var tests = []testReconcileItem{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
},
|
||||
},
|
||||
@@ -131,7 +132,7 @@ var tests = []testReconcileItem{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
},
|
||||
},
|
||||
@@ -153,7 +154,7 @@ var tests = []testReconcileItem{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
},
|
||||
Labels: map[string]string{},
|
||||
@@ -167,7 +168,7 @@ var tests = []testReconcileItem{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: "456",
|
||||
op.VersionAnnotation: "456",
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
},
|
||||
Labels: map[string]string{},
|
||||
@@ -180,7 +181,7 @@ var tests = []testReconcileItem{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
},
|
||||
Labels: map[string]string{},
|
||||
@@ -192,6 +193,59 @@ var tests = []testReconcileItem{
|
||||
passKey: password,
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Test Updating Type of Existing Kubernetes Secret using OnePasswordItem",
|
||||
customResource: &onepasswordv1.OnePasswordItem{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: onePasswordItemKind,
|
||||
APIVersion: onePasswordItemAPIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
},
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
Spec: onepasswordv1.OnePasswordItemSpec{
|
||||
ItemPath: itemPath,
|
||||
},
|
||||
Type: string(corev1.SecretTypeBasicAuth),
|
||||
},
|
||||
existingSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
},
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
Type: corev1.SecretTypeBasicAuth,
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
expectedError: nil,
|
||||
expectedResultSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
op.ItemPathAnnotation: itemPath,
|
||||
},
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
Type: corev1.SecretTypeBasicAuth,
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
opItem: map[string]string{
|
||||
userKey: username,
|
||||
passKey: password,
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Custom secret type",
|
||||
customResource: &onepasswordv1.OnePasswordItem{
|
||||
@@ -206,6 +260,7 @@ var tests = []testReconcileItem{
|
||||
Spec: onepasswordv1.OnePasswordItemSpec{
|
||||
ItemPath: itemPath,
|
||||
},
|
||||
Type: "custom",
|
||||
},
|
||||
existingSecret: nil,
|
||||
expectedError: nil,
|
||||
@@ -217,6 +272,51 @@ var tests = []testReconcileItem{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
},
|
||||
},
|
||||
Type: corev1.SecretType("custom"),
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
opItem: map[string]string{
|
||||
userKey: username,
|
||||
passKey: password,
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Error if secret type is changed",
|
||||
customResource: &onepasswordv1.OnePasswordItem{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: onePasswordItemKind,
|
||||
APIVersion: onePasswordItemAPIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: onepasswordv1.OnePasswordItemSpec{
|
||||
ItemPath: itemPath,
|
||||
},
|
||||
Type: "custom",
|
||||
},
|
||||
existingSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
},
|
||||
},
|
||||
Type: corev1.SecretTypeOpaque,
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
expectedError: kubernetessecrets.ErrCannotUpdateSecretType,
|
||||
expectedResultSecret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
op.VersionAnnotation: fmt.Sprint(version),
|
||||
},
|
||||
},
|
||||
Type: corev1.SecretTypeOpaque,
|
||||
Data: expectedSecretData,
|
||||
},
|
||||
opItem: map[string]string{
|
||||
|
@@ -7,13 +7,17 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"reflect"
|
||||
|
||||
errs "errors"
|
||||
|
||||
"github.com/1Password/connect-sdk-go/onepassword"
|
||||
|
||||
"github.com/1Password/onepassword-operator/pkg/utils"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"reflect"
|
||||
kubeValidate "k8s.io/apimachinery/pkg/util/validation"
|
||||
|
||||
kubernetesClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
@@ -27,9 +31,11 @@ const restartAnnotation = OnepasswordPrefix + "/last-restarted"
|
||||
const ItemPathAnnotation = OnepasswordPrefix + "/item-path"
|
||||
const RestartDeploymentsAnnotation = OnepasswordPrefix + "/auto-restart"
|
||||
|
||||
var ErrCannotUpdateSecretType = errs.New("Cannot change secret type. Secret type is immutable")
|
||||
|
||||
var log = logf.Log
|
||||
|
||||
func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretName, namespace string, item *onepassword.Item, autoRestart string, labels map[string]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) error {
|
||||
|
||||
itemVersion := fmt.Sprint(item.Version)
|
||||
|
||||
@@ -49,7 +55,9 @@ func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretNa
|
||||
}
|
||||
secretAnnotations[RestartDeploymentsAnnotation] = autoRestart
|
||||
}
|
||||
secret := BuildKubernetesSecretFromOnePasswordItem(secretName, namespace, secretAnnotations, labels, *item)
|
||||
|
||||
// "Opaque" and "" secret types are treated the same by Kubernetes.
|
||||
secret := BuildKubernetesSecretFromOnePasswordItem(secretName, namespace, secretAnnotations, labels, secretType, *item)
|
||||
|
||||
currentSecret := &corev1.Secret{}
|
||||
err := kubeClient.Get(context.Background(), types.NamespacedName{Name: secret.Name, Namespace: secret.Namespace}, currentSecret)
|
||||
@@ -60,7 +68,14 @@ func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretNa
|
||||
return err
|
||||
}
|
||||
|
||||
if ! reflect.DeepEqual(currentSecret.Annotations, secretAnnotations) || ! reflect.DeepEqual(currentSecret.Labels, labels) {
|
||||
currentAnnotations := currentSecret.Annotations
|
||||
currentLabels := currentSecret.Labels
|
||||
currentSecretType := string(currentSecret.Type)
|
||||
if !reflect.DeepEqual(currentSecretType, secretType) {
|
||||
return ErrCannotUpdateSecretType
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(currentAnnotations, secretAnnotations) || !reflect.DeepEqual(currentLabels, labels) {
|
||||
log.Info(fmt.Sprintf("Updating Secret %v at namespace '%v'", secret.Name, secret.Namespace))
|
||||
currentSecret.ObjectMeta.Annotations = secretAnnotations
|
||||
currentSecret.ObjectMeta.Labels = labels
|
||||
@@ -72,7 +87,7 @@ func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretNa
|
||||
return nil
|
||||
}
|
||||
|
||||
func BuildKubernetesSecretFromOnePasswordItem(name, namespace string, annotations map[string]string, labels map[string]string, item onepassword.Item) *corev1.Secret {
|
||||
func BuildKubernetesSecretFromOnePasswordItem(name, namespace string, annotations map[string]string, labels map[string]string, secretType string, item onepassword.Item) *corev1.Secret {
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: formatSecretName(name),
|
||||
@@ -80,11 +95,12 @@ func BuildKubernetesSecretFromOnePasswordItem(name, namespace string, annotation
|
||||
Annotations: annotations,
|
||||
Labels: labels,
|
||||
},
|
||||
Data: BuildKubernetesSecretData(item.Fields),
|
||||
Data: BuildKubernetesSecretData(item.Fields, item.Files),
|
||||
Type: corev1.SecretType(secretType),
|
||||
}
|
||||
}
|
||||
|
||||
func BuildKubernetesSecretData(fields []*onepassword.ItemField) map[string][]byte {
|
||||
func BuildKubernetesSecretData(fields []*onepassword.ItemField, files []*onepassword.File) map[string][]byte {
|
||||
secretData := map[string][]byte{}
|
||||
for i := 0; i < len(fields); i++ {
|
||||
if fields[i].Value != "" {
|
||||
@@ -92,6 +108,23 @@ func BuildKubernetesSecretData(fields []*onepassword.ItemField) map[string][]byt
|
||||
secretData[key] = []byte(fields[i].Value)
|
||||
}
|
||||
}
|
||||
|
||||
// populate unpopulated fields from files
|
||||
for _, file := range files {
|
||||
content, err := file.Content()
|
||||
if err != nil {
|
||||
log.Error(err, "Could not load contents of file %s", file.Name)
|
||||
continue
|
||||
}
|
||||
if content != nil {
|
||||
key := file.Name
|
||||
if secretData[key] == nil {
|
||||
secretData[key] = content
|
||||
} else {
|
||||
log.Info(fmt.Sprintf("File '%s' ignored because of a field with the same name", file.Name))
|
||||
}
|
||||
}
|
||||
}
|
||||
return secretData
|
||||
}
|
||||
|
||||
|
@@ -35,7 +35,9 @@ func TestCreateKubernetesSecretFromOnePasswordItem(t *testing.T) {
|
||||
secretAnnotations := map[string]string{
|
||||
"testAnnotation": "exists",
|
||||
}
|
||||
err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretAnnotations)
|
||||
secretType := ""
|
||||
|
||||
err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretType, secretAnnotations)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
@@ -66,7 +68,10 @@ func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) {
|
||||
kubeClient := fake.NewFakeClient()
|
||||
secretLabels := map[string]string{}
|
||||
secretAnnotations := map[string]string{}
|
||||
err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretAnnotations)
|
||||
secretType := ""
|
||||
|
||||
err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretType, secretAnnotations)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
@@ -77,7 +82,7 @@ func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) {
|
||||
newItem.Version = 456
|
||||
newItem.Vault.ID = "hfnjvi6aymbsnfc2xeeoheizda"
|
||||
newItem.ID = "h46bb3jddvay7nxopfhvlwg35q"
|
||||
err = CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &newItem, restartDeploymentAnnotation, secretLabels, secretAnnotations)
|
||||
err = CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &newItem, restartDeploymentAnnotation, secretLabels, secretType, secretAnnotations)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
@@ -93,7 +98,7 @@ func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) {
|
||||
func TestBuildKubernetesSecretData(t *testing.T) {
|
||||
fields := generateFields(5)
|
||||
|
||||
secretData := BuildKubernetesSecretData(fields)
|
||||
secretData := BuildKubernetesSecretData(fields, nil)
|
||||
if len(secretData) != len(fields) {
|
||||
t.Errorf("Unexpected number of secret fields returned. Expected 3, got %v", len(secretData))
|
||||
}
|
||||
@@ -111,8 +116,9 @@ func TestBuildKubernetesSecretFromOnePasswordItem(t *testing.T) {
|
||||
item := onepassword.Item{}
|
||||
item.Fields = generateFields(5)
|
||||
labels := map[string]string{}
|
||||
secretType := ""
|
||||
|
||||
kubeSecret := BuildKubernetesSecretFromOnePasswordItem(name, namespace, annotations, labels, item)
|
||||
kubeSecret := BuildKubernetesSecretFromOnePasswordItem(name, namespace, annotations, labels, secretType, item)
|
||||
if kubeSecret.Name != strings.ToLower(name) {
|
||||
t.Errorf("Expected name value: %v but got: %v", name, kubeSecret.Name)
|
||||
}
|
||||
@@ -134,6 +140,7 @@ func TestBuildKubernetesSecretFixesInvalidLabels(t *testing.T) {
|
||||
}
|
||||
labels := map[string]string{}
|
||||
item := onepassword.Item{}
|
||||
secretType := ""
|
||||
|
||||
item.Fields = []*onepassword.ItemField{
|
||||
{
|
||||
@@ -146,7 +153,7 @@ func TestBuildKubernetesSecretFixesInvalidLabels(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
kubeSecret := BuildKubernetesSecretFromOnePasswordItem(name, namespace, annotations, labels, item)
|
||||
kubeSecret := BuildKubernetesSecretFromOnePasswordItem(name, namespace, annotations, labels, secretType, item)
|
||||
|
||||
// Assert Secret's meta.name was fixed
|
||||
if kubeSecret.Name != expectedName {
|
||||
@@ -164,6 +171,39 @@ func TestBuildKubernetesSecretFixesInvalidLabels(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateKubernetesTLSSecretFromOnePasswordItem(t *testing.T) {
|
||||
secretName := "tls-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 := "kubernetes.io/tls"
|
||||
|
||||
err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretType, secretAnnotations)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
createdSecret := &corev1.Secret{}
|
||||
err = kubeClient.Get(context.Background(), types.NamespacedName{Name: secretName, Namespace: namespace}, createdSecret)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Secret was not created: %v", err)
|
||||
}
|
||||
|
||||
if createdSecret.Type != corev1.SecretTypeTLS {
|
||||
t.Errorf("Expected secretType to be of tyype corev1.SecretTypeTLS, got %s", string(createdSecret.Type))
|
||||
}
|
||||
}
|
||||
|
||||
func compareAnnotationsToItem(annotations map[string]string, item onepassword.Item, t *testing.T) {
|
||||
actualVaultId, actualItemId, err := ParseVaultIdAndItemIdFromPath(annotations[ItemPathAnnotation])
|
||||
if err != nil {
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
type TestClient struct {
|
||||
GetVaultsFunc func() ([]onepassword.Vault, error)
|
||||
GetVaultsByTitleFunc func(title string) ([]onepassword.Vault, error)
|
||||
GetVaultFunc func(uuid string) (*onepassword.Vault, error)
|
||||
GetItemFunc func(uuid string, vaultUUID string) (*onepassword.Item, error)
|
||||
GetItemsFunc func(vaultUUID string) ([]onepassword.Item, error)
|
||||
GetItemsByTitleFunc func(title string, vaultUUID string) ([]onepassword.Item, error)
|
||||
@@ -14,11 +15,14 @@ type TestClient struct {
|
||||
CreateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
|
||||
UpdateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
|
||||
DeleteItemFunc func(item *onepassword.Item, vaultUUID string) error
|
||||
GetFileFunc func(uuid string, itemUUID string, vaultUUID string) (*onepassword.File, error)
|
||||
GetFileContentFunc func(file *onepassword.File) ([]byte, error)
|
||||
}
|
||||
|
||||
var (
|
||||
GetGetVaultsFunc func() ([]onepassword.Vault, error)
|
||||
DoGetVaultsByTitleFunc func(title string) ([]onepassword.Vault, error)
|
||||
DoGetVaultFunc func(uuid string) (*onepassword.Vault, error)
|
||||
GetGetItemFunc func(uuid string, vaultUUID string) (*onepassword.Item, error)
|
||||
DoGetItemsByTitleFunc func(title string, vaultUUID string) ([]onepassword.Item, error)
|
||||
DoGetItemByTitleFunc func(title string, vaultUUID string) (*onepassword.Item, error)
|
||||
@@ -26,6 +30,8 @@ var (
|
||||
DoDeleteItemFunc func(item *onepassword.Item, vaultUUID string) error
|
||||
DoGetItemsFunc func(vaultUUID string) ([]onepassword.Item, error)
|
||||
DoUpdateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
|
||||
DoGetFileFunc func(uuid string, itemUUID string, vaultUUID string) (*onepassword.File, error)
|
||||
DoGetFileContentFunc func(file *onepassword.File) ([]byte, error)
|
||||
)
|
||||
|
||||
// Do is the mock client's `Do` func
|
||||
@@ -37,6 +43,10 @@ func (m *TestClient) GetVaultsByTitle(title string) ([]onepassword.Vault, error)
|
||||
return DoGetVaultsByTitleFunc(title)
|
||||
}
|
||||
|
||||
func (m *TestClient) GetVault(uuid string) (*onepassword.Vault, error) {
|
||||
return DoGetVaultFunc(uuid)
|
||||
}
|
||||
|
||||
func (m *TestClient) GetItem(uuid string, vaultUUID string) (*onepassword.Item, error) {
|
||||
return GetGetItemFunc(uuid, vaultUUID)
|
||||
}
|
||||
@@ -64,3 +74,11 @@ func (m *TestClient) DeleteItem(item *onepassword.Item, vaultUUID string) error
|
||||
func (m *TestClient) UpdateItem(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error) {
|
||||
return DoUpdateItemFunc(item, vaultUUID)
|
||||
}
|
||||
|
||||
func (m *TestClient) GetFile(uuid string, itemUUID string, vaultUUID string) (*onepassword.File, error) {
|
||||
return DoGetFileFunc(uuid, itemUUID, vaultUUID)
|
||||
}
|
||||
|
||||
func (m *TestClient) GetFileContent(file *onepassword.File) ([]byte, error) {
|
||||
return DoGetFileContentFunc(file)
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package onepassword
|
||||
|
||||
import corev1 "k8s.io/api/core/v1"
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func AreContainersUsingSecrets(containers []corev1.Container, secrets map[string]*corev1.Secret) bool {
|
||||
for i := 0; i < len(containers); i++ {
|
||||
@@ -13,6 +15,15 @@ func AreContainersUsingSecrets(containers []corev1.Container, secrets map[string
|
||||
}
|
||||
}
|
||||
}
|
||||
envFromVariables := containers[i].EnvFrom
|
||||
for j := 0; j < len(envFromVariables); j++ {
|
||||
if envFromVariables[j].SecretRef != nil {
|
||||
_, ok := secrets[envFromVariables[j].SecretRef.Name]
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -28,6 +39,15 @@ func AppendUpdatedContainerSecrets(containers []corev1.Container, secrets map[st
|
||||
}
|
||||
}
|
||||
}
|
||||
envFromVariables := containers[i].EnvFrom
|
||||
for j := 0; j < len(envFromVariables); j++ {
|
||||
if envFromVariables[j].SecretRef != nil {
|
||||
secret, ok := secrets[envFromVariables[j].SecretRef.LocalObjectReference.Name]
|
||||
if ok {
|
||||
updatedDeploymentSecrets[secret.Name] = secret
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return updatedDeploymentSecrets
|
||||
}
|
||||
|
@@ -4,9 +4,10 @@ import (
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestAreContainersUsingSecrets(t *testing.T) {
|
||||
func TestAreContainersUsingSecretsFromEnv(t *testing.T) {
|
||||
secretNamesToSearch := map[string]*corev1.Secret{
|
||||
"onepassword-database-secret": &corev1.Secret{},
|
||||
"onepassword-api-key": &corev1.Secret{},
|
||||
@@ -18,7 +19,26 @@ func TestAreContainersUsingSecrets(t *testing.T) {
|
||||
"some_other_key",
|
||||
}
|
||||
|
||||
containers := generateContainers(containerSecretNames)
|
||||
containers := generateContainersWithSecretRefsFromEnv(containerSecretNames)
|
||||
|
||||
if !AreContainersUsingSecrets(containers, secretNamesToSearch) {
|
||||
t.Errorf("Expected that containers were using secrets but they were not detected.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAreContainersUsingSecretsFromEnvFrom(t *testing.T) {
|
||||
secretNamesToSearch := map[string]*corev1.Secret{
|
||||
"onepassword-database-secret": {},
|
||||
"onepassword-api-key": {},
|
||||
}
|
||||
|
||||
containerSecretNames := []string{
|
||||
"onepassword-database-secret",
|
||||
"onepassword-api-key",
|
||||
"some_other_key",
|
||||
}
|
||||
|
||||
containers := generateContainersWithSecretRefsFromEnvFrom(containerSecretNames)
|
||||
|
||||
if !AreContainersUsingSecrets(containers, secretNamesToSearch) {
|
||||
t.Errorf("Expected that containers were using secrets but they were not detected.")
|
||||
@@ -27,17 +47,39 @@ func TestAreContainersUsingSecrets(t *testing.T) {
|
||||
|
||||
func TestAreContainersNotUsingSecrets(t *testing.T) {
|
||||
secretNamesToSearch := map[string]*corev1.Secret{
|
||||
"onepassword-database-secret": &corev1.Secret{},
|
||||
"onepassword-api-key": &corev1.Secret{},
|
||||
"onepassword-database-secret": {},
|
||||
"onepassword-api-key": {},
|
||||
}
|
||||
|
||||
containerSecretNames := []string{
|
||||
"some_other_key",
|
||||
}
|
||||
|
||||
containers := generateContainers(containerSecretNames)
|
||||
containers := generateContainersWithSecretRefsFromEnv(containerSecretNames)
|
||||
|
||||
if AreContainersUsingSecrets(containers, secretNamesToSearch) {
|
||||
t.Errorf("Expected that containers were not using secrets but they were detected.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendUpdatedContainerSecretsParsesEnvFromEnv(t *testing.T) {
|
||||
secretNamesToSearch := map[string]*corev1.Secret{
|
||||
"onepassword-database-secret": {},
|
||||
"onepassword-api-key": {ObjectMeta: metav1.ObjectMeta{Name: "onepassword-api-key"}},
|
||||
}
|
||||
|
||||
containerSecretNames := []string{
|
||||
"onepassword-api-key",
|
||||
}
|
||||
|
||||
containers := generateContainersWithSecretRefsFromEnvFrom(containerSecretNames)
|
||||
|
||||
updatedDeploymentSecrets := map[string]*corev1.Secret{}
|
||||
updatedDeploymentSecrets = AppendUpdatedContainerSecrets(containers, secretNamesToSearch, updatedDeploymentSecrets)
|
||||
|
||||
secretKeyName := "onepassword-api-key"
|
||||
|
||||
if updatedDeploymentSecrets[secretKeyName] != secretNamesToSearch[secretKeyName] {
|
||||
t.Errorf("Expected that updated Secret from envfrom is found.")
|
||||
}
|
||||
}
|
||||
|
@@ -39,7 +39,7 @@ func TestIsDeploymentUsingSecretsUsingContainers(t *testing.T) {
|
||||
}
|
||||
|
||||
deployment := &appsv1.Deployment{}
|
||||
deployment.Spec.Template.Spec.Containers = generateContainers(containerSecretNames)
|
||||
deployment.Spec.Template.Spec.Containers = generateContainersWithSecretRefsFromEnv(containerSecretNames)
|
||||
if !IsDeploymentUsingSecrets(deployment, secretNamesToSearch) {
|
||||
t.Errorf("Expected that deployment was using secrets but they were not detected.")
|
||||
}
|
||||
|
@@ -30,6 +30,14 @@ func GetOnePasswordItemByPath(opConnectClient connect.Client, path string) (*one
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, file := range item.Files {
|
||||
_, err := opConnectClient.GetFileContent(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return item, nil
|
||||
}
|
||||
|
||||
|
@@ -17,8 +17,7 @@ func generateVolumes(names []string) []corev1.Volume {
|
||||
}
|
||||
return volumes
|
||||
}
|
||||
|
||||
func generateContainers(names []string) []corev1.Container {
|
||||
func generateContainersWithSecretRefsFromEnv(names []string) []corev1.Container {
|
||||
containers := []corev1.Container{}
|
||||
for i := 0; i < len(names); i++ {
|
||||
container := corev1.Container{
|
||||
@@ -40,3 +39,16 @@ func generateContainers(names []string) []corev1.Container {
|
||||
}
|
||||
return containers
|
||||
}
|
||||
|
||||
func generateContainersWithSecretRefsFromEnvFrom(names []string) []corev1.Container {
|
||||
containers := []corev1.Container{}
|
||||
for i := 0; i < len(names); i++ {
|
||||
container := corev1.Container{
|
||||
EnvFrom: []corev1.EnvFromSource{
|
||||
{SecretRef: &corev1.SecretEnvSource{LocalObjectReference: corev1.LocalObjectReference{Name: names[i]}}},
|
||||
},
|
||||
}
|
||||
containers = append(containers, container)
|
||||
}
|
||||
return containers
|
||||
}
|
||||
|
@@ -3,9 +3,10 @@ package onepassword
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
v1 "github.com/1Password/onepassword-operator/pkg/apis/onepassword/v1"
|
||||
"time"
|
||||
|
||||
v1 "github.com/1Password/onepassword-operator/pkg/apis/onepassword/v1"
|
||||
|
||||
kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets"
|
||||
"github.com/1Password/onepassword-operator/pkg/utils"
|
||||
|
||||
@@ -121,7 +122,8 @@ func (h *SecretUpdateHandler) updateKubernetesSecrets() (map[string]map[string]*
|
||||
|
||||
item, err := GetOnePasswordItemByPath(h.opConnectClient, OnePasswordItemPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to retrieve item: %v", err)
|
||||
log.Error(err, "failed to retrieve 1Password item at path \"%s\" for secret \"%s\"", secret.Annotations[ItemPathAnnotation], secret.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
itemVersion := fmt.Sprint(item.Version)
|
||||
@@ -138,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, *item)
|
||||
updatedSecret := kubeSecrets.BuildKubernetesSecretFromOnePasswordItem(secret.Name, secret.Namespace, secret.Annotations, secret.Labels, string(secret.Type), *item)
|
||||
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 {
|
||||
|
Reference in New Issue
Block a user