diff --git a/.github/codecov.yml b/.github/codecov.yml deleted file mode 100644 index fb1653f3b..000000000 --- a/.github/codecov.yml +++ /dev/null @@ -1,5 +0,0 @@ -ignore: -- pkg/provider/**/fake -coverage: - round: down - precision: 2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79538a6de..ddf43f263 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,9 +12,7 @@ env: # Common versions GO_VERSION: '1.17' GOLANGCI_VERSION: 'v1.42.1' - # list of available versions: https://storage.googleapis.com/kubebuilder-tools - # TODO: 1.21.2 does not shut down properly with controller-runtime 0.9.2 - KUBEBUILDER_TOOLS_VERSION: '1.20.2' + KUBERNETES_VERSION: '1.23.x' DOCKER_BUILDX_VERSION: 'v0.4.2' # Common users. We can't run a step 'if secrets.GHCR_USERNAME != ""' but we can run @@ -164,22 +162,22 @@ jobs: key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }} restore-keys: ${{ runner.os }}-pkg- - - name: Add envtest binaries - run: | - curl -sSLo envtest-bins.tar.gz "https://storage.googleapis.com/kubebuilder-tools/kubebuilder-tools-${{env.KUBEBUILDER_TOOLS_VERSION}}-linux-amd64.tar.gz" - sudo mkdir -p /usr/local/kubebuilder - sudo tar -C /usr/local/kubebuilder --strip-components=1 -zvxf envtest-bins.tar.gz + - name: Add setup-envtest + run: | + go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest + setup-envtest use ${{env.KUBERNETES_VERSION}} -p env --os $(go env GOOS) --arch $(go env GOARCH) - name: Cache envtest binaries uses: actions/cache@v2.1.7 with: - path: /usr/local/kubebuilder - key: ${{ runner.os }}-kubebuilder-${{env.KUBEBUILDER_TOOLS_VERSION}} + path: /home/runner/.local/share/kubebuilder-envtest/ + key: ${{ runner.os }}-kubebuilder-${{env.KUBERNETES_VERSION}} restore-keys: ${{ runner.os }}-kubebuilder- - name: Run Unit Tests run: | export KUBEBUILDER_ATTACH_CONTROL_PLANE_OUTPUT=true + source <(setup-envtest use ${{env.KUBERNETES_VERSION}} -p env --os $(go env GOOS) --arch $(go env GOARCH)) make test diff --git a/.github/workflows/e2e-managed.yml b/.github/workflows/e2e-managed.yml index adf221f6f..623b0d296 100644 --- a/.github/workflows/e2e-managed.yml +++ b/.github/workflows/e2e-managed.yml @@ -1,6 +1,5 @@ # Run secret-dependent e2e tests only after /ok-to-test-managed approval on: - pull_request: repository_dispatch: types: [ok-to-test-managed-command] @@ -49,6 +48,48 @@ jobs: steps: + # create new status check for this specific provider + - uses: actions/github-script@v1 + if: ${{ always() }} + env: + number: ${{ github.event.client_payload.pull_request.number }} + provider: ${{ github.event.client_payload.slash_command.args.named.provider }} + job: ${{ github.job }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { data: pull } = await github.pulls.get({ + ...context.repo, + pull_number: process.env.number + }); + const ref = pull.head.sha; + console.log("\n\nPR sha: " + ref) + const { data: checks } = await github.checks.listForRef({ + ...context.repo, + ref + }); + const job_name = process.env.job + "-" + process.env.provider + console.log("\n\nPR CHECKS: " + checks) + const check = checks.check_runs.filter(c => c.name === job_name); + console.log("\n\nPR Filtered CHECK: " + check) + console.log(check) + if(check && check.length > 0){ + const { data: result } = await github.checks.update({ + ...context.repo, + check_run_id: check[0].id, + status: 'in_progress', + }); + return result; + } + const { data: result } = await github.checks.create({ + ...context.repo, + name: job_name, + head_sha: pull.head.sha, + status: 'in_progress', + }); + return result; + + # Check out merge commit - name: Fork based /ok-to-test-managed checkout uses: actions/checkout@v2 @@ -164,6 +205,7 @@ jobs: if: ${{ always() }} env: number: ${{ github.event.client_payload.pull_request.number }} + provider: ${{ github.event.client_payload.slash_command.args.named.provider }} job: ${{ github.job }} # Conveniently, job.status maps to https://developer.github.com/v3/checks/runs/#update-a-check-run conclusion: ${{ job.status }} @@ -180,8 +222,9 @@ jobs: ...context.repo, ref }); + const job_name = process.env.job + "-" + process.env.provider console.log("\n\nPR CHECKS: " + checks) - const check = checks.check_runs.filter(c => c.name === process.env.job); + const check = checks.check_runs.filter(c => c.name === job_name); console.log("\n\nPR Filtered CHECK: " + check) console.log(check) const { data: result } = await github.checks.update({ diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index ed5b5d23e..15966c048 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -9,6 +9,8 @@ env: GO_VERSION: '1.17' GOLANGCI_VERSION: 'v1.33' DOCKER_BUILDX_VERSION: 'v0.4.2' + KIND_VERSION: 'v0.11.1' + KIND_IMAGE: 'kindest/node:v1.23.3' # Common users. We can't run a step 'if secrets.GHCR_USERNAME != ""' but we can run # a step 'if env.GHCR_USERNAME' != ""', so we copy these to succinctly test whether @@ -71,9 +73,9 @@ jobs: - name: Setup kind uses: engineerd/setup-kind@v0.5.0 with: - version: "v0.11.1" + version: ${{env.KIND_VERSION}} wait: 10m - node_image: kindest/node:v1.20.7 + node_image: ${{env.KIND_IMAGE}} name: external-secrets - name: Setup Docker Buildx @@ -134,9 +136,9 @@ jobs: - name: Setup kind uses: engineerd/setup-kind@v0.5.0 with: - version: "v0.11.1" + version: ${{env.KIND_VERSION}} wait: 10m - node_image: kindest/node:v1.20.7 + node_image: ${{env.KIND_IMAGE}} name: external-secrets - name: Setup Docker Buildx diff --git a/.github/workflows/helm.yml b/.github/workflows/helm.yml index a0a91df30..0f82f9ed0 100644 --- a/.github/workflows/helm.yml +++ b/.github/workflows/helm.yml @@ -26,7 +26,7 @@ jobs: make helm.generate - name: Set up Helm - uses: azure/setup-helm@v1.1 + uses: azure/setup-helm@v2.0 with: version: v3.4.2 @@ -62,7 +62,7 @@ jobs: git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - name: Set up Helm - uses: azure/setup-helm@v1.1 + uses: azure/setup-helm@v2.0 with: version: v3.4.2 diff --git a/apis/externalsecrets/v1alpha1/secretstore_fake_types.go b/apis/externalsecrets/v1alpha1/secretstore_fake_types.go new file mode 100644 index 000000000..5c21b6fd0 --- /dev/null +++ b/apis/externalsecrets/v1alpha1/secretstore_fake_types.go @@ -0,0 +1,27 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +// FakeProvider configures a fake provider that returns static values. +type FakeProvider struct { + Data []FakeProviderData `json:"data"` +} + +type FakeProviderData struct { + Key string `json:"key"` + Value string `json:"value,omitempty"` + ValueMap map[string]string `json:"valueMap,omitempty"` + Version string `json:"version,omitempty"` +} diff --git a/apis/externalsecrets/v1alpha1/secretstore_types.go b/apis/externalsecrets/v1alpha1/secretstore_types.go index 0018504ae..3f90a5ec3 100644 --- a/apis/externalsecrets/v1alpha1/secretstore_types.go +++ b/apis/externalsecrets/v1alpha1/secretstore_types.go @@ -81,6 +81,10 @@ type SecretStoreProvider struct { // Webhook configures this store to sync secrets using a generic templated webhook // +optional Webhook *WebhookProvider `json:"webhook,omitempty"` + + // Fake configures a store with static key/value pairs + // +optional + Fake *FakeProvider `json:"fake,omitempty"` } type SecretStoreRetrySettings struct { diff --git a/apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go b/apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go index 00e4e17c1..cf377f8b2 100644 --- a/apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go +++ b/apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go @@ -599,6 +599,50 @@ func (in *ExternalSecretTemplateMetadata) DeepCopy() *ExternalSecretTemplateMeta return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FakeProvider) DeepCopyInto(out *FakeProvider) { + *out = *in + if in.Data != nil { + in, out := &in.Data, &out.Data + *out = make([]FakeProviderData, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FakeProvider. +func (in *FakeProvider) DeepCopy() *FakeProvider { + if in == nil { + return nil + } + out := new(FakeProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FakeProviderData) DeepCopyInto(out *FakeProviderData) { + *out = *in + if in.ValueMap != nil { + in, out := &in.ValueMap, &out.ValueMap + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FakeProviderData. +func (in *FakeProviderData) DeepCopy() *FakeProviderData { + if in == nil { + return nil + } + out := new(FakeProviderData) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GCPSMAuth) DeepCopyInto(out *GCPSMAuth) { *out = *in @@ -939,6 +983,11 @@ func (in *SecretStoreProvider) DeepCopyInto(out *SecretStoreProvider) { *out = new(WebhookProvider) (*in).DeepCopyInto(*out) } + if in.Fake != nil { + in, out := &in.Fake, &out.Fake + *out = new(FakeProvider) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretStoreProvider. diff --git a/deploy/crds/external-secrets.io_clustersecretstores.yaml b/deploy/crds/external-secrets.io_clustersecretstores.yaml index a4fa020bb..8a71f2998 100644 --- a/deploy/crds/external-secrets.io_clustersecretstores.yaml +++ b/deploy/crds/external-secrets.io_clustersecretstores.yaml @@ -380,6 +380,29 @@ spec: required: - vaultUrl type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object gcpsm: description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider diff --git a/deploy/crds/external-secrets.io_secretstores.yaml b/deploy/crds/external-secrets.io_secretstores.yaml index ac2da8e28..18a60f554 100644 --- a/deploy/crds/external-secrets.io_secretstores.yaml +++ b/deploy/crds/external-secrets.io_secretstores.yaml @@ -380,6 +380,29 @@ spec: required: - vaultUrl type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object gcpsm: description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider diff --git a/docs/contributing-process.md b/docs/contributing-process.md index 1b875b0d1..f89a43f9e 100644 --- a/docs/contributing-process.md +++ b/docs/contributing-process.md @@ -31,6 +31,82 @@ for the lifecycle of the PR: review, merging, ping on inactivity, close. We close pull requests or issues if there is no response from the author for a period of time. Feel free to reopen if you want to get back on it. +### Triggering e2e tests + +We have an extensive set of e2e tests that test the integration with *real* cloud provider APIs. +Maintainers must trigger these kind of tests manually for PRs that come from forked repositories. These tests run inside a `kind` cluster in the GitHub Actions runner: + +``` +/ok-to-test sha=xxxxxx +``` + +#### Executing e2e tests locally + +You have to prepare your shell environment with the necessary variables so the e2e test +runner knows what credentials to use. See `e2e/run.sh` for the variables that are passed in. +If you e.g. want to test AWS integration make sure set all `AWS_*` variables mentioned +in that file. + +Use [ginkgo labels](https://onsi.github.io/ginkgo/#spec-labels) to select the tests +you want to execute. You have to specify `!managed` to ensure that you do not +run managed tests. + +``` +make test.e2e GINKGO_LABELS='gcp&&!managed' +``` + +#### Managed Kubernetes e2e tests + +There's another suite of e2e tests that integrate with managed Kuberentes offerings. +They create real infrastructure at a cloud provider and deploy the controller +into that environment. +This is necessary to test the authentication integration +([GCP Worklaod Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity), +[EKS IRSA](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)...). + +These tests are time intensive (~20-45min) and must be triggered manually by +a maintainer when a particular provider or authentication mechanism was changed: + +``` +/ok-to-test-managed sha=xxxxxx provider=aws +# or +/ok-to-test-managed sha=xxxxxx provider=gcp +``` + +Both tests can run in parallel. Once started they add a dynamic GitHub check `integration-managed-(gcp|aws)` to the PR that triggered the test. + + +### Executing Managed Kubernetes e2e tests locally + +You have to prepare your shell environment with the necessary variables so the e2e +test runner knows what credentials to use. See `.github/workflows/e2e-managed.yml` +for the variables that are passed in. If you e.g. want to test AWS integration make +sure set all variables containing `AWS_*` and `TF_VAR_AWS_*` mentioned in that file. + +Then execute `tf.apply.aws` or `tf.apply.gcp` to create the infrastructure. + +``` +make tf.apply.aws +``` + +Then run the `managed` testsuite. You will need push permissions to the external-secrets ghcr repository. You can set `IMAGE_REGISTRY` to control which image registry is used to store the controller and e2e test images in. + +You also have to setup a proper Kubeconfig so the e2e test pod gets deployed into the managed cluster. + +``` +aws eks update-kubeconfig --name ${AWS_CLUSTER_NAME} +or +gcloud container clusters get-credentials ${GCP_GKE_CLUSTER} --region europe-west1-b +``` + +Use [ginkgo labels](https://onsi.github.io/ginkgo/#spec-labels) to select the tests +you want to execute. + +``` +# you may have to set IMAGE_REGISTRY=docker.io/your-user/external-secrets +make test.e2e.managed GINKGO_LABELS='gcp' +``` + ## Proposal Process Before we introduce significant changes to the project we want to gather feedback from the community to ensure that we progress in the right direction before we diff --git a/docs/provider-fake.md b/docs/provider-fake.md new file mode 100644 index 000000000..f538f9fd5 --- /dev/null +++ b/docs/provider-fake.md @@ -0,0 +1,26 @@ +We provide a `fake` implementation to help with testing. This provider returns static key/value pairs and nothing else. +To use the `fake` provider simply create a `SecretStore` or `ClusterSecretStore` and configure it like in the following example: + +!!! note inline end + The provider returns static data configured in `value` or `valueMap`. You can define a `version`, too. If set the `remoteRef` from an ExternalSecret must match otherwise no value is returned. + +```yaml +{% include 'fake-provider-store.yaml' %} +``` + +Please note that `value` is intended for exclusive use with `data` and `valueMap` for `dataFrom`. +Here is an example `ExternalSecret` that displays this behavior: + +!!! warning inline end + This provider supports specifying different `data[].version` configurations. However, `data[].property` is ignored. + +```yaml +{% include 'fake-provider-es.yaml' %} +``` + +This results in the following secret: + + +```yaml +{% include 'fake-provider-secret.yaml' %} +``` diff --git a/docs/snippets/fake-provider-es.yaml b/docs/snippets/fake-provider-es.yaml new file mode 100644 index 000000000..cb3498836 --- /dev/null +++ b/docs/snippets/fake-provider-es.yaml @@ -0,0 +1,18 @@ +apiVersion: external-secrets.io/v1alpha1 +kind: ExternalSecret +metadata: + name: example +spec: + refreshInterval: 1h + secretStoreRef: + name: fake + kind: ClusterSecretStore + target: + name: secret-to-be-created + data: + - secretKey: foo_bar + remoteRef: + key: /foo/bar + version: v1 + dataFrom: + - key: /foo/baz \ No newline at end of file diff --git a/docs/snippets/fake-provider-secret.yaml b/docs/snippets/fake-provider-secret.yaml new file mode 100644 index 000000000..ac4d40d57 --- /dev/null +++ b/docs/snippets/fake-provider-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-to-be-created + namespace: default +data: + foo_bar: SEVMTE8x # HELLO1 (via data) + foo: ZXhhbXBsZQ== # example (via dataFrom) + other: dGhpbmc= # thing (via dataFrom) diff --git a/docs/snippets/fake-provider-store.yaml b/docs/snippets/fake-provider-store.yaml new file mode 100644 index 000000000..6d429235e --- /dev/null +++ b/docs/snippets/fake-provider-store.yaml @@ -0,0 +1,20 @@ +apiVersion: external-secrets.io/v1alpha1 +kind: ClusterSecretStore +metadata: + name: fake +spec: + provider: + fake: + data: + - key: "/foo/bar" + value: "HELLO1" + version: "v1" + - key: "/foo/bar" + value: "HELLO2" + version: "v2" + - key: "/foo/baz" + valueMap: + foo: example + other: thing + + diff --git a/docs/spec.md b/docs/spec.md index a1b3aa453..80483267a 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -714,6 +714,237 @@ string +

