diff --git a/src/packerlicious/builder.py b/src/packerlicious/builder.py index dbbb4a4..d8a1697 100644 --- a/src/packerlicious/builder.py +++ b/src/packerlicious/builder.py @@ -255,7 +255,7 @@ class AmazonChroot(PackerBuilder): 'ami_regions': ([str], False), 'ami_users': ([str], False), 'ami_virtualization_type': (str, False), - 'chroot_mounts': ([[str]], False), + 'chroot_mounts': (validator.jagged_array(str), False), 'command_wrapper': (str, False), 'copy_files': ([str], False), 'custom_endpoint_ec2': (str, False), @@ -1034,8 +1034,8 @@ class ParallelsIso(PackerBuilder): 'output_directory': (str, False), 'parallels_tools_guest_path': (str, False), 'parallels_tools_mode': (str, False), - 'prlctl': ([[str]], False), - 'prlctl_post': ([[str]], False), + 'prlctl': (validator.jagged_array(str), False), + 'prlctl_post': (validator.jagged_array(str), False), 'prlctl_version_file': (str, False), 'shutdown_command': (str, False), 'shutdown_timeout': (str, False), @@ -1068,8 +1068,8 @@ class ParallelsPvm(PackerBuilder): 'output_directory': (str, False), 'parallels_tools_guest_path': (str, False), 'parallels_tools_mode': (str, False), - 'prlctl': ([[str]], False), - 'prlctl_post': ([[str]], False), + 'prlctl': (validator.jagged_array(str), False), + 'prlctl_post': (validator.jagged_array(str), False), 'prlctl_version_file': (str, False), 'reassign_mac': (bool, False), 'shutdown_command': (str, False), @@ -1145,7 +1145,7 @@ class Qemu(PackerBuilder): 'net_device': (str, False), 'output_directory': (str, False), 'qemu_binary': (str, False), - 'qemuargs': ([[str]], False), + 'qemuargs': (validator.jagged_array(str), False), } def validate(self): @@ -1239,8 +1239,8 @@ class VirtualboxIso(PackerBuilder): 'ssh_host_port_min': (int, False), 'ssh_host_port_max': (int, False), 'ssh_skip_nat_mapping': (validator.boolean, False), - 'vboxmanage': ([[str]], False), - 'vboxmanage_post': ([[str]], False), + 'vboxmanage': (validator.jagged_array(str), False), + 'vboxmanage_post': (validator.jagged_array(str), False), 'virtualbox_version_file': (str, False), 'vm_name': (str, False), 'vrdp_bind_address': (str, False), @@ -1302,8 +1302,8 @@ class VirtualboxOvf(PackerBuilder): 'ssh_host_port_max': (int, False), 'ssh_skip_nat_mapping': (validator.boolean, False), 'target_path': (str, False), - 'vboxmanage': ([[str]], False), - 'vboxmanage_post': ([[str]], False), + 'vboxmanage': (validator.jagged_array(str), False), + 'vboxmanage_post': (validator.jagged_array(str), False), 'virtualbox_version_file': (str, False), 'vm_name': (str, False), 'vrdp_bind_address': (str, False), diff --git a/src/packerlicious/validator.py b/src/packerlicious/validator.py index 86df11a..14f4741 100644 --- a/src/packerlicious/validator.py +++ b/src/packerlicious/validator.py @@ -196,3 +196,27 @@ def string_list_item_checker(x): ', '.join(str(j) for j in allowed_values)) return string_list_item_checker + + +def jagged_array(expected_type): + def jagged_array_checker(x): + # ensure outer list is present + if not isinstance(x, list): + raise ValueError("%r is not a valid array of array of %r" % (x, expected_type)) + + try: + l = list(x) + for sub_list in l: + # ensure inner sublist also exists + if not isinstance(sub_list, list): + raise ValueError("%r is not a valid array of array of %r" % (x, expected_type)) + + for value in sub_list: + if not isinstance(value, expected_type): + raise TypeError + + return l + except(ValueError, TypeError): + raise ValueError("%r is not a valid array of array of %r" % (x, expected_type)) + + return jagged_array_checker diff --git a/src/thirdparty/troposphere/__init__.py b/src/thirdparty/troposphere/__init__.py index 54b8aed..5059d5a 100644 --- a/src/thirdparty/troposphere/__init__.py +++ b/src/thirdparty/troposphere/__init__.py @@ -160,17 +160,7 @@ def __setattr__(self, name, value): # type checks (as above accept AWSHelperFn because # we can't do the validation ourselves) for v in value: - # If we're expecting a list of list with the same type - if isinstance(v, list): - # Double check that it's a list of list - if not all(isinstance(elem, list) for elem in value): - self._raise_type(name, v, expected_type) - # Make sure all elements in list are of the expected_type[0][0] - else: - for elem in v: - if not isinstance(elem, expected_type[0][0]): - self._raise_type(name, elem, expected_type[0][0]) - elif not isinstance(v, tuple(expected_type)) \ + if not isinstance(v, tuple(expected_type)) \ and not isinstance(v, AWSHelperFn): self._raise_type(name, v, expected_type) # Validated so assign it diff --git a/tests/packerlicious/test_template.py b/tests/packerlicious/test_template.py index 7a50e91..8f62395 100644 --- a/tests/packerlicious/test_template.py +++ b/tests/packerlicious/test_template.py @@ -140,145 +140,64 @@ def test_variable_no_duplicate_entries(self): assert to_json == json.dumps(json.loads(expected_json), sort_keys=True, indent=2, separators=(',', ': ')) - - expected_json_1 = """ - { - "builders": [ - { - "boot_wait": "10s", - "floppy_files": [ - "" - ], - "guest_additions_path": "VBoxGuestAdditions_{{.Version}}.iso", - "guest_os_type": "Ubuntu_64", - "http_directory": "", - "iso_checksum": "sha512", - "iso_checksum_type": "sha512", - "iso_url": "", - "ssh_port": 22, - "type": "virtualbox-iso", - "vboxmanage": [ - [ - "modifyvm {{.Name}} --memory 1024" - ], - [ - "modifyvm {{.Name}} --cpus 1" - ] - ], - "virtualbox_version_file": ".vbox_version", - "vm_name": "" - } - ] - } - """ - expected_json_2 = """ - { - "builders": [ + def test_jagged_array_render(self): + expected_json = """ { - "boot_wait": "10s", - "floppy_files": [ - "" - ], - "guest_additions_path": "VBoxGuestAdditions_{{.Version}}.iso", - "guest_os_type": "Ubuntu_64", - "http_directory": "", - "iso_checksum": "sha512", - "iso_checksum_type": "sha512", - "iso_url": "", - "ssh_port": 22, - "type": "virtualbox-iso", - "vboxmanage": [ - [ - "modifyvm {{.Name}} --memory 1024" - ], - [ - "modifyvm", - "{{.Name}}", - "--cpus 1" - ] - ], - "virtualbox_version_file": ".vbox_version", - "vm_name": "" + "builders": [ + { + "boot_wait": "10s", + "floppy_files": [ + "" + ], + "guest_additions_path": "VBoxGuestAdditions_{{.Version}}.iso", + "guest_os_type": "Ubuntu_64", + "http_directory": "", + "iso_checksum": "sha512", + "iso_checksum_type": "sha512", + "iso_url": "", + "ssh_port": 22, + "type": "virtualbox-iso", + "vboxmanage": [ + [ + "modifyvm", "{{.Name}}", "--memory", "1024" + ], + [ + "modifyvm", "{{.Name}}", "--vram", "36" + ], + [ + "modifyvm", "{{.Name}}", "--cpus", "1" + ] + ], + "virtualbox_version_file": ".vbox_version", + "vm_name": "my_name" + } + ] } - ] - } - """ - assertion = 'type' if sys.version_info[0] < 3 else 'class' - exception_1 = ": None.vboxmanage is <" + assertion \ - + " 'list'>, expected [[<" + assertion + " 'str'>]]" - exception_2 = ": None.vboxmanage is <" + assertion\ - + " 'int'>, expected <" + assertion + " 'str'>" - @pytest.mark.parametrize('test_input,exception,expected', [ - pytest.param( - [['modifyvm {{.Name}} --memory 1024'], ['modifyvm {{.Name}} --cpus 1']], - False, - expected_json_1, - marks=pytest.mark.basic - ), - pytest.param( - [['modifyvm {{.Name}} --memory 1024'], ['modifyvm', '{{.Name}}', '--cpus 1']], - False, - expected_json_2, - marks=pytest.mark.basic - ), - pytest.param( - [['modifyvm {{.Name}} --memory 1024'], 1], - True, - exception_1, - marks=pytest.mark.basic - ), - pytest.param( - [['modifyvm {{.Name}} --memory 1024'], [1]], - True, - exception_2, - marks=pytest.mark.basic - ) - ]) - def test_list_of_list_type_check(self, test_input, exception, expected): + """ t = Template() - HTTP_DIR = "" - ISO_URL = "" - ISO_CHECKSUM_TYPE = "sha512" - ISO_CHECKSUM = "sha512" - VM_NAME = "" - VBOX_MANAGE=test_input - if exception: - with pytest.raises(TypeError) as excinfo: - t.add_builder( - builder.VirtualboxIso( - boot_wait="10s", - guest_os_type="Ubuntu_64", - http_directory=HTTP_DIR, - iso_url=ISO_URL, - iso_checksum_type=ISO_CHECKSUM_TYPE, - iso_checksum=ISO_CHECKSUM, - ssh_port=22, - guest_additions_path="VBoxGuestAdditions_{{.Version}}.iso", - virtualbox_version_file=".vbox_version", - vm_name=VM_NAME, - floppy_files=[""], - vboxmanage=VBOX_MANAGE - ) - ) - assert expected == str(excinfo.value) - else: - t.add_builder( - builder.VirtualboxIso( - boot_wait="10s", - guest_os_type="Ubuntu_64", - http_directory=HTTP_DIR, - iso_url=ISO_URL, - iso_checksum_type=ISO_CHECKSUM_TYPE, - iso_checksum=ISO_CHECKSUM, - ssh_port=22, - guest_additions_path="VBoxGuestAdditions_{{.Version}}.iso", - virtualbox_version_file=".vbox_version", - vm_name=VM_NAME, - floppy_files=[""], - vboxmanage=VBOX_MANAGE - ) + t.add_builder( + builder.VirtualboxIso( + boot_wait="10s", + guest_os_type="Ubuntu_64", + http_directory="", + iso_url="", + iso_checksum_type="sha512", + iso_checksum="sha512", + ssh_port=22, + guest_additions_path="VBoxGuestAdditions_{{.Version}}.iso", + virtualbox_version_file=".vbox_version", + vm_name="my_name", + floppy_files=[""], + vboxmanage=[ + "modifyvm {{.Name}} --memory 1024".split(), + "modifyvm {{.Name}} --vram 36".split(), + "modifyvm {{.Name}} --cpus 1".split() + ] + # vboxmanage=[['modifyvm {{.Name}} --memory 1024', "modifyvm {{.Name}} --cpus 1"]] ) - to_json = t.to_json() - assert to_json == json.dumps(json.loads(expected), sort_keys=True, indent=2, + ) + + to_json = t.to_json() + assert to_json == json.dumps(json.loads(expected_json), sort_keys=True, indent=2, separators=(',', ': ')) diff --git a/tests/packerlicious/test_validator.py b/tests/packerlicious/test_validator.py index ad113bf..05e5259 100644 --- a/tests/packerlicious/test_validator.py +++ b/tests/packerlicious/test_validator.py @@ -1,7 +1,6 @@ # Copyright (c) 2012-2013, Mark Peek # All rights reserved. # -import unittest import pytest from thirdparty.troposphere import Parameter, Ref @@ -11,28 +10,29 @@ from packerlicious.validator import s3_bucket_name, encoding, status from packerlicious.validator import iam_path, iam_names, iam_role_name from packerlicious.validator import iam_group_name, iam_user_name, elb_name +from packerlicious.validator import jagged_array from packerlicious.validator import mutually_exclusive -class TestValidator(unittest.TestCase): +class TestValidator(object): def test_boolean(self): for x in [True, "True", "true", 1, "1"]: - self.assertEqual(boolean(x), "true", repr(x)) + assert boolean(x) == "true", repr(x) for x in [False, "False", "false", 0, "0"]: - self.assertEqual(boolean(x), "false", repr(x)) + assert boolean(x) == "false", repr(x) for x in ["000", "111", "abc"]: with pytest.raises(ValueError): boolean(x) def test_integer(self): - self.assertEqual(integer(-1), -1) - self.assertEqual(integer("-1"), "-1") - self.assertEqual(integer(0), 0) - self.assertEqual(integer("0"), "0") - self.assertEqual(integer(65535), 65535) - self.assertEqual(integer("65535"), "65535") - self.assertEqual(integer(1.0), 1.0) + assert integer(-1) == -1 + assert integer("-1") == "-1" + assert integer(0) == 0 + assert integer("0") == "0" + assert integer(65535) == 65535 + assert integer("65535") == "65535" + assert integer(1.0) == 1.0 with pytest.raises(ValueError): integer("string") with pytest.raises(ValueError): @@ -42,9 +42,9 @@ def test_integer(self): def test_integer_range(self): between_ten_and_twenty = integer_range(10, 20) - self.assertEqual(between_ten_and_twenty(10), 10) - self.assertEqual(between_ten_and_twenty(15), 15) - self.assertEqual(between_ten_and_twenty(20), 20) + assert between_ten_and_twenty(10) == 10 + assert between_ten_and_twenty(15) == 15 + assert between_ten_and_twenty(20) == 20 for i in (-1, 9, 21, 1111111): with pytest.raises(ValueError): between_ten_and_twenty(i) @@ -157,4 +157,25 @@ def test_mutually_exclusive(self): with pytest.raises(ValueError): mutually_exclusive('abc', ['a', 'b', 'c'], conds) - + @pytest.mark.parametrize('test_value', [ + 'a_string_value', + 1234, + ['a_list_0', 'a_list_1'], + [['jagged_array_0,0'], 1234], + [['jagged_array_0,0'], [1234]], + ]) + def test_jagged_array_failure(self, test_value): + jagged_array_validator = jagged_array(str) + with pytest.raises(ValueError): + jagged_array_validator(test_value) + + @pytest.mark.parametrize('test_value', [ + [['jagged_array_0,0', 'jagged_array_0,1'], ['jagged_array_1,0']], + ]) + def test_jagged_array_success(self, test_value): + jagged_array_validator = jagged_array(str) + validator_return_value = jagged_array_validator(test_value) + + for i, sub_list in enumerate(test_value): + for j, value in enumerate(sub_list): + assert validator_return_value[i][j] == value