These files are configured in a way so that an Ubuntu cloud-image is modified by downloading the RKE2 install script from upstream as well as installing the qemu-guest-agent. This is done so the Ubuntu image can now function in an airgapped environment as an RKE2 node. Previous methods I've done involved using libguestfs tools and it was a bit clunky due to how it needed to be managed. Packer's QEMU provider fixes that for me.
Unfortunately, Packer's QEMU provider must run locally as there is no Harvester provider that would run these commands on a remote Harvester cluster to save us the dependency problem. Perhaps in the future we can explore that level of sophistication, but for now this works great.
There is a post-install provisioner that uploads the resulting image to Harvester using a VirtualMachineImage CR template. If you do not wish to upload to Harvester, feel free to comment out that section in spec.pkr.hcl, it is located towards the bottom and starts with the lines post-processor "shell-local"
Keep in mind that Packer has VM specs such as core and memory counts purely to build a temporary VM instance in order to load and modify the upstream Canonical cloud image for Ubuntu. The image output format is qcow2 and as such, it does not contain any specifications around core/memory/storage. qcow2 is not the equivalent of ova or ovf, it is closer to vmdk.
Packer automatically handles rebuilding and compressing the finalized image and there are some cleanup steps that reset the cloud-init status so the image can be run as a new image where cloud-init runs again.
In a production environment, the upload to Harvester would likely be done in a separate step unless you're building in an airgap. Further, you might have mandates to start with a server iso as opposed to a cloud image. Given that, there are a lot more steps involved to install one of those that involves Packer's UI interaction capabilities. There is plenty of information online around Ubuntu as an example with packer for that, it should be easy enough to transpose.
Also, in production, you'll want to add checksum compute capabilities and likely add steps that generate a manifest for your image to be added to Hauler so that it may be brought into the airgap.
Ensure qemu and yq are installed and that you are running on a Linux OS of some sort. Edit the build.sh file and ensure your harvester password is correct.
Run the build script ./build.sh, see example below:
$ ./build.sh
2024/04/08 08:27:23 [INFO] Packer version: 1.10.2 [go1.20.12 linux amd64]
2024/04/08 08:27:23 [TRACE] discovering plugins in /usr/bin
2024/04/08 08:27:23 [TRACE] discovering plugins in .
2024/04/08 08:27:23 [TRACE] discovering plugins in /home/deathstar/.config/packer/plugins
2024/04/08 08:27:23 [INFO] Discovered potential plugin: qemu = /home/deathstar/.config/packer/plugins/github.com/hashicorp/qemu/packer-plugin-qemu_v1.1.0_x5.0_linux_amd64
2024/04/08 08:27:23 [INFO] found external [-packer-default-plugin-name-] builders from qemu plugin
2024/04/08 08:27:23 [INFO] PACKER_CONFIG env var not set; checking the default config file path
2024/04/08 08:27:23 [INFO] PACKER_CONFIG env var set; attempting to open config file: /home/deathstar/.packerconfig
2024/04/08 08:27:23 [WARN] Config file doesn't exist: /home/deathstar/.packerconfig
2024/04/08 08:27:23 [INFO] Setting cache directory: /home/deathstar/.cache/packer
2024/04/08 08:27:23 [TRACE] listing potential installations for "github.com/hashicorp/qemu" that match ">= 1.0.9". plugingetter.ListInstallationsOptions{FromFolders:[]string{"/usr/bin", ".", "/home/deathstar/.config/packer/plugins"}, BinaryInstallationOptions:plugingetter.BinaryInstallationOptions{APIVersionMajor:"5", APIVersionMinor:"0", OS:"linux", ARCH:"amd64", Ext:"", Checksummers:[]plugingetter.Checksummer{plugingetter.Checksummer{Type:"sha256", Hash:(*sha256.digest)(0xc0003e2180)}}}}
2024/04/08 08:27:23 [TRACE] Found the following "github.com/hashicorp/qemu" installations: [{/home/deathstar/.config/packer/plugins/github.com/hashicorp/qemu/packer-plugin-qemu_v1.1.0_x5.0_linux_amd64 v1.1.0}]
2024/04/08 08:27:23 [INFO] found external [-packer-default-plugin-name-] builders from qemu plugin
...
==> image_build.qemu.ubuntu_cloud: Running post-processor: (type shell-local)
==> image_build.qemu.ubuntu_cloud (shell-local): Running local shell script: upload.sh
2024/04/08 08:30:44 packer-post-processor-shell-local plugin: [INFO] (shell-local): starting local command: bash -c HARVESTER_PASSWORD='<sensitive>' HARVESTER_VIP='10.10.0.10' IMAGE_FILE='output//ubuntu-jammy-rke2-amd64.img' IMAGE_NAME='ubuntu-jammy-rke2' NAMESPACE='default' PACKER_BUILDER_TYPE='qemu' PACKER_BUILD_NAME='ubuntu_cloud' PACKER_HTTP_ADDR='10.0.2.2:0' PACKER_HTTP_IP='10.0.2.2' PACKER_HTTP_PORT='0' /home/deathstar/airgapping_harvester_with_hauler/services/ubuntu_image/upload.sh
2024/04/08 08:30:44 packer-post-processor-shell-local plugin: [INFO] (shell-local communicator): Executing local shell command [bash -c HARVESTER_PASSWORD='<sensitive>' HARVESTER_VIP='10.10.0.10' IMAGE_FILE='output//ubuntu-jammy-rke2-amd64.img' IMAGE_NAME='ubuntu-jammy-rke2' NAMESPACE='default' PACKER_BUILDER_TYPE='qemu' PACKER_BUILD_NAME='ubuntu_cloud' PACKER_HTTP_ADDR='10.0.2.2:0' PACKER_HTTP_IP='10.0.2.2' PACKER_HTTP_PORT='0' /home/deathstar/airgapping_harvester_with_hauler/services/ubuntu_image/upload.sh]
...
Build 'image_build.qemu.ubuntu_cloud' finished after 3 minutes 37 seconds.
==> Wait completed after 3 minutes 37 seconds