Nicolas Massé 3 months ago
parent
commit
a463554fc7
  1. 0
      bootc/config.toml
  2. 8
      bootc/scenario2/Containerfile
  3. 10
      bootc/scenario2/root/etc/default/migrate-vm-printserver.env
  4. 18
      bootc/scenario2/root/etc/greenboot/check/required.d/30_printserver_check.sh
  5. 1
      bootc/scenario2/root/etc/libvirt/qemu/networks/autostart/default.xml
  6. 19
      bootc/scenario2/root/etc/libvirt/qemu/networks/default.xml
  7. 19
      bootc/scenario2/root/etc/systemd/system/migrate-vm@.service
  8. 56
      bootc/scenario2/root/usr/local/bin/migrate-vm.sh
  9. 1
      bootc/scenario3a/root/etc/default/bootstrap-vm-nextcloud.env
  10. 4
      bootc/scenario3a/root/etc/systemd/system/bootstrap-vm@.service
  11. 16
      bootc/scenario3a/root/usr/local/bin/bootstrap-vm.sh
  12. 2
      bootc/scenario3a/root/usr/local/libvirt/images/nextcloud/.gitignore
  13. 40
      bootc/scripts/build.sh
  14. 6
      flightctl/fleets.yaml

0
bootc/scenario3a/config.toml → bootc/config.toml

8
bootc/scenario2/Containerfile

@ -0,0 +1,8 @@
FROM edge-registry.itix.fr/demo-edge-retail/base:latest
ADD --chown=root:root root /
RUN <<EOF
set -Eeuo pipefail
systemctl enable migrate-vm@printserver.service
EOF

10
bootc/scenario2/root/etc/default/migrate-vm-printserver.env

@ -0,0 +1,10 @@
DOMAIN_VCPUS=4
DOMAIN_RAM=8192
DOMAIN_OS_VARIANT=win11
DOMAIN_MAC_ADDRESS=04:00:00:00:00:02
RCLONE_CONFIG_HYPERV_TYPE=smb
RCLONE_CONFIG_HYPERV_HOST=192.168.2.72
RCLONE_CONFIG_HYPERV_USER=Red Hat
RCLONE_CONFIG_HYPERV_PASS=3EIvjq3rHcHYcfktOS6HpZyvxefr1A
DOMAIN_HYPERV_DISK_LOCATION=hyperv:c$/ProgramData/Microsoft/Windows/Virtual Hard Disks/Print Server.vhdx
DOMAIN_HYPERV_DISK_NAME=Print Server.vhdx

18
bootc/scenario2/root/etc/greenboot/check/required.d/30_printserver_check.sh

@ -0,0 +1,18 @@
#!/bin/bash
set -Eeuo pipefail
MAX_ATTEMPTS=60
for (( attempt=1; attempt<=MAX_ATTEMPTS; attempt++ )); do
echo "Checking VM ($attempt/$MAX_ATTEMPTS)..."
if virsh domstate printserver | grep -q 'running'; then
echo "The printserver VM is running."
exit 0
fi
sleep 5
done
echo "printserver VM is not running correctly after $MAX_ATTEMPTS attempts!"
exit 1

1
bootc/scenario2/root/etc/libvirt/qemu/networks/autostart/default.xml

@ -0,0 +1 @@
../default.xml

19
bootc/scenario2/root/etc/libvirt/qemu/networks/default.xml

@ -0,0 +1,19 @@
<network>
<name>default</name>
<bridge name="virbr0" stp="on" delay="5" />
<forward mode='nat' />
<domain name="libvirt.test" />
<dns>
<host ip='192.168.122.1'>
<hostname>host</hostname>
</host>
</dns>
<ip address="192.168.122.1" netmask="255.255.255.0" localPtr="yes">
<dhcp>
<range start="192.168.122.100" end="192.168.122.200">
<lease expiry='24' unit='hours'/>
</range>
<host mac="04:00:00:00:00:01" name="nextcloud" ip="192.168.122.2" />
</dhcp>
</ip>
</network>

19
bootc/scenario2/root/etc/systemd/system/migrate-vm@.service

@ -0,0 +1,19 @@
[Unit]
Description=RHDE VM Migration Service
Documentation=man:systemd.service(5)
After=network-online.target
Wants=network-online.target
# Only start if the VM root disk does not exist
ConditionPathExists=!/var/lib/libvirt/images/%i/root.qcow2
# Remain started to avoid race conditions
Persistent=true
[Service]
Type=oneshot
ExecStart=/usr/local/bin/migrate-vm.sh %i
EnvironmentFile=/etc/default/migrate-vm-%i.env
[Install]
WantedBy=multi-user.target

