From 8f116f5ac603396e31449534ecf5252d9cdd7721 Mon Sep 17 00:00:00 2001 From: ggouzi Date: Thu, 13 Aug 2026 15:07:24 +0200 Subject: [PATCH] test/e2e/run: Adjust tests to make script and TF plan work in offline environments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Gaëtan GOUZI remove boolean create_profile/create_project + check exist instead + rename example file Signed-off-by: ggouzi --- test/e2e/main.tf | 56 +++++++++++++------ test/e2e/run | 21 +++---- ...rraform.tfvars.custom-image-server.example | 23 ++++++++ test/e2e/variables.tf | 30 ++++++++++ 4 files changed, 102 insertions(+), 28 deletions(-) create mode 100644 test/e2e/terraform.tfvars.custom-image-server.example diff --git a/test/e2e/main.tf b/test/e2e/main.tf index 61592c408..63131d5a0 100644 --- a/test/e2e/main.tf +++ b/test/e2e/main.tf @@ -3,17 +3,31 @@ data "lxd_info" "cluster" { remote = var.remote } -# Project +# Check whether the project already exists +data "external" "project_exists" { + program = ["bash", "-c", "lxc project list \"${var.remote}:\" -f csv | awk -F, '{gsub(/ \\(current\\)$/, \"\", $1); print $1}' | grep -qxF \"${var.project}\" && echo '{\"exists\": \"true\"}' || echo '{\"exists\": \"false\"}'"] +} + +# Check whether the profile already exists +data "external" "profile_exists" { + program = ["bash", "-c", "lxc profile list --project \"${var.project}\" \"${var.remote}:\" -f csv 2>/dev/null | awk -F, '{gsub(/ \\(current\\)$/, \"\", $1); print $1}' | grep -qxF \"${var.profile}\" && echo '{\"exists\": \"true\"}' || echo '{\"exists\": \"false\"}'"] +} + +# Project - created only when it does not already exist resource "lxd_project" "e2e" { - name = "e2e-testing" + count = data.external.project_exists.result["exists"] == "true" ? 0 : 1 + name = var.project remote = var.remote } -# Profile +# Profile - created only when it does not already exist resource "lxd_profile" "e2e" { - name = "e2e-testing" - project = lxd_project.e2e.name - remote = lxd_project.e2e.remote + count = data.external.profile_exists.result["exists"] == "true" ? 0 : 1 + name = var.profile + project = var.project + remote = var.remote + + depends_on = [lxd_project.e2e] # Configuration config = { @@ -44,20 +58,24 @@ resource "lxd_profile" "e2e" { # Images resource "lxd_cached_image" "ctn" { count = var.containers_per_host > 0 ? 1 : 0 - project = lxd_project.e2e.name - remote = lxd_project.e2e.remote - source_remote = "ubuntu-minimal-daily" + project = coalesce(var.image_project, var.project) + remote = var.remote + source_remote = var.image_remote source_image = "24.04" type = "container" + copy_aliases = var.copy_image_aliases + depends_on = [lxd_project.e2e] } resource "lxd_cached_image" "vm" { count = var.vms_per_host > 0 ? 1 : 0 - project = lxd_project.e2e.name - remote = lxd_project.e2e.remote - source_remote = "ubuntu-minimal-daily" + project = coalesce(var.image_project, var.project) + remote = var.remote + source_remote = var.image_remote source_image = "24.04" type = "virtual-machine" + copy_aliases = var.copy_image_aliases + depends_on = [lxd_project.e2e] } # Containers @@ -69,11 +87,12 @@ resource "lxd_instance" "e2e-ctn" { name = each.key target = each.value type = "container" - remote = lxd_project.e2e.remote - project = lxd_project.e2e.name - profiles = [lxd_profile.e2e.name] + remote = var.remote + project = var.project + profiles = [var.profile] image = lxd_cached_image.ctn[0].fingerprint wait_for_network = true + depends_on = [lxd_project.e2e, lxd_profile.e2e] } # VMs @@ -85,12 +104,13 @@ resource "lxd_instance" "e2e-vm" { name = each.key target = each.value type = "virtual-machine" - remote = lxd_project.e2e.remote - project = lxd_project.e2e.name - profiles = [lxd_profile.e2e.name] + remote = var.remote + project = var.project + profiles = [var.profile] image = lxd_cached_image.vm[0].fingerprint wait_for_network = true allow_restart = true + depends_on = [lxd_project.e2e, lxd_profile.e2e] config = { "migration.stateful" = "true" diff --git a/test/e2e/run b/test/e2e/run index ff1b71e19..730ed8f4a 100755 --- a/test/e2e/run +++ b/test/e2e/run @@ -19,6 +19,7 @@ done set -- "${ARGS[@]}" REMOTE="${REMOTE:-${1:-""}}" +PROJECT="${PROJECT:-e2e-testing}" DESTROY="${DESTROY:-prompt}" EVACUATION_COUNTS="${EVACUATION_COUNTS:-1}" @@ -173,15 +174,15 @@ apply() { # 2025-04-01T14:38:07.671+0000 7f8284b55640 -1 librbd::image::OpenRequest: failed to find snapshot readonly # 2025-04-01T14:38:07.671+0000 7f826bfff640 -1 librbd::image::CloneRequest: 0x55b8434ed7c0 handle_open_parent: failed to open parent image: (2) No such file or directory # rbd: clone error: (2) No such file or directory) - retry terraform apply -auto-approve -var "remote=${REMOTE}" + retry terraform apply -auto-approve -var "remote=${REMOTE}" -var "project=${PROJECT}" } destroy() { echo "terraform destroy" if [ "${DESTROY}" = "prompt" ]; then - terraform destroy -var "remote=${REMOTE}" + terraform destroy -var "remote=${REMOTE}" -var "project=${PROJECT}" elif [ "${DESTROY}" = "yes" ]; then - terraform destroy -auto-approve -var "remote=${REMOTE}" + terraform destroy -auto-approve -var "remote=${REMOTE}" -var "project=${PROJECT}" elif [ "${DESTROY}" = "no" ] ; then echo "Skipping destroy" else @@ -199,12 +200,12 @@ setup() { } instanceList() { - lxc list --project e2e-testing -f csv -c n "${REMOTE}:" + lxc list --project "${PROJECT}" -f csv -c n "${REMOTE}:" } instanceListByMember() { local member="${1}" - lxc list --project e2e-testing -f csv -c n "${REMOTE}:" location="${member}" + lxc list --project "${PROJECT}" -f csv -c n "${REMOTE}:" location="${member}" } clusterMembers() { @@ -212,7 +213,7 @@ clusterMembers() { [ "${state}" = "ALL" ] && state=".*" # XXX: cannot use the column number for the member state as if multiple # roles are provided, the column number will change - lxc cluster list --project e2e-testing -f csv "${REMOTE}:" | awk -F, "/,${state},/ {print \$1}" + lxc cluster list --project "${PROJECT}" -f csv "${REMOTE}:" | awk -F, "/,${state},/ {print \$1}" } randomPick() { @@ -225,7 +226,7 @@ randomPick() { getIPv4() { local instance="${1}" - lxc list --project e2e-testing -f csv -c 4 "${REMOTE}:" name="${instance}" | cut -d" " -f1 + lxc list --project "${PROJECT}" -f csv -c 4 "${REMOTE}:" name="${instance}" | cut -d" " -f1 } checkConnectivity() { @@ -242,7 +243,7 @@ checkConnectivity() { dstIPv4="$(getIPv4 "${dst}")" echo -n " ● ${src} => ${dst} (${dstIPv4}):" - lxc exec --project e2e-testing "${REMOTE}:${src}" -- bash -c "grep -qm1 ^SSH- < /dev/tcp/${dstIPv4}/22" + lxc exec --project "${PROJECT}" "${REMOTE}:${src}" -- bash -c "grep -qm1 ^SSH- < /dev/tcp/${dstIPv4}/22" } # Use the retry function in case the target isn't yet reachable. @@ -275,7 +276,7 @@ evacuation() { for member in $(clusterMembers ONLINE); do echo "Evacuating ${member}" - lxc cluster evacuate --project e2e-testing --force "${REMOTE}:${member}" + lxc cluster evacuate --project "${PROJECT}" --force "${REMOTE}:${member}" echo "Waiting for ${member} to be rebooted" if [ -x "./reboot" ]; then @@ -288,7 +289,7 @@ evacuation() { read -rp "Press enter to continue with the evacuation test" fi - lxc cluster restore --project e2e-testing --force "${REMOTE}:${member}" + lxc cluster restore --project "${PROJECT}" --force "${REMOTE}:${member}" echo -n "Allow time for stopped instances on ${member} to start back again and have an IPv4 address" for instance in $(instanceListByMember "${member}"); do diff --git a/test/e2e/terraform.tfvars.custom-image-server.example b/test/e2e/terraform.tfvars.custom-image-server.example new file mode 100644 index 000000000..4dd0b7f44 --- /dev/null +++ b/test/e2e/terraform.tfvars.custom-image-server.example @@ -0,0 +1,23 @@ +# LXD remote corresponding to the MicroCloud deployment to test. +remote = "mc" + +# LXD remote to fetch source images from +image_remote = "pcr-d" + +# LXD project to cache source images into. Only needed when `project` has features.images=false +# (here, canonical-testing shares images with default). +image_project = "default" + +# Whether to copy the source image's aliases to the cached image copy +# Set to true if image_remote's images carry aliases +copy_image_aliases = false + +# LXD project/profile to use. By default, a project and profile named e2e-testing +# are created. Can be configured to reuse an existing project/profile by providing +# their names here; the project and profile will be created only if they do not already exist. +project = "canonical-testing" +profile = "canonical-testing-profile" + +# Number of containers/VMs to create per cluster member. +# containers_per_host = 9 +# vms_per_host = 3 diff --git a/test/e2e/variables.tf b/test/e2e/variables.tf index 1da802b10..aab6f9e0c 100644 --- a/test/e2e/variables.tf +++ b/test/e2e/variables.tf @@ -7,6 +7,36 @@ variable "remote" { } } +variable "image_remote" { + description = "LXD remote to fetch images from" + type = string + default = "ubuntu-minimal-daily" +} + +variable "image_project" { + description = "LXD project to cache source images into. Defaults to `project`. Set to \"default\" when `project` has features.images=false and shares images with another project instead" + type = string + default = null +} + +variable "copy_image_aliases" { + description = "Whether to copy the source image's aliases to the cached image copy. Set to true if image_remote's images carry aliases" + type = bool + default = false +} + +variable "project" { + description = "LXD project to use for e2e testing resources" + type = string + default = "e2e-testing" +} + +variable "profile" { + description = "LXD profile to use for e2e testing instances" + type = string + default = "e2e-testing" +} + variable "containers_per_host" { description = "Number of containers per host" type = number