diff --git a/scripts/set b/scripts/set index bb16d5e2..297c04ea 100755 --- a/scripts/set +++ b/scripts/set @@ -57,9 +57,6 @@ else esac fi -perl_line="\$yaml->{${option//./\}->\{}}=${value};" -log_debug "perl line: ${perl_line}" - tmp=$(mktemp) log_debug "Temporary perl file: ${tmp}" @@ -69,8 +66,34 @@ use warnings; use YAML::XS; my \$file="${file}"; my \$yaml = YAML::XS::LoadFile("\$file"); - -${perl_line} +my \$valref = \\\$yaml; + +for my \$component (split(/\\./, "${option}")) { + if (ref(\$valref) eq 'SCALAR' && defined(\$\$valref)) { + print STDERR ("Key resolved to a SCALAR at '\$component'; cannot continue.\n"); + exit(1); + } + elsif (\$component =~ /^\\d+\$/ && (!defined(\$\$valref) || ref(\$\$valref) eq 'ARRAY')) { + \$valref = \\\$\$valref->[\$component]; + } + elsif (\$component eq 'APPEND' && ref(\$\$valref) eq 'ARRAY') { + \$valref = \\\$\$valref->[\$#{\$\$valref}+1]; + } + elsif (!defined(\$\$valref) || ref(\$\$valref) eq 'HASH') { + \$valref = \\\$\$valref->{\$component}; + } + else { + print STDERR ("Key resolved to a " . ref(\$\$valref) . " reference; refusing to overwrite.\n"); + exit(1); + } +} +if (!defined(\$\$valref) || ref(\$valref) eq 'SCALAR') { + \$\$valref = ${value}; +} +else { + print STDERR ("Key resolved to a " . ref(\$\$valref) . " reference; refusing to overwrite.\n"); + exit(1); +} YAML::XS::DumpFile(\$file, \$yaml); EOF diff --git a/t/test_ngcpcfg_set_and_del.py b/t/test_ngcpcfg_set_and_del.py index a4bb1eee..29a70be1 100644 --- a/t/test_ngcpcfg_set_and_del.py +++ b/t/test_ngcpcfg_set_and_del.py @@ -235,6 +235,200 @@ foo: assert out.returncode == 0 +@pytest.mark.tt_33030 +def test_set_action_set_array_element(ngcpcfgcli, tmpdir): + tmpfile = tmpdir.join("tmpfile.txt") + tmpfile.write('''--- +foo: + bar: + - 'blah' + - 'baz' +''') + out = ngcpcfgcli("set", str(tmpfile), "foo.bar.0=\'moo\'") + assert tmpfile.read() == '''--- +foo: + bar: + - moo + - baz +''' + assert "" in out.stdout + assert "" in out.stderr + assert out.returncode == 0 + + +@pytest.mark.tt_33030 +def test_set_action_append_array_element(ngcpcfgcli, tmpdir): + tmpfile = tmpdir.join("tmpfile.txt") + tmpfile.write('''--- +foo: + bar: + - blah + - baz +''') + out = ngcpcfgcli("set", str(tmpfile), "foo.bar.2=\'moo\'") + assert tmpfile.read() == '''--- +foo: + bar: + - blah + - baz + - moo +''' + assert "" in out.stdout + assert "" in out.stderr + assert out.returncode == 0 + + +@pytest.mark.tt_33030 +def test_set_action_auto_append_array_element(ngcpcfgcli, tmpdir): + tmpfile = tmpdir.join("tmpfile.txt") + tmpfile.write('''--- +foo: + bar: + - blah + - baz +''') + out = ngcpcfgcli("set", str(tmpfile), "foo.bar.APPEND=\'moo\'") + assert tmpfile.read() == '''--- +foo: + bar: + - blah + - baz + - moo +''' + assert "" in out.stdout + assert "" in out.stderr + assert out.returncode == 0 + + +@pytest.mark.tt_33030 +def test_set_action_add_new_array_one_element(ngcpcfgcli, tmpdir): + tmpfile = tmpdir.join("tmpfile.txt") + tmpfile.write('''--- +foo: + moo: blah +''') + out = ngcpcfgcli("set", str(tmpfile), "foo.bar.0=\'boo\'") + assert tmpfile.read() == '''--- +foo: + bar: + - boo + moo: blah +''' + assert "" in out.stdout + assert "" in out.stderr + assert out.returncode == 0 + + +@pytest.mark.tt_33030 +def test_set_action_set_array_subelement(ngcpcfgcli, tmpdir): + tmpfile = tmpdir.join("tmpfile.txt") + tmpfile.write('''--- +foo: + bar: + - blah: quux + foo: meh + - blah: zarg + foo: ruuk + moo: blah +''') + out = ngcpcfgcli("set", str(tmpfile), "foo.bar.1.foo=\'boo\'") + assert tmpfile.read() == '''--- +foo: + bar: + - blah: quux + foo: meh + - blah: zarg + foo: boo + moo: blah +''' + assert "" in out.stdout + assert "" in out.stderr + assert out.returncode == 0 + + +@pytest.mark.tt_33030 +def test_set_action_set_hash_instead_of_array(ngcpcfgcli, tmpdir): + tmpfile = tmpdir.join("tmpfile.txt") + tmpfile.write('''--- +foo: + bar: + - blah: quux + foo: meh + - blah: zarg + foo: ruuk + moo: blah +''') + out = ngcpcfgcli("set", str(tmpfile), "foo.bar.baz=123") + assert tmpfile.read() == '''--- +foo: + bar: + - blah: quux + foo: meh + - blah: zarg + foo: ruuk + moo: blah +''' + assert "" in out.stdout + assert "Key resolved to a ARRAY reference; refusing to overwrite." \ + in out.stderr + assert out.returncode == 1 + + +@pytest.mark.tt_33030 +def test_set_action_set_array_instead_of_scalar(ngcpcfgcli, tmpdir): + tmpfile = tmpdir.join("tmpfile.txt") + tmpfile.write('''--- +foo: + bar: + - blah: quux + foo: meh + - blah: zarg + foo: ruuk + moo: blah +''') + out = ngcpcfgcli("set", str(tmpfile), "foo.bar=123") + assert tmpfile.read() == '''--- +foo: + bar: + - blah: quux + foo: meh + - blah: zarg + foo: ruuk + moo: blah +''' + assert "" in out.stdout + assert "Key resolved to a ARRAY reference; refusing to overwrite." \ + in out.stderr + assert out.returncode == 1 + + +@pytest.mark.tt_33030 +def test_set_action_set_hash_instead_of_scalar(ngcpcfgcli, tmpdir): + tmpfile = tmpdir.join("tmpfile.txt") + tmpfile.write('''--- +foo: + bar: + - blah: quux + foo: meh + - blah: zarg + foo: ruuk + moo: blah +''') + out = ngcpcfgcli("set", str(tmpfile), "foo=123") + assert tmpfile.read() == '''--- +foo: + bar: + - blah: quux + foo: meh + - blah: zarg + foo: ruuk + moo: blah +''' + assert "" in out.stdout + assert "Key resolved to a HASH reference; refusing to overwrite." \ + in out.stderr + assert out.returncode == 1 + ############################################################### # ngcpcfg del ###############################################################