From 80514a256eed68ce4215a4f4d260d56242af6b59 Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Fri, 20 Oct 2017 23:34:33 -0500 Subject: [PATCH 01/17] refactor: Remove Installer Related: #10 --- packer.py | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/packer.py b/packer.py index 33cc535..59a0be9 100644 --- a/packer.py +++ b/packer.py @@ -227,28 +227,6 @@ def _parse_inspection_output(self, output): return parts -class Installer(object): - def __init__(self, packer_path, installer_path): - self.packer_path = packer_path - self.installer_path = installer_path - - def install(self): - with open(self.installer_path, 'rb') as f: - zip = zipfile.ZipFile(f) - for path in zip.namelist(): - zip.extract(path, self.packer_path) - exec_path = os.path.join(self.packer_path, 'packer') - if not self._verify_packer_installed(exec_path): - raise PackerException('packer installation failed. ' - 'Executable could not be found under: ' - '{0}'.format(exec_path)) - else: - return exec_path - - def _verify_packer_installed(self, packer_path): - return os.path.isfile(packer_path) - - class ValidationObject(): pass From e3034acdd9d2404f1237fc3f8d156525e4d768e8 Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Fri, 20 Oct 2017 23:48:15 -0500 Subject: [PATCH 02/17] docs: Remove Installer references --- README.md | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/README.md b/README.md index 8f078d2..32eb4ef 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ The interface has been developed vs. Packer v0.7.5. ## Installation -You must have Packer installed prior to using this client though as installer class is provided to install packer for you. +You must have Packer installed prior to using this client. ```shell pip install python-packer @@ -153,19 +153,6 @@ p = packer.Packer(packerfile, ...) print(p.version()) ``` -### PackerInstaller.install() - -This installs packer to `packer_path` using the `installer_path` and verifies that the installation was successful. - -```python - -packer_path = '/usr/bin/' -installer_path = 'Downloads/packer_0.7.5_linux_amd64.zip' - -p = packer.Installer(packer_path, installer_path) -p.install() -``` - ## Shell Interaction The [sh](http://amoffat.github.io/sh/) Python module is used to execute Packer. From 021a2c41e0cc40663d02d683c405992d0cbd434a Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Fri, 20 Oct 2017 23:49:01 -0500 Subject: [PATCH 03/17] style: Use yapf to reformat file --- packer.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/packer.py b/packer.py index 59a0be9..e774c0a 100644 --- a/packer.py +++ b/packer.py @@ -1,8 +1,9 @@ -import sh -import os import json +import os import zipfile +import sh + DEFAULT_PACKER_PATH = 'packer' @@ -10,8 +11,14 @@ class Packer(object): """A packer client """ - def __init__(self, packerfile, exc=None, only=None, vars=None, - var_file=None, exec_path=DEFAULT_PACKER_PATH, out_iter=None, + def __init__(self, + packerfile, + exc=None, + only=None, + vars=None, + var_file=None, + exec_path=DEFAULT_PACKER_PATH, + out_iter=None, err_iter=None): """ :param string packerfile: Path to Packer template file @@ -24,8 +31,8 @@ def __init__(self, packerfile, exc=None, only=None, vars=None, self.packerfile = self._validate_argtype(packerfile, str) self.var_file = var_file if not os.path.isfile(self.packerfile): - raise OSError('packerfile not found at path: {0}'.format( - self.packerfile)) + raise OSError( + 'packerfile not found at path: {0}'.format(self.packerfile)) self.exc = self._validate_argtype(exc or [], list) self.only = self._validate_argtype(only or [], list) self.vars = self._validate_argtype(vars or {}, dict) @@ -41,7 +48,10 @@ def __init__(self, packerfile, exc=None, only=None, vars=None, self.packer = sh.Command(exec_path) self.packer = self.packer.bake(**kwargs) - def build(self, parallel=True, debug=False, force=False, + def build(self, + parallel=True, + debug=False, + force=False, machine_readable=False): """Executes a `packer build` @@ -115,7 +125,7 @@ def inspect(self, mrf=True): result = self.packer_cmd() if mrf: result.parsed_output = self._parse_inspection_output( - result.stdout.decode()) + result.stdout.decode()) else: result.parsed_output = None return result @@ -176,8 +186,8 @@ def _add_opt(self, option): def _validate_argtype(self, arg, argtype): if not isinstance(arg, argtype): - raise PackerException('{0} argument must be of type {1}'.format( - arg, argtype)) + raise PackerException( + '{0} argument must be of type {1}'.format(arg, argtype)) return arg def _append_base_arguments(self): From 1d2fcc494a0f6079ca0e2f6ec141e5e390c09ef6 Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Sun, 22 Oct 2017 13:31:42 -0500 Subject: [PATCH 04/17] refactor: Use native code to mimic sh bake behavior --- packer.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packer.py b/packer.py index e774c0a..8fc9d1d 100644 --- a/packer.py +++ b/packer.py @@ -45,8 +45,17 @@ def __init__(self, kwargs["_err"] = err_iter kwargs["_out_bufsize"] = 1 - self.packer = sh.Command(exec_path) - self.packer = self.packer.bake(**kwargs) + command = [] + command.append(exec_path) + command.extend(self.dict_to_command(kwargs)) + self.packer = command + + def dict_to_command(self, kwargs): + """Convert dict to '--key=value' command parameters""" + param = [] + for parameter, value in kwargs.items(): + param.append('--{}={}'.format(parameter, value)) + return param def build(self, parallel=True, From a6ffde0c15945758bb3386559ea7fcdee9c74a20 Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Sun, 22 Oct 2017 13:37:55 -0500 Subject: [PATCH 05/17] refactor: Rough conversion using subprocess instead of sh --- packer.py | 63 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/packer.py b/packer.py index 8fc9d1d..c18cb17 100644 --- a/packer.py +++ b/packer.py @@ -1,8 +1,9 @@ +import copy import json import os +import subprocess import zipfile -import sh DEFAULT_PACKER_PATH = 'packer' @@ -69,7 +70,9 @@ def build(self, :param bool force: Force artifact output even if exists :param bool machine_readable: Make output machine-readable """ - self.packer_cmd = self.packer.build + cmd = copy.copy(self.packer) + cmd.append('build') + self.packer_cmd = cmd self._add_opt('-parallel=true' if parallel else None) self._add_opt('-debug' if debug else None) @@ -78,18 +81,20 @@ def build(self, self._append_base_arguments() self._add_opt(self.packerfile) - return self.packer_cmd() + return subprocess.run(self.packer_cmd) def fix(self, to_file=None): """Implements the `packer fix` function :param string to_file: File to output fixed template to """ - self.packer_cmd = self.packer.fix + cmd = copy.copy(self.packer) + cmd.append('fix') + self.packer_cmd = cmd self._add_opt(self.packerfile) - result = self.packer_cmd() + result = subprocess.run(cmd, stdout=subprocess.PIPE) if to_file: with open(to_file, 'w') as f: f.write(result.stdout.decode()) @@ -126,17 +131,19 @@ def inspect(self, mrf=True): :param bool mrf: output in machine-readable form. """ - self.packer_cmd = self.packer.inspect + cmd = copy.copy(self.packer) + cmd.append('inspect') + self.packer_cmd = cmd self._add_opt('-machine-readable' if mrf else None) self._add_opt(self.packerfile) - result = self.packer_cmd() - if mrf: - result.parsed_output = self._parse_inspection_output( - result.stdout.decode()) - else: - result.parsed_output = None + result = subprocess.run(self.packer_cmd) + # if mrf: + # result.parsed_output = self._parse_inspection_output( + # result.stdout.decode()) + # else: + # result.parsed_output = None return result def push(self, create=True, token=False): @@ -144,13 +151,15 @@ def push(self, create=True, token=False): UNTESTED! Must be used alongside an Atlas account """ - self.packer_cmd = self.packer.push + cmd = copy.copy(self.packer) + cmd.append('push') + self.packer_cmd = cmd self._add_opt('-create=true' if create else None) self._add_opt('-tokn={0}'.format(token) if token else None) self._add_opt(self.packerfile) - return self.packer_cmd() + return subprocess.run(cmd) def validate(self, syntax_only=False): """Validates a Packer Template file (`packer validate`) @@ -159,7 +168,9 @@ def validate(self, syntax_only=False): :param bool syntax_only: Whether to validate the syntax only without validating the configuration itself. """ - self.packer_cmd = self.packer.validate + cmd = copy.copy(self.packer) + cmd.append('validate') + self.packer_cmd = cmd self._add_opt('-syntax-only' if syntax_only else None) self._append_base_arguments() @@ -168,16 +179,16 @@ def validate(self, syntax_only=False): # as sh raises an exception rather than return a value when execution # fails we create an object to return the exception and the validation # state - try: - validation = self.packer_cmd() - validation.succeeded = validation.exit_code == 0 - validation.error = None - except Exception as ex: - validation = ValidationObject() - validation.succeeded = False - validation.failed = True - validation.error = ex.message - return validation + # try: + # validation = subprocess.run(self.packer_cmd) + # validation.succeeded = validation.exit_code == 0 + # validation.error = None + # except Exception as ex: + # validation = ValidationObject() + # validation.succeeded = False + # validation.failed = True + # validation.error = ex.message + return subprocess.run(self.packer_cmd) def version(self): """Returns Packer's version number (`packer version`) @@ -191,7 +202,7 @@ def version(self): def _add_opt(self, option): if option: - self.packer_cmd = self.packer_cmd.bake(option) + self.packer_cmd.append(option) def _validate_argtype(self, arg, argtype): if not isinstance(arg, argtype): From 8ba0b6e2b8ec274e4869819bc924a7b8e048ce6e Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Sun, 22 Oct 2017 13:42:34 -0500 Subject: [PATCH 06/17] refactor: Use different logic to get version number --- packer.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packer.py b/packer.py index c18cb17..b0c81ae 100644 --- a/packer.py +++ b/packer.py @@ -198,7 +198,11 @@ def version(self): the `packer v` prefix so that you don't have to parse the version yourself. """ - return self.packer.version().split('v')[1].rstrip('\n') + cmd = copy.copy(self.packer) + cmd.append('version') + output = subprocess.run(cmd, stdout=subprocess.PIPE) + version = output.stdout.decode().split('\n')[0].split('v')[1] + return version def _add_opt(self, option): if option: From 08cecc3898181cb7a4023fb39d0dbb2b3342c87e Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Sun, 22 Oct 2017 22:41:37 -0500 Subject: [PATCH 07/17] feat: Create command wrapper --- packer.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packer.py b/packer.py index b0c81ae..4e8655a 100644 --- a/packer.py +++ b/packer.py @@ -4,6 +4,7 @@ import subprocess import zipfile +from collections import namedtuple DEFAULT_PACKER_PATH = 'packer' @@ -260,6 +261,15 @@ def _parse_inspection_output(self, output): parts['provisioners'].append(provisioner) return parts + def _run_command(self, command): + """Wrapper to execute command""" + PackerCommand = namedtuple('PackerCommand', ['stdout', 'stderr']) + executed = subprocess.run( + command, stderr=subprocess.PIPE, stdout=subprocess.PIPE) + packer_command = PackerCommand(executed.stdout.decode(), + executed.stderr.decode()) + return packer_command + class ValidationObject(): pass From ab6002ba9e3e8510b8e0ed798bbb5f7b69ba23e5 Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Sun, 22 Oct 2017 22:44:23 -0500 Subject: [PATCH 08/17] refactor: Use wrapper for version call --- packer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packer.py b/packer.py index 4e8655a..c08d90d 100644 --- a/packer.py +++ b/packer.py @@ -201,8 +201,8 @@ def version(self): """ cmd = copy.copy(self.packer) cmd.append('version') - output = subprocess.run(cmd, stdout=subprocess.PIPE) - version = output.stdout.decode().split('\n')[0].split('v')[1] + output = self._run_command(cmd) + version = output.stdout.split('\n')[0].split('v')[1] return version def _add_opt(self, option): From cb5472fced31275d0ab6925293cd8804b08963d0 Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Mon, 23 Oct 2017 22:23:28 -0500 Subject: [PATCH 09/17] chore: Rename variables so it is more accurate --- packer.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packer.py b/packer.py index c08d90d..d6369ec 100644 --- a/packer.py +++ b/packer.py @@ -3,7 +3,6 @@ import os import subprocess import zipfile - from collections import namedtuple DEFAULT_PACKER_PATH = 'packer' @@ -263,12 +262,12 @@ def _parse_inspection_output(self, output): def _run_command(self, command): """Wrapper to execute command""" - PackerCommand = namedtuple('PackerCommand', ['stdout', 'stderr']) + PackerOutput = namedtuple('PackerOutput', ['stdout', 'stderr']) executed = subprocess.run( command, stderr=subprocess.PIPE, stdout=subprocess.PIPE) - packer_command = PackerCommand(executed.stdout.decode(), - executed.stderr.decode()) - return packer_command + packer_output = PackerOutput(executed.stdout.decode(), + executed.stderr.decode()) + return packer_output class ValidationObject(): From 817bf773aba5ec7459d01f7f650f26692bf7f7af Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Mon, 23 Oct 2017 22:59:59 -0500 Subject: [PATCH 10/17] refactor: Build use wrapper command --- packer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packer.py b/packer.py index d6369ec..936e506 100644 --- a/packer.py +++ b/packer.py @@ -81,7 +81,7 @@ def build(self, self._append_base_arguments() self._add_opt(self.packerfile) - return subprocess.run(self.packer_cmd) + return self._run_command(self.packer_cmd) def fix(self, to_file=None): """Implements the `packer fix` function From 380b7279bced0eeb01114cd32695440b345d134f Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Mon, 23 Oct 2017 23:11:00 -0500 Subject: [PATCH 11/17] refactor: Convert fix to use command wrapper --- packer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packer.py b/packer.py index 936e506..03c6101 100644 --- a/packer.py +++ b/packer.py @@ -94,11 +94,11 @@ def fix(self, to_file=None): self._add_opt(self.packerfile) - result = subprocess.run(cmd, stdout=subprocess.PIPE) + result = self._run_command(self.packer_cmd) if to_file: with open(to_file, 'w') as f: - f.write(result.stdout.decode()) - result.fixed = json.loads(result.stdout.decode()) + f.write(result.stdout) + result = json.loads(result.stdout) return result def inspect(self, mrf=True): From 12723da9d82b696edc084eb3c614a614cf5826fb Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Mon, 23 Oct 2017 23:16:21 -0500 Subject: [PATCH 12/17] refactor: Convert push to use wrapper command --- packer.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packer.py b/packer.py index 03c6101..4dff207 100644 --- a/packer.py +++ b/packer.py @@ -155,11 +155,12 @@ def push(self, create=True, token=False): cmd.append('push') self.packer_cmd = cmd - self._add_opt('-create=true' if create else None) - self._add_opt('-tokn={0}'.format(token) if token else None) + # self._add_opt('-create=true' if create else None) + self._add_opt('-token={0}'.format(token) if token else None) self._add_opt(self.packerfile) - return subprocess.run(cmd) + result = self._run_command(self.packer_cmd) + return result def validate(self, syntax_only=False): """Validates a Packer Template file (`packer validate`) From 8ace2e7be6701bb06d3dd53e1e4531beda8b6198 Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Thu, 26 Oct 2017 00:04:50 -0500 Subject: [PATCH 13/17] feat: Add returncode to PackerOutput --- packer.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packer.py b/packer.py index 4dff207..b76d075 100644 --- a/packer.py +++ b/packer.py @@ -263,11 +263,13 @@ def _parse_inspection_output(self, output): def _run_command(self, command): """Wrapper to execute command""" - PackerOutput = namedtuple('PackerOutput', ['stdout', 'stderr']) + PackerOutput = namedtuple('PackerOutput', + ['stdout', 'stderr', 'returncode']) executed = subprocess.run( command, stderr=subprocess.PIPE, stdout=subprocess.PIPE) packer_output = PackerOutput(executed.stdout.decode(), - executed.stderr.decode()) + executed.stderr.decode(), + executed.returncode) return packer_output From a570512be4467974ac827b963e2c6ddba41ec95b Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Thu, 26 Oct 2017 00:40:34 -0500 Subject: [PATCH 14/17] refactor: Raise exception when validation fails --- packer.py | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/packer.py b/packer.py index b76d075..0e0f9b1 100644 --- a/packer.py +++ b/packer.py @@ -177,19 +177,10 @@ def validate(self, syntax_only=False): self._append_base_arguments() self._add_opt(self.packerfile) - # as sh raises an exception rather than return a value when execution - # fails we create an object to return the exception and the validation - # state - # try: - # validation = subprocess.run(self.packer_cmd) - # validation.succeeded = validation.exit_code == 0 - # validation.error = None - # except Exception as ex: - # validation = ValidationObject() - # validation.succeeded = False - # validation.failed = True - # validation.error = ex.message - return subprocess.run(self.packer_cmd) + result = self._run_command(self.packer_cmd) + if result.returncode: + raise PackerException(result.stdout) + return result def version(self): """Returns Packer's version number (`packer version`) @@ -273,9 +264,5 @@ def _run_command(self, command): return packer_output -class ValidationObject(): - pass - - class PackerException(Exception): pass From 686d08485636bce0b4081f1a7bfe5fe4e77366b0 Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Thu, 26 Oct 2017 01:08:33 -0500 Subject: [PATCH 15/17] refactor: Inspect use wrapper command --- packer.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packer.py b/packer.py index 0e0f9b1..60179c2 100644 --- a/packer.py +++ b/packer.py @@ -138,12 +138,10 @@ def inspect(self, mrf=True): self._add_opt('-machine-readable' if mrf else None) self._add_opt(self.packerfile) - result = subprocess.run(self.packer_cmd) - # if mrf: - # result.parsed_output = self._parse_inspection_output( - # result.stdout.decode()) - # else: - # result.parsed_output = None + output = self._run_command(self.packer_cmd) + result = output.stdout + if mrf: + result = self._parse_inspection_output(output.stdout) return result def push(self, create=True, token=False): From 37076cd0e59f496c6012f8d2915403cf828b9978 Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Thu, 26 Oct 2017 01:40:09 -0500 Subject: [PATCH 16/17] test: Add test cases for utility methods --- tests/test_packer.py | 1 - tests/test_utils.py | 61 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 tests/test_utils.py diff --git a/tests/test_packer.py b/tests/test_packer.py index 1ec037b..3285f29 100644 --- a/tests/test_packer.py +++ b/tests/test_packer.py @@ -3,7 +3,6 @@ import testtools import os -PACKER_PATH = '/usr/bin/packer' TEST_RESOURCES_DIR = 'tests/resources' TEST_PACKERFILE = os.path.join(TEST_RESOURCES_DIR, 'simple-test.json') TEST_BAD_PACKERFILE = os.path.join(TEST_RESOURCES_DIR, 'badpackerfile.json') diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..ac30fc2 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,61 @@ +import os + +import packer +import testtools + +TEST_RESOURCES_DIR = 'tests/resources' +TEST_PACKERFILE = os.path.join(TEST_RESOURCES_DIR, 'simple-test.json') +TEST_BAD_PACKERFILE = os.path.join(TEST_RESOURCES_DIR, 'badpackerfile.json') + +PACKER = packer.Packer(TEST_PACKERFILE) + + +def test_dict_to_command(): + """Validate dict is converted to a command properly""" + kwargs = {'test': 'value'} + cmd = PACKER.dict_to_command(kwargs) + assert cmd == ['--test=value'] + + +def test_join_comma(): + """Validate list concatination is correct""" + output = PACKER._join_comma(['hello', 'world']) + assert output == 'hello,world' + + +def test_run_command(): + """Check returned output from executed command""" + cmd = ['packer', 'version'] + output = PACKER._run_command(cmd) + + assert 'Packer v' in output.stdout + assert '' in output.stderr + assert 0 == output.returncode + + +def test_parse_inspection_output(): + """Check returned output from executed command""" + output = '''1508999535,,ui,say,Variables: + 1508999535,,ui,say, + 1508999535,,ui,say, + 1508999535,,ui,say,Builders: + 1508999535,,template-builder,docker,docker + 1508999535,,ui,say, docker + 1508999535,,ui,say, + 1508999535,,ui,say,Provisioners: + 1508999535,,template-provisioner,shell + 1508999535,,ui,say, shell + 1508999535,,ui,say,Note: If your build names contain user variables or template functions such as 'timestamp'%!(PACKER_COMMA) these are processed at build time%!(PACKER_COMMA) and therefore only show in their raw form here.''' + + output = PACKER._parse_inspection_output(output) + + assert { + 'builders': [{ + 'name': 'docker', + 'type': 'docker' + }], + 'provisioners': [{ + 'type': 'shell' + }], + 'variables': [] + } == output From 7acdaf274ecf809870257d1714a388501006e156 Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Thu, 26 Oct 2017 13:40:33 -0500 Subject: [PATCH 17/17] refactor: Use variables names when parsing inspection output --- packer.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packer.py b/packer.py index 60179c2..b8b45cc 100644 --- a/packer.py +++ b/packer.py @@ -217,9 +217,11 @@ def _append_base_arguments(self): self._add_opt('-except={0}'.format(self._join_comma(self.exc))) elif self.only: self._add_opt('-only={0}'.format(self._join_comma(self.only))) + for var, value in self.vars.items(): self._add_opt("-var") self._add_opt("{0}={1}".format(var, value)) + if self.var_file: self._add_opt('-var-file={0}'.format(self.var_file)) @@ -236,17 +238,21 @@ def _parse_inspection_output(self, output): parts = {'variables': [], 'builders': [], 'provisioners': []} for line in output.splitlines(): line = line.split(',') - if line[2].startswith('template'): - del line[0:2] + + packer_type = line[2] + if packer_type.startswith('template'): + del line[0:2] # Remove date component = line[0] + name = line[1] + if component == 'template-variable': - variable = {"name": line[1], "value": line[2]} + variable = {"name": name, "value": line[2]} parts['variables'].append(variable) elif component == 'template-builder': - builder = {"name": line[1], "type": line[2]} + builder = {"name": name, "type": line[2]} parts['builders'].append(builder) elif component == 'template-provisioner': - provisioner = {"type": line[1]} + provisioner = {"type": name} parts['provisioners'].append(provisioner) return parts