ClusterExternalSecret +

+

+

ClusterExternalSecret is the Schema for the clusterexternalsecrets API.

+

+ + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+metadata
+ + +Kubernetes meta/v1.ObjectMeta + + +
+Refer to the Kubernetes API documentation for the fields of the +metadata field. +
+spec
+ + +ClusterExternalSecretSpec + + +
+
+
+ + + + + + + + + + + + + +
+externalSecretSpec
+ + +ExternalSecretSpec + + +
+

The spec for the ExternalSecrets to be created

+
+externalSecretName
+ +string + +
+(Optional) +

The name of the external secrets to be created defaults to the name of the ClusterExternalSecret

+
+namespaceSelector
+ + +Kubernetes meta/v1.LabelSelector + + +
+

The labels to select by to find the Namespaces to create the ExternalSecrets in.

+
+
+status
+ + +ClusterExternalSecretStatus + + +
+
+

ClusterExternalSecretConditionType +(string alias)

+

+(Appears on: +ClusterExternalSecretStatus) +

+

+

+ + + + + + + + + + + + + + +
ValueDescription

"NotReady"

"PartiallyReady"

"Ready"

+

ClusterExternalSecretSpec +

+

+(Appears on: +ClusterExternalSecret) +

+

+

ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret.

+

+ + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+externalSecretSpec
+ + +ExternalSecretSpec + + +
+

The spec for the ExternalSecrets to be created

+
+externalSecretName
+ +string + +
+(Optional) +

The name of the external secrets to be created defaults to the name of the ClusterExternalSecret

+
+namespaceSelector
+ + +Kubernetes meta/v1.LabelSelector + + +
+

The labels to select by to find the Namespaces to create the ExternalSecrets in.

+
+

ClusterExternalSecretStatus +

+

+(Appears on: +ClusterExternalSecret) +

+

+

ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret.

+

+ + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+type
+ + +ClusterExternalSecretConditionType + + +
+
+status
+ + +Kubernetes core/v1.ConditionStatus + + +
+
+externalSecretStatuses
+ + +[]ExternalSecretStatus + + +
+(Optional) +

ClusterSecretStore

@@ -1084,6 +1315,7 @@ string

(Appears on: +ClusterExternalSecretSpec, ExternalSecret)

@@ -1171,6 +1403,7 @@ If multiple entries are specified, the Secret keys are merged in the specified o

(Appears on: +ClusterExternalSecretStatus, ExternalSecret)

@@ -1486,6 +1719,95 @@ map[string]string +

FakeProvider +

+

+(Appears on: +SecretStoreProvider) +

+

+

FakeProvider configures a fake provider that returns static values

+

+ + + + + + + + + + + + + +
FieldDescription
+data
+ + +[]FakeProviderData + + +
+
+

FakeProviderData +

+

+(Appears on: +FakeProvider) +

+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+key
+ +string + +
+
+value
+ +string + +
+
+valueMap
+ +map[string]string + +
+
+version
+ +string + +
+

GCPSMAuth

@@ -2310,6 +2632,20 @@ WebhookProvider

Webhook configures this store to sync secrets using a generic templated webhook

+ + +fake
+ + +FakeProvider + + + + +(Optional) +

Fake configures a store with static key/value pairs

+ +

