1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-14 11:57:59 +00:00

feat(e2e): implement aws tests, enhance gcp tests

Signed-off-by: Moritz Johner <beller.moritz@googlemail.com>
This commit is contained in:
Moritz Johner 2022-01-21 21:05:37 +01:00
parent 6aa5c80f74
commit 008268ee00
61 changed files with 1342 additions and 743 deletions

View file

@ -24,24 +24,62 @@ env:
GCP_KSA_NAME: ${{ secrets.GCP_KSA_NAME}} # Kubernetes Service Account
TF_VAR_GCP_GSA_NAME: ${{ secrets.GCP_GSA_NAME}} # Goolge Service Account for tf
TF_VAR_GCP_KSA_NAME: ${{ secrets.GCP_KSA_NAME}} # Kubernetes Service Account for tf
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_SA_NAME: ${{ secrets.AWS_SA_NAME }}
AWS_SA_NAMESPACE: ${{ secrets.AWS_SA_NAMESPACE }}
AWS_REGION: "eu-west-1"
AWS_CLUSTER_NAME: "eso-e2e-managed"
TF_VAR_AWS_SA_NAME: ${{ secrets.AWS_SA_NAME }}
TF_VAR_AWS_SA_NAMESPACE: ${{ secrets.AWS_SA_NAMESPACE }}
TF_VAR_AWS_REGION: "eu-west-1"
TF_VAR_AWS_CLUSTER_NAME: "eso-e2e-managed"
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID}}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET}}
TENANT_ID: ${{ secrets.TENANT_ID}}
VAULT_URL: ${{ secrets.VAULT_URL}}
IMAGE_REGISTRY: ghcr.io/external-secrets/external-secrets
E2E_IMAGE_REGISTRY: ghcr.io/external-secrets/external-secrets-e2e
E2E_VERSION: test
name: e2e tests
jobs:
# Repo owner has commented /ok-to-test-managed on a (fork-based) pull request
integration-fork-managed:
integration-managed:
runs-on: ubuntu-latest
if:
github.event_name == 'repository_dispatch'
if: github.event_name == 'repository_dispatch'
steps:
# set status=in_progress
- uses: actions/github-script@v1
id: update-check-run
env:
number: ${{ github.event.client_payload.slash_command.args.named.pull }}
job: ${{ github.job }}
conclusion: ${{ job.status }}
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
});
console.log("\n\nPR CHECKS: " + checks)
const check = checks.check_runs.filter(c => c.name === process.env.job);
console.log("\n\nPR Filtered CHECK: " + check)
console.log(check)
const { data: result } = await github.checks.update({
...context.repo,
check_run_id: check[0].id,
status: 'in_progress',
});
return result;
# Check out merge commit
- name: Fork based /ok-to-test-managed checkout
uses: actions/checkout@v2
@ -75,13 +113,7 @@ jobs:
path: ${{ steps.go.outputs.mod-cache }}
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-pkg-
- name: Setup gcloud CLI
uses: google-github-actions/setup-gcloud@master
with:
service_account_key: ${{ env.GCP_SM_SA_GKE_JSON }}
project_id: ${{ env.GCP_PROJECT_ID }}
- name: Setup TFLint
uses: terraform-linters/setup-tflint@v1
with:
@ -91,40 +123,52 @@ jobs:
run: find ${{ github.workspace }} | grep tf$ | xargs -n1 dirname | xargs -IXXX -n1 /bin/sh -c 'set -o errexit; cd XXX; pwd; tflint --loglevel=info .; cd - >/dev/null'
- name: Setup TF Gcloud Provider
if: github.event.client_payload.slash_command.args.named.provider == 'gcp'
run: |-
mkdir -p terraform/gcp/secrets
echo ${GCP_SM_SA_GKE_JSON} > terraform/gcp/secrets/gcloud-service-account-key.json
- name: Show TF GKE
- name: Show TF
run: |-
make tf.show.gcp
PROVIDER=${{github.event.client_payload.slash_command.args.named.provider}}
make tf.show.${PROVIDER}
- name: Setup Infracost
uses: infracost/actions/setup@v1
with:
api-key: ${{ secrets.INFRACOST_API_KEY }}
- name: Generate Infracost JSON for GKE
run: infracost breakdown --path terraform/gcp/plan.json --format json --out-file /tmp/infracost.json
- name: Generate Infracost JSON for AWS
run: infracost breakdown --path terraform/${{github.event.client_payload.slash_command.args.named.provider}}/plan.json --format json --out-file /tmp/infracost.json
- name: Post Infracost comment
uses: infracost/actions/comment@v1
with:
path: /tmp/infracost.json
# Choose the commenting behavior, 'update' is a good default:
behavior: update # Create a single comment and update it. The "quietest" option.
# behavior: delete-and-new # Delete previous comments and create a new one.
# behavior: hide-and-new # Minimize previous comments and create a new one.
# behavior: new # Create a new cost estimate comment on every push.
behavior: update
- name: Apply TF GKE
- name: Apply TF
run: |-
make tf.apply.gcp
PROVIDER=${{github.event.client_payload.slash_command.args.named.provider}}
make tf.apply.${PROVIDER}
- name: Setup gcloud CLI
if: github.event.client_payload.slash_command.args.named.provider == 'gcp'
uses: google-github-actions/setup-gcloud@master
with:
service_account_key: ${{ env.GCP_SM_SA_GKE_JSON }}
project_id: ${{ env.GCP_PROJECT_ID }}
- name: Get the GKE credentials
if: github.event.client_payload.slash_command.args.named.provider == 'gke'
run: |-
gcloud container clusters get-credentials "$GCP_GKE_CLUSTER" --zone "$GCP_GKE_ZONE" --project "$GCP_PROJECT_ID"
- name: Get the AWS credentials
if: github.event.client_payload.slash_command.args.named.provider == 'aws'
run: |-
aws --region $AWS_REGION eks update-kubeconfig --name $AWS_CLUSTER_NAME
- name: Login to Docker
uses: docker/login-action@v1
if: env.GHCR_USERNAME != ''
@ -133,25 +177,25 @@ jobs:
username: ${{ secrets.GHCR_USERNAME }}
password: ${{ secrets.GHCR_TOKEN }}
- name: Run e2e Tests for GCP
- name: Run managed e2e Tests
run: |
export E2E_VERSION=$GITHUB_SHA
export PR_IMG_TAG=$GITHUB_SHA
export PATH=$PATH:$(go env GOPATH)/bin
go get github.com/onsi/ginkgo/ginkgo
make test.e2e.managed FOCUS="gcpmanaged"
PROVIDER=${{github.event.client_payload.slash_command.args.named.provider}}
go get github.com/onsi/ginkgo/v2/ginkgo
make test.e2e.managed GINKGO_LABELS="${PROVIDER}"
- name: Destroy TF GKE
- name: Destroy TF
if: always()
run: |-
make tf.destroy.gcp
PROVIDER=${{github.event.client_payload.slash_command.args.named.provider}}
make tf.destroy.${PROVIDER}
# Update check run called "integration-fork"
# set status=completed
- uses: actions/github-script@v1
id: update-check-run
if: ${{ always() }}
env:
number: ${{ github.event.client_payload.pull_request.number }}
number: ${{ github.event.client_payload.slash_command.args.named.pull }}
job: ${{ github.job }}
# Conveniently, job.status maps to https://developer.github.com/v3/checks/runs/#update-a-check-run
conclusion: ${{ job.status }}

View file

@ -19,12 +19,12 @@ env:
GCP_GSA_NAME: ${{ secrets.GCP_GSA_NAME}} # Goolge Service Account
GCP_KSA_NAME: ${{ secrets.GCP_KSA_NAME}} # Kubernetes Service Account
GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID}}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID}}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET}}
TENANT_ID: ${{ secrets.TENANT_ID}}
VAULT_URL: ${{ secrets.VAULT_URL}}
E2E_IMAGE_REGISTRY: local/external-secrets-e2e
E2E_VERSION: test
name: e2e tests
@ -87,7 +87,7 @@ jobs:
BUILD_ARGS: "--load"
run: |
export PATH=$PATH:$(go env GOPATH)/bin
go get github.com/onsi/ginkgo/ginkgo
go get github.com/onsi/ginkgo/v2/ginkgo
make test.e2e
# Repo owner has commented /ok-to-test on a (fork-based) pull request
@ -150,7 +150,7 @@ jobs:
BUILD_ARGS: "--load"
run: |
export PATH=$PATH:$(go env GOPATH)/bin
go get github.com/onsi/ginkgo/ginkgo
go get github.com/onsi/ginkgo/v2/ginkgo
make test.e2e
# Update check run called "integration-fork"

View file

@ -30,6 +30,7 @@ jobs:
token: ${{ env.TOKEN }} # GitHub App installation access token
# token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} # PAT or OAuth token will also work
reaction-token: ${{ secrets.GITHUB_TOKEN }}
static-args: pull=${{ github.event.client_payload.pull_request.number }}
issue-type: pull-request
commands: ok-to-test-managed
permission: maintain

View file

@ -14,9 +14,7 @@ BUILD_ARGS ?=
all: $(addprefix build-,$(ARCH))
# Image registry for build/push image targets
IMAGE_REGISTRY ?= ghcr.io/external-secrets/external-secrets
PR_IMG_TAG ?=
export IMAGE_REGISTRY ?= ghcr.io/external-secrets/external-secrets
CRD_DIR ?= deploy/crds
@ -35,10 +33,10 @@ endif
# check if there are any existing `git tag` values
ifeq ($(shell git tag),)
# no tags found - default to initial tag `v0.0.0`
VERSION := $(shell echo "v0.0.0-$$(git rev-list HEAD --count)-g$$(git describe --dirty --always)" | sed 's/-/./2' | sed 's/-/./2')
export VERSION := $(shell echo "v0.0.0-$$(git rev-list HEAD --count)-g$$(git describe --dirty --always)" | sed 's/-/./2' | sed 's/-/./2')
else
# use tags
VERSION := $(shell git describe --dirty --always --tags --exclude 'helm*' | sed 's/-/./2' | sed 's/-/./2')
export VERSION := $(shell git describe --dirty --always --tags --exclude 'helm*' | sed 's/-/./2' | sed 's/-/./2')
endif
# ====================================================================================
@ -87,13 +85,13 @@ test: generate ## Run tests
test.e2e: generate ## Run e2e tests
@$(INFO) go test e2e-tests
$(MAKE) -C ./e2e test
@$(OK) go test unit-tests
@$(OK) go test e2e-tests
.PHONY: test.e2e.managed
test.e2e.managed: generate ## Run e2e tests
@$(INFO) go test e2e-tests
test.e2e.managed: generate ## Run e2e tests managed
@$(INFO) go test e2e-tests-managed
$(MAKE) -C ./e2e test.managed
@$(OK) go test unit-tests
@$(OK) go test e2e-tests-managed
.PHONY: build
build: $(addprefix build-,$(ARCH)) ## Build binary
@ -211,7 +209,7 @@ docker.push: ## Push the docker image to the registry
@docker push $(IMAGE_REGISTRY):$(VERSION)
@$(OK) docker push
# RELEASE_TAG is tag to promote. Default is promooting to main branch, but can be overriden
# RELEASE_TAG is tag to promote. Default is promoting to main branch, but can be overriden
# to promote a tag to a specific version.
RELEASE_TAG ?= main
SOURCE_TAG ?= $(VERSION)
@ -230,29 +228,27 @@ docker.promote: ## Promote the docker image to the registry
# ====================================================================================
# Terraform
tf.plan.gcp: ## Runs terrform plan for gcp provider bringing GKE up
@cd $(TF_DIR)/gcp; \
tf.plan.%: ## Runs terrform plan for a provider
@cd $(TF_DIR)/$*; \
terraform init; \
terraform plan -auto-approve
terraform plan
tf.apply.gcp: ## Runs terrform apply for gcp provider bringing GKE up
@cd $(TF_DIR)/gcp; \
tf.apply.%: ## Runs terrform apply for a provider
@cd $(TF_DIR)/$*; \
terraform init; \
terraform apply -auto-approve
tf.destroy.gcp: ## Runs terrform destroy for gcp provider bringing GKE down
@cd $(TF_DIR)/gcp; \
tf.destroy.%: ## Runs terrform destroy for a provider
@cd $(TF_DIR)/$*; \
terraform init; \
terraform destroy -auto-approve
tf.show.gcp: ## Runs terrform show for gcp and outputs to a file
@cd $(TF_DIR)/gcp; \
tf.show.%: ## Runs terrform show for a provider and outputs to a file
@cd $(TF_DIR)/$*; \
terraform init; \
terraform plan -out tfplan.binary; \
terraform show -json tfplan.binary > plan.json
# ====================================================================================
# Help

