From ac5b0ec690c3a5ad690a2d821b4c28cbf6e2792a 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 Squashed commits: - showcase: fix insecure-registries empty bug; add odroidm1; bump hooks - showcase: not all images are ubuntu - showcase: introduce insecureRegistries/tinkWorkerImage/tinkWorkerImagePrefix - tinkWorkerImage defaults to quay.io/tinkerbellrpardini/tink-worker:v0.11.0-alpha-rpardini3 - showcase: introduce grpcTLS/grpcInsecureTLS - showcase: generateJobs default to true, allow disabling Jobs - showcase: introduce image.doRestoreGRUBNormalcy for GRUB+EFI entry juggling and enable it for fatso images - showcase: introduce image.doAddEFIBootEntry for efibootmgr juggling and enable it for fatso images - showcase: WiP: nanopct6 (ipxe won't load, even with DTB) - showcase: use rpardini's fork of waitdaemon at quay.io - showcase: bump Hook to 20240604-0609 (further mdev by-id fixes) - showcase: bump Hook to 20240603-0515 (mdev by-id fixes); bump fatso images to v1008 - showcase: bump Hooks; bump images; add rpi4b armbian cloud image; add odroidhc4 example - showcase: fix default userdata script - showcase: bump all armsurvivors Armbian images to 24.05.20-armsurvivors-184; add t95z-cloud-edge image; use qcow2-to-img conversion where relevant - showcase: add support for `xz-qcow2-to-img-gz` image conversion - showcase: bump Hook to 20240520-0941 (finally working armbian's again!) - showcase: bump Hook to 20240520-0729 - showcase: bump Hook to 20240519-1935 (new scheme) - showcase: switch to 20240519-1405 Hook, with new (old?) naming convention - showcase: add demo t95z (meson64) - showcase: bump fatso - showcase: bump images - showcase: add ntp stuff - showcase: bump images, reorg - showcase: add image's 'doInjectHegelCloudInit'; set device's `userData` on the Hardware - add configuration hegelURL - some examples - showcase: bump Hook version; add `latest-lts-xxx` Hooks; add more examples - showcase: bump fatso images - showcase: allow to override hook's defaults via `hookOverride: {}` on the device - showcase: bump hook versions; fix example - showcase: bump hook versions; add examples for peg/pegk and skipDownload - showcase: hook: allow skipping hook download via `skipDownload` - showcase: devices: show `imgstat` before `boot` - showcase: bump fatso images version to 1001; add local variant examples - showcase: HACK: change default Hook amd64 bootMode to reboot (this needs override per-device) - showcase: example 'fatso' baremetal images with download-only and local example - showcase: bump orangepi3b image - showcase: introduce 'download-only' and 'local' non-conversions - showcase: reduce WAIT_SECONDS to zero - showcase: introduce doFixResolvConf at image level - showcase: install pkgs with DEBIAN_FRONTEND=noniteractive, no suggests, no recommends; 180s timeout - showcase: default netplan to device `e*` (not `en*`) so it matches `eth0` too - showcase: add `rock-5b-edge-trixie-uefi-dtb` image for Rock-5b - showcase: add armbian cloud-k8s images for uefi arm64 and amd64 - showcase: bump hook to rpardini's 20240404-2216; add `armbian-rk3588-edge` hook definition (but no board example yet) - showcase: VM examples - showcase: add R58X 3588 example & UEFI cloud image - showcase: validate hookRef not unset - showcase: 0.0.2: disable kexec on arm64, it doesn't really work (yet) - showcase: add README --- tinkerbell/showcase/Chart.yaml | 6 + tinkerbell/showcase/README.md | 31 ++ tinkerbell/showcase/templates/devices.yaml | 344 ++++++++++++ tinkerbell/showcase/templates/hooks.yaml | 105 ++++ tinkerbell/showcase/templates/images.yaml | 184 +++++++ tinkerbell/showcase/values.yaml | 600 +++++++++++++++++++++ 6 files changed, 1270 insertions(+) create mode 100644 tinkerbell/showcase/Chart.yaml create mode 100644 tinkerbell/showcase/README.md 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..0aa4663f --- /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.2" + diff --git a/tinkerbell/showcase/README.md b/tinkerbell/showcase/README.md new file mode 100644 index 00000000..08239583 --- /dev/null +++ b/tinkerbell/showcase/README.md @@ -0,0 +1,31 @@ +#### 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 \ No newline at end of file diff --git a/tinkerbell/showcase/templates/devices.yaml b/tinkerbell/showcase/templates/devices.yaml new file mode 100644 index 00000000..7a392edd --- /dev/null +++ b/tinkerbell/showcase/templates/devices.yaml @@ -0,0 +1,344 @@ +{{- 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 }} + # Getting hook from hookRef: '{{$mergedDevice.hookRef}}' +{{- if not $mergedDevice.hookRef }}{{- fail (printf "Device '%s', hookRef '%s': %s" $deviceId $mergedDevice.hookRef "hookRef unset?") }}{{- end }} + +{{- $hookObj := index $.Values.provision.hook $mergedDevice.hookRef }} +{{- if not $hookObj }}{{- fail (printf "Device '%s', hookRef '%s': %s" $deviceId $mergedDevice.hookRef "hookRef not found") }}{{- end }} + # if the device has a hookOverride property, merge it on top of the hookObj; pre-merge: {{$hookObj.bootMode}} +{{ if $mergedDevice.hookOverride }} + # YES!!!!!!!!!! has hookOverride {{$deviceId}} +{{ $hookObj = merge $mergedDevice.hookOverride $hookObj }} +{{ else }} + # NO!!!!!!!!!! no hookOverride {{$deviceId}} +{{ end }} + # Reboot mode after merge: {{$hookObj.bootMode}} + +{{- $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" }} +{{- $espPartitionDevice := "unknown" }} +{{- $espPartitionNumber := "1" }} + +{{- if hasPrefix "/dev/disk/" $rootDiskDevice }} + # YES! {{$rootDiskDevice}} begins with /dev/disk +{{- $rootDiskRootfsPartitionDevice = printf "%s-part%s" $rootDiskDevice $rootDiskRootfsPartitionNumber }} +{{- $espPartitionDevice = printf "%s-part%s" $rootDiskDevice $espPartitionNumber }} +{{- 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 }} +{{- $espPartitionDevice = printf "%sp%s" $rootDiskDevice $espPartitionNumber }} +{{- else }} + # NO! {{$rootDiskDevice}} does not end with a digit - REGEX NOT MATCH +{{- $rootDiskRootfsPartitionDevice = printf "%s%s" $rootDiskDevice $rootDiskRootfsPartitionNumber }} +{{- $espPartitionDevice = printf "%s%s" $rootDiskDevice $espPartitionNumber }} +{{- end }} +{{- end }} + # Thus at the end of the day, $rootDiskRootfsPartitionDevice is {{$rootDiskRootfsPartitionDevice}} and ESP is {{$espPartitionDevice}} +--- +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 }}" + userData: | + {{- $mergedDevice.userData | default "#!/bin/bash\necho 'tinkerbell-showcase: userData not configured for device' >&2;" | nindent 4 }} + metadata: + facility: + facility_code: tinkerbell-showcase + plan_slug: "{{$deviceId}}" + instance: + hostname: "{{ $deviceId }}" + id: "{{ $dev.mac }}" + tags: + - "tinkerbell-showcase" + operating_system: + distro: "ubuntu" # @TODO + image_tag: "latest" + slug: "ubuntuslug" + os_slug: "ubuntu_24_04" + version: "24.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 }} + time_servers: + {{- range $mergedDevice.ipv4.time_servers }} + - {{ . | quote }} + {{- end }} + 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={{ $.Values.tinkerbell.tinkWorkerImagePrefix }}{{ $.Values.tinkerbell.tinkWorkerImage }} facility= syslog_host={{ $.Values.tinkerbell.syslogHost }} grpc_authority={{ $.Values.tinkerbell.grpcAuthority }} tinkerbell_tls={{ $.Values.tinkerbell.grpcTLS }} tinkerbell_insecure_tls={{ $.Values.tinkerbell.grpcInsecureTLS }} worker_id={{$dev.mac}} hw_addr={{$dev.mac}} {{ if $.Values.tinkerbell.insecureRegistries.enabled }}insecure_registries={{ $.Values.tinkerbell.insecureRegistries.registries }} {{end}} 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 }} + imgstat + 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-image-{{ $deviceId }}" + 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 }} + + {{- if ($imageObj.doFixResolvConf) }} + - 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" + {{- end }} + + + {{- if $imageObj.doUserAndSshSetup }} + - name: "install-packages-{{ $deviceId }}" + image: {{ $.Values.actions.repository }}/cexec:{{ $.Values.actions.version }} + timeout: 180 # 3 minutes + environment: + BLOCK_DEVICE: {{$rootDiskRootfsPartitionDevice}} + FS_TYPE: ext4 + CHROOT: y + DEFAULT_INTERPRETER: "/bin/sh -c" + CMD_LINE: "apt -y update && DEBIAN_FRONTEND=noninteractive apt -y install openssl neofetch --no-install-recommends --no-install-suggests" + - 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: e* + dhcp4: true + UID: 0 + GID: 0 + MODE: 0644 + DIRMODE: 0755 + {{- end }} + + {{- if ($imageObj.doInjectHegelCloudInit ) }} + # Based on https://tinkerbell.org/docs/integrations/cloudinit/#setup-cloud-init-to-use-hegel + - name: "inject-cloud-init-hegel-cfg-{{ $deviceId }}" + image: {{ $.Values.actions.repository }}/writefile:{{ $.Values.actions.version }} + timeout: 90 + environment: + DEST_PATH: /etc/cloud/cloud.cfg.d/10_tinkerbell.cfg + CONTENTS: | + datasource: + Ec2: + metadata_urls: ["{{ $.Values.tinkerbell.hegelURL }}"] + strict_id: false + manage_etc_hosts: localhost + warnings: + dsid_missing_source: off + DEST_DISK: {{$rootDiskRootfsPartitionDevice}} + FS_TYPE: ext4 + UID: 0 + GID: 0 + MODE: 0600 + DIRMODE: 0700 + + - name: "inject-cloud-init-hegel-ds-{{ $deviceId }}" + image: {{ $.Values.actions.repository }}/writefile:{{ $.Values.actions.version }} + timeout: 90 + environment: + DEST_PATH: /etc/cloud/ds-identify.cfg + CONTENTS: | + datasource: Ec2 + DEST_DISK: {{$rootDiskRootfsPartitionDevice}} + FS_TYPE: ext4 + UID: 0 + GID: 0 + MODE: 0600 + DIRMODE: 0700 + {{- end }} + + {{- if ($imageObj.doFixResolvConf ) }} + - 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" + {{- end }} + + {{- if ($imageObj.doAddEFIBootEntry ) }} + - name: "add-efi-boot-entry-{{ $deviceId }}" + image: {{ $.Values.actions.repository }}/cexec:{{ $.Values.actions.version }} + timeout: 90 + environment: + BLOCK_DEVICE: {{$rootDiskRootfsPartitionDevice}} + FS_TYPE: ext4 + CHROOT: y + DEFAULT_INTERPRETER: "/bin/bash -c" + CMD_LINE: "set -x; mount; lsblk; blkid; mount -o remount,rw /sys; mount -t efivarfs none /sys/firmware/efi/efivars; mount; efibootmgr --verbose; efibootmgr --create --disk '{{ $rootDiskDevice }}' --label PROViSiONED; efibootmgr --verbose; umount /sys/firmware/efi/efivars; sync" + {{- end }} + + {{- if ($imageObj.doRestoreGRUBNormalcy ) }} + - name: "restore-grub-normalcy-{{ $deviceId }}" + image: {{ $.Values.actions.repository }}/cexec:{{ $.Values.actions.version }} + timeout: 90 + environment: + BLOCK_DEVICE: {{$rootDiskRootfsPartitionDevice}} + FS_TYPE: ext4 + CHROOT: y + DEFAULT_INTERPRETER: "/bin/bash -c" + CMD_LINE: "set -x; mount; lsblk; blkid; mount -o remount,rw /sys; mount -t efivarfs none /sys/firmware/efi/efivars; mount '{{ $espPartitionDevice }}' /boot; mount; rm -rfv /boot/ubuntu /boot/loader /boot/EFI; mkdir -pv /boot/EFI; update-initramfs -k all -c; echo 'GRUB_CMDLINE_LINUX_DEFAULT=\"${GRUB_CMDLINE_LINUX_DEFAULT} {{$mergedDevice.extraKernelCommandLine}}\"' > /etc/default/grub.d/60-provisioned.cfg; cat /etc/default/grub.d/60-provisioned.cfg; mv -v /etc/os-release /etc/os-release.hold; echo 'NAME={{$mergedDevice.imageRef}}' > /etc/os-release; grub-install --efi-directory /boot '--bootloader-id={{$mergedDevice.imageRef}}'; update-grub; tree /boot; cat /boot/grub/grub.cfg; efibootmgr --verbose; umount /sys/firmware/efi/efivars; umount /boot; mv -v /etc/os-release.hold /etc/os-release; sync" + {{- end }} + + {{- if eq $hookObj.bootMode "kexec" }} + - name: "kexec-{{ $deviceId }}" + image: {{ $.Values.actions.repository }}/waitdaemon:{{ $.Values.actions.version }} + timeout: 90 + pid: host + environment: + BLOCK_DEVICE: {{$rootDiskRootfsPartitionDevice}} + FS_TYPE: ext4 + IMAGE: {{ $.Values.actions.repository }}/kexec:{{ $.Values.actions.version }} + WAIT_SECONDS: 0 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + {{- end }} + + {{- if eq $hookObj.bootMode "reboot" }} + - name: "reboot-{{ $deviceId }}" + image: {{ $.Values.actions.repository }}/waitdaemon:{{ $.Values.actions.version }} + timeout: 90 + pid: host + command: ["reboot"] + environment: + IMAGE: alpine + WAIT_SECONDS: 0 + 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..921efceb --- /dev/null +++ b/tinkerbell/showcase/templates/hooks.yaml @@ -0,0 +1,105 @@ +{{- if .Values.generateJobs }} +{{- 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 + + {{ + if $hook.skipDownload + }} + echo "Skipping download for hook {{$hookId}}." + ls -lah "/output/{{$hook.kernel}}" + ls -lah "/output/{{$hook.initrd}}" + touch "/output/download_hook_{{$hookId}}_{{$hash}}.done" + {{ else }} + 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" {{ end }} + +--- +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 }} +{{- 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..0e6b6bea --- /dev/null +++ b/tinkerbell/showcase/templates/images.yaml @@ -0,0 +1,184 @@ +{{- if .Values.generateJobs }} +{{- 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 }} + + {{- if eq "xz-qcow2-to-img-gz" $image.conversion }} + # Download, decompress xz, convert qcow2 to img, compress img to gz. + 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 qemu-img + wget "$image_url" -O "${file}.tmp.xz" + echo "Done downloading image. Decompressing xz..." + pixz -d < "${file}.tmp.xz" > "${file}.tmp.unxz" + rm -v "${file}.tmp.xz" + echo "Done decompressing xz, converting qcow2 to img..." + qemu-img convert -O raw "${file}.tmp.unxz" "${file}.tmp.raw" + rm -v "${file}.tmp.unxz" + echo "Done converting qcow2 to img, compressing img to gz..." + pigz < "${file}.tmp.raw" > "${file}" + rm -v "${file}.tmp.raw" + echo "Done converting image." + else + echo "Image file $file already exists. Skipping download." + fi + {{- end }} + + {{- if eq "download-only" $image.conversion }} + # Download only. Image is already in format necessary. + image_url="{{ $image.downloadURL }}" + file="/output/{{ $image.image }}" + if [[ ! -f "$file" ]]; then + echo "Download-only Image file $file does not exist. Downloading...." + wget "$image_url" -O "${file}.tmp" + echo "Done downloading image. Moving..." + mv -v "${file}.tmp" "$file" + echo "Done moving image." + rm -f "${file}.tmp.xz" + else + echo "Download-only image file $file already exists. Skipping download." + fi + {{- end }} + + {{- if eq "local" $image.conversion }} + # Not even download anything. Image should be in there already somehow. + image_url="{{ $image.downloadURL }}" + file="/output/{{ $image.image }}" + if [[ ! -f "$file" ]]; then + echo "Image file $file does not exist. Please deploy image file manually, or use a different conversion than none. Sleeping 10s and exiting with error 66." + sleep 10 + exit 66 + else + echo "No-conversion image file $file already exists. Ready to use." + 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 }} +{{- end }} diff --git a/tinkerbell/showcase/values.yaml b/tinkerbell/showcase/values.yaml new file mode 100644 index 00000000..18f6613b --- /dev/null +++ b/tinkerbell/showcase/values.yaml @@ -0,0 +1,600 @@ +# 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 + hegelURL: "http://192.168.66.71:32061" # Hegel URL + syslogHost: "192.168.66.71" # Syslog host, usually smee + grpcAuthority: "192.168.66.71:42113" # Tink's gRPC authority + grpcTLS: "false" # true/false (string): Whether to use TLS for gRPC + grpcInsecureTLS: "false" # true/false (string): Whether to use insecure TLS for gRPC (if using grpcTLS) + insecureRegistries: # hit insecure registries over HTTP + enabled: false # enable and set comma-sep list below + registries: "" # (string) comma-separated list of insecure registries (will be accessed over HTTP if listed here) + tinkWorkerImage: "quay.io/tinkerbellrpardini/tink-worker:v0.11.0-alpha-rpardini3" # (string) Docker image to use for tink-worker, complete with tag + tinkWorkerImagePrefix: "" # prefix to add to tinkWorkerImage, for easy pass-through mirroring it + +actions: + repository: "quay.io/tinkerbellrpardini/actions" # rpardini's fork with some fixes for arm64 + version: "latest" + +generateJobs: true # Whether to generate jobs or not + +# 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/20240711-2229/" + hookDownloadId: "20240711-2229" # 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: reboot # @TODO: this needs to be set per-device & per-image as well # kexec + + "latest-lts-amd64": + enabled: false + arch: "x86_64" + downloadFile: "hook_latest-lts-x86_64.tar.gz" + kernel: "vmlinuz-latest-lts-x86_64" + initrd: "initramfs-latest-lts-x86_64" + kernelCommandLine: "" + bootMode: reboot + + "peg-default-amd64": + enabled: false + arch: "x86_64" + downloadFile: "hook_peg-default-amd64.tar.gz" + kernel: "vmlinuz-peg-default-amd64" + initrd: "initramfs-peg-default-amd64" + kernelCommandLine: "" + bootMode: reboot + + "dev-default-hook-amd64": + enabled: false + skipDownload: true # Don't download it, it's already there; just enable it + arch: "x86_64" + downloadFile: "hook_x86_64.tar.gz" + kernel: "vmlinuz-x86_64" + initrd: "initramfs-x86_64" + kernelCommandLine: "" + bootMode: reboot + + + "default-hook-aarch64": + enabled: false + arch: "aarch64" + downloadFile: "hook_aarch64.tar.gz" + kernel: "vmlinuz-aarch64" + initrd: "initramfs-aarch64" + kernelCommandLine: "" + bootMode: reboot # kexec is no-go here, kernel problem? (persists after enabling CONFIG_KEXEC & trying to fix TB action) + + "latest-lts-aarch64": + enabled: false + arch: "aarch64" + downloadFile: "hook_latest-lts-aarch64.tar.gz" + kernel: "vmlinuz-latest-lts-aarch64" + initrd: "initramfs-latest-lts-aarch64" + kernelCommandLine: "" + bootMode: reboot # 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: reboot # 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-rk3588-edge": + enabled: false + arch: "aarch64" + downloadFile: "hook_armbian-rk3588-edge.tar.gz" + kernel: "vmlinuz-armbian-rk3588-edge" + initrd: "initramfs-armbian-rk3588-edge" + 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 + doFixResolvConf: true + doInjectHegelCloudInit: 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 + doFixResolvConf: true + doInjectHegelCloudInit: true + rootfsPartitionNumber: "1" + + "fatso-ubuntu-noble-baremetal-amd64": + enabled: false + arch: "x86_64" + downloadURL: "https://github.com/k8s-avengers/fatso-images/releases/download/ubuntu-noble-baremetal-cloud-k8s-nvidia/ubuntu-noble-baremetal-cloud-k8s-nvidia-v1008.img.gz" + conversion: "download-only" + image: "ubuntu-noble-baremetal-cloud-k8s-nvidia-v1008.img.gz" + doGrowPart: false + doUserAndSshSetup: false + doFixResolvConf: false + doInjectHegelCloudInit: true + rootfsPartitionNumber: "2" # First one is ESP + #doAddEFIBootEntry: true + doRestoreGRUBNormalcy: true + + "fatso-el9-baremetal-amd64": + enabled: false + arch: "x86_64" + downloadURL: "https://github.com/k8s-avengers/fatso-images/releases/download/el-9-baremetal/el-9-baremetal-v1008.img.gz" + conversion: "download-only" + image: "el-9-baremetal-v1008.img.gz" + doGrowPart: false + doUserAndSshSetup: false + doFixResolvConf: false + rootfsPartitionNumber: "2" # First one is ESP + #doAddEFIBootEntry: true + doRestoreGRUBNormalcy: true + + "armbian-uefi-arm64-edge-cloud-k8s": + enabled: false + arch: "aarch64" + downloadURL: "https://github.com/armsurvivors/armbian-release/releases/download/24.05.22-armsurvivors-189/Armbian-unofficial_24.05.22-armsurvivors-189_Uefi-arm64_bookworm_edge_6.8.4-metadata-cloud-k8s-1.28.img.qcow2.xz" + image: "Armbian-unofficial_24.05.22-armsurvivors-189_Uefi-arm64_bookworm_edge_6.8.4-metadata-cloud-k8s-1.28.img.gz" + conversion: "xz-qcow2-to-img-gz" + doGrowPart: false # Armbian does it itself when booted + doUserAndSshSetup: false # Armbian does it itself when booted + doFixResolvConf: true + rootfsPartitionNumber: "2" # 1 is ESP, 2 rootfs + + "armbian-uefi-x86-edge-cloud-k8s": + enabled: false + arch: "aarch64" + downloadURL: "https://github.com/armsurvivors/armbian-release/releases/download/24.05.22-armsurvivors-189/Armbian-unofficial_24.05.22-armsurvivors-189_Uefi-x86_bookworm_edge_6.8.4-metadata-cloud-k8s-1.28.img.qcow2.xz" + image: "Armbian-unofficial_24.05.22-armsurvivors-189_Uefi-x86_bookworm_edge_6.8.4-metadata-cloud-k8s-1.28.img.gz" + conversion: "xz-qcow2-to-img-gz" + doGrowPart: false # Armbian does it itself when booted + doUserAndSshSetup: false # Armbian does it itself when booted + doFixResolvConf: true + rootfsPartitionNumber: "2" # 1 is ESP, 2 rootfs + + + "armbian-rpardini-rockpro64-edge-uboot": + enabled: false + arch: "aarch64" + downloadURL: "https://github.com/armsurvivors/armbian-release/releases/download/24.05.22-armsurvivors-189/Armbian-unofficial_24.05.22-armsurvivors-189_Rockpro64_bookworm_edge_6.8.2.img.xz" + image: "Armbian-unofficial_24.05.22-armsurvivors-189_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 + doFixResolvConf: true + rootfsPartitionNumber: "1" + + "armbian-rpardini-orangepi3b-edge-uboot": + enabled: false + arch: "aarch64" + downloadURL: "https://github.com/armsurvivors/armbian-release/releases/download/24.05.22-armsurvivors-189/Armbian-unofficial_24.05.22-armsurvivors-189_Orangepi3b_bookworm_edge_6.8.9.img.xz" + image: "Armbian-unofficial_24.05.22-armsurvivors-189_Orangepi3b_bookworm_edge_6.8.9.img.gz" + conversion: "xz-to-gz" + doGrowPart: false # Armbian does it itself when booted + doUserAndSshSetup: false # Armbian does it itself when booted + doFixResolvConf: true + rootfsPartitionNumber: "1" + + "armbian-rpardini-odroidm1-edge-uboot": + enabled: false + arch: "aarch64" + downloadURL: "https://github.com/armsurvivors/armbian-release/releases/download/24.07.07-armsurvivors-304/Armbian-unofficial_24.07.07-armsurvivors-304_Odroidm1_bookworm_edge_6.9.8.img.xz" + image: "Armbian-unofficial_24.07.07-armsurvivors-304_Odroidm1_bookworm_edge_6.9.8.img.gz" + conversion: "xz-to-gz" + doGrowPart: false # Armbian does it itself when booted + doUserAndSshSetup: false # Armbian does it itself when booted + doFixResolvConf: true + rootfsPartitionNumber: "1" + + "armbian-rpardini-t95z-cloud-edge-uboot": + enabled: false + arch: "aarch64" + downloadURL: "https://github.com/armsurvivors/armbian-release/releases/download/24.05.22-armsurvivors-189/Armbian-unofficial_24.05.22-armsurvivors-189_Aml-t95z-plus_bookworm_edge_6.8.10-metadata-cloud.img.qcow2.xz" + image: "Armbian-unofficial_24.05.22-armsurvivors-189_Aml-t95z-plus_bookworm_edge_6.8.10-metadata-cloud.img.gz" + conversion: "xz-qcow2-to-img-gz" + doGrowPart: false # Armbian does it itself when booted + doUserAndSshSetup: false # Armbian does it itself when booted + doFixResolvConf: true + doInjectHegelCloudInit: true + rootfsPartitionNumber: "1" # rootfs is the one and only + + "armbian-rpardini-odroidhc4-cloud-uboot": + enabled: false + arch: "aarch64" + downloadURL: "https://github.com/armsurvivors/armbian-release/releases/download/24.05.22-armsurvivors-189/Armbian-unofficial_24.05.22-armsurvivors-189_Odroidhc4_bookworm_edge_6.8.10-metadata-cloud.img.qcow2.xz" + image: "Armbian-unofficial_24.05.22-armsurvivors-189_Odroidhc4_bookworm_edge_6.8.10-metadata-cloud.img.gz" + conversion: "xz-qcow2-to-img-gz" + doGrowPart: false # Armbian does it itself when booted + doUserAndSshSetup: false # Armbian does it itself when booted + doFixResolvConf: true + doInjectHegelCloudInit: true + rootfsPartitionNumber: "1" # rootfs is the one and only + + "armbian-rpardini-nanopct6-cloud-uboot": + enabled: false + arch: "aarch64" + downloadURL: "https://github.com/armsurvivors/armbian-release/releases/download/24.06.07-armsurvivors-215/Armbian-unofficial_24.06.07-armsurvivors-215_Nanopct6_bookworm_edge_6.10.0-rc2-metadata-cloud.img.qcow2.xz" + image: "Armbian-unofficial_24.06.07-armsurvivors-215_Nanopct6_bookworm_edge_6.10.0-rc2-metadata-cloud.img.gz" + conversion: "xz-qcow2-to-img-gz" + doGrowPart: false # Armbian does it itself when booted + doUserAndSshSetup: false # Armbian does it itself when booted + doFixResolvConf: true + doInjectHegelCloudInit: true + rootfsPartitionNumber: "1" # rootfs is the one and only + + "armbian-rpardini-rpi4b-cloud-notefi": + enabled: false + arch: "aarch64" + downloadURL: "https://github.com/armsurvivors/armbian-release/releases/download/24.05.22-armsurvivors-190/Armbian-unofficial_24.05.22-armsurvivors-190_Rpi4b_bookworm_current_6.6.31-metadata-cloud.img.qcow2.xz" + image: "Armbian-unofficial_24.05.22-armsurvivors-190_Rpi4b_bookworm_current_6.6.31-metadata-cloud.img.gz" + conversion: "xz-qcow2-to-img-gz" + doGrowPart: false # Armbian does it itself when booted + doUserAndSshSetup: false # Armbian does it itself when booted + doFixResolvConf: true + doInjectHegelCloudInit: true + 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.05.22-armsurvivors-189/Armbian-unofficial_24.05.22-armsurvivors-189_Mekotronics-r58x-pro_bookworm_vendor_6.1.43.img.xz" + image: "Armbian-unofficial_24.05.22-armsurvivors-189_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 + doFixResolvConf: true + rootfsPartitionNumber: "1" # first partition, ext4 rootfs + + "armbian-rpardini-r58x-vendor-uefi-cloud": # @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.05.22-armsurvivors-189/Armbian-unofficial_24.05.22-armsurvivors-189_Mekotronics-r58x-pro_jammy_vendor_6.1.43-cloud-amazingfated-edk2-custom-userdata.img.xz" + image: "Armbian-unofficial_24.05.22-armsurvivors-189_Mekotronics-r58x-pro_jammy_vendor_6.1.43-cloud-amazingfated-edk2-custom-userdata.img.gz" + conversion: "xz-to-gz" + doGrowPart: false # Armbian does it itself when booted + doUserAndSshSetup: false # Armbian does it itself when booted + doFixResolvConf: true + rootfsPartitionNumber: "2" # first partition is ESP, 2nd is ext4 rootfs + + "rock-5b-edge-trixie-uefi-dtb": # @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.05.22-armsurvivors-189/Armbian-unofficial_24.05.22-armsurvivors-189_Rock-5b_trixie_edge_6.8.4-cloud-edk2-custom-userdata.img.xz" + image: "Armbian-unofficial_24.05.22-armsurvivors-189_Rock-5b_trixie_edge_6.8.4-cloud-edk2-custom-userdata.img.gz" + conversion: "xz-to-gz" # @TODO: "qcow2-xz-to-img-gz" one day for completeness + doGrowPart: false # Armbian does it itself when booted + doUserAndSshSetup: false # Armbian does it itself when booted + doFixResolvConf: true + rootfsPartitionNumber: "2" # first partition is ESP, 2nd is ext4 rootfs + + # local development versions of fatso images; identical + "fatso-ubuntu-noble-local-amd64": + enabled: false + arch: "x86_64" + downloadURL: "not-used-but-must-change-if-image-changes::ubuntu-noble-baremetal-cloud-k8s-nvidia-v666-local.img.gz" + conversion: "local" # should be there already somehow + image: "ubuntu-noble-baremetal-cloud-k8s-nvidia-v666-local.img.gz" + doGrowPart: false + doUserAndSshSetup: false + doFixResolvConf: false + doInjectHegelCloudInit: true + rootfsPartitionNumber: "2" # First one is ESP + #doAddEFIBootEntry: true + doRestoreGRUBNormalcy: true + + "fatso-el9-local-amd64": + enabled: false + arch: "x86_64" + downloadURL: "not-used-but-must-change-if-image-changes::el-9-baremetal-v666-local.img.gz" + conversion: "local" # should be there already somehow + image: "el-9-baremetal-v666-local.img.gz" + doGrowPart: false + doUserAndSshSetup: false + doFixResolvConf: false + rootfsPartitionNumber: "2" # First one is ESP + #doAddEFIBootEntry: true + doRestoreGRUBNormalcy: true + + +# 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" ] + time_servers: [ "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" # not really used in this case; Hook dev environment controls the kernel there + 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": # For Armbian kernel Hook, and Ubuntu cloud image + 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" + imageRef: "ubuntu-jammy-cloud-arm64" + extraKernelCommandLine: "console=ttyAMA0" + + "vm04": # For default arm64 kernel Hook, and Ubuntu cloud image + enabled: false + arch: aarch64 + mac: "52:54:00:01:03:04" + rootDisk: "/dev/vdb" + ipv4: + address: "192.168.99.41" + hookRef: "default-hook-aarch64" + imageRef: "ubuntu-jammy-cloud-arm64" # defined above + extraKernelCommandLine: "console=ttyAMA0" + + "vm08": + enabled: false + arch: x86_64 + mac: "52:54:00:01:03:08" + rootDisk: "/dev/disk/by-id/virtio-root_disk_serial" + ipv4: + address: "192.168.99.59" + hookRef: "default-hook-amd64" + imageRef: "fatso-ubuntu-noble-baremetal-amd64" + extraKernelCommandLine: "console=ttyS0" + # userData is just standard cloud-init, thus supports #cloud-config and #include etc. + # Attention: if ever ran, this gives rpardini root access to your machine. Use your own scripts; + userData: | + #include https://cloud-init.pardini.net/rpardini/oldskool-rpardini/master/base + + "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" + + "t95z": + enabled: false + arch: aarch64 + mac: "c8:63:14:71:2a:6d" + rootDisk: "/dev/sda" # USB Disk plugged in + ipv4: + address: "192.168.99.27" + hookRef: "armbian-meson64-edge" + imageRef: "armbian-rpardini-t95z-cloud-edge-uboot" + extraKernelCommandLine: "console=ttyAML0,115200" + + "odroidhc4": + enabled: false + arch: aarch64 + mac: "00:1e:06:49:15:91" + rootDisk: "/dev/sda" # SATA Disk plugged in + ipv4: + address: "192.168.99.28" + hookRef: "armbian-meson64-edge" + imageRef: "armbian-rpardini-odroidhc4-cloud-uboot" + 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" + + "odroidm1": + enabled: false + arch: aarch64 + mac: "fe:9b:e6:5c:7d:15" + rootDisk: "/dev/disk/by-id/nvme-WDC_WDS100T2B0C-00PXH0_21411V443507" + ipv4: + address: "192.168.99.56" + hookRef: "armbian-rockchip64-edge" + imageRef: "armbian-rpardini-odroidm1-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-cloud-notefi" + 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 + rootDisk: "/dev/nvme0NOENOENOENEyet" + ipv4: + address: "192.168.99.55" + hookRef: "armbian-rk35xx-vendor" + imageRef: "armbian-rpardini-r58x-vendor-uefi-cloud" # "armbian-rpardini-r58x-vendor-uboot" + extraKernelCommandLine: "console=tty1 console=ttyFIQ0,1500000 " # # Last console defined "wins", thus use tty1 last if no serial console + + "nanopct6lan1": + enabled: false + arch: aarch64 + mac: "8e:b4:90:97:32:30" + rootDisk: "/dev/nvme0NOENOENOENEyet" + ipv4: + address: "192.168.99.171" + hookRef: "armbian-rk3588-edge" + imageRef: "armbian-rpardini-nanopct6-cloud-uboot" # "armbian-rpardini-r58x-vendor-uboot" + extraKernelCommandLine: "console=ttyS2,1500000 " # # Last console defined "wins", thus use tty1 last if no serial console + + "nanopct6lan2": + enabled: false + arch: aarch64 + mac: "8e:b4:90:97:32:31" + rootDisk: "/dev/nvme0NOENOENOENEyet" + ipv4: + address: "192.168.99.172" + hookRef: "armbian-rk3588-edge" + imageRef: "armbian-rpardini-nanopct6-cloud-uboot" # "armbian-rpardini-r58x-vendor-uboot" + extraKernelCommandLine: "console=ttyS2,1500000 " # # Last console defined "wins", thus use tty1 last if no serial console