Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions .github/workflows/acceptance-kiota.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Runs the kiota pilot's generated acceptance tests against a live ThousandEyes tenant.
#
# A separate workflow from acceptance.yml rather than a matrix leg, deliberately: the two
# pilots exist to prove two dialects of the same generator, and a failure must name which
# dialect broke without reading logs. The resty pilot passing while this one fails is the
# single most useful signal this workflow can produce -- it means the API did not move,
# the kiota binding did.
#
# Everything else follows the resty workflow's rules, for the same reasons it has them:
# hand-triggered or scheduled, never `pull_request`, because the token is a long-lived
# credential the ThousandEyes API cannot revoke; gated on the same `acceptance`
# environment, whose secrets this run reuses -- one tenant, one credential, two dialects.
name: go | Acceptance tests (kiota)

# No workflow_dispatch inputs; see acceptance.yml for the injection and CKV_GHA_7
# reasoning, which applies here unchanged.
on:
workflow_dispatch:
schedule:
# Weekly, Monday 07:30 UTC: after the resty run's 06:00 slot. The shared concurrency
# group below is what actually serialises them; the offset just keeps the queue short.
- cron: "30 7 * * 1"

permissions:
contents: read

# The SAME group as acceptance.yml, not a kiota-specific one. Both pilots create objects
# in the same tenant with the same tfacc- fixture prefixes, so two live runs -- whichever
# workflows they came from -- would fight over state that is not Terraform's.
concurrency:
group: acceptance-${{ github.repository }}
cancel-in-progress: false

jobs:
acceptance:
name: "🌍 Acceptance, kiota dialect (live tenant)"
runs-on: ubuntu-24.04-arm
# The same gated environment as the resty run: its required reviewers and its
# THOUSANDEYES_* secrets apply to both dialects identically.
environment: acceptance

steps:
- name: Harden Runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit

- name: Check Out
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: "go.mod"
cache-dependency-path: "go.sum"
cache: true

# terraform-plugin-testing downloads a Terraform binary at run time unless one is on
# the PATH; pinning it here keeps a scheduled failure unambiguous between API drift
# and CLI drift -- the same version the resty workflow pins.
- name: Set up Terraform
uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1
with:
terraform_version: "1.14.*"
terraform_wrapper: false

- name: Refuse to run without a credential
env:
THOUSANDEYES_BEARER_TOKEN: ${{ secrets.THOUSANDEYES_BEARER_TOKEN }}
run: |
if [ -z "${THOUSANDEYES_BEARER_TOKEN}" ]; then
echo "::error::THOUSANDEYES_BEARER_TOKEN is not set for the 'acceptance' environment."
echo "Every acceptance test would skip, and the run would report success having"
echo "tested nothing. That is the failure this step exists to prevent."
exit 1
fi
echo "✅ A credential is present."

# TF_ACC is what terraform-plugin-testing gates on; without it every TestAcc skips.
# The token reaches the provider through the environment and never through a flag,
# because a flag lands in the process table and in shell history.
- name: Run the acceptance tests
working-directory: pilot/thousandeyes-kiota
env:
TF_ACC: "1"
THOUSANDEYES_BEARER_TOKEN: ${{ secrets.THOUSANDEYES_BEARER_TOKEN }}
THOUSANDEYES_ACCOUNT_GROUP_ID: ${{ secrets.THOUSANDEYES_ACCOUNT_GROUP_ID }}
run: |
echo "::group::📦 Download"
go mod download
echo "::endgroup::"

# -count=1 defeats the test cache -- a cached pass proves nothing about a live
# API -- and -p 1 serialises test packages against the one shared tenant, both
# for the reasons acceptance.yml records at length.
go test \
-v \
-count=1 \
-p 1 \
-timeout 90m \
-run 'TestAcc' \
./...

# No sweep step, exactly as in acceptance.yml and for the reasons it records:
# terraform-plugin-testing destroys what each test created, the generated
# CheckDestroyed proves the API agrees, and `probe sweep` tracks the prober's
# ledger, not Terraform's objects.
- name: Note on cleanup
if: always()
run: |
echo "Acceptance tests destroy what they create; CheckDestroyed fails the test if the"
echo "API still holds the object. If this job was cancelled or the runner died, look"
echo "for objects prefixed 'tfacc-' in the tenant."
61 changes: 61 additions & 0 deletions .github/workflows/codegen-verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ jobs:
-blueprint blueprints/thousandeyes \
-out pilot/thousandeyes
echo "::endgroup::"
echo "::group::🔁 Regenerating the kiota pilot provider"
go run ./cmd/tfpfgen provider generate \
-blueprint blueprints/thousandeyes-kiota \
-out pilot/thousandeyes-kiota
echo "::endgroup::"

