From 2f7dd5d7bd79f7a4281d196bd1545a2b2539575d Mon Sep 17 00:00:00 2001 From: mloviska Date: Fri, 26 Aug 2022 14:56:36 +0200 Subject: [PATCH 1/3] Dump initramfs sosreport to console In case of provisioning tools (ignition, combustion) errors, users need to inspect `/run/initramfs/sosreport.txt` --- tests/microos/disk_boot.pm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/microos/disk_boot.pm b/tests/microos/disk_boot.pm index 3ce0e27106fd..5473295d286e 100644 --- a/tests/microos/disk_boot.pm +++ b/tests/microos/disk_boot.pm @@ -31,4 +31,12 @@ sub test_flags { return {fatal => 1}; } +sub post_fail_hook { + assert_screen 'emergency-mode'; + send_key 'ret'; + enter_cmd "echo '##### initramfs logs #####'> /dev/$serialdev"; + script_run "cat /run/initramfs/rdsosreport.txt > /dev/$serialdev "; + enter_cmd "echo '##### END #####'> /dev/$serialdev"; +} + 1; From 8eb0a8101784c3782ed3c1443d0d9827e42ea647 Mon Sep 17 00:00:00 2001 From: mloviska Date: Fri, 26 Aug 2022 14:59:41 +0200 Subject: [PATCH 2/3] Add configuration for ignition In order to test ignition provisioning alone, it would be better to upload the butane's config into repo. As of now, this version of config tests additional features: 1) creates a users with and home and without 2) creates a test directory and text file 3) sets hostname 4) creates `/home` on separate `xfs` partition 5) enables `sshd` 6) enables custom oneshot systemd service --- data/microos/butane/config.fcc | 67 +++++++ schedule/sle-micro/raw_image.yaml | 6 +- tests/microos/verify_setup.pm | 302 ++++++++++++++++++++++++++++++ 3 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 data/microos/butane/config.fcc create mode 100644 tests/microos/verify_setup.pm diff --git a/data/microos/butane/config.fcc b/data/microos/butane/config.fcc new file mode 100644 index 000000000000..ec301bf9f14e --- /dev/null +++ b/data/microos/butane/config.fcc @@ -0,0 +1,67 @@ +variant: fcos +version: 1.1.0 +passwd: + users: + - name: root + password_hash: $6$eEm2HpuzI7dfE4i7$dbYiTRLhrqVvwryR7zmMEcnrp13IqZ3mzLbsx9EeHAX7849PibGVgX5vdPuaeYYIO7hVfcboI9/JDpGiDZhHf/ + - name: bernhard + password_hash: $6$eEm2HpuzI7dfE4i7$dbYiTRLhrqVvwryR7zmMEcnrp13IqZ3mzLbsx9EeHAX7849PibGVgX5vdPuaeYYIO7hVfcboI9/JDpGiDZhHf/ + uid: 1001 + gecos: Bernhard M. Wiedemann + - name: HomelessTester + uid: 2002 + no_create_home: true + primary_group: geekos + groups: + - users + - geekos + groups: + - name: geekos + gid: 2002 +systemd: + units: + - name: sshd.service + enabled: true + mask: false + - name: create_test_file.service + enabled: true + contents: | + [Unit] + Description=Just a Test! + [Service] + Type=oneshot + RemainAfterExit=no + ExecStart=/usr/bin/touch /var/log/flagfile + [Install] + WantedBy=multi-user.target +storage: + disks: + - device: /dev/vdc + wipe_table: true + partitions: + - number: 1 + label: testing_part + filesystems: + - path: /home + device: /dev/disk/by-partlabel/testing_part + format: ext4 + wipe_filesystem: true + with_mount_unit: true + label: home + files: + - path: /etc/hostname + overwrite: true + contents: + inline: cucaracha + - path: /home/bernhard/testdir/hello + overwrite: true + mode: 0600 + user: + name: bernhard + contents: + inline: Hello there! + directories: + - path: /home/bernhard/testdir + mode: 0755 + user: + name: bernhard diff --git a/schedule/sle-micro/raw_image.yaml b/schedule/sle-micro/raw_image.yaml index 1bd7b09c55d7..d6c0877efe08 100644 --- a/schedule/sle-micro/raw_image.yaml +++ b/schedule/sle-micro/raw_image.yaml @@ -46,7 +46,10 @@ conditional_schedule: - microos/disk_boot 'combustion+ignition': - microos/disk_boot - + image_customization: + TEST_PROVISIONING: + '1': + - microos/verify_setup schedule: - '{{boot}}' - transactional/host_config @@ -56,6 +59,7 @@ schedule: - '{{maintenance}}' - '{{selinux}}' - '{{k3s}}' + - '{{image_customization}}' - microos/networking - microos/libzypp_config - microos/image_checks diff --git a/tests/microos/verify_setup.pm b/tests/microos/verify_setup.pm new file mode 100644 index 000000000000..0269120126be --- /dev/null +++ b/tests/microos/verify_setup.pm @@ -0,0 +1,302 @@ +# SUSE's openQA tests +# +# Copyright 2022 SUSE LLC +# SPDX-License-Identifier: FSFAP + +# Summary: Basic check for ignition/combustion setup +# Test module expects configured image according to +# data/microos/butane/config.fcc either by combustion +# or ignition +# Maintainer: qa-c team + +use Mojo::Base qw(opensusebasetest); +use testapi; +use utils qw(systemctl); +use YAML::PP; +use File::Basename qw(basename); + +my $data; +my $fail = 0; + +sub load_test_data { + my $ypp = YAML::PP->new(); + $data = $ypp->load_file(sprintf("%s/data/microos/butane/config.fcc", get_var('CASEDIR'))); + # remove butane metadata + delete $data->{version}; + delete $data->{variant}; +} + +sub get_test_object { + my ($entry, $test_object) = @_; + return $data->{$entry}{$test_object}; +} + +sub print_summary { + my $tests = shift; + my (@errors) = @_; + + if (@errors) { + record_info('Fail', join("\n", @errors), result => 'fail'); + $fail = 1; + } else { + record_info("OK", "All $tests passed!"); + } +} + +sub systemd_tests { + my $units = get_test_object('systemd', 'units'); + my @errors = (); + + if ($units) { + record_info('systemd', 'Checking system setup'); + } else { + record_info('SKIP', 'Skiping systemd tests!'); + return; + } + + foreach my $unit (@$units) { + my $name = $unit->{name}; + systemctl("is-enabled $name", expect_false => !$unit->{enabled}); + systemctl("is-active $name", expect_false => ($name =~ /sshd/) ? 0 : 1); + + if (exists $unit->{contents}) { + if (script_run("grep 'Just a Test!' /etc/systemd/system/$name") != 0) { + push @errors, "$units->{name} config file was not created as expected"; + } + + if (script_run('test -e /var/log/flagfile') != 0) { + push @errors, 'Oneshort test service is missing expected data'; + } + } + } + + print_summary('systemd', @errors); +} + +sub disk_tests { + my $disks = get_test_object('storage', 'disks'); + my $filesystems = get_test_object('storage', 'filesystems'); + my $partitions = (); + my @errors = (); + + if ($disks && $filesystems) { + record_info('drives & fs', 'Checking filesystem and drives setup'); + } else { + record_info('SKIP', 'Skipping filesystem and drives setup'); + return; + } + + foreach my $disk (@$disks) { + # maps label => partition + foreach my $p (@{$disk->{partitions}}) { + $partitions->{$p->{label}} = $disk->{device} . $p->{number}; + } + + } + + foreach my $fs (@$filesystems) { + if (exists $fs->{device}) { + delete $fs->{wipe_filesystem}; + + my $label = basename($fs->{device}); + if (script_run("readlink -e $fs->{device} | grep $partitions->{$label}")) { + push @errors, "Partition label $label is assigned to wrong partition or drive"; + } + delete $fs->{device}; + + if (exists $fs->{with_mount_unit} && $fs->{with_mount_unit} == 1 && + script_run("systemd-mount --no-legend --no-pager --list | grep $partitions->{$label}")) { + push @errors, "Partition $partitions->{$label} was not mounted by systemd"; + } + delete $fs->{with_mount_unit}; + + if (exists $fs->{label} && script_run("blkid --label $fs->{label}")) { + push @errors, "Drive by label $fs->{label} was not found"; + } + delete $fs->{label}; + + my $out = script_output("findmnt --noheadings $partitions->{$label}", proceed_on_failure => 1); + if ($out !~ $fs->{format}) { + push @errors, "Partition $partitions->{$label} was not formatted as $fs->{format}"; + } + if ($out !~ $fs->{path}) { + push @errors, "Partition $partitions->{$label} was not mounted in $fs->{path}"; + } + } + } + + print_summary('drives & fs', @errors); +} + +sub user_tests { + my $users = get_test_object('passwd', 'users'); + my @errors = (); + + if ($users) { + record_info('users', 'Checking users setup'); + } else { + record_info('SKIP', 'Skiping users tests!'); + return; + } + + foreach my $user (@$users) { + my $home; + if (exists $user->{home_dir}) { + $home = $user->{home_dir}; + } elsif ($user->{name} eq 'root') { + $home = '/root'; + } else { + $home = sprintf('/home/%s', $user->{name}); + } + + if (script_run("test -d $home") != 0 && !(exists $user->{no_create_home})) { + push @errors, "$user->{name}'s home directory was wrongly set"; + } + + if (exists $user->{password_hash}) { + my $match = join(':', $user->{name}, $user->{password_hash}); + if (script_run("grep '$match' /etc/shadow") != 0) { + push @errors, "$user->{name}'s password is not correct!"; + } + } + + if (exists $user->{uid} && script_run("id -u $user->{uid} -n | grep $user->{name}") != 0) { + push @errors, "$user->{name}'s UID is not correct!"; + } + + if (exists $user->{groups}) { + foreach my $group (@{$user->{groups}}) { + if (script_run("id $user->{name} -Gn | grep $group") != 0) { + push @errors, "$user->{name} is missing $group"; + } + } + } + + if (exists $user->{gecos} && + script_run("grep -e '^$user->{name}.*$user->{gecos}' /etc/passwd") != 0) { + push @errors, "$user->{name}'s GECOS is not correct!"; + } + } + + print_summary('users', @errors); +} + +sub group_tests { + my $groups = get_test_object('passwd', 'groups'); + my $etc_group = script_output('cat /etc/group'); + my @errors = (); + + if ($groups) { + record_info('groups', 'checking groups setup'); + } else { + record_info('SKIP', 'Skiping groups tests!'); + return; + } + + foreach my $group (@$groups) { + push my @settings, $group->{name}; + push @settings, '.*'; + push @settings, $group->{gid} if exists $group->{gid}; + + my $match = join(':', @settings); + if ($etc_group !~ /$match/) { + die "Missing group settings for $group->{name}"; + } + } + + print_summary('groups', @errors); +} + +sub directory_tests { + my $directories = get_test_object('storage', 'directories'); + my @errors = (); + + if ($directories) { + record_info('directories', 'checking directories setup'); + } else { + record_info('SKIP', 'Skiping directories tests!'); + return; + } + + foreach my $dir (@$directories) { + if (script_run("test -e $dir->{path}") != 0) { + record_info('Missing', "File $dir->{path} has not been created!", result => 'fail'); + push @errors, "Directory $dir->{path} does not exist"; + next; + } + + my $dir_data = script_output("stat --format='%F %U %a' $dir->{path}", proceed_on_failure => 1); + + if ($dir_data !~ /directory/) { + push @errors, "$dir->{path} is not a directory"; + } + + if ($dir_data !~ /$dir->{user}->{name}/) { + push @errors, "$dir->{path} is not owned by $dir->{user}->{name}"; + } + + if ($dir_data !~ /$dir->{mode}/) { + push @errors, "$dir->{path}'s permissions are not set to $dir->{mode}"; + } + } + + print_summary('directories', @errors); +} + +sub file_tests { + my $files = get_test_object('storage', 'files'); + my @errors = (); + + if ($files) { + record_info('files', 'checking files setup'); + } else { + record_info('SKIP', 'Skiping files tests!'); + return; + } + + foreach my $file (@$files) { + if (script_run("test -e $file->{path}") != 0) { + record_info('Missing', "File $file->{path} has not been created!", result => 'fail'); + push @errors, "File $file->{path} does not exist"; + next; + } + + my $file_data = script_output("stat --format='%F %U %a' $file->{path}", proceed_on_failure => 1); + + if ($file_data !~ /regular file/) { + push @errors, "$file->{path} is not a file"; + } + + if (exists $file->{user} && $file_data !~ /$file->{user}->{name}/) { + push @errors, "$file->{path} is not owned by $file->{user}->{name}"; + } + + if (exists $file->{mode} && $file_data !~ /$file->{mode}/) { + push @errors, "$file->{path}'s permissions are not set to $file->{mode}"; + } + + if (exists $file->{contents} && + script_run("grep '$file->{contents}->{inline}' $file->{path}")) { + push @errors, "$file->{path}'s does not contain expected string: $file->{contents}->{inline}"; + } + } + + print_summary('file_tests', @errors); +} + +sub run { + my $self = shift; + $self->select_serial_terminal(); + load_test_data(); + + systemd_tests(); + user_tests(); + group_tests(); + directory_tests(); + file_tests(); + disk_tests(); + + $self->result('failure') if $fail; +} + +1; From ef71e25c720b51a5ce5bb98f79c1cd7c45a48dac Mon Sep 17 00:00:00 2001 From: mloviska Date: Thu, 22 Sep 2022 17:27:02 +0200 Subject: [PATCH 3/3] Add Combustion script for SLEM --- data/microos/butane/script | 81 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100755 data/microos/butane/script diff --git a/data/microos/butane/script b/data/microos/butane/script new file mode 100755 index 000000000000..83e7bd3fc867 --- /dev/null +++ b/data/microos/butane/script @@ -0,0 +1,81 @@ +#!/bin/bash +# combustion: network +# Redirect output to the console +exec > >(exec tee -a /dev/console) 2>&1 +set -eux + +# +### drives and filesystems +# +DRIVE='/dev/vdc' +PART='/dev/vdc1' + +# create partition and filesystem on additional drive +echo "label: gpt" | sfdisk "$DRIVE" +echo "name=testing_part" | sfdisk /dev/vdc -N1 + +# label new partition for testing and create labeled ext4 +mkfs.ext4 -L home "$PART" + +# mount new partition +mount -t ext4 "$PART" /home + +# DEBUG +blkid +lsblk + +# +### users and groups +# groups +groupadd --gid 2002 geekos + +# users +echo 'root:$6$eEm2HpuzI7dfE4i7$dbYiTRLhrqVvwryR7zmMEcnrp13IqZ3mzLbsx9EeHAX7849PibGVgX5vdPuaeYYIO7hVfcboI9/JDpGiDZhHf/' | chpasswd -e +useradd --no-create-home --uid 2002 --gid geekos --groups users HomelessTester +useradd --create-home --uid 1001 --comment "Bernhard M. Wiedemann" --no-user-group --gid users bernhard +echo 'bernhard:$6$eEm2HpuzI7dfE4i7$dbYiTRLhrqVvwryR7zmMEcnrp13IqZ3mzLbsx9EeHAX7849PibGVgX5vdPuaeYYIO7hVfcboI9/JDpGiDZhHf/' | chpasswd -e + +# +### files and directories +# +echo "cucaracha" > /etc/hostname +mkdir --mode=0755 /home/bernhard/testdir +chown bernhard:users /home/bernhard/testdir +echo "Hello there!" > /home/bernhard/testdir/hello +chown bernhard:users /home/bernhard/testdir/hello +chmod 0600 /home/bernhard/testdir/hello + +# +### systemd units +# +cat << EOF > /etc/systemd/system/create_test_file.service +[Unit] +Description=Just a Test! +[Service] +Type=oneshot +RemainAfterExit=no +ExecStart=/usr/bin/touch /var/log/flagfile +[Install] +WantedBy=multi-user.target +EOF + +cat << EOF > /etc/systemd/system/home.mount +[Unit] +Before=local-fs.target +Requires=systemd-fsck@dev-disk-by\x2dpartlabel-testing_part.service +After=systemd-fsck@dev-disk-by\x2dpartlabel-testing_part.service + +[Mount] +Where=/home +What=/dev/disk/by-partlabel/testing_part +Type=ext4 + +[Install] +RequiredBy=local-fs.target +EOF + +systemctl enable sshd.service +systemctl enable create_test_file.service + +echo Combustion was here > /usr/share/combustion-welcome +curl conncheck.opensuse.org