Skip to content

fix: report PIS bake completion - #9260

Open
r2k1 wants to merge 12 commits into
mainfrom
r2k1-pis-dual-completion-markers
Open

fix: report PIS bake completion#9260
r2k1 wants to merge 12 commits into
mainfrom
r2k1-pis-dual-completion-markers

Conversation

@r2k1

@r2k1 r2k1 commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

What

Make aks-node-controller provision-wait report the result of a PIS image bake.

How

  • Pre-provision writes both base_prep.complete and provision.complete after provision.json.
  • Normal provisioning continues to write provision.complete.
  • supportsScriptlessPhase2 no longer excludes a pre-provision run, so AgentBaker honours a caller that requests scriptless generation for a bake.

AKS-RP generalization removes the per-run result and configuration files before image capture while preserving base_prep.complete.

Rollout

Rollout is controlled by the RP's existing PreProvisionOnly fallback in PR 16870787 — no AgentBaker flag is needed.

  1. Ship AKS-RP cleanup in PR 16870787.
  2. Ship this change in a Linux VHD.
  3. Remove the RP fallback for that VHD version, together with mapping PreProvisionOnly into AKSNodeConfig.

Validation

  • AgentBaker package tests
  • ShellSpec marker tests
  • Linux VHD PIS bake and derived-node E2E

The unchanged CSE command `aks-node-controller provision-wait` waits for
/opt/azure/containers/provision.complete. A pre-provision (PIS bake) run
wrote only base_prep.complete, so provision-wait never saw a result.

cse_start.sh now always creates provision.complete and additionally creates
base_prep.complete when PRE_PROVISION_ONLY=true. base_prep.complete stays the
durable phase state captured in the image. Image generalization removes
provision.complete before capture, so nodes created from the image still run
nodePrep and report their own result.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Windows Unit Test Results

  3 files   13 suites   43s ⏱️
404 tests 404 ✅ 0 💤 0 ❌
407 runs  407 ✅ 0 💤 0 ❌

Results for commit e6090c3.

♻️ This comment has been updated with latest results.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Unifies Linux CSE completion reporting for normal provisioning and PIS image-bake runs.

Changes:

  • Always writes provision.complete; PIS runs also write base_prep.complete.
  • Cleans transient provisioning state before E2E image capture.
  • Adds ShellSpec coverage and documents marker semantics.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
parts/linux/cloud-init/artifacts/cse_start.sh Updates completion-marker handling.
spec/parts/linux/cloud-init/artifacts/cse_start_spec.sh Tests marker behavior across modes.
e2e/test_helpers.go Validates and cleans PIS bake state.
aks-node-controller/README.md Documents dual-stage reporting and cleanup.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Suppressed comments (4)

pkg/agent/datamodel/types.go:1825

  • 🔴 High Risk — 🔒 Security: This public switch's documentation says no image-pipeline cleanup is required, but the generated aks-node-controller-nbc-cmd.sh embeds the bake cluster's TLS bootstrap token. Rewriting it on the next boot prevents runtime use of the old token but does not remove that credential from the captured image. Document the RP scrub as a prerequisite before enabling this flag, consistent with the rollout requirement and the E2E generalization step.
	// watches it, so it must stay false until the target VHD contains both. It is not a switch for
	// image-pipeline behaviour: the bake's completion marker is volatile and its CSE command file
	// is rewritten from each node's own CustomData on every boot, so no cleanup step is required
	// for correctness.

pkg/agent/baker.go:267

  • 🟢 Low Risk — 🏗️ Architecture: This comment names the wrong completion contract. The updated bake deliberately does not create provision.complete; it creates /run/azure/pre-provision.complete, and the updated controller must watch that marker. Correcting this is important because this comment explains the VHD compatibility gate.
// A PreProvisionOnly (PIS image bake) run only produces provision.complete on a VHD whose
// cse_start.sh writes it for the pre-provision phase. Older VHDs write only base_prep.complete, so
// provision-wait would block there until the CSE timeout. PreProvisionOnly therefore stays on the
// legacy CSE command unless the caller opts in through EnableScriptlessPreProvision, which is what
// gates enablement on the first VHD version containing the change.

