Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions aks-node-controller/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ type App struct {
// grpcDialContext overrides how the gRPC LPS client dials, letting tests point the client at
// an in-process (bufconn) server. When nil, the real TLS dial to the apiserver front is used.
grpcDialContext func(ctx context.Context, target string) (net.Conn, error)
// httpDownload overrides the real HTTP GET for download-hotfix artifact fetching, letting
// unit tests inject canned binary content or errors without real networking. When nil, the
// real HTTP download is used.
httpDownload func(ctx context.Context, url string) ([]byte, error)
// downloadDir overrides the directory where artifact downloads are staged. When empty,
// defaults to filepath.Dir(hotfixBinaryPath). Used for testing.
downloadDir string
}

// provision.json values are emitted as strings by the shell jq invocation.
Expand Down
21 changes: 15 additions & 6 deletions aks-node-controller/checkhotfix.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ func (a *App) checkHotfix(ctx context.Context) (checkHotfixOutcome, error) {
// value keeps the reported outcome consistent with what download-hotfix will actually read:
// a pointer with no entry for this node's base stages nothing resolvable, so it must report
// noHotfixForBase, not LPSRead.
staged := hotfixConfig{Hotfixes: cfg.Hotfixes}
staged := hotfixConfig{Hotfixes: cfg.Hotfixes, Artifacts: cfg.Artifacts}

if err := writeHotfixConfig(hotfixPath, staged); err != nil {
return outcomeFailed, fmt.Errorf("writing hotfix config: %w", err)
Expand Down Expand Up @@ -437,15 +437,16 @@ func (a *App) coldStartHotfixConfig() (hotfixConfig, bool, error) {
// Lenient parse: the AKSNodeConfig is protojson, but the cold-start pointer is an
// out-of-contract top-level object, so parse it permissively with encoding/json.
var lenient struct {
Hotfixes map[string]string `json:"hotfixes"`
Hotfixes map[string]string `json:"hotfixes"`
Artifacts map[string]map[string]artifactInfo `json:"artifacts"`
}
if err := json.Unmarshal(raw, &lenient); err != nil {
return hotfixConfig{}, false, fmt.Errorf("parsing cold-start hotfixes from node config: %w", err)
}
if len(lenient.Hotfixes) == 0 {
return hotfixConfig{}, false, nil
}
return hotfixConfig{Hotfixes: lenient.Hotfixes}, true, nil
return hotfixConfig{Hotfixes: lenient.Hotfixes, Artifacts: lenient.Artifacts}, true, nil
}

// writeHotfixConfig stages the LPS-served hotfixes map to the path download-hotfix reads.
Expand Down Expand Up @@ -474,13 +475,21 @@ func writeHotfixConfig(path string, cfg hotfixConfig) error {
hotfixes = map[string]string{}
}
out := struct {
Version string `json:"version,omitempty"`
ScriptsVersion string `json:"scripts_version,omitempty"`
Hotfixes map[string]string `json:"hotfixes"`
Version string `json:"version,omitempty"`
ScriptsVersion string `json:"scripts_version,omitempty"`
Hotfixes map[string]string `json:"hotfixes"`
Artifacts map[string]map[string]artifactInfo `json:"artifacts,omitempty"`
}{
Version: existing.Version,
ScriptsVersion: existing.ScriptsVersion,
Hotfixes: hotfixes,
Artifacts: cfg.Artifacts,
}
Comment thread
abigailliang-aks-sig-node marked this conversation as resolved.
// Preserve existing artifacts when the incoming config has none (e.g. LPS response
// doesn't include artifacts yet). This mirrors the Version/ScriptsVersion preservation
// and avoids erasing artifacts that cloud-init originally wrote.
if out.Artifacts == nil {
out.Artifacts = existing.Artifacts
}
data, err := json.Marshal(out)
if err != nil {
Expand Down
Loading
Loading