From 8b47f89c761f876f5e33e224b0537f30c5fffa67 Mon Sep 17 00:00:00 2001 From: Ramon Quitales Date: Fri, 8 Mar 2024 15:37:52 -0800 Subject: [PATCH 1/5] ci: use kind clusters for PR status checks --- .github/workflows/run-acceptance-tests.yml | 136 ++------------------- 1 file changed, 7 insertions(+), 129 deletions(-) diff --git a/.github/workflows/run-acceptance-tests.yml b/.github/workflows/run-acceptance-tests.yml index 6551f9ae8f..de1be250b7 100644 --- a/.github/workflows/run-acceptance-tests.yml +++ b/.github/workflows/run-acceptance-tests.yml @@ -251,9 +251,8 @@ jobs: runs-on: pulumi-ubuntu-8core needs: - build_sdks - - build-test-cluster strategy: - fail-fast: true + fail-fast: false # Surface all test failures matrix: language: - nodejs @@ -341,13 +340,6 @@ jobs: pip3 install pipenv - name: Install dependencies run: make install_${{ matrix.language}}_sdk - - name: Make Kube Directory - run: mkdir -p "~/.kube/" - - name: Download Kubeconfig - uses: actions/download-artifact@v4 - with: - name: config - path: ~/.kube/ - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: @@ -390,9 +382,14 @@ jobs: with: version: v2.5.0 token: ${{ secrets.GITHUB_TOKEN }} + - name: Setup KinD cluster + uses: helm/kind-action@v1 + with: + cluster_name: kind-integration-tests-${{ matrix.language }} + node_image: kindest/node:v1.29.2 - name: Run tests run: cd tests/sdk/${{ matrix.language }} && go test -v -count=1 -cover -timeout - 2h -parallel 4 ./... + 2h -parallel 4 -short ./... - if: failure() && github.event_name == 'push' name: Notify Slack uses: 8398a7/action-slack@v3 @@ -413,125 +410,6 @@ jobs: needs: - test - lint - - destroy-test-cluster - build-test-cluster: - runs-on: ubuntu-latest - name: build-test-cluster - outputs: - stack-name: ${{ steps.stackname.outputs.stack-name }} - permissions: - contents: read - id-token: write - steps: - - name: Checkout Repo - uses: actions/checkout@v4 - with: - lfs: true - ref: ${{ env.PR_COMMIT_SHA }} - - name: Install Go - uses: actions/setup-go@v5 - with: - go-version: ${{ env.GOVERSION }} - - name: Install Pulumi CLI - uses: pulumi/actions@v5 - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODEVERSION }} - registry-url: https://registry.npmjs.org - - name: Authenticate to Google Cloud - uses: google-github-actions/auth@v0 - with: - workload_identity_provider: projects/${{ env.GOOGLE_PROJECT_NUMBER - }}/locations/global/workloadIdentityPools/${{ - env.GOOGLE_CI_WORKLOAD_IDENTITY_POOL }}/providers/${{ - env.GOOGLE_CI_WORKLOAD_IDENTITY_PROVIDER }} - service_account: ${{ env.GOOGLE_CI_SERVICE_ACCOUNT_EMAIL }} - - name: Setup gcloud auth - uses: google-github-actions/setup-gcloud@v2 - with: - install_components: gke-gcloud-auth-plugin - - name: Install Kubectl - run: > - curl -LO - https://storage.googleapis.com/kubernetes-release/release/$(curl -s - https://storage.googleapis.com/kubernetes-release/release/stable-1.28.txt)/bin/linux/amd64/kubectl - - chmod +x ./kubectl - - sudo mv kubectl /usr/local/bin - - name: Login to Google Cloud Registry - run: gcloud --quiet auth configure-docker - - name: Set stack name in output - id: stackname - run: echo 'stack-name=${{ env.PULUMI_TEST_OWNER }}/${{ github.sha }}-${{ - github.run_id }}-${{ github.run_attempt }}' >> "$GITHUB_OUTPUT" - - name: Create test infrastructure - run: ./scripts/ci-cluster-create.sh ${{ steps.stackname.outputs.stack-name }} - - name: Upload Kubernetes Artifacts - uses: actions/upload-artifact@v4 - with: - name: config - path: ~/.kube/config - if: github.event_name == 'repository_dispatch' || - github.event.pull_request.head.repo.full_name == github.repository - destroy-test-cluster: - runs-on: ubuntu-latest - name: teardown-test-cluster - needs: - - build-test-cluster - - test - if: ${{ always() }} && github.event.pull_request.head.repo.full_name == - github.repository - permissions: - contents: read - id-token: write - steps: - - name: Checkout Repo - uses: actions/checkout@v4 - with: - lfs: true - ref: ${{ env.PR_COMMIT_SHA }} - - name: Install Go - uses: actions/setup-go@v5 - with: - go-version: ${{ env.GOVERSION }} - - name: Install Pulumi CLI - uses: pulumi/actions@v5 - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODEVERSION }} - registry-url: https://registry.npmjs.org - - name: Authenticate to Google Cloud - uses: google-github-actions/auth@v0 - with: - workload_identity_provider: projects/${{ env.GOOGLE_PROJECT_NUMBER - }}/locations/global/workloadIdentityPools/${{ - env.GOOGLE_CI_WORKLOAD_IDENTITY_POOL }}/providers/${{ - env.GOOGLE_CI_WORKLOAD_IDENTITY_PROVIDER }} - service_account: ${{ env.GOOGLE_CI_SERVICE_ACCOUNT_EMAIL }} - - name: Setup gcloud auth - uses: google-github-actions/setup-gcloud@v2 - with: - install_components: gke-gcloud-auth-plugin - - name: Install Kubectl - run: > - curl -LO - https://storage.googleapis.com/kubernetes-release/release/$(curl -s - https://storage.googleapis.com/kubernetes-release/release/stable-1.28.txt)/bin/linux/amd64/kubectl - - chmod +x ./kubectl - - sudo mv kubectl /usr/local/bin - - name: Login to Google Cloud Registry - run: gcloud --quiet auth configure-docker - - name: Destroy test infra - run: ./scripts/ci-cluster-destroy.sh ${{ - needs.build-test-cluster.outputs.stack-name }} - - uses: geekyeggo/delete-artifact@v1 - with: - name: config lint: runs-on: ubuntu-latest steps: From e2921f2780b36f5c4cdaaedc9194513f7f71c6b8 Mon Sep 17 00:00:00 2001 From: Ramon Quitales Date: Fri, 8 Mar 2024 18:09:15 -0800 Subject: [PATCH 2/5] tests: update simple tests to be compatible with kind --- tests/sdk/dotnet/dotnet_test.go | 1 + .../dotnet/kustomize/helloWorld/kustomization.yaml | 1 - tests/sdk/dotnet/kustomize/helloWorld/service.yaml | 12 ------------ tests/sdk/go/kustomize/helloWorld/kustomization.yaml | 1 - tests/sdk/go/kustomize/helloWorld/service.yaml | 12 ------------ tests/sdk/nodejs/nodejs_test.go | 5 +++++ tests/sdk/nodejs/provider/step1/index.ts | 6 ++++-- .../helloWorld/kustomization.yaml | 1 - .../helloWorld/service.yaml | 12 ------------ .../python/kustomize/helloWorld/kustomization.yaml | 1 - tests/sdk/python/kustomize/helloWorld/service.yaml | 12 ------------ 11 files changed, 10 insertions(+), 54 deletions(-) delete mode 100644 tests/sdk/dotnet/kustomize/helloWorld/service.yaml delete mode 100644 tests/sdk/go/kustomize/helloWorld/service.yaml delete mode 100644 tests/sdk/python/kustomize-unconfigured-provider/helloWorld/service.yaml delete mode 100644 tests/sdk/python/kustomize/helloWorld/service.yaml diff --git a/tests/sdk/dotnet/dotnet_test.go b/tests/sdk/dotnet/dotnet_test.go index 77c2c831a1..b1f93e0cf2 100644 --- a/tests/sdk/dotnet/dotnet_test.go +++ b/tests/sdk/dotnet/dotnet_test.go @@ -66,6 +66,7 @@ func TestDotnet_Basic(t *testing.T) { } func TestDotnet_Guestbook(t *testing.T) { + tests.SkipIfShort(t) // An external load balancer is required. test := baseOptions.With(integration.ProgramTestOptions{ Dir: "guestbook", Quick: true, diff --git a/tests/sdk/dotnet/kustomize/helloWorld/kustomization.yaml b/tests/sdk/dotnet/kustomize/helloWorld/kustomization.yaml index 41965951e8..882d52bfec 100644 --- a/tests/sdk/dotnet/kustomize/helloWorld/kustomization.yaml +++ b/tests/sdk/dotnet/kustomize/helloWorld/kustomization.yaml @@ -5,5 +5,4 @@ commonLabels: resources: - deployment.yaml -- service.yaml - configMap.yaml diff --git a/tests/sdk/dotnet/kustomize/helloWorld/service.yaml b/tests/sdk/dotnet/kustomize/helloWorld/service.yaml deleted file mode 100644 index e238f70021..0000000000 --- a/tests/sdk/dotnet/kustomize/helloWorld/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: the-service -spec: - selector: - deployment: hello - type: LoadBalancer - ports: - - protocol: TCP - port: 8666 - targetPort: 8080 diff --git a/tests/sdk/go/kustomize/helloWorld/kustomization.yaml b/tests/sdk/go/kustomize/helloWorld/kustomization.yaml index 41965951e8..882d52bfec 100644 --- a/tests/sdk/go/kustomize/helloWorld/kustomization.yaml +++ b/tests/sdk/go/kustomize/helloWorld/kustomization.yaml @@ -5,5 +5,4 @@ commonLabels: resources: - deployment.yaml -- service.yaml - configMap.yaml diff --git a/tests/sdk/go/kustomize/helloWorld/service.yaml b/tests/sdk/go/kustomize/helloWorld/service.yaml deleted file mode 100644 index e238f70021..0000000000 --- a/tests/sdk/go/kustomize/helloWorld/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: the-service -spec: - selector: - deployment: hello - type: LoadBalancer - ports: - - protocol: TCP - port: 8666 - targetPort: 8080 diff --git a/tests/sdk/nodejs/nodejs_test.go b/tests/sdk/nodejs/nodejs_test.go index 68c5298890..642c5b8951 100644 --- a/tests/sdk/nodejs/nodejs_test.go +++ b/tests/sdk/nodejs/nodejs_test.go @@ -453,6 +453,7 @@ func TestCRDs(t *testing.T) { } func TestPod(t *testing.T) { + tests.SkipIfShort(t) // statuses are different in Kind cluster test := baseOptions.With(integration.ProgramTestOptions{ Dir: filepath.Join("delete-before-replace", "step1"), Quick: true, @@ -794,6 +795,7 @@ func TestGet(t *testing.T) { } func TestIstio(t *testing.T) { + tests.SkipIfShort(t) test := baseOptions.With(integration.ProgramTestOptions{ Dir: filepath.Join("istio", "step1"), Quick: true, @@ -1153,6 +1155,7 @@ func TestRenderYAML(t *testing.T) { assert.Equal(t, len(files), 2) }, }) + integration.ProgramTest(t, &test) } @@ -1954,6 +1957,7 @@ func TestClientSideDriftCorrectSSA(t *testing.T) { // 5. Re-enable access to the unreachable cluster and run `pulumi up` again. // 6. Validate that the resource in the unreachable cluster was updated. func TestSkipUpdateUnreachableFlag(t *testing.T) { + tests.SkipIfShort(t) var ns0, ns1, cm0, cm1 string test := baseOptions.With(integration.ProgramTestOptions{ @@ -2264,6 +2268,7 @@ func TestEmptyItemNormalization(t *testing.T) { // 3. Update the DeploymentPatch resource to further patch the deployment, setting the image to nginx:1.14.0, // and verify that other fields managed by kubectl-client-side-apply remain unaffected (Step 2). func TestFieldManagerPatchResources(t *testing.T) { + tests.SkipIfShort(t) testFolder := "field-manager-patch-resources" createDeployment := func() string { diff --git a/tests/sdk/nodejs/provider/step1/index.ts b/tests/sdk/nodejs/provider/step1/index.ts index a73015f8d8..cede2f645d 100644 --- a/tests/sdk/nodejs/provider/step1/index.ts +++ b/tests/sdk/nodejs/provider/step1/index.ts @@ -20,15 +20,17 @@ import * as path from "path"; const ns1 = new k8s.core.v1.Namespace("ns1"); const ns2 = new k8s.core.v1.Namespace("ns2"); +const kcfg = process.env.KUBECONFIG|| path.join(os.homedir(), ".kube", "config"); + // Create a new provider using the contents of a k8s config. const kubeconfigContentsProvider = new k8s.Provider("kubeconfigContentsProvider", { - kubeconfig: fs.readFileSync(path.join(os.homedir(), ".kube", "config")).toString(), + kubeconfig: fs.readFileSync(kcfg).toString(), namespace: ns1.metadata.name, }); // Create a new provider using the path to a k8s config. const kubeconfigPathProvider = new k8s.Provider("kubeconfigPathProvider", { - kubeconfig: "~/.kube/config", + kubeconfig: kcfg, namespace: ns1.metadata.name, }); diff --git a/tests/sdk/python/kustomize-unconfigured-provider/helloWorld/kustomization.yaml b/tests/sdk/python/kustomize-unconfigured-provider/helloWorld/kustomization.yaml index 41965951e8..882d52bfec 100644 --- a/tests/sdk/python/kustomize-unconfigured-provider/helloWorld/kustomization.yaml +++ b/tests/sdk/python/kustomize-unconfigured-provider/helloWorld/kustomization.yaml @@ -5,5 +5,4 @@ commonLabels: resources: - deployment.yaml -- service.yaml - configMap.yaml diff --git a/tests/sdk/python/kustomize-unconfigured-provider/helloWorld/service.yaml b/tests/sdk/python/kustomize-unconfigured-provider/helloWorld/service.yaml deleted file mode 100644 index e238f70021..0000000000 --- a/tests/sdk/python/kustomize-unconfigured-provider/helloWorld/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: the-service -spec: - selector: - deployment: hello - type: LoadBalancer - ports: - - protocol: TCP - port: 8666 - targetPort: 8080 diff --git a/tests/sdk/python/kustomize/helloWorld/kustomization.yaml b/tests/sdk/python/kustomize/helloWorld/kustomization.yaml index 41965951e8..882d52bfec 100644 --- a/tests/sdk/python/kustomize/helloWorld/kustomization.yaml +++ b/tests/sdk/python/kustomize/helloWorld/kustomization.yaml @@ -5,5 +5,4 @@ commonLabels: resources: - deployment.yaml -- service.yaml - configMap.yaml diff --git a/tests/sdk/python/kustomize/helloWorld/service.yaml b/tests/sdk/python/kustomize/helloWorld/service.yaml deleted file mode 100644 index e238f70021..0000000000 --- a/tests/sdk/python/kustomize/helloWorld/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: the-service -spec: - selector: - deployment: hello - type: LoadBalancer - ports: - - protocol: TCP - port: 8666 - targetPort: 8080 From 440694635d8928600d69ceb12422453bb1a9520c Mon Sep 17 00:00:00 2001 From: Ramon Quitales Date: Mon, 11 Mar 2024 18:21:24 -0700 Subject: [PATCH 3/5] chore: add message to explain why tests are skipped in short mode --- tests/sdk/dotnet/dotnet_test.go | 2 +- tests/sdk/nodejs/examples/examples_test.go | 21 ++++++++------------- tests/sdk/nodejs/nodejs_test.go | 10 +++------- tests/sdk/nodejs/preview_test.go | 1 + tests/util.go | 4 ++-- 5 files changed, 15 insertions(+), 23 deletions(-) diff --git a/tests/sdk/dotnet/dotnet_test.go b/tests/sdk/dotnet/dotnet_test.go index b1f93e0cf2..b41f86c04c 100644 --- a/tests/sdk/dotnet/dotnet_test.go +++ b/tests/sdk/dotnet/dotnet_test.go @@ -66,7 +66,7 @@ func TestDotnet_Basic(t *testing.T) { } func TestDotnet_Guestbook(t *testing.T) { - tests.SkipIfShort(t) // An external load balancer is required. + tests.SkipIfShort(t, "test creates a load balancer and requires a Cloud cluster") test := baseOptions.With(integration.ProgramTestOptions{ Dir: "guestbook", Quick: true, diff --git a/tests/sdk/nodejs/examples/examples_test.go b/tests/sdk/nodejs/examples/examples_test.go index 6dd6327ca5..b50c0631c4 100644 --- a/tests/sdk/nodejs/examples/examples_test.go +++ b/tests/sdk/nodejs/examples/examples_test.go @@ -43,7 +43,7 @@ func TestAccMinimal(t *testing.T) { } func TestAccGuestbook(t *testing.T) { - tests.SkipIfShort(t) + tests.SkipIfShort(t, "test provisions a load balancer and requires a cloud provider cluster to run") test := getBaseOptions(t). With(integration.ProgramTestOptions{ Dir: filepath.Join(getCwd(t), "guestbook"), @@ -134,7 +134,7 @@ func TestAccGuestbook(t *testing.T) { } func TestAccIngress(t *testing.T) { - tests.SkipIfShort(t) + tests.SkipIfShort(t, "test provisions a load balancer and requires a cloud provider cluster to run") testNetworkingV1 := getBaseOptions(t). With(integration.ProgramTestOptions{ Dir: filepath.Join(getCwd(t), "ingress"), @@ -164,7 +164,7 @@ func TestAccIngress(t *testing.T) { } func TestAccHelm(t *testing.T) { - tests.SkipIfShort(t) + tests.SkipIfShort(t, "test provisions a load balancer and requires a cloud provider cluster to run") test := getBaseOptions(t). With(integration.ProgramTestOptions{ Dir: filepath.Join(getCwd(t), "helm", "step1"), @@ -190,7 +190,6 @@ func TestAccHelm(t *testing.T) { } func TestHelmNoDefaultProvider(t *testing.T) { - tests.SkipIfShort(t) test := getBaseOptions(t). With(integration.ProgramTestOptions{ Dir: filepath.Join(getCwd(t), "helm-no-default-provider"), @@ -204,7 +203,6 @@ func TestHelmNoDefaultProvider(t *testing.T) { } func TestAccHelmApiVersions(t *testing.T) { - tests.SkipIfShort(t) test := getBaseOptions(t). With(integration.ProgramTestOptions{ Dir: filepath.Join(getCwd(t), "helm-api-versions", "step1"), @@ -221,7 +219,6 @@ func TestAccHelmApiVersions(t *testing.T) { } func TestAccHelmKubeVersion(t *testing.T) { - tests.SkipIfShort(t) test := getBaseOptions(t). With(integration.ProgramTestOptions{ Dir: filepath.Join(getCwd(t), "helm-kube-version", "step1"), @@ -263,7 +260,7 @@ func TestAccHelmAllowCRDRendering(t *testing.T) { } func TestAccHelmLocal(t *testing.T) { - tests.SkipIfShort(t) + tests.SkipIfShort(t, "test provisions a load balancer and requires a cloud provider cluster to run") test := getBaseOptions(t). With(integration.ProgramTestOptions{ Dir: filepath.Join(getCwd(t), "helm-local", "step1"), @@ -307,7 +304,7 @@ func TestAccHelmLocal(t *testing.T) { } func TestAccPrometheusOperator(t *testing.T) { - tests.SkipIfShort(t) + tests.SkipIfShort(t, "test provisions a load balancer and requires a cloud provider cluster to run") test := getBaseOptions(t). With(integration.ProgramTestOptions{ Dir: filepath.Join(getCwd(t), "prometheus-operator"), @@ -354,7 +351,6 @@ func TestAccPrometheusOperator(t *testing.T) { //} func TestAccProvider(t *testing.T) { - tests.SkipIfShort(t) test := getBaseOptions(t). With(integration.ProgramTestOptions{ Dir: filepath.Join(getCwd(t), "provider"), @@ -364,7 +360,7 @@ func TestAccProvider(t *testing.T) { } func TestHelmRelease(t *testing.T) { - tests.SkipIfShort(t) + tests.SkipIfShort(t, "test provisions a load balancer and requires a cloud provider cluster to run") validationFunc := func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { assert.NotEmpty(t, stackInfo.Outputs["redisMasterClusterIP"].(string)) assert.Equal(t, stackInfo.Outputs["status"], "deployed") @@ -427,7 +423,6 @@ func TestHelmRelease(t *testing.T) { func TestHelmReleaseCRD(t *testing.T) { // Validate that Helm charts with CRDs work across create/update/refresh/delete cycles. // https://github.com/pulumi/pulumi-kubernetes/issues/1712 - tests.SkipIfShort(t) test := getBaseOptions(t). With(integration.ProgramTestOptions{ Dir: filepath.Join(getCwd(t), "helm-release-crd", "step1"), @@ -447,7 +442,7 @@ func TestHelmReleaseCRD(t *testing.T) { func TestHelmReleaseNamespace(t *testing.T) { // Validate fix for https://github.com/pulumi/pulumi-kubernetes/issues/1710 - tests.SkipIfShort(t) + tests.SkipIfShort(t, "test provisions a load balancer and requires a cloud provider cluster to run") test := getBaseOptions(t). With(integration.ProgramTestOptions{ Dir: filepath.Join(getCwd(t), "helm-release-namespace", "step1"), @@ -517,7 +512,7 @@ func TestHelmReleaseRedis(t *testing.T) { } // Validate fix for https://github.com/pulumi/pulumi-kubernetes/issues/1933 - tests.SkipIfShort(t) + tests.SkipIfShort(t, "test provisions a load balancer and requires a cloud provider cluster to run") test := getBaseOptions(t). With(integration.ProgramTestOptions{ Dir: filepath.Join(getCwd(t), "helm-release-redis", "step1"), diff --git a/tests/sdk/nodejs/nodejs_test.go b/tests/sdk/nodejs/nodejs_test.go index 642c5b8951..6e8a8fdcb5 100644 --- a/tests/sdk/nodejs/nodejs_test.go +++ b/tests/sdk/nodejs/nodejs_test.go @@ -453,7 +453,6 @@ func TestCRDs(t *testing.T) { } func TestPod(t *testing.T) { - tests.SkipIfShort(t) // statuses are different in Kind cluster test := baseOptions.With(integration.ProgramTestOptions{ Dir: filepath.Join("delete-before-replace", "step1"), Quick: true, @@ -489,7 +488,7 @@ func TestPod(t *testing.T) { conditions, _ := openapi.Pluck(pod.Outputs, "status", "conditions") ready := conditions.([]any)[1].(map[string]any) readyType := ready["type"] - assert.Equal(t, "Ready", readyType) + assert.Equal(t, "Ready", readyType, conditions) readyStatus := ready["status"] assert.Equal(t, "True", readyStatus) @@ -795,7 +794,7 @@ func TestGet(t *testing.T) { } func TestIstio(t *testing.T) { - tests.SkipIfShort(t) + tests.SkipIfShort(t, "test provisions a load balancer and requires a cloud provider cluster to run") test := baseOptions.With(integration.ProgramTestOptions{ Dir: filepath.Join("istio", "step1"), Quick: true, @@ -1957,7 +1956,6 @@ func TestClientSideDriftCorrectSSA(t *testing.T) { // 5. Re-enable access to the unreachable cluster and run `pulumi up` again. // 6. Validate that the resource in the unreachable cluster was updated. func TestSkipUpdateUnreachableFlag(t *testing.T) { - tests.SkipIfShort(t) var ns0, ns1, cm0, cm1 string test := baseOptions.With(integration.ProgramTestOptions{ @@ -2203,8 +2201,7 @@ func ignoreChageTest(t *testing.T, testFolderName string) { // and has a controller backing it. We create 2 pods to test egress between them, rather than hitting // a live URL, to avoid flakiness. func TestEmptyItemNormalization(t *testing.T) { - tests.SkipIfShort(t) - + tests.SkipIfShort(t, "test requires a cluster with NetworkPolicy support") validateProgram := func(networkingEnabled bool) func(*testing.T, integration.RuntimeValidationStackInfo) { return func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { ns, ok := stackInfo.Outputs["podANamespace"].(string) @@ -2268,7 +2265,6 @@ func TestEmptyItemNormalization(t *testing.T) { // 3. Update the DeploymentPatch resource to further patch the deployment, setting the image to nginx:1.14.0, // and verify that other fields managed by kubectl-client-side-apply remain unaffected (Step 2). func TestFieldManagerPatchResources(t *testing.T) { - tests.SkipIfShort(t) testFolder := "field-manager-patch-resources" createDeployment := func() string { diff --git a/tests/sdk/nodejs/preview_test.go b/tests/sdk/nodejs/preview_test.go index 453032932f..5e2aaa20cb 100644 --- a/tests/sdk/nodejs/preview_test.go +++ b/tests/sdk/nodejs/preview_test.go @@ -148,6 +148,7 @@ func createSAKubeconfig(t *testing.T, saName string) (string, error) { // TestPreviewWithApply tests the `pulumi preview` CUJ where the user Pulumi program contains an Apply call on status subresoruces. // This is to ensure we don't fail preview, since status fields are only populated after the resource is created on cluster. func TestPreviewWithApply(t *testing.T) { + tests.SkipIfShort(t, "test requires a load balancer and won't work on kind clusters") var externalIP, nsName, svcName string test := baseOptions.With(integration.ProgramTestOptions{ Dir: "preview-apply", diff --git a/tests/util.go b/tests/util.go index 88eef3c5e4..c82a89277f 100644 --- a/tests/util.go +++ b/tests/util.go @@ -15,9 +15,9 @@ import ( ) // SkipIfShort skips the test if the -short flag is passed to `go test`. -func SkipIfShort(t *testing.T) { +func SkipIfShort(t *testing.T, msg string) { if testing.Short() { - t.Skip("skipping long-running test in short mode") + t.Skip("skipping", t.Name(), "in short mode:", msg) } } From 9af77fb3f8667349241fb94b3572bab5483c7e39 Mon Sep 17 00:00:00 2001 From: Ramon Quitales Date: Mon, 11 Mar 2024 18:25:53 -0700 Subject: [PATCH 4/5] tests: fix TestPod to search conditions instead of hardcoding index positions --- tests/sdk/nodejs/nodejs_test.go | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/tests/sdk/nodejs/nodejs_test.go b/tests/sdk/nodejs/nodejs_test.go index 6e8a8fdcb5..4c7a18e37a 100644 --- a/tests/sdk/nodejs/nodejs_test.go +++ b/tests/sdk/nodejs/nodejs_test.go @@ -453,6 +453,19 @@ func TestCRDs(t *testing.T) { } func TestPod(t *testing.T) { + getCondition := func(conditions []any, conditionType string) map[string]any { + // Order of conditions is not guaranteed, so we need to search for the condition of the given type. + for _, condition := range conditions { + conditionMap, ok := condition.(map[string]any) + require.True(t, ok, "condition items should be maps") + if conditionMap["type"] == conditionType { + return conditionMap + } + } + t.Fatalf("condition of type %s not found", conditionType) + return nil + } + test := baseOptions.With(integration.ProgramTestOptions{ Dir: filepath.Join("delete-before-replace", "step1"), Quick: true, @@ -486,9 +499,7 @@ func TestPod(t *testing.T) { // Status "Ready" is "True". conditions, _ := openapi.Pluck(pod.Outputs, "status", "conditions") - ready := conditions.([]any)[1].(map[string]any) - readyType := ready["type"] - assert.Equal(t, "Ready", readyType, conditions) + ready := getCondition(conditions.([]any), "Ready") readyStatus := ready["status"] assert.Equal(t, "True", readyStatus) @@ -539,9 +550,7 @@ func TestPod(t *testing.T) { // Status "Ready" is "True". conditions, _ := openapi.Pluck(pod.Outputs, "status", "conditions") - ready := conditions.([]any)[1].(map[string]any) - readyType := ready["type"] - assert.Equal(t, "Ready", readyType) + ready := getCondition(conditions.([]any), "Ready") readyStatus := ready["status"] assert.Equal(t, "True", readyStatus) From 6f8196de3729830e6a61f8e37c4a4b01c80a7c22 Mon Sep 17 00:00:00 2001 From: Ramon Quitales Date: Tue, 12 Mar 2024 13:22:46 -0700 Subject: [PATCH 5/5] tests: break out Golang integration tests to run concurrently --- tests/sdk/go/go_test.go | 1283 +++++++++++++++++++-------------------- 1 file changed, 640 insertions(+), 643 deletions(-) diff --git a/tests/sdk/go/go_test.go b/tests/sdk/go/go_test.go index 9ed1533c4b..6051ac92b1 100644 --- a/tests/sdk/go/go_test.go +++ b/tests/sdk/go/go_test.go @@ -45,6 +45,15 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) +func getCwd(t *testing.T) string { + cwd, err := os.Getwd() + if err != nil { + t.FailNow() + } + + return cwd +} + var baseOptions = &integration.ProgramTestOptions{ Verbose: true, Dependencies: []string{ @@ -59,755 +68,748 @@ var baseOptions = &integration.ProgramTestOptions{ }, } -// TestGo runs Go SDK tests sequentially to avoid OOM errors in CI -func TestGo(t *testing.T) { - cwd, err := os.Getwd() - if !assert.NoError(t, err) { - t.FailNow() +func TestBasic(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "basic"), + ExpectRefreshChanges: true, + Quick: true, + }) + integration.ProgramTest(t, &options) +} + +func TestYaml(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "yaml"), + Quick: true, + ExpectRefreshChanges: true, + OrderedConfig: []integration.ConfigValue{ + { + Key: "pulumi:disable-default-providers[0]", + Value: "kubernetes", + Path: true, + }, + }, + }) + integration.ProgramTest(t, &options) +} + +func TestHelmLocal(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "helm-local", "step1"), + Quick: true, + ExpectRefreshChanges: true, + ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { + // Verify resource creation order using the Event stream. The Chart resources must be created + // first, followed by the dependent ConfigMap. (The ConfigMap doesn't actually need the Chart, but + // it creates almost instantly, so it's a good choice to test creation ordering) + cmRegex := regexp.MustCompile(`ConfigMap::nginx-server-block`) + svcRegex := regexp.MustCompile(`Service::nginx`) + deployRegex := regexp.MustCompile(`Deployment::nginx`) + dependentRegex := regexp.MustCompile(`ConfigMap::foo`) + + var configmapFound, serviceFound, deploymentFound, dependentFound bool + for _, e := range stackInfo.Events { + if e.ResOutputsEvent != nil { + switch { + case cmRegex.MatchString(e.ResOutputsEvent.Metadata.URN): + configmapFound = true + case svcRegex.MatchString(e.ResOutputsEvent.Metadata.URN): + serviceFound = true + case deployRegex.MatchString(e.ResOutputsEvent.Metadata.URN): + deploymentFound = true + case dependentRegex.MatchString(e.ResOutputsEvent.Metadata.URN): + dependentFound = true + } + assert.Falsef(t, dependentFound && !(configmapFound && serviceFound && deploymentFound), + "dependent ConfigMap created before all chart resources were ready") + fmt.Println(e.ResOutputsEvent.Metadata.URN) + } + } + }, + }) + integration.ProgramTest(t, &options) +} + +func TestHelmReleaseImport(t *testing.T) { + + chart := bitnamiNginxChart + chartVersion := bitnamiNginxChart.Versions[0] + + // Run a program test for each of the various ways to import a Helm chart. + type runOptions struct { + InstallHelmRepository bool } - t.Run("Basic", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "basic"), - ExpectRefreshChanges: true, - Quick: true, + run := func(t *testing.T, options integration.ProgramTestOptions, opts runOptions) { + // create a Helm environment with a chart repository + var repos []repo.Entry + if opts.InstallHelmRepository { + repos = append(repos, chart.HelmRepo) + } + he, cleanup, err := createHelmEnvironment(t, repos...) + require.NoError(t, err, "failed to create Helm environment") + t.Cleanup(func() { + contract.IgnoreError(cleanup()) }) + + // pre-install the Helm chart to be imported + namespace := getRandomNamespace("importtest") + chartPath := filepath.Join(getCwd(t), chart.TestPath) + require.NoError(t, createRelease("mynginx", namespace, chartPath, true)) + t.Cleanup(func() { + contract.IgnoreError(deleteRelease("mynginx", namespace)) + }) + + // Import a Helm release using the `import` option on the `helm.Release` resource. + // The program inputs MUST exactly match the provider-generated inputs, + // or Pulumi will report: "error: inputs to import do not match the existing resource". + hValues, _ := json.Marshal(chartVersion.Values) + successCriteria := func(t *testing.T, stack integration.RuntimeValidationStackInfo) { + assert.NotEmpty(t, stack.Outputs["svc_ip"]) + assert.NotEmpty(t, stack.Outputs["resourceNames"]) + } + options = options.With(integration.ProgramTestOptions{ + Config: map[string]string{ + "namespace": namespace, + "name": "mynginx", + "values": string(hValues), + "import-id": fmt.Sprintf("%s/%s", namespace, "mynginx"), + }, + Env: he.EnvVars(), + Quick: true, + ExpectRefreshChanges: true, + ExtraRuntimeValidation: successCriteria, + NoParallel: true, + DestroyOnCleanup: true, + }) + integration.ProgramTest(t, &options) + } + + // 1. Import by searching the local chart repositories for a matching chart. + t.Run("chart reference", func(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "helm-release-import", "step1-remote"), + Config: map[string]string{ + "chart": chart.ChartReference(), // bitnami/nginx + "version": chartVersion.Version, // 15.3.4 + }, + }) + run(t, options, runOptions{ + InstallHelmRepository: true, + }) }) - t.Run("YAML", func(t *testing.T) { + // 2. Import by searching for an unpacked chart in the program directory. + t.Run("chart directory", func(t *testing.T) { options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "yaml"), - Quick: true, - ExpectRefreshChanges: true, - OrderedConfig: []integration.ConfigValue{ - { - Key: "pulumi:disable-default-providers[0]", - Value: "kubernetes", - Path: true, - }, + Dir: filepath.Join(getCwd(t), "helm-release-import", "step1-local-directory"), + Config: map[string]string{ + "chart": chart.Name, // nginx + "version": chartVersion.Version, }, }) - integration.ProgramTest(t, &options) + run(t, options, runOptions{ + InstallHelmRepository: false, + }) }) - t.Run("Helm Local", func(t *testing.T) { + // 3. Import by searching for a chart archive in the program directory. + t.Run("chart archive", func(t *testing.T) { options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-local", "step1"), - Quick: true, - ExpectRefreshChanges: true, - ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { - // Verify resource creation order using the Event stream. The Chart resources must be created - // first, followed by the dependent ConfigMap. (The ConfigMap doesn't actually need the Chart, but - // it creates almost instantly, so it's a good choice to test creation ordering) - cmRegex := regexp.MustCompile(`ConfigMap::nginx-server-block`) - svcRegex := regexp.MustCompile(`Service::nginx`) - deployRegex := regexp.MustCompile(`Deployment::nginx`) - dependentRegex := regexp.MustCompile(`ConfigMap::foo`) - - var configmapFound, serviceFound, deploymentFound, dependentFound bool - for _, e := range stackInfo.Events { - if e.ResOutputsEvent != nil { - switch { - case cmRegex.MatchString(e.ResOutputsEvent.Metadata.URN): - configmapFound = true - case svcRegex.MatchString(e.ResOutputsEvent.Metadata.URN): - serviceFound = true - case deployRegex.MatchString(e.ResOutputsEvent.Metadata.URN): - deploymentFound = true - case dependentRegex.MatchString(e.ResOutputsEvent.Metadata.URN): - dependentFound = true - } - assert.Falsef(t, dependentFound && !(configmapFound && serviceFound && deploymentFound), - "dependent ConfigMap created before all chart resources were ready") - fmt.Println(e.ResOutputsEvent.Metadata.URN) - } - } + Dir: filepath.Join(getCwd(t), "helm-release-import", "step1-local-tar"), + Config: map[string]string{ + "chart": fmt.Sprintf("%s-%s.tgz", chart.Name, chartVersion.Version), // nginx-15.3.4.tgz + "version": chartVersion.Version, }, }) - integration.ProgramTest(t, &options) + run(t, options, runOptions{ + InstallHelmRepository: false, + }) }) +} - t.Run("Helm Release Import (Option)", func(t *testing.T) { +func TestHelmReleaseImportTool(t *testing.T) { - chart := bitnamiNginxChart - chartVersion := bitnamiNginxChart.Versions[0] + chart := bitnamiNginxChart + chartVersion := bitnamiNginxChart.Versions[0] - // Run a program test for each of the various ways to import a Helm chart. - type runOptions struct { - InstallHelmRepository bool - } - run := func(t *testing.T, options integration.ProgramTestOptions, opts runOptions) { - // create a Helm environment with a chart repository - var repos []repo.Entry - if opts.InstallHelmRepository { - repos = append(repos, chart.HelmRepo) - } - he, cleanup, err := createHelmEnvironment(t, repos...) - require.NoError(t, err, "failed to create Helm environment") - t.Cleanup(func() { - contract.IgnoreError(cleanup()) - }) - - // pre-install the Helm chart to be imported - namespace := getRandomNamespace("importtest") - chartPath := filepath.Join(cwd, chart.TestPath) - require.NoError(t, createRelease("mynginx", namespace, chartPath, true)) - t.Cleanup(func() { - contract.IgnoreError(deleteRelease("mynginx", namespace)) - }) - - // Import a Helm release using the `import` option on the `helm.Release` resource. - // The program inputs MUST exactly match the provider-generated inputs, - // or Pulumi will report: "error: inputs to import do not match the existing resource". - hValues, _ := json.Marshal(chartVersion.Values) - successCriteria := func(t *testing.T, stack integration.RuntimeValidationStackInfo) { - assert.NotEmpty(t, stack.Outputs["svc_ip"]) - assert.NotEmpty(t, stack.Outputs["resourceNames"]) - } - options = options.With(integration.ProgramTestOptions{ - Config: map[string]string{ - "namespace": namespace, - "name": "mynginx", - "values": string(hValues), - "import-id": fmt.Sprintf("%s/%s", namespace, "mynginx"), - }, - Env: he.EnvVars(), - Quick: true, - ExpectRefreshChanges: true, - ExtraRuntimeValidation: successCriteria, - NoParallel: true, - DestroyOnCleanup: true, - }) - - integration.ProgramTest(t, &options) + // Run a program test for each of the various ways to import a Helm chart. + type runOptions struct { + InstallHelmRepository bool + ExpectHelmUpgrade bool + } + run := func(t *testing.T, baseOptions integration.ProgramTestOptions, opts runOptions) { + // create a Helm environment with a chart repository + var repos []repo.Entry + if opts.InstallHelmRepository { + repos = append(repos, chart.HelmRepo) } - - // 1. Import by searching the local chart repositories for a matching chart. - t.Run("chart reference", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release-import", "step1-remote"), - Config: map[string]string{ - "chart": chart.ChartReference(), // bitnami/nginx - "version": chartVersion.Version, // 15.3.4 - }, - }) - run(t, options, runOptions{ - InstallHelmRepository: true, - }) + he, cleanup, err := createHelmEnvironment(t, repos...) + require.NoError(t, err, "failed to create Helm environment") + t.Cleanup(func() { + contract.IgnoreError(cleanup()) }) - // 2. Import by searching for an unpacked chart in the program directory. - t.Run("chart directory", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release-import", "step1-local-directory"), - Config: map[string]string{ - "chart": chart.Name, // nginx - "version": chartVersion.Version, - }, - }) - run(t, options, runOptions{ - InstallHelmRepository: false, - }) + // pre-install the Helm chart to be imported + namespace := getRandomNamespace("importtest") + chartPath := filepath.Join(getCwd(t), chart.TestPath) + require.NoError(t, createRelease("mynginx", namespace, chartPath, true)) + t.Cleanup(func() { + contract.IgnoreError(deleteRelease("mynginx", namespace)) }) - // 3. Import by searching for a chart archive in the program directory. - t.Run("chart archive", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release-import", "step1-local-tar"), - Config: map[string]string{ - "chart": fmt.Sprintf("%s-%s.tgz", chart.Name, chartVersion.Version), // nginx-15.3.4.tgz - "version": chartVersion.Version, - }, - }) - run(t, options, runOptions{ - InstallHelmRepository: false, - }) + // Import a Helm release using the `pulumi import` tool. + // The provider infers the chart reference from the existing release, + // by searching the local environment for a matching chart. + // The program inputs may or may not match the provider-generated inputs; + // if not, import succeeds with a warning and a subsequent deployment may cause a Helm upgrade. + hValues, _ := json.Marshal(chartVersion.Values) + successCriteria := func(t *testing.T, stack integration.RuntimeValidationStackInfo) { + assert.NotEmpty(t, stack.Outputs["svc_ip"]) + assert.NotEmpty(t, stack.Outputs["resourceNames"]) + } + options := baseOptions.With(integration.ProgramTestOptions{ + Config: map[string]string{ + "namespace": namespace, + "name": "mynginx", + "values": string(hValues), + }, + Env: he.EnvVars(), + Quick: true, + ExpectRefreshChanges: true, + ExtraRuntimeValidation: successCriteria, + NoParallel: true, + DestroyOnCleanup: true, }) - }) + pt := integration.ProgramTestManualLifeCycle(t, &options) - t.Run("Helm Release Import (Tool)", func(t *testing.T) { + require.NoError(t, pt.TestLifeCyclePrepare(), "prepare") + t.Cleanup(pt.TestCleanUp) - chart := bitnamiNginxChart - chartVersion := bitnamiNginxChart.Versions[0] + require.NoError(t, pt.TestLifeCycleInitialize(), "initialize") - // Run a program test for each of the various ways to import a Helm chart. - type runOptions struct { - InstallHelmRepository bool - ExpectHelmUpgrade bool - } - run := func(t *testing.T, baseOptions integration.ProgramTestOptions, opts runOptions) { - // create a Helm environment with a chart repository - var repos []repo.Entry - if opts.InstallHelmRepository { - repos = append(repos, chart.HelmRepo) - } - he, cleanup, err := createHelmEnvironment(t, repos...) - require.NoError(t, err, "failed to create Helm environment") - t.Cleanup(func() { - contract.IgnoreError(cleanup()) - }) - - // pre-install the Helm chart to be imported - namespace := getRandomNamespace("importtest") - chartPath := filepath.Join(cwd, chart.TestPath) - require.NoError(t, createRelease("mynginx", namespace, chartPath, true)) - t.Cleanup(func() { - contract.IgnoreError(deleteRelease("mynginx", namespace)) - }) - - // Import a Helm release using the `pulumi import` tool. - // The provider infers the chart reference from the existing release, - // by searching the local environment for a matching chart. - // The program inputs may or may not match the provider-generated inputs; - // if not, import succeeds with a warning and a subsequent deployment may cause a Helm upgrade. - hValues, _ := json.Marshal(chartVersion.Values) - successCriteria := func(t *testing.T, stack integration.RuntimeValidationStackInfo) { - assert.NotEmpty(t, stack.Outputs["svc_ip"]) - assert.NotEmpty(t, stack.Outputs["resourceNames"]) - } - options := baseOptions.With(integration.ProgramTestOptions{ - Config: map[string]string{ - "namespace": namespace, - "name": "mynginx", - "values": string(hValues), - }, - Env: he.EnvVars(), - Quick: true, - ExpectRefreshChanges: true, - ExtraRuntimeValidation: successCriteria, - NoParallel: true, - DestroyOnCleanup: true, - }) - pt := integration.ProgramTestManualLifeCycle(t, &options) - - require.NoError(t, pt.TestLifeCyclePrepare(), "prepare") - t.Cleanup(pt.TestCleanUp) - - require.NoError(t, pt.TestLifeCycleInitialize(), "initialize") - - // Import the Helm release: `pulumi import [type] [name] [id] [flags]` - id := fmt.Sprintf("%s/%s", namespace, "mynginx") - require.NoError(t, - pt.RunPulumiCommand("import", "--yes", "kubernetes:helm.sh/v3:Release", "test", id), - "import failed") - - // Run an update to verify that the Helm release was imported. - require.NoError(t, pt.TestPreviewUpdateAndEdits(), "update") - - // assert that the release wasn't upgraded by the import operation. - release, err := getRelease("mynginx", namespace) - require.NoError(t, err, "getRelease") - if !opts.ExpectHelmUpgrade { - assert.Equal(t, 1, release.Version, "release version") - } else { - assert.Equal(t, 2, release.Version, "release version") - } + // Import the Helm release: `pulumi import [type] [name] [id] [flags]` + id := fmt.Sprintf("%s/%s", namespace, "mynginx") + require.NoError(t, + pt.RunPulumiCommand("import", "--yes", "kubernetes:helm.sh/v3:Release", "test", id), + "import failed") + + // Run an update to verify that the Helm release was imported. + require.NoError(t, pt.TestPreviewUpdateAndEdits(), "update") + + // assert that the release wasn't upgraded by the import operation. + release, err := getRelease("mynginx", namespace) + require.NoError(t, err, "getRelease") + if !opts.ExpectHelmUpgrade { + assert.Equal(t, 1, release.Version, "release version") + } else { + assert.Equal(t, 2, release.Version, "release version") } + } - // 1. Import by searching the local chart repositories for a matching chart. - t.Run("chart reference", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release-import", "step1-remote"), - Config: map[string]string{ - "chart": chart.ChartReference(), // bitnami/nginx - "version": chartVersion.Version, // 15.3.4 - }, - ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { - }, - }) - run(t, options, runOptions{ - InstallHelmRepository: true, - ExpectHelmUpgrade: false, - }) + // 1. Import by searching the local chart repositories for a matching chart. + t.Run("chart reference", func(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "helm-release-import", "step1-remote"), + Config: map[string]string{ + "chart": chart.ChartReference(), // bitnami/nginx + "version": chartVersion.Version, // 15.3.4 + }, + ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { + }, }) - - // 2. Import by searching for an unpacked chart in the program directory. - t.Run("chart directory", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release-import", "step1-local-directory"), - Config: map[string]string{ - "chart": chart.Name, // nginx - "version": chartVersion.Version, - }, - }) - run(t, options, runOptions{ - InstallHelmRepository: false, - ExpectHelmUpgrade: false, - }) + run(t, options, runOptions{ + InstallHelmRepository: true, + ExpectHelmUpgrade: false, }) + }) - // 3. Import by searching for a chart archive in the program directory. - t.Run("chart archive", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release-import", "step1-local-tar"), - Config: map[string]string{ - "chart": fmt.Sprintf("%s-%s.tgz", chart.Name, chartVersion.Version), // nginx-15.3.4.tgz - "version": chartVersion.Version, - }, - }) - run(t, options, runOptions{ - InstallHelmRepository: false, - ExpectHelmUpgrade: false, - }) + // 2. Import by searching for an unpacked chart in the program directory. + t.Run("chart directory", func(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "helm-release-import", "step1-local-directory"), + Config: map[string]string{ + "chart": chart.Name, // nginx + "version": chartVersion.Version, + }, }) - - // 4. Import without matching a chart. The tool gives a warning, and a subsequent deployment - // will cause a Helm upgrade to "correct" the inputs. - t.Run("manual", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release-import", "step1-remote"), - Config: map[string]string{ - "chart": chart.Name, // nginx - "repo": chart.HelmRepo.URL, // https://charts.bitnami.com/bitnami - "version": chartVersion.Version, // 15.3.4 - }, - }) - run(t, options, runOptions{ - InstallHelmRepository: false, - ExpectHelmUpgrade: true, - }) + run(t, options, runOptions{ + InstallHelmRepository: false, + ExpectHelmUpgrade: false, }) }) - t.Run("Import Deployment Created by Helm", func(t *testing.T) { - baseDir := filepath.Join(cwd, "helm-import-deployment", "step1") - namespace := getRandomNamespace("importdepl") - chartPath := filepath.Join(baseDir, "./nginx") - require.NoError(t, createRelease("mynginx", namespace, chartPath, true)) - defer func() { - contract.IgnoreError(deleteRelease("mynginx", namespace)) - }() + // 3. Import by searching for a chart archive in the program directory. + t.Run("chart archive", func(t *testing.T) { options := baseOptions.With(integration.ProgramTestOptions{ - Dir: baseDir, + Dir: filepath.Join(getCwd(t), "helm-release-import", "step1-local-tar"), Config: map[string]string{ - "namespace": namespace, + "chart": fmt.Sprintf("%s-%s.tgz", chart.Name, chartVersion.Version), // nginx-15.3.4.tgz + "version": chartVersion.Version, }, - ExpectRefreshChanges: true, - NoParallel: true, - Verbose: true, }) - integration.ProgramTest(t, &options) + run(t, options, runOptions{ + InstallHelmRepository: false, + ExpectHelmUpgrade: false, + }) }) - t.Run("Helm Remote", func(t *testing.T) { + // 4. Import without matching a chart. The tool gives a warning, and a subsequent deployment + // will cause a Helm upgrade to "correct" the inputs. + t.Run("manual", func(t *testing.T) { options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm", "step1"), - Quick: true, - ExpectRefreshChanges: true, + Dir: filepath.Join(getCwd(t), "helm-release-import", "step1-remote"), + Config: map[string]string{ + "chart": chart.Name, // nginx + "repo": chart.HelmRepo.URL, // https://charts.bitnami.com/bitnami + "version": chartVersion.Version, // 15.3.4 + }, }) - integration.ProgramTest(t, &options) + run(t, options, runOptions{ + InstallHelmRepository: false, + ExpectHelmUpgrade: true, + }) + }) +} + +func TestImportDeploymentHelm(t *testing.T) { + baseDir := filepath.Join(getCwd(t), "helm-import-deployment", "step1") + namespace := getRandomNamespace("importdepl") + chartPath := filepath.Join(baseDir, "./nginx") + require.NoError(t, createRelease("mynginx", namespace, chartPath, true)) + defer func() { + contract.IgnoreError(deleteRelease("mynginx", namespace)) + }() + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: baseDir, + Config: map[string]string{ + "namespace": namespace, + }, + ExpectRefreshChanges: true, + NoParallel: true, + Verbose: true, }) + integration.ProgramTest(t, &options) +} - t.Run("Helm Release", func(t *testing.T) { - chart := bitnamiNginxChart - chartVersion := bitnamiNginxChart.Versions[0] +func TestHelmRemote(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "helm", "step1"), + Quick: true, + ExpectRefreshChanges: true, + }) + integration.ProgramTest(t, &options) +} - // Run a program test for each of the various ways to install a Helm chart. - type runOptions struct { - InstallHelmRepository bool +func TestHelmRelease(t *testing.T) { + chart := bitnamiNginxChart + chartVersion := bitnamiNginxChart.Versions[0] + + // Run a program test for each of the various ways to install a Helm chart. + type runOptions struct { + InstallHelmRepository bool + } + run := func(t *testing.T, options integration.ProgramTestOptions, opts runOptions) { + // create a Helm environment with a chart repository + var repos []repo.Entry + if opts.InstallHelmRepository { + repos = append(repos, chart.HelmRepo) } - run := func(t *testing.T, options integration.ProgramTestOptions, opts runOptions) { - // create a Helm environment with a chart repository - var repos []repo.Entry - if opts.InstallHelmRepository { - repos = append(repos, chart.HelmRepo) - } - he, cleanup, err := createHelmEnvironment(t, repos...) - require.NoError(t, err, "failed to create Helm environment") - t.Cleanup(func() { - contract.IgnoreError(cleanup()) - }) - - hValues, _ := json.Marshal(chartVersion.Values) - successCriteria := func(t *testing.T, stack integration.RuntimeValidationStackInfo) { - assert.NotEmpty(t, stack.Outputs["svc_ip"]) - assert.NotEmpty(t, stack.Outputs["resourceNames"]) - } - options = options.With(integration.ProgramTestOptions{ - Config: map[string]string{ - "values": string(hValues), - }, - Env: he.EnvVars(), - Quick: true, - ExpectRefreshChanges: true, - ExtraRuntimeValidation: successCriteria, - NoParallel: true, - DestroyOnCleanup: true, - }) - - integration.ProgramTest(t, &options) + he, cleanup, err := createHelmEnvironment(t, repos...) + require.NoError(t, err, "failed to create Helm environment") + t.Cleanup(func() { + contract.IgnoreError(cleanup()) + }) + + hValues, _ := json.Marshal(chartVersion.Values) + successCriteria := func(t *testing.T, stack integration.RuntimeValidationStackInfo) { + assert.NotEmpty(t, stack.Outputs["svc_ip"]) + assert.NotEmpty(t, stack.Outputs["resourceNames"]) } + options = options.With(integration.ProgramTestOptions{ + Config: map[string]string{ + "values": string(hValues), + }, + Env: he.EnvVars(), + Quick: true, + ExpectRefreshChanges: true, + ExtraRuntimeValidation: successCriteria, + NoParallel: true, + DestroyOnCleanup: true, + }) - // There's "six ways" to reference a Helm chart, and we test each of them here. + integration.ProgramTest(t, &options) + } - // 1. By chart reference: helm install mymaria example/mariadb - t.Run("chart reference", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release", "step1"), - Config: map[string]string{ - "chart": chart.ChartReference(), // bitnami/nginx - "version": chartVersion.Version, // 15.3.4 - }, - }) - run(t, options, runOptions{ - InstallHelmRepository: true, - }) + // There's "six ways" to reference a Helm chart, and we test each of them here. + + // 1. By chart reference: helm install mymaria example/mariadb + t.Run("chart reference", func(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "helm-release", "step1"), + Config: map[string]string{ + "chart": chart.ChartReference(), // bitnami/nginx + "version": chartVersion.Version, // 15.3.4 + }, }) + run(t, options, runOptions{ + InstallHelmRepository: true, + }) + }) - // 2. By path to an unpacked chart directory: helm install mynginx ./nginx - t.Run("chart directory", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release", "step1"), - Config: map[string]string{ - "chart": filepath.Join(cwd, chart.TestPath), // "/workspace/tests/testdata/helm/nginx" - "version": chartVersion.Version, - }, - }) - run(t, options, runOptions{ - InstallHelmRepository: false, - }) + // 2. By path to an unpacked chart directory: helm install mynginx ./nginx + t.Run("chart directory", func(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "helm-release", "step1"), + Config: map[string]string{ + "chart": filepath.Join(getCwd(t), chart.TestPath), // "/workspace/tests/testdata/helm/nginx" + "version": chartVersion.Version, + }, + }) + run(t, options, runOptions{ + InstallHelmRepository: false, }) + }) - // 3. By path to a packaged chart: helm install mynginx ./nginx-1.2.3.tgz - t.Run("chart archive", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release", "step1"), - Config: map[string]string{ - "chart": filepath.Join(cwd, chart.TestArchive), // /workspace/tests/testdata/nginx-15.3.4.tgz - "version": chartVersion.Version, - }, - }) - run(t, options, runOptions{ - InstallHelmRepository: false, - }) + // 3. By path to a packaged chart: helm install mynginx ./nginx-1.2.3.tgz + t.Run("chart archive", func(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "helm-release", "step1"), + Config: map[string]string{ + "chart": filepath.Join(getCwd(t), chart.TestArchive), // /workspace/tests/testdata/nginx-15.3.4.tgz + "version": chartVersion.Version, + }, + }) + run(t, options, runOptions{ + InstallHelmRepository: false, }) + }) - // 4. By absolute URL: helm install mynginx https://example.com/charts/nginx-1.2.3.tgz - t.Run("absolute URL", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release", "step1"), - Config: map[string]string{ - "chart": chart.ChartURL, // https://charts.bitnami.com/bitnami/nginx-15.3.4.tgz - "version": chartVersion.Version, - }, - }) - run(t, options, runOptions{ - InstallHelmRepository: false, - }) + // 4. By absolute URL: helm install mynginx https://example.com/charts/nginx-1.2.3.tgz + t.Run("absolute URL", func(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "helm-release", "step1"), + Config: map[string]string{ + "chart": chart.ChartURL, // https://charts.bitnami.com/bitnami/nginx-15.3.4.tgz + "version": chartVersion.Version, + }, }) + run(t, options, runOptions{ + InstallHelmRepository: false, + }) + }) - // 5. By chart reference and repo url: helm install --repo https://example.com/charts/ mynginx nginx - t.Run("chart reference and repo url", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release", "step1"), - Config: map[string]string{ - "chart": chart.Name, // nginx - "repo": chart.HelmRepo.URL, // https://charts.bitnami.com/bitnami - "version": chartVersion.Version, // 15.3.4 - }, - }) - run(t, options, runOptions{ - InstallHelmRepository: false, - }) + // 5. By chart reference and repo url: helm install --repo https://example.com/charts/ mynginx nginx + t.Run("chart reference and repo url", func(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "helm-release", "step1"), + Config: map[string]string{ + "chart": chart.Name, // nginx + "repo": chart.HelmRepo.URL, // https://charts.bitnami.com/bitnami + "version": chartVersion.Version, // 15.3.4 + }, + }) + run(t, options, runOptions{ + InstallHelmRepository: false, }) + }) - // 6. By OCI registries: helm install mynginx --version 1.2.3 oci://example.com/charts/nginx - t.Run("oci chart", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release", "step1"), - Config: map[string]string{ - "chart": chart.OciURL, // oci://registry-1.docker.io/bitnamicharts/nginx - "version": chartVersion.Version, // 15.3.4 - }, - }) - run(t, options, runOptions{ - InstallHelmRepository: false, - }) + // 6. By OCI registries: helm install mynginx --version 1.2.3 oci://example.com/charts/nginx + t.Run("oci chart", func(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "helm-release", "step1"), + Config: map[string]string{ + "chart": chart.OciURL, // oci://registry-1.docker.io/bitnamicharts/nginx + "version": chartVersion.Version, // 15.3.4 + }, + }) + run(t, options, runOptions{ + InstallHelmRepository: false, }) }) +} - t.Run("Helm Release (Local Chart Versioning)", func(t *testing.T) { - validateVersion := func(t *testing.T, stack integration.RuntimeValidationStackInfo, expected string) { - actual, ok := stack.Outputs["version"].(string) - if !ok { - t.Fatalf("expected a version output") - } - assert.Equal(t, expected, actual, "expected version to be %d", expected) +func TestHelmReleaseLocalChartVersioning(t *testing.T) { + validateVersion := func(t *testing.T, stack integration.RuntimeValidationStackInfo, expected string) { + actual, ok := stack.Outputs["version"].(string) + if !ok { + t.Fatalf("expected a version output") } - validateReplicas := func(t *testing.T, stack integration.RuntimeValidationStackInfo, expected float64) { - actual, ok := stack.Outputs["replicas"].(float64) - if !ok { - t.Fatalf("expected a replicas output") - } - assert.Equal(t, expected, actual, "expected replicas to be %d", expected) + assert.Equal(t, expected, actual, "expected version to be %d", expected) + } + validateReplicas := func(t *testing.T, stack integration.RuntimeValidationStackInfo, expected float64) { + actual, ok := stack.Outputs["replicas"].(float64) + if !ok { + t.Fatalf("expected a replicas output") } + assert.Equal(t, expected, actual, "expected replicas to be %d", expected) + } - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-release-local", "step1"), - Quick: true, - ExpectRefreshChanges: true, - ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { - validateReplicas(t, stack, 1) - }, - EditDirs: []integration.EditDir{ - { - Dir: filepath.Join("helm-release-local", "step2"), - Additive: true, - ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { - // expect the change in values.yaml (replicaCount: 2) to NOT be detected - // because Pulumi detects version changes only. - validateReplicas(t, stack, 1) - validateVersion(t, stack, "6.0.5") - }, - ExpectFailure: false, + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "helm-release-local", "step1"), + Quick: true, + ExpectRefreshChanges: true, + ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { + validateReplicas(t, stack, 1) + }, + EditDirs: []integration.EditDir{ + { + Dir: filepath.Join("helm-release-local", "step2"), + Additive: true, + ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { + // expect the change in values.yaml (replicaCount: 2) to NOT be detected + // because Pulumi detects version changes only. + validateReplicas(t, stack, 1) + validateVersion(t, stack, "6.0.5") }, - { - Dir: filepath.Join("helm-release-local", "step3"), - Additive: true, - ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { - // bump the chart version and expect Pulumi to perform an upgrade. - validateReplicas(t, stack, 2) - validateVersion(t, stack, "6.1.0") - }, - ExpectFailure: false, + ExpectFailure: false, + }, + { + Dir: filepath.Join("helm-release-local", "step3"), + Additive: true, + ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { + // bump the chart version and expect Pulumi to perform an upgrade. + validateReplicas(t, stack, 2) + validateVersion(t, stack, "6.1.0") }, - { - Dir: filepath.Join("helm-release-local", "step4"), - Additive: true, - ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { - // bump the chart version again but with ignoreChanges: ["version"] - // expect no change in the number of replicas. - validateReplicas(t, stack, 2) - validateVersion(t, stack, "6.1.0") - }, - ExpectFailure: false, + ExpectFailure: false, + }, + { + Dir: filepath.Join("helm-release-local", "step4"), + Additive: true, + ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { + // bump the chart version again but with ignoreChanges: ["version"] + // expect no change in the number of replicas. + validateReplicas(t, stack, 2) + validateVersion(t, stack, "6.1.0") }, + ExpectFailure: false, }, - }) - integration.ProgramTest(t, &options) + }, }) + integration.ProgramTest(t, &options) +} - t.Run("Helm Release (Partial Error)", func(t *testing.T) { - // Validate that we only see a single release in the namespace - success or failure. - validation := func(t *testing.T, stack integration.RuntimeValidationStackInfo) { - var namespace string - for _, res := range stack.Deployment.Resources { - if res.Type == "kubernetes:helm.sh/v3:Release" { - ns, found := res.Outputs["namespace"] - assert.True(t, found) - namespace = ns.(string) - } +func TestHelmReleasePartialError(t *testing.T) { + // Validate that we only see a single release in the namespace - success or failure. + validation := func(t *testing.T, stack integration.RuntimeValidationStackInfo) { + var namespace string + for _, res := range stack.Deployment.Resources { + if res.Type == "kubernetes:helm.sh/v3:Release" { + ns, found := res.Outputs["namespace"] + assert.True(t, found) + namespace = ns.(string) } - assert.NotEmpty(t, namespace) - releases, err := listReleases(namespace) - assert.NoError(t, err) - assert.Len(t, releases, 1) } + assert.NotEmpty(t, namespace) + releases, err := listReleases(namespace) + assert.NoError(t, err) + assert.Len(t, releases, 1) + } - test := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join("helm-partial-error", "step1"), - SkipRefresh: false, - SkipEmptyPreviewUpdate: true, - SkipPreview: true, - Verbose: true, - ExpectFailure: true, - ExtraRuntimeValidation: validation, - EditDirs: []integration.EditDir{ - { - Dir: filepath.Join("helm-partial-error", "step2"), - Additive: true, - ExtraRuntimeValidation: validation, - ExpectFailure: false, - }, + test := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join("helm-partial-error", "step1"), + SkipRefresh: false, + SkipEmptyPreviewUpdate: true, + SkipPreview: true, + Verbose: true, + ExpectFailure: true, + ExtraRuntimeValidation: validation, + EditDirs: []integration.EditDir{ + { + Dir: filepath.Join("helm-partial-error", "step2"), + Additive: true, + ExtraRuntimeValidation: validation, + ExpectFailure: false, }, - }) - integration.ProgramTest(t, &test) + }, }) + integration.ProgramTest(t, &test) +} - t.Run("Helm API Versions", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-api-versions", "step1"), - Quick: true, - ExpectRefreshChanges: true, - }) - integration.ProgramTest(t, &options) +func TestHelmAPIVersions(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "helm-api-versions", "step1"), + Quick: true, + ExpectRefreshChanges: true, }) + integration.ProgramTest(t, &options) +} - t.Run("Helm Kube Version", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-kube-version", "step1"), - Quick: true, - ExpectRefreshChanges: true, - }) - integration.ProgramTest(t, &options) +func TestHelmKubeVersion(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "helm-kube-version", "step1"), + Quick: true, + ExpectRefreshChanges: true, }) + integration.ProgramTest(t, &options) +} - t.Run("Helm Skip CRD Rendering", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join("helm-skip-crd-rendering", "step1"), - Quick: true, - SkipRefresh: true, - ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { - assert.NotNil(t, stackInfo.Deployment) - assert.Equal(t, 8, len(stackInfo.Deployment.Resources)) - - for _, res := range stackInfo.Deployment.Resources { - if res.Type == "kubernetes:core/v1:Pod" { - annotations, ok := openapi.Pluck(res.Inputs, "metadata", "annotations") - if strings.Contains(res.ID.String(), "skip-crd") { - assert.False(t, ok) - } else { - assert.True(t, ok) - assert.Contains(t, annotations, "pulumi.com/skipAwait") - } +func TestHelmSkipCRDRendering(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join("helm-skip-crd-rendering", "step1"), + Quick: true, + SkipRefresh: true, + ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { + assert.NotNil(t, stackInfo.Deployment) + assert.Equal(t, 8, len(stackInfo.Deployment.Resources)) + + for _, res := range stackInfo.Deployment.Resources { + if res.Type == "kubernetes:core/v1:Pod" { + annotations, ok := openapi.Pluck(res.Inputs, "metadata", "annotations") + if strings.Contains(res.ID.String(), "skip-crd") { + assert.False(t, ok) + } else { + assert.True(t, ok) + assert.Contains(t, annotations, "pulumi.com/skipAwait") } } - }, - }) - integration.ProgramTest(t, &options) + } + }, }) + integration.ProgramTest(t, &options) +} - t.Run("Kustomize", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "kustomize"), - Quick: true, - OrderedConfig: []integration.ConfigValue{ - { - Key: "pulumi:disable-default-providers[0]", - Value: "kubernetes", - Path: true, - }, +func TestKustomize(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "kustomize"), + Quick: true, + OrderedConfig: []integration.ConfigValue{ + { + Key: "pulumi:disable-default-providers[0]", + Value: "kubernetes", + Path: true, }, - }) - integration.ProgramTest(t, &options) + }, }) + integration.ProgramTest(t, &options) +} - t.Run("Secrets", func(t *testing.T) { - secretMessage := "secret message for testing" +func TestSecrets(t *testing.T) { + secretMessage := "secret message for testing" - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "secrets"), - Quick: true, - Config: map[string]string{ - "message": secretMessage, - }, - ExpectRefreshChanges: true, - ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { - assert.NotNil(t, stackInfo.Deployment) - state, err := json.Marshal(stackInfo.Deployment) - assert.NoError(t, err) + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "secrets"), + Quick: true, + Config: map[string]string{ + "message": secretMessage, + }, + ExpectRefreshChanges: true, + ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { + assert.NotNil(t, stackInfo.Deployment) + state, err := json.Marshal(stackInfo.Deployment) + assert.NoError(t, err) - assert.NotContains(t, string(state), secretMessage) + assert.NotContains(t, string(state), secretMessage) - // The program converts the secret message to base64, to make a ConfigMap from it, so the state - // should also not contain the base64 encoding of secret message. - assert.NotContains(t, string(state), b64.StdEncoding.EncodeToString([]byte(secretMessage))) - }, - }) - integration.ProgramTest(t, &options) + // The program converts the secret message to base64, to make a ConfigMap from it, so the state + // should also not contain the base64 encoding of secret message. + assert.NotContains(t, string(state), b64.StdEncoding.EncodeToString([]byte(secretMessage))) + }, }) + integration.ProgramTest(t, &options) +} - t.Run("SecretsWithUnknowns", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "secrets-with-unknowns"), - Quick: false, - }) - integration.ProgramTest(t, &options) +func TestSecretsWithUnknowns(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "secrets-with-unknowns"), + Quick: false, }) + integration.ProgramTest(t, &options) +} - t.Run("ServerSideApply", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "server-side-apply"), - ExpectRefreshChanges: true, - OrderedConfig: []integration.ConfigValue{ - { - Key: "pulumi:disable-default-providers[0]", - Value: "kubernetes", - Path: true, - }, +func TestServerSideApply(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "server-side-apply"), + ExpectRefreshChanges: true, + OrderedConfig: []integration.ConfigValue{ + { + Key: "pulumi:disable-default-providers[0]", + Value: "kubernetes", + Path: true, }, - EditDirs: []integration.EditDir{ - { - Dir: filepath.Join("server-side-apply", "step2"), - Additive: true, - ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { - // Validate patched CustomResource - crPatchedLabels := stackInfo.Outputs["crPatchedLabels"].(map[string]any) - fooV, ok, err := unstructured.NestedString(crPatchedLabels, "foo") - assert.True(t, ok) - assert.NoError(t, err) - assert.Equal(t, "foo", fooV) - }, + }, + EditDirs: []integration.EditDir{ + { + Dir: filepath.Join("server-side-apply", "step2"), + Additive: true, + ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { + // Validate patched CustomResource + crPatchedLabels := stackInfo.Outputs["crPatchedLabels"].(map[string]any) + fooV, ok, err := unstructured.NestedString(crPatchedLabels, "foo") + assert.True(t, ok) + assert.NoError(t, err) + assert.Equal(t, "foo", fooV) }, }, - }) - integration.ProgramTest(t, &options) + }, }) + integration.ProgramTest(t, &options) +} - // Test to ensure https://github.com/pulumi/pulumi-kubernetes/issues/2336 is fixed. This spins up a deployment pod with - // 2 containers using CSA. Then, it updates the deployment to use SSA while deleting one of the containers. - t.Run("switchSSADeleteContainer", func(t *testing.T) { - validation := func(expectedContainers string) func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { - return func(t *testing.T, stack integration.RuntimeValidationStackInfo) { - ns, ok := stack.Outputs["namespace"].(string) - if !ok { - t.Fatalf("expected a string namespace output") - } - - // Check that the stack has the expected number of deployments/resources. - var count int - for _, res := range stack.Deployment.Resources { - // Validate that the deployment has the expected number of containers. We use kubectl to verify this, - // as there have been issues in the past with Pulumi outputs not accurately reflecting the state of the - // cluster. - if !strings.Contains(string(res.URN), "v1:Deployment::deployment") { - continue - } +// Test to ensure https://github.com/pulumi/pulumi-kubernetes/issues/2336 is fixed. This spins up a deployment pod with +// 2 containers using CSA. Then, it updates the deployment to use SSA while deleting one of the containers. +func TestSwitchSSADeleteContainer(t *testing.T) { + validation := func(expectedContainers string) func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) { + return func(t *testing.T, stack integration.RuntimeValidationStackInfo) { + ns, ok := stack.Outputs["namespace"].(string) + if !ok { + t.Fatalf("expected a string namespace output") + } - count++ - out, err := exec.Command("kubectl", "get", "deployment", "-o", "jsonpath={.spec.template.spec.containers[*].name}", "-n", ns, "nginx").CombinedOutput() - assert.NoError(t, err) - assert.Equal(t, expectedContainers, string(out)) + // Check that the stack has the expected number of deployments/resources. + var count int + for _, res := range stack.Deployment.Resources { + // Validate that the deployment has the expected number of containers. We use kubectl to verify this, + // as there have been issues in the past with Pulumi outputs not accurately reflecting the state of the + // cluster. + if !strings.Contains(string(res.URN), "v1:Deployment::deployment") { + continue } - if count != 1 { - t.Errorf("expected 1 resource, got %d", count) - } + count++ + out, err := exec.Command("kubectl", "get", "deployment", "-o", "jsonpath={.spec.template.spec.containers[*].name}", "-n", ns, "nginx").CombinedOutput() + assert.NoError(t, err) + assert.Equal(t, expectedContainers, string(out)) + } + + if count != 1 { + t.Errorf("expected 1 resource, got %d", count) } } + } - test := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join("switch-ssa-delete-container", "step1"), - Verbose: true, - ExtraRuntimeValidation: validation("nginx sidecar"), - EditDirs: []integration.EditDir{ - { - Dir: filepath.Join("switch-ssa-delete-container", "step2"), - Additive: true, - ExpectNoChanges: false, - ExtraRuntimeValidation: validation("nginx"), - }, + test := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join("switch-ssa-delete-container", "step1"), + Verbose: true, + ExtraRuntimeValidation: validation("nginx sidecar"), + EditDirs: []integration.EditDir{ + { + Dir: filepath.Join("switch-ssa-delete-container", "step2"), + Additive: true, + ExpectNoChanges: false, + ExtraRuntimeValidation: validation("nginx"), }, - }) - integration.ProgramTest(t, &test) + }, }) + integration.ProgramTest(t, &test) +} - // Test to ensure that we can get a resource from the default namespace. This uses the wordpress chart as it requires the - // default namespace to be present in the GVK get request. - t.Run("ChartGetResource", func(t *testing.T) { - options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "helm-get-default-namespace", "step1"), - Quick: true, - EditDirs: []integration.EditDir{ - { - Dir: filepath.Join(cwd, "helm-get-default-namespace", "step2"), - Additive: true, - ExpectNoChanges: false, - }, +// Test to ensure that we can get a resource from the default namespace. This uses the wordpress chart as it requires the +// default namespace to be present in the GVK get request. +func TestChartGetResource(t *testing.T) { + options := baseOptions.With(integration.ProgramTestOptions{ + Dir: filepath.Join(getCwd(t), "helm-get-default-namespace", "step1"), + Quick: true, + EditDirs: []integration.EditDir{ + { + Dir: filepath.Join(getCwd(t), "helm-get-default-namespace", "step2"), + Additive: true, + ExpectNoChanges: false, }, - }) - integration.ProgramTest(t, &options) + }, }) + integration.ProgramTest(t, &options) } // TestOptionPropagation tests the handling of resource options by the various compoonent resources. @@ -820,16 +822,11 @@ func TestOptionPropagation(t *testing.T) { // format.UseStringerRepresentation = true format.RegisterCustomFormatter(pulumirpctesting.FormatDebugInterceptorLog) - cwd, err := os.Getwd() - if !assert.NoError(t, err) { - t.FailNow() - } - grpcLog, err := pulumirpctesting.NewDebugInterceptorLog(t) require.NoError(t, err) options := baseOptions.With(integration.ProgramTestOptions{ - Dir: filepath.Join(cwd, "options"), + Dir: filepath.Join(getCwd(t), "options"), Env: []string{grpcLog.Env()}, Quick: true, ExpectRefreshChanges: false,