fix(terraform): allow juju provider 1.x and 2.x#1657
Draft
marceloneppel wants to merge 14 commits into
Draft
Conversation
The juju terraform provider constraint was pinned to '~> 1.0' (>= 1.0.0, < 2.0.0), which blocks higher-level modules that require juju provider 2.x (Juju 4.x controllers). Widening to '>= 1.0, < 3.0' lets the module compose with both v1 and v2 consumers while keeping a ceiling against an unvetted major 3.x. v1 consumers still resolve to the latest 1.x (e.g. v1.5.6); v2 consumers resolve to the latest 2.x (e.g. v2.1.1). Unsatisfiable and beyond-cap constraints fail as expected. The v1->v2 provider upgrade is non-breaking for the resource surface this module uses (juju_application). Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
The terraform module shipped with no CI: 'terraform/**' was in ci.yaml's paths-ignore, so terraform-only changes ran no checks and a broken/unresolvable provider constraint would land unnoticed (the exact failure mode downstream consumers hit when pairing this module with a juju provider 2.x requirement). Add a lightweight lane mirroring canonical/iam-bundle-integration's static checks, plus a real-deploy test like canonical/charmed-kubeflow-solutions: - .github/workflows/terraform.yaml: triggered on terraform/** changes (the inverse of the path-ignore). Static job runs fmt/init/validate against the live registry, then the constraint-composition suite. - tests/terraform/test_compositions.py (tox -e terraform): parametrized cases that source the real module alongside a sibling juju provider constraint (v1, v2, unsatisfiable, beyond-cap) and assert terraform init resolves (or fails) as expected. This is the regression guard for the constraint widened in the previous commit. - tests/integration/terraform/test_terraform_deploy.py: jubilant test that applies the module (published 16/stable charm) into the concierge 'testing' model and waits for active/idle. Runs via the existing spread integration lane (1:1 task at tests/spread/test_terraform_deploy.py); no actions-operator. - terraform/.gitignore: ignore .terraform/ and the lock file so a consumed module never ships a pin that overrides downstream constraint resolution. Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
…action The repo's zizmor policy (.github/zizmor.yaml) allowlists third-party actions to a trusted set (canonical/*, actions/*, docker/login-action, tiobe/tics-github-action); hashicorp/setup-terraform is not on it, so the Lint .github/workflows/ job failed with forbidden-uses (and unpinned-uses). Installing terraform via snap needs no allowlist entry and adds no RCE-capable action to the trusted set, matching how the spread deploy task already provisions terraform. Verified with zizmor 1.27.0: no findings. Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
The generated terraform-docs tables still showed the juju provider as '~> 1.0.0' after the constraint was widened to '>= 1.0, < 3.0'. Sync the Requirements and Providers tables so the rendered module docs match versions.tf. Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
…e reason The '>= 2.9' case only failed because juju provider 2.9 does not exist yet; once a 2.9.x ships (inside the '< 3.0' cap) it would resolve successfully and flip the case to spuriously red, so it guarded nothing durable. Drop it and keep '>= 3.0' (beyond_cap), which stays correct after a 3.x releases because the cap keeps blocking it. Tighten the negative branch to assert the init failure is an unsatisfiable-constraint error rather than any non-zero exit, so an unrelated failure can no longer pass the case. Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
Without an error predicate, juju.wait() spins the full 20-minute timeout if postgresql-k8s lands in an error state before raising. Pass error=jubilant.any_error so the test fails immediately with a legible cause, matching the repo's other jubilant integration tests. Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
CI or a developer may need to point the tests at a specific terraform build rather than whatever is first on PATH. Honor a TF_BINARY env var (falling back to "terraform") in both the composition and deploy tests, and pass it through the relevant tox envs. Matches the convention used by canonical/mysql-plans. Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
…eploy test On CI the arm64 leg hung for ~2h inside 'terraform init' (a transient registry/network stall) and only stopped at the spread kill-timeout, because the subprocess had no timeout and captured its output — so the stall was neither bounded nor visible in the log. Pass timeout=10m and let terraform stream to the job log (drop capture_output), using check=True to surface a non-zero exit. A stall now fails the test in minutes with live output instead of wedging the runner. Matches the pattern in canonical/mysql-plans' deploy test. Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
The previous fix used a single 10m timeout for both terraform commands. On the arm64 CI substrate 'terraform apply' — which the juju provider blocks on while it deploys postgresql-k8s and waits for it to settle — exceeded 10m mid-deploy (log showed 'Still creating...' progressing), so the test failed on a legit but slow deploy rather than a real fault. Split the budgets: 'init' keeps a tight 5m cap (it only downloads the provider, and guards the transient registry stall seen earlier), while 'apply' gets the same 20m budget as the deploy wait that follows it. amd64 already passes well under this; the wider apply budget covers the slower arm64 deploy. Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
… default The module defaults constraints to 'arch=amd64'. On the arm64 CI runner the juju provider turned that into a pod nodeSelector 'kubernetes.io/arch=amd64', which no arm64 node satisfies, so postgresql-k8s stayed Pending (FailedScheduling) and 'terraform apply' blocked until the timeout — misdiagnosed earlier as a slow deploy needing a bigger timeout. Pass 'constraints=arch=$(dpkg --print-architecture)' so the module deploys for the host's architecture, mirroring what spread's prepare-each already does at the model level for every other integration test. Verified on an arm64 box: the pod schedules immediately and postgresql-k8s reaches active (apply returns in ~40s once units are created; juju.wait then covers settle). Collapse the two terraform timeouts into one 15m bound now that apply is not the long pole. Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
Three gaps surfaced by an adversarial comparison to canonical/mysql-plans: 1. fmt lacked -diff: a failing fmt -check exited non-zero with no diff, so a contributor couldn't see what to fix. Add -diff (and a locally-runnable [testenv:lint-terraform] tox env mirroring fmt/init/validate), so the same lint runs locally via 'tox run -e lint-terraform' as in CI. The workflow now calls that env instead of inlining three commands. 2. Deploy test only passed juju_model + constraints, leaving storage_directives and config unexercised — a postgres-critical wiring hole. Pass storage directives (data/archive/logs/temp) and a profile config, and assert the application_name module output after apply. Validated: tox run -e lint-terraform passes (fmt -diff, init, validate); the -var HCL map syntax parses (terraform validate with vars); composition tests 5/5. Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
The deploy test's `_run_terraform` helper runs `terraform` without capture_output so the slow init/apply stream their progress live to the log (a stall stays visible). The final `terraform output -raw application_name` call reused that helper, so its `CompletedProcess.stdout` was None and the assertion `output.stdout.strip()` raised AttributeError on both the amd64 and arm64 legs, even though the deploy itself reached active/idle. Give the helper an opt-in `capture` and set it only on the output call, so init/apply keep streaming while the assertion reads a real captured value. Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
The Usage examples invoked `-var='juju_model_name=...'` and
`-var='storage={...}'`, but the module declares neither variable: the model
input is `juju_model` (a model UUID, not a name) and the storage input is
`storage_directives`. Every documented `terraform apply` therefore failed with
an undeclared-variable error.
Point the examples at the real variables, show capturing the model UUID (as
the deploy test does), and drop the LXD-only storage pool specifiers
(`,lxd`, `tmpfs,`) that do not apply to this Kubernetes module.
Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue
The terraform module's juju provider constraint was pinned to
~> 1.0(>= 1.0.0, < 2.0.0). A downstream consumer reported pairing this module with a sibling module requiring juju provider>= 1.1.xand hitting a constraint conflict.Reproducing on a Linux box showed
~> 1.0does not conflict with>= 1.1.1— both resolve to juju provider v1.5.6 (the~> 1.0operator means>= 1.0.0, < 2.0.0, not< 1.1.0; the conflicting~> 1.0.0form only ever existed on unmerged branches and never shipped in a tag). The real conflict is the v2 cap: a higher-level module requiring juju provider 2.x (v2.0.0 released May 2026, v2.1.1 current; supports Juju 3.x and 4.x, drops 2.9) hits the< 2.0.0upper bound and failsterraform init. Per the upstream v2 release notes the v1→v2 upgrade is non-breaking for the resource surface this module uses (a singlejuju_application).Separately, the module shipped with no terraform CI —
terraform/**was inci.yaml'spaths-ignore, so terraform-only changes ran no checks at all and a broken/unresolvable constraint would land unnoticed.Solution
Widen the constraint —
terraform/versions.tf:~> 1.0→>= 1.0, < 3.0. Allows both v1.x and v2.x consumers while keeping a ceiling against an unvetted major 3.x. v1 consumers still resolve to the latest 1.x (e.g. v1.5.6); v2 consumers resolve to the latest 2.x (e.g. v2.1.1); beyond-cap constraints fail as expected. The generatedterraform/README.mdtables are regenerated to match.Add a terraform test lane (mirrors
canonical/iam-bundle-integration's static checks andcanonical/mysql-plans' deploy test):.github/workflows/terraform.yaml— triggered onterraform/**changes (the inverse of the path-ignore). Installs terraform via snap, then runsterraform fmt/init/validateand the constraint-composition suite.tests/terraform/test_compositions.py+tox -e terraform— 5 parametrized cases sourcing the real module alongside a sibling juju provider constraint (v1, identity>= 1.1.1, v2, v2-pinned, beyond-cap) and assertingterraform initresolves to the expected provider major or fails for an unsatisfiable-constraint reason. Regression guard for the widened constraint.tests/integration/terraform/test_terraform_deploy.py+tests/spread/test_terraform_deploy.py/task.yaml— jubilant test applying the module (published16/stablecharm) into the conciergetestingmodel and waiting for active/idle, failing fast on an app error. Runs via the existing spread integration lane (microk8s); noactions-operator.TF_BINARYenv override in both test lanes (and the relevant tox envs) to point CI at a specific terraform build.terraform/.gitignore— ignore.terraform/and the lock file, standard hygiene for a module consumed as a git source.Checklist