56
bootc/scenario2/root/usr/local/bin/migrate-vm.sh

@ -0,0 +1,56 @@
#!/bin/bash
set -Eeuo pipefail
if [[ $# -ne 1 ]]; then
echo "Usage: $0 <vm-name>"
exit 1
fi
VM="${1}"
if [ -d "/var/lib/libvirt/images/${VM}/" ]; then
echo "VM ${VM} already exists. Please remove it first."
exit 1
fi
temp_dir=$(mktemp -d)
cleanup() {
local exit_code=$?
rm -rf "$temp_dir"
if [ $exit_code -ne 0 ]; then
echo "An error occurred. Cleaning up..."
virsh destroy "${VM}" || true
virsh undefine "${VM}" --nvram || true
rm -rf "/var/lib/libvirt/images/${VM}/"
fi
}
trap cleanup EXIT
# Create a temporary directory to hold the VM image and copy the base image there
install -m 0710 -o root -g qemu --context=system_u:object_r:virt_image_t:s0 -d "$temp_dir"
install -m 0710 -o root -g qemu -Z -d "/var/lib/libvirt/images/${VM}"
# Migrate the VM disk from Hyper-V to KVM
echo "Copying the VM disk from Hyper-V..."
rclone copy "$DOMAIN_HYPERV_DISK_LOCATION" "$temp_dir"
echo "Converting the VM disk to qcow2 format..."
qemu-img convert -O qcow2 "$temp_dir/$DOMAIN_HYPERV_DISK_NAME" "/var/lib/libvirt/images/${VM}/root.qcow2"
restorecon -F "/var/lib/libvirt/images/${VM}/root.qcow2"
# Create and start the VM using virt-install
echo "Creating and starting the VM ${VM}..."
virt-install --name "${VM}" \
--autostart \
--cpu=host-passthrough \
--vcpus=${DOMAIN_VCPUS} \
--ram=${DOMAIN_RAM} \
--os-variant=${DOMAIN_OS_VARIANT} \
--disk=path=/var/lib/libvirt/images/${VM}/root.qcow2,bus=sata,format=qcow2 \
--disk=path=/usr/share/virtio-win/virtio-win.iso,device=cdrom,bus=sata \
--boot uefi \
--import \
--network=network=default,mac=${DOMAIN_MAC_ADDRESS} \
--noautoconsole
echo "VM ${VM} has been created and started."
exit 0

1
bootc/scenario3a/root/etc/default/bootstrap-vm-nextcloud.env

@ -4,3 +4,4 @@ DOMAIN_DISK_SIZE=100
DOMAIN_OS_VARIANT=rhel9.6 DOMAIN_OS_VARIANT=rhel9.6
DOMAIN_MAC_ADDRESS=04:00:00:00:00:01 DOMAIN_MAC_ADDRESS=04:00:00:00:00:01
FLIGHTCTL_LABELS_OVERRIDE={ "type": "virtualmachine", "vm.name": "nextcloud", "scenario": "scenario1" } FLIGHTCTL_LABELS_OVERRIDE={ "type": "virtualmachine", "vm.name": "nextcloud", "scenario": "scenario1" }
DOMAIN_DISK_SOURCE=edge-registry.itix.fr/demo-edge-retail/scenario1:latest

4
bootc/scenario3a/root/etc/systemd/system/bootstrap-vm@.service

@ -5,9 +5,11 @@ Documentation=man:systemd.service(5)
# Only start if the VM root disk does not exist # Only start if the VM root disk does not exist
ConditionPathExists=!/var/lib/libvirt/images/%i/root.qcow2 ConditionPathExists=!/var/lib/libvirt/images/%i/root.qcow2
# Remain started to avoid race conditions
Persistent=true
[Service] [Service]
Type=oneshot Type=oneshot
Persistent=true
ExecStart=/usr/local/bin/bootstrap-vm.sh %i ExecStart=/usr/local/bin/bootstrap-vm.sh %i
EnvironmentFile=/etc/default/bootstrap-vm-%i.env EnvironmentFile=/etc/default/bootstrap-vm-%i.env

16
bootc/scenario3a/root/usr/local/bin/bootstrap-vm.sh

@ -28,7 +28,13 @@ trap cleanup EXIT
# Create a temporary directory to hold the VM image and copy the base image there # Create a temporary directory to hold the VM image and copy the base image there
install -m 0710 -o root -g qemu --context=system_u:object_r:virt_image_t:s0 -d "$temp_dir" install -m 0710 -o root -g qemu --context=system_u:object_r:virt_image_t:s0 -d "$temp_dir"
install -m 0770 -o root -g qemu --context=system_u:object_r:virt_image_t:s0 "/usr/local/libvirt/images/${VM}/qcow2/disk.qcow2" "$temp_dir/root.qcow2"
# Pull the base image defined in the environment file
podman artifact pull "${DOMAIN_DISK_SOURCE}"
podman artifact extract "${DOMAIN_DISK_SOURCE}" "$temp_dir/root.qcow2"
chown root:qemu "$temp_dir/root.qcow2"
chmod 0660 "$temp_dir/root.qcow2"
chcon system_u:object_r:virt_image_t:s0 "$temp_dir/root.qcow2"
# Inject the Flightctl configuration file (w/ enrollment certificates) into the VM image # Inject the Flightctl configuration file (w/ enrollment certificates) into the VM image
# Note: The injected config file will be moved to the right place in the VM by a systemd override in the base image # Note: The injected config file will be moved to the right place in the VM by a systemd override in the base image
@ -39,6 +45,7 @@ if [ -f /etc/flightctl/config.yaml ]; then
else else
cp /etc/flightctl/config.yaml "$temp_dir/config.yaml" cp /etc/flightctl/config.yaml "$temp_dir/config.yaml"
fi fi
echo "Injecting Flightctl configuration into the VM image..."
guestfish --add "$temp_dir/root.qcow2" -m /dev/sda4 <<EOF guestfish --add "$temp_dir/root.qcow2" -m /dev/sda4 <<EOF
copy-in $temp_dir/config.yaml /ostree/deploy/default/var/lib/private/flightctl/ copy-in $temp_dir/config.yaml /ostree/deploy/default/var/lib/private/flightctl/
EOF EOF
@ -47,16 +54,19 @@ fi
# Inject the OSTree auth.json file into the VM image if it exists on the host # Inject the OSTree auth.json file into the VM image if it exists on the host
# Note: The injected config file will be moved to the right place in the VM by a systemd override in the base image # Note: The injected config file will be moved to the right place in the VM by a systemd override in the base image
if [ -f /etc/ostree/auth.json ]; then if [ -f /etc/ostree/auth.json ]; then
echo "Injecting OSTree auth.json into the VM image..."
guestfish --add "$temp_dir/root.qcow2" -m /dev/sda4 <<'EOF' guestfish --add "$temp_dir/root.qcow2" -m /dev/sda4 <<'EOF'
copy-in /etc/ostree/auth.json /ostree/deploy/default/var/lib/private/flightctl/ copy-in /etc/ostree/auth.json /ostree/deploy/default/var/lib/private/flightctl/
EOF EOF
fi fi
# Copy the VM image to the libvirt images directory # Copy the VM image to the libvirt images directory
echo "Copying the VM disk to the libvirt images directory..."
install -m 0710 -o root -g qemu -Z -d "/var/lib/libvirt/images/${VM}" install -m 0710 -o root -g qemu -Z -d "/var/lib/libvirt/images/${VM}"
install -m 0660 -o root -g qemu -Z "$temp_dir/root.qcow2" "/var/lib/libvirt/images/${VM}/root.qcow2" install -m 0660 -o root -g qemu -Z "$temp_dir/root.qcow2" "/var/lib/libvirt/images/${VM}/root.qcow2"
# Create and start the VM using virt-install # Create and start the VM using virt-install
echo "Creating and starting the VM ${VM}..."
virt-install --name "${VM}" \ virt-install --name "${VM}" \
--autostart \ --autostart \
--cpu=host-passthrough \ --cpu=host-passthrough \
@ -72,4 +82,8 @@ virt-install --name "${VM}" \
--noautoconsole --noautoconsole
echo "VM ${VM} has been created and started." echo "VM ${VM} has been created and started."
# Cleanup the pulled image
podman artifact rm "${DOMAIN_DISK_SOURCE}" || true
exit 0 exit 0

2
bootc/scenario3a/root/usr/local/libvirt/images/nextcloud/.gitignore

@ -1,2 +0,0 @@
qcow2
manifest-qcow2.json

40
bootc/scripts/build.sh

@ -7,12 +7,30 @@ if [[ "$UID" -ne 0 ]]; then
exit 1 exit 1
fi fi
if [ "$#" -ne 1 ]; then if [ "$#" -lt 1 ]; then
echo "Usage: $0 <target-image>" echo "Usage: $0 <bootc-target-image> [qcow2-target-image]"
exit 1 exit 1
fi fi
TARGET_IMAGE="$1" TARGET_IMAGE="$1"
QCOW2_TARGET_IMAGE="${2:-}"
# Parses the target image to get the tag and registry
# Example: myregistry.com/myimage:tag -> (myregistry.com/myimage + tag)
if [[ "$TARGET_IMAGE" == *":"* ]]; then
TARGET_IMAGE_NAME="${TARGET_IMAGE%%:*}"
TARGET_IMAGE_TAG="${TARGET_IMAGE##*:}"
else
TARGET_IMAGE_NAME="$TARGET_IMAGE"
TARGET_IMAGE_TAG="latest"
TARGET_IMAGE="${IMAGE_NAME}:latest"
fi
# Compute the qcow2 target image if not provided
if [ -z "$QCOW2_TARGET_IMAGE" ]; then
QCOW2_TARGET_IMAGE="${TARGET_IMAGE_NAME}-qcow2:${TARGET_IMAGE_TAG}"
fi
OCI_REGISTRY="${TARGET_IMAGE%%/*}" OCI_REGISTRY="${TARGET_IMAGE%%/*}"
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")" PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
@ -48,5 +66,23 @@ if [ -x "$PWD/custom.sh" ]; then
"$PWD/custom.sh" "$PWD/custom.sh"
fi fi
echo "Building and pushing image $TARGET_IMAGE..."
podman build --no-cache -t "${TARGET_IMAGE}" . podman build --no-cache -t "${TARGET_IMAGE}" .
podman push --sign-by-sigstore-private-key "$PROJECT_DIR/signing-key.private" --sign-passphrase-file "$PROJECT_DIR/signing-key.pass" "${TARGET_IMAGE}" podman push --sign-by-sigstore-private-key "$PROJECT_DIR/signing-key.private" --sign-passphrase-file "$PROJECT_DIR/signing-key.pass" "${TARGET_IMAGE}"
echo "Building and pushing image $QCOW2_TARGET_IMAGE..."
temp_dir="$(mktemp -d)"
trap 'rm -rf "$temp_dir"' EXIT
function bootc_image_builder () {
local config="$1"
shift
podman run --rm -it --privileged --pull=newer --security-opt label=type:unconfined_t -v "$config:/$(basename $config):ro" \
-v $temp_dir:/output -v /var/lib/containers/storage:/var/lib/containers/storage \
registry.redhat.io/rhel10/bootc-image-builder:latest --config "/$(basename $config)" "$@"
}
bootc_image_builder "$PROJECT_DIR/config.toml" --type qcow2 "$TARGET_IMAGE"
podman artifact add "$QCOW2_TARGET_IMAGE" "$temp_dir/qcow2/disk.qcow2"
podman artifact push --sign-by-sigstore-private-key "$PROJECT_DIR/signing-key.private" --sign-passphrase-file "$PROJECT_DIR/signing-key.pass" "$QCOW2_TARGET_IMAGE"
podman artifact rm "$QCOW2_TARGET_IMAGE"

6
flightctl/fleets.yaml

@ -19,7 +19,8 @@ spec:
os: os:
image: edge-registry.itix.fr/demo-edge-retail/base:latest image: edge-registry.itix.fr/demo-edge-retail/base:latest
systemd: systemd:
matchPatterns: [] matchPatterns:
- greenboot-healthcheck.service
--- ---
apiVersion: flightctl.io/v1alpha1 apiVersion: flightctl.io/v1alpha1
kind: Fleet kind: Fleet
@ -53,6 +54,7 @@ spec:
- nextcloud-db.service - nextcloud-db.service
- nextcloud-nginx.service - nextcloud-nginx.service
- nextcloud-redis.service - nextcloud-redis.service
- greenboot-healthcheck.service
--- ---
apiVersion: flightctl.io/v1alpha1 apiVersion: flightctl.io/v1alpha1
kind: Fleet kind: Fleet
@ -80,6 +82,7 @@ spec:
- bootstrap-vm@nextcloud.service - bootstrap-vm@nextcloud.service
- libvirtd.service - libvirtd.service
- nftables.service - nftables.service
- greenboot-healthcheck.service
--- ---
apiVersion: flightctl.io/v1alpha1 apiVersion: flightctl.io/v1alpha1
kind: Fleet kind: Fleet
@ -112,3 +115,4 @@ spec:
- odoo-app.service - odoo-app.service
- odoo-db.service - odoo-db.service
- odoo-init.service - odoo-init.service
- greenboot-healthcheck.service

Loading…
Cancel
Save