From dee95349c570af263ce96d5f920c645af2a30c89 Mon Sep 17 00:00:00 2001 From: Christopher Swenson Date: Tue, 19 Jul 2022 14:51:42 -0700 Subject: [PATCH] Test against Vault Enterprise (#11) Test against Vault Enterprise We run the tests again but overwrite the `vault:dev` image with the Enterprise image and ensure that the license is loaded. --- .github/workflows/tests.yaml | 8 ++- CHANGELOG.md | 2 + Makefile | 33 +++++++++- go.mod | 2 +- integrationtest/creds_integration_test.go | 8 +++ integrationtest/integration_test.go | 74 +++++++++++++++++++++++ integrationtest/vault/Dockerfile | 6 ++ integrationtest/wal_rollback_test.go | 5 +- 8 files changed, 131 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index a35fa52..7201b3e 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -55,6 +55,8 @@ jobs: fail-fast: false matrix: kind-k8s-version: [1.21.12, 1.22.9, 1.23.6, 1.24.1] + enterprise: ["", "-ent"] + name: Integration test ${{ matrix.enterprise }} kind ${{ matrix.kind-k8s-version }} steps: - uses: actions/checkout@v2 - name: Create K8s Kind Cluster @@ -79,7 +81,9 @@ jobs: key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - - run: make setup-integration-test + - env: + VAULT_LICENSE_CI: ${{ secrets.VAULT_LICENSE_CI }} + run: make setup-integration-test${{ matrix.enterprise }} - env: INTEGRATION_TESTS: true - run: make integration-test TESTARGS="-v" + run: make integration-test TESTARGS="-v" \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index db692f1..a10e312 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## Unreleased +* Test against Vault Enterprise [[GH-11](https://github.com/hashicorp/vault-plugin-secrets-kubernetes/pull/11)] + ## 0.1.1 (May 26th, 2022) ### Changes diff --git a/Makefile b/Makefile index da412a1..32ca619 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,8 @@ KIND_K8S_VERSION?=v1.24.1 PKG=github.com/hashicorp/vault-plugin-secrets-kubernetes LDFLAGS?="-X '$(PKG).WALRollbackMinAge=10s'" +RUNNER_TEMP ?= $(TMPDIR) + .PHONY: default default: dev @@ -48,14 +50,28 @@ delete-kind: .PHONY: vault-image vault-image: - GOOS=linux GOARCH=amd64 make dev + GOOS=linux make dev docker build -f integrationtest/vault/Dockerfile bin/ --tag=hashicorp/vault:dev +.PHONY: vault-image-ent +vault-image-ent: + GOOS=linux make dev + docker build -f integrationtest/vault/Dockerfile --target enterprise bin/ --tag=hashicorp/vault:dev + # Create Vault inside the cluster with a locally-built version of kubernetes secrets. -.PHONY: setup-integration-test -setup-integration-test: teardown-integration-test vault-image +.PHONY: setup-integration-test-common +setup-integration-test-common: SET_LICENSE=$(if $(VAULT_LICENSE_CI),--set server.enterpriseLicense.secretName=vault-license) +setup-integration-test-common: teardown-integration-test kind --name ${KIND_CLUSTER_NAME} load docker-image hashicorp/vault:dev kubectl create namespace test + + # don't log the license + printenv VAULT_LICENSE_CI > $(RUNNER_TEMP)/vault-license.txt || true + if [ -s $(RUNNER_TEMP)/vault-license.txt ]; then \ + kubectl -n test create secret generic vault-license --from-file license=$(RUNNER_TEMP)/vault-license.txt; \ + rm -rf $(RUNNER_TEMP)/vault-license.txt; \ + fi + helm install vault vault --repo https://helm.releases.hashicorp.com --version=0.19.0 \ --wait --timeout=5m \ --namespace=test \ @@ -64,6 +80,7 @@ setup-integration-test: teardown-integration-test vault-image --set server.image.tag=dev \ --set server.image.pullPolicy=Never \ --set injector.enabled=false \ + $(SET_LICENSE) \ --set server.extraArgs="-dev-plugin-dir=/vault/plugin_directory" kubectl patch --namespace=test statefulset vault --patch-file integrationtest/vault/hostPortPatch.yaml kubectl apply --namespace=test -f integrationtest/vault/testRoles.yaml @@ -73,6 +90,16 @@ setup-integration-test: teardown-integration-test vault-image kubectl delete --namespace=test pod vault-0 kubectl wait --namespace=test --for=condition=Ready --timeout=5m pod -l app.kubernetes.io/name=vault +.PHONY: setup-integration-test +setup-integration-test: vault-image setup-integration-test-common + +.PHONY: setup-integration-test-ent +setup-integration-test-ent: check-license vault-image-ent setup-integration-test-common + +.PHONY: check-license +check-license: + (printenv VAULT_LICENSE_CI > /dev/null) || (echo "VAULT_LICENSE_CI must be set"; exit 1) + .PHONY: teardown-integration-test teardown-integration-test: helm uninstall vault --namespace=test || true diff --git a/go.mod b/go.mod index 017a2f9..20b411e 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,7 @@ require ( github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/go-uuid v1.0.2 // indirect - github.com/hashicorp/go-version v1.2.0 // indirect + github.com/hashicorp/go-version v1.2.0 github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect diff --git a/integrationtest/creds_integration_test.go b/integrationtest/creds_integration_test.go index 530a9f9..fd7cc0b 100644 --- a/integrationtest/creds_integration_test.go +++ b/integrationtest/creds_integration_test.go @@ -19,6 +19,8 @@ func TestCreds_ttl(t *testing.T) { path, umount := mountHelper(t, client) defer umount() + client, delNamespace := namespaceHelper(t, client) + defer delNamespace() // create default config _, err = client.Logical().Write(path+"/config", map[string]interface{}{}) @@ -129,6 +131,8 @@ func TestCreds_service_account_name(t *testing.T) { path, umount := mountHelper(t, client) defer umount() + client, delNamespace := namespaceHelper(t, client) + defer delNamespace() // create default config _, err = client.Logical().Write(path+"/config", map[string]interface{}{}) @@ -196,6 +200,8 @@ func TestCreds_kubernetes_role_name(t *testing.T) { path, umount := mountHelper(t, client) defer umount() + client, delNamespace := namespaceHelper(t, client) + defer delNamespace() // create default config _, err = client.Logical().Write(path+"/config", map[string]interface{}{}) @@ -276,6 +282,8 @@ func TestCreds_generated_role_rules(t *testing.T) { path, umount := mountHelper(t, client) defer umount() + client, delNamespace := namespaceHelper(t, client) + defer delNamespace() // create default config _, err = client.Logical().Write(path+"/config", map[string]interface{}{}) diff --git a/integrationtest/integration_test.go b/integrationtest/integration_test.go index 80dcb30..7a47809 100644 --- a/integrationtest/integration_test.go +++ b/integrationtest/integration_test.go @@ -76,6 +76,8 @@ func TestConfig(t *testing.T) { path, umount := mountHelper(t, client) defer umount() + client, delNamespace := namespaceHelper(t, client) + defer delNamespace() // create _, err = client.Logical().Write(path+"/config", map[string]interface{}{ @@ -126,6 +128,8 @@ func TestRole(t *testing.T) { path, umount := mountHelper(t, client) defer umount() + client, delNamespace := namespaceHelper(t, client) + defer delNamespace() // create default config _, err = client.Logical().Write(path+"/config", map[string]interface{}{}) @@ -191,6 +195,42 @@ func TestRole(t *testing.T) { assert.Nil(t, result) } +func isEnterprise(client *api.Client) bool { + req := client.NewRequest("GET", "/v1/sys/license/status") + resp, err := client.RawRequest(req) + if err != nil { + return false + } + return resp.StatusCode == 200 +} + +func createNamespace(client *api.Client, namespace string) error { + req := client.NewRequest("PUT", "/v1/sys/namespaces/"+namespace) + resp, err := client.RawRequest(req) + if err != nil { + return err + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return fmt.Errorf("error creating namespace. Server returned status %d %s", resp.StatusCode, resp.Status) + } + return nil +} + +func deleteNamespace(client *api.Client, namespace string) error { + req := client.NewRequest("DELETE", "/v1/sys/namespaces/"+namespace) + resp, err := client.RawRequest(req) + if err != nil { + return err + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return fmt.Errorf("error creating namespace. Server returned status %d %s", resp.StatusCode, resp.Status) + } + return nil +} + +// mountHelper creates the kubernetes mount. func mountHelper(t *testing.T, client *api.Client) (string, func()) { t.Helper() @@ -211,6 +251,40 @@ func mountHelper(t *testing.T, client *api.Client) (string, func()) { } } +// namespaceHelper creates a Vault Enterprise namespace and returns a client with the namespace changed to it. +func namespaceHelper(t *testing.T, client *api.Client) (*api.Client, func()) { + t.Helper() + + var err error + namespace := "" + newClient := client + + if isEnterprise(client) { + namespace := randomWithPrefix("somenamespace") + if err != nil { + t.Fatal(err) + } + err = createNamespace(client, namespace) + if err != nil { + t.Fatal(err) + } + newClient, err := client.Clone() + if err != nil { + t.Fatal(err) + } + newClient.SetNamespace(namespace) + } + + return newClient, func() { + if namespace != "" { + err = deleteNamespace(client, namespace) + if err != nil { + t.Fatal(err) + } + } + } +} + const sampleRules = `rules: - apiGroups: [""] resources: ["pods"] diff --git a/integrationtest/vault/Dockerfile b/integrationtest/vault/Dockerfile index e2f0ab7..f26cdd0 100644 --- a/integrationtest/vault/Dockerfile +++ b/integrationtest/vault/Dockerfile @@ -1,3 +1,9 @@ +FROM docker.mirror.hashicorp.services/hashicorp/vault-enterprise:1.10.3-ent as enterprise + +# Don't use `kubernetes` as plugin name to ensure we don't silently fall back to +# the built-in kubernetes secrets plugin if something goes wrong. +COPY --chown=vault:vault vault-plugin-secrets-kubernetes /vault/plugin_directory/kubernetes-dev + FROM docker.mirror.hashicorp.services/hashicorp/vault:1.10.3 # Don't use `kubernetes` as plugin name to ensure we don't silently fall back to diff --git a/integrationtest/wal_rollback_test.go b/integrationtest/wal_rollback_test.go index 326f6af..f530b24 100644 --- a/integrationtest/wal_rollback_test.go +++ b/integrationtest/wal_rollback_test.go @@ -23,11 +23,14 @@ func TestCreds_wal_rollback(t *testing.T) { } // Pick up VAULT_ADDR and VAULT_TOKEN from env vars - client, err := api.NewClient(nil) + baseClient, err := api.NewClient(nil) if err != nil { t.Fatal(err) } + client, delNamespace := namespaceHelper(t, baseClient) + defer delNamespace() + t.Run("generated_role_rules", func(t *testing.T) { t.Parallel() mountPath, umount := mountHelper(t, client)