Compare commits

..

1 Commits

Author SHA1 Message Date
Joris Coenen
8ee4478c46 Add GoReleaser configuration for publishing docker images
Should build both an amd64 and arm64 image and combine both in a single manifest. Does require some modifications to the GitHub Actions to correctly push to DockerHub.

Used this blog post as inspiration: https://carlosbecker.com/posts/multi-platform-docker-images-goreleaser-gh-actions/
2021-04-21 13:45:49 +02:00
31 changed files with 232 additions and 472 deletions

View File

@@ -1 +1 @@
1.0.2
v1.0.0

View File

@@ -1,36 +0,0 @@
---
name: Bug report
about: Report bugs and errors found while using the Operator.
title: ''
labels: bug
assignees: ''
---
### Your environment
<!-- Version of the Operator when the error occurred -->
Operator Version:
<!-- What version of the Connect server are you running?
You can get this information from the Integrations section in 1Password
https://start.1password.com/integrations/active
-->
Connect Server Version:
<!-- What version of Kubernetes have you deployed the operator to? -->
Kubernetes Version:
## What happened?
<!-- Describe the bug or error -->
## What did you expect to happen?
<!-- Describe what should have happened -->
## Steps to reproduce
1. <!-- Describe Steps to reproduce the issue -->
## Notes & Logs
<!-- Paste any logs here that may help with debugging.
Remember to remove any sensitive information before sharing! -->

View File

@@ -1,9 +0,0 @@
# docs: https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser
blank_issues_enabled: true
contact_links:
- name: 1Password Community
url: https://1password.community/categories/secrets-automation
about: Please ask general Secrets Automation questions here.
- name: 1Password Security Bug Bounty
url: https://bugcrowd.com/agilebits
about: Please report security vulnerabilities here.

View File

@@ -1,32 +0,0 @@
---
name: Feature request
about: Suggest an idea for the Operator
title: ''
labels: feature-request
assignees: ''
---
### Summary
<!-- Briefly describe the feature in one or two sentences. You can include more details later. -->
### Use cases
<!-- Describe the use cases that make this feature useful to others.
The description should help the reader understand why the feature is necessary.
The better we understand your use case, the better we can help create an appropriate solution. -->
### Proposed solution
<!-- If you already have an idea for how the feature should work, use this space to describe it.
We'll work with you to find a workable approach, and any implementation details are appreciated.
-->
### Is there a workaround to accomplish this today?
<!-- If there's a way to accomplish this feature request without changes to the codebase, we'd like to hear it.
-->
### References & Prior Work
<!-- If a similar feature was implemented in another project or tool, add a link so we can better understand your request.
Links to relevant documentation or RFCs are also appreciated. -->
* <!-- Reference 1 -->
* <!-- Reference 2, etc -->

View File

@@ -1,15 +1,13 @@
name: release
name: goreleaser
on:
push:
tags:
- 'v*'
- '*'
jobs:
release-docker:
goreleaser:
runs-on: ubuntu-latest
env:
DOCKER_CLI_EXPERIMENTAL: "enabled"
steps:
-
name: Checkout
@@ -17,41 +15,15 @@ jobs:
with:
fetch-depth: 0
-
name: Docker meta
id: meta
uses: crazy-max/ghaction-docker-meta@v2
name: Set up Go
uses: actions/setup-go@v2
with:
images: |
1password/onepassword-operator
# Publish image for x.y.z and x.y
# The latest tag is automatically added for semver tags
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Get the version from tag
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v}
go-version: 1.15
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Docker Login
uses: docker/login-action@v1
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
operator_version=${{ steps.get_version.outputs.VERSION }}
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

55
.goreleaser.yml Normal file
View File

@@ -0,0 +1,55 @@
project_name: onepassword-operator
builds:
- env:
- CGO_ENABLED=0
binary: manager
main: ./cmd/manager/main.go
flags:
- -mod=vendor
- -trimpath
ldflags:
- -s -w -X "github.com/1Password/onepassword-operator/version.Version={{ .Version }}"
mod_timestamp: '{{ .CommitTimestamp }}'
goos:
- linux
goarch:
- amd64
- arm64
dockers:
- image_templates: ["1password/{{ .ProjectName }}:{{ .Version }}-amd64"]
goos: linux
goarch: amd64
dockerfile: Dockerfile-goreleaser
use_buildx: true
extra_files:
- deploy/connect/
build_flag_templates:
- --platform=linux/amd64
- --label=org.opencontainers.image.title={{ .ProjectName }}
- --label=org.opencontainers.image.description={{ .ProjectName }}
- --label=org.opencontainers.image.url=https://github.com/1Password/onepassword-operator
- --label=org.opencontainers.image.source=https://github.com/1Password/onepassword-operator
- --label=org.opencontainers.image.version={{ .Version }}
- --label=org.opencontainers.image.revision={{ .FullCommit }}
- --label=org.opencontainers.image.licenses=MIT
- image_templates: ["1password/{{ .ProjectName }}:{{ .Version }}-arm64v8"]
goos: linux
goarch: arm64
dockerfile: Dockerfile-goreleaser
use_buildx: true
extra_files:
- deploy/connect/
build_flag_templates:
- --platform=linux/arm64/v8
- --label=org.opencontainers.image.title={{ .ProjectName }}
- --label=org.opencontainers.image.description={{ .ProjectName }}
- --label=org.opencontainers.image.url=https://github.com/1Password/onepassword-operator
- --label=org.opencontainers.image.source=https://github.com/1Password/onepassword-operator
- --label=org.opencontainers.image.version={{ .Version }}
- --label=org.opencontainers.image.revision={{ .FullCommit }}
- --label=org.opencontainers.image.licenses=MIT
docker_manifests:
- name_template: 1password/{{ .ProjectName }}:{{ .Version }}
image_templates:
- 1password/{{ .ProjectName }}:{{ .Version }}-amd64
- 1password/{{ .ProjectName }}:{{ .Version }}-arm64v8

View File

