feat: cluster upgrade lifecycle (kubeadm upgrade, ADR-12 + R1) (#9) #18
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: | |
| - main | |
| pull_request: | |
| # Cancel superseded runs on the same ref to save runner time. | |
| concurrency: | |
| group: ci-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| # Fast Go gates: format, vet, test (with coverage), build, lint. | |
| # The Go toolchain is read from go.mod so CI can never drift from the | |
| # module's `go` directive (CLAUDE.md no-drift rule, hardening item C3). | |
| gates: | |
| name: build / vet / test / lint | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Set up Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: go.mod | |
| check-latest: false | |
| - name: gofmt -s (check only) | |
| run: make fmt-check | |
| - name: go vet | |
| run: make vet | |
| - name: go test (with coverage) | |
| run: make test | |
| - name: go build | |
| run: make build | |
| - name: golangci-lint | |
| uses: golangci/golangci-lint-action@v9 | |
| with: | |
| # Pin to the version validated locally against the v2 config. | |
| version: v2.12.2 | |
| # Derive the image-build matrix from the single source of truth for the | |
| # supported Kubernetes window: kubeadm.SupportedMinors in version.go (ADR-3). | |
| # Rolling the window there automatically rolls this CI matrix. | |
| discover: | |
| name: resolve supported version window | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| outputs: | |
| minors: ${{ steps.minors.outputs.minors }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Extract SupportedMinors -> JSON matrix | |
| id: minors | |
| run: | | |
| set -euo pipefail | |
| # SupportedMinors is a static string-literal slice; pull each | |
| # "major.minor" token off its declaration line. | |
| minors="$(grep 'SupportedMinors *=' internal/kubeadm/version.go \ | |
| | grep -oE '[0-9]+\.[0-9]+' \ | |
| | jq -R . | jq -cs .)" | |
| if [ "$minors" = "[]" ]; then | |
| echo "could not parse SupportedMinors from version.go" >&2 | |
| exit 1 | |
| fi | |
| echo "Supported window: $minors" | |
| echo "minors=$minors" >> "$GITHUB_OUTPUT" | |
| # Product deliverable: the multi-stage Kairos image bundling the provider | |
| # plus kubeadm/kubelet/kubectl/containerd/runc/CNI (all checksum-verified | |
| # inside the Dockerfile). Built once per supported minor; each minor's patch | |
| # is resolved to the latest upstream release at build time so pins never go | |
| # stale as the window rolls. | |
| image: | |
| name: image build (k8s ${{ matrix.minor }}) | |
| needs: discover | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| minor: ${{ fromJSON(needs.discover.outputs.minors) }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Resolve Kubernetes patch + crictl versions | |
| id: ver | |
| run: | | |
| set -euo pipefail | |
| k8s="$(curl -fsSL "https://dl.k8s.io/release/stable-${{ matrix.minor }}.txt")" | |
| case "$k8s" in | |
| v${{ matrix.minor }}.*) : ;; | |
| *) echo "unexpected patch ${k8s} for minor ${{ matrix.minor }}" >&2; exit 1 ;; | |
| esac | |
| # cri-tools publishes one release per minor: v<minor>.0. | |
| crictl="v${{ matrix.minor }}.0" | |
| echo "k8s=${k8s} crictl=${crictl}" | |
| echo "k8s=${k8s}" >> "$GITHUB_OUTPUT" | |
| echo "crictl=${crictl}" >> "$GITHUB_OUTPUT" | |
| - name: Build Kairos image | |
| run: | | |
| make image \ | |
| VERSION=ci-${{ matrix.minor }} \ | |
| KUBERNETES_VERSION=${{ steps.ver.outputs.k8s }} \ | |
| CRICTL_VERSION=${{ steps.ver.outputs.crictl }} | |
| - name: Verify bundled binaries match the target minor | |
| run: | | |
| set -euo pipefail | |
| img="kairos-kubeadm:ci-${{ matrix.minor }}" | |
| kubeadm_v="$(docker run --rm --entrypoint /usr/bin/kubeadm "$img" version -o short)" | |
| crictl_v="$(docker run --rm --entrypoint /usr/bin/crictl "$img" --version)" | |
| echo "bundled kubeadm: ${kubeadm_v}" | |
| echo "bundled crictl: ${crictl_v}" | |
| case "$kubeadm_v" in | |
| v${{ matrix.minor }}.*) ;; | |
| *) echo "kubeadm minor mismatch: want v${{ matrix.minor }}.x, got ${kubeadm_v}" >&2; exit 1 ;; | |
| esac | |
| case "$crictl_v" in | |
| *v${{ matrix.minor }}.*) ;; | |
| *) echo "crictl minor mismatch: want v${{ matrix.minor }}.x, got ${crictl_v}" >&2; exit 1 ;; | |
| esac | |
| echo "OK: image bundles the k8s ${{ matrix.minor }} toolchain" |