View file

@ -4,7 +4,7 @@ FROM golang:$GO_VERSION-buster as builder
ENV KUBECTL_VERSION="v1.21.2"
ENV HELM_VERSION="v3.7.1"
RUN go get -u github.com/onsi/ginkgo/ginkgo
RUN go get -u github.com/onsi/ginkgo/v2/ginkgo
RUN wget -q https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl -O /usr/local/bin/kubectl && \
chmod +x /usr/local/bin/kubectl && \
wget -q https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz -O - | tar -xzO linux-amd64/helm > /usr/local/bin/helm && \

View file

@ -2,15 +2,11 @@ MAKEFLAGS += --warn-undefined-variables
SHELL := /bin/bash
.SHELLFLAGS := -euo pipefail -c
IMG_TAG = test
IMG = local/external-secrets-e2e:$(IMG_TAG)
KIND_IMG = "kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6"
BUILD_ARGS ?=
IMAGE_REGISTRY ?=
export FOCUS := $(FOCUS)
KIND_IMG = "kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6"
BUILD_ARGS ?=
export E2E_IMAGE_REGISTRY ?=
export E2E_VERSION ?=
export E2E_IMAGE_REGISTRY ?= ghcr.io/external-secrets/external-secrets-e2e
export GINKGO_LABELS ?= !managed
start-kind: ## Start kind cluster
kind create cluster \
@ -21,49 +17,35 @@ start-kind: ## Start kind cluster
test: e2e-image ## Run e2e tests against current kube context
$(MAKE) -C ../ docker.build \
IMAGE_REGISTRY=local/external-secrets \
VERSION=$(IMG_TAG) \
IMAGE_REGISTRY=$(IMAGE_REGISTRY) \
VERSION=$(VERSION) \
ARCH=amd64 \
BUILD_ARGS="${BUILD_ARGS} --build-arg TARGETARCH=amd64 --build-arg TARGETOS=linux"
kind load docker-image --name="external-secrets" local/external-secrets:$(IMG_TAG)
kind load docker-image --name="external-secrets" $(IMG)
kind load docker-image --name="external-secrets" $(IMAGE_REGISTRY):$(VERSION)
kind load docker-image --name="external-secrets" $(E2E_IMAGE_REGISTRY):$(VERSION)
./run.sh
test.managed: e2e-remote-values e2e-image.managed ## Run e2e tests against current kube context
test.managed: e2e-image ## Run e2e tests against current kube context
$(MAKE) -C ../ docker.build \
VERSION=$(PR_IMG_TAG) \
VERSION=$(VERSION) \
ARCH=amd64 \
BUILD_ARGS="${BUILD_ARGS} --build-arg TARGETARCH=amd64 --build-arg TARGETOS=linux"
$(MAKE) -C ../ docker.push \
VERSION=$(PR_IMG_TAG)
VERSION=$(VERSION)
$(MAKE) -C ../ docker.push \
IMAGE_REGISTRY=$(E2E_IMAGE_REGISTRY) \
VERSION=$(E2E_VERSION)
VERSION=$(VERSION)
./run.sh
e2e-remote-values:
sed -i "s|repository: [^ ]*|repository: $(IMAGE_REGISTRY)|g" k8s/eso.values.yaml
sed -i "s|tag: [^ ]*|tag: $(PR_IMG_TAG)|g" k8s/eso.values.yaml
sed -i "s|repository: [^ ]*|repository: $(IMAGE_REGISTRY)|g" k8s/eso.scoped.values.yaml
sed -i "s|tag: [^ ]*|tag: $(PR_IMG_TAG)|g" k8s/eso.scoped.values.yaml
e2e-bin:
CGO_ENABLED=0 go run github.com/onsi/ginkgo/ginkgo build .
CGO_ENABLED=0 go run github.com/onsi/ginkgo/v2/ginkgo build .
e2e-image: e2e-bin
-rm -rf ./k8s/deploy
mkdir -p k8s
$(MAKE) -C ../ helm.generate
cp -r ../deploy ./k8s
docker build $(BUILD_ARGS) -t $(IMG) .
e2e-image.managed: e2e-bin
-rm -rf ./k8s/deploy
mkdir -p k8s
$(MAKE) -C ../ helm.generate
cp -r ../deploy ./k8s
docker build $(BUILD_ARGS) -t ghcr.io/external-secrets/external-secrets-e2e:$(E2E_VERSION) .
docker build $(BUILD_ARGS) -t $(E2E_IMAGE_REGISTRY):$(VERSION) .
stop-kind: ## Stop kind cluster
kind delete cluster \

View file

