feat(ci): add the GARM E2E driver and its credential path (ISD-5876) - #316
Draft
cbartz wants to merge 1 commit into
Draft
feat(ci): add the GARM E2E driver and its credential path (ISD-5876)#316cbartz wants to merge 1 commit into
cbartz wants to merge 1 commit into
Conversation
cbartz
force-pushed
the
feat/garm-e2e-driver-ISD-5876
branch
from
August 21, 2026 05:48
8a12890 to
3da80f1
Compare
9 tasks
cbartz
force-pushed
the
feat/garm-e2e-driver-ISD-5876
branch
4 times, most recently
from
August 21, 2026 06:24
760778f to
9fa3f87
Compare
cbartz
force-pushed
the
feat/garm-e2e-driver-ISD-5876
branch
from
August 21, 2026 06:25
9fa3f87 to
b46b19a
Compare
cbartz
force-pushed
the
feat/garm-e2e-driver-ISD-5876
branch
from
August 21, 2026 07:40
718503b to
de0b4d4
Compare
Resolves everything the end-to-end test needs to authenticate: the OpenStack username and password from Vault via AppRole, the rest of the tenant settings and a dedicated GitHub App from repository secrets. Runs on the private-endpoint runner, triggered manually, and is not a merge gate. The stub test asserts only that the credentials reach pytest; the suite that uses them follows. Split out of the dispatch-target PR so that file could be reviewed on its own. The content is unchanged from the revision verified green on the private-endpoint runner in run 32379213143.
cbartz
force-pushed
the
feat/garm-e2e-driver-ISD-5876
branch
from
August 21, 2026 13:05
de0b4d4 to
b98d408
Compare
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.
What this PR does
Adds
garm_e2e.yaml, the driver for the GARM end-to-end test, and the credential pathit needs: the OpenStack username and password from Vault via AppRole, the rest of the
tenant settings and a dedicated GitHub App from repository secrets. It runs on the
private-endpoint runner, is triggered manually, and is not a merge gate.
Also adds the
tox -e garm-e2eenvironment, thecharms/tests/e2e/suite directory, anda stub test that asserts only that the credentials reach pytest. The end-to-end test
itself follows in the next PR in the stack.
Why we need it
The credential path is the part most likely to be got wrong and the part that is
worthless to review in the abstract, so it lands on its own and is proven against the
real infrastructure before anything is built on top of it. That already paid for itself:
it surfaced a Vault KV v1/v2 response-shape mismatch and two derived values that the
runner's automatic masking does not cover.
Landing it also registers its
workflow_dispatch, which gives the end-to-endimplementation an on-demand
--refloop on the private-endpoint runner instead of atrigger that fires on every push to a scarce shared resource.
Test plan
Run on the real infrastructure, green:
https://github.com/canonical/github-runner-operators/actions/runs/32379213143
Every step passed on the private-endpoint runner: the tenant settings and GitHub App
loaded from secrets, the OpenStack username and password came from Vault, a Keystone
token was issued against the tenant, and the suite reported
11 passed. The run alsoconfirms the declared token scope is what the job actually gets — the log records
Contents: read/Metadata: readand nothing more. It was triggered by a temporarylabel-gated
pull_requesttrigger, since removed. The content here is unchanged fromthe revision that produced that run.
The log was audited for leaked credentials: no PEM markers, no Vault token, no
rendered config, no base64 run long enough to be the encoded private key, and no Vault
hostname — that last one matters because the workflow normalises the address with
rstrip("/"), and masking matches literal substrings, so the derived form is maskedexplicitly rather than relying on the runner. The Vault step's entire output is one
line:
Tenant credentials fetched and masked.Every occurrence of a credentialvariable elsewhere is
***or a bare variable name, including the pytest parametrizeIDs, which print a setting's name and never its value.
The Vault step was exercised against a stand-in server for every response shape it can
meet: KV v2, KV v1, an empty secret, one missing a field, and a 403 — confirming it
authenticates in the first two and fails without writing a partial environment in the
rest.
Private-key normalisation was verified against a generated RSA key: a pasted PEM, a
pre-encoded value, and a double-normalised value all reduce to one line and decode to a
key
AppAuthaccepts.The stub test passes with all eleven settings present and fails naming only the
variable when one is missing — verified that a sentinel password in the environment
appears nowhere in a failing run's output.
scripts/check_agents_md.pypasses; everyrunblock passesbash -n.Review focus
are
::add-mask::ed in the step that reads them; the Vault response is parsed inmemory and never written to a file, so no failure path can leave it on a runner whose
disk outlives the job; steps use
set -euo pipefailand neverset -x.value the workflow transforms — the base64 of the private key, the normalised Vault
address — is not covered by the runner's automatic masking of the secret it came from.
issues it; the workflow normalises it to one line, because the pytest environment
template is line-oriented and a multi-line value would break the env file.
pytest.failrather thanassertin the stub test is deliberate: pytest'sassertion rewriting introspects the expression and would dump the whole of
os.environinto the failure output.included. The tradeoff is that GitHub masks secret values by literal substring, so a
common value redacts wherever it appears in a log.
Breaking changes
None. The workflow is
workflow_dispatch-only and is not in any required status check,so nothing runs automatically and existing CI is untouched.
New dependencies and workflow changes
charms/tests/e2e/suite directory andtox -e garm-e2eenvironment, keptseparate from
charms/tests/integration/so the end-to-end test stays out of the PRmatrix.
Checklist
docs/changelog.mdwith user-relevant changesterraform fmtpasses andtflintreports no errorsAGENTS.md.copilot-collections.yamlor.github/instructions/: I re-checked whether theAGENTS.md"12-factor divergences" guidance still matches the upstream copilot-collections guidanceNotes on the unchecked items:
than a
moduleslist, and the end-to-end suite is deliberately excluded from thatmatrix.
spread.yamlis untouched here; it changes in the next PR in the stack.