fix: publish provision result atomically - #9401
Conversation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Windows Unit Test Results 3 files 14 suites 57s ⏱️ Results for commit 4c730ed. ♻️ This comment has been updated with latest results. |
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Pull request overview
This PR makes provisioning result publication atomic so readers never observe a partially-written provision.json, while keeping provision.complete as a compatibility signal until the deprecation window ends.
Changes:
- Add atomic “stage then rename” publishing for
provision.jsonincse_start.sh, and ensure completion markers are created after publication. - Add Go-side atomic fallback publishing (
writeProvisionResultAtomically) and tests covering preservation/idempotency/error paths. - Update documentation to describe the new provisioning result semantics and compatibility plan.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| spec/parts/linux/cloud-init/artifacts/cse_start_spec.sh | Adds shellspec coverage for atomic provision result publishing and failure modes. |
| parts/linux/cloud-init/artifacts/cse_start.sh | Introduces publishProvisionResponse to stage+rename provision.json and write completion markers. |
| aks-node-controller/const.go | Documents future removal of provision.complete compatibility. |
| aks-node-controller/app_test.go | Adds tests for atomic publishing, preservation, and fsnotify event semantics. |
| aks-node-controller/app.go | Implements Go fallback atomic write + completion marker logic. |
| aks-node-controller/README.md | Updates provisioning flow docs to emphasize atomic provision.json publication and compatibility behavior. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
🟡 Changes recommended
Unquoted JSON expansion can corrupt results, and baked PIS results can mask real-node bootstrap failures.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
parts/linux/cloud-init/artifacts/cse_start.sh:17
- 🟡 Medium Risk — 🔧 Script Logic: Expanding
responseunquoted allows field splitting and pathname expansion in both emitted and staged JSON. For example, anOutputvalue containingfiles * hereis rewritten using filenames from the working directory, corrupting diagnostics and potentially producing invalid JSON. Useprintfwith a quoted argument for both writes.
if ! echo ${response} > "${response_tmp}" ||
- Files reviewed: 6/6 changed files
- Comments generated: 1
- Review effort level: Balanced
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The fallback can preserve successful or stale JSON and then signal completion after the actual provisioning command failed.
Review details
Suppressed comments (1)
aks-node-controller/app.go:727
- 🔴 High Risk — 🔧 Script Logic: Preserving every existing result can turn a publisher failure into a reported success.
cse_start.shrenames a JSON containing the underlyingEXIT_CODEbefore creating the markers; if marker creation then fails (the new ShellSpec explicitly covers this), the script exits non-zero and this handler runs. This branch preserves the already-writtenExitCode: "0", createsprovision.complete, andprovision-waitconsequently returns success even thoughrunProvisionCommandfailed (the same issue occurs with a stale successful destination after a failed rename). Preserve an existing result only when it represents the current failed CSE attempt; otherwise publish the non-zeroprovisionResultbefore signaling completion.
if _, statErr := os.Stat(filepaths.ProvisionJSONFile); errors.Is(statErr, os.ErrNotExist) {
- Files reviewed: 7/7 changed files
- Comments generated: 0 new
- Review effort level: Balanced
There was a problem hiding this comment.
🟡 Changes recommended
The updated documentation incorrectly presents the systemd service as the standard Linux launch path.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 7/7 changed files
- Comments generated: 1
- Review effort level: Balanced
| ### Provisioning Flow | ||
|
|
||
| Here is an indepth explanation of the provisioning flow. Upon first startup, CustomData is made available to the VM, after which cloud-init is able to process the content, in this case, writing the bootstrap config to disk. The binary is triggered by a systemd unit, [`aks-node-controller.service`](https://github.com/Azure/AgentBaker/blob/dev/parts/linux/cloud-init/artifacts/aks-node-controller.service) which is automatically run once cloud-init is complete. In this way, we are ensuring the bootstrapping config is present on the node and can proceeed to run the go binary to start the bootstrapping process. | ||
| On first startup, cloud-init processes CustomData and writes the bootstrap configuration to disk. The cloud-boothook starts [`aks-node-controller.service`](../parts/linux/cloud-init/artifacts/aks-node-controller.service) after the configuration is available, and the controller starts the bootstrap process. |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The provisioning documentation incorrectly presents the compatibility service fallback as the standard launcher path.
Review details
Suppressed comments (1)
aks-node-controller/README.md:85
- 🟡 Medium Risk — 🔄 Backward Compatibility: This still describes the service as the normal entrypoint, but standard Linux boothooks invoke
aks-node-controller-launcher.shdirectly and only fall back tosystemctl start aks-node-controller.serviceon older VHDs without the launcher (pkg/agent/baker.go:84-90). Documenting both paths is important because supported VHDs can use either entrypoint.
On first startup, cloud-init processes CustomData and writes the bootstrap configuration to disk. The cloud-boothook starts [`aks-node-controller.service`](../parts/linux/cloud-init/artifacts/aks-node-controller.service) after the configuration is available, and the controller starts the bootstrap process.
- Files reviewed: 6/6 changed files
- Comments generated: 0 new
- Review effort level: Balanced
AgentBaker Linux gate detectiveRun: https://msazure.visualstudio.com/CloudNativeCompute/_build/results?buildId=180296613 TL;DR: Ubuntu 20.04 FIPS Build VHD failed during Ubuntu Pro attach after repeated /v1/context/machines/token 503/502 responses, then the final attach path exited because the machine was already attached. Likely cause / signature: $signature - Ubuntu Pro/UA dependency or attach-state flake during FIPS Packer provisioning. Confidence: Medium Recommended owner/action: Node Lifecycle/VHD owner should inspect Ubuntu Pro token service availability and make the FIPS attach flow idempotent around transient 502/503 plus already-attached state. Strongest alternative: PR-caused pre-provision marker regression is possible because this PR changes pre-provision publication/marker behavior, but the direct failing signal is external token-service 503/502 followed by UA attach state, not a local marker parse/assertion failure. Evidence: timeline failed Build VHD for build2004fipsgen2containerd; Build VHD log shows repeated token endpoint 503/502, This machine is already attached, Packer exit status 182, and cleanup/artifact publish path; build metadata associates the run with PR 9401. Wiki signature: vhd-ubuntu-pro-machine-token-service-unavailable |
What this PR does / why we need it:
Both Linux writers stage
provision.jsonbeside the target and rename it into place to prevent partial reads. Bash keeps the detailed CSE result; ANC writes a fallback when no result exists. Quotedprintfpreserves the shell payload on stdout and disk. Bash logs publication errors without changing the CSE exit code or skipping log upload.Bash pre-provisioning keeps the existing marker behavior: it writes only
base_prep.complete, notprovision.complete. Dual-marker support is deferred until AKS-RP generalization cleanup is globally deployed.We keep
provision.completefor normal provisioning compatibility and plan to remove it after 2026-10-01, once compatible VHDs have shipped. This change does not addfsyncor guarantee power-loss durability.Which issue(s) this PR fixes:
N/A