# git diff rather than `tfpfgen provider generate -check`, deliberately. verify compares
# the files the blueprints produce against disk, so it cannot see a stale file
Expand Down Expand Up @@ -204,6 +209,11 @@ jobs:
-module pilot/thousandeyes \
-out blueprints/thousandeyes/static.facts.json -check
echo "::endgroup::"
echo "::group::🔗 Bindings (kiota pilot)"
go run ./cmd/tfpfgen bindings check \
-blueprint blueprints/thousandeyes-kiota \
-module pilot/thousandeyes-kiota
echo "::endgroup::"

interop:
name: "🔀 Round-trip through tfplugingen-framework"
Expand Down Expand Up @@ -306,6 +316,57 @@ jobs:

echo "✅ HashiCorp's own generator accepted the export and produced formatted Go."

sdk-drift:
name: "🧰 Kiota SDK matches the snapshot"
runs-on: ubuntu-24.04-arm
if: github.event.pull_request.draft == false
env:
# The version must equal the committed kiota-lock.json's kiotaVersion --
# tfpfgen refuses otherwise -- and the checksum is recorded here because
# the release publishes none.
KIOTA_VERSION: "1.34.1"
KIOTA_SHA256: "0dc6ccc5d00cd870a47434d8209758f708ff2b905d318731407210c0c6938144"

steps:
- name: Harden Runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit

- name: Check Out
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: "go.mod"
cache-dependency-path: "go.sum"
cache: true

- name: Install the pinned kiota
run: |
echo "::group::🧰 kiota v${KIOTA_VERSION}"
curl -sL -o /tmp/kiota.zip \
"https://github.com/microsoft/kiota/releases/download/v${KIOTA_VERSION}/linux-arm64.zip"
echo "${KIOTA_SHA256} /tmp/kiota.zip" | sha256sum -c -
mkdir -p "$HOME/kiota"
unzip -q /tmp/kiota.zip -d "$HOME/kiota"
chmod +x "$HOME/kiota/kiota"
echo "$HOME/kiota" >> "$GITHUB_PATH"
echo "::endgroup::"

- name: Regenerate the SDK and diff
run: |
echo "::group::🧰 sdk generate -check"
go run ./cmd/tfpfgen sdk generate \
-out pilot/thousandeyes-kiota/internal/sdk \
-include "/tags,/tags/**" \
-client-name ThousandEyesClient \
-check
echo "::endgroup::"

probe-replay:
name: "🔬 Re-derive probe facts offline"
runs-on: ubuntu-24.04-arm
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@ This file is maintained by [release-please](.github/workflows/release-please.yml
from conventional commit messages. Add entries by writing good commit messages,
not by editing this file by hand.

## Unreleased — Kiota SDK generation and the kiotaFluent dialect

`tfpfgen sdk generate` derives a Go SDK from a pinned OpenAPI snapshot with
Microsoft Kiota (PATH tool, hard version gate on the committed
kiota-lock.json; `-mode embed` into the provider module by default,
`-mode external` with its own go.mod; `-check` regenerates and
byte-compares). The reserved `kiotaFluent` dialect is implemented end to
end: blueprints record fluent chains as data (`Operation.chain`), the
emitter renders them with method access, nil-result guards and enum parse
companions, `bindings check` walks chains and Get/Set pairs against the
real SDK with did-you-mean, `blueprint draft -sdk-dialect kiotaFluent`
infers the whole shape from the snapshot, and `provider.sdk` gains
`mode`/`generator` with a go.mod assertion in the postcheck. A second
pilot, `pilot/thousandeyes-kiota`, binds the `tag` resource against an
embedded kiota SDK and re-derives identical facts from the shared
`recordings/thousandeyes` — the probe layer is wire-level, so switching
dialect is a binding change, not an evidence change.

## Unreleased — provider push

`tfpfgen provider push -out DIR -repo URL` publishes the generated provider
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,13 @@ flowchart LR
bp -. "provider generate -check\n(fails on drift)" .-> prov
```

with drift checking: `provider generate -check`. Alongside the main line,
with drift checking: `provider generate -check`. An optional `sdk generate`
stage sits between the snapshot and the blueprints: it derives a Go SDK from
the same pinned OpenAPI document with [Microsoft
Kiota](https://github.com/microsoft/kiota) — embedded in the provider tree or
as a standalone module — and the `kiotaFluent` dialect binds fluent
request-builder chains against it, so a provider no longer requires a
hand-written SDK. Alongside the main line,
`bindings facts` derives static facts from the pinned SDK for `blueprint merge`
to fold in, human curation (scenarios, hints, presence) feeds the same merge,
and `provider generate` finishes with a postcheck battery (build · docs · fmt).
Expand Down
55 changes: 55 additions & 0 deletions blueprints/thousandeyes-kiota/provider.blueprint.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading