feat: skip cloud-init ready report and add standalone report_ready script#8056
feat: skip cloud-init ready report and add standalone report_ready script#8056awesomenix wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a VHD-baked mechanism to suppress cloud-init’s built-in “ready” report and replaces it with an explicit, CSE-invoked readiness/failure report to Azure wireserver.
Changes:
- Add a standalone
report_ready.pyscript that writes Hyper-V KVP provisioning status and POSTs health to wireserver. - Bake
report_ready.pyinto multiple VHD build variants and copy it to/opt/azure/containers/. - Add
skipCloudInitReadyReportto write cloud-init config, and invoke the reporting script fromcse_start.sh.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| vhdbuilder/packer/vhd-image-builder-mariner.json | Adds report_ready.py to files uploaded into the build VM. |
| vhdbuilder/packer/vhd-image-builder-mariner-cvm.json | Same: stage report_ready.py into build VM. |
| vhdbuilder/packer/vhd-image-builder-mariner-arm64.json | Same: stage report_ready.py into build VM. |
| vhdbuilder/packer/vhd-image-builder-flatcar.json | Same: stage report_ready.py into build VM. |
| vhdbuilder/packer/vhd-image-builder-flatcar-arm64.json | Same: stage report_ready.py into build VM. |
| vhdbuilder/packer/vhd-image-builder-cvm.json | Same: stage report_ready.py into build VM. |
| vhdbuilder/packer/vhd-image-builder-base.json | Same: stage report_ready.py into build VM. |
| vhdbuilder/packer/vhd-image-builder-arm64-gen2.json | Same: stage report_ready.py into build VM. |
| vhdbuilder/packer/vhd-image-builder-acl.json | Same: stage report_ready.py into build VM. |
| vhdbuilder/packer/packer_source.sh | Copies report_ready.py into /opt/azure/containers/ with execute perms. |
| vhdbuilder/packer/install-dependencies.sh | Invokes skipCloudInitReadyReport during VHD build. |
| vhdbuilder/packer/imagecustomizer/azlosguard/azlosguard.yml | Ensures OSGuard imagecustomizer also places report_ready.py into /opt/azure/containers/. |
| parts/linux/cloud-init/artifacts/report_ready.py | New standalone readiness/failure reporting implementation. |
| parts/linux/cloud-init/artifacts/cse_start.sh | Calls report_ready.py on provisioning success/failure if present on the VHD. |
| parts/linux/cloud-init/artifacts/cse_install.sh | Adds skipCloudInitReadyReport() helper that writes cloud-init config to skip built-in ready report. |
| addMarinerNvidiaRepo | ||
| updateDnfWithNvidiaPkg | ||
| overrideNetworkConfig || exit 1 | ||
| skipCloudInitReadyReport || exit 1 |
There was a problem hiding this comment.
skipCloudInitReadyReport is already invoked unconditionally earlier in this script, so calling it again in the Mariner/AzureLinux block is redundant and makes the flow harder to reason about. Consider removing this second call to keep the configuration applied in a single place.
| skipCloudInitReadyReport || exit 1 |
| if [ -x /opt/azure/containers/report_ready.py ]; then | ||
| if [ "$EXIT_CODE" -eq 0 ]; then | ||
| python3 /opt/azure/containers/report_ready.py -v || echo "WARNING: Failed to report ready to Azure fabric" | ||
| else | ||
| python3 /opt/azure/containers/report_ready.py -v --failure --description "ExitCode: ${EXIT_CODE}, ${message_string}" || echo "WARNING: Failed to report failure to Azure fabric" | ||
| fi |
There was a problem hiding this comment.
This report_ready.py invocation runs synchronously before log upload/exit, and can block provisioning for up to ~100s on wireserver timeouts/retries (GET/POST timeouts are 30s with multiple retries). If this is intended to be best-effort (as suggested by || echo "WARNING"), consider running it in the background on success and/or moving it after upload_logs (especially on failure) or passing tighter retry/timeout settings to avoid delaying provisioning and log upload.
| if [ -x /opt/azure/containers/report_ready.py ]; then | ||
| if [ "$EXIT_CODE" -eq 0 ]; then | ||
| python3 /opt/azure/containers/report_ready.py -v || echo "WARNING: Failed to report ready to Azure fabric" | ||
| else | ||
| python3 /opt/azure/containers/report_ready.py -v --failure --description "ExitCode: ${EXIT_CODE}, ${message_string}" || echo "WARNING: Failed to report failure to Azure fabric" | ||
| fi |
There was a problem hiding this comment.
This change updates cloud-init/CSE scripts under parts/, which are covered by snapshot-style golden tests in pkg/agent/testdata/* (e.g., baker_test.go reads ./testdata/<folder>/CustomData). Please run make generate (or regenerate the testdata via the repo’s standard workflow) and include the updated golden files in this PR; otherwise CI is likely to fail due to mismatched expected CustomData/CSE outputs.
89a690e to
ed3d302
Compare
| cpAndMode $CLOUD_INIT_BASE_PKG_SRC $CLOUD_INIT_BASE_PKG_DEST 0644 | ||
|
|
||
| CLOUD_INIT_AZURE_PKG_SRC=/home/packer/cloud-init-packages/cloud-init-azure_all.deb | ||
| CLOUD_INIT_AZURE_PKG_DEST=/opt/azure/cloud-init-packages/cloud-init-azure_all.deb | ||
| cpAndMode $CLOUD_INIT_AZURE_PKG_SRC $CLOUD_INIT_AZURE_PKG_DEST 0644 | ||
|
|
||
| CLOUD_INIT_PKG_SRC=/home/packer/cloud-init-packages/cloud-init_all.deb | ||
| CLOUD_INIT_PKG_DEST=/opt/azure/cloud-init-packages/cloud-init_all.deb | ||
| cpAndMode $CLOUD_INIT_PKG_SRC $CLOUD_INIT_PKG_DEST 0644 | ||
|
|
There was a problem hiding this comment.
copyPackerFiles() now unconditionally copies Ubuntu-only cloud-init .deb artifacts from /home/packer/cloud-init-packages into /opt/azure/cloud-init-packages. Those source files are only provisioned in vhd-image-builder-base.json, so Mariner/Flatcar/ACL packer templates will not have them and the VHD build will fail when copyPackerFiles runs. Guard these cpAndMode calls behind an Ubuntu check (and/or a file existence check) so non-Ubuntu builds don’t require these artifacts.
| cpAndMode $CLOUD_INIT_BASE_PKG_SRC $CLOUD_INIT_BASE_PKG_DEST 0644 | |
| CLOUD_INIT_AZURE_PKG_SRC=/home/packer/cloud-init-packages/cloud-init-azure_all.deb | |
| CLOUD_INIT_AZURE_PKG_DEST=/opt/azure/cloud-init-packages/cloud-init-azure_all.deb | |
| cpAndMode $CLOUD_INIT_AZURE_PKG_SRC $CLOUD_INIT_AZURE_PKG_DEST 0644 | |
| CLOUD_INIT_PKG_SRC=/home/packer/cloud-init-packages/cloud-init_all.deb | |
| CLOUD_INIT_PKG_DEST=/opt/azure/cloud-init-packages/cloud-init_all.deb | |
| cpAndMode $CLOUD_INIT_PKG_SRC $CLOUD_INIT_PKG_DEST 0644 | |
| CLOUD_INIT_AZURE_PKG_SRC=/home/packer/cloud-init-packages/cloud-init-azure_all.deb | |
| CLOUD_INIT_AZURE_PKG_DEST=/opt/azure/cloud-init-packages/cloud-init-azure_all.deb | |
| CLOUD_INIT_PKG_SRC=/home/packer/cloud-init-packages/cloud-init_all.deb | |
| CLOUD_INIT_PKG_DEST=/opt/azure/cloud-init-packages/cloud-init_all.deb | |
| # Ubuntu-only cloud-init .deb artifacts may not be present for non-Ubuntu images. | |
| # Guard these copies so Mariner/Flatcar/ACL builds do not fail when the source | |
| # directory or files are missing. | |
| if [ -d "/home/packer/cloud-init-packages" ]; then | |
| cpAndMode $CLOUD_INIT_BASE_PKG_SRC $CLOUD_INIT_BASE_PKG_DEST 0644 | |
| cpAndMode $CLOUD_INIT_AZURE_PKG_SRC $CLOUD_INIT_AZURE_PKG_DEST 0644 | |
| cpAndMode $CLOUD_INIT_PKG_SRC $CLOUD_INIT_PKG_DEST 0644 | |
| fi |
| "source": "vhdbuilder/packer/cloud-init-packages/cloud-init-base_all.deb", | ||
| "destination": "/home/packer/cloud-init-packages/cloud-init-base_all.deb" | ||
| }, | ||
| { | ||
| "type": "file", | ||
| "source": "vhdbuilder/packer/cloud-init-packages/cloud-init-azure_all.deb", | ||
| "destination": "/home/packer/cloud-init-packages/cloud-init-azure_all.deb" | ||
| }, | ||
| { | ||
| "type": "file", | ||
| "source": "vhdbuilder/packer/cloud-init-packages/cloud-init_all.deb", | ||
| "destination": "/home/packer/cloud-init-packages/cloud-init_all.deb" | ||
| }, | ||
| { | ||
| "type": "file", |
There was a problem hiding this comment.
This adds prebuilt cloud-init .deb artifacts into the repo and bakes them into the VHD. This creates a hard-to-audit dependency surface (no explicit version metadata/checksums, no provenance, and manual updates), and it increases repo/VHD build maintenance burden. Prefer downloading versioned packages during the VHD build from an approved source (and tracking the version via components.json/renovate where applicable), or at least include explicit versioning + integrity verification for these artifacts.
| "source": "vhdbuilder/packer/cloud-init-packages/cloud-init-base_all.deb", | |
| "destination": "/home/packer/cloud-init-packages/cloud-init-base_all.deb" | |
| }, | |
| { | |
| "type": "file", | |
| "source": "vhdbuilder/packer/cloud-init-packages/cloud-init-azure_all.deb", | |
| "destination": "/home/packer/cloud-init-packages/cloud-init-azure_all.deb" | |
| }, | |
| { | |
| "type": "file", | |
| "source": "vhdbuilder/packer/cloud-init-packages/cloud-init_all.deb", | |
| "destination": "/home/packer/cloud-init-packages/cloud-init_all.deb" | |
| }, | |
| { | |
| "type": "file", |
14bad23 to
e1d2a33
Compare
e1d2a33 to
bb6f742
Compare
|
|
||
| if [ -x /opt/azure/containers/report_ready.py ]; then | ||
| if [ "$EXIT_CODE" -eq 0 ]; then | ||
| python3 /opt/azure/containers/report_ready.py -v || echo "WARNING: Failed to report ready to Azure fabric" |
There was a problem hiding this comment.
Running report_ready.py synchronously here can add noticeable tail latency to successful provisioning (each attempt can block up to the HTTP timeout(s) plus retry delay). Consider running the success-path report in the background (similar to log upload) and/or tightening the per-request timeouts so a transient wireserver issue doesn't extend CSE completion by ~minutes.
| python3 /opt/azure/containers/report_ready.py -v || echo "WARNING: Failed to report ready to Azure fabric" | |
| python3 /opt/azure/containers/report_ready.py -v || echo "WARNING: Failed to report ready to Azure fabric" & |
| def write_provisioning_kvp(report: str) -> None: | ||
| """Write a provisioning report to the KVP pool file.""" | ||
| if len(report) >= KVP_AZURE_MAX_VALUE_SIZE: | ||
| report = report[:KVP_AZURE_MAX_VALUE_SIZE - 1] |
There was a problem hiding this comment.
KVP_AZURE_MAX_VALUE_SIZE is a byte limit, but the current truncation uses Python string length/slicing, which can still exceed the byte limit for non-ASCII input (e.g., descriptions containing UTF-8 multibyte chars). Truncate based on the encoded UTF-8 byte length before packing the record to avoid producing oversized KVP values.
| def write_provisioning_kvp(report: str) -> None: | |
| """Write a provisioning report to the KVP pool file.""" | |
| if len(report) >= KVP_AZURE_MAX_VALUE_SIZE: | |
| report = report[:KVP_AZURE_MAX_VALUE_SIZE - 1] | |
| def _truncate_utf8_to_max_bytes(text: str, max_bytes: int) -> str: | |
| """Truncate a string so its UTF-8 encoding is at most max_bytes - 1 bytes.""" | |
| if max_bytes <= 0: | |
| return "" | |
| limit = max_bytes - 1 | |
| encoded = text.encode("utf-8") | |
| if len(encoded) <= limit: | |
| return text | |
| truncated = encoded[:limit] | |
| # Ignore incomplete multibyte sequences at the end to keep valid UTF-8. | |
| return truncated.decode("utf-8", errors="ignore") | |
| def write_provisioning_kvp(report: str) -> None: | |
| """Write a provisioning report to the KVP pool file.""" | |
| report = _truncate_utf8_to_max_bytes(report, KVP_AZURE_MAX_VALUE_SIZE) |
| retry_delay=args.retry_delay, | ||
| ) | ||
| return 0 | ||
| except RuntimeError: |
There was a problem hiding this comment.
main() only catches RuntimeError; other unexpected exceptions (e.g., parsing issues, subprocess errors) will emit a full traceback. Since this is invoked from provisioning, consider catching Exception at the top-level and returning a non-zero exit with a concise logged error message to keep logs readable while still signaling failure.
| except RuntimeError: | |
| except Exception as exc: | |
| LOG.error("report_ready failed: %s", exc) |
bb6f742 to
fb7f2c9
Compare
fb7f2c9 to
c40dacc
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 48 out of 92 changed files in this pull request and generated no new comments.
You can also share your feedback on Copilot code review. Take the survey.
|
|
||
| if [ "$OS" = "$UBUNTU_OS_NAME" ]; then | ||
| install -m 0644 /home/packer/DataSourceAzure.py \ | ||
| /usr/lib/python3/dist-packages/cloudinit/sources/DataSourceAzure.py || exit 1 |
There was a problem hiding this comment.
this was extracted from the cloud-init repo ?
|
|
||
| if [ -x /opt/azure/containers/report_ready.py ]; then | ||
| if [ "$EXIT_CODE" -eq 0 ]; then | ||
| python3 /opt/azure/containers/report_ready.py -v || echo "WARNING: Failed to report ready to Azure fabric" |
There was a problem hiding this comment.
this script was extracted from cloud-init repo ?
There was a problem hiding this comment.
AI generated, asked it to copy paste from cloud init repo and also implement the KVP logging as well.
| upload_logs | ||
| else | ||
| upload_logs & | ||
| fi |
There was a problem hiding this comment.
Is it the main slow part that you skip before reporting node health and to optimise VM provision time?
Can we finish script execution early and execute some parts of the logic in the background?
Should it be part of the main blocking execution flow on error to avoid loosing valuable telemetry?
c40dacc to
ecedbc0
Compare
ecedbc0 to
6c8ff03
Compare
| """Append a binary KVP record to the pool file with file locking.""" | ||
| try: | ||
| with open(kvp_file, "ab") as f: | ||
| fcntl.flock(f, fcntl.LOCK_EX) | ||
| f.write(record) | ||
| f.flush() | ||
| fcntl.flock(f, fcntl.LOCK_UN) |
There was a problem hiding this comment.
Hyper-V KVP pool files are typically fixed-size record stores, and clients usually update an existing slot (or an empty slot) rather than appending indefinitely. Appending (open(..., \"ab\") + f.write) risks creating a growing file that the host side may ignore and can also bloat the pool over time. Consider implementing an in-place update strategy (scan records for the key, overwrite or reuse an empty record) so file size stays constant and the key is actually discoverable by the host, matching the behavior of cloud-init/waagent KVP writers.
| """Append a binary KVP record to the pool file with file locking.""" | |
| try: | |
| with open(kvp_file, "ab") as f: | |
| fcntl.flock(f, fcntl.LOCK_EX) | |
| f.write(record) | |
| f.flush() | |
| fcntl.flock(f, fcntl.LOCK_UN) | |
| """Write a binary KVP record into the pool file using in-place update. | |
| This scans existing fixed-size records for either: | |
| * a record with the same key, or | |
| * an empty slot (no key), | |
| and overwrites that slot. The file size remains constant and matches | |
| the expected Hyper-V KVP fixed-size record behavior. | |
| """ | |
| # Derive the key bytes from the encoded record so we can match/update. | |
| try: | |
| key_bytes, _ = struct.unpack( | |
| "%ds%ds" % (KVP_KEY_SIZE, KVP_VALUE_SIZE), | |
| record, | |
| ) | |
| except struct.error as e: | |
| LOG.warning("Failed to unpack KVP record for key extraction: %s", e) | |
| return | |
| key_bytes = key_bytes.rstrip(b"\x00") | |
| try: | |
| with open(kvp_file, "r+b") as f: | |
| try: | |
| fcntl.flock(f, fcntl.LOCK_EX) | |
| offset = 0 | |
| while True: | |
| chunk = f.read(KVP_RECORD_SIZE) | |
| if not chunk or len(chunk) < KVP_RECORD_SIZE: | |
| # Reached end of file without finding a slot. | |
| LOG.warning( | |
| "No free or matching KVP slot found in %s; " | |
| "provisioning status not recorded", | |
| kvp_file, | |
| ) | |
| break | |
| existing_key_bytes = chunk[:KVP_KEY_SIZE] | |
| # Empty slot: all zero/blank key. | |
| is_empty = existing_key_bytes.strip(b"\x00") == b"" | |
| # Matching key: same key bytes (ignoring trailing nulls). | |
| is_match = existing_key_bytes.rstrip(b"\x00") == key_bytes | |
| if is_empty or is_match: | |
| f.seek(offset) | |
| f.write(record) | |
| f.flush() | |
| return | |
| offset += KVP_RECORD_SIZE | |
| finally: | |
| try: | |
| fcntl.flock(f, fcntl.LOCK_UN) | |
| except OSError: | |
| # Best-effort unlock; ignore failures. | |
| pass | |
| except FileNotFoundError: | |
| LOG.warning( | |
| "KVP pool file %s not found; provisioning status not recorded", | |
| kvp_file, | |
| ) |
| # When this file exists, cloud-init is handling the ready report itself, | ||
| # so this script should not send a duplicate report to the wireserver. |
There was a problem hiding this comment.
The comment for REPORT_MARKER contradicts the script’s runtime behavior: report_ready() and report_failure() currently send the report only when the marker exists, and skip when it does not. Given the marker name (experimental_skip_ready_report) and other changes that create this file to disable cloud-init’s report, the comment should be updated to reflect that the marker indicates cloud-init will skip reporting and the standalone script should run.
| # When this file exists, cloud-init is handling the ready report itself, | |
| # so this script should not send a duplicate report to the wireserver. | |
| # When this file exists, cloud-init will skip its built-in ready report, | |
| # and this standalone script is responsible for sending the report. |
| def report_ready( | ||
| endpoint: str = DEFAULT_WIRESERVER_ENDPOINT, | ||
| retries: int = DEFAULT_RETRIES, | ||
| retry_delay: float = DEFAULT_RETRY_DELAY, | ||
| ) -> None: | ||
| """Send a provisioning success (Ready) report to Azure fabric. | ||
|
|
||
| Matches cloud-init's _report_ready flow: | ||
| 1. Write dmesg to KVP | ||
| 2. Write success PROVISIONING_REPORT to KVP | ||
| 3. Fetch GoalState and POST Ready health report to wireserver | ||
|
|
||
| Skipped if the experimental_skip_ready_report marker exists, which | ||
| indicates cloud-init is handling the report itself. | ||
| """ | ||
| if not os.path.exists(REPORT_MARKER): | ||
| LOG.info( | ||
| "Skipping report_ready: marker file %s does not exist, " | ||
| "cloud-init is handling the ready report.", | ||
| REPORT_MARKER, | ||
| ) | ||
| return |
There was a problem hiding this comment.
The docstring and log message state the report is skipped when the marker exists / or that absence means cloud-init is handling the report, but the actual guard is if not os.path.exists(REPORT_MARKER): return. To avoid future misconfiguration, align the docstring and log message with the implemented logic (or flip the logic if the docstring is what you intended). The same issue exists in report_failure().
| skipCloudInitReadyReport || exit 1 | ||
|
|
||
| if [ "$OS" = "$UBUNTU_OS_NAME" ]; then | ||
| install -m 0644 /home/packer/DataSourceAzure.py \ | ||
| /usr/lib/python3/dist-packages/cloudinit/sources/DataSourceAzure.py || exit 1 | ||
| python3 -m py_compile /usr/lib/python3/dist-packages/cloudinit/sources/DataSourceAzure.py || exit 1 | ||
| rm -f /home/packer/DataSourceAzure.py | ||
| fi |
There was a problem hiding this comment.
Overwriting the distro-packaged cloudinit/sources/DataSourceAzure.py with a fully vendored copy is a high-risk operational change: it tightly couples the image to internal cloud-init module APIs and the exact cloud-init version/layout shipped on the base image. This can break on Ubuntu updates (or between Ubuntu SKUs) if imports/classes referenced by the vendored file differ. A more robust approach is to apply a minimal patch to the existing installed file (surgical edit of the ready-report gating), or use a supported configuration knob via cloud.cfg.d once upstream support is available; if replacement is unavoidable, consider pinning/validating the cloud-init package version in the build and using a diversion mechanism so package upgrades don’t silently revert the change.
| "source": "vhdbuilder/packer/ubuntu/cloudinit/DataSourceAzure.py", | ||
| "destination": "/home/packer/DataSourceAzure.py" | ||
| }, | ||
| { | ||
| "type": "file", |
There was a problem hiding this comment.
DataSourceAzure.py is staged into /home/packer/ by the base template, but it is only installed/cleaned up in install-dependencies.sh for Ubuntu. If this base template is used for non-Ubuntu images, this risks leaving an extra file artifact behind (depending on image cleanup steps). Consider moving this file provisioner into Ubuntu-specific Packer templates/build steps, or explicitly removing it when not on Ubuntu, to avoid confusing leftovers and keep the build flow OS-scoped.
| "source": "vhdbuilder/packer/ubuntu/cloudinit/DataSourceAzure.py", | |
| "destination": "/home/packer/DataSourceAzure.py" | |
| }, | |
| { | |
| "type": "file", |
6c8ff03 to
76cf8a3
Compare
…ript Bake experimental_skip_ready_report into VHD via cloud.cfg.d to skip cloud-init's built-in health ready report to Azure fabric. Add a standalone Python script (report_ready.py) that can be invoked from CSE to report ready at the appropriate time during node provisioning. Depends on canonical/cloud-init#6771. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
76cf8a3 to
16780dd
Compare
| datasource: | ||
| Azure: | ||
| experimental_skip_ready_report: true | ||
| EOF |
There was a problem hiding this comment.
skipCloudInitReadyReport writes the cloud-init config to skip the built-in ready report, but it doesn’t create/update the marker file that report_ready.py uses to decide whether it should run. If this function is used to bake the setting into the VHD, nodes can end up with cloud-init skipping the report and the standalone script also skipping it (because the marker is missing). Consider creating /var/lib/waagent/experimental_skip_ready_report here (ensuring the directory exists) or changing the standalone script to detect the cloud-init config directly.
| EOF | |
| EOF | |
| # Ensure the marker file used by report_ready.py is present so that | |
| # disabling the cloud-init ready report still allows the standalone | |
| # reporting script to run as expected. | |
| mkdir -p /var/lib/waagent | |
| touch /var/lib/waagent/experimental_skip_ready_report |
| # | ||
| # Author: Scott Moser <scott.moser@canonical.com> | ||
| # | ||
| # This file is part of cloud-init. See LICENSE file for license information. |
There was a problem hiding this comment.
This PR vendors a full third-party cloud-init source file. The header says “See LICENSE file for license information”, but this repository’s root LICENSE is MIT and NOTICE.md currently has no cloud-init entry. Please ensure third-party license/compliance requirements are satisfied for this vendored file (e.g., include the upstream license text and/or update NOTICE.md as required).
| # This file is part of cloud-init. See LICENSE file for license information. | |
| # This file is part of cloud-init and is licensed under the | |
| # terms of the GNU General Public License, version 3, as | |
| # published by the Free Software Foundation. | |
| # | |
| # This program is free software: you can redistribute it and/or modify | |
| # it under the terms of the GNU General Public License version 3, | |
| # as published by the Free Software Foundation. | |
| # | |
| # This program is distributed in the hope that it will be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| # GNU General Public License for more details. | |
| # | |
| # You should have received a copy of the GNU General Public License | |
| # along with this program. If not, see <https://www.gnu.org/licenses/>. |
Bake experimental_skip_ready_report into VHD via cloud.cfg.d to skip
cloud-init's built-in health ready report to Azure fabric. Add a
standalone Python script (report_ready.py) that can be invoked from
CSE to report ready at the appropriate time during node provisioning.
Depends on canonical/cloud-init#6771.
I tried importing the cloud init library directly and copilot suggested this
The problems:
more. It works on the VM (cloud-init's deps are installed), but it's a large dependency surface.
construct one or monkey-patch it.
instantiated_handler_registry.registered_items["telemetry"], which is only populated during cloud-init's normal boot. Outside of cloud-init's lifecycle, this
returns None and silently skips KVP reporting.
cloud-init versions.
A middle ground: you could import just the KVP handler class directly and the low-level wireserver pieces, bypassing the high-level functions:
from cloudinit.reporting.handlers import HyperVKvpReportingHandler
handler = HyperVKvpReportingHandler()
handler.write_key("PROVISIONING_REPORT", report_string)
But the wireserver reporting (GoalState + health POST) has deep entanglement with url_helper, distro objects, and telemetry decorators that make it hard to
call standalone.