SecretStoreRef diff --git a/e2e/Makefile b/e2e/Makefile index adaf57e59..b106d5676 100644 --- a/e2e/Makefile +++ b/e2e/Makefile @@ -2,7 +2,7 @@ MAKEFLAGS += --warn-undefined-variables SHELL := /bin/bash .SHELLFLAGS := -euo pipefail -c -KIND_IMG = "kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6" +KIND_IMG = "kindest/node:v1.23.3@sha256:0df8215895129c0d3221cda19847d1296c4f29ec93487339149333bd9d899e5a" BUILD_ARGS ?= export E2E_IMAGE_REGISTRY ?= ghcr.io/external-secrets/external-secrets-e2e diff --git a/e2e/suite/aws/common.go b/e2e/suite/aws/common.go new file mode 100644 index 000000000..b725b74a7 --- /dev/null +++ b/e2e/suite/aws/common.go @@ -0,0 +1,97 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package common + +import ( + "context" + + // nolint + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1" + esmetav1 "github.com/external-secrets/external-secrets/apis/meta/v1" + "github.com/external-secrets/external-secrets/e2e/framework" +) + +const ( + WithReferencedIRSA = "with referenced IRSA" + WithMountedIRSA = "with mounted IRSA" + StaticCredentialsSecretName = "provider-secret" +) + +func ReferencedIRSAStoreName(f *framework.Framework) string { + return "irsa-ref-" + f.Namespace.Name +} + +func MountedIRSAStoreName(f *framework.Framework) string { + return "irsa-mounted-" + f.Namespace.Name +} + +func UseClusterSecretStore(tc *framework.TestCase) { + tc.ExternalSecret.Spec.SecretStoreRef.Kind = esv1alpha1.ClusterSecretStoreKind + tc.ExternalSecret.Spec.SecretStoreRef.Name = ReferencedIRSAStoreName(tc.Framework) +} + +func UseMountedIRSAStore(tc *framework.TestCase) { + tc.ExternalSecret.Spec.SecretStoreRef.Kind = esv1alpha1.SecretStoreKind + tc.ExternalSecret.Spec.SecretStoreRef.Name = MountedIRSAStoreName(tc.Framework) +} + +// StaticStore is namespaced and references +// static credentials from a secret. +func SetupStaticStore(f *framework.Framework, kid, sak, region string, serviceType esv1alpha1.AWSServiceType) { + awsCreds := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: StaticCredentialsSecretName, + Namespace: f.Namespace.Name, + }, + StringData: map[string]string{ + "kid": kid, + "sak": sak, + }, + } + err := f.CRClient.Create(context.Background(), awsCreds) + Expect(err).ToNot(HaveOccurred()) + + secretStore := &esv1alpha1.SecretStore{ + ObjectMeta: metav1.ObjectMeta{ + Name: f.Namespace.Name, + Namespace: f.Namespace.Name, + }, + Spec: esv1alpha1.SecretStoreSpec{ + Provider: &esv1alpha1.SecretStoreProvider{ + AWS: &esv1alpha1.AWSProvider{ + Service: serviceType, + Region: region, + Auth: esv1alpha1.AWSAuth{ + SecretRef: &esv1alpha1.AWSAuthSecretRef{ + AccessKeyID: esmetav1.SecretKeySelector{ + Name: StaticCredentialsSecretName, + Key: "kid", + }, + SecretAccessKey: esmetav1.SecretKeySelector{ + Name: StaticCredentialsSecretName, + Key: "sak", + }, + }, + }, + }, + }, + }, + } + err = f.CRClient.Create(context.Background(), secretStore) + Expect(err).ToNot(HaveOccurred()) +} diff --git a/e2e/suite/aws/parameterstore/parameterstore.go b/e2e/suite/aws/parameterstore/parameterstore.go new file mode 100644 index 000000000..3337bf460 --- /dev/null +++ b/e2e/suite/aws/parameterstore/parameterstore.go @@ -0,0 +1,45 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package aws + +import ( + + // nolint + . "github.com/onsi/ginkgo/v2" + + "github.com/external-secrets/external-secrets/e2e/framework" + "github.com/external-secrets/external-secrets/e2e/suite/common" +) + +var _ = Describe("[aws] ", Label("aws", "parameterstore"), func() { + f := framework.New("eso-aws-ps") + prov := NewFromEnv(f) + + DescribeTable("sync secrets", + framework.TableFunc(f, + prov), + Entry(common.SimpleDataSync(f)), + Entry(common.NestedJSONWithGJSON(f)), + Entry(common.JSONDataFromSync(f)), + Entry(common.JSONDataWithProperty(f)), + Entry(common.JSONDataWithTemplate(f)), + Entry(common.DockerJSONConfig(f)), + Entry(common.DataPropertyDockerconfigJSON(f)), + Entry(common.SSHKeySync(f)), + Entry(common.SSHKeySyncDataProperty(f)), + Entry(common.SyncWithoutTargetName(f)), + Entry(common.JSONDataWithoutTargetName(f)), + ) +}) diff --git a/e2e/suite/aws/parameterstore/parameterstore_managed.go b/e2e/suite/aws/parameterstore/parameterstore_managed.go new file mode 100644 index 000000000..5b611cd65 --- /dev/null +++ b/e2e/suite/aws/parameterstore/parameterstore_managed.go @@ -0,0 +1,85 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package aws + +import ( + + // nolint + . "github.com/onsi/ginkgo/v2" + + "github.com/external-secrets/external-secrets/e2e/framework" + "github.com/external-secrets/external-secrets/e2e/framework/addon" + awscommon "github.com/external-secrets/external-secrets/e2e/suite/aws" + "github.com/external-secrets/external-secrets/e2e/suite/common" +) + +// here we use the global eso instance +// that uses the service account in the default namespace +// which was created by terraform. +var _ = Describe("[awsmanaged] IRSA via referenced service account", Label("aws", "parameterstore", "managed"), func() { + f := framework.New("eso-aws-ps-managed") + prov := NewFromEnv(f) + + // nolint + DescribeTable("sync secrets", + framework.TableFunc(f, + prov), + framework.Compose(awscommon.WithReferencedIRSA, f, common.SimpleDataSync, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.NestedJSONWithGJSON, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.JSONDataFromSync, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.JSONDataWithProperty, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.JSONDataWithTemplate, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.DockerJSONConfig, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.DataPropertyDockerconfigJSON, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.SSHKeySync, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.SSHKeySyncDataProperty, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.SyncWithoutTargetName, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.JSONDataWithoutTargetName, awscommon.UseClusterSecretStore), + ) +}) + +// here we create a central eso instance in the default namespace +// that mounts the service account which was created by terraform. +var _ = Describe("[awsmanaged] with mounted IRSA", Label("aws", "parameterstore", "managed"), func() { + f := framework.New("eso-aws-ps-irsa-managed") + prov := NewFromEnv(f) + + // each test case gets its own ESO instance + BeforeEach(func() { + f.Install(addon.NewESO( + addon.WithControllerClass(f.BaseName), + addon.WithServiceAccount(prov.ServiceAccountName), + addon.WithReleaseName(f.Namespace.Name), + addon.WithNamespace("default"), + )) + }) + + // nolint + DescribeTable("sync secrets", + framework.TableFunc(f, + prov), + framework.Compose(awscommon.WithMountedIRSA, f, common.SimpleDataSync, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.NestedJSONWithGJSON, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.JSONDataFromSync, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.JSONDataWithProperty, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.JSONDataWithTemplate, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.DockerJSONConfig, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.DataPropertyDockerconfigJSON, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.SSHKeySync, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.SSHKeySyncDataProperty, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.SyncWithoutTargetName, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.JSONDataWithoutTargetName, awscommon.UseMountedIRSAStore), + ) +}) diff --git a/e2e/suite/aws/parameterstore/provider.go b/e2e/suite/aws/parameterstore/provider.go new file mode 100644 index 000000000..040309af3 --- /dev/null +++ b/e2e/suite/aws/parameterstore/provider.go @@ -0,0 +1,165 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package aws + +import ( + "context" + "os" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/ssm" + + //nolint + . "github.com/onsi/ginkgo/v2" + + // nolint + . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1" + esmetav1 "github.com/external-secrets/external-secrets/apis/meta/v1" + "github.com/external-secrets/external-secrets/e2e/framework" + "github.com/external-secrets/external-secrets/e2e/framework/log" + common "github.com/external-secrets/external-secrets/e2e/suite/aws" +) + +type Provider struct { + ServiceAccountName string + ServiceAccountNamespace string + + region string + client *ssm.SSM + framework *framework.Framework +} + +func NewProvider(f *framework.Framework, kid, sak, region, saName, saNamespace string) *Provider { + sess, err := session.NewSessionWithOptions(session.Options{ + Config: aws.Config{ + Credentials: credentials.NewStaticCredentials(kid, sak, ""), + Region: aws.String(region), + }, + }) + if err != nil { + Fail(err.Error()) + } + sm := ssm.New(sess) + prov := &Provider{ + ServiceAccountName: saName, + ServiceAccountNamespace: saNamespace, + region: region, + client: sm, + framework: f, + } + + BeforeEach(func() { + common.SetupStaticStore(f, kid, sak, region, esv1alpha1.AWSServiceParameterStore) + prov.SetupReferencedIRSAStore() + prov.SetupMountedIRSAStore() + }) + + AfterEach(func() { + // Cleanup ClusterSecretStore + err := prov.framework.CRClient.Delete(context.Background(), &esv1alpha1.ClusterSecretStore{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.ReferencedIRSAStoreName(f), + }, + }) + Expect(err).ToNot(HaveOccurred()) + }) + + return prov +} + +func NewFromEnv(f *framework.Framework) *Provider { + kid := os.Getenv("AWS_ACCESS_KEY_ID") + sak := os.Getenv("AWS_SECRET_ACCESS_KEY") + region := "eu-west-1" + saName := os.Getenv("AWS_SA_NAME") + saNamespace := os.Getenv("AWS_SA_NAMESPACE") + return NewProvider(f, kid, sak, region, saName, saNamespace) +} + +// CreateSecret creates a secret at the provider. +func (s *Provider) CreateSecret(key, val string) { + _, err := s.client.PutParameter(&ssm.PutParameterInput{ + Name: aws.String(key), + Value: aws.String(val), + DataType: aws.String("text"), + Type: aws.String("String"), + }) + Expect(err).ToNot(HaveOccurred()) +} + +// DeleteSecret deletes a secret at the provider. +func (s *Provider) DeleteSecret(key string) { + _, err := s.client.DeleteParameter(&ssm.DeleteParameterInput{ + Name: aws.String(key), + }) + Expect(err).ToNot(HaveOccurred()) +} + +// MountedIRSAStore is a SecretStore without auth config +// ESO relies on the pod-mounted ServiceAccount when using this store. +func (s *Provider) SetupMountedIRSAStore() { + secretStore := &esv1alpha1.SecretStore{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.MountedIRSAStoreName(s.framework), + Namespace: s.framework.Namespace.Name, + }, + Spec: esv1alpha1.SecretStoreSpec{ + Provider: &esv1alpha1.SecretStoreProvider{ + AWS: &esv1alpha1.AWSProvider{ + Service: esv1alpha1.AWSServiceParameterStore, + Region: s.region, + Auth: esv1alpha1.AWSAuth{}, + }, + }, + }, + } + err := s.framework.CRClient.Create(context.Background(), secretStore) + Expect(err).ToNot(HaveOccurred()) +} + +// ReferncedIRSAStore is a ClusterStore +// that references a (IRSA-) ServiceAccount in the default namespace. +func (s *Provider) SetupReferencedIRSAStore() { + log.Logf("creating IRSA ClusterSecretStore %s", s.framework.Namespace.Name) + secretStore := &esv1alpha1.ClusterSecretStore{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.ReferencedIRSAStoreName(s.framework), + }, + } + _, err := controllerutil.CreateOrUpdate(context.Background(), s.framework.CRClient, secretStore, func() error { + secretStore.Spec.Provider = &esv1alpha1.SecretStoreProvider{ + AWS: &esv1alpha1.AWSProvider{ + Service: esv1alpha1.AWSServiceParameterStore, + Region: s.region, + Auth: esv1alpha1.AWSAuth{ + JWTAuth: &esv1alpha1.AWSJWTAuth{ + ServiceAccountRef: &esmetav1.ServiceAccountSelector{ + Name: s.ServiceAccountName, + Namespace: &s.ServiceAccountNamespace, + }, + }, + }, + }, + } + return nil + }) + Expect(err).ToNot(HaveOccurred()) +} diff --git a/e2e/suite/aws/provider.go b/e2e/suite/aws/secretsmanager/provider.go similarity index 67% rename from e2e/suite/aws/provider.go rename to e2e/suite/aws/secretsmanager/provider.go index 3e1bc0185..61ef9c1e8 100644 --- a/e2e/suite/aws/provider.go +++ b/e2e/suite/aws/secretsmanager/provider.go @@ -29,7 +29,6 @@ import ( // nolint . "github.com/onsi/gomega" - v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -37,24 +36,19 @@ import ( esmetav1 "github.com/external-secrets/external-secrets/apis/meta/v1" "github.com/external-secrets/external-secrets/e2e/framework" "github.com/external-secrets/external-secrets/e2e/framework/log" + common "github.com/external-secrets/external-secrets/e2e/suite/aws" ) -type SMProvider struct { +type Provider struct { ServiceAccountName string ServiceAccountNamespace string - kid string - sak string region string client *secretsmanager.SecretsManager framework *framework.Framework } -const ( - staticCredentialsSecretName = "provider-secret" -) - -func NewSMProvider(f *framework.Framework, kid, sak, region, saName, saNamespace string) *SMProvider { +func NewProvider(f *framework.Framework, kid, sak, region, saName, saNamespace string) *Provider { sess, err := session.NewSessionWithOptions(session.Options{ Config: aws.Config{ Credentials: credentials.NewStaticCredentials(kid, sak, ""), @@ -65,18 +59,16 @@ func NewSMProvider(f *framework.Framework, kid, sak, region, saName, saNamespace Fail(err.Error()) } sm := secretsmanager.New(sess) - prov := &SMProvider{ + prov := &Provider{ ServiceAccountName: saName, ServiceAccountNamespace: saNamespace, - kid: kid, - sak: sak, region: region, client: sm, framework: f, } BeforeEach(func() { - prov.SetupStaticStore() + common.SetupStaticStore(f, kid, sak, region, esv1alpha1.AWSServiceSecretsManager) prov.SetupReferencedIRSAStore() prov.SetupMountedIRSAStore() }) @@ -85,7 +77,7 @@ func NewSMProvider(f *framework.Framework, kid, sak, region, saName, saNamespace // Cleanup ClusterSecretStore err := prov.framework.CRClient.Delete(context.Background(), &esv1alpha1.ClusterSecretStore{ ObjectMeta: metav1.ObjectMeta{ - Name: prov.ReferencedIRSAStoreName(), + Name: common.ReferencedIRSAStoreName(f), }, }) Expect(err).ToNot(HaveOccurred()) @@ -94,17 +86,17 @@ func NewSMProvider(f *framework.Framework, kid, sak, region, saName, saNamespace return prov } -func NewFromEnv(f *framework.Framework) *SMProvider { +func NewFromEnv(f *framework.Framework) *Provider { kid := os.Getenv("AWS_ACCESS_KEY_ID") sak := os.Getenv("AWS_SECRET_ACCESS_KEY") region := "eu-west-1" saName := os.Getenv("AWS_SA_NAME") saNamespace := os.Getenv("AWS_SA_NAMESPACE") - return NewSMProvider(f, kid, sak, region, saName, saNamespace) + return NewProvider(f, kid, sak, region, saName, saNamespace) } // CreateSecret creates a secret at the provider. -func (s *SMProvider) CreateSecret(key, val string) { +func (s *Provider) CreateSecret(key, val string) { // we re-use some secret names throughout our test suite // due to the fact that there is a short delay before the secret is actually deleted // we have to retry creating the secret @@ -129,7 +121,7 @@ func (s *SMProvider) CreateSecret(key, val string) { // DeleteSecret deletes a secret at the provider. // There may be a short delay between calling this function // and the removal of the secret on the provider side. -func (s *SMProvider) DeleteSecret(key string) { +func (s *Provider) DeleteSecret(key string) { log.Logf("deleting secret %s", key) _, err := s.client.DeleteSecret(&secretsmanager.DeleteSecretInput{ SecretId: aws.String(key), @@ -140,10 +132,10 @@ func (s *SMProvider) DeleteSecret(key string) { // MountedIRSAStore is a SecretStore without auth config // ESO relies on the pod-mounted ServiceAccount when using this store. -func (s *SMProvider) SetupMountedIRSAStore() { +func (s *Provider) SetupMountedIRSAStore() { secretStore := &esv1alpha1.SecretStore{ ObjectMeta: metav1.ObjectMeta{ - Name: s.MountedIRSAStoreName(), + Name: common.MountedIRSAStoreName(s.framework), Namespace: s.framework.Namespace.Name, }, Spec: esv1alpha1.SecretStoreSpec{ @@ -160,17 +152,13 @@ func (s *SMProvider) SetupMountedIRSAStore() { Expect(err).ToNot(HaveOccurred()) } -func (s *SMProvider) MountedIRSAStoreName() string { - return "irsa-mounted-" + s.framework.Namespace.Name -} - // ReferncedIRSAStore is a ClusterStore // that references a (IRSA-) ServiceAccount in the default namespace. -func (s *SMProvider) SetupReferencedIRSAStore() { +func (s *Provider) SetupReferencedIRSAStore() { log.Logf("creating IRSA ClusterSecretStore %s", s.framework.Namespace.Name) secretStore := &esv1alpha1.ClusterSecretStore{ ObjectMeta: metav1.ObjectMeta{ - Name: s.ReferencedIRSAStoreName(), + Name: common.ReferencedIRSAStoreName(s.framework), }, } _, err := controllerutil.CreateOrUpdate(context.Background(), s.framework.CRClient, secretStore, func() error { @@ -192,53 +180,3 @@ func (s *SMProvider) SetupReferencedIRSAStore() { }) Expect(err).ToNot(HaveOccurred()) } - -func (s *SMProvider) ReferencedIRSAStoreName() string { - return "irsa-ref-" + s.framework.Namespace.Name -} - -// StaticStore is namespaced and references -// static credentials from a secret. -func (s *SMProvider) SetupStaticStore() { - awsCreds := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: staticCredentialsSecretName, - Namespace: s.framework.Namespace.Name, - }, - StringData: map[string]string{ - "kid": s.kid, - "sak": s.sak, - }, - } - err := s.framework.CRClient.Create(context.Background(), awsCreds) - Expect(err).ToNot(HaveOccurred()) - - secretStore := &esv1alpha1.SecretStore{ - ObjectMeta: metav1.ObjectMeta{ - Name: s.framework.Namespace.Name, - Namespace: s.framework.Namespace.Name, - }, - Spec: esv1alpha1.SecretStoreSpec{ - Provider: &esv1alpha1.SecretStoreProvider{ - AWS: &esv1alpha1.AWSProvider{ - Service: esv1alpha1.AWSServiceSecretsManager, - Region: s.region, - Auth: esv1alpha1.AWSAuth{ - SecretRef: &esv1alpha1.AWSAuthSecretRef{ - AccessKeyID: esmetav1.SecretKeySelector{ - Name: staticCredentialsSecretName, - Key: "kid", - }, - SecretAccessKey: esmetav1.SecretKeySelector{ - Name: staticCredentialsSecretName, - Key: "sak", - }, - }, - }, - }, - }, - }, - } - err = s.framework.CRClient.Create(context.Background(), secretStore) - Expect(err).ToNot(HaveOccurred()) -} diff --git a/e2e/suite/aws/secretsmanager.go b/e2e/suite/aws/secretsmanager/secretsmanager.go similarity index 100% rename from e2e/suite/aws/secretsmanager.go rename to e2e/suite/aws/secretsmanager/secretsmanager.go diff --git a/e2e/suite/aws/secretsmanager/secretsmanager_managed.go b/e2e/suite/aws/secretsmanager/secretsmanager_managed.go new file mode 100644 index 000000000..84f2e9509 --- /dev/null +++ b/e2e/suite/aws/secretsmanager/secretsmanager_managed.go @@ -0,0 +1,85 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package aws + +import ( + + // nolint + . "github.com/onsi/ginkgo/v2" + + "github.com/external-secrets/external-secrets/e2e/framework" + "github.com/external-secrets/external-secrets/e2e/framework/addon" + awscommon "github.com/external-secrets/external-secrets/e2e/suite/aws" + "github.com/external-secrets/external-secrets/e2e/suite/common" +) + +// here we use the global eso instance +// that uses the service account in the default namespace +// which was created by terraform. +var _ = Describe("[awsmanaged] IRSA via referenced service account", Label("aws", "secretsmanager", "managed"), func() { + f := framework.New("eso-aws-managed") + prov := NewFromEnv(f) + + // nolint + DescribeTable("sync secretsmanager secrets", + framework.TableFunc(f, + prov), + framework.Compose(awscommon.WithReferencedIRSA, f, common.SimpleDataSync, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.NestedJSONWithGJSON, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.JSONDataFromSync, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.JSONDataWithProperty, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.JSONDataWithTemplate, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.DockerJSONConfig, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.DataPropertyDockerconfigJSON, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.SSHKeySync, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.SSHKeySyncDataProperty, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.SyncWithoutTargetName, awscommon.UseClusterSecretStore), + framework.Compose(awscommon.WithReferencedIRSA, f, common.JSONDataWithoutTargetName, awscommon.UseClusterSecretStore), + ) +}) + +// here we create a central eso instance in the default namespace +// that mounts the service account which was created by terraform. +var _ = Describe("[awsmanaged] with mounted IRSA", Label("aws", "secretsmanager", "managed"), func() { + f := framework.New("eso-aws-managed") + prov := NewFromEnv(f) + + // each test case gets its own ESO instance + BeforeEach(func() { + f.Install(addon.NewESO( + addon.WithControllerClass(f.BaseName), + addon.WithServiceAccount(prov.ServiceAccountName), + addon.WithReleaseName(f.Namespace.Name), + addon.WithNamespace("default"), + )) + }) + + // nolint + DescribeTable("sync secretsmanager secrets", + framework.TableFunc(f, + prov), + framework.Compose(awscommon.WithMountedIRSA, f, common.SimpleDataSync, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.NestedJSONWithGJSON, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.JSONDataFromSync, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.JSONDataWithProperty, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.JSONDataWithTemplate, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.DockerJSONConfig, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.DataPropertyDockerconfigJSON, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.SSHKeySync, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.SSHKeySyncDataProperty, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.SyncWithoutTargetName, awscommon.UseMountedIRSAStore), + framework.Compose(awscommon.WithMountedIRSA, f, common.JSONDataWithoutTargetName, awscommon.UseMountedIRSAStore), + ) +}) diff --git a/e2e/suite/aws/secretsmanager_managed.go b/e2e/suite/aws/secretsmanager_managed.go deleted file mode 100644 index 6d83d3d73..000000000 --- a/e2e/suite/aws/secretsmanager_managed.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package aws - -import ( - - // nolint - . "github.com/onsi/ginkgo/v2" - - esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1" - "github.com/external-secrets/external-secrets/e2e/framework" - "github.com/external-secrets/external-secrets/e2e/framework/addon" - "github.com/external-secrets/external-secrets/e2e/suite/common" -) - -const ( - withReferencedIRSA = "with referenced IRSA" - withMountedIRSA = "with mounted IRSA" -) - -// here we use the global eso instance -// that uses the service account in the default namespace -// which was created by terraform. -var _ = Describe("[awsmanaged] IRSA via referenced service account", Label("aws", "secretsmanager", "managed"), func() { - f := framework.New("eso-aws-managed") - prov := NewFromEnv(f) - - DescribeTable("sync secrets", - framework.TableFunc(f, - prov), - framework.Compose(withReferencedIRSA, f, common.SimpleDataSync, useClusterSecretStore(prov)), - framework.Compose(withReferencedIRSA, f, common.NestedJSONWithGJSON, useClusterSecretStore(prov)), - framework.Compose(withReferencedIRSA, f, common.JSONDataFromSync, useClusterSecretStore(prov)), - framework.Compose(withReferencedIRSA, f, common.JSONDataWithProperty, useClusterSecretStore(prov)), - framework.Compose(withReferencedIRSA, f, common.JSONDataWithTemplate, useClusterSecretStore(prov)), - framework.Compose(withReferencedIRSA, f, common.DockerJSONConfig, useClusterSecretStore(prov)), - framework.Compose(withReferencedIRSA, f, common.DataPropertyDockerconfigJSON, useClusterSecretStore(prov)), - framework.Compose(withReferencedIRSA, f, common.SSHKeySync, useClusterSecretStore(prov)), - framework.Compose(withReferencedIRSA, f, common.SSHKeySyncDataProperty, useClusterSecretStore(prov)), - framework.Compose(withReferencedIRSA, f, common.SyncWithoutTargetName, useClusterSecretStore(prov)), - framework.Compose(withReferencedIRSA, f, common.JSONDataWithoutTargetName, useClusterSecretStore(prov)), - ) -}) - -// here we create a central eso instance in the default namespace -// that mounts the service account which was created by terraform. -var _ = Describe("[awsmanaged] with mounted IRSA", Label("aws", "secretsmanager", "managed"), func() { - f := framework.New("eso-aws-managed") - prov := NewFromEnv(f) - - // each test case gets its own ESO instance - BeforeEach(func() { - f.Install(addon.NewESO( - addon.WithControllerClass(f.BaseName), - addon.WithServiceAccount(prov.ServiceAccountName), - addon.WithReleaseName(f.Namespace.Name), - addon.WithNamespace("default"), - )) - }) - - DescribeTable("sync secrets", - framework.TableFunc(f, - prov), - framework.Compose(withMountedIRSA, f, common.SimpleDataSync, useMountedIRSAStore(prov)), - framework.Compose(withMountedIRSA, f, common.NestedJSONWithGJSON, useMountedIRSAStore(prov)), - framework.Compose(withMountedIRSA, f, common.JSONDataFromSync, useMountedIRSAStore(prov)), - framework.Compose(withMountedIRSA, f, common.JSONDataWithProperty, useMountedIRSAStore(prov)), - framework.Compose(withMountedIRSA, f, common.JSONDataWithTemplate, useMountedIRSAStore(prov)), - framework.Compose(withMountedIRSA, f, common.DockerJSONConfig, useMountedIRSAStore(prov)), - framework.Compose(withMountedIRSA, f, common.DataPropertyDockerconfigJSON, useMountedIRSAStore(prov)), - framework.Compose(withMountedIRSA, f, common.SSHKeySync, useMountedIRSAStore(prov)), - framework.Compose(withMountedIRSA, f, common.SSHKeySyncDataProperty, useMountedIRSAStore(prov)), - framework.Compose(withMountedIRSA, f, common.SyncWithoutTargetName, useMountedIRSAStore(prov)), - framework.Compose(withMountedIRSA, f, common.JSONDataWithoutTargetName, useMountedIRSAStore(prov)), - ) -}) - -func useClusterSecretStore(prov *SMProvider) func(*framework.TestCase) { - return func(tc *framework.TestCase) { - tc.ExternalSecret.Spec.SecretStoreRef.Kind = esv1alpha1.ClusterSecretStoreKind - tc.ExternalSecret.Spec.SecretStoreRef.Name = prov.ReferencedIRSAStoreName() - } -} - -func useMountedIRSAStore(prov *SMProvider) func(*framework.TestCase) { - return func(tc *framework.TestCase) { - tc.ExternalSecret.Spec.SecretStoreRef.Kind = esv1alpha1.SecretStoreKind - tc.ExternalSecret.Spec.SecretStoreRef.Name = prov.MountedIRSAStoreName() - } -} diff --git a/e2e/suite/azure/azure_cert.go b/e2e/suite/azure/azure_cert.go new file mode 100644 index 000000000..46ec353d1 --- /dev/null +++ b/e2e/suite/azure/azure_cert.go @@ -0,0 +1,66 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +limitations under the License. +*/ +package azure + +import ( + "fmt" + + // nolint + . "github.com/onsi/ginkgo/v2" + v1 "k8s.io/api/core/v1" + + // nolint + esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1" + "github.com/external-secrets/external-secrets/e2e/framework" +) + +// azure keyvault type=cert should get a certificate from the api. +var _ = Describe("[azure]", Label("azure", "keyvault", "cert"), func() { + f := framework.New("eso-azure-certtype") + prov := newFromEnv(f) + var certBytes []byte + var certName string + + BeforeEach(func() { + certName = fmt.Sprintf("%s-%s", f.Namespace.Name, "certtest") + prov.CreateCertificate(certName) + certBytes = prov.GetCertificate(certName) + }) + + AfterEach(func() { + prov.DeleteCertificate(certName) + }) + + ff := framework.TableFunc(f, prov) + It("should sync keyvault objects with type=cert", func() { + ff(func(tc *framework.TestCase) { + secretKey := "azkv-cert" + + tc.ExpectedSecret = &v1.Secret{ + Type: v1.SecretTypeOpaque, + Data: map[string][]byte{ + secretKey: certBytes, + }, + } + tc.ExternalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{ + { + SecretKey: secretKey, + RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{ + Key: "cert/" + certName, + }, + }, + } + }) + }) + +}) diff --git a/e2e/suite/azure/azure_key.go b/e2e/suite/azure/azure_key.go new file mode 100644 index 000000000..d9da2b501 --- /dev/null +++ b/e2e/suite/azure/azure_key.go @@ -0,0 +1,69 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +limitations under the License. +*/ +package azure + +import ( + "encoding/json" + "fmt" + + "github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault" + + // nolint + . "github.com/onsi/ginkgo/v2" + v1 "k8s.io/api/core/v1" + + esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1" + "github.com/external-secrets/external-secrets/e2e/framework" +) + +// azure keyvault type=key should retrieve a jwk from the api. +var _ = Describe("[azure]", Label("azure", "keyvault", "key"), func() { + f := framework.New("eso-azure-keytype") + prov := newFromEnv(f) + var jwk *keyvault.JSONWebKey + var keyName string + + BeforeEach(func() { + keyName = fmt.Sprintf("%s-%s", f.Namespace.Name, "keytest") + jwk = prov.CreateKey(keyName) + }) + + AfterEach(func() { + prov.DeleteKey(keyName) + }) + + ff := framework.TableFunc(f, prov) + + It("should sync keyvault objects with type=key", func() { + ff(func(tc *framework.TestCase) { + secretKey := "azkv-key" + keyBytes, _ := json.Marshal(jwk) + + tc.ExpectedSecret = &v1.Secret{ + Type: v1.SecretTypeOpaque, + Data: map[string][]byte{ + secretKey: keyBytes, + }, + } + tc.ExternalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{ + { + SecretKey: secretKey, + RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{ + Key: "key/" + keyName, + }, + }, + } + }) + }) + +}) diff --git a/e2e/suite/azure/azure.go b/e2e/suite/azure/azure_secret.go similarity index 89% rename from e2e/suite/azure/azure.go rename to e2e/suite/azure/azure_secret.go index 79a96aef5..818ecb390 100644 --- a/e2e/suite/azure/azure.go +++ b/e2e/suite/azure/azure_secret.go @@ -21,7 +21,8 @@ import ( "github.com/external-secrets/external-secrets/e2e/suite/common" ) -var _ = Describe("[azure]", Label("azure", "keyvault"), func() { +// keyvault type=secret should behave just like any other secret store. +var _ = Describe("[azure]", Label("azure", "keyvault", "secret"), func() { f := framework.New("eso-azure") prov := newFromEnv(f) diff --git a/e2e/suite/azure/provider.go b/e2e/suite/azure/provider.go index 7354176db..b035bc64e 100644 --- a/e2e/suite/azure/provider.go +++ b/e2e/suite/azure/provider.go @@ -15,16 +15,15 @@ package azure import ( "context" "os" + "time" "github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault" kvauth "github.com/Azure/go-autorest/autorest/azure/auth" - // nolint - . "github.com/onsi/gomega" - // nolint . "github.com/onsi/ginkgo/v2" - + // nolint + . "github.com/onsi/gomega" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilpointer "k8s.io/utils/pointer" @@ -100,6 +99,83 @@ func (s *azureProvider) DeleteSecret(key string) { Expect(err).ToNot(HaveOccurred()) } +func (s *azureProvider) CreateKey(key string) *keyvault.JSONWebKey { + out, err := s.client.CreateKey( + context.Background(), + s.vaultURL, + key, + keyvault.KeyCreateParameters{ + Kty: keyvault.RSA, + KeyAttributes: &keyvault.KeyAttributes{ + RecoveryLevel: keyvault.Purgeable, + Enabled: utilpointer.BoolPtr(true), + }, + }, + ) + Expect(err).ToNot(HaveOccurred()) + return out.Key +} + +func (s *azureProvider) DeleteKey(key string) { + _, err := s.client.DeleteKey(context.Background(), s.vaultURL, key) + Expect(err).ToNot(HaveOccurred()) +} + +func (s *azureProvider) CreateCertificate(key string) { + _, err := s.client.CreateCertificate( + context.Background(), + s.vaultURL, + key, + keyvault.CertificateCreateParameters{ + CertificatePolicy: &keyvault.CertificatePolicy{ + X509CertificateProperties: &keyvault.X509CertificateProperties{ + Subject: utilpointer.String("CN=e2e.test"), + ValidityInMonths: utilpointer.Int32(42), + }, + IssuerParameters: &keyvault.IssuerParameters{ + Name: utilpointer.String("Self"), + }, + Attributes: &keyvault.CertificateAttributes{ + RecoveryLevel: keyvault.Purgeable, + Enabled: utilpointer.BoolPtr(true), + }, + }, + CertificateAttributes: &keyvault.CertificateAttributes{ + RecoveryLevel: keyvault.Purgeable, + Enabled: utilpointer.BoolPtr(true), + }, + }, + ) + Expect(err).ToNot(HaveOccurred()) +} + +func (s *azureProvider) GetCertificate(key string) []byte { + attempts := 20 + for { + out, err := s.client.GetCertificate( + context.Background(), + s.vaultURL, + key, + "", + ) + Expect(err).ToNot(HaveOccurred()) + if out.Cer != nil { + return *out.Cer + } + + attempts-- + if attempts <= 0 { + Fail("failed fetching azkv certificate") + } + <-time.After(time.Second * 5) + } +} + +func (s *azureProvider) DeleteCertificate(key string) { + _, err := s.client.DeleteCertificate(context.Background(), s.vaultURL, key) + Expect(err).ToNot(HaveOccurred()) +} + func (s *azureProvider) CreateSecretStore() { azureCreds := &v1.Secret{ ObjectMeta: metav1.ObjectMeta{ diff --git a/e2e/suite/import.go b/e2e/suite/import.go index fd9c332f8..6e5d5fdb6 100644 --- a/e2e/suite/import.go +++ b/e2e/suite/import.go @@ -16,7 +16,8 @@ package suite import ( // import different e2e test suites. - _ "github.com/external-secrets/external-secrets/e2e/suite/aws" + _ "github.com/external-secrets/external-secrets/e2e/suite/aws/parameterstore" + _ "github.com/external-secrets/external-secrets/e2e/suite/aws/secretsmanager" _ "github.com/external-secrets/external-secrets/e2e/suite/azure" _ "github.com/external-secrets/external-secrets/e2e/suite/gcp" _ "github.com/external-secrets/external-secrets/e2e/suite/vault" diff --git a/go.mod b/go.mod index 9845e864f..f3239e312 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ replace ( ) require ( - cloud.google.com/go v0.99.0 + cloud.google.com/go v0.100.2 // indirect cloud.google.com/go/secretmanager v1.0.0 github.com/Azure/azure-sdk-for-go v61.1.0+incompatible github.com/Azure/go-autorest/autorest/azure/auth v0.5.7 @@ -45,33 +45,33 @@ require ( github.com/PaesslerAG/jsonpath v0.1.1 github.com/ahmetb/gen-crd-api-reference-docs v0.3.0 github.com/akeylesslabs/akeyless-go-cloud-id v0.3.2 - github.com/akeylesslabs/akeyless-go/v2 v2.15.24 - github.com/aliyun/alibaba-cloud-sdk-go v1.61.1458 + github.com/akeylesslabs/akeyless-go/v2 v2.15.25 + github.com/aliyun/alibaba-cloud-sdk-go v1.61.1473 github.com/aws/aws-sdk-go v1.38.6 github.com/crossplane/crossplane-runtime v0.15.1 github.com/go-logr/logr v1.2.2 github.com/golang-jwt/jwt/v4 v4.2.0 github.com/google/go-cmp v0.5.7 github.com/google/uuid v1.2.0 - github.com/googleapis/gax-go v1.0.3 + github.com/googleapis/gax-go/v2 v2.1.1 github.com/hashicorp/vault/api v1.3.1 github.com/huandu/xstrings v1.3.2 // indirect github.com/lestrrat-go/jwx v1.2.1 - github.com/onsi/ginkgo/v2 v2.0.0 + github.com/onsi/ginkgo/v2 v2.1.1 github.com/onsi/gomega v1.17.0 github.com/oracle/oci-go-sdk/v45 v45.2.0 github.com/prometheus/client_golang v1.11.0 github.com/prometheus/client_model v0.2.0 github.com/stretchr/testify v1.7.0 github.com/tidwall/gjson v1.12.1 - github.com/xanzy/go-gitlab v0.50.1 + github.com/xanzy/go-gitlab v0.54.3 github.com/yandex-cloud/go-genproto v0.0.0-20210809082946-a97da516c588 github.com/yandex-cloud/go-sdk v0.0.0-20210809100642-c13c40a429fa github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a go.uber.org/zap v1.20.0 golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 - google.golang.org/api v0.61.0 + google.golang.org/api v0.64.0 google.golang.org/genproto v0.0.0-20220118154757-00ab72f36ad5 google.golang.org/grpc v1.43.0 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b @@ -85,7 +85,10 @@ require ( software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78 ) +require cloud.google.com/go/iam v0.1.1 + require ( + cloud.google.com/go/compute v0.1.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.18 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect @@ -95,7 +98,6 @@ require ( github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/BurntSushi/toml v0.3.1 // indirect github.com/PaesslerAG/gval v1.0.0 // indirect github.com/armon/go-metrics v0.3.10 // indirect github.com/armon/go-radix v1.0.0 // indirect @@ -103,16 +105,11 @@ require ( github.com/aws/aws-sdk-go-v2 v0.23.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect - github.com/census-instrumentation/opencensus-proto v0.2.1 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect - github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect - github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect github.com/dimchansky/utfbom v1.1.1 // indirect - github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021 // indirect - github.com/envoyproxy/protoc-gen-validate v0.1.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/fatih/color v1.13.0 // indirect github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect @@ -131,10 +128,9 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/go-querystring v1.0.0 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect - github.com/googleapis/gax-go/v2 v2.1.1 // indirect github.com/googleapis/gnostic v0.5.5 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -194,8 +190,6 @@ require ( go.opencensus.io v0.23.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect - golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5 // indirect - golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect golang.org/x/mod v0.4.2 // indirect golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d // indirect golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect @@ -212,7 +206,6 @@ require ( gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - honnef.co/go/tools v0.1.4 // indirect k8s.io/apiextensions-apiserver v0.23.0 // indirect k8s.io/component-base v0.23.0 // indirect k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c // indirect diff --git a/go.sum b/go.sum index e13b10d47..0da8eafef 100644 --- a/go.sum +++ b/go.sum @@ -25,17 +25,23 @@ cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aD cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0 h1:y/cM2iqGgGi5D5DQZl6D9STN/3dR/Vx5Mp8s752oJTY= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= +cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v0.1.0 h1:rSUBvAyVwNJ5uQCKNJFMwPtTvJkfN38b6Pvb9zZoqJ8= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/iam v0.1.1 h1:4CapQyNFjiksks1/x7jsvsygFPhihslYk5GptIrlX68= +cloud.google.com/go/iam v0.1.1/go.mod h1:CKqrcnI/suGpybEHxZ7BMehL0oA4LpdyJdUlTl9jVMw= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -78,7 +84,6 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= @@ -107,15 +112,15 @@ github.com/ahmetb/gen-crd-api-reference-docs v0.3.0 h1:+XfOU14S4bGuwyvCijJwhhBIj github.com/ahmetb/gen-crd-api-reference-docs v0.3.0/go.mod h1:TdjdkYhlOifCQWPs1UdTma97kQQMozf5h26hTuG70u8= github.com/akeylesslabs/akeyless-go-cloud-id v0.3.2 h1:1h4udX3Y5KgSG0m4Th2bHfaYxZB9fbngiij9PrKEp6c= github.com/akeylesslabs/akeyless-go-cloud-id v0.3.2/go.mod h1:ionnWiARf5TYoFGuHS7syh51/7lYosZejpZbnECFJcU= -github.com/akeylesslabs/akeyless-go/v2 v2.15.24 h1:q++f8n4l66kSptlo9cxrYq8fGHg25GdjqOsw0vbKUAE= -github.com/akeylesslabs/akeyless-go/v2 v2.15.24/go.mod h1:uOdXD49NCCe4rexeSc2aBU5Qv4KZgJE6YlbtYalvb+I= +github.com/akeylesslabs/akeyless-go/v2 v2.15.25 h1:9iztd2fXVfj4b3LRsyvGNRheFoz8IFs/+K4GaAshZc4= +github.com/akeylesslabs/akeyless-go/v2 v2.15.25/go.mod h1:uOdXD49NCCe4rexeSc2aBU5Qv4KZgJE6YlbtYalvb+I= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/aliyun/alibaba-cloud-sdk-go v1.61.1458 h1:pMdm+s6k9yeAYJNqgZIpZcDBuh2SNR3Q137G9rpxDZc= -github.com/aliyun/alibaba-cloud-sdk-go v1.61.1458/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU= +github.com/aliyun/alibaba-cloud-sdk-go v1.61.1473 h1:rUoiu7Duq0hr4mjlQWZMORKaCbNXaYvYN2HFJQt228E= +github.com/aliyun/alibaba-cloud-sdk-go v1.61.1473/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -152,7 +157,6 @@ github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9cop github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= -github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= @@ -170,12 +174,10 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 h1:hzAQntlaYRkVSFEfj9OTWlVV1H155FMD8BTKktLv0QI= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 h1:zH8ljVhhq7yC0MIeUL/IviMtY8hx2mK8cN9wEYb8ggw= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= @@ -222,9 +224,7 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021 h1:fP+fF0up6oPY49OrjPrhIJ8yQfdIM85NXMLkMg1EXVs= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -340,7 +340,6 @@ github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -391,8 +390,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -422,9 +421,6 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go v1.0.3 h1:9dMLqhaibYONnDRcnHdUs9P8Mw64jLlZTYlDe3leBtQ= -github.com/googleapis/gax-go v1.0.3/go.mod h1:QyXYajJFdARxGzjwUfbDFIse7Spkw81SJ4LrBJXtlQ8= -github.com/googleapis/gax-go/v2 v2.0.2/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -682,8 +678,8 @@ github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvw github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.0.0 h1:CcuG/HvWNkkaqCUpJifQY8z7qEMBJya6aLPx6ftGyjQ= -github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.1 h1:LCnPB85AvFNr91s0B2aDzEiiIg6MUwLYbryC1NSlWi8= +github.com/onsi/ginkgo/v2 v2.1.1/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= @@ -814,8 +810,8 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1 github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= -github.com/xanzy/go-gitlab v0.50.1 h1:eH1G0/ZV1j81rhGrtbcePjbM5Ern7mPA4Xjt+yE+2PQ= -github.com/xanzy/go-gitlab v0.50.1/go.mod h1:Q+hQhV508bDPoBijv7YjK/Lvlb4PhVhJdKqXVQrUoAE= +github.com/xanzy/go-gitlab v0.54.3 h1:fPfZ3Jcu5dPc3xyIYtAALZsEgoyKNFNuULD+TdJ7Zvk= +github.com/xanzy/go-gitlab v0.54.3/go.mod h1:F0QEXwmqiBUxCgJm8fE9S+1veX4XC9Z4cfaAbqwk4YM= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= @@ -904,7 +900,6 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI= golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190221220918-438050ddec5e/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= @@ -914,11 +909,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5 h1:FR+oGxGfbQu1d+jglI3rCkjAjUnhRSZcUxr+DqlDLNo= -golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -930,7 +922,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -1109,6 +1100,8 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -1135,7 +1128,6 @@ golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1243,8 +1235,10 @@ google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6 google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0 h1:TXXKS1slM3b2bZNJwD5DV/Tp6/M2cLzLOLh9PjDhrw8= google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.64.0 h1:l3pi8ncrQgB9+ncFw3A716L8lWujnXniBYbxWqqy6tE= +google.golang.org/api v0.64.0/go.mod h1:931CdxA8Rm4t6zqTFGSsgwbAEZ2+GMYurbndwSimebM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1319,10 +1313,13 @@ google.golang.org/genproto v0.0.0-20210921142501-181ce0d877f6/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220118154757-00ab72f36ad5 h1:zzNejm+EgrbLfDZ6lu9Uud2IVvHySPl8vQzf04laR5Q= google.golang.org/genproto v0.0.0-20220118154757-00ab72f36ad5/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1348,6 +1345,7 @@ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM= google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= @@ -1413,7 +1411,6 @@ gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919 h1:tmXTu+dfa+d9Evp8NpJdgOy6+rt8/x4yG7qPBrtNfLY= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1421,8 +1418,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.1.4 h1:SadWOkti5uVN1FAMgxn165+Mw00fuQKyk4Gyn/inxNQ= -honnef.co/go/tools v0.1.4/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= k8s.io/api v0.23.0 h1:WrL1gb73VSC8obi8cuYETJGXEoFNEh3LU0Pt+Sokgro= k8s.io/api v0.23.0/go.mod h1:8wmDdLBHBNxtOIytwLstXt5E9PddnZb0GaMcqsvDBpg= k8s.io/apiextensions-apiserver v0.23.0 h1:uii8BYmHYiT2ZTAJxmvc3X8UhNYMxl2A0z0Xq3Pm+WY= diff --git a/hack/api-docs/mkdocs.yml b/hack/api-docs/mkdocs.yml index 3490cd1b5..35413fac1 100644 --- a/hack/api-docs/mkdocs.yml +++ b/hack/api-docs/mkdocs.yml @@ -61,6 +61,7 @@ nav: - Oracle: - Oracle Vault: provider-oracle-vault.md - Webhook: provider-webhook.md + - Fake: provider-fake.md - References: - API specification: spec.md - Contributing: diff --git a/pkg/controllers/externalsecret/externalsecret_controller_template.go b/pkg/controllers/externalsecret/externalsecret_controller_template.go index b20e416e4..a3d9463d8 100644 --- a/pkg/controllers/externalsecret/externalsecret_controller_template.go +++ b/pkg/controllers/externalsecret/externalsecret_controller_template.go @@ -64,9 +64,7 @@ func (r *Reconciler) applyTemplate(ctx context.Context, es *esv1alpha1.ExternalS // if no data was provided by template fallback // to value from the provider if len(es.Spec.Target.Template.Data) == 0 { - for k, v := range dataMap { - secret.Data[k] = v - } + secret.Data = dataMap } secret.Annotations[esv1alpha1.AnnotationDataHash] = utils.ObjectHash(secret.Data) diff --git a/pkg/controllers/externalsecret/externalsecret_controller_test.go b/pkg/controllers/externalsecret/externalsecret_controller_test.go index 151d63569..64e9a6b60 100644 --- a/pkg/controllers/externalsecret/externalsecret_controller_test.go +++ b/pkg/controllers/externalsecret/externalsecret_controller_test.go @@ -31,8 +31,8 @@ import ( esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1" "github.com/external-secrets/external-secrets/pkg/provider" - "github.com/external-secrets/external-secrets/pkg/provider/fake" "github.com/external-secrets/external-secrets/pkg/provider/schema" + "github.com/external-secrets/external-secrets/pkg/provider/testing/fake" ) var ( @@ -654,6 +654,85 @@ var _ = Describe("ExternalSecret controller", func() { } } + // when a provider secret was deleted it must be deleted from + // the secret aswell + refreshSecretValueMap := func(tc *testCase) { + fakeProvider.WithGetSecretMap(map[string][]byte{ + "foo": []byte("1111"), + "bar": []byte("2222"), + }, nil) + tc.externalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{} + tc.externalSecret.Spec.DataFrom = []esv1alpha1.ExternalSecretDataRemoteRef{ + { + Key: remoteKey, + }, + } + tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second} + tc.checkSecret = func(es *esv1alpha1.ExternalSecret, secret *v1.Secret) { + // check values + Expect(string(secret.Data["foo"])).To(Equal("1111")) + Expect(string(secret.Data["bar"])).To(Equal("2222")) + + // update provider secret + sec := &v1.Secret{} + fakeProvider.WithGetSecretMap(map[string][]byte{ + "foo": []byte("1111"), + }, nil) + secretLookupKey := types.NamespacedName{ + Name: ExternalSecretTargetSecretName, + Namespace: ExternalSecretNamespace, + } + Eventually(func() bool { + err := k8sClient.Get(context.Background(), secretLookupKey, sec) + if err != nil { + return false + } + return string(sec.Data["foo"]) == "1111" && + sec.Data["bar"] == nil // must not be defined, it was deleted + }, timeout, interval).Should(BeTrue()) + } + } + + // when a provider secret was deleted it must be deleted from + // the secret aswell when using a template + refreshSecretValueMapTemplate := func(tc *testCase) { + fakeProvider.WithGetSecretMap(map[string][]byte{ + "foo": []byte("1111"), + "bar": []byte("2222"), + }, nil) + tc.externalSecret.Spec.Target.Template = &esv1alpha1.ExternalSecretTemplate{} + tc.externalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{} + tc.externalSecret.Spec.DataFrom = []esv1alpha1.ExternalSecretDataRemoteRef{ + { + Key: remoteKey, + }, + } + tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second} + tc.checkSecret = func(es *esv1alpha1.ExternalSecret, secret *v1.Secret) { + // check values + Expect(string(secret.Data["foo"])).To(Equal("1111")) + Expect(string(secret.Data["bar"])).To(Equal("2222")) + + // update provider secret + sec := &v1.Secret{} + fakeProvider.WithGetSecretMap(map[string][]byte{ + "foo": []byte("1111"), + }, nil) + secretLookupKey := types.NamespacedName{ + Name: ExternalSecretTargetSecretName, + Namespace: ExternalSecretNamespace, + } + Eventually(func() bool { + err := k8sClient.Get(context.Background(), secretLookupKey, sec) + if err != nil { + return false + } + return string(sec.Data["foo"]) == "1111" && + sec.Data["bar"] == nil // must not be defined, it was deleted + }, timeout, interval).Should(BeTrue()) + } + } + refreshintervalZero := func(tc *testCase) { const targetProp = "targetProperty" const secretVal = "someValue" @@ -971,6 +1050,8 @@ var _ = Describe("ExternalSecret controller", func() { Entry("should refresh secret from template", refreshWithTemplate), Entry("should be able to use only metadata from template", onlyMetadataFromTemplate), Entry("should refresh secret value when provider secret changes", refreshSecretValue), + Entry("should refresh secret map when provider secret changes", refreshSecretValueMap), + Entry("should refresh secret map when provider secret changes when using a template", refreshSecretValueMapTemplate), Entry("should not refresh secret value when provider secret changes but refreshInterval is zero", refreshintervalZero), Entry("should fetch secret using dataFrom", syncWithDataFrom), Entry("should fetch secret using dataFrom and a template", syncWithDataFromTemplate), diff --git a/pkg/controllers/externalsecret/metrics.go b/pkg/controllers/externalsecret/metrics.go index 75b69a306..721cb4b7e 100644 --- a/pkg/controllers/externalsecret/metrics.go +++ b/pkg/controllers/externalsecret/metrics.go @@ -16,6 +16,7 @@ package externalsecret import ( "github.com/prometheus/client_golang/prometheus" + v1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/metrics" esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1" @@ -50,6 +51,62 @@ var ( // updateExternalSecretCondition updates the ExternalSecret conditions. func updateExternalSecretCondition(es *esv1alpha1.ExternalSecret, condition *esv1alpha1.ExternalSecretStatusCondition, value float64) { + switch condition.Type { + case esv1alpha1.ExternalSecretDeleted: + // Remove condition=Ready metrics when the object gets deleted. + externalSecretCondition.Delete(prometheus.Labels{ + "name": es.Name, + "namespace": es.Namespace, + "condition": string(esv1alpha1.ExternalSecretReady), + "status": string(v1.ConditionFalse), + }) + externalSecretCondition.Delete(prometheus.Labels{ + "name": es.Name, + "namespace": es.Namespace, + "condition": string(esv1alpha1.ExternalSecretReady), + "status": string(v1.ConditionTrue), + }) + + case esv1alpha1.ExternalSecretReady: + // Remove condition=Deleted metrics when the object gets ready. + externalSecretCondition.Delete(prometheus.Labels{ + "name": es.Name, + "namespace": es.Namespace, + "condition": string(esv1alpha1.ExternalSecretDeleted), + "status": string(v1.ConditionFalse), + }) + externalSecretCondition.Delete(prometheus.Labels{ + "name": es.Name, + "namespace": es.Namespace, + "condition": string(esv1alpha1.ExternalSecretDeleted), + "status": string(v1.ConditionTrue), + }) + // Toggle opposite Status to 0 + switch condition.Status { + case v1.ConditionFalse: + externalSecretCondition.With(prometheus.Labels{ + "name": es.Name, + "namespace": es.Namespace, + "condition": string(esv1alpha1.ExternalSecretReady), + "status": string(v1.ConditionTrue), + }).Set(0) + case v1.ConditionTrue: + externalSecretCondition.With(prometheus.Labels{ + "name": es.Name, + "namespace": es.Namespace, + "condition": string(esv1alpha1.ExternalSecretReady), + "status": string(v1.ConditionFalse), + }).Set(0) + case v1.ConditionUnknown: + break + default: + break + } + + default: + break + } + externalSecretCondition.With(prometheus.Labels{ "name": es.Name, "namespace": es.Namespace, diff --git a/pkg/controllers/externalsecret/suite_test.go b/pkg/controllers/externalsecret/suite_test.go index 011bbdcb2..169347dd6 100644 --- a/pkg/controllers/externalsecret/suite_test.go +++ b/pkg/controllers/externalsecret/suite_test.go @@ -15,6 +15,7 @@ limitations under the License. package externalsecret import ( + "context" "path/filepath" "testing" "time" @@ -40,6 +41,7 @@ import ( var cfg *rest.Config var k8sClient client.Client var testEnv *envtest.Environment +var cancel context.CancelFunc func TestAPIs(t *testing.T) { RegisterFailHandler(Fail) @@ -56,6 +58,9 @@ var _ = BeforeSuite(func() { CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "deploy", "crds")}, } + var ctx context.Context + ctx, cancel = context.WithCancel(context.Background()) + var err error cfg, err = testEnv.Start() Expect(err).ToNot(HaveOccurred()) @@ -87,12 +92,13 @@ var _ = BeforeSuite(func() { go func() { defer GinkgoRecover() - Expect(k8sManager.Start(ctrl.SetupSignalHandler())).ToNot(HaveOccurred()) + Expect(k8sManager.Start(ctx)).ToNot(HaveOccurred()) }() }) var _ = AfterSuite(func() { By("tearing down the test environment") + cancel() // stop manager err := testEnv.Stop() Expect(err).ToNot(HaveOccurred()) }) diff --git a/pkg/provider/fake/fake.go b/pkg/provider/fake/fake.go index bbf66fd4b..384feeb7a 100644 --- a/pkg/provider/fake/fake.go +++ b/pkg/provider/fake/fake.go @@ -16,6 +16,7 @@ package fake import ( "context" + "fmt" "sigs.k8s.io/controller-runtime/pkg/client" @@ -24,80 +25,72 @@ import ( "github.com/external-secrets/external-secrets/pkg/provider/schema" ) -var _ provider.Provider = &Client{} +var ( + errNotFound = fmt.Errorf("secret value not found") + errMissingStore = fmt.Errorf("missing store provider") + errMissingFakeProvider = fmt.Errorf("missing store provider fake") +) -// Client is a fake client for testing. -type Client struct { - NewFn func(context.Context, esv1alpha1.GenericStore, client.Client, - string) (provider.SecretsClient, error) - GetSecretFn func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) - GetSecretMapFn func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) +type Provider struct { + config *esv1alpha1.FakeProvider } -// New returns a fake provider/client. -func New() *Client { - v := &Client{ - GetSecretFn: func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) { - return nil, nil - }, - GetSecretMapFn: func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) { - return nil, nil - }, - } - - v.NewFn = func(context.Context, esv1alpha1.GenericStore, client.Client, string) (provider.SecretsClient, error) { - return v, nil - } - - return v -} - -// RegisterAs registers the fake client in the schema. -func (v *Client) RegisterAs(provider *esv1alpha1.SecretStoreProvider) { - schema.ForceRegister(v, provider) -} - -// GetSecret implements the provider.Provider interface. -func (v *Client) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) { - return v.GetSecretFn(ctx, ref) -} - -// WithGetSecret wraps secret data returned by this provider. -func (v *Client) WithGetSecret(secData []byte, err error) *Client { - v.GetSecretFn = func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) { - return secData, err - } - return v -} - -// GetSecretMap imeplements the provider.Provider interface. -func (v *Client) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) { - return v.GetSecretMapFn(ctx, ref) -} -func (v *Client) Close(ctx context.Context) error { - return nil -} - -// WithGetSecretMap wraps the secret data map returned by this fake provider. -func (v *Client) WithGetSecretMap(secData map[string][]byte, err error) *Client { - v.GetSecretMapFn = func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) { - return secData, err - } - return v -} - -// WithNew wraps the fake provider factory function. -func (v *Client) WithNew(f func(context.Context, esv1alpha1.GenericStore, client.Client, - string) (provider.SecretsClient, error)) *Client { - v.NewFn = f - return v -} - -// NewClient returns a new fake provider. -func (v *Client) NewClient(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) { - c, err := v.NewFn(ctx, store, kube, namespace) +func (p *Provider) NewClient(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) { + cfg, err := getProvider(store) if err != nil { return nil, err } - return c, nil + return &Provider{ + config: cfg, + }, nil +} + +func getProvider(store esv1alpha1.GenericStore) (*esv1alpha1.FakeProvider, error) { + if store == nil { + return nil, errMissingStore + } + spc := store.GetSpec() + if spc == nil || spc.Provider == nil || spc.Provider.Fake == nil { + return nil, errMissingFakeProvider + } + return spc.Provider.Fake, nil +} + +// GetSecret returns a single secret from the provider. +func (p *Provider) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) { + for _, data := range p.config.Data { + if data.Key == ref.Key && data.Version == ref.Version { + return []byte(data.Value), nil + } + } + return nil, errNotFound +} + +// GetSecretMap returns multiple k/v pairs from the provider. +func (p *Provider) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) { + for _, data := range p.config.Data { + if data.Key != ref.Key || data.Version != ref.Version || data.ValueMap == nil { + continue + } + return convertMap(data.ValueMap), nil + } + return nil, errNotFound +} + +func convertMap(in map[string]string) map[string][]byte { + m := make(map[string][]byte) + for k, v := range in { + m[k] = []byte(v) + } + return m +} + +func (p *Provider) Close(ctx context.Context) error { + return nil +} + +func init() { + schema.Register(&Provider{}, &esv1alpha1.SecretStoreProvider{ + Fake: &esv1alpha1.FakeProvider{}, + }) } diff --git a/pkg/provider/fake/fake_test.go b/pkg/provider/fake/fake_test.go new file mode 100644 index 000000000..7a88af5d5 --- /dev/null +++ b/pkg/provider/fake/fake_test.go @@ -0,0 +1,194 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package fake + +import ( + "context" + "testing" + + "github.com/onsi/gomega" + + esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1" +) + +func TestNewClient(t *testing.T) { + p := &Provider{} + gomega.RegisterTestingT(t) + + // nil store + _, err := p.NewClient(context.Background(), nil, nil, "") + gomega.Expect(err).To(gomega.HaveOccurred()) + + // missing provider + _, err = p.NewClient(context.Background(), &esv1alpha1.SecretStore{}, nil, "") + gomega.Expect(err).To(gomega.HaveOccurred()) +} + +func TestClose(t *testing.T) { + p := &Provider{} + gomega.RegisterTestingT(t) + err := p.Close(context.TODO()) + gomega.Expect(err).ToNot(gomega.HaveOccurred()) +} + +type testCase struct { + name string + input []esv1alpha1.FakeProviderData + request esv1alpha1.ExternalSecretDataRemoteRef + expValue string + expErr string +} + +func TestGetSecret(t *testing.T) { + gomega.RegisterTestingT(t) + p := &Provider{} + tbl := []testCase{ + { + name: "return err when not found", + input: []esv1alpha1.FakeProviderData{}, + request: esv1alpha1.ExternalSecretDataRemoteRef{ + Key: "/foo", + Version: "v2", + }, + expErr: "secret value not found", + }, + { + name: "get correct value from multiple versions", + input: []esv1alpha1.FakeProviderData{ + { + Key: "/foo", + Value: "bar2", + Version: "v2", + }, + { + Key: "junk", + Value: "xxxxx", + }, + { + Key: "/foo", + Value: "bar1", + Version: "v1", + }, + }, + request: esv1alpha1.ExternalSecretDataRemoteRef{ + Key: "/foo", + Version: "v2", + }, + expValue: "bar2", + }, + } + + for _, row := range tbl { + t.Run(row.name, func(t *testing.T) { + cl, err := p.NewClient(context.Background(), &esv1alpha1.SecretStore{ + Spec: esv1alpha1.SecretStoreSpec{ + Provider: &esv1alpha1.SecretStoreProvider{ + Fake: &esv1alpha1.FakeProvider{ + Data: row.input, + }, + }, + }, + }, nil, "") + gomega.Expect(err).ToNot(gomega.HaveOccurred()) + out, err := cl.GetSecret(context.Background(), row.request) + if row.expErr != "" { + gomega.Expect(err).To(gomega.MatchError(row.expErr)) + } else { + gomega.Expect(err).ToNot(gomega.HaveOccurred()) + } + gomega.Expect(string(out)).To(gomega.Equal(row.expValue)) + }) + } +} + +type testMapCase struct { + name string + input []esv1alpha1.FakeProviderData + request esv1alpha1.ExternalSecretDataRemoteRef + expValue map[string][]byte + expErr string +} + +func TestGetSecretMap(t *testing.T) { + gomega.RegisterTestingT(t) + p := &Provider{} + tbl := []testMapCase{ + { + name: "return err when not found", + input: []esv1alpha1.FakeProviderData{}, + request: esv1alpha1.ExternalSecretDataRemoteRef{ + Key: "/foo", + Version: "v2", + }, + expErr: "secret value not found", + }, + { + name: "get correct value from multiple versions", + input: []esv1alpha1.FakeProviderData{ + { + Key: "junk", + ValueMap: map[string]string{ + "junk": "ok", + }, + }, + { + Key: "/foo", + ValueMap: map[string]string{ + "foo": "bar", + "baz": "bang", + }, + Version: "v1", + }, + { + Key: "/foo", + ValueMap: map[string]string{ + "foo": "bar", + "baz": "bang", + }, + Version: "v2", + }, + }, + request: esv1alpha1.ExternalSecretDataRemoteRef{ + Key: "/foo", + Version: "v2", + }, + expValue: map[string][]byte{ + "foo": []byte("bar"), + "baz": []byte("bang"), + }, + }, + } + + for _, row := range tbl { + t.Run(row.name, func(t *testing.T) { + cl, err := p.NewClient(context.Background(), &esv1alpha1.SecretStore{ + Spec: esv1alpha1.SecretStoreSpec{ + Provider: &esv1alpha1.SecretStoreProvider{ + Fake: &esv1alpha1.FakeProvider{ + Data: row.input, + }, + }, + }, + }, nil, "") + gomega.Expect(err).ToNot(gomega.HaveOccurred()) + out, err := cl.GetSecretMap(context.Background(), row.request) + if row.expErr != "" { + gomega.Expect(err).To(gomega.MatchError(row.expErr)) + } else { + gomega.Expect(err).ToNot(gomega.HaveOccurred()) + } + gomega.Expect(out).To(gomega.Equal(row.expValue)) + }) + } +} diff --git a/pkg/provider/gcp/secretmanager/fake/fake.go b/pkg/provider/gcp/secretmanager/fake/fake.go index ce1eb8695..0cb7c062d 100644 --- a/pkg/provider/gcp/secretmanager/fake/fake.go +++ b/pkg/provider/gcp/secretmanager/fake/fake.go @@ -19,7 +19,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - grpc "github.com/googleapis/gax-go" + grpc "github.com/googleapis/gax-go/v2" secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1" ) diff --git a/pkg/provider/gcp/secretmanager/secretsmanager.go b/pkg/provider/gcp/secretmanager/secretsmanager.go index 2bd2d14e9..bc46a1c5a 100644 --- a/pkg/provider/gcp/secretmanager/secretsmanager.go +++ b/pkg/provider/gcp/secretmanager/secretsmanager.go @@ -19,7 +19,7 @@ import ( "fmt" secretmanager "cloud.google.com/go/secretmanager/apiv1" - "github.com/googleapis/gax-go" + "github.com/googleapis/gax-go/v2" "github.com/tidwall/gjson" "golang.org/x/oauth2" "golang.org/x/oauth2/google" diff --git a/pkg/provider/gcp/secretmanager/secretsmanager_workload_identity.go b/pkg/provider/gcp/secretmanager/secretsmanager_workload_identity.go index 4aa92bc30..9604871da 100644 --- a/pkg/provider/gcp/secretmanager/secretsmanager_workload_identity.go +++ b/pkg/provider/gcp/secretmanager/secretsmanager_workload_identity.go @@ -24,7 +24,7 @@ import ( iam "cloud.google.com/go/iam/credentials/apiv1" secretmanager "cloud.google.com/go/secretmanager/apiv1" - "github.com/googleapis/gax-go" + "github.com/googleapis/gax-go/v2" "golang.org/x/oauth2" "google.golang.org/api/option" credentialspb "google.golang.org/genproto/googleapis/iam/credentials/v1" diff --git a/pkg/provider/gcp/secretmanager/secretsmanager_workload_identity_test.go b/pkg/provider/gcp/secretmanager/secretsmanager_workload_identity_test.go index d3f5f32bc..9409c9400 100644 --- a/pkg/provider/gcp/secretmanager/secretsmanager_workload_identity_test.go +++ b/pkg/provider/gcp/secretmanager/secretsmanager_workload_identity_test.go @@ -21,7 +21,7 @@ import ( "net/http/httptest" "testing" - "github.com/googleapis/gax-go" + "github.com/googleapis/gax-go/v2" "github.com/stretchr/testify/assert" "golang.org/x/oauth2" credentialspb "google.golang.org/genproto/googleapis/iam/credentials/v1" diff --git a/pkg/provider/register/register.go b/pkg/provider/register/register.go index 10760941a..be69679ae 100644 --- a/pkg/provider/register/register.go +++ b/pkg/provider/register/register.go @@ -21,6 +21,7 @@ import ( _ "github.com/external-secrets/external-secrets/pkg/provider/alibaba" _ "github.com/external-secrets/external-secrets/pkg/provider/aws" _ "github.com/external-secrets/external-secrets/pkg/provider/azure/keyvault" + _ "github.com/external-secrets/external-secrets/pkg/provider/fake" _ "github.com/external-secrets/external-secrets/pkg/provider/gcp/secretmanager" _ "github.com/external-secrets/external-secrets/pkg/provider/gitlab" _ "github.com/external-secrets/external-secrets/pkg/provider/ibm" diff --git a/pkg/provider/testing/fake/fake.go b/pkg/provider/testing/fake/fake.go new file mode 100644 index 000000000..bbf66fd4b --- /dev/null +++ b/pkg/provider/testing/fake/fake.go @@ -0,0 +1,103 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package fake + +import ( + "context" + + "sigs.k8s.io/controller-runtime/pkg/client" + + esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1" + "github.com/external-secrets/external-secrets/pkg/provider" + "github.com/external-secrets/external-secrets/pkg/provider/schema" +) + +var _ provider.Provider = &Client{} + +// Client is a fake client for testing. +type Client struct { + NewFn func(context.Context, esv1alpha1.GenericStore, client.Client, + string) (provider.SecretsClient, error) + GetSecretFn func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) + GetSecretMapFn func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) +} + +// New returns a fake provider/client. +func New() *Client { + v := &Client{ + GetSecretFn: func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) { + return nil, nil + }, + GetSecretMapFn: func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) { + return nil, nil + }, + } + + v.NewFn = func(context.Context, esv1alpha1.GenericStore, client.Client, string) (provider.SecretsClient, error) { + return v, nil + } + + return v +} + +// RegisterAs registers the fake client in the schema. +func (v *Client) RegisterAs(provider *esv1alpha1.SecretStoreProvider) { + schema.ForceRegister(v, provider) +} + +// GetSecret implements the provider.Provider interface. +func (v *Client) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) { + return v.GetSecretFn(ctx, ref) +} + +// WithGetSecret wraps secret data returned by this provider. +func (v *Client) WithGetSecret(secData []byte, err error) *Client { + v.GetSecretFn = func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) { + return secData, err + } + return v +} + +// GetSecretMap imeplements the provider.Provider interface. +func (v *Client) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) { + return v.GetSecretMapFn(ctx, ref) +} +func (v *Client) Close(ctx context.Context) error { + return nil +} + +// WithGetSecretMap wraps the secret data map returned by this fake provider. +func (v *Client) WithGetSecretMap(secData map[string][]byte, err error) *Client { + v.GetSecretMapFn = func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) { + return secData, err + } + return v +} + +// WithNew wraps the fake provider factory function. +func (v *Client) WithNew(f func(context.Context, esv1alpha1.GenericStore, client.Client, + string) (provider.SecretsClient, error)) *Client { + v.NewFn = f + return v +} + +// NewClient returns a new fake provider. +func (v *Client) NewClient(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) { + c, err := v.NewFn(ctx, store, kube, namespace) + if err != nil { + return nil, err + } + return c, nil +} diff --git a/pkg/provider/webhook/webhook_test.go b/pkg/provider/webhook/webhook_test.go index 98387dc70..8211d4069 100644 --- a/pkg/provider/webhook/webhook_test.go +++ b/pkg/provider/webhook/webhook_test.go @@ -298,7 +298,9 @@ func testGetSecret(tc testCase, t *testing.T, client provider.SecretsClient) { Key: tc.Args.Key, Version: tc.Args.Version, } - secret, err := client.GetSecret(context.Background(), testRef) + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + secret, err := client.GetSecret(ctx, testRef) errStr := "" if err != nil { errStr = err.Error() diff --git a/terraform/aws/modules/cluster/irsa.tf b/terraform/aws/modules/cluster/irsa.tf index ba4a256a3..a4783f523 100644 --- a/terraform/aws/modules/cluster/irsa.tf +++ b/terraform/aws/modules/cluster/irsa.tf @@ -37,6 +37,25 @@ resource "aws_iam_role" "eso-e2e-irsa" { "arn:aws:iam::aws:policy/SecretsManagerReadWrite" ] + inline_policy { + name = "aws_ssm_parameterstore" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = [ + "ssm:GetParameter", + "ssm:PutParameter", + ] + Effect = "Allow" + Resource = "*" + }, + ] + }) + } + + } resource "null_resource" "apply_sa" {