Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
f08ee99
refactor: extract run-ID and label primitives to neutral packages
ayuskauskas Aug 21, 2026
29574fa
feat(agent): add run-scoped name helpers
ayuskauskas Aug 21, 2026
c72afe2
fix(agent): empty runID must not yield a trailing-dash name
ayuskauskas Aug 21, 2026
675a8fe
feat(agent): create run-scoped resources and drop create-or-update
ayuskauskas Aug 21, 2026
dc2b9f1
feat(agent): scope cleanup to created objects with UID preconditions
ayuskauskas Aug 21, 2026
9062f45
feat(agent): authorize pod selection via controlling ownerReference
ayuskauskas Aug 21, 2026
0425280
fix(agent): fail closed in ownedByJob on a zero Job UID
ayuskauskas Aug 21, 2026
813df45
feat(snapshotter): scope staging ConfigMap to the run and unify valid…
ayuskauskas Aug 21, 2026
1e641f4
feat(cli): treat job and service-account names as prefixes
ayuskauskas Aug 21, 2026
d7d3c2b
fix(cli): stop Ruling-7 tests from overclaiming single-generation cov…
ayuskauskas Aug 21, 2026
46a1469
docs: update selectors and names for run-scoped agent resources
ayuskauskas Aug 21, 2026
82e1a6d
test(agent): prove concurrent runs are isolated
ayuskauskas Aug 23, 2026
5cc3d9f
test(agent): add cleanup-ownership discriminator to concurrency proof
ayuskauskas Aug 23, 2026
e032598
fix(agent): isolate the staging ConfigMap from the validator's and sw…
ayuskauskas Aug 23, 2026
21b986e
fix(agent): do not fail Deploy on a forbidden adoption-drift Get
ayuskauskas Aug 23, 2026
6dd0e6c
docs(cli): correct why the single-run-ID invariant is untested
ayuskauskas Aug 23, 2026
4498989
test(e2e): add cluster-backed snapshot run-isolation checks
ayuskauskas Aug 24, 2026
f494122
test(e2e): drop RBAC fixture superseded by run-scoped agent RBAC
ayuskauskas Aug 24, 2026
49e8f15
docs: select the agent ServiceAccount by run ID when debugging
ayuskauskas Aug 24, 2026
f7ab9b8
fix(agent): gate the configmaps delete pre-flight on output ownership
ayuskauskas Aug 24, 2026
c6e76cd
fix(agent): reject an invalid RunID before Deploy creates anything
ayuskauskas Aug 24, 2026
7d7e8c8
test(agent): run-scope the names asserted by the deployer tests
ayuskauskas Aug 24, 2026
abd8c04
test(agent): cover a nil Controller ownerReference in ownedByJob
ayuskauskas Aug 24, 2026
d83360e
test(agent): state the cleanup discriminator instead of citing a report
ayuskauskas Aug 24, 2026
b57e4a0
test(e2e): scope agent Job cleanup to this script's own runs
ayuskauskas Aug 24, 2026
7070e4b
docs: add the blank line markdownlint MD031 wants before code fences
ayuskauskas Aug 24, 2026
e9b57c9
fix(agent): record create intent so a lost response cannot orphan an …
ayuskauskas Aug 24, 2026
d221e26
test(agent): cover ownership on the watch-based pod-discovery path
ayuskauskas Aug 24, 2026
19bc99a
fix(snapshotter): stop DeployAndCollect mutating the caller's AgentCo…
ayuskauskas Aug 24, 2026
85d9450
docs: correct agent label coverage, name prefixes, and validate flag …
ayuskauskas Aug 24, 2026
7828018
fix(cleanup): restore the name-based sweep for pre-ADR-020 agent left…
ayuskauskas Aug 24, 2026
9620e98
fix(cleanup): remove the pre-ADR-020 unlabeled cluster RBAC pair
ayuskauskas Aug 24, 2026
e8f859b
test(agent): drop the always-constant namespace param from watchDeployer
ayuskauskas Aug 24, 2026
d68edf5
test(agent): drop the leading blank line in the watch-path test
ayuskauskas Aug 24, 2026
bffa50c
test(agent): derive name-limit boundaries from defaults.MaxK8sNameLength
ayuskauskas Aug 24, 2026
d1fb564
fix(agent): reject an invalid resolved object name before Deploy writes
ayuskauskas Aug 24, 2026
5e3a43e
test(agent): report reactor failures without t.Fatalf on a worker gor…
ayuskauskas Aug 24, 2026
0b4b46e
test(agent): use fake.NewClientset over the deprecated constructor
ayuskauskas Aug 24, 2026
7135aee
docs: make the run-ID lookup self-contained and fix agent default drift
ayuskauskas Aug 24, 2026
1d51d60
docs(cleanup): warn that the agent label sweep terminates a live run
ayuskauskas Aug 24, 2026
d3e0673
test(e2e): poll until the leftover agent Job count settles
ayuskauskas Aug 24, 2026
3ee9bc8
refactor(agent): name the shared error-context keys
ayuskauskas Aug 24, 2026
f92aaba
fix(agent): establish cleanup ownership at create time, not by name
ayuskauskas Aug 25, 2026
4598fbc
feat(agent): run as an existing ServiceAccount and manage none of its…
ayuskauskas Aug 25, 2026
ae75239
feat(agent): add --add-roles-to-service-account to grant the agent's …
ayuskauskas Aug 25, 2026
db43974
docs: document exact-if-exists ServiceAccounts and the isolation waiver
ayuskauskas Aug 25, 2026
811d05a
test(agent): stop shadowing err in the existing-ServiceAccount test
ayuskauskas Aug 25, 2026
dec685d
feat(agent): write --add-roles-to-service-account RBAC as manifests
ayuskauskas Aug 25, 2026
8609496
feat(agent): verify caller and ServiceAccount permissions at the gate
ayuskauskas Aug 25, 2026
85403e8
docs(agent): document cleanup's label+UID re-verification of lost cre…
ayuskauskas Aug 25, 2026
a9e0d05
test(agent): cover cleanup's empty-RunID and missing-UID refuse paths
ayuskauskas Aug 25, 2026
c328b21
test(agent): make the empty-RunID guard row fail without the guard
ayuskauskas Aug 25, 2026
af60ee6
test(agent): satisfy whitespace, unparam and prealloc in the gate tests
ayuskauskas Aug 26, 2026
084e872
test(agent): drop seedServiceAccount's always-constant name param
ayuskauskas Aug 26, 2026
7bec43e
fix(agent): make the provisioned ClusterRole name injective
ayuskauskas Aug 26, 2026
583c798
test(agent): group the validation import with the other k8s imports
ayuskauskas Aug 26, 2026
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
6 changes: 3 additions & 3 deletions .github/actions/gpu-snapshot-validate/debug-snapshot-job.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ kubectl_kind() {
}

