Skip to content

Build & Push RKE2-HAMi Warewulf Image #11

Build & Push RKE2-HAMi Warewulf Image

Build & Push RKE2-HAMi Warewulf Image #11

name: Build & Push RKE2-HAMi Warewulf Image
# Every version knob defaults to "0" (latest). Override per-run via the inputs
# below, or set a persistent default as a repository Variable (Settings ->
# Secrets and variables -> Actions -> Variables) using the same names with an
# _OVERRIDE suffix (e.g. RKE2_VERSION_OVERRIDE, HAMI_VERSION_OVERRIDE).
on:
push:
branches:
- latest
workflow_dispatch:
inputs:
RKE2_VERSION_OVERRIDE:
description: 'RKE2 version (e.g. v1.31.5+rke2r1). "0" = latest.'
required: false
default: '0'
KERNEL_VERSION_OVERRIDE:
description: 'Kernel version (e.g. 6.8.0-31-generic). "0" = latest, "none" = no kernel.'
required: false
default: '0'
NVIDIA_INSTALL_ENABLED:
description: 'Bake NVIDIA driver into the image (1=Yes, 0=No).'
required: true
type: choice
options: ['1', '0']
default: '1'
NVIDIA_DRIVER_VERSION_OVERRIDE:
description: 'NVIDIA driver version (e.g. 570.148.08). "0" = latest production branch.'
required: false
default: '0'
NVIDIA_CONTAINER_TOOLKIT_VERSION_OVERRIDE:
description: 'nvidia-container-toolkit version (e.g. 1.17.8). "0" = latest.'
required: false
default: '0'
HELM_VERSION_OVERRIDE:
description: 'Helm version (e.g. v3.16.2). "0" = latest.'
required: false
default: '0'
SSG_VERSION_OVERRIDE:
description: 'SCAP Security Guide tag (e.g. v0.1.76). "0" = latest.'
required: false
default: '0'
HAMI_VERSION_OVERRIDE:
description: 'HAMi Helm chart version (e.g. 2.6.0). "0" = latest.'
required: false
default: '0'
FIRSTBOOT_ENABLED:
description: 'Enable firstboot Ansible service (1=Yes, 0=No).'
required: true
type: choice
options: ['1', '0']
default: '1'
STABLE_TAG:
description: 'Stable moving Docker tag. Empty = "<branch>-hami" (e.g. latest-hami).'
required: false
default: ''
COMMIT_OVERLAY:
description: 'Commit the rendered HAMi overlay back to the repo (1=Yes, 0=No).'
required: true
type: choice
options: ['1', '0']
default: '1'
schedule:
- cron: '0 3 * * 1' # Every Monday at 3:00 AM UTC
permissions:
contents: write
jobs:
build-and-push:
name: Build & Push RKE2-HAMi Node Image
runs-on: ubuntu-24.04
steps:
- name: πŸ› οΈ Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.ref_name }}
- name: πŸ”§ Resolve Versions & Verify Dependencies
id: config
env:
GH_TOKEN: ${{ github.token }}
INPUT_RKE2_VERSION: ${{ github.event.inputs.RKE2_VERSION_OVERRIDE }}
VAR_RKE2_VERSION: ${{ vars.RKE2_VERSION_OVERRIDE }}
INPUT_KERNEL_VERSION: ${{ github.event.inputs.KERNEL_VERSION_OVERRIDE }}
VAR_KERNEL_VERSION: ${{ vars.KERNEL_VERSION_OVERRIDE }}
INPUT_NVIDIA_ENABLED: ${{ github.event.inputs.NVIDIA_INSTALL_ENABLED }}
VAR_NVIDIA_ENABLED: ${{ vars.NVIDIA_INSTALL_ENABLED }}
INPUT_NVIDIA_VERSION: ${{ github.event.inputs.NVIDIA_DRIVER_VERSION_OVERRIDE }}
VAR_NVIDIA_VERSION: ${{ vars.NVIDIA_DRIVER_VERSION_OVERRIDE }}
INPUT_TOOLKIT_VERSION: ${{ github.event.inputs.NVIDIA_CONTAINER_TOOLKIT_VERSION_OVERRIDE }}
VAR_TOOLKIT_VERSION: ${{ vars.NVIDIA_CONTAINER_TOOLKIT_VERSION_OVERRIDE }}
INPUT_HELM_VERSION: ${{ github.event.inputs.HELM_VERSION_OVERRIDE }}
VAR_HELM_VERSION: ${{ vars.HELM_VERSION_OVERRIDE }}
INPUT_SSG_VERSION: ${{ github.event.inputs.SSG_VERSION_OVERRIDE }}
VAR_SSG_VERSION: ${{ vars.SSG_VERSION_OVERRIDE }}
INPUT_HAMI_VERSION: ${{ github.event.inputs.HAMI_VERSION_OVERRIDE }}
VAR_HAMI_VERSION: ${{ vars.HAMI_VERSION_OVERRIDE }}
INPUT_FIRSTBOOT_ENABLED: ${{ github.event.inputs.FIRSTBOOT_ENABLED }}
VAR_FIRSTBOOT_ENABLED: ${{ vars.FIRSTBOOT_ENABLED }}
run: |
set -e
lower() { echo "$1" | tr '[:upper:]' '[:lower:]'; }
# pick(): input wins, else repo var, else default ("0")
pick() { if [ -n "$1" ] && [ "$1" != "0" ]; then echo "$1"; elif [ -n "$2" ] && [ "$2" != "0" ]; then echo "$2"; else echo "0"; fi; }
echo "## Firstboot (default ON; only an explicit 0 disables it) ##"
if [ "$INPUT_FIRSTBOOT_ENABLED" = "0" ] || [ "$VAR_FIRSTBOOT_ENABLED" = "0" ]; then FB="false"; else FB="true"; fi
echo "FIRSTBOOT_ENABLED=$FB" >> $GITHUB_OUTPUT
echo "## RKE2 version (always resolved to a concrete version) ##"
RKE2_VER="$(pick "$INPUT_RKE2_VERSION" "$VAR_RKE2_VERSION")"
if [ "$RKE2_VER" = "0" ]; then
RKE2_VER=$(curl -fsSL -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/repos/rancher/rke2/releases/latest | jq -r .tag_name)
fi
[ -z "$RKE2_VER" ] || [ "$RKE2_VER" = "null" ] && { echo "❌ Could not resolve RKE2 version"; exit 1; }
K8S_VERSION="${RKE2_VER%%+*}" # v1.31.5+rke2r1 -> v1.31.5
echo "INSTALL_RKE2_VERSION=$RKE2_VER" >> $GITHUB_OUTPUT
echo "K8S_VERSION=$K8S_VERSION" >> $GITHUB_OUTPUT
echo "🎯 RKE2=$RKE2_VER K8s=$K8S_VERSION"
echo "## Kernel ##"
KVER="$(pick "$INPUT_KERNEL_VERSION" "$VAR_KERNEL_VERSION")"
if [ "$(lower "$KVER")" = "none" ]; then
echo "KERNEL_INSTALL_ENABLED=false" >> $GITHUB_OUTPUT
echo "KERNEL_VERSION=none" >> $GITHUB_OUTPUT
elif [ "$KVER" != "0" ]; then
sudo apt-get update -qq
apt-cache policy "linux-image-${KVER}" | grep -q "Candidate:" || { echo "❌ linux-image-${KVER} not found"; exit 1; }
apt-cache policy "linux-headers-${KVER}" | grep -q "Candidate:" || { echo "❌ linux-headers-${KVER} not found"; exit 1; }
echo "KERNEL_INSTALL_ENABLED=true" >> $GITHUB_OUTPUT
echo "KERNEL_VERSION=$KVER" >> $GITHUB_OUTPUT
else
sudo apt-get update -qq
GENERIC_VERSION=$(apt-cache policy linux-image-generic | grep Candidate | awk '{print $2}')
KERNEL_PKG=$(apt-cache show "linux-image-generic=$GENERIC_VERSION" | grep -E '^Depends:' | sed -E 's/.*linux-image-([0-9]+\.[0-9]+\.[0-9]+-[0-9]+-generic).*/\1/' | head -n1)
[ -z "$KERNEL_PKG" ] && { echo "❌ Could not parse latest kernel"; exit 1; }
echo "KERNEL_INSTALL_ENABLED=true" >> $GITHUB_OUTPUT
echo "KERNEL_VERSION=$KERNEL_PKG" >> $GITHUB_OUTPUT
KVER="$KERNEL_PKG"
fi
echo "🎯 Kernel=$KVER"
echo "## NVIDIA driver ##"
NV_ON=true
if [ "$INPUT_NVIDIA_ENABLED" = "0" ] || [ "$VAR_NVIDIA_ENABLED" = "0" ]; then NV_ON=false; fi
KIE="$(grep '^KERNEL_INSTALL_ENABLED=' $GITHUB_OUTPUT | cut -d= -f2)"
if [ "$NV_ON" = "true" ] && [ "$KIE" != "true" ]; then
echo "⚠️ NVIDIA needs a kernel; disabling NVIDIA install."; NV_ON=false
fi
NV_URL="0"
if [ "$NV_ON" = "true" ]; then
NV_VER="$(pick "$INPUT_NVIDIA_VERSION" "$VAR_NVIDIA_VERSION")"
RJSON=$(curl -fsSL https://docs.nvidia.com/datacenter/tesla/drivers/releases.json)
if [ "$NV_VER" != "0" ]; then
NV_URL=$(echo "$RJSON" | jq -r --arg v "$NV_VER" '.[] | .driver_info[] | select(.release_version==$v) | .runfile_url["x86_64"]' | head -n1)
else
NV_URL=$(echo "$RJSON" | jq -r 'first(.[] | select(.type=="production branch")) | .driver_info[0].runfile_url["x86_64"]')
fi
{ [ -z "$NV_URL" ] || [ "$NV_URL" = "null" ]; } && { echo "❌ Could not resolve NVIDIA driver URL"; exit 1; }
fi
echo "NVIDIA_INSTALL_ENABLED=$NV_ON" >> $GITHUB_OUTPUT
echo "NVIDIA_DRIVER_URL=$NV_URL" >> $GITHUB_OUTPUT
echo "🎯 NVIDIA=$NV_ON URL=$NV_URL"
echo "## nvidia-container-toolkit / Helm / SSG (empty = latest) ##"
TK="$(pick "$INPUT_TOOLKIT_VERSION" "$VAR_TOOLKIT_VERSION")"; [ "$TK" = "0" ] && TK=""
HV="$(pick "$INPUT_HELM_VERSION" "$VAR_HELM_VERSION")"; [ "$HV" = "0" ] && HV=""
SV="$(pick "$INPUT_SSG_VERSION" "$VAR_SSG_VERSION")"; [ "$SV" = "0" ] && SV=""
echo "NVIDIA_CONTAINER_TOOLKIT_VERSION=$TK" >> $GITHUB_OUTPUT
echo "HELM_VERSION=$HV" >> $GITHUB_OUTPUT
echo "SSG_VERSION=$SV" >> $GITHUB_OUTPUT
echo "## HAMi chart version (empty = latest chart from repo) ##"
HAMI="$(pick "$INPUT_HAMI_VERSION" "$VAR_HAMI_VERSION")"; [ "$HAMI" = "0" ] && HAMI=""
echo "HAMI_VERSION=$HAMI" >> $GITHUB_OUTPUT
echo "🎯 HAMi chart=${HAMI:-latest}"
echo "===== Resolved configuration ====="
cat $GITHUB_OUTPUT
- name: 🧩 Render HAMi overlay (hami.yaml)
run: |
set -e
OUT=overlays/rke2-gpu/rootfs/etc/rancher/manifests/hami.yaml
K8S="${{ steps.config.outputs.K8S_VERSION }}"
HAMI="${{ steps.config.outputs.HAMI_VERSION }}"
{
echo "apiVersion: helm.cattle.io/v1"
echo "kind: HelmChart"
echo "metadata:"
echo " name: hami"
echo " namespace: kube-system"
echo "spec:"
echo " targetNamespace: kube-system"
echo " chart: hami"
echo " repo: https://project-hami.github.io/HAMi/"
if [ -n "$HAMI" ]; then echo " version: \"$HAMI\""; fi
echo " valuesContent: |-"
echo " # kubeScheduler.imageTag is auto-set to the cluster Kubernetes version by CI."
echo " scheduler:"
echo " kubeScheduler:"
echo " imageTag: \"$K8S\""
echo " # Host provides the NVIDIA driver + toolkit; rke2-gpu overlay sets the"
echo " # containerd default runtime to \"nvidia\"."
echo " devicePlugin:"
echo " runtimeClassName: \"nvidia\""
} > "$OUT"
echo "----- rendered $OUT -----"; cat "$OUT"
- name: 🏷️ Generate Docker Tag Name
id: taggen
run: |
K8S="${{ steps.config.outputs.K8S_VERSION }}"
KERNEL_VER_RAW="${{ steps.config.outputs.KERNEL_VERSION }}"
KERNEL_INSTALL_ENABLED="${{ steps.config.outputs.KERNEL_INSTALL_ENABLED }}"
NVIDIA_ENABLED="${{ steps.config.outputs.NVIDIA_INSTALL_ENABLED }}"
NVIDIA_URL="${{ steps.config.outputs.NVIDIA_DRIVER_URL }}"
K8S_SLUG=$(echo "$K8S" | tr '.' '-' | tr '[:upper:]' '[:lower:]')
if [ "$KERNEL_INSTALL_ENABLED" = "false" ]; then
TAG_NAME="hami-${K8S_SLUG}-no-kernel"
else
KERNEL_VER=$(echo "u24.04-$KERNEL_VER_RAW" | tr '.' '-' | tr '[:upper:]' '[:lower:]')
TAG_NAME="${KERNEL_VER}-${K8S_SLUG}-hami"
if [ "$NVIDIA_ENABLED" = "true" ]; then
NVIDIA_VER=$(basename "$NVIDIA_URL" | grep -oP '[0-9]+\.[0-9]+\.[0-9]+' | tr '.' '-')
TAG_NAME="${TAG_NAME}-${NVIDIA_VER}"
fi
fi
# Stable moving tag so you can point Warewulf at one predictable name.
# Defaults to "<branch>-hami" (e.g. latest-hami); override via the
# STABLE_TAG input or a STABLE_TAG repo Variable.
STABLE_TAG="${INPUT_STABLE_TAG}"
[ -z "$STABLE_TAG" ] && STABLE_TAG="${VAR_STABLE_TAG}"
[ -z "$STABLE_TAG" ] && STABLE_TAG="${TAG_NAME_REF}-hami"
echo "Generated descriptive TAG_NAME: $TAG_NAME"
echo "Generated stable STABLE_TAG: $STABLE_TAG"
echo "TAG_NAME=$TAG_NAME" >> $GITHUB_OUTPUT
echo "STABLE_TAG=$STABLE_TAG" >> $GITHUB_OUTPUT
env:
TAG_NAME_REF: ${{ github.ref_name }}
INPUT_STABLE_TAG: ${{ github.event.inputs.STABLE_TAG }}
VAR_STABLE_TAG: ${{ vars.STABLE_TAG }}
- name: 🐳 Build Docker Image
run: |
docker build \
--build-arg NVIDIA_INSTALL_ENABLED=${{ steps.config.outputs.NVIDIA_INSTALL_ENABLED }} \
--build-arg NVIDIA_DRIVER_URL=${{ steps.config.outputs.NVIDIA_DRIVER_URL }} \
--build-arg KERNEL_VERSION=${{ steps.config.outputs.KERNEL_VERSION }} \
--build-arg KERNEL_INSTALL_ENABLED=${{ steps.config.outputs.KERNEL_INSTALL_ENABLED }} \
--build-arg FIRSTBOOT_ENABLED=${{ steps.config.outputs.FIRSTBOOT_ENABLED }} \
--build-arg INSTALL_RKE2_VERSION=${{ steps.config.outputs.INSTALL_RKE2_VERSION }} \
--build-arg NVIDIA_CONTAINER_TOOLKIT_VERSION=${{ steps.config.outputs.NVIDIA_CONTAINER_TOOLKIT_VERSION }} \
--build-arg HELM_VERSION=${{ steps.config.outputs.HELM_VERSION }} \
--build-arg SSG_VERSION=${{ steps.config.outputs.SSG_VERSION }} \
-t ${{ secrets.DOCKER_HUB_REPO }}:${{ steps.taggen.outputs.TAG_NAME }} \
-t ${{ secrets.DOCKER_HUB_REPO }}:${{ steps.taggen.outputs.STABLE_TAG }} .
- name: πŸš€ Login to DockerHub
run: |
echo "${{ secrets.DOCKER_HUB_TOKEN }}" | docker login -u "${{ secrets.DOCKER_HUB_USER }}" --password-stdin
- name: πŸš€ Push Image to DockerHub
run: |
# Descriptive, immutable tag (provenance) + stable moving tag (easy pointer)
docker push ${{ secrets.DOCKER_HUB_REPO }}:${{ steps.taggen.outputs.TAG_NAME }}
docker push ${{ secrets.DOCKER_HUB_REPO }}:${{ steps.taggen.outputs.STABLE_TAG }}
- name: πŸ“ Commit rendered HAMi overlay
if: ${{ github.event.inputs.COMMIT_OVERLAY != '0' }}
run: |
set -e
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
if git diff --quiet -- overlays/rke2-gpu/rootfs/etc/rancher/manifests/hami.yaml; then
echo "No overlay changes to commit."
else
git add overlays/rke2-gpu/rootfs/etc/rancher/manifests/hami.yaml
git commit -m "ci: sync HAMi overlay (chart=${{ steps.config.outputs.HAMI_VERSION }} k8s=${{ steps.config.outputs.K8S_VERSION }}) [skip ci]"
git push origin HEAD:${{ github.ref_name }}
fi