From d82269f90be197558159ebcddb031854390dee4e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 May 2023 17:11:56 +0000 Subject: [PATCH 1/7] Update actions/setup-go digest to fac708d --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80b1a70..16d863c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,7 +45,7 @@ jobs: submodules: true - name: Setup Go - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4 + uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4 with: go-version: ${{ env.GO_VERSION }} @@ -89,7 +89,7 @@ jobs: submodules: true - name: Setup Go - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4 + uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4 with: go-version: ${{ env.GO_VERSION }} @@ -135,7 +135,7 @@ jobs: run: git fetch --prune --unshallow - name: Setup Go - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4 + uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4 with: go-version: ${{ env.GO_VERSION }} @@ -184,7 +184,7 @@ jobs: run: git fetch --prune --unshallow - name: Setup Go - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4 + uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4 with: go-version: ${{ env.GO_VERSION }} @@ -246,7 +246,7 @@ jobs: run: git fetch --prune --unshallow - name: Setup Go - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4 + uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4 with: go-version: ${{ env.GO_VERSION }} From 7d5843bbbbdc6c63a9ecfd4fe798329fb970a770 Mon Sep 17 00:00:00 2001 From: Verveiko Denys Date: Mon, 22 May 2023 13:50:52 +0300 Subject: [PATCH 2/7] Move all pipelines to use reusable workflows --- .github/workflows/backport.yml | 23 +-- .github/workflows/ci.yml | 285 +-------------------------------- .github/workflows/commands.yml | 28 +--- .github/workflows/tag.yaml | 20 +-- 4 files changed, 17 insertions(+), 339 deletions(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index ddaa175..93b5095 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -11,24 +11,5 @@ on: # See also commands.yml for the /backport triggered variant of this workflow. jobs: - # NOTE(negz): I tested many backport GitHub actions before landing on this - # one. Many do not support merge commits, or do not support pull requests with - # more than one commit. This one does. It also handily links backport PRs with - # new PRs, and provides commentary and instructions when it can't backport. - # The main gotchas with this action are that it _only_ supports merge commits, - # and that PRs _must_ be labelled before they're merged to trigger a backport. - open-pr: - runs-on: ubuntu-22.04 - if: github.event.pull_request.merged - steps: - - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3 - with: - fetch-depth: 0 - - - name: Open Backport PR - uses: zeebe-io/backport-action@a759fd2d7d3314c9bb57d97a0350a12e878d3c7a # v1.2.0 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - github_workspace: ${{ github.workspace }} - version: v0.0.8 + backport: + uses: upbound/uptest/.github/workflows/provider-backport.yml@main \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16d863c..a20d45c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,282 +8,11 @@ on: pull_request: {} workflow_dispatch: {} -env: - # Common versions - GO_VERSION: '1.20.4' - GOLANGCI_VERSION: 'v1.52.2' - DOCKER_BUILDX_VERSION: 'v0.8.2' - - # Common users. We can't run a step 'if secrets.XXX != ""' but we can run a - # step 'if env.XXX' != ""', so we copy these to succinctly test whether - # credentials have been provided before trying to run steps that need them. - UPBOUND_MARKETPLACE_PUSH_ROBOT_USR: ${{ secrets.UPBOUND_MARKETPLACE_PUSH_ROBOT_USR }} jobs: - detect-noop: - runs-on: ubuntu-22.04 - outputs: - noop: ${{ steps.noop.outputs.should_skip }} - steps: - - name: Detect No-op Changes - id: noop - uses: fkirc/skip-duplicate-actions@12aca0a884f6137d619d6a8a09fcc3406ced5281 # v5.3.0 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - paths_ignore: '["**.md", "**.png", "**.jpg"]' - do_not_skip: '["workflow_dispatch", "schedule", "push"]' - - - lint: - runs-on: ubuntu-22.04 - needs: detect-noop - if: needs.detect-noop.outputs.noop != 'true' - - steps: - - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3 - with: - submodules: true - - - name: Setup Go - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4 - with: - go-version: ${{ env.GO_VERSION }} - - - name: Find the Go Build Cache - id: go - run: echo "cache=$(make go.cachedir)" >> $GITHUB_OUTPUT - - - name: Cache the Go Build Cache - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3 - with: - path: ${{ steps.go.outputs.cache }} - key: ${{ runner.os }}-build-lint-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-build-lint- - - - name: Cache Go Dependencies - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3 - with: - path: .work/pkg - key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-pkg- - - - name: Vendor Dependencies - run: make vendor vendor.check - - # We could run 'make lint' but we prefer this action because it leaves - # 'annotations' (i.e. it comments on PRs to point out linter violations). - - name: Lint - uses: golangci/golangci-lint-action@08e2f20817b15149a52b5b3ebe7de50aff2ba8c5 # v3 - with: - version: ${{ env.GOLANGCI_VERSION }} - - check-diff: - runs-on: ubuntu-22.04 - needs: detect-noop - if: needs.detect-noop.outputs.noop != 'true' - - steps: - - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3 - with: - submodules: true - - - name: Setup Go - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4 - with: - go-version: ${{ env.GO_VERSION }} - - - name: Install goimports - run: go install golang.org/x/tools/cmd/goimports - - - name: Find the Go Build Cache - id: go - run: echo "cache=$(make go.cachedir)" >> $GITHUB_OUTPUT - - - name: Cache the Go Build Cache - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3 - with: - path: ${{ steps.go.outputs.cache }} - key: ${{ runner.os }}-build-check-diff-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-build-check-diff- - - - name: Cache Go Dependencies - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3 - with: - path: .work/pkg - key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-pkg- - - - name: Vendor Dependencies - run: make vendor vendor.check - - - name: Check Diff - run: make check-diff - - unit-tests: - runs-on: ubuntu-22.04 - needs: detect-noop - if: needs.detect-noop.outputs.noop != 'true' - - steps: - - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3 - with: - submodules: true - - - name: Fetch History - run: git fetch --prune --unshallow - - - name: Setup Go - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4 - with: - go-version: ${{ env.GO_VERSION }} - - - name: Find the Go Build Cache - id: go - run: echo "cache=$(make go.cachedir)" >> $GITHUB_OUTPUT - - - name: Cache the Go Build Cache - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3 - with: - path: ${{ steps.go.outputs.cache }} - key: ${{ runner.os }}-build-unit-tests-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-build-unit-tests- - - - name: Cache Go Dependencies - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3 - with: - path: .work/pkg - key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-pkg- - - - name: Vendor Dependencies - run: make vendor vendor.check - - - name: Run Unit Tests - run: make -j2 test - - - name: Publish Unit Test Coverage - uses: codecov/codecov-action@894ff025c7b54547a9a2a1e9f228beae737ad3c2 # v3 - with: - flags: unittests - file: _output/tests/linux_amd64/coverage.txt - - local-deploy: - runs-on: ubuntu-22.04 - needs: detect-noop - if: needs.detect-noop.outputs.noop != 'true' - - steps: - - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3 - with: - submodules: true - - - name: Fetch History - run: git fetch --prune --unshallow - - - name: Setup Go - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4 - with: - go-version: ${{ env.GO_VERSION }} - - - name: Find the Go Build Cache - id: go - run: echo "cache=$(make go.cachedir)" >> $GITHUB_OUTPUT - - - name: Cache the Go Build Cache - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3 - with: - path: ${{ steps.go.outputs.cache }} - key: ${{ runner.os }}-build-unit-tests-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-build-unit-tests- - - - name: Cache Go Dependencies - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3 - with: - path: .work/pkg - key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-pkg- - - - name: Vendor Dependencies - run: make vendor vendor.check - - - name: Deploying locally built provider package and ensuring it is running - run: make local-deploy - - publish-artifacts: - runs-on: ubuntu-22.04 - needs: detect-noop - if: needs.detect-noop.outputs.noop != 'true' - - steps: - - name: Setup QEMU - uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2 - with: - platforms: all - - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c # v2 - with: - version: ${{ env.DOCKER_BUILDX_VERSION }} - install: true - - - name: Login to Upbound - uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2 - if: env.UPBOUND_MARKETPLACE_PUSH_ROBOT_USR != '' - with: - registry: xpkg.upbound.io - username: ${{ secrets.UPBOUND_MARKETPLACE_PUSH_ROBOT_USR }} - password: ${{ secrets.UPBOUND_MARKETPLACE_PUSH_ROBOT_PSW }} - - - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3 - with: - submodules: true - - - name: Fetch History - run: git fetch --prune --unshallow - - - name: Setup Go - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4 - with: - go-version: ${{ env.GO_VERSION }} - - - name: Find the Go Build Cache - id: go - run: echo "cache=$(make go.cachedir)" >> $GITHUB_OUTPUT - - - name: Cache the Go Build Cache - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3 - with: - path: ${{ steps.go.outputs.cache }} - key: ${{ runner.os }}-build-publish-artifacts-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-build-publish-artifacts- - - - name: Cache Go Dependencies - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3 - with: - path: .work/pkg - key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-pkg- - - - name: Vendor Dependencies - run: make vendor vendor.check - - - name: Build Artifacts - run: make -j2 build.all - env: - # We're using docker buildx, which doesn't actually load the images it - # builds by default. Specifying --load does so. - BUILD_ARGS: "--load" - - - name: Publish Artifacts to GitHub - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3 - with: - name: output - path: _output/** - - - name: Publish Artifacts - if: env.UPBOUND_MARKETPLACE_PUSH_ROBOT_USR != '' - run: make publish BRANCH_NAME=${GITHUB_REF##*/} + ci: + uses: upbound/uptest/.github/workflows/provider-ci.yml@main + with: + upjet-based-provider: false + secrets: + UPBOUND_MARKETPLACE_PUSH_ROBOT_USR: ${{ secrets.UPBOUND_MARKETPLACE_PUSH_ROBOT_USR }} + UPBOUND_MARKETPLACE_PUSH_ROBOT_PSW: ${{ secrets.UPBOUND_MARKETPLACE_PUSH_ROBOT_PSW }} \ No newline at end of file diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml index a45b55c..cbbd0db 100644 --- a/.github/workflows/commands.yml +++ b/.github/workflows/commands.yml @@ -3,29 +3,5 @@ name: Comment Commands on: issue_comment jobs: - backport: - runs-on: ubuntu-22.04 - if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/backport') - steps: - - name: Extract Command - id: command - uses: xt0rted/slash-command-action@bf51f8f5f4ea3d58abc7eca58f77104182b23e88 # v2 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - command: backport - reaction: "true" - reaction-type: "eyes" - allow-edits: "false" - permission-level: write - - - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3 - with: - fetch-depth: 0 - - - name: Open Backport PR - uses: zeebe-io/backport-action@a759fd2d7d3314c9bb57d97a0350a12e878d3c7a # v1.2.0 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - github_workspace: ${{ github.workspace }} - version: v0.0.4 + comment-commands: + uses: upbound/uptest/.github/workflows/provider-commands.yml@main diff --git a/.github/workflows/tag.yaml b/.github/workflows/tag.yaml index 96719ab..2a93895 100644 --- a/.github/workflows/tag.yaml +++ b/.github/workflows/tag.yaml @@ -1,7 +1,7 @@ name: Tag on: - workflow_dispatch: + workflow_dispatch: inputs: version: description: 'Release version (e.g. v0.1.0)' @@ -11,16 +11,8 @@ on: required: true jobs: - create-tag: - runs-on: ubuntu-22.04 - - steps: - - name: Checkout - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3 - - - name: Create Tag - uses: negz/create-tag@39bae1e0932567a58c20dea5a1a0d18358503320 # v1 - with: - version: ${{ github.event.inputs.version }} - message: ${{ github.event.inputs.message }} - token: ${{ secrets.GITHUB_TOKEN }} + tag: + uses: upbound/uptest/.github/workflows/provider-tag.yml@main + with: + version: ${{ github.event.inputs.version }} + message: ${{ github.event.inputs.message }} From 6948460b24f0de38d738063599fc3cd02376906b Mon Sep 17 00:00:00 2001 From: Alper Rifat Ulucinar Date: Mon, 22 May 2023 19:20:43 +0300 Subject: [PATCH 3/7] Exclude StoreConfig resource from example manifest checks Signed-off-by: Alper Rifat Ulucinar --- scripts/check-examples.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/check-examples.py b/scripts/check-examples.py index 01ece8e..8181e71 100755 --- a/scripts/check-examples.py +++ b/scripts/check-examples.py @@ -25,7 +25,8 @@ def load_crd_type(t): exception_set = { - 'ProviderConfigUsage.tf.upbound.io/v1beta1' + 'ProviderConfigUsage.tf.upbound.io/v1beta1', + 'StoreConfig.tf.upbound.io/v1beta1' } From 1b56d7afc62cc994935a7d01bdd4c8e181f6e252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20T=C3=BCrken?= Date: Fri, 3 Mar 2023 14:56:14 +0300 Subject: [PATCH 4/7] Remove old workflow and reuse new one --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a20d45c..5ae342d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,4 +15,4 @@ jobs: upjet-based-provider: false secrets: UPBOUND_MARKETPLACE_PUSH_ROBOT_USR: ${{ secrets.UPBOUND_MARKETPLACE_PUSH_ROBOT_USR }} - UPBOUND_MARKETPLACE_PUSH_ROBOT_PSW: ${{ secrets.UPBOUND_MARKETPLACE_PUSH_ROBOT_PSW }} \ No newline at end of file + UPBOUND_MARKETPLACE_PUSH_ROBOT_PSW: ${{ secrets.UPBOUND_MARKETPLACE_PUSH_ROBOT_PSW }} From 53bc3ba074421dfb04c67697f9eb84efaa2054e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20T=C3=BCrken?= Date: Mon, 6 Mar 2023 17:04:22 +0300 Subject: [PATCH 5/7] Update uptest version and add crddiff to Makefile --- Makefile | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d4686e6..83aa355 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ GO111MODULE = on KIND_VERSION = v0.15.0 UP_VERSION = v0.14.0 UP_CHANNEL = stable -UPTEST_VERSION = v0.3.0 +UPTEST_VERSION = v0.5.0 -include build/makelib/k8s_tools.mk # Setup Images @@ -125,6 +125,25 @@ local-deploy: build controlplane.up local.xpkg.deploy.provider.$(PROJECT_NAME) # - UPTEST_DATASOURCE_PATH, see https://github.com/upbound/uptest#injecting-dynamic-values-and-datasource e2e: local-deploy uptest +# TODO: please move this to the common build submodule +# once the use cases mature +crddiff: $(UPTEST) + @$(INFO) Checking breaking CRD schema changes + @for crd in $${MODIFIED_CRD_LIST}; do \ + if ! git cat-file -e "$${GITHUB_BASE_REF}:$${crd}" 2>/dev/null; then \ + echo "CRD $${crd} does not exist in the $${GITHUB_BASE_REF} branch. Skipping..." ; \ + continue ; \ + fi ; \ + echo "Checking $${crd} for breaking API changes..." ; \ + changes_detected=$$($(UPTEST) crddiff revision <(git cat-file -p "$${GITHUB_BASE_REF}:$${crd}") "$${crd}" 2>&1) ; \ + if [[ $$? != 0 ]] ; then \ + printf "\033[31m"; echo "Breaking change detected!"; printf "\033[0m" ; \ + echo "$${changes_detected}" ; \ + echo ; \ + fi ; \ + done + @$(OK) Checking breaking CRD schema changes + .PHONY: uptest e2e # ==================================================================================== # Special Targets From c743795a3e14d1de008addd53fbccea43a451476 Mon Sep 17 00:00:00 2001 From: Brian Dwyer Date: Wed, 10 May 2023 16:33:16 -0400 Subject: [PATCH 6/7] Add support for setting env variables in a workspace (cherry picked from commit 2acfe08d50f38632f89ccfe15b1d3f3fbb891c30) --- apis/v1beta1/workspace_types.go | 16 +++++++ apis/v1beta1/zz_generated.deepcopy.go | 32 +++++++++++++ internal/controller/workspace/workspace.go | 52 +++++++++++++++++++--- internal/terraform/terraform.go | 31 +++++++++++++ package/crds/tf.upbound.io_workspaces.yaml | 50 +++++++++++++++++++++ 5 files changed, 174 insertions(+), 7 deletions(-) diff --git a/apis/v1beta1/workspace_types.go b/apis/v1beta1/workspace_types.go index 9b895dc..f306193 100644 --- a/apis/v1beta1/workspace_types.go +++ b/apis/v1beta1/workspace_types.go @@ -67,6 +67,18 @@ type VarFile struct { SecretKeyReference *KeyReference `json:"secretKeyRef,omitempty"` } +// An EnvVar specifies an environment variable to be set for the workspace. +type EnvVar struct { + Name string `json:"name"` + Value string `json:"value,omitempty"` + + // A ConfigMap key containing the desired env var value. + ConfigMapKeyReference *KeyReference `json:"configMapKeyRef,omitempty"` + + // A Secret key containing the desired env var value. + SecretKeyReference *KeyReference `json:"secretKeyRef,omitempty"` +} + // A KeyReference references a key within a Secret or a ConfigMap. type KeyReference struct { // Namespace of the referenced resource. @@ -106,6 +118,10 @@ type WorkspaceParameters struct { // +optional Entrypoint string `json:"entrypoint"` + // Environment variables. + // +optional + Env []EnvVar `json:"env,omitempty"` + // Configuration variables. // +optional Vars []Var `json:"vars,omitempty"` diff --git a/apis/v1beta1/zz_generated.deepcopy.go b/apis/v1beta1/zz_generated.deepcopy.go index 7dbe2d0..53c7d00 100644 --- a/apis/v1beta1/zz_generated.deepcopy.go +++ b/apis/v1beta1/zz_generated.deepcopy.go @@ -25,6 +25,31 @@ import ( "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvVar) DeepCopyInto(out *EnvVar) { + *out = *in + if in.ConfigMapKeyReference != nil { + in, out := &in.ConfigMapKeyReference, &out.ConfigMapKeyReference + *out = new(KeyReference) + **out = **in + } + if in.SecretKeyReference != nil { + in, out := &in.SecretKeyReference, &out.SecretKeyReference + *out = new(KeyReference) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvVar. +func (in *EnvVar) DeepCopy() *EnvVar { + if in == nil { + return nil + } + out := new(EnvVar) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KeyReference) DeepCopyInto(out *KeyReference) { *out = *in @@ -441,6 +466,13 @@ func (in *WorkspaceObservation) DeepCopy() *WorkspaceObservation { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkspaceParameters) DeepCopyInto(out *WorkspaceParameters) { *out = *in + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.Vars != nil { in, out := &in.Vars, &out.Vars *out = make([]Var, len(*in)) diff --git a/internal/controller/workspace/workspace.go b/internal/controller/workspace/workspace.go index 6643418..82a5cba 100644 --- a/internal/controller/workspace/workspace.go +++ b/internal/controller/workspace/workspace.go @@ -19,6 +19,7 @@ package workspace import ( "context" "encoding/json" + "fmt" "os" "path/filepath" "strings" @@ -72,6 +73,7 @@ const ( errDestroy = "cannot destroy Terraform configuration" errVarFile = "cannot get tfvars" errVarMap = "cannot get tfvars from var map" + errVarResolution = "cannot resolve variables" errDeleteWorkspace = "cannot delete Terraform workspace" errChecksum = "cannot calculate workspace checksum" @@ -122,11 +124,13 @@ func Setup(mgr ctrl.Manager, o controller.Options, timeout time.Duration) error cps = append(cps, connection.NewDetailsManager(mgr.GetClient(), v1beta1.StoreConfigGroupVersionKind)) } c := &connector{ - kube: mgr.GetClient(), - usage: resource.NewProviderConfigUsageTracker(mgr.GetClient(), &v1beta1.ProviderConfigUsage{}), - logger: o.Logger, - fs: fs, - terraform: func(dir string) tfclient { return terraform.Harness{Path: tfPath, Dir: dir} }, + kube: mgr.GetClient(), + usage: resource.NewProviderConfigUsageTracker(mgr.GetClient(), &v1beta1.ProviderConfigUsage{}), + logger: o.Logger, + fs: fs, + terraform: func(dir string, envs ...string) tfclient { + return terraform.Harness{Path: tfPath, Dir: dir, Envs: envs} + }, } r := managed.NewReconciler(mgr, @@ -151,7 +155,7 @@ type connector struct { usage resource.Tracker logger logging.Logger fs afero.Afero - terraform func(dir string) tfclient + terraform func(dir string, envs ...string) tfclient } func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) { //nolint:gocyclo @@ -263,7 +267,41 @@ func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.E pc.Spec.PluginCache = new(bool) *pc.Spec.PluginCache = true } - tf := c.terraform(dir) + + var envs []string + for _, env := range cr.Spec.ForProvider.Env { + runtimeVal := env.Value + if runtimeVal == "" { + switch { + case env.ConfigMapKeyReference != nil: + cm := &corev1.ConfigMap{} + r := env.ConfigMapKeyReference + nn := types.NamespacedName{Namespace: r.Namespace, Name: r.Name} + if err := c.kube.Get(ctx, nn, cm); err != nil { + return nil, errors.Wrap(err, errVarResolution) + } + runtimeVal, ok = cm.Data[r.Key] + if !ok { + return nil, errors.Wrap(fmt.Errorf("couldn't find key %v in ConfigMap %v/%v", r.Key, r.Namespace, r.Name), errVarResolution) + } + case env.SecretKeyReference != nil: + s := &corev1.Secret{} + r := env.SecretKeyReference + nn := types.NamespacedName{Namespace: r.Namespace, Name: r.Name} + if err := c.kube.Get(ctx, nn, s); err != nil { + return nil, errors.Wrap(err, errVarResolution) + } + secretBytes, ok := s.Data[r.Key] + if !ok { + return nil, errors.Wrap(fmt.Errorf("couldn't find key %v in Secret %v/%v", r.Key, r.Namespace, r.Name), errVarResolution) + } + runtimeVal = string(secretBytes) + } + } + envs = append(envs, strings.Join([]string{env.Name, runtimeVal}, "=")) + } + + tf := c.terraform(dir, envs...) if cr.Status.AtProvider.Checksum != "" { checksum, err := tf.GenerateChecksum(ctx) if err != nil { diff --git a/internal/terraform/terraform.go b/internal/terraform/terraform.go index 7929abb..960ac66 100644 --- a/internal/terraform/terraform.go +++ b/internal/terraform/terraform.go @@ -119,6 +119,9 @@ type Harness struct { // Dir in which to execute the terraform binary. Dir string + // Environment Variables + Envs []string + // TODO(negz): Harness is a subset of exec.Cmd. If callers need more insight // into what the underlying Terraform binary is doing (e.g. for debugging) // we could consider allowing them to attach io.Writers to Stdout and Stdin @@ -172,6 +175,7 @@ func (h Harness) Init(ctx context.Context, cache bool, o ...InitOption) error { cmd.Env = append(cmd.Env, e) } cmd.Env = append(cmd.Env, "TF_CLI_CONFIG_FILE=./.terraformrc") + cmd.Env = append(cmd.Env, h.Envs...) err := sem.Acquire(ctx, 1) if err != nil { return errors.Wrap(err, errSemAcquire) @@ -188,6 +192,9 @@ func (h Harness) Init(ctx context.Context, cache bool, o ...InitOption) error { func (h Harness) Validate(ctx context.Context) error { cmd := exec.CommandContext(ctx, h.Path, "validate", "-json") //nolint:gosec cmd.Dir = h.Dir + if len(h.Envs) > 0 { + cmd.Env = append(os.Environ(), h.Envs...) + } type result struct { Valid bool `json:"valid"` @@ -220,6 +227,9 @@ func (h Harness) Validate(ctx context.Context) error { func (h Harness) Workspace(ctx context.Context, name string) error { cmd := exec.CommandContext(ctx, h.Path, "workspace", "select", "-no-color", name) //nolint:gosec cmd.Dir = h.Dir + if len(h.Envs) > 0 { + cmd.Env = append(os.Environ(), h.Envs...) + } if _, err := cmd.Output(); err == nil { // We successfully selected the workspace; we're done. @@ -239,6 +249,9 @@ func (h Harness) Workspace(ctx context.Context, name string) error { func (h Harness) DeleteCurrentWorkspace(ctx context.Context) error { cmd := exec.CommandContext(ctx, h.Path, "workspace", "show", "-no-color") //nolint:gosec cmd.Dir = h.Dir + if len(h.Envs) > 0 { + cmd.Env = append(os.Environ(), h.Envs...) + } n, err := cmd.Output() if err != nil { @@ -256,6 +269,9 @@ func (h Harness) DeleteCurrentWorkspace(ctx context.Context) error { } cmd = exec.CommandContext(ctx, h.Path, "workspace", "delete", "-no-color", name) //nolint:gosec cmd.Dir = h.Dir + if len(h.Envs) > 0 { + cmd.Env = append(os.Environ(), h.Envs...) + } _, err = cmd.Output() if err == nil { @@ -358,6 +374,9 @@ func (o Output) JSONValue() ([]byte, error) { func (h Harness) Outputs(ctx context.Context) ([]Output, error) { cmd := exec.CommandContext(ctx, h.Path, "output", "-json") //nolint:gosec cmd.Dir = h.Dir + if len(h.Envs) > 0 { + cmd.Env = append(os.Environ(), h.Envs...) + } type output struct { Sensitive bool `json:"sensitive"` @@ -410,6 +429,9 @@ func (h Harness) Outputs(ctx context.Context) ([]Output, error) { func (h Harness) Resources(ctx context.Context) ([]string, error) { cmd := exec.CommandContext(ctx, h.Path, "state", "list") //nolint:gosec cmd.Dir = h.Dir + if len(h.Envs) > 0 { + cmd.Env = append(os.Environ(), h.Envs...) + } out, err := cmd.Output() if err != nil { @@ -488,6 +510,9 @@ func (h Harness) Diff(ctx context.Context, o ...Option) (bool, error) { args := append([]string{"plan", "-no-color", "-input=false", "-detailed-exitcode", "-lock=false"}, ao.args...) cmd := exec.CommandContext(ctx, h.Path, args...) //nolint:gosec cmd.Dir = h.Dir + if len(h.Envs) > 0 { + cmd.Env = append(os.Environ(), h.Envs...) + } // The -detailed-exitcode flag will make terraform plan return: // 0 - Succeeded, diff is empty (no changes) @@ -516,6 +541,9 @@ func (h Harness) Apply(ctx context.Context, o ...Option) error { args := append([]string{"apply", "-no-color", "-auto-approve", "-input=false"}, ao.args...) cmd := exec.CommandContext(ctx, h.Path, args...) //nolint:gosec cmd.Dir = h.Dir + if len(h.Envs) > 0 { + cmd.Env = append(os.Environ(), h.Envs...) + } _, err := cmd.Output() return Classify(err) @@ -537,6 +565,9 @@ func (h Harness) Destroy(ctx context.Context, o ...Option) error { args := append([]string{"destroy", "-no-color", "-auto-approve", "-input=false"}, do.args...) cmd := exec.CommandContext(ctx, h.Path, args...) //nolint:gosec cmd.Dir = h.Dir + if len(h.Envs) > 0 { + cmd.Env = append(os.Environ(), h.Envs...) + } _, err := cmd.Output() return Classify(err) diff --git a/package/crds/tf.upbound.io_workspaces.yaml b/package/crds/tf.upbound.io_workspaces.yaml index 6f78e0e..c1bdfad 100644 --- a/package/crds/tf.upbound.io_workspaces.yaml +++ b/package/crds/tf.upbound.io_workspaces.yaml @@ -78,6 +78,56 @@ spec: default: "" description: Entrypoint for `terraform init` within the module type: string + env: + description: Environment variables. + items: + description: An EnvVar specifies an environment variable to + be set for the workspace. + properties: + configMapKeyRef: + description: A ConfigMap key containing the desired env + var value. + properties: + key: + description: Key within the referenced resource. + type: string + name: + description: Name of the referenced resource. + type: string + namespace: + description: Namespace of the referenced resource. + type: string + required: + - key + - name + - namespace + type: object + name: + type: string + secretKeyRef: + description: A Secret key containing the desired env var + value. + properties: + key: + description: Key within the referenced resource. + type: string + name: + description: Name of the referenced resource. + type: string + namespace: + description: Namespace of the referenced resource. + type: string + required: + - key + - name + - namespace + type: object + value: + type: string + required: + - name + type: object + type: array initArgs: description: Arguments to be included in the terraform init CLI command From bad2a8d0d04e36e8c56ca36f3f14481a43bf3017 Mon Sep 17 00:00:00 2001 From: Brian Dwyer Date: Wed, 10 May 2023 16:59:44 -0400 Subject: [PATCH 7/7] Update mocks & README (cherry picked from commit 212996a32986c7303eb84890dac5ce4ac1888bc2) --- README.md | 9 ++++++ .../controller/workspace/workspace_test.go | 28 +++++++++---------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 7edebde..e55060e 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,15 @@ spec: # Use any module source supported by terraform init -from-module. source: Remote module: https://github.com/crossplane/tf + # Environment variables can be passed through + env: + - name: TFENV_TERRAFORM_VERSION + value: '1.3.5' + - name: ENV_FROM_CONFIGMAP + configMapKeyRef: + namespace: my-namespace + name: my-config-map + key: target-key # Variables can be specified inline as a list of key-value pairs or as an json object, or loaded from a ConfigMap or Secret. vars: - key: region diff --git a/internal/controller/workspace/workspace_test.go b/internal/controller/workspace/workspace_test.go index 2d7f88f..5a2f932 100644 --- a/internal/controller/workspace/workspace_test.go +++ b/internal/controller/workspace/workspace_test.go @@ -123,7 +123,7 @@ func TestConnect(t *testing.T) { kube client.Client usage resource.Tracker fs afero.Afero - terraform func(dir string) tfclient + terraform func(dir string, envs ...string) tfclient } type args struct { @@ -215,7 +215,7 @@ func TestConnect(t *testing.T) { }, usage: resource.TrackerFn(func(_ context.Context, _ resource.Managed) error { return nil }), fs: afero.Afero{Fs: afero.NewMemMapFs()}, - terraform: func(_ string) tfclient { + terraform: func(_ string, _ ...string) tfclient { return &MockTf{ MockInit: func(ctx context.Context, cache bool, o ...terraform.InitOption) error { return nil }, } @@ -254,7 +254,7 @@ func TestConnect(t *testing.T) { errs: map[string]error{filepath.Join(tfDir, string(uid), tfCreds): errBoom}, }, }, - terraform: func(_ string) tfclient { + terraform: func(_ string, _ ...string) tfclient { return &MockTf{ MockInit: func(ctx context.Context, cache bool, o ...terraform.InitOption) error { return nil }, } @@ -293,7 +293,7 @@ func TestConnect(t *testing.T) { errs: map[string]error{filepath.Join(tfDir, string(uid), "subdir", tfCreds): errBoom}, }, }, - terraform: func(_ string) tfclient { + terraform: func(_ string, _ ...string) tfclient { return &MockTf{ MockInit: func(ctx context.Context, cache bool, o ...terraform.InitOption) error { return nil }, } @@ -337,7 +337,7 @@ func TestConnect(t *testing.T) { errs: map[string]error{filepath.Join("/tmp", tfDir, string(uid), ".git-credentials"): errBoom}, }, }, - terraform: func(_ string) tfclient { + terraform: func(_ string, _ ...string) tfclient { return &MockTf{ MockInit: func(ctx context.Context, cache bool, o ...terraform.InitOption) error { return nil }, } @@ -380,7 +380,7 @@ func TestConnect(t *testing.T) { errs: map[string]error{filepath.Join("/tmp", tfDir, string(uid)): errBoom}, }, }, - terraform: func(_ string) tfclient { + terraform: func(_ string, _ ...string) tfclient { return &MockTf{ MockInit: func(ctx context.Context, cache bool, o ...terraform.InitOption) error { return nil }, } @@ -421,7 +421,7 @@ func TestConnect(t *testing.T) { errs: map[string]error{filepath.Join(tfDir, string(uid), tfConfig): errBoom}, }, }, - terraform: func(_ string) tfclient { + terraform: func(_ string, _ ...string) tfclient { return &MockTf{ MockInit: func(ctx context.Context, cache bool, o ...terraform.InitOption) error { return nil }, } @@ -462,7 +462,7 @@ func TestConnect(t *testing.T) { errs: map[string]error{filepath.Join(tfDir, string(uid), "subdir", tfConfig): errBoom}, }, }, - terraform: func(_ string) tfclient { + terraform: func(_ string, _ ...string) tfclient { return &MockTf{ MockInit: func(ctx context.Context, cache bool, o ...terraform.InitOption) error { return nil }, } @@ -498,7 +498,7 @@ func TestConnect(t *testing.T) { errs: map[string]error{filepath.Join(tfDir, string(uid), tfMain): errBoom}, }, }, - terraform: func(_ string) tfclient { + terraform: func(_ string, _ ...string) tfclient { return &MockTf{ MockInit: func(ctx context.Context, cache bool, o ...terraform.InitOption) error { return nil }, } @@ -528,7 +528,7 @@ func TestConnect(t *testing.T) { }, usage: resource.TrackerFn(func(_ context.Context, _ resource.Managed) error { return nil }), fs: afero.Afero{Fs: afero.NewMemMapFs()}, - terraform: func(_ string) tfclient { + terraform: func(_ string, _ ...string) tfclient { return &MockTf{MockInit: func(_ context.Context, cache bool, _ ...terraform.InitOption) error { return errBoom }} }, }, @@ -552,7 +552,7 @@ func TestConnect(t *testing.T) { }, usage: resource.TrackerFn(func(_ context.Context, _ resource.Managed) error { return nil }), fs: afero.Afero{Fs: afero.NewMemMapFs()}, - terraform: func(_ string) tfclient { + terraform: func(_ string, _ ...string) tfclient { return &MockTf{ MockInit: func(ctx context.Context, cache bool, o ...terraform.InitOption) error { return nil }, MockWorkspace: func(_ context.Context, _ string) error { return errBoom }, @@ -578,7 +578,7 @@ func TestConnect(t *testing.T) { }, usage: resource.TrackerFn(func(_ context.Context, _ resource.Managed) error { return nil }), fs: afero.Afero{Fs: afero.NewMemMapFs()}, - terraform: func(_ string) tfclient { + terraform: func(_ string, _ ...string) tfclient { return &MockTf{ MockGenerateChecksum: func(ctx context.Context) (string, error) { return "", errBoom }, } @@ -612,7 +612,7 @@ func TestConnect(t *testing.T) { }, usage: resource.TrackerFn(func(_ context.Context, _ resource.Managed) error { return nil }), fs: afero.Afero{Fs: afero.NewMemMapFs()}, - terraform: func(_ string) tfclient { + terraform: func(_ string, _ ...string) tfclient { return &MockTf{ MockGenerateChecksum: func(ctx context.Context) (string, error) { return tfChecksum, nil }, MockWorkspace: func(_ context.Context, _ string) error { return nil }, @@ -648,7 +648,7 @@ func TestConnect(t *testing.T) { }, usage: resource.TrackerFn(func(_ context.Context, _ resource.Managed) error { return nil }), fs: afero.Afero{Fs: afero.NewMemMapFs()}, - terraform: func(_ string) tfclient { + terraform: func(_ string, _ ...string) tfclient { return &MockTf{ MockInit: func(ctx context.Context, cache bool, o ...terraform.InitOption) error { return nil }, MockGenerateChecksum: func(ctx context.Context) (string, error) { return tfChecksum, nil },