diff --git a/parts/linux/cloud-init/artifacts/aks-node-controller-wrapper.sh b/parts/linux/cloud-init/artifacts/aks-node-controller-wrapper.sh index 310c41da47e..3cfef6d8de4 100644 --- a/parts/linux/cloud-init/artifacts/aks-node-controller-wrapper.sh +++ b/parts/linux/cloud-init/artifacts/aks-node-controller-wrapper.sh @@ -7,7 +7,7 @@ done BIN_PATH="${BIN_PATH:-/opt/azure/containers/aks-node-controller}" HOTFIX_BIN="${BIN_PATH}-hotfix" -HOTFIX_JSON="/opt/azure/containers/aks-node-controller-hotfix.json" +HOTFIX_JSON="${HOTFIX_JSON:-/opt/azure/containers/aks-node-controller-hotfix.json}" CONFIG_PATH="${CONFIG_PATH:-/opt/azure/containers/aks-node-controller-config.json}" NBC_CMD_PATH="${NBC_CMD_PATH:-/opt/azure/containers/aks-node-controller-nbc-cmd.sh}" LOGGER_TAG="aks-node-controller-wrapper" @@ -27,6 +27,22 @@ if [ ! -f "$CONFIG_PATH" ] && [ ! -f "$NBC_CMD_PATH" ]; then exit 0 fi +# check-hotfix reads the hotfix pointer from the LPS endpoint (IMDS-attested) and refreshes +# $HOTFIX_JSON, which the download-hotfix block below consumes, so it must run first. +# Gated default-off behind ENABLE_PROVISIONING_HOTFIX so existing VHDs behave exactly as +# before; only the literal string "true" enables it. This env var is the on-node terminal +# of the EnableProvisioningHotfix aks-rp region toggle (toggle -> absvc -> ANC), so regions +# where the toggle is off see no behavior change. The command is fail-open (always exits 0), +# but we still wrap it defensively so it can never block provisioning. +if [ "${ENABLE_PROVISIONING_HOTFIX:-}" = "true" ]; then + log "ENABLE_PROVISIONING_HOTFIX=true; running check-hotfix to refresh hotfix pointer" + if "$BIN_PATH" check-hotfix; then + log "ANC check-hotfix completed; hotfix pointer refresh attempted" + else + log "ANC check-hotfix failed; continuing (fail-open)" + fi +fi + if [ -f "$HOTFIX_JSON" ]; then log "Found ANC hotfix config at ${HOTFIX_JSON}; running download-hotfix" if "$BIN_PATH" download-hotfix; then diff --git a/spec/parts/linux/cloud-init/artifacts/aks_node_controller_wrapper_spec.sh b/spec/parts/linux/cloud-init/artifacts/aks_node_controller_wrapper_spec.sh index 2dac2046abd..825c21f2867 100644 --- a/spec/parts/linux/cloud-init/artifacts/aks_node_controller_wrapper_spec.sh +++ b/spec/parts/linux/cloud-init/artifacts/aks_node_controller_wrapper_spec.sh @@ -35,11 +35,14 @@ EOF export BIN_PATH="${TEST_DIR}/aks-node-controller" export CONFIG_PATH="${TEST_DIR}/aks-node-controller-config.json" export NBC_CMD_PATH="${TEST_DIR}/aks-node-controller-nbc-cmd.sh" + # Point hotfix pointer at a test-local path (absent by default) so tests never + # touch the production /opt/azure path and can control the download-hotfix branch. + export HOTFIX_JSON="${TEST_DIR}/aks-node-controller-hotfix.json" } cleanup_wrapper_test() { rm -rf "$TEST_DIR" - unset BIN_PATH CONFIG_PATH NBC_CMD_PATH TEST_DIR BIN_DIR + unset BIN_PATH CONFIG_PATH NBC_CMD_PATH TEST_DIR BIN_DIR HOTFIX_JSON ENABLE_PROVISIONING_HOTFIX CHECK_HOTFIX_EXIT } create_fake_aks_node_controller() { @@ -51,6 +54,21 @@ EOF chmod +x "$BIN_PATH" } + # Records each subcommand (first arg) on its own line in calls log so ordering across + # multiple invocations (check-hotfix vs download-hotfix vs provision) is observable. + # CHECK_HOTFIX_EXIT controls the exit code of the check-hotfix invocation only. + create_recording_aks_node_controller() { + cat >"$BIN_PATH" <<'EOF' +#!/bin/sh +printf '%s\n' "$1" >>"${TEST_DIR}/calls" +if [ "$1" = "check-hotfix" ]; then + exit "${CHECK_HOTFIX_EXIT:-0}" +fi +exit 0 +EOF + chmod +x "$BIN_PATH" + } + BeforeEach setup_wrapper_test AfterEach cleanup_wrapper_test @@ -108,4 +126,64 @@ EOF The variable secondArg should eq "--nbc-cmd=${NBC_CMD_PATH}" The variable thirdArg should eq "" End + + It 'does not call check-hotfix when ENABLE_PROVISIONING_HOTFIX is unset' + touch "$CONFIG_PATH" + create_recording_aks_node_controller + + When run bash "$SCRIPT" + The status should be success + The output should not include "running check-hotfix" + The path "${TEST_DIR}/calls" should be exist + # Only provision should have been recorded; no check-hotfix line. + calls=$(cat "${TEST_DIR}/calls") + The variable calls should eq "provision" + End + + It 'treats a non-true ENABLE_PROVISIONING_HOTFIX value as disabled' + touch "$CONFIG_PATH" + create_recording_aks_node_controller + export ENABLE_PROVISIONING_HOTFIX="1" + + When run bash "$SCRIPT" + The status should be success + The output should not include "running check-hotfix" + calls=$(cat "${TEST_DIR}/calls") + The variable calls should eq "provision" + End + + It 'runs check-hotfix before download-hotfix when ENABLE_PROVISIONING_HOTFIX is true' + touch "$CONFIG_PATH" "$HOTFIX_JSON" + create_recording_aks_node_controller + export ENABLE_PROVISIONING_HOTFIX="true" + + When run bash "$SCRIPT" + The status should be success + The output should include "running check-hotfix" + The output should include "ANC check-hotfix completed" + firstCall=$(sed -n '1p' "${TEST_DIR}/calls") + secondCall=$(sed -n '2p' "${TEST_DIR}/calls") + thirdCall=$(sed -n '3p' "${TEST_DIR}/calls") + The variable firstCall should eq "check-hotfix" + The variable secondCall should eq "download-hotfix" + The variable thirdCall should eq "provision" + End + + # Fail-open also covers the backward-compat case where ENABLE_PROVISIONING_HOTFIX=true reaches + # a node whose VHD-baked binary predates 2.1b: `check-hotfix` is an unknown subcommand + # there and exits non-zero, which the wrapper tolerates so provisioning still proceeds. + It 'proceeds to provision when check-hotfix fails (fail-open)' + touch "$CONFIG_PATH" + create_recording_aks_node_controller + export ENABLE_PROVISIONING_HOTFIX="true" + export CHECK_HOTFIX_EXIT="1" + + When run bash "$SCRIPT" + The status should be success + The output should include "ANC check-hotfix failed; continuing (fail-open)" + firstCall=$(sed -n '1p' "${TEST_DIR}/calls") + lastCall=$(tail -n 1 "${TEST_DIR}/calls") + The variable firstCall should eq "check-hotfix" + The variable lastCall should eq "provision" + End End