@@ -12,23 +12,6 @@
---
[//]: # (START/v1.0.2)
# v1.0.2
## Fixes
* Name normalizer added to handle non-conforming item names.
---
[//]: # (START/v1.0.1)
# v1.0.1
## Features
* 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)
# v1.0.0

View File

@@ -14,9 +14,11 @@ COPY vendor/ vendor/
# Build
ARG operator_version=dev
RUN CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64 \
GO111MODULE=on \
go build \
-ldflags "-X \"github.com/1Password/onepassword-operator/version.Version=$operator_version\"" \
-ldflags "-X version.Version=$operator_version" \
-mod vendor \
-a -o manager main.go

9
Dockerfile-goreleaser Normal file
View File

@@ -0,0 +1,9 @@
# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
FROM gcr.io/distroless/static:nonroot
WORKDIR /
COPY ./manager .
USER nonroot:nonroot
COPY deploy/connect/ deploy/connect/
ENTRYPOINT ["/manager"]

View File

@@ -20,12 +20,12 @@ test/coverage: ## Run test suite with coverage report
go test -v ./... -cover
build: ## Build operator Docker image
@docker build -f Dockerfile --build-arg operator_version=$(curVersion) -t $(DOCKER_IMG_TAG) .
@docker build -f Dockerfile --build-arg operator_version=$(curVersion) -t $(DOCKER_IMG_TAG)
@echo "Successfully built and tagged image."
@echo "Tag: $(DOCKER_IMG_TAG)"
build/local: ## Build local version of the operator Docker image
@docker build -f Dockerfile -t local/$(DOCKER_IMG_TAG) .
@docker build -f Dockerfile -t local/$(DOCKER_IMG_TAG)
build/binary: clean ## Build operator binary
@mkdir -p dist

View File

@@ -2,7 +2,7 @@
The 1Password Connect Kubernetes Operator provides the ability to integrate Kubernetes with 1Password. This Operator manages `OnePasswordItem` Custom Resource Definitions (CRDs) that define the location of an Item stored in 1Password. The `OnePasswordItem` CRD, when created, will be used to compose a Kubernetes Secret containing the contents of the specified item.
The 1Password Connect Kubernetes Operator also allows for Kubernetes Secrets to be composed from a 1Password Item through annotation of an Item Reference on a deployment.
The 1Password Connect Kubernetes Operator also allows for Kubernetes Secrets to be composed from a 1Password Item through annotation of an Item Path on a deployment.
The 1Password Connect Kubernetes Operator will continually check for updates from 1Password for any Kubernetes Secret that it has generated. If a Kubernetes Secret is updated, any Deployment using that secret can be automatically restarted.
@@ -13,8 +13,8 @@ Prerequisites:
- [1Password Command Line Tool Installed](https://1password.com/downloads/command-line/)
- [kubectl installed](https://kubernetes.io/docs/tasks/tools/install-kubectl/)
- [docker installed](https://docs.docker.com/get-docker/)
- [Generated a 1password-credentials.json file and issued a 1Password Connect API Token for the K8s Operator integration](https://support.1password.com/secrets-automation/)
- [1Password Connect deployed to Kubernetes](https://support.1password.com/connect-deploy-kubernetes/#step-2-deploy-a-1password-connect-server). **NOTE**: If customization of the 1Password Connect deployment is not required you can skip this prerequisite.
- [Generated a 1password-credentials.json file and issued a 1Password Connect API Token for the K8s Operator integration](https://support.b5dev.com/cs/connect)
- [1Password Connect deployed to Kubernetes](https://support.b5dev.com/cs/connect-deploy-kubernetes/#step-2-deploy-a-connect-server). **NOTE**: If customization of the 1Password Connect deployment is not required you can skip this prerequisite.
### Quickstart for Deploying 1Password Connect to Kubernetes
@@ -53,15 +53,15 @@ Adding this environment variable will have the operator automatically deploy a d
"Create a Connect token for the operator and save it as a Kubernetes Secret:
```bash
$ kubectl create secret generic onepassword-token --from-literal=token=<OP_CONNECT_TOKEN>"
$ kubectl create secret generic op-operator-connect-token --from-literal=token=<OP_CONNECT_TOKEN>"
```
If you do not have a token for the operator, you can generate a token and save it to kubernetes with the following command:
```bash
$ kubectl create secret generic onepassword-token --from-literal=token=$(op create connect token <server> op-k8s-operator --vault <vault>)
$ kubectl create secret generic op-operator-connect-token --from-literal=token=$(op create connect token <server> op-k8s-operator --vault <vault>)
```
[More information on generating a token can be found here](https://support.1password.com/secrets-automation/#appendix-issue-additional-access-tokens)
[More information on generating a token can be found here](https://support.1password.com/cs/secrets-automation/#appendix-issue-additional-access-tokens)
**Set Permissions For Operator**
@@ -84,9 +84,9 @@ An sample Deployment yaml can be found at `/deploy/operator.yaml`.
To further configure the 1Password Kubernetes Operator the Following Environment variables can be set in the operator yaml:
- **WATCH_NAMESPACE:** comma separated list of what Namespaces to watch for changes.
- **OP_CONNECT_HOST** (required): Specifies the host name within Kubernetes in which to access the 1Password Connect.
- **WATCH_NAMESPACE:** (default: watch all namespaces): Comma separated list of what Namespaces to watch for changes.
- **POLLING_INTERVAL** (default: 600): The number of seconds the 1Password Kubernetes Operator will wait before checking for updates from 1Password Connect.
- **POLLING_INTERVAL** (default: 600)**:** The number of seconds the 1Password Kubernetes Operator will wait before checking for updates from 1Password Connect.
- **MANAGE_CONNECT** (default: false): If set to true, on deployment of the operator, a default configuration of the OnePassword Connect Service will be deployed to the `default` namespace.
- **AUTO_RESTART** (default: false): If set to true, the operator will restart any deployment using a secret from 1Password Connect. This can be overwritten by namespace, deployment, or individual secret. More details on AUTO_RESTART can be found in the ["Configuring Automatic Rolling Restarts of Deployments"](#configuring-automatic-rolling-restarts-of-deployments) section.
@@ -102,11 +102,11 @@ To create a Kubernetes Secret from a 1Password item, create a yaml file with the
```yaml
apiVersion: onepassword.com/v1
kind: OnePasswordItem
kind: OnePasswordItem # {insert_new_name}
metadata:
name: <item_name> #this name will also be used for naming the generated kubernetes secret
spec:
itemReference: "op://<vault_id_or_title>/<item_id_or_title>"
itemPath: "vaults/<vault_id_or_title>/items/<item_id_or_title>"
```
Deploy the OnePasswordItem to Kubernetes:
@@ -131,25 +131,20 @@ kind: Deployment
metadata:
name: deployment-example
annotations:
operator.1password.io/item-reference: "op://<vault>/<item>"
operator.1password.io/item-name: "<secret_name>"
operator.1password.io/item-path: "vaults/{vault_id_or_title}/items/{item_id_or_title}"
operator.1password.io/item-name: "{secret_name}"
```
Applying this yaml file will create a Kubernetes Secret with the name `<secret_name>` and contents from the location specified at the specified Item Reference.
Applying this yaml file will create a Kubernetes Secret with the name `<secret_name>` and contents from the location specified at the specified Item Path.
Note: Deleting the Deployment that you've created will automatically delete the created Kubernetes Secret only if the deployment is still annotated with `operator.1password.io/item-reference` and `operator.1password.io/item-name` and no other deployment is using the secret.
Note: Deleting the Deployment that you've created will automatically delete the created Kubernetes Secret only if the deployment is still annotated with `operator.1password.io/item-path` and `operator.1password.io/item-name` and no other deployment is using the secret.
If a 1Password Item that is linked to a Kubernetes Secret is updated within the POLLING_INTERVAL the associated Kubernetes Secret will be updated. However, if you do not want a specific secret to be updated you can add the tag `operator.1password.io:ignore-secret` to the item stored in 1Password. While this tag is in place, any updates made to an item will not trigger an update to the associated secret in Kubernetes.
---
**NOTE**
If multiple 1Password vaults/items have the same `title` when using a title in the access reference, the desired action will be performed on the oldest vault/item.
Titles and field names that include white space and other characters that are not a valid [DNS subdomain name](https://kubernetes.io/docs/concepts/configuration/secret/) will create Kubernetes secrets that have titles and fields in the following format:
- Invalid characters before the first alphanumeric character and after the last alphanumeric character will be removed
- All whitespaces between words will be replaced by `-`
- All the letters will be lower-cased.
If multiple 1Password vaults/items have the same `title` when using a title in the access path, the desired action will be performed on the oldest vault/item. Furthermore, titles that include white space characters cannot be used.
---
@@ -168,8 +163,7 @@ apiVersion: v1
kind: Namespace
metadata:
name: "example-namespace"
annotations:
operator.1password.io/auto-restart: "true"
operator.1password.io/auto-restart: "true"
```
If the value is not set, the auto reset settings on the operator will be used. This value can be overwritten by deployment.
@@ -181,8 +175,7 @@ apiVersion: v1
kind: Deployment
metadata:
name: "example-deployment"
annotations:
operator.1password.io/auto-restart: "true"
operator.1password.io/auto-restart: "true"
```
If the value is not set, the auto reset settings on the namespace will be used.
@@ -194,8 +187,7 @@ apiVersion: onepassword.com/v1
kind: OnePasswordItem
metadata:
name: example
annotations:
operator.1password.io/auto-restart: "true"
operator.1password.io/auto-restart: "true"
```
If the value is not set, the auto reset settings on the deployment will be used.
@@ -232,4 +224,4 @@ make test/coverage
Please file requests via [**BugCrowd**](https://bugcrowd.com/agilebits).
For information about security practices, please visit our [Security homepage](https://bugcrowd.com/agilebits).
For information about security practices, please visit our [Security homepage](https://bugcrowd.com/agilebits).

View File

@@ -83,11 +83,9 @@ func main() {
printVersion()
namespace := os.Getenv(k8sutil.WatchNamespaceEnvVar)
deploymentNamespace, err := k8sutil.GetOperatorNamespace()
namespace, err := k8sutil.GetWatchNamespace()
if err != nil {
log.Error(err, "Failed to get namespace")
log.Error(err, "Failed to get watch namespace")
os.Exit(1)
}
@@ -141,7 +139,7 @@ func main() {
go func() {
connectStarted := false
for connectStarted == false {
err := op.SetupConnect(mgr.GetClient(), deploymentNamespace)
err := op.SetupConnect(mgr.GetClient())
// Cache Not Started is an acceptable error. Retry until cache is started.
if err != nil && !errors.Is(err, &cache.ErrCacheNotStarted{}) {
log.Error(err, "")
@@ -178,8 +176,7 @@ func main() {
ticker.Stop()
return
case <-ticker.C:
err := updatedSecretsPoller.UpdateKubernetesSecretsTask()
log.Error(err, "Error occured during update secret task")
updatedSecretsPoller.UpdateKubernetesSecretsTask()
}
}
}()

View File

@@ -2,6 +2,7 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: onepassword-connect
namespace: default
spec:
selector:
matchLabels:

View File

@@ -2,6 +2,7 @@ apiVersion: v1
kind: Service
metadata:
name: onepassword-connect
namespace: default
spec:
type: NodePort
selector:

View File

@@ -33,7 +33,7 @@ spec:
spec:
description: OnePasswordItemSpec defines the desired state of OnePasswordItem
properties:
itemReference:
itemPath:
type: string
type: object
status:

View File

@@ -3,4 +3,4 @@ kind: OnePasswordItem
metadata:
name: example
spec:
itemReference: "op://<vault_id>/<item_id>"
itemPath: "vaults/<vault_id>/items/<item_id>"

View File

@@ -16,7 +16,6 @@ spec:
containers:
- name: onepassword-connect-operator
image: 1password/onepassword-operator
imagePullPolicy: Never
command: ["/manager"]
env:
- name: WATCH_NAMESPACE

View File

@@ -8,7 +8,7 @@ import (
// OnePasswordItemSpec defines the desired state of OnePasswordItem
type OnePasswordItemSpec struct {
ItemReference string `json:"itemReference,omitempty"`
ItemPath string `json:"itemPath,omitempty"`
}
// OnePasswordItemStatus defines the observed state of OnePasswordItem

View File

@@ -192,11 +192,11 @@ func (r *ReconcileDeployment) HandleApplyingDeployment(namespace string, annotat
secretName := annotations[op.NameAnnotation]
if len(secretName) == 0 {
reqLog.Info("No 'item-name' annotation set. 'item-reference' and 'item-name' must be set as annotations to add new secret.")
reqLog.Info("No 'item-name' annotation set. 'item-path' and 'item-name' must be set as annotations to add new secret.")
return nil
}
item, err := op.GetOnePasswordItemByReference(r.opConnectClient, annotations[op.ItemReferenceAnnotation])
item, err := op.GetOnePasswordItemByPath(r.opConnectClient, annotations[op.ItemPathAnnotation])
if err != nil {
return fmt.Errorf("Failed to retrieve item: %v", err)
}

View File

@@ -52,7 +52,7 @@ var (
"password": []byte(password),
"username": []byte(username),
}
ItemReference = fmt.Sprintf("op://%v/%v", vaultId, itemId)
itemPath = fmt.Sprintf("vaults/%v/items/%v", vaultId, itemId)
)
var (
@@ -76,8 +76,8 @@ var tests = []testReconcileItem{
finalizer,
},
Annotations: map[string]string{
op.ItemReferenceAnnotation: ItemReference,
op.NameAnnotation: name,
op.ItemPathAnnotation: itemPath,
op.NameAnnotation: name,
},
},
},
@@ -90,8 +90,8 @@ var tests = []testReconcileItem{
Name: "another-deployment",
Namespace: namespace,
Annotations: map[string]string{
op.ItemReferenceAnnotation: ItemReference,
op.NameAnnotation: name,
op.ItemPathAnnotation: itemPath,
op.NameAnnotation: name,
},
},
Spec: appsv1.DeploymentSpec{
@@ -152,8 +152,8 @@ var tests = []testReconcileItem{
finalizer,
},
Annotations: map[string]string{
op.ItemReferenceAnnotation: ItemReference,
op.NameAnnotation: name,
op.ItemPathAnnotation: itemPath,
op.NameAnnotation: name,
},
},
},
@@ -166,8 +166,8 @@ var tests = []testReconcileItem{
Name: "another-deployment",
Namespace: namespace,
Annotations: map[string]string{
op.ItemReferenceAnnotation: ItemReference,
op.NameAnnotation: name,
op.ItemPathAnnotation: itemPath,
op.NameAnnotation: name,
},
},
Spec: appsv1.DeploymentSpec{
@@ -235,8 +235,8 @@ var tests = []testReconcileItem{
finalizer,
},
Annotations: map[string]string{
op.ItemReferenceAnnotation: ItemReference,
op.NameAnnotation: name,
op.ItemPathAnnotation: itemPath,
op.NameAnnotation: name,
},
},
},
@@ -268,8 +268,8 @@ var tests = []testReconcileItem{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
op.ItemReferenceAnnotation: ItemReference,
op.NameAnnotation: name,
op.ItemPathAnnotation: itemPath,
op.NameAnnotation: name,
},
},
},
@@ -310,8 +310,8 @@ var tests = []testReconcileItem{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
op.ItemReferenceAnnotation: ItemReference,
op.NameAnnotation: name,
op.ItemPathAnnotation: itemPath,
op.NameAnnotation: name,
},
},
},
@@ -352,8 +352,8 @@ var tests = []testReconcileItem{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
op.ItemReferenceAnnotation: ItemReference,
op.NameAnnotation: name,
op.ItemPathAnnotation: itemPath,
op.NameAnnotation: name,
},
},
},

View File

@@ -146,7 +146,7 @@ func (r *ReconcileOnePasswordItem) HandleOnePasswordItem(resource *onepasswordv1
secretName := resource.GetName()
autoRestart := resource.Annotations[op.RestartDeploymentsAnnotation]
item, err := onepassword.GetOnePasswordItemByReference(r.opConnectClient, resource.Spec.ItemReference)
item, err := onepassword.GetOnePasswordItemByPath(r.opConnectClient, resource.Spec.ItemPath)
if err != nil {
return fmt.Errorf("Failed to retrieve item: %v", err)
}

View File

@@ -31,9 +31,6 @@ const (
itemId = "nwrhuano7bcwddcviubpp4mhfq"
username = "test-user"
password = "QmHumKc$mUeEem7caHtbaBaJ"
firstHost = "http://localhost:8080"
awsKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
iceCream = "freezing blue 20%"
userKey = "username"
passKey = "password"
version = 123
@@ -55,7 +52,7 @@ var (
"password": []byte(password),
"username": []byte(username),
}
itemReference = fmt.Sprintf("op://%v/%v", vaultId, itemId)
itemPath = fmt.Sprintf("vaults/%v/items/%v", vaultId, itemId)
)
var (
@@ -79,7 +76,7 @@ var tests = []testReconcileItem{
},
},
Spec: onepasswordv1.OnePasswordItemSpec{
ItemReference: itemReference,
ItemPath: itemPath,
},
},
existingSecret: &corev1.Secret{
@@ -111,7 +108,7 @@ var tests = []testReconcileItem{
Namespace: namespace,
},
Spec: onepasswordv1.OnePasswordItemSpec{
ItemReference: itemReference,
ItemPath: itemPath,
},
},
existingSecret: &corev1.Secret{
@@ -152,7 +149,7 @@ var tests = []testReconcileItem{
Namespace: namespace,
},
Spec: onepasswordv1.OnePasswordItemSpec{
ItemReference: itemReference,
ItemPath: itemPath,
},
},
existingSecret: &corev1.Secret{
@@ -193,7 +190,7 @@ var tests = []testReconcileItem{
Namespace: namespace,
},
Spec: onepasswordv1.OnePasswordItemSpec{
ItemReference: itemReference,
ItemPath: itemPath,
},
},
existingSecret: nil,
@@ -213,79 +210,6 @@ var tests = []testReconcileItem{
passKey: password,
},
},
{
testName: "Secret from 1Password item with invalid K8s labels",
customResource: &onepasswordv1.OnePasswordItem{
TypeMeta: metav1.TypeMeta{
Kind: onePasswordItemKind,
APIVersion: onePasswordItemAPIVersion,
},
ObjectMeta: metav1.ObjectMeta{
Name: "!my sECReT it3m%",
Namespace: namespace,
},
Spec: onepasswordv1.OnePasswordItemSpec{
ItemReference: itemReference,
},
},
existingSecret: nil,
expectedError: nil,
expectedResultSecret: &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "my-secret-it3m",
Namespace: namespace,
Annotations: map[string]string{
op.VersionAnnotation: fmt.Sprint(version),
},
},
Data: expectedSecretData,
},
opItem: map[string]string{
userKey: username,
passKey: password,
},
},
{
testName: "Secret from 1Password item with fields and sections that have invalid K8s labels",
customResource: &onepasswordv1.OnePasswordItem{
TypeMeta: metav1.TypeMeta{
Kind: onePasswordItemKind,
APIVersion: onePasswordItemAPIVersion,
},
ObjectMeta: metav1.ObjectMeta{
Name: "!my sECReT it3m%",
Namespace: namespace,
},
Spec: onepasswordv1.OnePasswordItemSpec{
ItemReference: itemReference,
},
},
existingSecret: nil,
expectedError: nil,
expectedResultSecret: &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "my-secret-it3m",
Namespace: namespace,
Annotations: map[string]string{
op.VersionAnnotation: fmt.Sprint(version),
},
},
Data: map[string][]byte{
"password": []byte(password),
"username": []byte(username),
"first-host": []byte(firstHost),
"aws-access-key": []byte(awsKey),
"ice-cream-type": []byte(iceCream),
},
},
opItem: map[string]string{
userKey: username,
passKey: password,
"first host": firstHost,
"AWS Access Key": awsKey,
"😄 ice-cream type": iceCream,
},
},
}
func TestReconcileOnePasswordItem(t *testing.T) {
@@ -317,10 +241,7 @@ func TestReconcileOnePasswordItem(t *testing.T) {
mocks.GetGetItemFunc = func(uuid string, vaultUUID string) (*onepassword.Item, error) {
item := onepassword.Item{}
item.Fields = []*onepassword.ItemField{}
for k, v := range testData.opItem {
item.Fields = append(item.Fields, &onepassword.ItemField{Label: k, Value: v})
}
item.Fields = generateFields(testData.opItem["username"], testData.opItem["password"])
item.Version = version
item.Vault.ID = vaultUUID
item.ID = uuid
@@ -336,8 +257,8 @@ func TestReconcileOnePasswordItem(t *testing.T) {
// watched resource .
req := reconcile.Request{
NamespacedName: types.NamespacedName{
Name: testData.customResource.ObjectMeta.Name,
Namespace: testData.customResource.ObjectMeta.Namespace,
Name: name,
Namespace: namespace,
},
}
_, err := r.Reconcile(req)

View File

@@ -4,17 +4,12 @@ import (
"context"
"fmt"
"regexp"
"strings"
"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"
kubeValidate "k8s.io/apimachinery/pkg/util/validation"
kubernetesClient "sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"
)
@@ -23,7 +18,7 @@ const OnepasswordPrefix = "operator.1password.io"
const NameAnnotation = OnepasswordPrefix + "/item-name"
const VersionAnnotation = OnepasswordPrefix + "/item-version"
const restartAnnotation = OnepasswordPrefix + "/last-restarted"
const ItemReferenceAnnotation = OnepasswordPrefix + "/item-reference"
const ItemPathAnnotation = OnepasswordPrefix + "/item-path"
const RestartDeploymentsAnnotation = OnepasswordPrefix + "/auto-restart"
var log = logf.Log
@@ -32,8 +27,8 @@ func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretNa
itemVersion := fmt.Sprint(item.Version)
annotations := map[string]string{
VersionAnnotation: itemVersion,
ItemReferenceAnnotation: fmt.Sprintf("op://%v/%v", item.Vault.ID, item.ID),
VersionAnnotation: itemVersion,
ItemPathAnnotation: fmt.Sprintf("vaults/%v/items/%v", item.Vault.ID, item.ID),
}
if autoRestart != "" {
_, err := utils.StringToBool(autoRestart)
@@ -68,7 +63,7 @@ func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretNa
func BuildKubernetesSecretFromOnePasswordItem(name, namespace string, annotations map[string]string, item onepassword.Item) *corev1.Secret {
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: formatSecretName(name),
Name: name,
Namespace: namespace,
Annotations: annotations,
},
@@ -80,33 +75,8 @@ func BuildKubernetesSecretData(fields []*onepassword.ItemField) map[string][]byt
secretData := map[string][]byte{}
for i := 0; i < len(fields); i++ {
if fields[i].Value != "" {
key := formatSecretName(fields[i].Label)
secretData[key] = []byte(fields[i].Value)
secretData[fields[i].Label] = []byte(fields[i].Value)
}
}
return secretData
}
// formatSecretName rewrites a value to be a valid Secret name or Secret data key.
//
// The Secret meta.name and data keys must be valid DNS subdomain names (https://kubernetes.io/docs/concepts/configuration/secret/#overview-of-secrets)
func formatSecretName(value string) string {
if errs := kubeValidate.IsDNS1123Subdomain(value); len(errs) == 0 {
return value
}
return createValidSecretName(value)
}
var invalidDNS1123Chars = regexp.MustCompile("[^a-z0-9-]+")
func createValidSecretName(value string) string {
result := strings.ToLower(value)
result = invalidDNS1123Chars.ReplaceAllString(result, "-")
if len(result) > kubeValidate.DNS1123SubdomainMaxLength {
result = result[0:kubeValidate.DNS1123SubdomainMaxLength]
}
// first and last character MUST be alphanumeric
return strings.Trim(result, "-")
}

View File

@@ -6,8 +6,6 @@ import (
"strings"
"testing"
kubeValidate "k8s.io/apimachinery/pkg/util/validation"
"github.com/1Password/connect-sdk-go/onepassword"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
@@ -43,7 +41,7 @@ func TestCreateKubernetesSecretFromOnePasswordItem(t *testing.T) {
t.Errorf("Secret was not created: %v", err)
}
compareFields(item.Fields, createdSecret.Data, t)
compareAnnotationsToItem(item.Vault.ID, item.ID, createdSecret.Annotations, item, t)
compareAnnotationsToItem(createdSecret.Annotations, item, t)
}
func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) {
@@ -79,7 +77,7 @@ func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) {
t.Errorf("Secret was not found: %v", err)
}
compareFields(newItem.Fields, updatedSecret.Data, t)
compareAnnotationsToItem(newItem.Vault.ID, newItem.ID, updatedSecret.Annotations, newItem, t)
compareAnnotationsToItem(updatedSecret.Annotations, newItem, t)
}
func TestBuildKubernetesSecretData(t *testing.T) {
fields := generateFields(5)
@@ -103,7 +101,7 @@ func TestBuildKubernetesSecretFromOnePasswordItem(t *testing.T) {
item.Fields = generateFields(5)
kubeSecret := BuildKubernetesSecretFromOnePasswordItem(name, namespace, annotations, item)
if kubeSecret.Name != strings.ToLower(name) {
if kubeSecret.Name != name {
t.Errorf("Expected name value: %v but got: %v", name, kubeSecret.Name)
}
if kubeSecret.Namespace != namespace {
@@ -115,45 +113,11 @@ func TestBuildKubernetesSecretFromOnePasswordItem(t *testing.T) {
compareFields(item.Fields, kubeSecret.Data, t)
}
func TestBuildKubernetesSecretFixesInvalidLabels(t *testing.T) {
name := "inV@l1d k8s secret%name"
expectedName := "inv-l1d-k8s-secret-name"
namespace := "someNamespace"
annotations := map[string]string{
"annotationKey": "annotationValue",
func compareAnnotationsToItem(annotations map[string]string, item onepassword.Item, t *testing.T) {
actualVaultId, actualItemId, err := ParseVaultIdAndItemIdFromPath(annotations[ItemPathAnnotation])
if err != nil {
t.Errorf("Was unable to parse Item Path")
}
item := onepassword.Item{}
item.Fields = []*onepassword.ItemField{
{
Label: "label w%th invalid ch!rs-",
Value: "value1",
},
{
Label: strings.Repeat("x", kubeValidate.DNS1123SubdomainMaxLength+1),
Value: "name exceeds max length",
},
}
kubeSecret := BuildKubernetesSecretFromOnePasswordItem(name, namespace, annotations, item)
// Assert Secret's meta.name was fixed
if kubeSecret.Name != expectedName {
t.Errorf("Expected name value: %v but got: %v", name, kubeSecret.Name)
}
if kubeSecret.Namespace != namespace {
t.Errorf("Expected namespace value: %v but got: %v", namespace, kubeSecret.Namespace)
}
// assert labels were fixed for each data key
for key := range kubeSecret.Data {
if !validLabel(key) {
t.Errorf("Expected valid kubernetes label, got %s", key)
}
}
}
func compareAnnotationsToItem(actualVaultId, actualItemId string, annotations map[string]string, item onepassword.Item, t *testing.T) {
if actualVaultId != item.Vault.ID {
t.Errorf("Expected annotation vault id to be %v but was %v", item.Vault.ID, actualVaultId)
}
@@ -193,9 +157,10 @@ func generateFields(numToGenerate int) []*onepassword.ItemField {
return fields
}
func validLabel(v string) bool {
if err := kubeValidate.IsDNS1123Subdomain(v); len(err) > 0 {
return false
func ParseVaultIdAndItemIdFromPath(path string) (string, string, error) {
splitPath := strings.Split(path, "/")
if len(splitPath) == 4 && splitPath[0] == "vaults" && splitPath[2] == "items" {
return splitPath[1], splitPath[3], nil
}
return true
return "", "", fmt.Errorf("%q is not an acceptable path for One Password item. Must be of the format: `vaults/{vault_id}/items/{item_id}`", path)
}

View File

@@ -9,7 +9,7 @@ import (
const (
OnepasswordPrefix = "operator.1password.io"
ItemReferenceAnnotation = OnepasswordPrefix + "/item-reference"
ItemPathAnnotation = OnepasswordPrefix + "/item-path"
NameAnnotation = OnepasswordPrefix + "/item-name"
VersionAnnotation = OnepasswordPrefix + "/item-version"
RestartAnnotation = OnepasswordPrefix + "/last-restarted"

View File

@@ -22,7 +22,7 @@ func TestFilterAnnotations(t *testing.T) {
if len(filteredAnnotations) != 2 {
t.Errorf("Unexpected number of filtered annotations returned. Expected 2, got %v", len(filteredAnnotations))
}
_, found := filteredAnnotations[ItemReferenceAnnotation]
_, found := filteredAnnotations[ItemPathAnnotation]
if !found {
t.Errorf("One Password Annotation was filtered when it should not have been")
}
@@ -87,7 +87,7 @@ func TestGetNoAnnotationsForDeployment(t *testing.T) {
func getValidAnnotations() map[string]string {
return map[string]string{
ItemReferenceAnnotation: "op://b3e4c7fc-8bf7-4c22-b8bb-147539f10e4f/b3e4c7fc-8bf7-4c22-b8bb-147539f10e4f",
NameAnnotation: "secretName",
ItemPathAnnotation: "vaults/b3e4c7fc-8bf7-4c22-b8bb-147539f10e4f/items/b3e4c7fc-8bf7-4c22-b8bb-147539f10e4f",
NameAnnotation: "secretName",
}
}

View File

@@ -2,7 +2,6 @@ package onepassword
import (
"context"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"os"
appsv1 "k8s.io/api/apps/v1"
@@ -18,13 +17,13 @@ var logConnectSetup = logf.Log.WithName("ConnectSetup")
var deploymentPath = "deploy/connect/deployment.yaml"
var servicePath = "deploy/connect/service.yaml"
func SetupConnect(kubeClient client.Client, deploymentNamespace string) error {
err := setupService(kubeClient, servicePath, deploymentNamespace)
func SetupConnect(kubeClient client.Client) error {
err := setupService(kubeClient, servicePath)
if err != nil {
return err
}
err = setupDeployment(kubeClient, deploymentPath, deploymentNamespace)
err = setupDeployment(kubeClient, deploymentPath)
if err != nil {
return err
}
@@ -32,22 +31,22 @@ func SetupConnect(kubeClient client.Client, deploymentNamespace string) error {
return nil
}
func setupDeployment(kubeClient client.Client, deploymentPath string, deploymentNamespace string) error {
func setupDeployment(kubeClient client.Client, deploymentPath string) error {
existingDeployment := &appsv1.Deployment{}
// check if deployment has already been created
err := kubeClient.Get(context.Background(), types.NamespacedName{Name: "onepassword-connect", Namespace: deploymentNamespace}, existingDeployment)
err := kubeClient.Get(context.Background(), types.NamespacedName{Name: "onepassword-connect", Namespace: "default"}, existingDeployment)
if err != nil {
if errors.IsNotFound(err) {
logConnectSetup.Info("No existing Connect deployment found. Creating Deployment")
return createDeployment(kubeClient, deploymentPath, deploymentNamespace)
return createDeployment(kubeClient, deploymentPath)
}
}
return err
}
func createDeployment(kubeClient client.Client, deploymentPath string, deploymentNamespace string) error {
deployment, err := getDeploymentToCreate(deploymentPath, deploymentNamespace)
func createDeployment(kubeClient client.Client, deploymentPath string) error {
deployment, err := getDeploymentToCreate(deploymentPath)
if err != nil {
return err
}
@@ -60,16 +59,12 @@ func createDeployment(kubeClient client.Client, deploymentPath string, deploymen
return nil
}
func getDeploymentToCreate(deploymentPath string, deploymentNamespace string) (*appsv1.Deployment, error) {
func getDeploymentToCreate(deploymentPath string) (*appsv1.Deployment, error) {
f, err := os.Open(deploymentPath)
if err != nil {
return nil, err
}
deployment := &appsv1.Deployment{
ObjectMeta: v1.ObjectMeta{
Namespace: deploymentNamespace,
},
}
deployment := &appsv1.Deployment{}
err = yaml.NewYAMLOrJSONDecoder(f, 4096).Decode(deployment)
if err != nil {
@@ -78,30 +73,26 @@ func getDeploymentToCreate(deploymentPath string, deploymentNamespace string) (*
return deployment, nil
}
func setupService(kubeClient client.Client, servicePath string, deploymentNamespace string) error {
func setupService(kubeClient client.Client, servicePath string) error {
existingService := &corev1.Service{}
//check if service has already been created
err := kubeClient.Get(context.Background(), types.NamespacedName{Name: "onepassword-connect", Namespace: deploymentNamespace}, existingService)
err := kubeClient.Get(context.Background(), types.NamespacedName{Name: "onepassword-connect", Namespace: "default"}, existingService)
if err != nil {
if errors.IsNotFound(err) {
logConnectSetup.Info("No existing Connect service found. Creating Service")
return createService(kubeClient, servicePath, deploymentNamespace)
return createService(kubeClient, servicePath)
}
}
return err
}
func createService(kubeClient client.Client, servicePath string, deploymentNamespace string) error {
func createService(kubeClient client.Client, servicePath string) error {
f, err := os.Open(servicePath)
if err != nil {
return err
}
service := &corev1.Service{
ObjectMeta: v1.ObjectMeta{
Namespace: deploymentNamespace,
},
}
service := &corev1.Service{}
err = yaml.NewYAMLOrJSONDecoder(f, 4096).Decode(service)
if err != nil {

View File

@@ -25,7 +25,7 @@ func TestServiceSetup(t *testing.T) {
// Create a fake client to mock API calls.
client := fake.NewFakeClientWithScheme(s, objs...)
err := setupService(client, "../../deploy/connect/service.yaml", defaultNamespacedName.Namespace)
err := setupService(client, "../../deploy/connect/service.yaml")
if err != nil {
t.Errorf("Error Setting Up Connect: %v", err)
@@ -50,7 +50,7 @@ func TestDeploymentSetup(t *testing.T) {
// Create a fake client to mock API calls.
client := fake.NewFakeClientWithScheme(s, objs...)
err := setupDeployment(client, "../../deploy/connect/deployment.yaml", defaultNamespacedName.Namespace)
err := setupDeployment(client, "../../deploy/connect/deployment.yaml")
if err != nil {
t.Errorf("Error Setting Up Connect: %v", err)

View File

@@ -11,16 +11,11 @@ import (
var logger = logf.Log.WithName("retrieve_item")
const (
secretReferencePrefix = "op://"
)
func GetOnePasswordItemByReference(opConnectClient connect.Client, reference string) (*onepassword.Item, error) {
vaultValue, itemValue, err := ParseReference(reference)
func GetOnePasswordItemByPath(opConnectClient connect.Client, path string) (*onepassword.Item, error) {
vaultValue, itemValue, err := ParseVaultAndItemFromPath(path)
if err != nil {
return nil, err
}
vaultId, err := getVaultId(opConnectClient, vaultValue)
if err != nil {
return nil, err
@@ -38,28 +33,12 @@ func GetOnePasswordItemByReference(opConnectClient connect.Client, reference str
return item, nil
}
func ParseReference(reference string) (string, string, error) {
if !strings.HasPrefix(reference, secretReferencePrefix) {
return "", "", fmt.Errorf("secret reference should start with `op://`")
}
path := strings.TrimPrefix(reference, secretReferencePrefix)
func ParseVaultAndItemFromPath(path string) (string, string, error) {
splitPath := strings.Split(path, "/")
if len(splitPath) != 2 {
return "", "", fmt.Errorf("Invalid secret reference : %s. Secret references should match op://<vault>/<item>", reference)
if len(splitPath) == 4 && splitPath[0] == "vaults" && splitPath[2] == "items" {
return splitPath[1], splitPath[3], nil
}
vault := splitPath[0]
if vault == "" {
return "", "", fmt.Errorf("Invalid secret reference : %s. Vault can't be empty.", reference)
}
item := splitPath[1]
if item == "" {
return "", "", fmt.Errorf("Invalid secret reference : %s. Item can't be empty.", reference)
}
return vault, item, nil
return "", "", fmt.Errorf("%q is not an acceptable path for One Password item. Must be of the format: `vaults/{vault_id}/items/{item_id}`", path)
}
func getVaultId(client connect.Client, vaultIdentifier string) (string, error) {

View File

@@ -110,13 +110,13 @@ func (h *SecretUpdateHandler) updateKubernetesSecrets() (map[string]map[string]*
for i := 0; i < len(secrets.Items); i++ {
secret := secrets.Items[i]
itemReference := secret.Annotations[ItemReferenceAnnotation]
itemPath := secret.Annotations[ItemPathAnnotation]
currentVersion := secret.Annotations[VersionAnnotation]
if len(itemReference) == 0 || len(currentVersion) == 0 {
if len(itemPath) == 0 || len(currentVersion) == 0 {
continue
}
item, err := GetOnePasswordItemByReference(h.opConnectClient, secret.Annotations[ItemReferenceAnnotation])
item, err := GetOnePasswordItemByPath(h.opConnectClient, secret.Annotations[ItemPathAnnotation])
if err != nil {
return nil, fmt.Errorf("Failed to retrieve item: %v", err)
}

View File

@@ -51,7 +51,7 @@ var (
"password": []byte(password),
"username": []byte(username),
}
itemReference = fmt.Sprintf("op://%v/%v", vaultId, itemId)
itemPath = fmt.Sprintf("vaults/%v/items/%v", vaultId, itemId)
)
var defaultNamespace = &corev1.Namespace{
@@ -73,8 +73,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
NameAnnotation: "unlrelated secret",
ItemReferenceAnnotation: itemReference,
NameAnnotation: "unlrelated secret",
ItemPathAnnotation: itemPath,
},
},
},
@@ -83,8 +83,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: "old version",
ItemReferenceAnnotation: itemReference,
VersionAnnotation: "old version",
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,
@@ -95,8 +95,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: fmt.Sprint(itemVersion),
ItemReferenceAnnotation: itemReference,
VersionAnnotation: fmt.Sprint(itemVersion),
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,
@@ -149,8 +149,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: "old version",
ItemReferenceAnnotation: itemReference,
VersionAnnotation: "old version",
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,
@@ -161,8 +161,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: fmt.Sprint(itemVersion),
ItemReferenceAnnotation: itemReference,
VersionAnnotation: fmt.Sprint(itemVersion),
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,
@@ -186,8 +186,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
ItemReferenceAnnotation: itemReference,
NameAnnotation: name,
ItemPathAnnotation: itemPath,
NameAnnotation: name,
},
},
},
@@ -196,8 +196,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: "old version",
ItemReferenceAnnotation: itemReference,
VersionAnnotation: "old version",
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,
@@ -208,8 +208,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: fmt.Sprint(itemVersion),
ItemReferenceAnnotation: itemReference,
VersionAnnotation: fmt.Sprint(itemVersion),
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,
@@ -255,8 +255,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: "old version",
ItemReferenceAnnotation: itemReference,
VersionAnnotation: "old version",
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,
@@ -267,8 +267,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: fmt.Sprint(itemVersion),
ItemReferenceAnnotation: itemReference,
VersionAnnotation: fmt.Sprint(itemVersion),
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,
@@ -292,8 +292,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
ItemReferenceAnnotation: itemReference,
NameAnnotation: name,
ItemPathAnnotation: itemPath,
NameAnnotation: name,
},
},
},
@@ -302,8 +302,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: fmt.Sprint(itemVersion),
ItemReferenceAnnotation: itemReference,
VersionAnnotation: fmt.Sprint(itemVersion),
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,
@@ -314,8 +314,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: fmt.Sprint(itemVersion),
ItemReferenceAnnotation: itemReference,
VersionAnnotation: fmt.Sprint(itemVersion),
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,
@@ -369,8 +369,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: "old version",
ItemReferenceAnnotation: itemReference,
VersionAnnotation: "old version",
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,
@@ -381,8 +381,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: fmt.Sprint(itemVersion),
ItemReferenceAnnotation: itemReference,
VersionAnnotation: fmt.Sprint(itemVersion),
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,
@@ -439,7 +439,7 @@ var tests = []testUpdateSecretTask{
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: "old version",
ItemReferenceAnnotation: itemReference,
ItemPathAnnotation: itemPath,
RestartDeploymentsAnnotation: "true",
},
},
@@ -452,7 +452,7 @@ var tests = []testUpdateSecretTask{
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: fmt.Sprint(itemVersion),
ItemReferenceAnnotation: itemReference,
ItemPathAnnotation: itemPath,
RestartDeploymentsAnnotation: "true",
},
},
@@ -510,7 +510,7 @@ var tests = []testUpdateSecretTask{
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: "old version",
ItemReferenceAnnotation: itemReference,
ItemPathAnnotation: itemPath,
RestartDeploymentsAnnotation: "false",
},
},
@@ -523,7 +523,7 @@ var tests = []testUpdateSecretTask{
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: fmt.Sprint(itemVersion),
ItemReferenceAnnotation: itemReference,
ItemPathAnnotation: itemPath,
RestartDeploymentsAnnotation: "false",
},
},
@@ -580,8 +580,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: "old version",
ItemReferenceAnnotation: itemReference,
VersionAnnotation: "old version",
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,
@@ -592,8 +592,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: fmt.Sprint(itemVersion),
ItemReferenceAnnotation: itemReference,
VersionAnnotation: fmt.Sprint(itemVersion),
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,
@@ -657,8 +657,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: "old version",
ItemReferenceAnnotation: itemReference,
VersionAnnotation: "old version",
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,
@@ -669,8 +669,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: fmt.Sprint(itemVersion),
ItemReferenceAnnotation: itemReference,
VersionAnnotation: fmt.Sprint(itemVersion),
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,
@@ -730,8 +730,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: "old version",
ItemReferenceAnnotation: itemReference,
VersionAnnotation: "old version",
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,
@@ -742,8 +742,8 @@ var tests = []testUpdateSecretTask{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
VersionAnnotation: fmt.Sprint(itemVersion),
ItemReferenceAnnotation: itemReference,
VersionAnnotation: fmt.Sprint(itemVersion),
ItemPathAnnotation: itemPath,
},
},
Data: expectedSecretData,