Skip to content
This repository was archived by the owner on Oct 24, 2025. It is now read-only.

Commit 2e71d83

Browse files
committed
Merge pull request #6 from nir0s/code-optimization
refactoring
2 parents 1ffd4d9 + 6d3255a commit 2e71d83

File tree

9 files changed

+80
-94
lines changed

9 files changed

+80
-94
lines changed

CHANGELOG.md

Lines changed: 0 additions & 6 deletions
This file was deleted.

README.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
python-packer
22
=============
33

4-
* Master Branch [![Build Status](https://travis-ci.org/nir0s/python-packer.svg?branch=master)](https://travis-ci.org/nir0s/python-packer)
5-
* PyPI [![PyPI](http://img.shields.io/pypi/dm/python-packer.svg)](http://img.shields.io/pypi/dm/python-packer.svg)
6-
* Version [![PypI](http://img.shields.io/pypi/v/python-packer.svg)](http://img.shields.io/pypi/v/python-packer.svg)
4+
[![Build Status](https://travis-ci.org/nir0s/python-packer.svg?branch=master)](https://travis-ci.org/nir0s/python-packer)
5+
[![PyPI](http://img.shields.io/pypi/dm/python-packer.svg)](http://img.shields.io/pypi/dm/python-packer.svg)
6+
[![PypI](http://img.shields.io/pypi/v/python-packer.svg)](http://img.shields.io/pypi/v/python-packer.svg)
77

88

99
A Python interface for [packer.io](http://www.packer.io)
@@ -15,10 +15,11 @@ The interface has been developed vs. Packer v0.7.5.
1515

1616
## Installation
1717

18-
You must have Packer installed prior to using this client (DUH!)
18+
You must have Packer installed prior to using this client though as installer class is provided to install packer for you.
1919

2020
```shell
2121
pip install python-packer
22+
2223
# or, for dev:
2324
pip install https://github.com/nir0s/python-packer/archive/master.tar.gz
2425
```
@@ -172,3 +173,19 @@ As such, return values from all functional methods (`validate`, `build`, etc..)
172173
will return an `sh` execution object. This is meant for you to be able to read stdout, stderr, exit codes and more after executing the commands. With the progression of `python-packer` less abstract objects will return and more concise return values will be provided.
173174

174175
Additionally, to verify that all errors return with as much info as possible, error handling is done gently. Most errors will raise an `sh` exception so that you're able to interact with them. Again, as this module progresses, these exceptions will be handled properly.
176+
177+
178+
## Testing
179+
180+
Please contribute. Currently tests are not really developed.
181+
182+
```shell
183+
git clone git@github.com:nir0s/python-packer.git
184+
cd python-packer
185+
pip install tox
186+
tox
187+
```
188+
189+
## Contributions..
190+
191+
..are always welcome.

TODO.md

Whitespace-only changes.

packer/__init__.py renamed to packer.py

Lines changed: 51 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,22 @@
33
import json
44
import zipfile
55

6-
__version__ = '0.0.3'
6+
__version__ = '0.1.0'
77

88
DEFAULT_PACKER_PATH = 'packer'
99

1010

11-
class Packer():
12-
"""Packer interface using the `sh` module (http://amoffat.github.io/sh/)
11+
class Packer(object):
12+
"""A packer client.
1313
"""
14+
1415
def __init__(self, packerfile, exc=None, only=None, vars=None,
1516
var_file=None, exec_path=DEFAULT_PACKER_PATH):
1617
"""
1718
:param string packerfile: Path to Packer template file
1819
:param list exc: List of builders to exclude
1920
:param list only: List of builders to include
20-
:param dict vars: Key Value pairs of template variables
21+
:param dict vars: key=value pairs of template variables
2122
:param string var_file: Path to variables file
2223
:param string exec_path: Path to Packer executable
2324
"""
@@ -26,34 +27,39 @@ def __init__(self, packerfile, exc=None, only=None, vars=None,
2627
if not os.path.isfile(self.packerfile):
2728
raise OSError('packerfile not found at path: {0}'.format(
2829
self.packerfile))
29-
self.exc = self._validate_argtype(exc if exc else [], list)
30-
self.only = self._validate_argtype(only if only else [], list)
31-
self.vars = self._validate_argtype(vars if vars else {}, dict)
30+
self.exc = self._validate_argtype(exc or [], list)
31+
self.only = self._validate_argtype(only or [], list)
32+
self.vars = self._validate_argtype(vars or {}, dict)
33+
3234
self.packer = sh.Command(exec_path)
3335

3436
def build(self, parallel=True, debug=False, force=False):
35-
"""Executes a Packer build (`packer build`)
37+
"""Executes a `packer build`
3638
3739
:param bool parallel: Run builders in parallel
3840
:param bool debug: Run in debug mode
3941
:param bool force: Force artifact output even if exists
4042
"""
41-
self.ccmd = self.packer.build
43+
self.packer_cmd = self.packer.build
44+
4245
self._add_opt('-parallel=true' if parallel else None)
4346
self._add_opt('-debug' if debug else None)
4447
self._add_opt('-force' if force else None)
4548
self._append_base_arguments()
4649
self._add_opt(self.packerfile)
47-
return self.ccmd()
50+
51+
return self.packer_cmd()
4852

4953
def fix(self, to_file=None):
5054
"""Implements the `packer fix` function
5155
5256
:param string to_file: File to output fixed template to
5357
"""
54-
self.ccmd = self.packer.fix
58+
self.packer_cmd = self.packer.fix
59+
5560
self._add_opt(self.packerfile)
56-
result = self.ccmd()
61+
62+
result = self.packer_cmd()
5763
if to_file:
5864
with open(to_file, 'w') as f:
5965
f.write(result.stdout)
@@ -87,27 +93,33 @@ def inspect(self, mrf=True):
8793
"name": "amazon"
8894
}
8995
]
96+
9097
:param bool mrf: output in machine-readable form.
9198
"""
92-
self.ccmd = self.packer.inspect
99+
self.packer_cmd = self.packer.inspect
100+
93101
self._add_opt('-machine-readable' if mrf else None)
94102
self._add_opt(self.packerfile)
95-
result = self.ccmd()
103+
104+
result = self.packer_cmd()
96105
if mrf:
97-
result.parsed_output = self._parse_inspection_output(
98-
result.stdout)
106+
result.parsed_output = self._parse_inspection_output(result.stdout)
107+
else:
108+
result.parsed_output = None
99109
return result
100110

101111
def push(self, create=True, token=False):
102112
"""Implmenets the `packer push` function
103113
104114
UNTESTED! Must be used alongside an Atlas account
105115
"""
106-
self.ccmd = self.packer.push
116+
self.packer_cmd = self.packer.push
117+
107118
self._add_opt('-create=true' if create else None)
108119
self._add_opt('-tokn={0}'.format(token) if token else None)
109120
self._add_opt(self.packerfile)
110-
return self.ccmd()
121+
122+
return self.packer_cmd()
111123

112124
def validate(self, syntax_only=False):
113125
"""Validates a Packer Template file (`packer validate`)
@@ -116,16 +128,18 @@ def validate(self, syntax_only=False):
116128
:param bool syntax_only: Whether to validate the syntax only
117129
without validating the configuration itself.
118130
"""
119-
self.ccmd = self.packer.validate
131+
self.packer_cmd = self.packer.validate
132+
120133
self._add_opt('-syntax-only' if syntax_only else None)
121134
self._append_base_arguments()
122135
self._add_opt(self.packerfile)
136+
123137
# as sh raises an exception rather than return a value when execution
124138
# fails we create an object to return the exception and the validation
125139
# state
126140
try:
127-
validation = self.ccmd()
128-
validation.succeeded = True if validation.exit_code == 0 else False
141+
validation = self.packer_cmd()
142+
validation.succeeded = validation.exit_code == 0
129143
validation.error = None
130144
except Exception as ex:
131145
validation = ValidationObject()
@@ -146,7 +160,7 @@ def version(self):
146160

147161
def _add_opt(self, option):
148162
if option:
149-
self.ccmd = self.ccmd.bake(option)
163+
self.packer_cmd = self.packer_cmd.bake(option)
150164

151165
def _validate_argtype(self, arg, argtype):
152166
if not isinstance(arg, argtype):
@@ -164,47 +178,43 @@ def _append_base_arguments(self):
164178
if self.exc and self.only:
165179
raise PackerException('Cannot provide both "except" and "only"')
166180
elif self.exc:
167-
self._add_opt('-except={0}'.format(self._joinc(self.exc)))
181+
self._add_opt('-except={0}'.format(self._join_comma(self.exc)))
168182
elif self.only:
169-
self._add_opt('-only={0}'.format(self._joinc(self.only)))
183+
self._add_opt('-only={0}'.format(self._join_comma(self.only)))
170184
for var, value in self.vars.items():
171185
self._add_opt("-var '{0}={1}'".format(var, value))
172186
if self.var_file:
173187
self._add_opt('-var-file={0}'.format(self.var_file))
174188

175-
def _joinc(self, lst):
189+
def _join_comma(self, lst):
176190
"""Returns a comma delimited string from a list"""
177191
return str(','.join(lst))
178192

179-
def _joins(self, lst):
180-
"""Returns a space delimited string from a list"""
181-
return str(' '.join(lst))
182-
183193
def _parse_inspection_output(self, output):
184194
"""Parses the machine-readable output `packer inspect` provides.
185195
186196
See the inspect method for more info.
187197
This has been tested vs. Packer v0.7.5
188198
"""
189199
parts = {'variables': [], 'builders': [], 'provisioners': []}
190-
for l in output.splitlines():
191-
l = l.split(',')
192-
if l[2].startswith('template'):
193-
del l[0:2]
194-
component = l[0]
200+
for line in output.splitlines():
201+
line = line.split(',')
202+
if line[2].startswith('template'):
203+
del line[0:2]
204+
component = line[0]
195205
if component == 'template-variable':
196-
variable = {"name": l[1], "value": l[2]}
206+
variable = {"name": line[1], "value": line[2]}
197207
parts['variables'].append(variable)
198208
elif component == 'template-builder':
199-
builder = {"name": l[1], "type": l[2]}
209+
builder = {"name": line[1], "type": line[2]}
200210
parts['builders'].append(builder)
201211
elif component == 'template-provisioner':
202-
provisioner = {"type": l[1]}
212+
provisioner = {"type": line[1]}
203213
parts['provisioners'].append(provisioner)
204214
return parts
205215

206216

207-
class Installer():
217+
class Installer(object):
208218
def __init__(self, packer_path, installer_path):
209219
self.packer_path = packer_path
210220
self.installer_path = installer_path
@@ -215,15 +225,15 @@ def install(self):
215225
for path in zip.namelist():
216226
zip.extract(path, self.packer_path)
217227
exec_path = os.path.join(self.packer_path, 'packer')
218-
if not self._verify(exec_path):
228+
if not self._verify_packer_installed(exec_path):
219229
raise PackerException('packer installation failed. '
220230
'Executable could not be found under: '
221231
'{0}'.format(exec_path))
222232
else:
223233
return exec_path
224234

225-
def _verify(self, packer):
226-
return True if os.path.isfile(packer) else False
235+
def _verify_packer_installed(self, packer_path):
236+
return os.path.isfile(packer_path)
227237

228238

229239
class ValidationObject():

setup.py

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
from setuptools import setup
2-
# from setuptools import find_packages
3-
from setuptools.command.test import test as testcommand
4-
import sys
5-
import re
61
import os
72
import codecs
3+
from setuptools import setup
84

95
here = os.path.abspath(os.path.dirname(__file__))
106

@@ -13,45 +9,18 @@ def read(*parts):
139
# intentionally *not* adding an encoding option to open
1410
return codecs.open(os.path.join(here, *parts), 'r').read()
1511

16-
17-
def find_version(*file_paths):
18-
version_file = read(*file_paths)
19-
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
20-
version_file, re.M)
21-
if version_match:
22-
print('VERSION: ', version_match.group(1))
23-
return version_match.group(1)
24-
raise RuntimeError("Unable to find version string.")
25-
26-
27-
class Tox(testcommand):
28-
def finalize_options(self):
29-
testcommand.finalize_options(self)
30-
self.test_args = []
31-
self.test_suite = True
32-
33-
def run_tests(self):
34-
# import here, cause outside the eggs aren't loaded
35-
import tox
36-
errcode = tox.cmdline(self.test_args)
37-
sys.exit(errcode)
38-
3912
setup(
4013
name='python-packer',
41-
version=find_version('packer', '__init__.py'),
14+
version='0.1.0',
4215
url='https://github.com/nir0s/python-packer',
4316
author='nir0s',
4417
author_email='nir36g@gmail.com',
4518
license='LICENSE',
4619
platforms='All',
4720
description='A Python interface for Hashicorp\'s Packer',
4821
long_description=read('README.rst'),
49-
packages=['packer'],
50-
install_requires=[
51-
"sh==1.11",
52-
],
53-
tests_require=['nose', 'tox'],
54-
cmdclass={'test': Tox},
22+
py_modules=['packer'],
23+
install_requires=["sh==1.11"],
5524
classifiers=[
5625
'Programming Language :: Python',
5726
'Natural Language :: English',
File renamed without changes.
File renamed without changes.

packer/tests/test_packer.py renamed to tests/test_packer.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import os
55

66
PACKER_PATH = '/usr/bin/packer'
7-
TEST_RESOURCES_DIR = 'packer/tests/resources/'
7+
TEST_RESOURCES_DIR = 'tests/resources'
88
TEST_PACKERFILE = os.path.join(TEST_RESOURCES_DIR, 'packerfile.json')
99
TEST_BAD_PACKERFILE = os.path.join(TEST_RESOURCES_DIR, 'badpackerfile.json')
1010

@@ -23,10 +23,6 @@ def test_inspect(self):
2323
p = packer.Packer(TEST_PACKERFILE)
2424
p.inspect()
2525

26-
def test_push(self):
27-
p = packer.Packer(TEST_PACKERFILE)
28-
p.push()
29-
3026
def test_validate(self):
3127
p = packer.Packer(TEST_PACKERFILE)
3228
p.validate()

tox.ini

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ envlist=flake8,py27,py26
55
[testenv:py26]
66
deps =
77
-rdev-requirements.txt
8-
commands=nosetests --with-cov --cov-report term-missing --cov packer packer/tests -v
8+
commands=nosetests --with-cov --cov-report term-missing --cov packer tests -v
99

1010
[testenv:py27]
1111
deps =
1212
-rdev-requirements.txt
13-
commands=nosetests --with-cov --cov-report term-missing --cov packer packer/tests -v
13+
commands=nosetests --with-cov --cov-report term-missing --cov packer tests -v
1414

1515
[testenv:flake8]
1616
deps =
1717
flake8
1818
-rdev-requirements.txt
19-
commands=flake8 packer
19+
commands=flake8 packer.py

0 commit comments

Comments
 (0)