From bbb43cdf5513692afd540fb33bcf25d389e3713a Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Sun, 16 Apr 2023 22:12:52 +0100 Subject: [PATCH] test(packer): add initial packer file to create archlinux & ubuntu tests image. --- tests/packer/archlinux.pkr.hcl | 73 +++++++++++++ tests/packer/init/clean.sh | 184 +++++++++++++++++++++++++++++++++ tests/packer/main.pkr.hcl | 54 ++++++++++ tests/packer/ubuntu.pkr.hcl | 39 +++++++ tests/packer/variables.pkr.hcl | 75 ++++++++++++++ 5 files changed, 425 insertions(+) create mode 100644 tests/packer/archlinux.pkr.hcl create mode 100755 tests/packer/init/clean.sh create mode 100644 tests/packer/main.pkr.hcl create mode 100644 tests/packer/ubuntu.pkr.hcl create mode 100644 tests/packer/variables.pkr.hcl diff --git a/tests/packer/archlinux.pkr.hcl b/tests/packer/archlinux.pkr.hcl new file mode 100644 index 00000000..a7994ea2 --- /dev/null +++ b/tests/packer/archlinux.pkr.hcl @@ -0,0 +1,73 @@ +# apparmor.d - Full set of apparmor profiles +# Copyright (C) 2023 Alexandre Pujol +# SPDX-License-Identifier: GPL-2.0-only + +source "qemu" "archlinux-gnome" { + disk_image = true + iso_url = "https://geo.mirror.pkgbuild.com/images/latest/Arch-Linux-x86_64-cloudimg.qcow2" + iso_checksum = "file:https://geo.mirror.pkgbuild.com/images/latest/Arch-Linux-x86_64-cloudimg.qcow2.SHA256" + iso_target_path = "${var.iso_dir}/archlinux-cloudimg-amd64.img" + cpus = 6 + memory = 4096 + disk_size = "${var.disk_size}" + accelerator = "kvm" + headless = true + ssh_username = "${var.username}" + ssh_password = "${var.password}" + ssh_port = 22 + ssh_wait_timeout = "1000s" + disk_compression = true + disk_detect_zeroes = "unmap" + disk_discard = "unmap" + output_directory = "${var.output}" + vm_name = "${var.prefix}-${source.name}.qcow2" + boot_wait = "10s" + shutdown_command = "echo ${var.password} | sudo shutdown -hP now" + cd_label = "cidata" + cd_content = { + "meta-data" = "" + "user-data" = templatefile("${path.cwd}/packer/init/${source.name}.user-data.yml", + { + username = "${var.username}" + password = "${var.password}" + ssh_key = file("${var.ssh_publickey}") + hostname = "${var.prefix}-${source.name}" + } + ) + } +} + +source "qemu" "archlinux-kde" { + disk_image = true + iso_url = "https://geo.mirror.pkgbuild.com/images/latest/Arch-Linux-x86_64-cloudimg.qcow2" + iso_checksum = "file:https://geo.mirror.pkgbuild.com/images/latest/Arch-Linux-x86_64-cloudimg.qcow2.SHA256" + iso_target_path = "${var.iso_dir}/archlinux-cloudimg-amd64.img" + cpus = 6 + memory = 4096 + disk_size = "${var.disk_size}" + accelerator = "kvm" + headless = true + ssh_username = "${var.username}" + ssh_password = "${var.password}" + ssh_port = 22 + ssh_wait_timeout = "1000s" + disk_compression = true + disk_detect_zeroes = "unmap" + disk_discard = "unmap" + output_directory = "${var.output}" + vm_name = "${var.prefix}-${source.name}.qcow2" + boot_wait = "10s" + shutdown_command = "echo ${var.password} | sudo shutdown -hP now" + cd_label = "cidata" + cd_content = { + "meta-data" = "" + "user-data" = templatefile("${path.cwd}/packer/init/${source.name}.user-data.yml", + { + username = "${var.username}" + password = "${var.password}" + ssh_key = file("${var.ssh_publickey}") + hostname = "${var.prefix}-${source.name}" + } + ) + } +} diff --git a/tests/packer/init/clean.sh b/tests/packer/init/clean.sh new file mode 100755 index 00000000..26f59b7b --- /dev/null +++ b/tests/packer/init/clean.sh @@ -0,0 +1,184 @@ +#!/usr/bin/env bash +# apparmor.d - Full set of apparmor profiles +# Copyright (C) 2023 Alexandre Pujol +# SPDX-License-Identifier: GPL-2.0-only + +set -u + +# shellcheck source=/dev/null +_lsb_release() { . /etc/os-release || exit 1; echo "$ID"; } +DISTRIBUTION="$(_lsb_release)" +readonly SELF="$0" +readonly DISTRIBUTION + +export HISTSIZE=0 +export DEBIAN_FRONTEND=noninteractive + +readonly green='\e[0;32m' Bold='\e[1m' +_msg() { printf '%b- %s%b\n' "$Bold" "$*" "$green" >&2; } +_disksize() { df --total --block-size=1 --output=size | tail -1; } +_diskused() { df --total --block-size=1 --output=used | tail -1; } + +_sshdgenkeys() { + cat <<-_EOF >/usr/lib/systemd/system/sshdgenkeys.service + [Unit] + Description=SSH Key Generation + ConditionPathExists=|!/etc/ssh/ssh_host_ecdsa_key + ConditionPathExists=|!/etc/ssh/ssh_host_ecdsa_key.pub + ConditionPathExists=|!/etc/ssh/ssh_host_ed25519_key + ConditionPathExists=|!/etc/ssh/ssh_host_ed25519_key.pub + ConditionPathExists=|!/etc/ssh/ssh_host_rsa_key + ConditionPathExists=|!/etc/ssh/ssh_host_rsa_key.pub + + [Service] + ExecStart=/usr/bin/ssh-keygen -A + Type=oneshot + RemainAfterExit=yes + _EOF + mkdir -p /usr/lib/systemd/system/ssh.service.d + cat <<-_EOF >/usr/lib/systemd/system/ssh.service.d/sshdgenkeys.conf + [Unit] + Wants=sshdgenkeys.service + After=sshdgenkeys.service + _EOF +} + +clean_debian() { + _msg "Apt clean configuration" + + _msg "Full system upgrade" + apt-get update -y + apt-get -qq -y --no-install-recommends upgrade + apt-get -qq -y --no-install-recommends dist-upgrade + + _msg "Clean the apt cache" + apt-get -y autoremove --purge + apt-get -y autoclean + apt-get -y clean +} + +clean_arch() { + _msg "Pacman clean configuration" + + pacman -Syu --noconfirm + pacman -Rsccn --noconfirm "$(pacman -Qdtq)" + pacman -Scc --noconfirm +} + +clean_opensuse() { + _msg "zypper clean configuration" + + zypper update -y + zypper clean -y +} + +# Make the image as impersonal as possible. +impersonalize() { + _msg "Make the image as impersonal as possible." + + # Remove remaining pkg file, docs and caches + dirs=( + /usr/share/doc + /usr/share/man + /var/cache/ + /var/lib/apt + /var/lib/dhcp + /var/tmp + ) + for dir in "${dirs[@]}"; do + if [[ -d "$dir" ]]; then + find "$dir" -mindepth 1 -delete + fi + done + + # Truncate any logs that have built up during the install + find /var/log -type f -exec truncate --size=0 {} \; + + # Truncate the machine-id + truncate --size=0 /etc/machine-id + truncate --size=0 /var/lib/dbus/machine-id + + remove=( + # Remove remaining pkg file, docs and caches + /usr/share/info/ + /usr/share/lintian/ + /usr/share/linda/ + + # Remove history & unique ids + /etc/adjtime + /etc/ansible/ + /etc/ssh/ssh_host_*_key* + /home/*/.ansible/ + /home/*/.bash_history + /home/*/.sudo_as_admin_successful + /home/*/.zsh_history + /root/.ansible/ + /root/.bash_history + /root/.wget-hsts + /var/cache/ldconfig/aux-cache + /var/lib/systemd/random-seed + /var/log/private + + # Remove itself + "$(readlink -f "$SELF")" + ) + rm -rvf "${remove[@]}" +} + + +# Free all unused storage block. +trim() { + local swapuuid swappart max size + max=11811160064 # Maximum size to reduce filesystem in RAM (11G) + size=$(_disksize) + + if swapuuid=$(blkid -o value -l -s UUID -t TYPE=swap); then + swappart=$(readlink -f /dev/disk/by-uuid/"$swapuuid") + _msg "Fill with 0 the swap partition to reduce box size" + swapoff "$swappart" + dd if=/dev/zero of="$swappart" bs=1M || true + mkswap -U "$swapuuid" "$swappart" + elif [[ -f /swap/swapfile ]];then + swapoff /swap/swapfile + truncate --size=0 /swap/swapfile + fi + + if [[ "$size" -lt "$max" ]]; then + _msg "Fill root filesystem with 0 to reduce box size" + dd if=/dev/zero of=/EMPTY bs=1M || true + rm -f /EMPTY + fi + + # Block until the empty file has been removed, otherwise, Packer will + # try to kill the box while the disk is still full and that is bad. + sync +} + +main() { + local begin end + begin=$(_diskused) + case "$DISTRIBUTION" in + debian | ubuntu ) + clean_debian + _sshdgenkeys + ;; + + opensuse*) + clean_opensuse + ;; + + arch) + clean_arch + ;; + esac + impersonalize + trim + + end="$(_diskused)" + ((res = begin - end)) + echo "Inital used space: $(numfmt --to=iec "$begin")" + echo "Final space: $(numfmt --to=iec "$end")" + echo "Saved space: $(numfmt --to=iec "$res")" +} + +main "$@" diff --git a/tests/packer/main.pkr.hcl b/tests/packer/main.pkr.hcl new file mode 100644 index 00000000..c6aa8ea1 --- /dev/null +++ b/tests/packer/main.pkr.hcl @@ -0,0 +1,54 @@ +# apparmor.d - Full set of apparmor profiles +# Copyright (C) 2023 Alexandre Pujol +# SPDX-License-Identifier: GPL-2.0-only + +build { + name = "main" + sources = [ + "source.qemu.archlinux-gnome", + "source.qemu.archlinux-kde", + "source.qemu.ubuntu-server", + ] + + provisioner "file" { + destination = "/tmp" + sources = ["${path.cwd}/packer/src"] + } + + provisioner "file" { + only = ["qemu.archlinux-gnome", "qemu.archlinux-kde"] + destination = "/tmp/src/" + sources = ["${path.cwd}/../apparmor.d-${var.version}-x86_64.pkg.tar.zst"] + } + + provisioner "file" { + only = ["qemu.ubuntu-server", "qemu.ubuntu-desktop"] + destination = "/tmp/src/" + sources = ["${path.cwd}/../apparmor.d_${var.version}_all.deb"] + } + + provisioner "shell" { + execute_command = "echo '${var.password}' | sudo -S sh -c '{{ .Vars }} {{ .Path }}'" + inline = [ + "while [ ! -f /var/lib/cloud/instance/boot-finished ]; do echo 'Waiting for Cloud-Init...'; sleep 20; done", + "cloud-init clean", # Remove logs and artifacts so cloud-init can re-run + "sh /tmp/src/init.sh" + ] + } + + provisioner "shell" { + script = "${path.cwd}/packer/init/clean.sh" + execute_command = "echo '${var.password}' | sudo -S sh -c '{{ .Vars }} {{ .Path }}'" + } + + post-processor "vagrant" { + output = "${var.iso_dir}/packer_${var.prefix}-${source.name}.box" + } + + post-processor "shell-local" { + inline = [ + "vagrant box add --force --name ${var.prefix}-${source.name} ${var.iso_dir}/packer_${var.prefix}-${source.name}.box" + ] + } + +} \ No newline at end of file diff --git a/tests/packer/ubuntu.pkr.hcl b/tests/packer/ubuntu.pkr.hcl new file mode 100644 index 00000000..dcea5eb0 --- /dev/null +++ b/tests/packer/ubuntu.pkr.hcl @@ -0,0 +1,39 @@ +# apparmor.d - Full set of apparmor profiles +# Copyright (C) 2023 Alexandre Pujol +# SPDX-License-Identifier: GPL-2.0-only + +source "qemu" "ubuntu-server" { + disk_image = true + iso_url = "https://cloud-images.ubuntu.com/${var.release.ubuntu}/current/${var.release.ubuntu}-server-cloudimg-amd64.img" + iso_checksum = "file:https://cloud-images.ubuntu.com/${var.release.ubuntu}/current/SHA256SUMS" + iso_target_path = "${var.iso_dir}/${source.name}-cloudimg-amd64.img" + cpus = 4 + memory = 2048 + disk_size = "${var.disk_size}" + accelerator = "kvm" + headless = true + ssh_username = "${var.username}" + ssh_password = "${var.password}" + ssh_port = 22 + ssh_wait_timeout = "1000s" + disk_compression = true + disk_detect_zeroes = "unmap" + disk_discard = "unmap" + output_directory = "${var.output}/" + vm_name = "${var.prefix}-${source.name}.qcow2" + boot_wait = "10s" + firmware = "/usr/share/edk2-ovmf/x64/OVMF_CODE.fd" + shutdown_command = "echo ${var.password} | sudo -S /sbin/shutdown -hP now" + cd_label = "cidata" + cd_content = { + "meta-data" = "" + "user-data" = templatefile("${path.cwd}/packer/init/${source.name}.user-data.yml", + { + username = "${var.username}" + password = "${var.password}" + ssh_key = file("${var.ssh_publickey}") + hostname = "${var.prefix}-${source.name}" + } + ) + } +} diff --git a/tests/packer/variables.pkr.hcl b/tests/packer/variables.pkr.hcl new file mode 100644 index 00000000..c6b7465e --- /dev/null +++ b/tests/packer/variables.pkr.hcl @@ -0,0 +1,75 @@ +# apparmor.d - Full set of apparmor profiles +# Copyright (C) 2023 Alexandre Pujol +# SPDX-License-Identifier: GPL-2.0-only + +# Variables definitions + +variable "username" { + description = "Admin username" + type = string + default = "user" +} + +variable "password" { + description = "Default admin password" + type = string + default = "user" +} + +variable "ssh_publickey" { + description = "Path to the ssh public key" + type = string + default = "~/.ssh/id_ed25519.pub" +} + +variable "ssh_privatekey" { + description = "Path to the ssh private key" + type = string + default = "~/.ssh/id_ed25519" +} + +variable "disk_size" { + description = "Disk size of the App VM to build" + type = string + default = "10G" +} + +variable "iso_dir" { + description = "Original ISO file directory" + type = string + default = "/var/lib/libvirt/images" +} + +variable "output" { + description = "Output build directory" + type = string + default = "/tmp/packer" +} + +variable "prefix" { + description = "Image name prefix" + type = string + default = "aa" +} + +variable "version" { + description = "apparmor.d version" + type = string + default = "0.001" +} + +variable "release" { + description = "Distribution release to use" + type = map(string) + default = { + "ubuntu" : "jammy", # 22.04 LTS + "debian" : "bullseye", # 11 + "opensuse" : "9", + } +} + +variable "flavor" { + description = "Distribution flavor to use (-desktop, -gnome, -kde...)" + type = string + default = "" +}