diff --git a/README.md b/README.md index d3e3004..0eeeffb 100644 --- a/README.md +++ b/README.md @@ -57,19 +57,32 @@ This repository gathers all the recipes (hence the name "Cookbook") to deploy Op To develop Podman Quadlets, it is advised to create a Fedora Virtual Machine dedicated to this task. -You can create a Fedora Virtual Machine with the following commands: +You can create a Fedora Virtual Machine with the following command: ```sh -TODO +sudo ./create-dev-vm.sh ``` -Dependencies to install in the VM: +Then, retrieve the IP address of your VM with the following command: ```sh -dnf install -y make systemd procps-ng @virtualization qemu-img virt-install coreos-installer xterm-resize butane yq podlet +sudo virsh domifaddr quadlets ``` +Then, on your host, add the following configuration to your `~/.ssh/config` file: +``` +Host quadlets + HostName + User root + ForwardAgent yes + StrictHostKeyChecking=no + UserKnownHostsFile=/dev/null +``` + +Finally, install the [Remote - SSH](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh&ssr=false#overview) extension for Visual Studio Code, and connect to your VM using the "Remote - SSH: Connect to Host..." command. + +If needed, you can also connect to your VM using `ssh root@quadlets` from your terminal or from the libvirt console using `sudo virsh console quadlets` with the login `root` and the password `root`. ## License diff --git a/cloud-init.dev.yaml b/cloud-init.dev.yaml new file mode 100644 index 0000000..021baf7 --- /dev/null +++ b/cloud-init.dev.yaml @@ -0,0 +1,39 @@ +#cloud-config + +users: +- name: root + ssh_authorized_keys: + - ssh-ed25519 REDACTED user@host + +chpasswd: + expire: false + users: + - name: root + password: root + type: text + +packages: +- '@virtualization' # Nested virtualization is required to run the tests of the Podman Quadlet Cookbook +- butane +- coreos-installer +- curl +- git +- git-lfs +- make +- net-tools +- nodejs-npm # Required to install Claude Code +- podlet # Tooling for Podman Quadlets development +- podman +- procps-ng +- qemu-img +- systemd +- virt-install +- xterm-resize # Required to fix the terminal when using `virsh console` with UEFI firmware +- yq + +write_files: +- path: /etc/ssh/sshd_config.d/00-vscode.conf + content: | + # This file is used to allow VS Code Remote SSH extension to connect to the VM as root user. + PermitRootLogin prohibit-password + permissions: '0600' diff --git a/create-dev-vm.sh b/create-dev-vm.sh new file mode 100755 index 0000000..81f7aac --- /dev/null +++ b/create-dev-vm.sh @@ -0,0 +1,161 @@ +#!/bin/bash +set -Eeuo pipefail + +# Script to create a Fedora Server VM with cloud-init +# Specs: 16GB RAM, 4 vCPU, 1TB sparse root disk + +# Configuration +VM_NAME="${VM_NAME:-quadlets}" +VM_RAM="${VM_RAM:-16384}" # Unit: MB +VM_VCPUS="${VM_VCPUS:-4}" +DISK_SIZE="${DISK_SIZE:-1024}" # Unit: GB +LIBVIRT_IMAGES_DIR="/var/lib/libvirt/images" +VM_DIR="${LIBVIRT_IMAGES_DIR}/${VM_NAME}" +CLOUD_INIT_CONFIG="$(dirname "$(realpath "$0")")/cloud-init.dev.yaml" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +log_info() { + echo -e "${GREEN}[INFO]${NC} $*" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $*" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $*" +} + +# Check if running as root or with sudo +if [[ $EUID -ne 0 ]]; then + log_error "This script must be run as root or with sudo" + exit 1 +fi + +# Check required commands +for cmd in virt-install genisoimage curl jq; do + if ! command -v "$cmd" &> /dev/null; then + log_error "Required command '$cmd' not found. Please install it first." + exit 1 + fi +done + +# Check if cloud-init config exists +if [[ ! -f "$CLOUD_INIT_CONFIG" ]]; then + log_error "Cloud-init config not found: $CLOUD_INIT_CONFIG" + exit 1 +fi + +# Get the latest Fedora Server version +log_info "Determining latest Fedora Server version..." +FEDORA_VERSION=$(curl -sSfL https://fedoraproject.org/releases.json | \ + jq -r '.[] | select(.variant=="Cloud") | .version' | \ + sort -rV | \ + head -n 1) + +if [[ -z "$FEDORA_VERSION" ]]; then + log_error "Could not determine latest Fedora version!" + exit 1 +fi + +log_info "Using Fedora Server version: $FEDORA_VERSION" + +# Get download URL from releases.json +log_info "Fetching download information..." +QCOW2_FULL_URL=$(curl -sSfL https://fedoraproject.org/releases.json | \ + jq -r --arg ver "$FEDORA_VERSION" --arg arch "$(arch)" \ + '.[] | select(.version==$ver and .variant=="Cloud" and .subvariant=="Cloud_Base" and .arch==$arch and (.link|endswith(".qcow2"))) | .link') + +if [[ -z "$QCOW2_FULL_URL" ]]; then + log_error "Could not find Qcow2 download URL for Fedora $FEDORA_VERSION" + exit 1 +fi + +QCOW2_FILENAME=$(basename "$QCOW2_FULL_URL") +mkdir -p "$VM_DIR" + +# Download Qcow2 image if not already present +QCOW2_PATH="${VM_DIR}/${QCOW2_FILENAME}" +if [[ -f "$QCOW2_PATH" ]]; then + log_info "Qcow2 image already exists: $QCOW2_PATH" +else + log_info "Downloading Qcow2 image..." + curl -fL -o "$QCOW2_PATH" "$QCOW2_FULL_URL" + log_info "Download complete" +fi + +# Create the VM disk from the base image +VM_DISK="${VM_DIR}/${VM_NAME}.qcow2" +if [[ -f "$VM_DISK" ]]; then + log_warn "VM disk already exists: $VM_DISK" +else + log_info "Creating VM disk (${DISK_SIZE}GB sparse)..." + qemu-img create -f qcow2 -F qcow2 -b "$QCOW2_PATH" "$VM_DISK" "${DISK_SIZE}G" +fi + +# Create cloud-init ISO +CLOUD_INIT_ISO="${VM_DIR}/cloud-init.iso" +USER_DATA="${VM_DIR}/user-data" +META_DATA="${VM_DIR}/meta-data" + +log_info "Creating cloud-init configuration files..." + +# Copy user-data from cloud-init config +cp "$CLOUD_INIT_CONFIG" "$USER_DATA" + +# Create meta-data file +cat > "$META_DATA" << EOF +instance-id: ${VM_NAME} +local-hostname: ${VM_NAME} +EOF + +log_info "Creating cloud-init ISO..." +genisoimage -output "$CLOUD_INIT_ISO" -volid cidata -joliet -rock "$USER_DATA" "$META_DATA" + +# Check if VM already exists +if virsh dominfo "$VM_NAME" &> /dev/null; then + log_warn "VM '$VM_NAME' already exists" +else + # Create the VM + log_info "Creating VM: $VM_NAME" + log_info " RAM: ${VM_RAM}MB (16GB)" + log_info " vCPUs: $VM_VCPUS" + log_info " Disk: ${DISK_SIZE}GB (sparse)" + + virt-install \ + --name "$VM_NAME" \ + --ram "$VM_RAM" \ + --vcpus "$VM_VCPUS" \ + --cpu host-passthrough \ + --disk path="$VM_DISK",format=qcow2,bus=virtio \ + --disk path="$CLOUD_INIT_ISO",device=cdrom \ + --os-variant fedora-unknown \ + --network network=default,model=virtio \ + --graphics none \ + --console pty,target.type=virtio \ + --serial pty \ + --sysinfo system.serial=ds=nocloud \ + --boot uefi \ + --import \ + --noautoconsole + + log_info "VM '$VM_NAME' created successfully!" + log_info "" + log_info "VM Details:" + log_info " Name: $VM_NAME" + log_info " Directory: $VM_DIR" + log_info " Disk: $VM_DISK" + log_info " Cloud-init: $CLOUD_INIT_ISO" + log_info "" + log_info "To start the VM: virsh start $VM_NAME" + log_info "To connect to console: virsh console $VM_NAME" + log_info "To get IP address: virsh domifaddr $VM_NAME" + log_info "To destroy the VM: virsh destroy $VM_NAME" + log_info "To delete the VM: virsh undefine --nvram $VM_NAME && rm -rf $VM_DIR" +fi +