feat: deprecate meshstack_tenant in favor of meshstack_tenant_v4 #1397
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Terraform Provider testing workflow. | |
| name: Tests | |
| # This GitHub action runs your tests for each pull request and push. | |
| # Optionally, you can turn it on using a schedule for regular testing. | |
| on: | |
| pull_request: | |
| paths-ignore: | |
| - 'README.md' | |
| push: | |
| branches: | |
| - main | |
| paths-ignore: | |
| - 'README.md' | |
| # Testing only needs permissions to read the repository contents. | |
| permissions: | |
| contents: read | |
| # Cancel superseded runs on the same ref (the acceptance job is expensive). | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| build: | |
| name: Go Build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 | |
| with: | |
| go-version-file: 'go.mod' | |
| cache: true | |
| - run: go mod tidy | |
| - run: go build -v . | |
| - name: git diff | |
| run: | | |
| git diff --compact-summary --exit-code || \ | |
| (echo; echo "Unexpected difference in directories after 'go mod tidy'. Run 'go mod tidy' command and commit."; exit 1) | |
| golangci: | |
| needs: [ build ] | |
| name: Go Lint and Format Check | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: read # Required for only-new-issues on PRs | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 | |
| with: | |
| go-version: stable | |
| - name: golangci-lint | |
| uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0 | |
| with: | |
| version: latest | |
| only-new-issues: true # Show only issues in changed code on PRs | |
| - name: Suggest fix command on failure | |
| if: failure() | |
| run: | | |
| echo "::error::Linting or formatting issues detected. Run 'golangci-lint run --fix' locally to automatically fix these issues, then commit the changes." | |
| generate: | |
| name: Generate Terraform Provider Docs | |
| needs: [ build ] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 | |
| with: | |
| go-version-file: 'go.mod' | |
| cache: true | |
| - uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1 | |
| with: | |
| terraform_wrapper: false | |
| - run: go generate | |
| - name: git diff | |
| run: | | |
| git diff --compact-summary --exit-code || \ | |
| (echo; echo "Unexpected difference in directories after code generation. Run 'go generate ./...' command and commit."; exit 1) | |
| test: | |
| name: Go Test | |
| needs: [ build ] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: write # For PR comments | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| fetch-depth: 0 # For base branch coverage comparison | |
| - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 | |
| with: | |
| go-version-file: 'go.mod' | |
| cache: true | |
| # Emit coverage as a binary coverage-data directory (GOCOVERDIR format) rather than a text | |
| # profile, so the acceptance job on a different runner can merge it with its own live-backend | |
| # coverage via `go tool covdata merge`. `-coverpkg=./...` attributes coverage across all | |
| # packages; the test binaries flush coverage on exit even when a test fails. | |
| - name: Run unit tests with gotestsum | |
| run: | | |
| mkdir -p covdata/unit | |
| go tool gotestsum --junitfile junit.xml --format testdox -- \ | |
| -coverpkg=./... ./... -args -test.gocoverdir="$PWD/covdata/unit" | |
| # Hand the unit coverage data to the acceptance job (a separate, self-hosted runner). Always | |
| # upload, even on test failure, so the combined report can still be produced. | |
| - name: Upload unit coverage data | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: covdata-unit | |
| path: covdata/unit | |
| retention-days: 1 | |
| if-no-files-found: warn | |
| # Post (or refresh) the single coverage comment with the unit figure and a placeholder for the | |
| # combined number. The acceptance job rewrites this same comment (matched by the HTML marker) | |
| # once it has merged in the live-backend coverage. | |
| - name: Post coverage to PR (unit; acceptance pending) | |
| if: ${{ always() && github.event_name == 'pull_request' }} | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| REPO: ${{ github.repository }} | |
| PR: ${{ github.event.pull_request.number }} | |
| shell: bash | |
| run: .github/scripts/coverage-comment.sh unit | |
| # Acceptance tests against a real, fully dockerized meshStack backend brought up from pre-built | |
| # images as service containers. Runs on the org's self-hosted mesh-runners (which can pull the | |
| # private GAR images); forks are skipped (no runners / secrets / MESHFED_REGISTRY variable). | |
| # The internal registry path comes from the MESHFED_REGISTRY repo/org configuration variable so | |
| # it is not hardcoded here. Canonical local equivalent: meshfed-release/docker-compose.acc.yml. | |
| acceptance: | |
| name: Go Acceptance Test | |
| needs: [ build, test ] | |
| # Run after the unit job (so its coverage artifact exists to merge) even when that job's tests | |
| # failed, but only on a green build and on a non-fork PR, a push to main, or a manual dispatch | |
| # (forks lack the self-hosted runners, the secrets and the MESHFED_REGISTRY variable). Running | |
| # on the main push (not just PRs) is what surfaces a provider/backend regression before a | |
| # release tag — the same failure that meshfed-release's source-built acceptance job would hit. | |
| if: >- | |
| always() && needs.build.result == 'success' && | |
| (github.event_name == 'workflow_dispatch' || | |
| github.event_name == 'push' || | |
| github.event.pull_request.head.repo.full_name == github.repository) | |
| runs-on: mesh-runners | |
| permissions: | |
| contents: read | |
| pull-requests: write # for the coverage comment | |
| # ARC runs these self-hosted runners in kubernetes container mode, where a job using service | |
| # containers MUST declare a job container — so nix-ci stays as the (GAR-available, proven) base. | |
| # The Go toolchain + module/build cache come from actions/setup-go below, aligned with the other | |
| # jobs in this workflow; tofu is provided by the nix-ci image. | |
| container: | |
| image: ${{ vars.MESHFED_REGISTRY }}/meshstack-container/nix-ci:latest | |
| # Full backend as service containers. On mesh-runners all services + the job container share one | |
| # network, so the images' baked localhost config resolves between them. Ports are de-conflicted | |
| # (meshfed-api on 8089 so the tf-runner's fixed :8080 health server does not collide). JVM | |
| # services use `--restart on-failure` to absorb the mariadb startup race; the readiness step is | |
| # the real gate. | |
| services: | |
| mariadb: | |
| image: ${{ vars.MESHFED_REGISTRY }}/meshstack-container/mariadb-ci | |
| ravendb: | |
| image: ${{ vars.MESHFED_REGISTRY }}/meshstack-container/ravendb-ci | |
| rabbitmq: | |
| image: ${{ vars.MESHFED_REGISTRY }}/dockerhub/rabbitmq:3.11.18-alpine | |
| env: | |
| RABBITMQ_DEFAULT_USER: guest | |
| RABBITMQ_DEFAULT_PASS: guest | |
| keycloak: | |
| image: ${{ vars.MESHFED_REGISTRY }}/meshstack-container/keycloak-ci:26 | |
| meshfed-api: | |
| image: ${{ vars.MESHFED_REGISTRY }}/meshstack-container/meshfed:latest | |
| env: | |
| SERVER_PORT: "8089" # off 8080 so the tf-runner health server can have it; mgmt 8180 | |
| options: --restart on-failure | |
| replicator: | |
| image: ${{ vars.MESHFED_REGISTRY }}/meshstack-container/replicator:latest | |
| env: | |
| SERVER_PORT: "7080" # move embedded server off 8080; mgmt 7180 | |
| options: --restart on-failure | |
| block-coordinator-api: | |
| image: ${{ vars.MESHFED_REGISTRY }}/meshstack-container/block-coordinator-api:latest | |
| options: --restart on-failure | |
| # Single poller of the magic runner UUID; fans work out by type. 127.0.0.1 forces IPv4 (the Go | |
| # mux would otherwise resolve localhost to [::1], which Tomcat does not listen on). The mux | |
| # holds no credentials: it forwards each runner's own Authorization on the claim, overriding | |
| # only forRunnerUuid with the magic UUID. | |
| multiplexing-block-runner: | |
| image: ${{ vars.MESHFED_REGISTRY }}/meshstack-container/multiplexing-block-runner-ci:latest | |
| env: | |
| BB_RUNNER_MUX_UPSTREAM_URL: http://127.0.0.1:8089 | |
| options: --restart on-failure | |
| # Standalone runners poll their mux port; own UUID is irrelevant (mux claims as the magic UUID), | |
| # so it is an arbitrary placeholder. They authenticate with the seeded managed-runners API key | |
| # (RUNNER_API_CLIENT_ID/SECRET → Bearer via POST /api/login, proxied through the mux) — the | |
| # runner-scoped key, NOT the broad provider admin key (MESHSTACK_API_KEY) which only the | |
| # provider client uses below. No basic auth. tf-block-runner bakes the matching dev private key | |
| # (secret decryption works out of the box) and installs OpenTofu at runtime via Nix. | |
| # | |
| # Pinned to :main (not :latest): the building-block-runner repo only refreshes :latest on a | |
| # tagged release, while :main is rebuilt on every merge to main. Tracking :main keeps the auth | |
| # fixes (API-key-over-basic-auth precedence) that let the tf runner work in this acc stack. | |
| tf-block-runner: | |
| image: ghcr.io/meshcloud/tf-block-runner:main | |
| env: | |
| RUNNER_API_URL: http://127.0.0.1:8300 | |
| RUNNER_UUID: 11111111-1111-1111-1111-111111111111 | |
| RUNNER_API_CLIENT_ID: ${{ secrets.RUNNER_API_CLIENT_ID }} | |
| RUNNER_API_CLIENT_SECRET: ${{ secrets.RUNNER_API_CLIENT_SECRET }} | |
| options: --restart on-failure | |
| manual-block-runner: | |
| image: ghcr.io/meshcloud/manual-block-runner:main # track merged main, see tf-block-runner | |
| env: | |
| RUNNER_API_URL: http://127.0.0.1:8301 | |
| RUNNER_UUID: 22222222-2222-2222-2222-222222222222 | |
| RUNNER_API_CLIENT_ID: ${{ secrets.RUNNER_API_CLIENT_ID }} | |
| RUNNER_API_CLIENT_SECRET: ${{ secrets.RUNNER_API_CLIENT_SECRET }} | |
| SERVER_PORT: "8104" # move its embedded web server off 8080 | |
| options: --restart on-failure | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 | |
| with: | |
| go-version-file: 'go.mod' | |
| cache: true | |
| # Unit coverage data from the `test` job (a different runner). `continue-on-error` so a missing | |
| # artifact (e.g. the unit job produced none) does not fail acceptance — the report step then | |
| # falls back to acceptance-only coverage. | |
| - name: Download unit coverage data | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: covdata-unit | |
| path: covdata/unit | |
| - name: Wait for backend to be ready | |
| timeout-minutes: 8 | |
| shell: bash | |
| run: | | |
| wait_http() { | |
| local url="$1" name="$2" deadline=$(( SECONDS + ${3:-360} )) | |
| echo "waiting for $name ($url)..." | |
| until curl -sf "$url" -o /dev/null; do | |
| [ "$SECONDS" -gt "$deadline" ] && { echo "::error::timeout waiting for $name ($url)"; return 1; } | |
| sleep 3 | |
| done | |
| echo "$name healthy" | |
| } | |
| # meshfed-api is the gate: it bootstraps API keys into Keycloak, so tests run before it is | |
| # healthy fail with auth errors. | |
| wait_http http://localhost:8180/actuator/health meshfed-api 420 | |
| wait_http http://localhost:7180/actuator/health replicator 240 | |
| wait_http http://localhost:8083/actuator/health block-coordinator-api 240 | |
| wait_http http://localhost:8309/healthz mux 120 | |
| echo "mux routing:"; curl -s http://localhost:8309/status || true | |
| - name: Run acceptance tests with coverage | |
| id: acc | |
| # Advisory ON PRs ONLY: a failed acceptance run must never block a PR, because the job runs | |
| # against the last *merged* meshfed-release backend images, so a change needing a companion | |
| # backend PR cannot go green here until that backend merges — gating on it would deadlock | |
| # cross-repo changes. On PRs, meshfed-release's source-built `terraform-provider-acceptance` | |
| # job is the real gate and failures here only surface via an advisory comment (see the last | |
| # step). On a push to main (and manual dispatch) there is no PR to comment on and the backend | |
| # images are already merged, so the run is NOT advisory: a failure reds the job, surfacing a | |
| # provider/backend regression here before a release tag — which is what this CI run would | |
| # otherwise only catch in meshfed-release. | |
| continue-on-error: ${{ github.event_name == 'pull_request' }} | |
| env: | |
| # The acceptance API key/secret (seeded by the mariadb-ci dev dump) come from repo secrets. | |
| MESHSTACK_ENDPOINT: http://localhost:8089 | |
| MESHSTACK_API_KEY: ${{ secrets.MESHSTACK_API_KEY }} | |
| MESHSTACK_API_SECRET: ${{ secrets.MESHSTACK_API_SECRET }} | |
| MESHSTACK_SKIP_VERSION_CHECK: "true" | |
| TF_ACC: "1" | |
| TF_ACC_PROVIDER_HOST: registry.opentofu.org | |
| shell: bash | |
| run: | | |
| mkdir -p covdata/acc | |
| export TF_ACC_TERRAFORM_PATH="$(command -v tofu)" | |
| # Select acceptance tests by the `TestAcc` naming convention across ALL packages (./...) | |
| # rather than restricting to ./internal/provider, so acceptance tests added in any package | |
| # are picked up automatically. Coverage is attributed across all packages via | |
| # -coverpkg=./... so the merged report reflects the real end-to-end exercise of the client | |
| # and helper packages too; it is emitted as a binary coverage-data dir for merging with the | |
| # unit job's data. | |
| # | |
| # Tee the output to a file so the next step can verify the run actually finished: a | |
| # self-hosted-runner truncation can cut the test process short while this step still exits | |
| # 0, which would otherwise pass as a green run. The default shell is `bash -eo pipefail`, | |
| # so a genuine test failure still propagates through the pipe and fails this step. | |
| go tool gotestsum --junitfile junit-acc.xml --format testdox -- \ | |
| -coverpkg=./... -count=1 -timeout 10m -run 'TestAcc' ./... \ | |
| -args -test.gocoverdir="$PWD/covdata/acc" 2>&1 | tee acc-output.log | |
| # Guard against the self-hosted runner truncating/killing the test process while the step above | |
| # still reports success (seen here, and in meshfed-release's e2e job). gotestsum prints a final | |
| # "DONE N tests ... in Xs" summary only when the run completes, so its absence means the run was | |
| # cut short and the result is UNKNOWN — fail hard so it can never be mistaken for green. This is | |
| # distinct from an advisory test failure: a completed run (DONE present) keeps `continue-on-error` | |
| # semantics below; only an incomplete run reds the job and demands a re-run. Exposes `complete` | |
| # for the coverage/advisory steps. Sets the output before exiting so they can still read it. | |
| - name: Verify acceptance run completed | |
| id: acccheck | |
| if: ${{ always() }} | |
| shell: bash | |
| run: | | |
| if [ -s acc-output.log ] && grep -qE '^DONE [0-9]+ tests' acc-output.log; then | |
| echo "complete=true" >> "$GITHUB_OUTPUT" | |
| echo "Acceptance run reached the gotestsum summary — the result is trustworthy." | |
| else | |
| echo "complete=false" >> "$GITHUB_OUTPUT" | |
| echo "::error::Acceptance run did not complete: no gotestsum 'DONE' summary in acc-output.log. The self-hosted runner truncated or killed the run, so the result is UNKNOWN (not a pass). Re-run the job." | |
| exit 1 | |
| fi | |
| # Merge the unit coverage (downloaded from the test job) with this job's acceptance coverage and | |
| # rewrite the single coverage comment with the combined figure. Always runs — even when the | |
| # acceptance tests fail the instrumented binaries still flush their coverage on exit — and | |
| # annotates the combined row with the acceptance outcome / incompleteness. | |
| - name: Report combined coverage on PR | |
| if: ${{ always() && github.event_name == 'pull_request' }} | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| REPO: ${{ github.repository }} | |
| PR: ${{ github.event.pull_request.number }} | |
| ACC_OUTCOME: ${{ steps.acc.outcome }} | |
| ACC_COMPLETE: ${{ steps.acccheck.outputs.complete }} | |
| shell: bash | |
| run: .github/scripts/coverage-comment.sh combined | |
| # Advisory PR comment: the acceptance result never blocks (continue-on-error above). Post a | |
| # comment when the latest run failed so the regression stays visible, and delete it once a later | |
| # run goes green. Idempotent via the HTML marker; only on PRs (needs a PR number). | |
| - name: Manage acceptance advisory comment | |
| if: ${{ always() && github.event_name == 'pull_request' }} | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| REPO: ${{ github.repository }} | |
| PR: ${{ github.event.pull_request.number }} | |
| OUTCOME: ${{ steps.acc.outcome }} | |
| COMPLETE: ${{ steps.acccheck.outputs.complete }} | |
| SERVER_URL: ${{ github.server_url }} | |
| RUN_ID: ${{ github.run_id }} | |
| RUN_ATTEMPT: ${{ github.run_attempt }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| MARKER='<!-- acceptance-advisory -->' | |
| # Link straight to THIS acceptance job, not the run: the run page shows green on purpose | |
| # (the acc step is continue-on-error), so the failing/truncated step is only visible inside | |
| # the job. Resolve the job's html_url by matching the ephemeral runner that ran it; fall back | |
| # to the run URL if the lookup fails. | |
| RUN_URL="$SERVER_URL/$REPO/actions/runs/$RUN_ID" | |
| JOB_URL=$(gh api "repos/$REPO/actions/runs/$RUN_ID/attempts/$RUN_ATTEMPT/jobs" \ | |
| --jq ".jobs[] | select(.runner_name == \"${RUNNER_NAME:-}\") | .html_url" 2>/dev/null | head -n1 || true) | |
| [ -n "$JOB_URL" ] || JOB_URL="$RUN_URL" | |
| existing=$(gh api "repos/$REPO/issues/$PR/comments" --paginate \ | |
| --jq ".[] | select(.body | startswith(\"$MARKER\")) | .id" | head -n1 || true) | |
| upsert() { # $1 = body | |
| if [ -n "$existing" ]; then | |
| gh api -X PATCH "repos/$REPO/issues/comments/$existing" -f body="$1" >/dev/null | |
| echo "updated advisory comment $existing" | |
| else | |
| gh api -X POST "repos/$REPO/issues/$PR/comments" -f body="$1" >/dev/null | |
| echo "posted advisory comment" | |
| fi | |
| } | |
| # Incomplete run (truncated/killed by the runner): the result is unknown, so surface it | |
| # loudly and never delete an existing warning by mistaking it for a pass. | |
| if [ "$COMPLETE" != "true" ]; then | |
| upsert "$(printf '%s\n%s\n\n%s' "$MARKER" \ | |
| "🛑 **The acceptance run did not complete.** The self-hosted runner truncated or killed the test process before gotestsum reported a summary, so the result is **unknown — not a pass**. Re-run the job; if it recurs it is a runner-infra issue." \ | |
| "🔗 [View the acceptance job]($JOB_URL)")" | |
| exit 0 | |
| fi | |
| # Completed run — act only on a definitive pass/fail; ignore cancelled/skipped. | |
| case "$OUTCOME" in | |
| success) | |
| if [ -n "$existing" ]; then | |
| gh api -X DELETE "repos/$REPO/issues/comments/$existing" | |
| echo "acceptance green — deleted advisory comment $existing" | |
| else | |
| echo "acceptance green — no advisory comment to delete" | |
| fi | |
| ;; | |
| failure) | |
| upsert "$(printf '%s\n%s\n\n%s' "$MARKER" \ | |
| "⚠️ **Acceptance tests failed on the latest run.** This is advisory and does **not** block merge — the job runs against the last merged meshfed-release backend images. If this change needs a companion backend change, validate the pair via meshfed-release's \`terraform-provider-acceptance\` job; otherwise this is a regression to fix." \ | |
| "🔗 [View the failing acceptance job]($JOB_URL)")" | |
| ;; | |
| *) | |
| echo "acceptance outcome '$OUTCOME' — leaving any existing comment untouched" | |
| ;; | |
| esac |