parts/linux/cloud-init/artifacts/cse_start.sh:129

  • The PR title and the first completion-marker section state that a bake always writes /opt/azure/containers/provision.complete and that no new marker is added, but this implementation explicitly omits that file and introduces /run/azure/pre-provision.complete. Please update the PR description/title (or the implementation) so the documented rollout contract is unambiguous.
    # result for its own. provision.complete is deliberately NOT written here: it is durable, and a
    # captured copy would make cse_main.sh exit before nodePrep.
    mkdir -p /run/azure
    touch /run/azure/pre-provision.complete

e2e/test_helpers.go:117

  • 🟢 Low Risk — 🏗️ Architecture: This helper's compatibility explanation still says the bake signals through provision.complete, although the E2E now relies on /run/azure/pre-provision.complete plus controller support for that marker. Update it so failures against older VHDs can be diagnosed from the actual prerequisite.
// A bake only reports its result through provision.complete on a VHD whose cse_start.sh writes that
// marker for a pre-provision run. Released VHDs predating that change write only base_prep.complete,
// so provision-wait would block there until the CSE timeout. Restrict this to images produced by the
// VHD build under test, which is what e2e_run.sh selects via SIG_VERSION_TAG_NAME=buildId.

Comment thread aks-node-controller/app.go Outdated
Comment on lines 797 to 799
if writeErr := a.writeProvisionResult(jsonPath, provisionResult); writeErr != nil {
slog.Error("failed to write provision.json file", "path", jsonPath, "error", writeErr)
}
signalProvisionFailure wrote the completion marker even when a forced
overwrite of provision.json had failed. The stale result was then still on
disk, so waking provision-wait made it report that content - a stale success
reported as this run's outcome. It now returns without writing a marker, so
provision-wait and CSE time out instead, which fails closed. The path where the
current failure publishes successfully is unchanged.

Also corrects the write-if-absent comment, which claimed nothing stale can
survive the reset: the reset deliberately short-circuits when the durable
marker shows this node already completed provisioning.

Drops two shellspec cases and two e2e assertions that were fully subsumed by
neighbouring checks, and records that gating the flag on a VHD version must
also scrub plaintext CustomData residue.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 21, 2026 04:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Suppressed comments (5)

e2e/config/config.go:176

  • 🟡 Medium Risk — 🧪 Test Gate: buildId only means that some explicitly selected build is used; e2e-local.sh allows VHD_BUILD_ID to point to any historical build. Such an older VHD is treated as supporting the new volatile marker, so the bake opts into provision-wait and times out. Gate this on an explicit capability/minimum version or on a verified current-build identity rather than the tag key alone.
