fix #7173: config: drop empty tag list when writing
When the last tag of a container gets removed, for example in the web UI, the API gets an empty 'tags' value, which then ends up as an empty 'tags:' line in the config and in the output of 'pct config'. As the config parser reads that line back as a single space, it also gets written out again on every following config change. Skip a tag list without any tag when writing the config, which also cleans up existing configs with such a line on their next change. Keep it in the pending section, where it is still needed for removing all tags with a pending change. Add a basic test for parsing and writing container configs, similar to the one in qemu-server, to cover this. Signed-off-by: Michal Fox <me@dualfroz.com>
This commit is contained in:
1 parent
8275178ff1
commit
96e2453797
6 files changed
+83
-3
No files matched your search
@@ -1243,7 +1243,7 @@ sub write_pct_config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
my $generate_raw_config = sub {
|
my $generate_raw_config = sub {
|
||||||
my ($conf) = @_;
|
my ($conf, $pending) = @_;
|
||||||
|
|
||||||
my $raw = '';
|
my $raw = '';
|
||||||
|
|
||||||
@@ -1265,6 +1265,8 @@ sub write_pct_config {
|
|||||||
my $value = $conf->{$key};
|
my $value = $conf->{$key};
|
||||||
die "detected invalid newline or carriage return inside property '$key'\n"
|
die "detected invalid newline or carriage return inside property '$key'\n"
|
||||||
if $value =~ m/[\r\n]/;
|
if $value =~ m/[\r\n]/;
|
||||||
|
# an empty tag list is only needed as pending change for removing all tags
|
||||||
|
next if $key eq 'tags' && !$pending && $value =~ m/^\s*$/;
|
||||||
$raw .= "$key: $value\n";
|
$raw .= "$key: $value\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1293,7 +1295,7 @@ sub write_pct_config {
|
|||||||
|
|
||||||
if (scalar(keys %{ $conf->{pending} })) {
|
if (scalar(keys %{ $conf->{pending} })) {
|
||||||
$raw .= "\n[pve:pending]\n";
|
$raw .= "\n[pve:pending]\n";
|
||||||
$raw .= &$generate_raw_config($conf->{pending});
|
$raw .= &$generate_raw_config($conf->{pending}, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach my $snapname (sort keys %{ $conf->{snapshots} }) {
|
foreach my $snapname (sort keys %{ $conf->{snapshots} }) {
|
||||||
|
|||||||
+4
-1
@@ -2,7 +2,7 @@ RUN_USERNS := lxc-usernsexec -m "u:0:`id -u`:1" -m "g:0:`id -g`:1" --
|
|||||||
|
|
||||||
all: test
|
all: test
|
||||||
|
|
||||||
test: test_setup test_snapshot test_bindmount test_idmap
|
test: test_setup test_snapshot test_bindmount test_idmap test_parse_config
|
||||||
|
|
||||||
test_setup: run_setup_tests.pl
|
test_setup: run_setup_tests.pl
|
||||||
if test -e /run/lock/sbuild; then \
|
if test -e /run/lock/sbuild; then \
|
||||||
@@ -24,5 +24,8 @@ test_bindmount: bindmount_test.pl
|
|||||||
test_idmap: run_idmap_tests.pl
|
test_idmap: run_idmap_tests.pl
|
||||||
./run_idmap_tests.pl
|
./run_idmap_tests.pl
|
||||||
|
|
||||||
|
test_parse_config: run_parse_config_tests.pl
|
||||||
|
./run_parse_config_tests.pl
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf tmprootfs
|
rm -rf tmprootfs
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# empty tag list
|
||||||
|
arch: amd64
|
||||||
|
cores: 1
|
||||||
|
hostname: notags
|
||||||
|
memory: 512
|
||||||
|
ostype: debian
|
||||||
|
rootfs: local-lvm:vm-8006-disk-0,size=8G
|
||||||
|
unprivileged: 1
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# empty tag list
|
||||||
|
arch: amd64
|
||||||
|
cores: 1
|
||||||
|
hostname: notags
|
||||||
|
memory: 512
|
||||||
|
ostype: debian
|
||||||
|
rootfs: local-lvm:vm-8006-disk-0,size=8G
|
||||||
|
tags:
|
||||||
|
unprivileged: 1
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# plain container
|
||||||
|
arch: amd64
|
||||||
|
cores: 1
|
||||||
|
hostname: plain
|
||||||
|
memory: 512
|
||||||
|
net0: name=eth0,bridge=vmbr0,hwaddr=BC:24:11:2C:69:EC,ip=dhcp,type=veth
|
||||||
|
ostype: debian
|
||||||
|
rootfs: local-lvm:vm-8006-disk-0,size=8G
|
||||||
|
swap: 512
|
||||||
|
tags: bar;foo
|
||||||
|
unprivileged: 1
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
|
||||||
|
# Tests parsing and writing container configuration files.
|
||||||
|
#
|
||||||
|
# If no expected file exists, the input is assumed to be equal to the expected output.
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use lib qw(..);
|
||||||
|
|
||||||
|
use Test::More;
|
||||||
|
|
||||||
|
use PVE::LXC::Config;
|
||||||
|
use PVE::Tools;
|
||||||
|
|
||||||
|
my $INPUT_DIR = './parse-config-input';
|
||||||
|
my $EXPECTED_DIR = './parse-config-expected';
|
||||||
|
|
||||||
|
PVE::Tools::dir_glob_foreach(
|
||||||
|
$INPUT_DIR,
|
||||||
|
'.*\.conf',
|
||||||
|
sub {
|
||||||
|
my ($file) = @_;
|
||||||
|
|
||||||
|
my $fake_config_fn = "$file/lxc/8006.conf";
|
||||||
|
my $input = PVE::Tools::file_get_contents("${INPUT_DIR}/${file}");
|
||||||
|
|
||||||
|
my $output = eval {
|
||||||
|
my $conf = PVE::LXC::Config::parse_pct_config($fake_config_fn, $input, 1);
|
||||||
|
PVE::LXC::Config::write_pct_config($fake_config_fn, $conf);
|
||||||
|
};
|
||||||
|
if (my $err = $@) {
|
||||||
|
note("got unexpected error '$err'");
|
||||||
|
fail($file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $expected_file = "${EXPECTED_DIR}/${file}";
|
||||||
|
my $expected =
|
||||||
|
-f $expected_file ? PVE::Tools::file_get_contents($expected_file) : $input;
|
||||||
|
|
||||||
|
is($output, $expected, $file);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
done_testing();
|
||||||
Reference in new issue
Block a user