@ -17,7 +17,7 @@ import (
"testing"
// nolint
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
// nolint
. "github.com/onsi/gomega"
@ -30,25 +30,16 @@ var _ = SynchronizedBeforeSuite(func() []byte {
cfg := &addon.Config{}
cfg.KubeConfig, cfg.KubeClientSet, cfg.CRClient = util.NewConfig()
By("installing localstack")
addon.InstallGlobalAddon(addon.NewLocalstack(), cfg)
By("waiting for localstack")
err := util.WaitForURL("http://localstack.default/health")
Expect(err).ToNot(HaveOccurred())
By("installing eso")
addon.InstallGlobalAddon(addon.NewESO(), cfg)
By("installing scoped eso")
addon.InstallGlobalAddon(addon.NewScopedESO(), cfg)
return nil
}, func([]byte) {})
var _ = SynchronizedAfterSuite(func() {}, func() {
By("Cleaning up global addons")
addon.UninstallGlobalAddons()
if CurrentGinkgoTestDescription().Failed {
if CurrentSpecReport().Failed() {
addon.PrintLogs()
}
})
@ -56,5 +47,5 @@ var _ = SynchronizedAfterSuite(func() {}, func() {
func TestE2E(t *testing.T) {
NewWithT(t)
RegisterFailHandler(Fail)
RunSpecs(t, "external-secrets e2e suite")
RunSpecs(t, "external-secrets e2e suite", Label("e2e"))
}

View file

@ -19,8 +19,6 @@ set -euo pipefail
NC='\e[0m'
BGREEN='\e[32m'
SLOW_E2E_THRESHOLD=${SLOW_E2E_THRESHOLD:-50}
FOCUS=${FOCUS:-.*}
E2E_NODES=${E2E_NODES:-5}
if [ ! -f "${HOME}/.kube/config" ]; then
@ -31,13 +29,13 @@ if [ ! -f "${HOME}/.kube/config" ]; then
fi
ginkgo_args=(
"-randomizeSuites"
"-randomizeAllSpecs"
"-flakeAttempts=2"
"--randomize-suites"
"--randomize-all"
"--flake-attempts=2"
"-p"
"-progress"
"-trace"
"-slowSpecThreshold=${SLOW_E2E_THRESHOLD}"
"--slow-spec-threshold=5m"
"-r"
"-v"
"-timeout=45m"
@ -45,9 +43,8 @@ ginkgo_args=(
kubectl apply -f /k8s/deploy/crds
echo -e "${BGREEN}Running e2e test suite (FOCUS=${FOCUS})...${NC}"
echo -e "${BGREEN}Running e2e test suite (LABELS=${GINKGO_LABELS})...${NC}"
ACK_GINKGO_RC=true ginkgo "${ginkgo_args[@]}" \
-focus="${FOCUS}" \
-skip="\[Serial\]|\[MemoryLeak\]" \
-label-filter="${GINKGO_LABELS}" \
-nodes="${E2E_NODES}" \
/e2e.test

View file

@ -14,7 +14,7 @@ limitations under the License.
package addon
import (
"github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/v2"
"github.com/onsi/gomega"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"

View file

@ -63,7 +63,7 @@ func (c *HelmChart) Install() error {
args := []string{"install", c.ReleaseName, c.Chart,
"--wait",
"--timeout", "600s",
"--timeout", "120s",
"--namespace", c.Namespace,
}

View file

@ -14,92 +14,99 @@ limitations under the License.
package addon
import (
"fmt"
"os"
// nolint
. "github.com/onsi/ginkgo"
// nolint
. "github.com/onsi/gomega"
// nolint
"github.com/external-secrets/external-secrets/e2e/framework/util"
. "github.com/onsi/ginkgo/v2"
)
type ESO struct {
Addon
*HelmChart
}
func NewESO() *ESO {
return &ESO{
func NewESO(mutators ...MutationFunc) *ESO {
eso := &ESO{
&HelmChart{
Namespace: "default",
ReleaseName: "eso",
Chart: "/k8s/deploy/charts/external-secrets",
Values: []string{"/k8s/eso.values.yaml"},
Vars: []StringTuple{
{
Key: "image.repository",
Value: os.Getenv("IMAGE_REGISTRY"),
},
{
Key: "image.tag",
Value: os.Getenv("VERSION"),
},
{
Key: "installCRDs",
Value: "false",
},
},
},
}
for _, f := range mutators {
f(eso)
}
return eso
}
type MutationFunc func(eso *ESO)
func WithReleaseName(name string) MutationFunc {
return func(eso *ESO) {
eso.HelmChart.ReleaseName = name
}
}
func WithNamespace(namespace string) MutationFunc {
return func(eso *ESO) {
eso.HelmChart.Namespace = namespace
}
}
func WithNamespaceScope(namespace string) MutationFunc {
return func(eso *ESO) {
eso.HelmChart.Vars = append(eso.HelmChart.Vars, StringTuple{
Key: "scopedNamespace",
Value: namespace,
})
}
}
func WithServiceAccount(saName string) MutationFunc {
return func(eso *ESO) {
eso.HelmChart.Vars = append(eso.HelmChart.Vars, []StringTuple{
{
Key: "serviceAccount.create",
Value: "false",
},
{
Key: "serviceAccount.name",
Value: "eso-e2e-test",
},
}...)
}
}
func WithControllerClass(class string) MutationFunc {
return func(eso *ESO) {
eso.HelmChart.Vars = append(eso.HelmChart.Vars, StringTuple{
Key: "extraArgs.controller-class",
Value: class,
})
}
}
func (l *ESO) Install() error {
By("Installing eso\n")
err := l.Addon.Install()
if err != nil {
return err
}
By("afterInstall eso\n")
err = l.afterInstall()
err := l.HelmChart.Install()
if err != nil {
return err
}
return nil
}
func (l *ESO) afterInstall() error {
err := gcpPreparation()
Expect(err).NotTo(HaveOccurred())
err = awsPreparation()
Expect(err).NotTo(HaveOccurred())
if err != nil {
return err
}
return nil
}
func gcpPreparation() error {
gcpProjectID := os.Getenv("GCP_PROJECT_ID")
gcpGSAName := os.Getenv("GCP_GSA_NAME")
gcpKSAName := os.Getenv("GCP_KSA_NAME")
_, kubeClientSet, _ := util.NewConfig()
annotations := make(map[string]string)
annotations["iam.gke.io/gcp-service-account"] = fmt.Sprintf("%s@%s.iam.gserviceaccount.com", gcpGSAName, gcpProjectID)
_, err := util.UpdateKubeSA(gcpKSAName, kubeClientSet, "default", annotations)
Expect(err).NotTo(HaveOccurred())
_, err = util.UpdateKubeSA("external-secrets-e2e", kubeClientSet, "default", annotations)
Expect(err).NotTo(HaveOccurred())
if err != nil {
return err
}
return nil
}
func awsPreparation() error {
return nil
}
func NewScopedESO() *ESO {
return &ESO{
&HelmChart{
Namespace: "default",
ReleaseName: "eso-aws-sm",
Chart: "/k8s/deploy/charts/external-secrets",
Values: []string{"/k8s/eso.scoped.values.yaml"},
},
}
}

View file

@ -1,44 +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 addon
import "github.com/external-secrets/external-secrets/e2e/framework/util"
type Localstack struct {
Addon
}
func NewLocalstack() *Localstack {
return &Localstack{
&HelmChart{
Namespace: "default",
ReleaseName: "localstack",
Chart: "localstack-charts/localstack",
ChartVersion: "0.2.0",
Repo: ChartRepo{
Name: "localstack-charts",
URL: "https://localstack.github.io/helm-charts",
},
Values: []string{"/k8s/localstack.values.yaml"},
},
}
}
func (l *Localstack) Install() error {
err := l.Addon.Install()
if err != nil {
return err
}
return util.WaitForURL("http://localstack.default/health")
}

View file

@ -32,7 +32,7 @@ import (
vault "github.com/hashicorp/vault/api"
// nolint
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

View file

@ -33,7 +33,7 @@ import (
// with the provided values.
func (f *Framework) WaitForSecretValue(namespace, name string, expected *v1.Secret) (*v1.Secret, error) {
secret := &v1.Secret{}
err := wait.PollImmediate(time.Second*2, time.Minute*2, func() (bool, error) {
err := wait.PollImmediate(time.Second*5, time.Minute, func() (bool, error) {
err := f.CRClient.Get(context.Background(), types.NamespacedName{
Namespace: namespace,
Name: name,

View file

@ -16,12 +16,10 @@ package framework
import (
// nolint
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
// nolint
. "github.com/onsi/gomega"
// nolint
. "github.com/onsi/ginkgo/extensions/table"
api "k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes"
kscheme "k8s.io/client-go/kubernetes/scheme"
@ -30,6 +28,7 @@ import (
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
"github.com/external-secrets/external-secrets/e2e/framework/addon"
"github.com/external-secrets/external-secrets/e2e/framework/log"
"github.com/external-secrets/external-secrets/e2e/framework/util"
)
@ -72,11 +71,9 @@ func New(baseName string) *Framework {
// BeforeEach creates a namespace.
func (f *Framework) BeforeEach() {
var err error
By("Building a namespace api object")
f.Namespace, err = util.CreateKubeNamespace(f.BaseName, f.KubeClientSet)
Expect(err).NotTo(HaveOccurred())
By("Using the namespace " + f.Namespace.Name)
log.Logf("created test namespace %s", f.Namespace.Name)
Expect(err).ToNot(HaveOccurred())
}
// AfterEach deletes the namespace and cleans up the registered addons.
@ -87,7 +84,7 @@ func (f *Framework) AfterEach() {
}
// reset addons to default once the run is done
f.Addons = []addon.Addon{}
By("deleting test namespace")
log.Logf("deleting test namespace %s", f.Namespace.Name)
err := util.DeleteKubeNamespace(f.Namespace.Name, f.KubeClientSet)
Expect(err).NotTo(HaveOccurred())
}
@ -111,13 +108,13 @@ func (f *Framework) Install(a addon.Addon) {
func Compose(descAppend string, f *Framework, fn func(f *Framework) (string, func(*TestCase)), tweaks ...func(*TestCase)) TableEntry {
desc, tfn := fn(f)
tweaks = append(tweaks, tfn)
te := Entry(desc + " " + descAppend)
// need to convert []func to []interface{}
ifs := make([]interface{}, len(tweaks))
for i := 0; i < len(tweaks); i++ {
ifs[i] = tweaks[i]
}
te.Parameters = ifs
te := Entry(desc+" "+descAppend, ifs...)
return te
}

View file

@ -14,12 +14,10 @@ limitations under the License.
package log
import (
"fmt"
"github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/v2"
)
// Logf logs the format string to ginkgo stdout.
func Logf(format string, args ...interface{}) {
fmt.Fprintf(ginkgo.GinkgoWriter, format, args...)
ginkgo.GinkgoWriter.Printf(format, args)
}

View file

@ -22,9 +22,7 @@ import (
"time"
// nolint
. "github.com/onsi/ginkgo"
// nolint
. "github.com/onsi/gomega"
. "github.com/onsi/ginkgo/v2"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -242,19 +240,25 @@ func NewConfig() (*restclient.Config, *kubernetes.Clientset, crclient.Client) {
kcPath := os.Getenv("KUBECONFIG")
if kcPath != "" {
kubeConfig, err = clientcmd.BuildConfigFromFlags("", kcPath)
Expect(err).NotTo(HaveOccurred())
if err != nil {
Fail(err.Error())
}
} else {
kubeConfig, err = restclient.InClusterConfig()
Expect(err).NotTo(HaveOccurred())
if err != nil {
Fail(err.Error())
}
}
By("creating a kubernetes client")
kubeClientSet, err := kubernetes.NewForConfig(kubeConfig)
Expect(err).NotTo(HaveOccurred())
if err != nil {
Fail(err.Error())
}
By("creating a controller-runtime client")
CRClient, err := crclient.New(kubeConfig, crclient.Options{Scheme: Scheme})
Expect(err).NotTo(HaveOccurred())
if err != nil {
Fail(err.Error())
}
return kubeConfig, kubeClientSet, CRClient
}

View file

@ -1,12 +0,0 @@
installCRDs: false
image:
repository: local/external-secrets
tag: test
scopedNamespace: test
extraEnv:
- name: AWS_SECRETSMANAGER_ENDPOINT
value: "http://localstack.default"
- name: AWS_STS_ENDPOINT
value: "http://localstack.default"
- name: AWS_SSM_ENDPOINT
value: "http://localstack.default"

View file

@ -1,11 +0,0 @@
installCRDs: false
image:
repository: local/external-secrets
tag: test
extraEnv:
- name: AWS_SECRETSMANAGER_ENDPOINT
value: "http://localstack.default"
- name: AWS_STS_ENDPOINT
value: "http://localstack.default"
- name: AWS_SSM_ENDPOINT
value: "http://localstack.default"

View file

@ -42,20 +42,22 @@ done
kubectl apply -f ${DIR}/k8s/deploy/crds
echo -e "Starting the e2e test pod"
echo -e "Starting the e2e test pod ${E2E_IMAGE_REGISTRY}:${VERSION}"
kubectl run --rm \
--attach \
--restart=Never \
--pod-running-timeout=10m \
--env="FOCUS=${FOCUS:-.*}" \
--pod-running-timeout=5m \
--env="GINKGO_LABELS=${GINKGO_LABELS:-.*}" \
--env="GCP_SM_SA_JSON=${GCP_SM_SA_JSON:-}" \
--env="GCP_PROJECT_ID=${GCP_PROJECT_ID:-}" \
--env="TF_VAR_GCP_PROJECT_ID=${TF_VAR_GCP_PROJECT_ID:-}" \
--env="GCP_GSA_NAME=${GCP_GSA_NAME:-}" \
--env="GCP_KSA_NAME=${GCP_KSA_NAME:-}" \
--env="TF_VAR_GCP_GSA_NAME=${TF_VAR_GCP_GSA_NAME:-}" \
--env="TF_VAR_GCP_KSA_NAME=${TF_VAR_GCP_KSA_NAME:-}" \
--env="GCP_GKE_ZONE=${GCP_GKE_ZONE:-}" \
--env="GCP_GKE_CLUSTER=${GCP_GKE_CLUSTER:-}" \
--env="AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-}" \
--env="AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-}" \
--env="AWS_SA_NAME=${AWS_SA_NAME:-}" \
--env="AWS_SA_NAMESPACE=${AWS_SA_NAMESPACE:-}" \
--env="AZURE_CLIENT_ID=${AZURE_CLIENT_ID:-}" \
--env="AZURE_CLIENT_SECRET=${AZURE_CLIENT_SECRET:-}" \
--env="AKEYLESS_ACCESS_ID=${AKEYLESS_ACCESS_ID:-}" \
@ -70,5 +72,7 @@ kubectl run --rm \
--env="ORACLE_REGION=${ORACLE_REGION:-}" \
--env="ORACLE_FINGERPRINT=${ORACLE_FINGERPRINT:-}" \
--env="ORACLE_KEY=${ORACLE_KEY:-}" \
--env="IMAGE_REGISTRY=${IMAGE_REGISTRY}" \
--env="VERSION=${VERSION}" \
--overrides='{ "apiVersion": "v1", "spec":{"serviceAccountName": "external-secrets-e2e"}}' \
e2e --image=${E2E_IMAGE_REGISTRY}:${E2E_VERSION}
e2e --image=${E2E_IMAGE_REGISTRY}:${VERSION}

View file

@ -15,23 +15,19 @@ limitations under the License.
package akeyless
import (
"os"
// nolint
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
// nolint
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/ginkgo/v2/extensions/table"
"github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/suite/common"
)
var _ = Describe("[akeyless] ", func() {
var _ = Describe("[akeyless]", Label("akeyless"), func() {
f := framework.New("eso-akeyless")
accessID := os.Getenv("AKEYLESS_ACCESS_ID")
accessType := os.Getenv("AKEYLESS_ACCESS_TYPE")
accessTypeParam := os.Getenv("AKEYLESS_ACCESS_TYPE_PARAM")
prov := newAkeylessProvider(f, accessID, accessType, accessTypeParam)
prov := newFromEnv(f)
DescribeTable("sync secrets", framework.TableFunc(f, prov),
Entry(common.SimpleDataSync(f)),

View file

@ -29,7 +29,7 @@ import (
"github.com/akeylesslabs/akeyless-go/v2"
//nolint
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
//nolint
. "github.com/onsi/gomega"
@ -75,6 +75,13 @@ func newAkeylessProvider(f *framework.Framework, accessID, accessType, accessTyp
return prov
}
func newFromEnv(f *framework.Framework) *akeylessProvider {
accessID := os.Getenv("AKEYLESS_ACCESS_ID")
accessType := os.Getenv("AKEYLESS_ACCESS_TYPE")
accessTypeParam := os.Getenv("AKEYLESS_ACCESS_TYPE_PARAM")
return newAkeylessProvider(f, accessID, accessType, accessTypeParam)
}
// CreateSecret creates a secret.
func (a *akeylessProvider) CreateSecret(key, val string) {
token, err := a.GetToken()

View file

@ -15,27 +15,19 @@ limitations under the License.
package alibaba
import (
"os"
// nolint
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
// nolint
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/ginkgo/v2/extensions/table"
"github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/suite/common"
)
var _ = Describe("[alibaba] ", func() {
var _ = Describe("[alibaba]", Label("alibaba"), func() {
f := framework.New("eso-alibaba")
accessKeyID := os.Getenv("ACCESS_KEY_ID")
accessKeySecret := os.Getenv("ACCESS_KEY_SECRET")
regionID := os.Getenv("REGION_ID")
prov := &alibabaProvider{}
if accessKeyID != "" && accessKeySecret != "" && regionID != "" {
prov = newAlibabaProvider(f, accessKeyID, accessKeySecret, regionID)
}
prov := newFromEnv(f)
DescribeTable("sync secrets", framework.TableFunc(f, prov),
Entry(common.SimpleDataSync(f)),

View file

@ -16,11 +16,12 @@ package alibaba
import (
"context"
"os"
"github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
//nolint
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
//nolint
. "github.com/onsi/gomega"
@ -54,6 +55,13 @@ func newAlibabaProvider(f *framework.Framework, accessKeyID, accessKeySecret, re
return prov
}
func newFromEnv(f *framework.Framework) *alibabaProvider {
accessKeyID := os.Getenv("ACCESS_KEY_ID")
accessKeySecret := os.Getenv("ACCESS_KEY_SECRET")
regionID := os.Getenv("REGION_ID")
return newAlibabaProvider(f, accessKeyID, accessKeySecret, regionID)
}
// CreateSecret creates a secret in both kv v1 and v2 provider.
func (s *alibabaProvider) CreateSecret(key, val string) {
client, err := kms.NewClientWithAccessKey(s.regionID, s.accessKeyID, s.accessKeySecret)

View file

@ -16,6 +16,8 @@ package aws
import (
"context"
"os"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
@ -23,79 +25,194 @@ import (
"github.com/aws/aws-sdk-go/service/secretsmanager"
//nolint
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
// 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"
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
esmetav1 "github.com/external-secrets/external-secrets/apis/meta/v1"
"github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/pkg/provider/aws/auth"
"github.com/external-secrets/external-secrets/e2e/framework/log"
)
type SMProvider struct {
url string
ServiceAccountName string
ServiceAccountNamespace string
kid string
sak string
region string
client *secretsmanager.SecretsManager
framework *framework.Framework
}
const secretName = "provider-secret"
const (
staticCredentialsSecretName = "provider-secret"
)
func newSMProvider(f *framework.Framework, url string) *SMProvider {
func NewSMProvider(f *framework.Framework, kid, sak, region, saName, saNamespace string) *SMProvider {
sess, err := session.NewSessionWithOptions(session.Options{
Config: aws.Config{
Credentials: credentials.NewStaticCredentials("foobar", "foobar", "secret-manager"),
EndpointResolver: auth.ResolveEndpointWithServiceMap(map[string]string{
"secretsmanager": url,
}),
Region: aws.String("eu-east-1"),
Credentials: credentials.NewStaticCredentials(kid, sak, ""),
Region: aws.String(region),
},
})
Expect(err).ToNot(HaveOccurred())
if err != nil {
Fail(err.Error())
}
sm := secretsmanager.New(sess)
prov := &SMProvider{
url: url,
client: sm,
framework: f,
ServiceAccountName: saName,
ServiceAccountNamespace: saNamespace,
kid: kid,
sak: sak,
region: region,
client: sm,
framework: f,
}
BeforeEach(prov.BeforeEach)
BeforeEach(func() {
prov.SetupStaticStore()
prov.SetupReferencedIRSAStore()
prov.SetupMountedIRSAStore()
})
AfterEach(func() {
// Cleanup ClusterSecretStore
err := prov.framework.CRClient.Delete(context.Background(), &esv1alpha1.ClusterSecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: prov.ReferencedIRSAStoreName(),
},
})
Expect(err).ToNot(HaveOccurred())
})
return prov
}
func NewFromEnv(f *framework.Framework) *SMProvider {
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)
}
// CreateSecret creates a secret at the provider.
func (s *SMProvider) CreateSecret(key, val string) {
_, err := s.client.CreateSecret(&secretsmanager.CreateSecretInput{
Name: aws.String(key),
SecretString: aws.String(val),
})
Expect(err).ToNot(HaveOccurred())
// 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
attempts := 20
for {
log.Logf("creating secret %s / attempts left: %d", key, attempts)
_, err := s.client.CreateSecret(&secretsmanager.CreateSecretInput{
Name: aws.String(key),
SecretString: aws.String(val),
})
if err == nil {
return
}
attempts--
if attempts < 0 {
Fail("unable to create secret: " + err.Error())
}
<-time.After(time.Second * 5)
}
}
// 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) {
log.Logf("deleting secret %s", key)
_, err := s.client.DeleteSecret(&secretsmanager.DeleteSecretInput{
SecretId: aws.String(key),
SecretId: aws.String(key),
ForceDeleteWithoutRecovery: aws.Bool(true),
})
Expect(err).ToNot(HaveOccurred())
}
func (s *SMProvider) BeforeEach() {
By("creating a AWS SM credentials secret")
// MountedIRSAStore is a SecretStore without auth config
// ESO relies on the pod-mounted ServiceAccount when using this store.
func (s *SMProvider) SetupMountedIRSAStore() {
secretStore := &esv1alpha1.SecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: s.MountedIRSAStoreName(),
Namespace: s.framework.Namespace.Name,
},
Spec: esv1alpha1.SecretStoreSpec{
Provider: &esv1alpha1.SecretStoreProvider{
AWS: &esv1alpha1.AWSProvider{
Service: esv1alpha1.AWSServiceSecretsManager,
Region: s.region,
Auth: esv1alpha1.AWSAuth{},
},
},
},
}
err := s.framework.CRClient.Create(context.Background(), secretStore)
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() {
log.Logf("creating IRSA ClusterSecretStore %s", s.framework.Namespace.Name)
secretStore := &esv1alpha1.ClusterSecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: s.ReferencedIRSAStoreName(),
},
}
_, err := controllerutil.CreateOrUpdate(context.Background(), s.framework.CRClient, secretStore, func() error {
secretStore.Spec.Provider = &esv1alpha1.SecretStoreProvider{
AWS: &esv1alpha1.AWSProvider{
Service: esv1alpha1.AWSServiceSecretsManager,
Region: s.region,
Auth: esv1alpha1.AWSAuth{
JWTAuth: &esv1alpha1.AWSJWTAuth{
ServiceAccountRef: &esmetav1.ServiceAccountSelector{
Name: s.ServiceAccountName,
Namespace: &s.ServiceAccountNamespace,
},
},
},
},
}
return nil
})
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: secretName,
Name: staticCredentialsSecretName,
Namespace: s.framework.Namespace.Name,
},
StringData: map[string]string{
"kid": "foobar",
"sak": "foobar",
"kid": s.kid,
"sak": s.sak,
},
}
err := s.framework.CRClient.Create(context.Background(), awsCreds)
Expect(err).ToNot(HaveOccurred())
By("creating a AWS SM secret store")
secretStore := &esv1alpha1.SecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: s.framework.Namespace.Name,
@ -105,15 +222,15 @@ func (s *SMProvider) BeforeEach() {
Provider: &esv1alpha1.SecretStoreProvider{
AWS: &esv1alpha1.AWSProvider{
Service: esv1alpha1.AWSServiceSecretsManager,
Region: "us-east-1",
Region: s.region,
Auth: esv1alpha1.AWSAuth{
SecretRef: &esv1alpha1.AWSAuthSecretRef{
AccessKeyID: esmeta.SecretKeySelector{
Name: secretName,
AccessKeyID: esmetav1.SecretKeySelector{
Name: staticCredentialsSecretName,
Key: "kid",
},
SecretAccessKey: esmeta.SecretKeySelector{
Name: secretName,
SecretAccessKey: esmetav1.SecretKeySelector{
Name: staticCredentialsSecretName,
Key: "sak",
},
},

View file

@ -15,103 +15,17 @@ limitations under the License.
package aws
import (
"context"
"fmt"
// nolint
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
// nolint
. "github.com/onsi/ginkgo/extensions/table"
// 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/client"
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
"github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/suite/common"
)
var _ = Describe("[aws] ", func() {
f := framework.New("eso-aws")
prov := newSMProvider(f, "http://localstack.default")
jwt := func(tc *framework.TestCase) {
saName := "my-sa"
err := f.CRClient.Create(context.Background(), &v1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: saName,
Namespace: f.Namespace.Name,
Annotations: map[string]string{
"eks.amazonaws.com/role-arn": "arn:aws:iam::account:role/my-example-role",
},
},
})
Expect(err).ToNot(HaveOccurred())
// create secret store
secretStore := &esv1alpha1.SecretStore{
TypeMeta: metav1.TypeMeta{
Kind: esv1alpha1.SecretStoreKind,
APIVersion: esv1alpha1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: f.Namespace.Name,
Namespace: f.Namespace.Name,
},
Spec: esv1alpha1.SecretStoreSpec{
Provider: &esv1alpha1.SecretStoreProvider{
AWS: &esv1alpha1.AWSProvider{
Service: esv1alpha1.AWSServiceSecretsManager,
Region: "us-east-1",
Auth: esv1alpha1.AWSAuth{
JWTAuth: &esv1alpha1.AWSJWTAuth{
ServiceAccountRef: &esmeta.ServiceAccountSelector{
Name: saName,
Namespace: &f.Namespace.Name,
},
},
},
},
},
},
}
err = f.CRClient.Patch(context.Background(), secretStore, client.Apply, client.FieldOwner("e2e-case"), client.ForceOwnership)
Expect(err).ToNot(HaveOccurred())
secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
secretKey2 := fmt.Sprintf("%s-%s", f.Namespace.Name, "other")
secretValue := "bar"
tc.Secrets = map[string]string{
secretKey1: secretValue,
secretKey2: secretValue,
}
tc.ExpectedSecret = &v1.Secret{
Type: v1.SecretTypeOpaque,
Data: map[string][]byte{
secretKey1: []byte(secretValue),
secretKey2: []byte(secretValue),
},
}
tc.ExternalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{
{
SecretKey: secretKey1,
RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
Key: secretKey1,
},
},
{
SecretKey: secretKey2,
RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
Key: secretKey2,
},
},
}
}
var _ = Describe("[aws] ", Label("aws", "secretsmanager"), func() {
f := framework.New("eso-aws-sm")
prov := NewFromEnv(f)
DescribeTable("sync secrets",
framework.TableFunc(f,
@ -121,7 +35,6 @@ var _ = Describe("[aws] ", func() {
Entry(common.JSONDataFromSync(f)),
Entry(common.JSONDataWithProperty(f)),
Entry(common.JSONDataWithTemplate(f)),
Entry("should sync secrets with jwt auth", jwt),
Entry(common.DockerJSONConfig(f)),
Entry(common.DataPropertyDockerconfigJSON(f)),
Entry(common.SSHKeySync(f)),

View file

@ -0,0 +1,102 @@
/*
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()
}
}

View file

@ -13,28 +13,17 @@ limitations under the License.
package azure
import (
"os"
// nolint
. "github.com/onsi/ginkgo"
// nolint
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/ginkgo/v2"
"github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/suite/common"
)
var _ = Describe("[azure] ", func() {
var _ = Describe("[azure]", Label("azure", "keyvault"), func() {
f := framework.New("eso-azure")
vaultURL := os.Getenv("VAULT_URL")
tenantID := os.Getenv("TENANT_ID")
clientID := os.Getenv("AZURE_CLIENT_ID")
clientSecret := os.Getenv("AZURE_CLIENT_SECRET")
prov := &azureProvider{}
if vaultURL != "" && tenantID != "" && clientID != "" && clientSecret != "" {
prov = newazureProvider(f, clientID, clientSecret, tenantID, vaultURL)
}
prov := newFromEnv(f)
DescribeTable("sync secrets", framework.TableFunc(f, prov),
Entry(common.SimpleDataSync(f)),

View file

@ -14,15 +14,17 @@ package azure
import (
"context"
"os"
"github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault"
kvauth "github.com/Azure/go-autorest/autorest/azure/auth"
// nolint
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
// nolint
. "github.com/onsi/gomega"
. "github.com/onsi/ginkgo/v2"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilpointer "k8s.io/utils/pointer"
@ -45,7 +47,9 @@ func newazureProvider(f *framework.Framework, clientID, clientSecret, tenantID,
clientCredentialsConfig := kvauth.NewClientCredentialsConfig(clientID, clientSecret, tenantID)
clientCredentialsConfig.Resource = "https://vault.azure.net"
authorizer, err := clientCredentialsConfig.Authorizer()
Expect(err).ToNot(HaveOccurred())
if err != nil {
Fail(err.Error())
}
basicClient := keyvault.New()
basicClient.Authorizer = authorizer
@ -57,10 +61,22 @@ func newazureProvider(f *framework.Framework, clientID, clientSecret, tenantID,
vaultURL: vaultURL,
client: &basicClient,
}
BeforeEach(prov.BeforeEach)
BeforeEach(func() {
prov.CreateSecretStore()
})
return prov
}
func newFromEnv(f *framework.Framework) *azureProvider {
vaultURL := os.Getenv("VAULT_URL")
tenantID := os.Getenv("TENANT_ID")
clientID := os.Getenv("AZURE_CLIENT_ID")
clientSecret := os.Getenv("AZURE_CLIENT_SECRET")
return newazureProvider(f, clientID, clientSecret, tenantID, vaultURL)
}
func (s *azureProvider) CreateSecret(key, val string) {
_, err := s.client.SetSecret(
context.Background(),
@ -84,7 +100,7 @@ func (s *azureProvider) DeleteSecret(key string) {
Expect(err).ToNot(HaveOccurred())
}
func (s *azureProvider) BeforeEach() {
func (s *azureProvider) CreateSecretStore() {
azureCreds := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "provider-secret",

View file

@ -17,12 +17,9 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"os"
// nolint
. "github.com/onsi/ginkgo"
// nolint
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/ginkgo/v2"
v1 "k8s.io/api/core/v1"
p12 "software.sslmate.com/src/go-pkcs12"
@ -32,21 +29,32 @@ import (
"github.com/external-secrets/external-secrets/e2e/suite/common"
)
var _ = Describe("[gcp] ", func() {
// This test uses the global ESO.
var _ = Describe("[gcp]", Label("gcp", "secretsmanager"), func() {
f := framework.New("eso-gcp")
credentials := os.Getenv("GCP_SM_SA_JSON")
projectID := os.Getenv("GCP_PROJECT_ID")
prov := &GcpProvider{}
prov := NewFromEnv(f, "")
if credentials != "" && projectID != "" {
prov = NewgcpProvider(f, credentials, projectID, "", "", "", "")
}
DescribeTable("sync secrets", framework.TableFunc(f, prov),
Entry(common.SimpleDataSync(f)),
Entry(common.JSONDataWithProperty(f)),
Entry(common.JSONDataFromSync(f)),
Entry(common.NestedJSONWithGJSON(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)),
Entry("should sync p12 encoded cert secret", p12Cert),
)
})
// P12Cert case creates a secret with a p12 cert containing a privkey and cert bundled together.
// It uses templating to generate a k8s secret of type tls with pem values
p12Cert := func(tc *framework.TestCase) {
cloudSecretName := fmt.Sprintf("%s-%s", f.Namespace.Name, "p12-cert-example")
certPEM := `-----BEGIN CERTIFICATE-----
// P12Cert case creates a secret with a p12 cert containing a privkey and cert bundled together.
// It uses templating to generate a k8s secret of type tls with pem values.
var p12Cert = func(tc *framework.TestCase) {
cloudSecretName := fmt.Sprintf("%s-%s", tc.Framework.Namespace.Name, "p12-cert-example")
certPEM := `-----BEGIN CERTIFICATE-----
MIIFQjCCBCqgAwIBAgISBHszg5W2maz/7CIxGrf7mqukMA0GCSqGSIb3DQEBCwUA
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
EwJSMzAeFw0yMTA3MjQxMjQyMzNaFw0yMTEwMjIxMjQyMzFaMCgxJjAkBgNVBAMT
@ -78,7 +86,7 @@ XMYitHfpGhc+DTTiTWMQ13J0b1j4yv8A7ZaG2366aa28oSTD6eQFhmVCBwa54j++
IOwzHn5R
-----END CERTIFICATE-----
`
privkeyPEM := `-----BEGIN PRIVATE KEY-----
privkeyPEM := `-----BEGIN PRIVATE KEY-----
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDJFE51myQDyqca
egyBDlHLkxVj+WCjcfOWEqrTa7bcnbDXjD4uIRTaFxIkpi/k5fKxt+rszna7bNdh
lezqSuRBmVg2kXDul5nQm1RtWRKlJP9fhvUYkoNKRGzt9OL6/6lv05P2tNu13yN8
@ -107,55 +115,39 @@ Jdx0ECYawviQoreDAyIXV6HouoeRbDtLZ9AJvxMoIjGcjAR2FQHc3yx4h/lf3Tfx
x6HaRh+EUwU51von6M9lEF9/p5Q=
-----END PRIVATE KEY-----
`
blockCert, _ := pem.Decode([]byte(certPEM))
cert, _ := x509.ParseCertificate(blockCert.Bytes)
blockPrivKey, _ := pem.Decode([]byte(privkeyPEM))
privkey, _ := x509.ParsePKCS8PrivateKey(blockPrivKey.Bytes)
emptyCACerts := []*x509.Certificate{}
p12Cert, _ := p12.Encode(rand.Reader, privkey, cert, emptyCACerts, "")
blockCert, _ := pem.Decode([]byte(certPEM))
cert, _ := x509.ParseCertificate(blockCert.Bytes)
blockPrivKey, _ := pem.Decode([]byte(privkeyPEM))
privkey, _ := x509.ParsePKCS8PrivateKey(blockPrivKey.Bytes)
emptyCACerts := []*x509.Certificate{}
p12Cert, _ := p12.Encode(rand.Reader, privkey, cert, emptyCACerts, "")
tc.Secrets = map[string]string{
cloudSecretName: string(p12Cert),
}
tc.ExpectedSecret = &v1.Secret{
Type: v1.SecretTypeTLS,
Data: map[string][]byte{
"tls.crt": []byte(certPEM),
"tls.key": []byte(privkeyPEM),
},
}
tc.ExternalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{
{
SecretKey: "mysecret",
RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
Key: cloudSecretName,
},
},
}
tc.ExternalSecret.Spec.Target.Template = &esv1alpha1.ExternalSecretTemplate{
Type: v1.SecretTypeTLS,
Data: map[string]string{
"tls.crt": "{{ .mysecret | pkcs12cert | pemCertificate }}",
"tls.key": "{{ .mysecret | pkcs12key | pemPrivateKey }}",
},
}
tc.Secrets = map[string]string{
cloudSecretName: string(p12Cert),
}
DescribeTable("sync secrets", framework.TableFunc(f, prov),
Entry(common.SimpleDataSync(f)),
Entry(common.JSONDataWithProperty(f)),
Entry(common.JSONDataFromSync(f)),
Entry(common.NestedJSONWithGJSON(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)),
Entry("should sync p12 encoded cert secret", p12Cert),
)
})
tc.ExpectedSecret = &v1.Secret{
Type: v1.SecretTypeTLS,
Data: map[string][]byte{
"tls.crt": []byte(certPEM),
"tls.key": []byte(privkeyPEM),
},
}
tc.ExternalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{
{
SecretKey: "mysecret",
RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
Key: cloudSecretName,
},
},
}
tc.ExternalSecret.Spec.Target.Template = &esv1alpha1.ExternalSecretTemplate{
Type: v1.SecretTypeTLS,
Data: map[string]string{
"tls.crt": "{{ .mysecret | pkcs12cert | pemCertificate }}",
"tls.key": "{{ .mysecret | pkcs12key | pemPrivateKey }}",
},
}
}

View file

@ -0,0 +1,111 @@
/*
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 gcp
import (
// nolint
. "github.com/onsi/ginkgo/v2"
// nolint
// . "github.com/onsi/gomega"
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 (
withPodID = "sync secrets with pod identity"
withSpecifcSA = "sync secrets with specificSA identity"
)
// Deploys eso to the default namespace
// that uses the service account provisioned by terraform
// to test pod-identity authentication.
var _ = Describe("[gcpmanaged] with pod identity", Label("gcp", "secretsmanager", "managed", "pod-identity"), func() {
f := framework.New("eso-gcpmanaged")
prov := NewFromEnv(f, f.BaseName)
// 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),
// uses pod id
framework.Compose(withPodID, f, common.SimpleDataSync, usePodIDESReference),
framework.Compose(withPodID, f, common.JSONDataWithProperty, usePodIDESReference),
framework.Compose(withPodID, f, common.JSONDataFromSync, usePodIDESReference),
framework.Compose(withPodID, f, common.NestedJSONWithGJSON, usePodIDESReference),
framework.Compose(withPodID, f, common.JSONDataWithTemplate, usePodIDESReference),
framework.Compose(withPodID, f, common.DockerJSONConfig, usePodIDESReference),
framework.Compose(withPodID, f, common.DataPropertyDockerconfigJSON, usePodIDESReference),
framework.Compose(withPodID, f, common.SSHKeySync, usePodIDESReference),
framework.Compose(withPodID, f, common.SSHKeySyncDataProperty, usePodIDESReference),
framework.Compose(withPodID, f, common.SyncWithoutTargetName, usePodIDESReference),
framework.Compose(withPodID, f, common.JSONDataWithoutTargetName, usePodIDESReference),
)
})
// We're using a namespace scoped ESO
// that runs WITHOUT pod identity (with default sa)
// It uses a specific service account defined in the ClusterSecretStore spec
// to authenticate against cloud provider APIs.
var _ = Describe("[gcpmanaged] with service account", Label("gcp", "secretsmanager", "managed", "service-account"), func() {
f := framework.New("eso-gcpmanaged")
prov := NewFromEnv(f, f.BaseName)
BeforeEach(func() {
f.Install(addon.NewESO(
addon.WithControllerClass(f.BaseName),
addon.WithReleaseName(f.Namespace.Name),
addon.WithNamespace(f.Namespace.Name),
))
})
DescribeTable("sync secrets",
framework.TableFunc(f,
prov),
// uses specific sa
framework.Compose(withSpecifcSA, f, common.JSONDataFromSync, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.JSONDataWithProperty, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.JSONDataFromSync, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.NestedJSONWithGJSON, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.JSONDataWithTemplate, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.DockerJSONConfig, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.DataPropertyDockerconfigJSON, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.SSHKeySync, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.SSHKeySyncDataProperty, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.SyncWithoutTargetName, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.JSONDataWithoutTargetName, useSpecifcSAESReference(prov)),
)
})
func usePodIDESReference(tc *framework.TestCase) {
tc.ExternalSecret.Spec.SecretStoreRef.Name = PodIDSecretStoreName
}
func useSpecifcSAESReference(prov *GcpProvider) func(*framework.TestCase) {
return func(tc *framework.TestCase) {
tc.ExternalSecret.Spec.SecretStoreRef.Kind = esv1alpha1.ClusterSecretStoreKind
tc.ExternalSecret.Spec.SecretStoreRef.Name = prov.SAClusterSecretStoreName()
}
}

View file

@ -15,116 +15,97 @@ package gcp
import (
"context"
"fmt"
"os"
secretmanager "cloud.google.com/go/secretmanager/apiv1"
// nolint
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
// nolint
. "github.com/onsi/gomega"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"golang.org/x/oauth2/jwt"
"google.golang.org/api/option"
secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
utilpointer "k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
esmeta "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"
gcpsm "github.com/external-secrets/external-secrets/pkg/provider/gcp/secretmanager"
)
const (
PodIDSecretStoreName = "pod-identity"
SpecifcSASecretStoreName = "specific-sa"
PodIDSecretStoreName = "pod-identity"
staticCredentialsSecretName = "provider-secret"
)
func makeStore(s *GcpProvider) *esv1alpha1.SecretStore {
return &esv1alpha1.SecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: s.framework.Namespace.Name,
Namespace: s.framework.Namespace.Name,
},
Spec: esv1alpha1.SecretStoreSpec{
Provider: &esv1alpha1.SecretStoreProvider{
GCPSM: &esv1alpha1.GCPSMProvider{
ProjectID: s.projectID,
},
},
},
}
}
func makeCStore(s *GcpProvider) *esv1alpha1.ClusterSecretStore {
return &esv1alpha1.ClusterSecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: s.framework.Namespace.Name,
Namespace: s.framework.Namespace.Name,
},
Spec: esv1alpha1.SecretStoreSpec{
Provider: &esv1alpha1.SecretStoreProvider{
GCPSM: &esv1alpha1.GCPSMProvider{
ProjectID: s.projectID,
},
},
},
}
}
// nolint // Better to keep names consistent even if it stutters;
type GcpProvider struct {
credentials string
projectID string
framework *framework.Framework
clusterLocation string
clusterName string
serviceAccountName string
serviceAccountNamespace string
ServiceAccountName string
ServiceAccountNamespace string
framework *framework.Framework
credentials string
projectID string
clusterLocation string
clusterName string
controllerClass string
}
func NewgcpProvider(f *framework.Framework, credentials, projectID string,
clusterLocation string, clusterName string, serviceAccountName string, serviceAccountNamespace string) *GcpProvider {
func NewGCPProvider(f *framework.Framework, credentials, projectID string,
clusterLocation string, clusterName string, serviceAccountName string, serviceAccountNamespace string, controllerClass string) *GcpProvider {
prov := &GcpProvider{
credentials: credentials,
projectID: projectID,
framework: f,
clusterLocation: clusterLocation,
clusterName: clusterName,
serviceAccountName: serviceAccountName,
serviceAccountNamespace: serviceAccountNamespace,
ServiceAccountName: serviceAccountName,
ServiceAccountNamespace: serviceAccountNamespace,
controllerClass: controllerClass,
}
BeforeEach(prov.BeforeEach)
BeforeEach(func() {
prov.CreateSAKeyStore(f.Namespace.Name)
prov.CreateSpecifcSASecretStore(f.Namespace.Name)
prov.CreatePodIDStore(f.Namespace.Name)
})
AfterEach(func() {
prov.DeleteSpecifcSASecretStore()
})
return prov
}
func (s *GcpProvider) getClient(ctx context.Context, credentials string) (client *secretmanager.Client, err error) {
if credentials == "" {
var ts oauth2.TokenSource
ts, err = google.DefaultTokenSource(ctx, gcpsm.CloudPlatformRole)
Expect(err).ToNot(HaveOccurred())
client, err = secretmanager.NewClient(ctx, option.WithTokenSource(ts))
Expect(err).ToNot(HaveOccurred())
} else {
var config *jwt.Config
config, err = google.JWTConfigFromJSON([]byte(s.credentials), gcpsm.CloudPlatformRole)
Expect(err).ToNot(HaveOccurred())
ts := config.TokenSource(ctx)
client, err = secretmanager.NewClient(ctx, option.WithTokenSource(ts))
Expect(err).ToNot(HaveOccurred())
}
func NewFromEnv(f *framework.Framework, controllerClass string) *GcpProvider {
projectID := os.Getenv("GCP_PROJECT_ID")
credentials := os.Getenv("GCP_SM_SA_JSON")
serviceAccountName := os.Getenv("GCP_KSA_NAME")
serviceAccountNamespace := "default"
clusterLocation := os.Getenv("GCP_GKE_ZONE")
clusterName := os.Getenv("GCP_GKE_CLUSTER")
return NewGCPProvider(f, credentials, projectID, clusterLocation, clusterName, serviceAccountName, serviceAccountNamespace, controllerClass)
}
func (s *GcpProvider) getClient(ctx context.Context) (client *secretmanager.Client, err error) {
var config *jwt.Config
config, err = google.JWTConfigFromJSON([]byte(s.credentials), gcpsm.CloudPlatformRole)
Expect(err).ToNot(HaveOccurred())
ts := config.TokenSource(ctx)
client, err = secretmanager.NewClient(ctx, option.WithTokenSource(ts))
Expect(err).ToNot(HaveOccurred())
return client, err
}
func (s *GcpProvider) CreateSecret(key, val string) {
ctx := context.Background()
client, err := s.getClient(ctx, s.credentials)
client, err := s.getClient(ctx)
Expect(err).ToNot(HaveOccurred())
defer client.Close()
// Create the request to create the secret.
@ -153,7 +134,7 @@ func (s *GcpProvider) CreateSecret(key, val string) {
func (s *GcpProvider) DeleteSecret(key string) {
ctx := context.Background()
client, err := s.getClient(ctx, s.credentials)
client, err := s.getClient(ctx)
Expect(err).ToNot(HaveOccurred())
Expect(err).ToNot(HaveOccurred())
defer client.Close()
@ -164,11 +145,27 @@ func (s *GcpProvider) DeleteSecret(key string) {
Expect(err).ToNot(HaveOccurred())
}
func (s *GcpProvider) BeforeEach() {
By("creating a gcp secret")
func makeStore(s *GcpProvider) *esv1alpha1.SecretStore {
return &esv1alpha1.SecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: s.framework.Namespace.Name,
Namespace: s.framework.Namespace.Name,
},
Spec: esv1alpha1.SecretStoreSpec{
Controller: s.controllerClass,
Provider: &esv1alpha1.SecretStoreProvider{
GCPSM: &esv1alpha1.GCPSMProvider{
ProjectID: s.projectID,
},
},
},
}
}
func (s *GcpProvider) CreateSAKeyStore(ns string) {
gcpCreds := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "provider-secret",
Name: staticCredentialsSecretName,
Namespace: s.framework.Namespace.Name,
},
StringData: map[string]string{
@ -180,23 +177,16 @@ func (s *GcpProvider) BeforeEach() {
err = s.framework.CRClient.Update(context.Background(), gcpCreds)
Expect(err).ToNot(HaveOccurred())
}
By("creating an secret stores gcp")
s.CreateSAKeyStore(s.framework.Namespace.Name)
s.CreatePodIDStore(s.framework.Namespace.Name)
s.CreateSpecifcSASecretStore(s.framework.Namespace.Name)
}
func (s *GcpProvider) CreateSAKeyStore(ns string) {
secretStore := makeStore(s)
secretStore.Spec.Provider.GCPSM.Auth = esv1alpha1.GCPSMAuth{
SecretRef: &esv1alpha1.GCPSMAuthSecretRef{
SecretAccessKey: esmeta.SecretKeySelector{
Name: "provider-secret",
Name: staticCredentialsSecretName,
Key: "secret-access-credentials",
},
},
}
err := s.framework.CRClient.Create(context.Background(), secretStore)
err = s.framework.CRClient.Create(context.Background(), secretStore)
Expect(err).ToNot(HaveOccurred())
}
@ -207,29 +197,45 @@ func (s *GcpProvider) CreatePodIDStore(ns string) {
Expect(err).ToNot(HaveOccurred())
}
func (s *GcpProvider) SAClusterSecretStoreName() string {
return "gcpsa-" + s.framework.Namespace.Name
}
func (s *GcpProvider) CreateSpecifcSASecretStore(ns string) {
clusterSecretStore := makeCStore(s)
clusterSecretStore.ObjectMeta.Name = SpecifcSASecretStoreName
clusterSecretStore.Spec.Provider.GCPSM.Auth = esv1alpha1.GCPSMAuth{
WorkloadIdentity: &esv1alpha1.GCPWorkloadIdentity{
ClusterLocation: s.clusterLocation,
ClusterName: s.clusterName,
ServiceAccountRef: esmeta.ServiceAccountSelector{
Name: s.serviceAccountName,
Namespace: utilpointer.StringPtr(s.serviceAccountNamespace),
},
clusterSecretStore := &esv1alpha1.ClusterSecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: s.SAClusterSecretStoreName(),
},
}
var cSS esv1alpha1.ClusterSecretStore
err := s.framework.CRClient.Get(context.Background(), types.NamespacedName{
Name: SpecifcSASecretStoreName,
}, &cSS)
if apierrors.IsNotFound(err) {
err := s.framework.CRClient.Create(context.Background(), clusterSecretStore)
Expect(err).ToNot(HaveOccurred())
} else {
log.Logf("%s CSStore already created", SpecifcSASecretStoreName)
}
_, err := controllerutil.CreateOrUpdate(context.Background(), s.framework.CRClient, clusterSecretStore, func() error {
clusterSecretStore.Spec.Controller = s.controllerClass
clusterSecretStore.Spec.Provider = &esv1alpha1.SecretStoreProvider{
GCPSM: &esv1alpha1.GCPSMProvider{
ProjectID: s.projectID,
Auth: esv1alpha1.GCPSMAuth{
WorkloadIdentity: &esv1alpha1.GCPWorkloadIdentity{
ClusterLocation: s.clusterLocation,
ClusterName: s.clusterName,
ServiceAccountRef: esmeta.ServiceAccountSelector{
Name: s.ServiceAccountName,
Namespace: utilpointer.StringPtr(s.ServiceAccountNamespace),
},
},
},
},
}
return nil
})
Expect(err).ToNot(HaveOccurred())
}
// Cleanup removes global resources that may have been
// created by this provider.
func (s *GcpProvider) DeleteSpecifcSASecretStore() {
err := s.framework.CRClient.Delete(context.Background(), &esv1alpha1.ClusterSecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: s.SAClusterSecretStoreName(),
},
})
Expect(err).ToNot(HaveOccurred())
}

View file

@ -1,86 +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.
limitations under the License.
*/
package gcpmanaged
import (
"os"
// nolint
. "github.com/onsi/ginkgo"
// nolint
. "github.com/onsi/ginkgo/extensions/table"
// nolint
// . "github.com/onsi/gomega"
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/suite/common"
"github.com/external-secrets/external-secrets/e2e/suite/gcp"
)
const (
withPodID = "sync secrets with pod identity"
withSpecifcSA = "sync secrets with specificSA identity"
)
var _ = Describe("[gcpmanaged] ", func() {
if os.Getenv("FOCUS") == "gcpmanaged" {
f := framework.New("eso-gcp-managed")
projectID := os.Getenv("GCP_PROJECT_ID")
clusterLocation := "europe-west1-b"
clusterName := "test-cluster"
serviceAccountName := os.Getenv("GCP_KSA_NAME")
serviceAccountNamespace := "default"
prov := &gcp.GcpProvider{}
if projectID != "" {
prov = gcp.NewgcpProvider(f, "", projectID, clusterLocation, clusterName, serviceAccountName, serviceAccountNamespace)
}
DescribeTable("sync secrets",
framework.TableFunc(f,
prov),
// uses pod id
framework.Compose(withPodID, f, common.SimpleDataSync, usePodIDESReference),
framework.Compose(withPodID, f, common.JSONDataWithProperty, usePodIDESReference),
framework.Compose(withPodID, f, common.JSONDataFromSync, usePodIDESReference),
framework.Compose(withPodID, f, common.NestedJSONWithGJSON, usePodIDESReference),
framework.Compose(withPodID, f, common.JSONDataWithTemplate, usePodIDESReference),
framework.Compose(withPodID, f, common.DockerJSONConfig, usePodIDESReference),
framework.Compose(withPodID, f, common.DataPropertyDockerconfigJSON, usePodIDESReference),
framework.Compose(withPodID, f, common.SSHKeySync, usePodIDESReference),
framework.Compose(withPodID, f, common.SSHKeySyncDataProperty, usePodIDESReference),
framework.Compose(withPodID, f, common.SyncWithoutTargetName, usePodIDESReference),
framework.Compose(withPodID, f, common.JSONDataWithoutTargetName, usePodIDESReference),
// uses specific sa
framework.Compose(withSpecifcSA, f, common.JSONDataFromSync, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.JSONDataWithProperty, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.JSONDataFromSync, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.NestedJSONWithGJSON, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.JSONDataWithTemplate, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.DockerJSONConfig, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.DataPropertyDockerconfigJSON, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.SSHKeySync, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.SSHKeySyncDataProperty, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.SyncWithoutTargetName, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.JSONDataWithoutTargetName, useSpecifcSAESReference),
)
}
})
func usePodIDESReference(tc *framework.TestCase) {
tc.ExternalSecret.Spec.SecretStoreRef.Name = gcp.PodIDSecretStoreName
}
func useSpecifcSAESReference(tc *framework.TestCase) {
tc.ExternalSecret.Spec.SecretStoreRef.Kind = esv1alpha1.ClusterSecretStoreKind
tc.ExternalSecret.Spec.SecretStoreRef.Name = gcp.SpecifcSASecretStoreName
}

View file

@ -18,27 +18,19 @@ package gitlab
// and in e2e/suite/common/common.go, but this breaks Azure provider.
import (
"os"
// nolint
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
// nolint
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/ginkgo/v2/extensions/table"
"github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/suite/common"
)
var _ = Describe("[gitlab] ", func() {
f := framework.New("esogitlab")
credentials := os.Getenv("GITLAB_TOKEN")
projectID := os.Getenv("GITLAB_PROJECT_ID")
prov := &gitlabProvider{}
if credentials != "" && projectID != "" {
prov = newGitlabProvider(f, credentials, projectID)
}
var _ = Describe("[gitlab]", Label("gitlab"), func() {
f := framework.New("eso-gitlab")
prov := newFromEnv(f)
DescribeTable("sync secrets", framework.TableFunc(f, prov),
Entry(common.SimpleDataSync(f)),

View file

@ -15,10 +15,11 @@ package gitlab
import (
"context"
"os"
"strings"
// nolint
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
// nolint
. "github.com/onsi/gomega"
@ -47,6 +48,12 @@ func newGitlabProvider(f *framework.Framework, credentials, projectID string) *g
return prov
}
func newFromEnv(f *framework.Framework) *gitlabProvider {
credentials := os.Getenv("GITLAB_TOKEN")
projectID := os.Getenv("GITLAB_PROJECT_ID")
return newGitlabProvider(f, credentials, projectID)
}
func (s *gitlabProvider) CreateSecret(key, val string) {
// **Open the client
client, err := gitlab.NewClient(s.credentials)

View file

@ -19,6 +19,5 @@ import (
_ "github.com/external-secrets/external-secrets/e2e/suite/aws"
_ "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/gcpmanaged"
_ "github.com/external-secrets/external-secrets/e2e/suite/vault"
)

View file

@ -13,25 +13,19 @@ limitations under the License.
package oracle
import (
"os"
// nolint
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
// nolint
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/ginkgo/v2/extensions/table"
"github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/suite/common"
)
var _ = Describe("[oracle] ", func() {
var _ = Describe("[oracle]", Label("oracle"), func() {
f := framework.New("eso-oracle")
tenancy := os.Getenv("OCI_TENANCY_OCID")
user := os.Getenv("OCI_USER_OCID")
region := os.Getenv("OCI_REGION")
fingerprint := os.Getenv("OCI_FINGERPRINT")
privateKey := os.Getenv("OCI_PRIVATE_KEY")
prov := newOracleProvider(f, tenancy, user, region, fingerprint, privateKey)
prov := newFromEnv(f)
DescribeTable("sync secrets", framework.TableFunc(f, prov),
Entry(common.SimpleDataSync(f)),

View file

@ -14,9 +14,10 @@ package oracle
import (
"context"
"os"
// nolint
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
// nolint
. "github.com/onsi/gomega"
@ -58,6 +59,15 @@ func newOracleProvider(f *framework.Framework, tenancy, user, region, fingerprin
return prov
}
func newFromEnv(f *framework.Framework) *oracleProvider {
tenancy := os.Getenv("OCI_TENANCY_OCID")
user := os.Getenv("OCI_USER_OCID")
region := os.Getenv("OCI_REGION")
fingerprint := os.Getenv("OCI_FINGERPRINT")
privateKey := os.Getenv("OCI_PRIVATE_KEY")
return newOracleProvider(f, tenancy, user, region, fingerprint, privateKey)
}
func (p *oracleProvider) CreateSecret(key, val string) {
configurationProvider := common.NewRawConfigurationProvider(p.tenancy, p.user, p.region, p.fingerprint, p.privateKey, nil)
client, err := vault.NewVaultsClientWithConfigurationProvider(configurationProvider)

View file

@ -21,7 +21,7 @@ import (
vault "github.com/hashicorp/vault/api"
//nolint
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
//nolint
. "github.com/onsi/gomega"

View file

@ -15,9 +15,7 @@ package vault
import (
// nolint
. "github.com/onsi/ginkgo"
// nolint
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/ginkgo/v2"
"github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/suite/common"
@ -32,12 +30,12 @@ const (
withK8s = "with kubernetes provider"
)
var _ = Describe("[vault] ", func() {
var _ = Describe("[vault]", Label("vault"), func() {
f := framework.New("eso-vault")
prov := newVaultProvider(f)
DescribeTable("sync secrets",
framework.TableFunc(f,
newVaultProvider(f)),
framework.TableFunc(f, prov),
// uses token auth
framework.Compose(withTokenAuth, f, common.JSONDataFromSync, useTokenAuth),
framework.Compose(withTokenAuth, f, common.JSONDataWithProperty, useTokenAuth),

5
go.mod
View file

@ -57,7 +57,7 @@ require (
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 v1.16.5
github.com/onsi/ginkgo/v2 v2.0.0
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
@ -133,6 +133,7 @@ require (
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-querystring v1.0.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
@ -174,7 +175,6 @@ require (
github.com/moby/spdystream v0.2.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
@ -211,7 +211,6 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // 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

4
go.sum
View file

@ -409,8 +409,10 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -678,6 +680,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/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=

View file

@ -56,7 +56,7 @@ func main() {
"Enabling this will ensure there is only one active controller manager.")
flag.IntVar(&concurrent, "concurrent", 1, "The number of concurrent ExternalSecret reconciles.")
flag.StringVar(&loglevel, "loglevel", "info", "loglevel to use, one of: debug, info, warn, error, dpanic, panic, fatal")
flag.StringVar(&namespace, "namespace", "", "watch external secrets scoped in the provided namespace only")
flag.StringVar(&namespace, "namespace", "", "watch external secrets scoped in the provided namespace only. ClusterSecretStore can be used but only work if it doesn't reference resources from other namespaces")
flag.Parse()
var lvl zapcore.Level

View file

@ -20,8 +20,7 @@ import (
"strconv"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
dto "github.com/prometheus/client_model/go"
v1 "k8s.io/api/core/v1"

View file

@ -19,7 +19,7 @@ import (
"testing"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"go.uber.org/zap/zapcore"
"k8s.io/client-go/kubernetes/scheme"
@ -89,7 +89,7 @@ var _ = BeforeSuite(func() {
defer GinkgoRecover()
Expect(k8sManager.Start(ctrl.SetupSignalHandler())).ToNot(HaveOccurred())
}()
}, 60)
})
var _ = AfterSuite(func() {
By("tearing down the test environment")

View file

@ -18,7 +18,7 @@ import (
"path/filepath"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
@ -65,7 +65,7 @@ var _ = BeforeSuite(func() {
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
Expect(err).ToNot(HaveOccurred())
Expect(k8sClient).ToNot(BeNil())
}, 60)
})
var _ = AfterSuite(func() {
By("tearing down the test environment")

8
terraform/aws/main.tf Normal file
View file

@ -0,0 +1,8 @@
module "cluster" {
source = "./modules/cluster"
cluster_name = var.AWS_CLUSTER_NAME
cluster_region = var.AWS_REGION
irsa_sa_name = var.AWS_SA_NAME
irsa_sa_namespace = var.AWS_SA_NAMESPACE
}

View file

@ -0,0 +1,60 @@
data "aws_eks_cluster_auth" "this" {
name = module.eks.cluster_id
}
data "aws_caller_identity" "current" {}
locals {
kubeconfig = yamlencode({
apiVersion = "v1"
kind = "Config"
current-context = "terraform"
clusters = [{
name = module.eks.cluster_id
cluster = {
certificate-authority-data = module.eks.cluster_certificate_authority_data
server = module.eks.cluster_endpoint
}
}]
contexts = [{
name = "terraform"
context = {
cluster = module.eks.cluster_id
user = "terraform"
}
}]
users = [{
name = "terraform"
user = {
token = data.aws_eks_cluster_auth.this.token
}
}]
})
# we have to allow the root account to access the api
aws_auth_configmap_yaml = <<-EOT
${chomp(module.eks.aws_auth_configmap_yaml)}
- rolearn: arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/admin
username: system:aws:root
groups:
- system:masters
EOT
}
resource "null_resource" "patch_cm" {
triggers = {
kubeconfig = base64encode(local.kubeconfig)
cmd_patch = <<-EOT
kubectl patch configmap/aws-auth --patch "${local.aws_auth_configmap_yaml}" -n kube-system --kubeconfig <(echo $KUBECONFIG | base64 --decode)
EOT
}
provisioner "local-exec" {
interpreter = ["/bin/bash", "-c"]
environment = {
KUBECONFIG = self.triggers.kubeconfig
}
command = self.triggers.cmd_patch
}
}

View file

@ -0,0 +1,57 @@
locals {
sa_manifest = <<-EOT
apiVersion: v1
kind: ServiceAccount
metadata:
name: ${local.serviceaccount_name}
namespace: ${local.serviceaccount_namespace}
annotations:
eks.amazonaws.com/role-arn: "${aws_iam_role.eso-e2e-irsa.arn}"
EOT
}
data "aws_iam_policy_document" "assume-policy" {
statement {
actions = ["sts:AssumeRoleWithWebIdentity"]
condition {
test = "StringEquals"
variable = "${trimprefix(module.eks.cluster_oidc_issuer_url, "https://")}:sub"
values = [
"system:serviceaccount:${local.serviceaccount_namespace}:${local.serviceaccount_name}"
]
}
principals {
type = "Federated"
identifiers = [module.eks.oidc_provider_arn]
}
}
}
resource "aws_iam_role" "eso-e2e-irsa" {
name = "eso-e2e-irsa"
path = "/"
assume_role_policy = data.aws_iam_policy_document.assume-policy.json
managed_policy_arns = [
"arn:aws:iam::aws:policy/SecretsManagerReadWrite"
]
}
resource "null_resource" "apply_sa" {
triggers = {
kubeconfig = base64encode(local.kubeconfig)
cmd_patch = <<-EOT
echo '${local.sa_manifest}' | kubectl --kubeconfig <(echo $KUBECONFIG | base64 --decode) apply -f -
EOT
}
provisioner "local-exec" {
interpreter = ["/bin/bash", "-c"]
environment = {
KUBECONFIG = self.triggers.kubeconfig
}
command = self.triggers.cmd_patch
}
}

View file

@ -0,0 +1,127 @@
provider "aws" {
region = local.region
}
locals {
name = var.cluster_name
cluster_version = "1.21"
region = var.cluster_region
serviceaccount_name = var.irsa_sa_name
serviceaccount_namespace = var.irsa_sa_namespace
tags = {
Example = local.name
GithubRepo = "external-secrets"
GithubOrg = "external-secrets"
}
}
module "eks" {
source = "git::https://github.com/terraform-aws-modules/terraform-aws-eks?ref=v18.2.0"
cluster_name = local.name
cluster_version = local.cluster_version
cluster_endpoint_private_access = true
cluster_endpoint_public_access = true
cluster_addons = {
coredns = {
resolve_conflicts = "OVERWRITE"
}
kube-proxy = {}
vpc-cni = {
resolve_conflicts = "OVERWRITE"
}
}
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
enable_irsa = true
# EKS Managed Node Group(s)
eks_managed_node_group_defaults = {
ami_type = "AL2_x86_64"
disk_size = 50
instance_types = ["m6i.large", "m5.large", "m5n.large", "m5zn.large"]
vpc_security_group_ids = [aws_security_group.additional.id]
}
eks_managed_node_groups = {
example = {
desired_size = 2
instance_types = ["t3.large"]
tags = local.tags
}
}
tags = local.tags
}
################################################################################
# Supporting resources
################################################################################
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 3.0"
name = local.name
cidr = "10.0.0.0/16"
azs = ["${local.region}a", "${local.region}b", "${local.region}c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"]
enable_nat_gateway = true
single_nat_gateway = true
enable_dns_hostnames = true
enable_flow_log = true
create_flow_log_cloudwatch_iam_role = true
create_flow_log_cloudwatch_log_group = true
public_subnet_tags = {
"kubernetes.io/cluster/${local.name}" = "shared"
"kubernetes.io/role/elb" = 1
}
private_subnet_tags = {
"kubernetes.io/cluster/${local.name}" = "shared"
"kubernetes.io/role/internal-elb" = 1
}
tags = local.tags
}
resource "aws_security_group" "additional" {
name_prefix = "${local.name}-additional"
vpc_id = module.vpc.vpc_id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
]
}
# 443, 53, 123 is already allowed
egress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
tags = local.tags
}

View file

@ -0,0 +1,135 @@
################################################################################
# Cluster
################################################################################
output "cluster_arn" {
description = "The Amazon Resource Name (ARN) of the cluster"
value = module.eks.cluster_arn
}
output "cluster_certificate_authority_data" {
description = "Base64 encoded certificate data required to communicate with the cluster"
value = module.eks.cluster_certificate_authority_data
}
output "cluster_endpoint" {
description = "Endpoint for your Kubernetes API server"
value = module.eks.cluster_endpoint
}
output "cluster_id" {
description = "The name/id of the EKS cluster. Will block on cluster creation until the cluster is really ready"
value = module.eks.cluster_id
}
output "cluster_oidc_issuer_url" {
description = "The URL on the EKS cluster for the OpenID Connect identity provider"
value = module.eks.cluster_oidc_issuer_url
}
output "cluster_platform_version" {
description = "Platform version for the cluster"
value = module.eks.cluster_platform_version
}
output "cluster_status" {
description = "Status of the EKS cluster. One of `CREATING`, `ACTIVE`, `DELETING`, `FAILED`"
value = module.eks.cluster_status
}
output "cluster_security_group_id" {
description = "Cluster security group that was created by Amazon EKS for the cluster. Managed node groups use this security group for control-plane-to-data-plane communication. Referred to as 'Cluster security group' in the EKS console"
value = module.eks.cluster_security_group_id
}
################################################################################
# Security Group
################################################################################
output "cluster_security_group_arn" {
description = "Amazon Resource Name (ARN) of the cluster security group"
value = module.eks.cluster_security_group_arn
}
################################################################################
# IRSA
################################################################################
output "oidc_provider_arn" {
description = "The ARN of the OIDC Provider if `enable_irsa = true`"
value = module.eks.oidc_provider_arn
}
################################################################################
# IAM Role
################################################################################
output "cluster_iam_role_name" {
description = "IAM role name of the EKS cluster"
value = module.eks.cluster_iam_role_name
}
output "cluster_iam_role_arn" {
description = "IAM role ARN of the EKS cluster"
value = module.eks.cluster_iam_role_arn
}
output "cluster_iam_role_unique_id" {
description = "Stable and unique string identifying the IAM role"
value = module.eks.cluster_iam_role_unique_id
}
################################################################################
# EKS Addons
################################################################################
output "cluster_addons" {
description = "Map of attribute maps for all EKS cluster addons enabled"
value = module.eks.cluster_addons
}
################################################################################
# EKS Identity Provider
################################################################################
output "cluster_identity_providers" {
description = "Map of attribute maps for all EKS identity providers enabled"
value = module.eks.cluster_identity_providers
}
################################################################################
# CloudWatch Log Group
################################################################################
output "cloudwatch_log_group_name" {
description = "Name of cloudwatch log group created"
value = module.eks.cloudwatch_log_group_name
}
output "cloudwatch_log_group_arn" {
description = "Arn of cloudwatch log group created"
value = module.eks.cloudwatch_log_group_arn
}
################################################################################
# Fargate Profile
################################################################################
output "fargate_profiles" {
description = "Map of attribute maps for all EKS Fargate Profiles created"
value = module.eks.fargate_profiles
}
################################################################################
# Additional
################################################################################
output "aws_auth_configmap_yaml" {
description = "Formatted yaml output for base aws-auth configmap containing roles used in cluster node groups/fargate profiles"
value = module.eks.aws_auth_configmap_yaml
}
output "eks_cluster_auth_token" {
value = data.aws_eks_cluster_auth.this.token
sensitive = true
}

View file

@ -0,0 +1,10 @@
terraform {
required_version = ">= 0.13"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.0"
}
}
}

View file

@ -0,0 +1,16 @@
variable "cluster_name" {
type = string
default = "eso-e2e-managed"
}
variable "irsa_sa_name" {
type = string
}
variable "irsa_sa_namespace" {
type = string
}
variable "cluster_region" {
type = string
}

11
terraform/aws/outputs.tf Normal file
View file

@ -0,0 +1,11 @@
output "cluster_arn" {
value = module.cluster.cluster_arn
}
output "cluster_iam_role_arn" {
value = module.cluster.cluster_iam_role_arn
}
output "aws_auth_configmap_yaml" {
value = module.cluster.aws_auth_configmap_yaml
}

11
terraform/aws/provider.tf Normal file
View file

@ -0,0 +1,11 @@
terraform {
required_version = ">= 0.13"
backend "s3" {
bucket = "eso-e2e-aws-tfstate"
key = "aws-tfstate"
region = "eu-west-1"
}
required_providers {}
}

View file

@ -0,0 +1,19 @@
variable "AWS_SA_NAME" {
type = string
default = "eso-e2e-test"
}
variable "AWS_SA_NAMESPACE" {
type = string
default = "default"
}
variable "AWS_REGION" {
type = string
default = "eu-west-1"
}
variable "AWS_CLUSTER_NAME" {
type = string
default = "eso-e2e-managed"
}

View file

@ -1,4 +1,5 @@
resource "google_service_account" "default" {
project = var.project_id
account_id = var.GCP_GSA_NAME
}
@ -27,6 +28,7 @@ resource "google_service_account_iam_member" "pod_identity_e2e" {
}
resource "google_container_cluster" "primary" {
project = var.project_id
name = "${var.env}-cluster"
location = var.zone
remove_default_node_pool = true
@ -43,6 +45,7 @@ resource "google_container_cluster" "primary" {
}
resource "google_container_node_pool" "nodes" {
project = var.project_id
name = "${google_container_cluster.primary.name}-node-pool"
location = google_container_cluster.primary.location
cluster = google_container_cluster.primary.name
@ -57,3 +60,20 @@ resource "google_container_node_pool" "nodes" {
]
}
}
provider "kubernetes" {
host = "https://${google_container_cluster.primary.endpoint}"
token = data.google_client_config.default.access_token
cluster_ca_certificate = base64decode(google_container_cluster.primary.master_auth.0.cluster_ca_certificate)
}
data "google_client_config" "default" {}
resource "kubernetes_service_account" "test" {
metadata {
name = var.GCP_KSA_NAME
annotations = {
"iam.gke.io/gcp-service-account" : "${var.GCP_GSA_NAME}@${var.project_id}.iam.gserviceaccount.com"
}
}
}

View file

@ -1,9 +1,11 @@
resource "google_compute_network" "env-vpc" {
project = var.project_id
name = "${var.env}-vpc"
auto_create_subnetworks = false
}
resource "google_compute_subnetwork" "env-subnet" {
project = var.project_id
name = "${google_compute_network.env-vpc.name}-subnet"
region = var.region
network = google_compute_network.env-vpc.name

View file

@ -13,3 +13,6 @@ variable "ip_service_range" {
variable "region" {
default = "europe-west1"
}
variable "project_id" {
type = string
}

View file

@ -11,6 +11,7 @@ module "test-network" {
env = var.env
region = var.region
ip_cidr_range = var.ip_cidr_range
project_id = var.project_id
}
module "test-cluster" {

View file

@ -5,6 +5,6 @@ package tools
import (
_ "github.com/ahmetb/gen-crd-api-reference-docs"
_ "github.com/onsi/ginkgo/ginkgo"
_ "github.com/onsi/ginkgo/v2/ginkgo"
_ "sigs.k8s.io/controller-tools/cmd/controller-gen"
)