From 75ea24cfa2f8df223f42eb920905ac93f677caea Mon Sep 17 00:00:00 2001 From: Ricardo Pardini Date: Sat, 16 Mar 2024 12:38:47 +0100 Subject: [PATCH] tinkerbell/charts: introduce `showcase` chart - `showcase` is a chart that, based on values.yaml dictionaries: - generates Tinkerbell CRs (Hardware/Template/Workflow) for both standard (UEFI) & exotic (supported by Armbian) devices - generates download/process jobs for multiple Hook flavors (see https://github.com/tinkerbell/hook/pull/205) - generates download/process jobs for a few OS images (Ubuntu Cloud Images, Armbian, etc) - should be independent of how one deployed Tinkerbell itself (stack chart, individual components, etc) - A few features: - validates values.yaml for common mistakes; arch must match, etc. - validates & handles rootDisk differences (re-invents "formatPartition()" a bit) - avoids re-downloading Hooks and Images that are already on disk, even if Job re-runs - allows easy way to use - custom Hooks - custom Kernel cmdline parameters at both the Hook & device level - for example `acpi=off` at Hook level and `console=ttyS0` at board level - custom OS images for deployment - reboot or kexec to finish deployment - different partition numbers for OS image's rootfs (some images have ESP, some have a separate `/boot`, etc) - control if growpart and/or ssh/user setup is done during provisioning or not - conversion of OS images (`qemu-to-raw-gzip` and `xz-to-gz`) - has a "merge" mechanism with a common way to set parameters like net gateway, UEFI, etc (also easy to override per-device) - default values have everything `enabled: false` thus showcase should produce nothing by default. - Hooks & Images can be forced `enabled: true` in values.yaml, or - `enabled: true` Devices automatically enable their Hook & Image - Probably missing: - More validations - Currently pointing to my Tinkerbell Actions, which I haven't PR'ed yet - How to use: - Clone it, edit the values.yaml to your liking, and deploy. Signed-off-by: Ricardo Pardini --- tinkerbell/showcase/Chart.yaml | 6 + tinkerbell/showcase/templates/devices.yaml | 251 +++++++++++++++++ tinkerbell/showcase/templates/hooks.yaml | 94 +++++++ tinkerbell/showcase/templates/images.yaml | 131 +++++++++ tinkerbell/showcase/values.yaml | 311 +++++++++++++++++++++ 5 files changed, 793 insertions(+) create mode 100644 tinkerbell/showcase/Chart.yaml create mode 100644 tinkerbell/showcase/templates/devices.yaml create mode 100644 tinkerbell/showcase/templates/hooks.yaml create mode 100644 tinkerbell/showcase/templates/images.yaml create mode 100644 tinkerbell/showcase/values.yaml diff --git a/tinkerbell/showcase/Chart.yaml b/tinkerbell/showcase/Chart.yaml new file mode 100644 index 00000000..b86bbac9 --- /dev/null +++ b/tinkerbell/showcase/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: showcase +description: Generates Tinkerbell CR's for a plethora of standard and exotic hardware; downloads & prepares Hook and OS images for provisioning +type: application +version: "0.0.1" + diff --git a/tinkerbell/showcase/templates/devices.yaml b/tinkerbell/showcase/templates/devices.yaml new file mode 100644 index 00000000..d8b9642a --- /dev/null +++ b/tinkerbell/showcase/templates/devices.yaml @@ -0,0 +1,251 @@ +{{- range $deviceId, $dev := .Values.hardware.devices }} + +{{- if not $dev.enabled }} +# Device not enabled: {{$deviceId}} +{{- else }} +# Device enabled: {{$deviceId}} +{{- $common := $.Values.hardware.common -}} +{{- $mergedDevice := merge $dev $common }} +{{- $hookObj := index $.Values.provision.hook $mergedDevice.hookRef }} +{{- if not $hookObj }}{{- fail (printf "Device '%s', hookRef '%s': %s" $deviceId $mergedDevice.hookRef "hookRef not found") }}{{- end }} +{{- $imageObj := index $.Values.provision.images $mergedDevice.imageRef }} +{{- if not $imageObj }}{{- (printf "Device '%s', imageRef '%s': %s" $deviceId $mergedDevice.imageRef "imageRef not found") }}{{- end }} +# Check sanity of arch across device / image / hook - they all must match +# Device arch: {{$dev.arch}} Image arch: {{$imageObj.arch}} Hook arch: {{$hookObj.arch}} +{{- if ne $imageObj.arch $hookObj.arch }}{{- fail (printf "Device '%s': '%s'" $deviceId "image and hook arch mismatch") }}{{- end }} +{{- if ne $dev.arch $imageObj.arch }}{{- fail (printf "Device '%s': '%s'" $deviceId "device and image arch mismatch") }}{{- end }} +{{- if ne $dev.arch $hookObj.arch }}{{- fail (printf "Device '%s': '%s'" $deviceId "device and hook arch mismatch") }}{{- end }} +{{- $rootDiskDevice := required (printf "Device '%s' - %s" $deviceId "rootDisk is required") $mergedDevice.rootDisk }} +{{- if not (hasPrefix "/dev" $rootDiskDevice) }}{{- fail (printf "Device '%s' (rootDisk '%s'): '%s'" $deviceId $rootDiskDevice "rootDisk does not begin with /dev") }}{{- end }} +{{- $rootDiskRootfsPartitionNumber := printf "%s" $imageObj.rootfsPartitionNumber }} + # rootDiskRootfsPartitionNumber is {{$rootDiskRootfsPartitionNumber}} +{{- $rootDiskRootfsPartitionDevice := "unknown" }} + +{{- if hasPrefix "/dev/disk/" $rootDiskDevice }} + # YES! {{$rootDiskDevice}} begins with /dev/disk +{{- $rootDiskRootfsPartitionDevice = printf "%s-part%s" $rootDiskDevice $rootDiskRootfsPartitionNumber }} +{{- else }} + # NO! {{$rootDiskDevice}} does not begin with /dev/disk +{{- if regexMatch "[0-9]$" $rootDiskDevice }} + # YES! {{$rootDiskDevice}} ends with a digit - REGEX MATCH +{{- $rootDiskRootfsPartitionDevice = printf "%sp%s" $rootDiskDevice $rootDiskRootfsPartitionNumber }} +{{- else }} + # NO! {{$rootDiskDevice}} does not end with a digit - REGEX NOT MATCH +{{- $rootDiskRootfsPartitionDevice = printf "%s%s" $rootDiskDevice $rootDiskRootfsPartitionNumber }} +{{- end }} +{{- end }} + # Thus at the end of the day, $rootDiskRootfsPartitionDevice is {{$rootDiskRootfsPartitionDevice}} +--- +apiVersion: "tinkerbell.org/v1alpha1" +kind: Hardware +metadata: + name: "{{ $deviceId }}-hardware" + labels: + "app.kubernetes.io/instance": "{{ $.Release.Name }}" + "app.kubernetes.io/part-of": "tinkerbell-showcase" +spec: + disks: + - device: "{{ $rootDiskDevice }}" + metadata: + facility: + facility_code: sandbox + instance: + hostname: "{{ $deviceId }}" + id: "{{ $dev.mac }}" + operating_system: + distro: "ubuntu" # @TODO + os_slug: "ubuntu_20_04" + version: "20.04" + interfaces: + - dhcp: + arch: "{{ $dev.arch }}" + hostname: "{{ $deviceId }}" + ip: + address: "{{ $dev.ipv4.address }}" + netmask: "{{ $dev.ipv4.netmask }}" + gateway: "{{ $dev.ipv4.gateway }}" + lease_time: 86400 + mac: "{{ $dev.mac }}" + name_servers: + {{- range $mergedDevice.ipv4.dns }} + - {{ . | quote }} + {{- end }} + uefi: {{ $dev.uefi }} + netboot: + allowPXE: true + allowWorkflow: true + ipxe: # @TODO + contents: | + echo Showcase starting for {{$deviceId}} with hook {{$mergedDevice.hookRef}} and image {{$mergedDevice.imageRef}}... + set download-url {{ $.Values.tinkerbell.hookURL }} + set kernel-params tink_worker_image=quay.io/tinkerbell/tink-worker:latest facility= syslog_host={{ $.Values.tinkerbell.syslogHost }} grpc_authority={{ $.Values.tinkerbell.grpcAuthority }} tinkerbell_tls=false worker_id={{$dev.mac}} hw_addr={{$dev.mac}} modules=loop,squashfs,sd-mod,usb-storage initrd={{ $hookObj.initrd }} {{$hookObj.kernelCommandLine}} {{ $dev.extraKernelCommandLine }} + echo Kernel image: ${download-url}/{{ $hookObj.kernel }} + echo Kernel initrd: ${download-url}/{{ $hookObj.initrd }} + echo Kernel cmdline: ${kernel-params} + kernel ${download-url}/{{ $hookObj.kernel }} ${kernel-params} + initrd ${download-url}/{{ $hookObj.initrd }} + boot +--- +apiVersion: "tinkerbell.org/v1alpha1" +kind: Template +metadata: + name: "{{ $deviceId }}-template" + labels: + "app.kubernetes.io/instance": "{{ $.Release.Name }}" + "app.kubernetes.io/part-of": "tinkerbell-showcase" +spec: + data: | + version: "0.1" + name: "{{ $deviceId }}-template" + global_timeout: 1800 + tasks: + - name: "os-installation-{{ $deviceId }}" + worker: "{{ $dev.mac }}" + volumes: + - /dev:/dev + - /dev/console:/dev/console + - /lib/firmware:/lib/firmware:ro + + actions: + - name: "stream-ubuntu-image-{{ $deviceId }}" + #image: quay.io/tinkerbell-actions/image2disk:v1.0.0 + image: {{ $.Values.actions.repository }}/image2disk:{{ $.Values.actions.version }} + timeout: 600 + environment: + DEST_DISK: "{{ $rootDiskDevice }}" + IMG_URL: "{{ $.Values.tinkerbell.imagesURL }}/{{ $imageObj.image }}" + COMPRESSED: true + + {{- if $imageObj.doGrowPart }} + - name: "grow-partition-{{ $deviceId }}" + image: {{ $.Values.actions.repository }}/cexec:{{ $.Values.actions.version }} + timeout: 90 + environment: + BLOCK_DEVICE: {{ $rootDiskRootfsPartitionDevice }} + FS_TYPE: ext4 + CHROOT: y + DEFAULT_INTERPRETER: "/bin/sh -c" + CMD_LINE: "growpart {{ $rootDiskDevice }} {{$rootDiskRootfsPartitionNumber}} && resize2fs {{$rootDiskRootfsPartitionDevice}}" + {{- end }} + + - name: "fix-resolv-{{ $deviceId }}" + image: {{ $.Values.actions.repository }}/cexec:{{ $.Values.actions.version }} + timeout: 90 + environment: + BLOCK_DEVICE: {{$rootDiskRootfsPartitionDevice}} + FS_TYPE: ext4 + CHROOT: y + DEFAULT_INTERPRETER: "/bin/sh -c" + CMD_LINE: "echo 'list /etc/resolv.conf: '; ls -la /etc/resolv.conf; echo 'cat /etc/resolv.conf'; cat /etc/resolv.conf; echo 'moving...'; mv -v /etc/resolv.conf /etc/resolv.conf.orig.tink; echo 'nameserver {{ index $mergedDevice.ipv4.dns 0 }} ' > /etc/resolv.conf; echo 'new resolf.conf:' ; cat /etc/resolv.conf" + + + {{- if $imageObj.doUserAndSshSetup }} + - name: "install-openssl-{{ $deviceId }}" + image: {{ $.Values.actions.repository }}/cexec:{{ $.Values.actions.version }} + timeout: 90 + environment: + BLOCK_DEVICE: {{$rootDiskRootfsPartitionDevice}} + FS_TYPE: ext4 + CHROOT: y + DEFAULT_INTERPRETER: "/bin/sh -c" + CMD_LINE: "apt -y update && apt -y install openssl" + - name: "create-user-{{ $deviceId }}" + image: {{ $.Values.actions.repository }}/cexec:{{ $.Values.actions.version }} + timeout: 90 + environment: + BLOCK_DEVICE: {{$rootDiskRootfsPartitionDevice}} + FS_TYPE: ext4 + CHROOT: y + DEFAULT_INTERPRETER: "/bin/sh -c" + CMD_LINE: "useradd -p $(openssl passwd -1 tink) -s /bin/bash -d /home/tink/ -m -G sudo tink" + - name: "enable-ssh-{{ $deviceId }}" + image: {{ $.Values.actions.repository }}/cexec:{{ $.Values.actions.version }} + timeout: 90 + environment: + BLOCK_DEVICE: {{$rootDiskRootfsPartitionDevice}} + FS_TYPE: ext4 + CHROOT: y + DEFAULT_INTERPRETER: "/bin/sh -c" + CMD_LINE: "ssh-keygen -A; systemctl enable ssh.service; echo 'PasswordAuthentication yes' > /etc/ssh/sshd_config.d/60-cloudimg-settings.conf" + - name: "disable-apparmor-{{ $deviceId }}" + image: {{ $.Values.actions.repository }}/cexec:{{ $.Values.actions.version }} + timeout: 90 + environment: + BLOCK_DEVICE: {{$rootDiskRootfsPartitionDevice}} + FS_TYPE: ext4 + CHROOT: y + DEFAULT_INTERPRETER: "/bin/sh -c" + CMD_LINE: "systemctl disable apparmor; systemctl disable snapd" + - name: "write-netplan-{{ $deviceId }}" + image: {{ $.Values.actions.repository }}/writefile:{{ $.Values.actions.version }} + timeout: 90 + environment: + DEST_DISK: {{$rootDiskRootfsPartitionDevice}} + FS_TYPE: ext4 + DEST_PATH: /etc/netplan/config.yaml + CONTENTS: | + network: + version: 2 + renderer: networkd + ethernets: + id0: + match: + name: en* + dhcp4: true + UID: 0 + GID: 0 + MODE: 0644 + DIRMODE: 0755 + {{- end }} + + - name: "revert-fix-resolv-{{ $deviceId }}" + image: {{ $.Values.actions.repository }}/cexec:{{ $.Values.actions.version }} + timeout: 90 + environment: + BLOCK_DEVICE: {{$rootDiskRootfsPartitionDevice}} + FS_TYPE: ext4 + CHROOT: y + DEFAULT_INTERPRETER: "/bin/sh -c" + CMD_LINE: "rm -v /etc/resolv.conf; mv -v /etc/resolv.conf.orig.tink /etc/resolv.conf" + + {{- if eq $hookObj.bootMode "kexec" }} + - name: "kexec-{{ $deviceId }}" + image: ghcr.io/jacobweinstock/waitdaemon:latest + timeout: 90 + pid: host + environment: + BLOCK_DEVICE: {{$rootDiskRootfsPartitionDevice}} + FS_TYPE: ext4 + IMAGE: {{ $.Values.actions.repository }}/kexec:{{ $.Values.actions.version }} + WAIT_SECONDS: 1 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + {{- end }} + + {{- if eq $hookObj.bootMode "reboot" }} + - name: "reboot-{{ $deviceId }}" + image: ghcr.io/jacobweinstock/waitdaemon:latest + timeout: 90 + pid: host + command: ["reboot"] + environment: + IMAGE: alpine + WAIT_SECONDS: 1 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + {{- end }} +--- +apiVersion: "tinkerbell.org/v1alpha1" +kind: Workflow +metadata: + name: "{{ $deviceId }}-workflow" + labels: + "app.kubernetes.io/instance": "{{ $.Release.Name }}" + "app.kubernetes.io/part-of": "tinkerbell-showcase" +spec: + templateRef: "{{ $deviceId }}-template" + hardwareRef: "{{ $deviceId }}-hardware" + hardwareMap: + device_1: "{{ $dev.mac }}" +{{- end }} +{{- end }} diff --git a/tinkerbell/showcase/templates/hooks.yaml b/tinkerbell/showcase/templates/hooks.yaml new file mode 100644 index 00000000..d462ef2b --- /dev/null +++ b/tinkerbell/showcase/templates/hooks.yaml @@ -0,0 +1,94 @@ +{{- range $hookId, $hook := .Values.provision.hook }} +{{- $common := $.Values.provision.common -}} +{{- $hook = merge $hook $common }} +--- +# --> HookId: {{$hookId}} + # downloadId: {{ $hook.hookDownloadId }} + # downloadFile: {{ $hook.downloadFile }} + # hookDownloadBaseUrl: {{ $hook.hookDownloadBaseUrl }} +{{- $hash := trunc 8 (sha1sum (printf "%s-%s-%s" $hook.hookDownloadId $hook.hookDownloadBaseUrl $hook.downloadFile)) }} +# Loop over $.Values.hardware.devices (a dictionary) and for each value, check if it's hookRef matches this $hookId - if so, enable this hook. +{{- $enabledByDeviceUsage := false }} +{{- range $device := $.Values.hardware.devices }} +# Testing {{$device.hookRef}} against {{$hookId}}... +{{- if and $device.enabled (eq $device.hookRef $hookId) -}} + # YES! +{{- $enabledByDeviceUsage = true -}} +{{- else -}} + # NO! +{{- end -}} +{{- end }} + +{{- if or $enabledByDeviceUsage $hook.enabled }} +# Enabled hook: {{$hookId}} - force enabled? {{$hook.enabled}} -- enabled by device usage? {{$enabledByDeviceUsage}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: "download-hook-{{$hookId}}-{{$hash}}" + namespace: "{{ $.Release.Namespace }}" + labels: + "app.kubernetes.io/instance": "{{ $.Release.Name }}" + "app.kubernetes.io/part-of": "tinkerbell-showcase" +data: + entrypoint.sh: | + #!/usr/bin/env bash + # This script is designed to download the Hook artifacts. + set -euxo pipefail + if [[ -f "/output/download_hook_{{$hookId}}_{{$hash}}.done" ]]; then + echo "Hook already downloaded ({{$hookId}} - hash: {{$hash}}), skipping." + exit 0 + fi + mkdir -p "/output/download_hook_{{$hookId}}" + cd "/output/download_hook_{{$hookId}}" + declare down_url="{{ $hook.hookDownloadBaseURL }}{{$hook.downloadFile}}" + declare down_file="/output/download_hook_{{$hookId}}/{{$hook.downloadFile}}" + wget -O "${down_file}.tmp" "${down_url}" + mv -v "${down_file}.tmp" "${down_file}" + mkdir -p "/output/download_hook_{{$hookId}}/extract" + cd "/output/download_hook_{{$hookId}}/extract" + tar -xvzf "${down_file}" + mv -v "/output/download_hook_{{$hookId}}/extract/"* /output/ + rm -rf "/output/download_hook_{{$hookId}}" + ls -lah "/output/{{$hook.kernel}}" + ls -lah "/output/{{$hook.initrd}}" + touch "/output/download_hook_{{$hookId}}_{{$hash}}.done" + +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: "download-hook-{{$hookId}}-{{$hash}}" + namespace: {{ $.Release.Namespace }} + labels: + "app.kubernetes.io/instance": "{{ $.Release.Name }}" + "app.kubernetes.io/part-of": "tinkerbell-showcase" +spec: + backoffLimit: 50 + template: + metadata: + labels: + app: download-hook + spec: + containers: + - name: download-hook-{{$hookId}} + image: bash:5 + command: [ "/script/entrypoint.sh" ] + volumeMounts: + - mountPath: /output + name: hook-artifacts + - mountPath: /script + name: configmap-volume + restartPolicy: OnFailure + volumes: + - name: hook-artifacts + hostPath: + path: {{ $.Values.tinkerbell.hostDirectory | quote }} + type: DirectoryOrCreate + - name: configmap-volume + configMap: + defaultMode: 0700 + name: "download-hook-{{$hookId}}-{{$hash}}" +{{- else }} +# Disabled Hook: {{$hookId}} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/tinkerbell/showcase/templates/images.yaml b/tinkerbell/showcase/templates/images.yaml new file mode 100644 index 00000000..4e8a34f7 --- /dev/null +++ b/tinkerbell/showcase/templates/images.yaml @@ -0,0 +1,131 @@ +{{- range $imageId, $image := .Values.provision.images }} +--- +# --> ImageId: {{ $imageId }} + # downloadUrl: "{{ $image.downloadURL }}" + # image: "{{ $image.image }}" + # conversion: "{{ $image.conversion }}" +{{- $hash := trunc 8 (sha1sum (printf "%s-%s-%s" $image.downloadURL $image.image $image.conversion )) }} +# Loop over $.Values.hardware.devices (a dictionary) and for each value, check if it's imageRef matches this $imageId - if so, enable this image. +{{- $enabledByDeviceUsage := false }} +{{- range $device := $.Values.hardware.devices }} +# Testing {{$device.imageRef}} against {{$imageId}}... +{{- if and $device.enabled (eq $device.imageRef $imageId) -}} +# YES! +{{- $enabledByDeviceUsage = true -}} +{{- else -}} +# NO! +{{- end -}} +{{- end }} +{{- if or $enabledByDeviceUsage $image.enabled }} +# ImageId: {{ $imageId }} - force enabled? {{ $image.enabled }} -- enabled by device usage? {{ $enabledByDeviceUsage }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: "download-image-{{$imageId}}-{{$hash}}" + labels: + "app.kubernetes.io/instance": "{{ $.Release.Name }}" + "app.kubernetes.io/part-of": "tinkerbell-showcase" +data: + entrypoint.sh: |- + #!/usr/bin/env bash + set -euxo pipefail + cat > /etc/apk/repositories << EOF; $(echo) + https://dl-cdn.alpinelinux.org/alpine/v$(cut -d'.' -f1,2 /etc/alpine-release)/main/ + https://dl-cdn.alpinelinux.org/alpine/v$(cut -d'.' -f1,2 /etc/alpine-release)/community/ + https://dl-cdn.alpinelinux.org/alpine/edge/community/ + EOF + {{- if eq "qemu-to-raw-gzip" $image.conversion }} + # This script is designed to download a cloud image file (.img) and then convert it to a .raw.gz file. + # This is purpose built so non-raw cloud image files can be used with the "image2disk" action. + # See https://artifacthub.io/packages/tbaction/tinkerbell-community/image2disk. + image_url=$1 + file=$2/${image_url##*/} + file=${file%.*}.raw.gz + if [[ ! -f "$file" ]]; then + echo "Image file $file does not exist. Downloading...." + apk add --update pigz qemu-img + wget "$image_url" -O image.img + qemu-img convert -O raw image.img image.raw + pigz < image.raw > "$file" + rm -f image.img image.raw + else + echo "File $file already exists, skipping download and conversion." + fi + {{- end }} + + {{- if eq "none" $image.conversion }} + # No conversion, just a simple download; image must be in raw format, possibly compressed. + image_url="{{ $image.downloadURL }}" + file="/output/{{ $image.image }}" + if [[ ! -f "$file" ]]; then + echo "Image file $file does not exist. Downloading...." + wget "$image_url" -O "${file}.tmp" + mv -v "${file}.tmp" "$file" + echo "Done downloading image." + else + echo "Image file $file already exists. Skipping download." + fi + {{- end }} + + {{- if eq "xz-to-gz" $image.conversion }} + # Download, then produce a .gz file from a .xz file, using pigz and pixz, and using a pipe. + image_url="{{ $image.downloadURL }}" + file="/output/{{ $image.image }}" + if [[ ! -f "$file" ]]; then + echo "Image file $file does not exist. Downloading...." + apk add --update pigz pixz + wget "$image_url" -O "${file}.tmp.xz" + echo "Done downloading image. Converting from xz to gz..." + pixz -d < "${file}.tmp.xz" | pigz > "${file}.tmp" + mv -v "${file}.tmp" "$file" + echo "Done converting image." + rm -f "${file}.tmp.xz" + else + echo "Image file $file already exists. Skipping download." + fi + + {{- end }} + +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: "download-image-{{$imageId}}-{{$hash}}" + labels: + "app.kubernetes.io/instance": "{{ $.Release.Name }}" + "app.kubernetes.io/part-of": "tinkerbell-showcase" +spec: + template: + metadata: + labels: + "app.kubernetes.io/instance": "{{ $.Release.Name }}" + "app.kubernetes.io/part-of": "tinkerbell-showcase" + spec: + containers: + - name: download-{{$imageId}} + image: bash:5 + command: [ "/script/entrypoint.sh" ] + args: + [ + "{{$image.downloadURL}}", + "/output", + ] + volumeMounts: + - mountPath: /output + name: image-artifacts + - mountPath: /script + name: configmap-volume + restartPolicy: OnFailure + volumes: + - name: image-artifacts + hostPath: + path: {{ $.Values.tinkerbell.hostDirectory }} + type: DirectoryOrCreate + - name: configmap-volume + configMap: + defaultMode: 0700 + name: "download-image-{{$imageId}}-{{$hash}}" +{{- else }} +# ImageId: {{ $imageId }} DISABLED - force enabled? {{ $image.enabled }} -- enabled by device usage? {{ $enabledByDeviceUsage }} +{{- end }} +{{- end }} diff --git a/tinkerbell/showcase/values.yaml b/tinkerbell/showcase/values.yaml new file mode 100644 index 00000000..6d808a3b --- /dev/null +++ b/tinkerbell/showcase/values.yaml @@ -0,0 +1,311 @@ +# Basic information on how Tinkerbell is setup. +tinkerbell: + hostDirectory: "/opt/hook" # hostPath where Hook & images are downloaded & served from + hookURL: "http://192.168.66.71:8080" # # URL where Hook is served from + imagesURL: "http://192.168.66.71:8080" # # URL where images are served from + syslogHost: "192.168.66.71" # Syslog host, usually smee + grpcAuthority: "192.168.66.71:42113" # Tink's gRPC authority + +actions: + repository: "quay.io/tinkerbellrpardini/actions" # rpardini's fork with some fixes for arm64 + version: "latest" + +# Generates download jobs and configs for easy show-off'ing of Tinkerbell on diverse hardware +provision: + # @TODO: many simultaneous download jobs can drive an SD-card/low-bandwidth cluster to its knees. Consider a single download job for all + + common: + # Used together with downloadFile field in each hook; + # Check https://github.com/rpardini/tinkerbell-hook/releases/ for updates + hookDownloadBaseURL: "https://github.com/rpardini/tinkerbell-hook/releases/download/20240331-0055/" + hookDownloadId: "20240331-0055" # If the ID changes, all hooks will be re-downloaded. Can't contain slashes or spaces + + # Define one key per hook built to be downloaded and made available. Those _must_ match the hardware definitions. + # Important: each definition will be auto-enabled when they're referenced in an enabled device. You can force them enabled: true + # to download/prepare it even if no device is using it. + hook: + "default-hook-amd64": + enabled: false + arch: "x86_64" + downloadFile: "hook-x86_64.tar.gz" + kernel: "vmlinuz-x86_64" + initrd: "initramfs-x86_64" + kernelCommandLine: "" + bootMode: kexec + + "default-hook-aarch64": + enabled: false + arch: "aarch64" + downloadFile: "hook-aarch64.tar.gz" + kernel: "vmlinuz-aarch64" + initrd: "initramfs-aarch64" + kernelCommandLine: "" + bootMode: kexec # kexec is no-go here, kernel problem? (persists after enabling CONFIG_KEXEC & trying to fix TB action) + + "armbian-uefi-x86-edge": + enabled: false + arch: "x86_64" + downloadFile: "hook-armbian-uefi-x86-edge.tar.gz" + kernel: "vmlinuz-armbian-uefi-x86-edge" + initrd: "initramfs-armbian-uefi-x86-edge" + kernelCommandLine: "intel_iommu=on iommu=pt" + bootMode: kexec + + + "armbian-uefi-arm64-edge": + enabled: false + arch: "aarch64" + downloadFile: "hook-armbian-uefi-arm64-edge.tar.gz" + kernel: "vmlinuz-armbian-uefi-arm64-edge" + initrd: "initramfs-armbian-uefi-arm64-edge" + kernelCommandLine: "" + bootMode: kexec # still no go with kexec -- this is a TB action problem for sure, not kernel problem + + "armbian-meson64-edge": + enabled: false + arch: "aarch64" + downloadFile: "hook-armbian-meson64-edge.tar.gz" + kernel: "vmlinuz-armbian-meson64-edge" + initrd: "initramfs-armbian-meson64-edge" + kernelCommandLine: "acpi=off efi=noruntime" + bootMode: reboot + + "armbian-rockchip64-edge": + enabled: false + arch: "aarch64" + downloadFile: "hook-armbian-rockchip64-edge.tar.gz" + kernel: "vmlinuz-armbian-rockchip64-edge" + initrd: "initramfs-armbian-rockchip64-edge" + kernelCommandLine: "" + bootMode: reboot + + "armbian-bcm2711-current": + enabled: false + arch: "aarch64" + downloadFile: "hook-armbian-bcm2711-current.tar.gz" + kernel: "vmlinuz-armbian-bcm2711-current" + initrd: "initramfs-armbian-bcm2711-current" + kernelCommandLine: "" + bootMode: reboot + + "armbian-rk35xx-vendor": + enabled: false + arch: "aarch64" + downloadFile: "hook-armbian-rk35xx-vendor.tar.gz" + kernel: "vmlinuz-armbian-rk35xx-vendor" + initrd: "initramfs-armbian-rk35xx-vendor" + kernelCommandLine: "acpi=off efi=noruntime splash=verbose" + bootMode: reboot + + "armbian-rk35xx-legacy": + enabled: false + arch: "aarch64" + downloadFile: "hook-armbian-rk35xx-legacy.tar.gz" + kernel: "vmlinuz-armbian-rk35xx-legacy" + initrd: "initramfs-armbian-rk35xx-legacy" + kernelCommandLine: "" # acpi=off efi=noruntime splash=verbose + bootMode: reboot + + # Define deployment images; we've a few examples using Ubuntu and Armbian + # Important: each definition will be auto-enabled when they're referenced in an enabled device. You can force them enabled: true + # to download/prepare it even if no device is using it. + images: + "ubuntu-jammy-cloud-arm64": + enabled: false + arch: "aarch64" + downloadURL: "https://cloud-images.ubuntu.com/daily/server/jammy/current/jammy-server-cloudimg-arm64.img" + image: "jammy-server-cloudimg-arm64.raw.gz" + conversion: "qemu-to-raw-gzip" + doGrowPart: true + doUserAndSshSetup: true + rootfsPartitionNumber: "1" # @schema type:[integer] # @TODO fix so this mustn't be a string + + "ubuntu-jammy-cloud-amd64": + enabled: false + arch: "x86_64" + downloadURL: "https://cloud-images.ubuntu.com/daily/server/jammy/current/jammy-server-cloudimg-amd64.img" + conversion: "qemu-to-raw-gzip" + image: "jammy-server-cloudimg-amd64.raw.gz" + doGrowPart: true + doUserAndSshSetup: true + rootfsPartitionNumber: "1" + + "armbian-rpardini-rockpro64-edge-uboot": + enabled: false + arch: "aarch64" + downloadURL: "https://github.com/armsurvivors/armbian-release/releases/download/24.03.30-armsurvivors-64/Armbian-unofficial_24.03.30-armsurvivors-64_Rockpro64_bookworm_edge_6.8.2.img.xz" + image: "Armbian-unofficial_24.03.30-armsurvivors-64_Rockpro64_bookworm_edge_6.8.2.img.gz" + conversion: "xz-to-gz" + doGrowPart: false # Armbian does it itself when booted + doUserAndSshSetup: false # Armbian does it itself when booted + rootfsPartitionNumber: "1" + + "armbian-rpardini-orangepi3b-edge-uboot": + enabled: false + arch: "aarch64" + downloadURL: "https://github.com/armsurvivors/armbian-release/releases/download/24.03.30-armsurvivors-64/Armbian-unofficial_24.03.30-armsurvivors-64_Orangepi3b_bookworm_edge_6.8.2.img.xz" + image: "Armbian-unofficial_24.03.30-armsurvivors-64_Orangepi3b_bookworm_edge_6.8.2.img.gz" + conversion: "xz-to-gz" + doGrowPart: false # Armbian does it itself when booted + doUserAndSshSetup: false # Armbian does it itself when booted + rootfsPartitionNumber: "1" + + "armbian-rpardini-rpi4b-current-not-efi": + enabled: false + arch: "aarch64" + downloadURL: "https://github.com/armsurvivors/armbian-release/releases/download/24.03.30-armsurvivors-64/Armbian-unofficial_24.03.30-armsurvivors-64_Rpi4b_bookworm_current_6.6.23.img.xz" + image: "Armbian-unofficial_24.03.30-armsurvivors-64_Rpi4b_bookworm_current_6.6.23.img.gz" + conversion: "xz-to-gz" + doGrowPart: false # Armbian does it itself when booted + doUserAndSshSetup: false # Armbian does it itself when booted + rootfsPartitionNumber: "2" # rpi image has a fat32 boot partition (config.txt and such), and partition 2 is the real ext4 rootfs + + "armbian-rpardini-r58x-vendor-uboot": # @TODO this is the wrong thing to use. we need a generic EFI-enabled image without forcing DTB. Same for all the others! + enabled: false + arch: "aarch64" + downloadURL: "https://github.com/armsurvivors/armbian-release/releases/download/24.03.30-armsurvivors-64/Armbian-unofficial_24.03.30-armsurvivors-64_Mekotronics-r58x-pro_bookworm_vendor_6.1.43.img.xz" + image: "Armbian-unofficial_24.03.30-armsurvivors-64_Mekotronics-r58x-pro_bookworm_vendor_6.1.43.img.gz" + conversion: "xz-to-gz" + doGrowPart: false # Armbian does it itself when booted + doUserAndSshSetup: false # Armbian does it itself when booted + rootfsPartitionNumber: "1" # first partition, ext4 rootfs + + "armbian-bookworm-meson64-current-cloud": + enabled: false + arch: "aarch64" + downloadURL: "https://..../" + image: "armbian-bookworm-meson64.img" + rootfsPartitionNumber: "1" + +# Generates CRs triplets with Hardware, Workflow, and Template, using by-id references to the hook and images above. + +hardware: + common: + uefi: true + ipv4: + gateway: "192.168.99.1" # Must be set, otherwise no internet access; can be overriden per-device + netmask: "255.255.255.0" + dns: [ "192.168.66.1" ] + + devices: # One entry for each machine + + # Examples for use in Hook development environment. + # There is a `bash build.sh build-run-qemu` command in Hook, that paired with + # `export MAC=11:22:33:44:55:66 TINK_SERVER=x.y.z.y` can be used with the below "machines". + # The MAC address is not really used by the qemu VM, but is passed via kernel cmdline. + "run-qemu-x86": + enabled: false + arch: x86_64 + mac: "11:22:33:44:55:66" + rootDisk: "/dev/sda" + ipv4: + address: "192.168.66.245" + hookRef: "armbian-uefi-x86-edge" # "default-hook-amd64" # defined above + imageRef: "ubuntu-jammy-cloud-amd64" # defined above + extraKernelCommandLine: "console=ttyS0" + + "run-qemu-arm64": + enabled: false + arch: aarch64 + mac: "11:22:33:44:55:77" + rootDisk: "/dev/vda" # wonder why linuxkit only uses virtio for arm64? intel is sda... + ipv4: + address: "192.168.66.246" + hookRef: "armbian-uefi-arm64-edge" # "default-hook-aarch64" # defined above + imageRef: "ubuntu-jammy-cloud-arm64" # defined above + extraKernelCommandLine: "console=ttyAMA0" + + + ### + ### + ### + # Examples for virtual machines, ran outside of Hook's build system. + # Most virtualization software has support for PXE booting. + # You will need to have (bridging/etc) setup to get a proper IP address from a real Smee, etc. + "vm03": + enabled: false + arch: aarch64 + mac: "52:54:00:01:03:03" + rootDisk: "/dev/vdb" + ipv4: + address: "192.168.99.41" + hookRef: "armbian-uefi-arm64-edge" # "default-hook-aarch64" # defined above + imageRef: "ubuntu-jammy-cloud-arm64" # defined above + extraKernelCommandLine: "console=ttyAMA0" + + "vm09": + enabled: false + arch: x86_64 + mac: "52:54:00:01:03:09" + rootDisk: "/dev/vdb" + ipv4: + address: "192.168.99.42" + hookRef: "armbian-uefi-x86-edge" # "default-hook-amd64" # defined above + imageRef: "ubuntu-jammy-cloud-amd64" # defined above + extraKernelCommandLine: "console=ttyS0" + + + ### + ### + ### + # Examples for actual bare-metal hardware, mostly arm64 SBCs. + + + # Khadas VIM3L and other meson64-based boards (u-boot) + "vim3l": + enabled: false + arch: aarch64 + mac: "c8:63:14:71:2a:6d" + rootDisk: "/dev/mmcblk0" + ipv4: + address: "192.168.99.51" + hookRef: "armbian-meson64-edge" + imageRef: "ubuntu-jammy-cloud-arm64" + extraKernelCommandLine: "console=ttyAML0,115200" + + # RockPro64 and other rockchip64-based boards (u-boot) + "rockpro64": + enabled: false + arch: aarch64 + mac: "2a:36:cb:a2:ae:b8" + #rootDisk: "/dev/mmcblk2" # mmcblk2 is the eMMC, mmcblk1 is the SD card + rootDisk: "/dev/disk/by-id/mmc-AJTD4R_0x0b05e84d" # much safer to use this style + ipv4: + address: "192.168.99.52" + hookRef: "armbian-rockchip64-edge" # defined above + imageRef: "armbian-rpardini-rockpro64-edge-uboot" # defined above + extraKernelCommandLine: "console=ttyS2,1500000" + + "orangepi3b": + enabled: false + arch: aarch64 + mac: "b6:a7:96:d5:f1:84" + rootDisk: "/dev/disk/by-id/mmc-A3A551_0x2f8c5bd0" # /dev/mmcblk0 is the eMMC + ipv4: + address: "192.168.99.53" + hookRef: "armbian-rockchip64-edge" + imageRef: "armbian-rpardini-orangepi3b-edge-uboot" # defined above + extraKernelCommandLine: "console=ttyS2,1500000" + + # Raspberry Pi 4B (bcm2711), using edk2 UEFI firmware on an SD card, and an external NVMe-USB disk + "rpi4b": + enabled: false + arch: aarch64 + mac: "dc:a6:32:ec:8b:49" + rootDisk: "/dev/disk/by-id/wwn-0x3001237923792379" + ipv4: + address: "192.168.99.54" + hookRef: "armbian-bcm2711-current" # works, as long as UEFI is set with ACPI+DTB and no 3gb limit. Use edk2 from https://github.com/pftf/RPi4 + imageRef: "armbian-rpardini-rpi4b-current-not-efi" # defined above + extraKernelCommandLine: "console=tty1 console=ttyAMA0,115200 loglevel=7" # Last console defined "wins", thus use tty1 last if no serial console + + "mekotronics-r58x-pro-via-usb": + enabled: false + arch: aarch64 + mac: "3c:18:a0:15:9a:9f" # 3C18A0159A9F + ipv4: + address: "192.168.99.55" + hookRef: "armbian-rk35xx-legacy" # "armbian-rk35xx-vendor" + imageRef: "armbian-rpardini-r58x-vendor-uboot" + extraKernelCommandLine: "console=ttyFIQ0:1500000 " # console=tty1 # Last console defined "wins", thus use tty1 last if no serial console + rootDisk: "/dev/nvme0NOENOENOENEyet"