echo "=== Snapshot Job ==="
kubectl_kind -n default get job aicr -o yaml || true
kubectl_kind -n default get job -l app.kubernetes.io/name=aicr -o yaml || true
echo "=== Snapshot Pods ==="
kubectl_kind -n default get pods -l app.kubernetes.io/name=aicr -o wide || true
echo "=== Snapshot Job describe ==="
kubectl_kind -n default describe job aicr || true
kubectl_kind -n default describe job -l app.kubernetes.io/name=aicr || true
echo "=== Snapshot Pod describe ==="
kubectl_kind -n default describe pods -l app.kubernetes.io/name=aicr || true
echo "=== Snapshot current logs ==="
kubectl_kind -n default logs -l app.kubernetes.io/name=aicr --all-containers --tail=200 || true
echo "=== Snapshot previous logs ==="
kubectl_kind -n default logs -l app.kubernetes.io/name=aicr --all-containers --previous --tail=200 || true
echo "=== Snapshot ConfigMap ==="
kubectl_kind -n default get configmap aicr-snapshot -o yaml || true
kubectl_kind -n default get configmap -l app.kubernetes.io/name=aicr -o yaml || true
3 changes: 2 additions & 1 deletion docs/integrator/automation.md
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,8 @@ metadata:
spec:
podSelector:
matchLabels:
job-name: aicr
app.kubernetes.io/name: aicr
app.kubernetes.io/component: snapshot-agent
policyTypes:
- Egress
egress:
Expand Down
81 changes: 67 additions & 14 deletions docs/integrator/go-library.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,8 @@ reported as no drift.
// CollectSnapshot deploys a snapshotter Job to the target cluster and
// returns the resulting Snapshot. cfg is a facade-owned struct that
// mirrors pkg/snapshotter.AgentConfig field for field; the mirror is
// enforced by a test, so a field added upstream cannot silently stay at
// its zero value here.
// enforced by TestAgentConfigMirrorsInternal, so a field added upstream
// cannot silently stay at its zero value here.
//
// The returned Snapshot carries the parsed form plus Snapshot.Raw — the
// exact bytes the agent emitted. Persist Raw rather than re-serializing
Expand All @@ -248,18 +248,25 @@ snapCtx, cancelSnap := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancelSnap()
snap, err := client.CollectSnapshot(snapCtx, &aicr.AgentConfig{
Kubeconfig: "/path/to/target-kubeconfig",
// Namespace, Image, JobName, and ServiceAccountName are all required on
// the SDK path. Only Namespace is validated; the rest are copied straight
// into the Job and RBAC objects, so an empty value becomes an empty
// metadata.name or container image that the API server rejects. The CLI
// defaults them from its own flags, which the facade does not share.
Namespace: "aicr-snapshot",
Image: "ghcr.io/nvidia/aicr:v0.19.0",
JobName: "aicr-snapshot",
ServiceAccountName: "aicr-agent",
Timeout: 5 * time.Minute,
Cleanup: true,
AKSGPUPoolsPath: "/path/to/aks-gpu-pools.json", // AKS only
// Namespace is required and validated (it becomes the RBAC/Job namespace
// and the internal staging ConfigMap's namespace). Image is not
// validated — an empty value becomes an empty container image that the
// API server rejects. JobName is an optional name prefix; leaving it
// unset defaults to "aicr" with a generated run ID appended, so every
// run gets its own uniquely named Job without the caller managing that.
//
// ServiceAccountName carries two meanings and is EXACT-IF-EXISTS. When
// a ServiceAccount of exactly that name already exists in Namespace it
// is used verbatim and the run creates NO ServiceAccount, Role,
// RoleBinding, ClusterRole or ClusterRoleBinding — and deletes none at
// cleanup. Otherwise it is a prefix and the run creates and owns the
// full run-scoped RBAC set. Leaving it unset keeps the run-scoped
// default and never probes for an existing ServiceAccount.
Namespace: "aicr-snapshot",
Image: "ghcr.io/nvidia/aicr:v0.19.0",
Timeout: 5 * time.Minute,
Cleanup: true,
AKSGPUPoolsPath: "/path/to/aks-gpu-pools.json", // AKS only
})
if err != nil {
log.Fatalf("collect snapshot: %v", err)
Expand Down Expand Up @@ -338,6 +345,52 @@ Valid phase values are `PhaseDeployment`, `PhaseConformance`, and
`ErrCodeInvalidRequest` before any cluster work, so a typo cannot
silently degrade to an empty run.

#### Running the agent as an existing ServiceAccount

`AgentConfig.ServiceAccountName` is **exact-if-exists**, so it carries two
meanings depending on the cluster:

- A ServiceAccount of exactly that name already exists in `Namespace`: the
agent pod runs as it verbatim, and `CollectSnapshot` creates **no**
ServiceAccount, Role, RoleBinding, ClusterRole, or ClusterRoleBinding, and
deletes none at cleanup.
- Otherwise it is a name prefix and the run creates and owns the full
run-scoped RBAC set, named `<prefix>-<run-id>`.

Leaving the field empty keeps the run-scoped default and never probes for an
existing ServiceAccount, so a stray ServiceAccount cannot capture a run.

Use the first form when the ServiceAccount must carry EKS IRSA or GKE Workload
Identity annotations: both providers pin trust to the ServiceAccount *name*, so
a run-scoped name can never be trusted by either. Grant it the agent's
permissions once — the objects it creates are permanent and no run cleanup
removes them:

```go
// Admin step, run once. Provisions and returns; it deploys no Job.
// Returns ErrCodeNotFound when the ServiceAccount does not exist.
res, err := snapshotter.ProvisionAgentRoles(ctx, &snapshotter.AgentRolesConfig{
Kubeconfig: "/path/to/target-kubeconfig",
Namespace: "gpu-operator",
ServiceAccountName: "irsa-snapshotter",
// DiscoverNetwork also grants the cluster-scoped MUTATING rules live
// network discovery needs — permanently, not for one run's lifetime.
DiscoverNetwork: false,
})
if err != nil {
log.Fatalf("provision agent roles: %v", err)
}
log.Printf("granted via %s/%s and %s/%s",
res.Role, res.RoleBinding, res.ClusterRole, res.ClusterRoleBinding)
```

Adopting one ServiceAccount across runs waives per-run permission isolation:
concurrent runs sharing it hold the same grants, and a `DiscoverNetwork`
provisioning leaves mutating cluster permissions in place until an operator
removes them. See
[Agent Deployment](../user/agent-deployment.md#using-an-existing-serviceaccount-irsa-and-workload-identity)
for the full migration path and teardown commands.

### Loading an existing recipe

When a recipe has already been resolved and persisted (for example a
Expand Down
Loading
Loading