- name
- acn-go-version-bump
- description
- Go version upgrade procedure for Azure Container Networking. Use when upgrading Go minor/patch versions, bumping MS Go toolchain, fixing FIPS/systemcrypto configuration, updating Dockerfile templates, or responding to Go CVE patches. Covers the 3-tier automation (digest refresh, patch bump, minor upgrade) and the manual steps for each tier.
- user-invocable
- true
- license
- MIT
- compatibility
- Designed for GitHub Copilot Coding Agent and Claude Code.
- metadata
- {"author":"behzad-mir","version":"4.6.0"}
- allowed-tools
- Read Edit Write Glob Grep Bash(go:*) Bash(make:*) Bash(skopeo:*) Bash(git:*) Bash(gh:*) Agent
**Persona:** You are a Go platform engineer maintaining the Azure Container Networking build toolchain. You understand MS Go's FIPS requirements, MCR image tagging, and the multi-file version propagation needed for Go upgrades in this repo.
**Modes:**
- **Upgrade mode** — performing a Go version upgrade (minor or patch). Analyze MS Go docs, assess repo impact, then execute changes.
- **FIPS audit mode** — verifying crypto configuration is correct for the current Go version and CGO settings.
- **Workflow debug mode** — troubleshooting the `go-version-check.yaml` automation workflow.
---
# Go Version Upgrade Procedure
## Step 0: Analyze MS Go Documentation (MANDATORY)
**CRITICAL: Before making ANY code changes, you MUST fetch and analyze ALL relevant MS Go documentation for the target version. Do not rely on hardcoded rules — requirements change between versions.**
### Documents to Fetch
**These docs may be pre-cached in `.github/ms-go-docs/` by `copilot-setup-steps.yml` (which runs before the agent firewall activates).** Try reading the cached files first. If they don't exist (e.g., running locally or cache wasn't populated), fall back to `gh api`.
```bash
# 1. FIPS README — the AUTHORITATIVE source for crypto configuration
# PREFERRED: read pre-cached file (firewall blocks cross-repo gh api calls)
cat .github/ms-go-docs/README.md 2>/dev/null || \
gh api "repos/microsoft/go/contents/eng/doc/fips/README.md?ref=microsoft/main" --jq '.content' | base64 -d
# 2. NocgoOpenSSL — detailed nocgo backend docs
cat .github/ms-go-docs/NocgoOpenSSL.md 2>/dev/null || \
gh api "repos/microsoft/go/contents/eng/doc/NocgoOpenSSL.md?ref=microsoft/main" --jq '.content' | base64 -d
# 3. Installation guide
cat .github/ms-go-docs/Installation.md 2>/dev/null || \
gh api "repos/microsoft/go/contents/eng/doc/Installation.md?ref=microsoft/main" --jq '.content' | base64 -d
# 4. Migration Guide — toolchain behavior, breaking changes
cat .github/ms-go-docs/MigrationGuide.md 2>/dev/null || \
gh api "repos/microsoft/go/contents/eng/doc/MigrationGuide.md?ref=microsoft/main" --jq '.content' | base64 -d
# 5. FIPS User Guide — runtime requirements, crypto API behavior
cat .github/ms-go-docs/UserGuide.md 2>/dev/null || \
gh api "repos/microsoft/go/contents/eng/doc/fips/UserGuide.md?ref=microsoft/main" --jq '.content' | base64 -d
# 6. Additional Features — all MS-specific patches
cat .github/ms-go-docs/AdditionalFeatures.md 2>/dev/null || \
gh api "repos/microsoft/go/contents/eng/doc/AdditionalFeatures.md?ref=microsoft/main" --jq '.content' | base64 -d
# 7. Upstream Go release notes (fetch from go.dev — may be blocked)
# curl -sL https://go.dev/doc/go1.<MINOR> || echo "Blocked by firewall — skip"
```
**Image digests are also pre-resolved** into `.github/image-digests/`:
```bash
cat .github/image-digests/go-image.txt # Go builder SHA
cat .github/image-digests/mariner-core.txt # Azure Linux base/core SHA
cat .github/image-digests/mariner-distroless.txt # Azure Linux distroless SHA
cat .github/image-digests/windows-hpc.txt # Windows HPC base image SHA
```
Use these cached values for Dockerfile updates when `skopeo` is blocked by the firewall.
### Analysis Procedure — GOEXPERIMENT Determination
**This is the step the agent failed on previously. Follow it precisely.**
After fetching the docs, you MUST determine the correct `GOEXPERIMENT` value for EVERY build in this repo. The answer depends on THREE things:
1. The target Go version number
2. Whether the build uses `CGO_ENABLED=0` or `CGO_ENABLED=1`
3. The platform (Linux vs Windows)
**How to find the answer in the docs:**
1. Open `eng/doc/fips/README.md` and find the section titled **"Usage: Common configurations"**
2. This section contains a table mapping (OS, CGO setting, Go version) → required GOEXPERIMENT
3. Extract the GOEXPERIMENT value for:
- Linux + CGO_ENABLED=1 → typically `systemcrypto` (uses OpenSSL via dlopen)
- Linux + CGO_ENABLED=0 → may need a special experiment (e.g., `ms_nocgo_opensslcrypto`)
- Windows → typically no GOEXPERIMENT needed (CNG backend works without CGO)
**CRITICAL UNDERSTANDING:** In MS Go, the crypto backend is NOT optional — it's mandatory for FIPS compliance. If a build requires `CGO_ENABLED=0` (static binary) on Linux, and the default crypto backend requires CGO, then you MUST set a GOEXPERIMENT that provides a nocgo-compatible backend. Without it, **the build will fail** with linker errors or crypto initialization panics.
**DO NOT** assume that "no GOEXPERIMENT" is safe for CGO=0 builds. Read the docs and determine what backend is used by default and whether it requires CGO.
### Cross-Reference with Repo
After determining the correct GOEXPERIMENT per (CGO, OS) pair, audit EVERY build path:
```bash
# Find all CGO settings in build scripts
grep -rn "CGO_ENABLED" .pipelines/build/scripts/ --include="*.sh"
# Find all CGO settings in Dockerfiles and templates
grep -rn "CGO_ENABLED" . --include="*.Dockerfile" --include="*.Dockerfile.tmpl" --include="Dockerfile.tmpl"
# Find CGO settings in Makefiles
grep -rn "CGO_ENABLED" Makefile */Makefile
# ALSO find implicit CGO=1 via -buildmode=c-shared (doesn't explicitly set CGO_ENABLED)
grep -rn "buildmode=c-shared" Makefile */Makefile .pipelines/build/scripts/
```
For EACH file that sets `CGO_ENABLED` OR uses `-buildmode=c-shared`, you MUST ensure the correct `GOEXPERIMENT` is set in the same scope.
### Full Analysis Checklist
#### A. Build Environment Requirements
- [ ] Required `GOEXPERIMENT` values per (CGO, OS) combination — **from fips/README.md table**
- [ ] Required build flags (`-buildmode`, `-ldflags`, etc.)
- [ ] `GOTOOLCHAIN` behavior changes
- [ ] Any new env vars introduced or deprecated
- [ ] `go mod tidy` behavior changes (stricter validation, new directives)
#### B. Runtime Dependencies
- [ ] Required system libraries (libc, libdl, libpthread, libcrypto)
- [ ] Base image requirements (what libraries must be present)
- [ ] Architecture-specific limitations
Cross-reference:
- Check `MARINER_DISTROLESS_IMG` in `build/images.mk`
- Verify runtime base images have required crypto libraries
#### C. Crypto/FIPS Changes (CRITICAL)
- [ ] Which crypto backend is selected BY DEFAULT (no GOEXPERIMENT set)?
- [ ] Does the default backend require CGO? If yes → CGO=0 builds WILL FAIL without intervention
- [ ] What GOEXPERIMENT enables a nocgo-compatible backend?
- [ ] Is `MS_GO_NOSYSTEMCRYPTO` deprecated?
- [ ] Any crypto API behavior changes (non-FIPS curves, key sizes)?
#### D. Compatibility & Breaking Changes
- [ ] Deprecated stdlib APIs
- [ ] Module system changes
- [ ] Key dependency support (controller-runtime, client-go, cilium)
### Output: Change Plan
Before making any code changes, produce a change plan:
```markdown
## MS Go <VERSION> Upgrade — Requirements Analysis
### Source Documents Reviewed
- [ ] eng/doc/fips/README.md: <GOEXPERIMENT table findings>
- [ ] eng/doc/NocgoOpenSSL.md: <nocgo backend details>
- [ ] docs/go1.XX.md: <version-specific changes>
- [ ] eng/doc/MigrationGuide.md: <relevant migration steps>
- [ ] eng/doc/fips/UserGuide.md: <runtime requirements>
### GOEXPERIMENT Determination (from fips/README.md)
| Build Configuration | GOEXPERIMENT Required | Reason |
|---|---|---|
| Linux + CGO_ENABLED=1 | <value from docs> | <why> |
| Linux + CGO_ENABLED=0 | <value from docs> | <why — if blank, explain why safe> |
| Windows (any CGO) | <value from docs> | <why> |
### Files Requiring GOEXPERIMENT Changes
List EVERY file that needs modification:
| File | CGO Setting | Current GOEXPERIMENT | Required GOEXPERIMENT | Action |
|---|---|---|---|---|
| .pipelines/build/scripts/cni.sh | 0 | (none) | <value> | Add export |
| .pipelines/build/scripts/cns.sh | 0 | (none) | <value> | Add export |
| ... | ... | ... | ... | ... |
| Makefile (all CGO=0 targets) | 0 | (none) | <value> | Add inline |
### Risk Assessment
- Breaking changes affecting this codebase: ...
- FIPS compliance impact: ...
```
**Only proceed with code changes after the analysis is complete and EVERY build path is accounted for.**
---
## Step 1: Execute Version Bump
### Go Version Strategy
ACN uses **floating minor version tags** for the Go build image (`build/images.mk`):
- `GO_IMG` uses a 2-part minor version tag (e.g., `golang:1.26-azurelinux3.0`)
- The floating tag resolves to the latest patch via SHA digest at `make dockerfiles` time
**Separate compatibility from the preferred toolchain:**
- The `go` directive is the module's minimum language/toolchain and dependency
compatibility floor. Keep it at the lowest version supported by the source
and every required dependency.
- The `toolchain` directive selects the exact preferred patch used for
development and CI. Patch bumps update this directive in every independently
tested module without raising the `go` floor.
- Use a full release version for the floor when dependencies require it. For
example, `go 1.25` sorts before `go 1.25.0` and cannot satisfy dependencies
that declare `go 1.25.0`.
**Example:** If the compatibility floor is Go 1.25 and the supported build
toolchain is Go 1.26.7, use this in every module:
```go
go 1.25.0
toolchain go1.26.7
```
### Version Sources (ALL must be updated — do NOT skip any)
**⚠️ IMPORTANT: The ROOT `go.mod` is the FIRST file to update. Do NOT only update sub-modules.**
```
build/images.mk (GO_IMG=golang:1.XX-azurelinux3.0) ← primary image tag
│
├── ROOT MODULE:
│ └── → go.mod (go floor + exact toolchain)
│
├── BUILD ENVIRONMENT:
│ ├── → tools-go/go.mod (same floor + toolchain)
│ ├── → .devcontainer/Dockerfile (VARIANT="1.XX") ← dev container version
│ ├── → .pipelines/build/scripts/install-go.sh (DEFAULT_IMAGE SHA)
│ ├── → bpf-prog/ipv6-hp-bpf/linux.Dockerfile (Go image SHA)
│ ├── → npm/linux.Dockerfile (tag 1.XX.Y)
│ ├── → npm/windows.Dockerfile (tag 1.XX.Y)
│ └── → All .tmpl Dockerfiles (via `make dockerfiles`)
│
└── INDEPENDENT MODULES (update toolchain in EACH; raise go floor only when required):
├── → azure-ipam/go.mod
├── → azure-ip-masq-merger/go.mod
├── → azure-iptables-monitor/go.mod
├── → bpf-prog/ipv6-hp-bpf/go.mod
├── → cilium-log-collector/go.mod
├── → cni/go.mod
├── → crd/go.mod
├── → dropgz/go.mod
├── → npm/go.mod
├── → pkgerrlint/go.mod
├── → tools/azure-npm-to-cilium-validator/go.mod
└── → zapai/go.mod
```
### Files to Update (in order)
1. **`go.mod` (ROOT)** — Update the exact `toolchain` directive.
- Preserve the `go` compatibility floor unless source or dependencies require
a newer language version.
2. **`build/images.mk`** — Update `GO_IMG` tag
- ALWAYS use 2-part floating tag: `1.27-azurelinux3.0`, never `1.27.0-azurelinux3.0`
3. **`tools-go/go.mod`** — Update `toolchain` to match root
4. **All sub-module `go.mod` files** — Update `toolchain` to match (see full list above)
5. **Do NOT run `go mod tidy`** — it times out in the agent environment
- Existing `go.sum` files remain valid for pure version bumps (deps don't change)
- `tools-go/go.sum` is handled by the migration step (copy from `tools.go.sum`)
- CI or a follow-up commit will reconcile any checksum drift if needed
6. **`.devcontainer/Dockerfile`** — Update `VARIANT` arg to `"1.XX"`
7. **`.pipelines/build/scripts/install-go.sh`** — Update `DEFAULT_IMAGE` to new Go image digest:
```bash
# Read the pre-cached Go image digest (resolved during copilot-setup-steps)
NEW_GO_DIGEST=$(cat .github/image-digests/go-image.txt 2>/dev/null)
# If cache exists, use it to update install-go.sh:
if [ -n "$NEW_GO_DIGEST" ]; then
sed -i "s|DEFAULT_IMAGE=\".*\"|DEFAULT_IMAGE=\"${NEW_GO_DIGEST}\"|" .pipelines/build/scripts/install-go.sh
fi
```
Also update the `skopeo inspect` comment above it to reference the new tag.
8. **`bpf-prog/ipv6-hp-bpf/linux.Dockerfile`** — Update Go image tag and SHA
- ⚠️ This Dockerfile uses a **plain Debian-based Go image** (e.g., `golang:1.26.4`), NOT the `-azurelinux3.0` variant used elsewhere
- It needs `apt-get` for BPF tooling (llvm, clang, libbpf-dev), which is only available on Debian
- **Do NOT use the same digest as `install-go.sh` or `build/images.mk`** — those use the Azure Linux image
- Resolve the correct digest separately:
```bash
skopeo inspect "docker://mcr.microsoft.com/oss/go/microsoft/golang:1.XX.Y" --format "{{.Digest}}"
```
(using the plain patch tag, e.g., `1.26.6`, without `-azurelinux3.0`)
9. **`npm/linux.Dockerfile`** and **`npm/windows.Dockerfile`** — Update Go tag
- ⚠️ npm Dockerfiles use **plain patch tag** (e.g., `golang:1.26.4`), NOT the `-azurelinux3.0` suffixed tag
- The npm builder uses Ubuntu as runtime base, not Azure Linux
- `npm/windows.Dockerfile` builds on a **Linux builder** (`--platform=linux/amd64`) cross-compiling with `GOOS=windows` — it still needs `GOEXPERIMENT` for CGO_ENABLED=0 on the Linux build stage
- Replace `MS_GO_NOSYSTEMCRYPTO=1` with appropriate `GOEXPERIMENT=<value>` in BOTH files
10. **Run `make dockerfiles`** — Regenerate all template-based Dockerfiles
### Step 1b: Apply GOEXPERIMENT to ALL Build Paths (CRITICAL)
**This step is where the previous agent failed. Do NOT skip any file.**
Based on your GOEXPERIMENT determination from Step 0, you must update EVERY build path. Here is the COMPLETE list of locations that need GOEXPERIMENT:
#### Pipeline Build Scripts (`.pipelines/build/scripts/*.sh`)
Each script that sets `CGO_ENABLED` MUST also export the correct `GOEXPERIMENT`:
```bash
# For CGO_ENABLED=0 scripts (Linux-only guard if script is multi-platform):
if [[ "$GOOS" == "linux" ]] || [[ -z "$GOOS" ]]; then
export GOEXPERIMENT=<value_for_cgo0>
fi
export CGO_ENABLED=0
# For CGO_ENABLED=1 scripts:
export GOEXPERIMENT=<value_for_cgo1>
export CGO_ENABLED=1
```
Scripts to update:
- `.pipelines/build/scripts/cni.sh`
View on GitHub