func (c *Configuration) UsesFreshlyBuiltVHD() bool {
	return c.SIGVersionTagName == "buildId"

aks-node-controller/app.go:227

  • 🟡 Medium Risk — 🔧 Script Logic: This reset now runs for provision --dry-run, so a command documented as only printing the command can delete the live provision.json. Keep both the reset and failure-marker writes disabled in dry-run mode; otherwise even an invalid dry run can mutate provisioning state.
	if resetErr := a.resetStaleProvisionResult(); resetErr != nil {

pkg/agent/baker.go:267

  • This compatibility comment describes the obsolete durable-marker design: a successful bake deliberately never writes provision.complete; it reports through the volatile marker. Document both the new marker and the required controller support so callers understand the actual version gate.
// A PreProvisionOnly (PIS image bake) run only produces provision.complete on a VHD whose
// cse_start.sh writes it for the pre-provision phase. Older VHDs write only base_prep.complete, so
// provision-wait would block there until the CSE timeout. PreProvisionOnly therefore stays on the
// legacy CSE command unless the caller opts in through EnableScriptlessPreProvision, which is what
// gates enablement on the first VHD version containing the change.

e2e/test_helpers.go:117

  • This comment says the bake depends on provision.complete, but the changed script intentionally uses /run/azure/pre-provision.complete and never writes the durable marker on success. Correcting this matters because this helper's gate is specifically meant to avoid hanging on VHDs without the volatile-marker contract.
// A bake only reports its result through provision.complete on a VHD whose cse_start.sh writes that
// marker for a pre-provision run. Released VHDs predating that change write only base_prep.complete,
// so provision-wait would block there until the CSE timeout. Restrict this to images produced by the
// VHD build under test, which is what e2e_run.sh selects via SIG_VERSION_TAG_NAME=buildId.

parts/linux/cloud-init/artifacts/cse_start.sh:127

  • The PR title and “Dual completion markers” section say the bake always writes durable provision.complete, but this implementation deliberately does not and instead introduces /run/azure/pre-provision.complete. Update the PR contract and rollout text to match the volatile-marker design; otherwise downstream enablement may check for the wrong VHD capability.
    # The bake reports its result through a volatile marker on tmpfs, which
    # `aks-node-controller provision-wait` also watches. /run is cleared on boot, so this marker
    # cannot survive image capture and a node created from the image can never mistake the bake's
    # result for its own. provision.complete is deliberately NOT written here: it is durable, and a
    # captured copy would make cse_main.sh exit before nodePrep.

Comment thread pkg/agent/datamodel/types.go Outdated
Comment on lines +1830 to +1833
// TODO: before the flag is turned on, the change that gates it on a VHD version must also scrub
// the plaintext CustomData residue an image capture would otherwise retain, using supported
// cloud-init cleanup. Deleting aks-node-controller-nbc-cmd.sh addresses one known credential
// file and does not make a captured image free of provisioning secrets.
A minimum VHD version carrying the volatile marker and its watcher is
necessary but not sufficient. The parser renders PRE_PROVISION_ONLY from
aksnodeconfig Configuration.PreProvisionOnly, so unless the RP's
GetAKSNodeConfig propagates NodeBootstrappingConfiguration.PreProvisionOnly
into it, a bake driven by a provision config renders PRE_PROVISION_ONLY=false,
runs full provisioning instead of basePrep only, and writes the durable
provision.complete into the captured image.

Documentation only. No behaviour change, no version named, and the plaintext
CustomData scrub TODO for the final flag-flip is retained.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 21, 2026 04:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.

Suppressed comments (3)

pkg/agent/baker.go:264

  • This comment still says a bake produces provision.complete, but the new pre-provision branch deliberately never writes that durable marker; it writes /run/azure/pre-provision.complete, and the controller must watch it. Keeping the capability contract accurate is important for callers implementing the minimum-VHD rollout gate.
// the CSE command is `aks-node-controller provision-wait` and it reports the result recorded in
// provision.json once provision.complete appears.
//
// A PreProvisionOnly (PIS image bake) run only produces provision.complete on a VHD whose
// cse_start.sh writes it for the pre-provision phase. Older VHDs write only base_prep.complete, so

aks-node-controller/app.go:939

  • 🟡 Medium Risk — 🔧 Script Logic: The volatile marker lasts for the entire boot, but this fast path treats any existing copy as belonging to the current attempt. If provisioning is restarted on the same bake VM, the previous marker remains, so provision-wait can immediately read the previous result (or race with resetStaleProvisionResult removing it) before the new cse_start.sh run finishes. Clear or rotate this marker at the start of each attempt, with ordering that guarantees the waiter cannot observe the old copy, and cover the retry path in tests.
	if marker, found := firstExistingMarker(markers); found {

e2e/test_helpers.go:115

  • This capability description names provision.complete, but the bake path now explicitly avoids that durable marker and relies on /run/azure/pre-provision.complete plus matching controller support. As written, it can lead maintainers to gate only on the script change and overlook the controller-side prerequisite.
// A bake only reports its result through provision.complete on a VHD whose cse_start.sh writes that
// marker for a pre-provision run. Released VHDs predating that change write only base_prep.complete,

@r2k1

r2k1 commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

CI evidence for the volatile-marker design (branch head bedb573d21).

AKS Linux VHD Build - PR check-in gate: pass (build 177594993) — builds VHDs from this PR and runs E2E against them, the only place a bake can exercise the new markers.

gotestsum hides t.Logf for passing tests, so I pulled the raw e2e-test-log/test-log.json artifact to confirm the assertions ran rather than being skipped:

scriptless CSE (provision-wait):  true   Test_Ubuntu2404_VHDCaching/VHDCreation
                                  true   Test_Ubuntu2604Minimal_VHDCaching/VHDCreation

pass  Test_Ubuntu2404_VHDCaching/VHDCreation
pass  Test_Ubuntu2404_VHDCaching/VHDCreation/VMProvision
pass  Test_Ubuntu2604Minimal_VHDCaching/VHDCreation
pass  Test_Ubuntu2604Minimal_VHDCaching/VHDCreation/VMProvision
failing top-level tests: none

Stage ordering shows the assertions gate the capture — === Creating VHD Image === only runs once every stage-1 check passes, and generalization runs immediately after:

[151.1s] preparing AKS node done -> validating VM...
         === Creating VHD Image ===
[155.0s] Removing per-run provisioning state before image capture...
[256.4s] validating VM done (105.3s)
VMProvision: [151.8s] preparing AKS node done -> validating VM done (98.3s)

So on a VHD built from this PR: the bake wrote base_prep.complete plus the volatile /run/azure/pre-provision.complete, did not write provision.complete (generalization now asserts its absence rather than deleting it, and never fired), and provision-wait — driven purely by the volatile marker — exited 0 printing provision.json. The node created from the captured image then saw no volatile marker, kept the baked base_prep.complete, skipped basePrep, wrote a fresh provision.complete / provision.json / nbc-cmd this boot, and reached Ready.

Backward compatibility on released VHDs (Agentbaker E2E, where UsesFreshlyBuiltVHD() is false): both Linux VHDCaching scenarios passed and the new test ! -e /opt/azure/containers/provision.complete assertion never fired, confirming old VHDs never wrote the durable marker for a bake.

Flakes seen and resolved on retry (none marker-related — grep -c for provision.complete|pre-provision.complete|base_prep|provision.json in the failure output was 0):

  • Agentbaker E2E: 8 × error executing command on pod: Internal error occurred: error sending request (apiserver exec transport) plus perf thresholds; retry left only ubuntuSnapshotUpdate took 2.214s, exceeds threshold 2s, the identical signature on refs/heads/main build 177574603. Now passing.
  • Gate E2E first attempt: Task_configureKubeletAndKubectl took 31.093s, exceeds threshold 27s — a CSE perf threshold, not a VHDCaching test. Now passing.
  • Agentbaker Windows E2E: first attempt was a 17-minute VMSS context deadline exceeded. Now passing.
  • lint jobs once failed in ~12s with golangci-lint config verifyfailing loading https://golangci-lint.run/jsonschema/golangci.v2.8.jsonschema.json: context deadline exceeded; a network flake fetching the schema, which also hit lint (aks-live-patching), a module this PR does not touch. Now passing.

Still red: Agentbaker GPU E2E (GalleryImageNotFound), which fails on every recent run including refs/heads/main — pre-existing and unrelated.

All GitHub checks pass.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 24, 2026 22:56
@r2k1 r2k1 changed the title fix: always write provision.complete so PIS bake reports CSE result fix: report PIS bake completion through a volatile marker Aug 24, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.

@r2k1 r2k1 changed the title fix: report PIS bake completion through a volatile marker fix: report PIS bake completion Aug 25, 2026
provision-wait waits for /opt/azure/containers/provision.complete, which a
pre-provision (PIS image bake) run never wrote, so the bake could not report a
result through the unchanged CSE command. cse_start.sh now always writes it,
and additionally writes base_prep.complete when PRE_PROVISION_ONLY is set.

RP image generalization removes provision.complete before capture, so a node
created from the image still provisions and reports its own result. The
aks-node-controller is unchanged.

EnableScriptlessPreProvision (default false) makes a bake's use of scriptless
generation explicit and gates it on the coordinated rollout.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 25, 2026 00:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Comment thread pkg/agent/datamodel/types.go Outdated
Comment on lines +1817 to +1821
// EnableScriptlessPreProvision lets a PreProvisionOnly (image bake) run use scriptless phase 2,
// so the bake reports its result through the same `aks-node-controller provision-wait` CSE
// command as a normal node. Default false. Enabling it requires a VHD whose cse_start.sh writes
// /run/azure/pre-provision.complete and whose aks-node-controller watches it, and requires the
// caller to propagate PreProvisionOnly into the AKSNodeConfig it builds.
The RP's PreProvisionOnly fallback already gates the rollout, so a second
capability flag duplicated that control. supportsScriptlessPhase2 now depends
only on EnableScriptlessNBCCSECmd, so AgentBaker honours a caller that requests
scriptless during a bake.

Two existing tests asserted the previous fallback and are updated: a bake now
renders scriptless custom data and gets the provision-wait CSE command.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 25, 2026 01:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Comment thread e2e/test_helpers.go Outdated
Comment on lines +159 to +160
cleanup := "sudo rm -f /opt/azure/containers/provision.complete /var/log/azure/aks/provision.json" +
" /opt/azure/containers/aks-node-controller-config.json /opt/azure/containers/aks-node-controller-nbc-cmd.sh"
@r2k1

r2k1 commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

CI on 1cd647093e (+81/−23).

Everything passes except Agentbaker Windows E2E, which is broken on main.

  • AKS Linux VHD Build - PR check-in gatepass (1h18m, build 178044449)
  • gate e2e Run AgentBaker E2Epass
  • Agentbaker E2Epass
  • Agentbaker GPU E2Epass
  • all GitHub checks (go-test, shellspec, shellcheck, lint ×3, cue, CodeQL, Pester, version-consistency, …) — pass

The gate builds VHDs from this PR and runs E2E against them, so it is the only place a bake exercises the new marker. From its e2e-test-log artifact:

pass  Test_Ubuntu2404_VHDCaching/VHDCreation
pass  Test_Ubuntu2404_VHDCaching/VHDCreation/VMProvision
pass  Test_Ubuntu2604Minimal_VHDCaching/VHDCreation
pass  Test_Ubuntu2604Minimal_VHDCaching/VHDCreation/VMProvision

Stage ordering shows the assertions gate the capture — === Creating VHD Image === runs only after stage-1 validation, then the RP-matching cleanup, then stage 2 provisions from the captured image:

[120.8s] preparing AKS node done -> validating VM...
         === Creating VHD Image ===
[228.3s] validating VM done (107.5s)
VMProvision: [123.1s] preparing AKS node done -> validating VM...

So on a VHD built from this PR the bake wrote both base_prep.complete and provision.complete, provision-wait reported its result, and after generalization removed the per-run files the derived node provisioned normally and skipped basePrep.

Windows: Test_Windows2022_VHDCaching_LegacyTLSBootstrap/VHDCreation/VMProvision with WINDOWS_CSE_ERROR_START_NODE_RESET_SCRIPT_TASK. The identical test and signature fails on refs/heads/main build 177913450, and Windows E2E has failed on every recent scheduled main run (177913450, 177823134, 177757831, 177623061 — four consecutive days). This PR only changes parts/linux/...; the E2E helper changes are gated behind !IsWindows(), so the Windows path is untouched.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 25, 2026 03:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Suppressed comments (1)

e2e/test_helpers.go:161

  • 🟡 Medium Risk — 🏗️ Architecture: This generalization cleanup leaves /opt/azure/containers/enabled_features.sh in the captured image. The scriptless boothook only writes that file when the current request has feature toggles, so a derived node with no toggles will not overwrite a bake-time file; aks-node-controller-launcher.sh will then export the stale bake-time flags. Remove this per-run configuration file before capture alongside the other controller inputs.
			cleanup := "sudo rm -f /opt/azure/containers/provision.complete /var/log/azure/aks/provision.json" +
				" /opt/azure/containers/aks-node-controller-config.json /opt/azure/containers/aks-node-controller-nbc-cmd.sh" +
				" /opt/azure/containers/boothook.sh /opt/bin/boothook.sh"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants