From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id C7B161FF0C0 for ; Wed, 04 Nov 2026 20:06:33 +0100 (CET) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 88E2821740; Wed, 04 Nov 2026 20:05:23 +0100 (CET) From: =?UTF-8?q?Michael=20K=C3=B6ppl?= To: pve-devel@lists.proxmox.com Subject: [PATCH e2e-tests 14/38] tests: container: verify lifecycle and guest setup Date: Wed, 4 Nov 2026 20:03:21 +0100 Message-ID: <20261104190345.2573464-15-m.koeppl@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20261104190345.2573464-1-m.koeppl@proxmox.com> References: <20261104190345.2573464-1-m.koeppl@proxmox.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1793819034552 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.225 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: UF5BAX7CXKYAP76JQJFTVHVZSR5WOGWU X-Message-ID-Hash: UF5BAX7CXKYAP76JQJFTVHVZSR5WOGWU X-MailFrom: m.koeppl@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Add test that covers creating, starting, shutting down, and destroying an unprivileged container. Also check that hostname, hosts, DNS, timezone, static network configuration are actually set up inside the guest. Signed-off-by: Michael Köppl --- tests/pve_ct_lifecycle.pl | 182 ++++++++++++++++++++++++++++++++++++++ tests/testcases.ron | 9 ++ 2 files changed, 191 insertions(+) create mode 100755 tests/pve_ct_lifecycle.pl diff --git a/tests/pve_ct_lifecycle.pl b/tests/pve_ct_lifecycle.pl new file mode 100755 index 0000000..2a2920e --- /dev/null +++ b/tests/pve_ct_lifecycle.pl @@ -0,0 +1,182 @@ +#!/usr/bin/perl + +=head1 NAME + +C - Test the lifecycle and guest setup of a container. + +=head1 DESCRIPTION + +This testcase creates an unprivileged container, starts it, shuts it down and +destroys it again. While it is running, it verifies that the hostname, hosts +file, DNS, timezone and static network configuration were set up inside the +guest, and that the container and the node can reach each other over vmbr0. + +=cut + +use strict; +use warnings; + +use Test::More; + +use lib '.'; +use Proxmox::Test::Helper qw(CONTAINER_TEMPLATE get_pve_instance); +use Proxmox::Test::SDN qw(ping_from_container); +use Proxmox::Test::Testcase; +use Proxmox::Test::Util qw(poll_until); + +my $pve = get_pve_instance('pve1'); +my $node = $pve->get_nodename(); +my $client = $pve->client(); + +my $ct_ip = '10.250.251.10'; +my $node_ip = '10.250.251.1'; + +my $test_container = { + vmid => $client->get('/cluster/nextid'), + node => $node, + ostemplate => CONTAINER_TEMPLATE, + rootfs => 'local-lvm:2', + storage => 'local-lvm', + memory => 256, + unprivileged => 1, + features => 'nesting=1', + hostname => 'ct-lifecycle', + searchdomain => 'e2e.test', + nameserver => '10.250.251.53', + timezone => 'Europe/Vienna', + net0 => "name=eth0,bridge=vmbr0,ip=$ct_ip/24", +}; + +my $vmid = $test_container->{vmid}; +my $rootfs_volid; + +Proxmox::Test::Testcase->new('pve_ct_lifecycle')->with([{}])->step( + 'create container', + sub { + my $upid = $client->post("/nodes/$node/lxc", $test_container); + $pve->task_ok($upid, "created container $vmid") + or die "creating container $vmid failed\n"; + }, + sub { + $pve->destroy_guest('lxc', $vmid); + }, +)->step( + 'verify container config', + sub { + my $conf = $client->get("/nodes/$node/lxc/$vmid/config", {}); + + is($conf->{hostname}, $test_container->{hostname}, 'config has hostname'); + is($conf->{unprivileged}, 1, 'config marks container as unprivileged'); + is($conf->{memory}, $test_container->{memory}, 'config has memory'); + is($conf->{features}, $test_container->{features}, 'config has features'); + is( + $conf->{searchdomain}, $test_container->{searchdomain}, 'config has search domain', + ); + is($conf->{nameserver}, $test_container->{nameserver}, 'config has nameserver'); + is($conf->{timezone}, $test_container->{timezone}, 'config has timezone'); + + like($conf->{rootfs} // '', qr/^local-lvm:/, 'rootfs is allocated on local-lvm'); + my %net0 = map { split(/=/, $_, 2) } split(/,/, $conf->{net0} // ''); + is($net0{name}, 'eth0', 'net0 has interface name'); + is($net0{bridge}, 'vmbr0', 'net0 has bridge'); + is($net0{ip}, "$ct_ip/24", 'net0 has static address'); + + ($rootfs_volid) = ($conf->{rootfs} // '') =~ m/^([^,]+)/; + }, +)->step( + 'start container', + sub { + $pve->start_guest('lxc', $vmid); + pass("container $vmid reports status 'running'"); + }, +)->step( + 'add node address on vmbr0', + sub { + $pve->run_command("ip addr add $node_ip/24 dev vmbr0"); + }, + sub { + $pve->run_command("ip addr del $node_ip/24 dev vmbr0 2>/dev/null; true"); + }, +)->step( + 'verify guest setup', + sub { + my $hostname = $test_container->{hostname}; + my $searchdomain = $test_container->{searchdomain}; + my $nameserver = $test_container->{nameserver}; + my $timezone = $test_container->{timezone}; + + is($pve->ct_exec($vmid, 'cat /etc/hostname'), "$hostname\n", '/etc/hostname is set up'); + like( + $pve->ct_exec($vmid, 'cat /etc/hosts'), + qr/^\Q$ct_ip\E\s+\Q$hostname.$searchdomain\E\s+\Q$hostname\E\s*$/m, + '/etc/hosts maps the container address to its hostname', + ); + + my $resolv = $pve->ct_exec($vmid, 'cat /etc/resolv.conf'); + like($resolv, qr/^search \Q$searchdomain\E$/m, + '/etc/resolv.conf has the search domain'); + like($resolv, qr/^nameserver \Q$nameserver\E$/m, '/etc/resolv.conf has the nameserver'); + + is( + $pve->ct_exec($vmid, 'readlink /etc/localtime'), + "/usr/share/zoneinfo/$timezone\n", + '/etc/localtime points to the configured timezone', + ); + }, +)->step( + 'verify network connectivity', + sub { + # The static address can take a bit of time after the start task has finished, as the + # static address is applied by the container during boot. + poll_until( + sub { + $pve->ct_exec($vmid, 'ip -4 -o addr show dev eth0') =~ + m/\binet \Q$ct_ip\E\/24\b/; + }, + timeout => 60, + interval => 2, + description => "eth0 in container $vmid to get $ct_ip", + ); + pass("eth0 in container $vmid has $ct_ip"); + + ping_from_container($test_container, $node_ip, 5); + + poll_until( + sub { $pve->node_can_ping($ct_ip) }, + timeout => 30, + interval => 3, + description => "container $vmid to be pingable from the node", + ); + ok($pve->node_can_ping($ct_ip), "node can ping container $vmid"); + }, +)->step( + 'shutdown container', + sub { + my $upid = $client->post("/nodes/$node/lxc/$vmid/status/shutdown", { timeout => 60 }); + $pve->task_ok($upid, "shut down container $vmid") + or die "shutting down container $vmid failed\n"; + + is( + $client->get("/nodes/$node/lxc/$vmid/status/current", {})->{status}, + 'stopped', + "container $vmid reports status 'stopped' after shutdown", + ); + }, +)->step( + 'destroy container', + sub { + my $upid = $client->delete("/nodes/$node/lxc/$vmid", {}); + $pve->task_ok($upid, "destroyed container $vmid") + or die "destroying container $vmid failed\n"; + + ok(!$pve->guest_exists('lxc', $vmid), "container $vmid is gone"); + + my $content = $client->get("/nodes/$node/storage/local-lvm/content", { vmid => $vmid }); + ok( + defined($rootfs_volid) && !grep({ $_->{volid} eq $rootfs_volid } $content->@*), + "rootfs volume of container $vmid is gone", + ); + }, +)->run(); + +done_testing(); diff --git a/tests/testcases.ron b/tests/testcases.ron index eac2926..391eb09 100644 --- a/tests/testcases.ron +++ b/tests/testcases.ron @@ -20,6 +20,15 @@ TestcaseConfig( ], tag: "basic", ), + Testcase( + name: "pve_ct_lifecycle", + run: "./tests/pve_ct_lifecycle.pl", + timeout: 300, + instances: [ + "pve1", + ], + tag: "basic", + ), Testcase( name: "pve_ldap_realm", run: "./tests/ldap_realm.pl", -- 2.47.3