Merge branch 'develop' into asynch
This commit is contained in:
commit
68954d1447
8 changed files with 225 additions and 20 deletions
|
@ -1,5 +1,5 @@
|
|||
[bumpversion]
|
||||
current_version = 0.8.6
|
||||
current_version = 0.9.0
|
||||
commit = True
|
||||
tag = False
|
||||
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -6,3 +6,5 @@
|
|||
.cache
|
||||
/.pypirc
|
||||
/.tox/
|
||||
.dropbox
|
||||
Icon
|
6
CHANGELOG.md
Normal file
6
CHANGELOG.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Changelog
|
||||
## [0.9.0]
|
||||
### Fixed
|
||||
1. [#12] Output function doesn't accept parameter 'module'
|
||||
1. [#16] Handle empty space/special characters when passing string to command line options
|
||||
1. Tested with terraform 0.10.0
|
|
@ -10,7 +10,15 @@ import tempfile
|
|||
|
||||
from python_terraform.tfstate import Tfstate
|
||||
|
||||
try: # Python 2.7+
|
||||
from logging import NullHandler
|
||||
except ImportError:
|
||||
class NullHandler(logging.Handler):
|
||||
def emit(self, record):
|
||||
pass
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(NullHandler())
|
||||
|
||||
|
||||
class IsFlagged:
|
||||
|
@ -122,7 +130,7 @@ class Terraform(object):
|
|||
|
||||
def plan(self, dir_or_plan=None, detailed_exitcode=IsFlagged, **kwargs):
|
||||
"""
|
||||
refert to https://www.terraform.io/docs/commands/plan.html
|
||||
refer to https://www.terraform.io/docs/commands/plan.html
|
||||
:param detailed_exitcode: Return a detailed exit code when the command exits.
|
||||
:param dir_or_plan: relative path to plan/folder
|
||||
:param kwargs: options
|
||||
|
@ -134,6 +142,35 @@ class Terraform(object):
|
|||
args = self._generate_default_args(dir_or_plan)
|
||||
return self.cmd('plan', *args, **options)
|
||||
|
||||
def init(self, dir_or_plan=None, backend_config=None,
|
||||
reconfigure=IsFlagged, force_copy=IsNotFlagged, backend=True,
|
||||
**kwargs):
|
||||
"""
|
||||
refer to https://www.terraform.io/docs/commands/init.html
|
||||
|
||||
By default, this assumes you want to use backend config, and tries to
|
||||
init fresh. The flags -reconfigure and -backend=true are default.
|
||||
|
||||
:param backend_config: a dictionary of backend config options. eg.
|
||||
t = Terraform()
|
||||
t.init(backend_config={'access_key': 'myaccesskey',
|
||||
'secret_key': 'mysecretkey', 'bucket': 'mybucketname'})
|
||||
:param reconfigure: whether or not to force reconfiguration of backend
|
||||
:param force_copy: whether or not to migrate from the previous backend
|
||||
settings to the new backend settings
|
||||
:param backend: whether or not to use backend settings for init
|
||||
:param kwargs: options
|
||||
:return: ret_code, stdout, stderr
|
||||
"""
|
||||
options = kwargs
|
||||
options['backend_config'] = backend_config
|
||||
options['reconfigure'] = reconfigure
|
||||
options['force_copy'] = force_copy
|
||||
options['backend'] = backend
|
||||
options = self._generate_default_options(options)
|
||||
args = self._generate_default_args(dir_or_plan)
|
||||
return self.cmd('init', *args, **options)
|
||||
|
||||
def generate_cmd_string(self, cmd, *args, **kwargs):
|
||||
"""
|
||||
for any generate_cmd_string doesn't written as public method of terraform
|
||||
|
@ -170,13 +207,18 @@ class Terraform(object):
|
|||
cmds += ['-{k}={v}'.format(k=k, v=sub_v)]
|
||||
continue
|
||||
|
||||
# right now we assume only variables will be passed as dict
|
||||
# since map type sent in string won't work, create temp var file for
|
||||
# variables, and clean it up later
|
||||
if type(v) is dict:
|
||||
filename = self.temp_var_files.create(v)
|
||||
cmds += ['-var-file={0}'.format(filename)]
|
||||
continue
|
||||
if 'backend-config' in k:
|
||||
for bk, bv in v.items():
|
||||
cmds += ['-backend-config={k}={v}'.format(k=bk, v=bv)]
|
||||
continue
|
||||
|
||||
# since map type sent in string won't work, create temp var file for
|
||||
# variables, and clean it up later
|
||||
else:
|
||||
filename = self.temp_var_files.create(v)
|
||||
cmds += ['-var-file={0}'.format(filename)]
|
||||
continue
|
||||
|
||||
# simple flag,
|
||||
if v is IsFlagged:
|
||||
|
@ -192,8 +234,7 @@ class Terraform(object):
|
|||
cmds += ['-{k}={v}'.format(k=k, v=v)]
|
||||
|
||||
cmds += args
|
||||
cmd = ' '.join(cmds)
|
||||
return cmd
|
||||
return cmds
|
||||
|
||||
def cmd(self, cmd, *args, **kwargs):
|
||||
"""
|
||||
|
@ -224,8 +265,8 @@ class Terraform(object):
|
|||
stderr = sys.stderr
|
||||
stdout = sys.stdout
|
||||
|
||||
cmd_string = self.generate_cmd_string(cmd, *args, **kwargs)
|
||||
log.debug('command: {c}'.format(c=cmd_string))
|
||||
cmds = self.generate_cmd_string(cmd, *args, **kwargs)
|
||||
log.debug('command: {c}'.format(c=' '.join(cmds)))
|
||||
|
||||
working_folder = self.working_dir if self.working_dir else None
|
||||
|
||||
|
@ -233,7 +274,7 @@ class Terraform(object):
|
|||
if self.is_env_vars_included:
|
||||
environ_vars = os.environ.copy()
|
||||
|
||||
p = subprocess.Popen(cmd_string, stdout=stdout, stderr=stderr, shell=True,
|
||||
p = subprocess.Popen(cmds, stdout=stdout, stderr=stderr,
|
||||
cwd=working_folder, env=environ_vars)
|
||||
|
||||
synchronous = kwargs.pop('synchronous', True)
|
||||
|
@ -280,14 +321,19 @@ class Terraform(object):
|
|||
:return: states file in dict type
|
||||
"""
|
||||
|
||||
if not file_path:
|
||||
file_path = self.state
|
||||
working_dir = self.working_dir or ''
|
||||
|
||||
file_path = file_path or self.state or ''
|
||||
|
||||
if not file_path:
|
||||
file_path = 'terraform.tfstate'
|
||||
backend_path = os.path.join(file_path, '.terraform', 'terraform.tfstate')
|
||||
|
||||
if self.working_dir:
|
||||
file_path = os.path.join(self.working_dir, file_path)
|
||||
if os.path.exists(os.path.join(working_dir, backend_path)):
|
||||
file_path = backend_path
|
||||
else:
|
||||
file_path = os.path.join(file_path, 'terraform.tfstate')
|
||||
|
||||
file_path = os.path.join(working_dir, file_path)
|
||||
|
||||
self.tfstate = Tfstate.load_file(file_path)
|
||||
|
||||
|
|
2
setup.py
2
setup.py
|
@ -20,7 +20,7 @@ except IOError:
|
|||
|
||||
setup(
|
||||
name=module_name,
|
||||
version='0.8.7',
|
||||
version='0.9.0',
|
||||
url='https://github.com/beelit94/python-terraform',
|
||||
license='MIT',
|
||||
author='Freddy Tan',
|
||||
|
|
|
@ -14,6 +14,7 @@ logging.basicConfig(level=logging.DEBUG)
|
|||
root_logger = logging.getLogger()
|
||||
current_path = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
FILE_PATH_WITH_SPACE_AND_SPACIAL_CHARS = "test 'test.out!"
|
||||
STRING_CASES = [
|
||||
[
|
||||
lambda x: x.generate_cmd_string('apply', 'the_folder',
|
||||
|
@ -45,6 +46,14 @@ CMD_CASES = [
|
|||
1,
|
||||
'command: terraform import -no-color aws_instance.foo i-abcd1234',
|
||||
''
|
||||
],
|
||||
# test with space and special character in file path
|
||||
[
|
||||
lambda x: x.cmd('plan', 'var_to_output', out=FILE_PATH_WITH_SPACE_AND_SPACIAL_CHARS),
|
||||
'',
|
||||
0,
|
||||
'',
|
||||
'var_to_output'
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@ -83,9 +92,13 @@ class TestTerraform(object):
|
|||
""" teardown any state that was previously setup with a setup_method
|
||||
call.
|
||||
"""
|
||||
exclude = ['test_tfstate_file',
|
||||
'test_tfstate_file2',
|
||||
'test_tfstate_file3']
|
||||
|
||||
def purge(dir, pattern):
|
||||
for root, dirnames, filenames in os.walk(dir):
|
||||
dirnames[:] = [d for d in dirnames if d not in exclude]
|
||||
for filename in fnmatch.filter(filenames, pattern):
|
||||
f = os.path.join(root, filename)
|
||||
os.remove(f)
|
||||
|
@ -94,7 +107,9 @@ class TestTerraform(object):
|
|||
shutil.rmtree(d)
|
||||
|
||||
purge('.', '*.tfstate')
|
||||
purge('.', '*.tfstate.backup')
|
||||
purge('.', '*.terraform')
|
||||
purge('.', FILE_PATH_WITH_SPACE_AND_SPACIAL_CHARS)
|
||||
|
||||
@pytest.mark.parametrize([
|
||||
"method", "expected"
|
||||
|
@ -156,6 +171,18 @@ class TestTerraform(object):
|
|||
tf.read_state_file()
|
||||
assert tf.tfstate.modules[0]['path'] == ['root']
|
||||
|
||||
def test_state_default(self):
|
||||
cwd = os.path.join(current_path, 'test_tfstate_file2')
|
||||
tf = Terraform(working_dir=cwd)
|
||||
tf.read_state_file()
|
||||
assert tf.tfstate.modules[0]['path'] == ['default']
|
||||
|
||||
def test_state_default_backend(self):
|
||||
cwd = os.path.join(current_path, 'test_tfstate_file3')
|
||||
tf = Terraform(working_dir=cwd)
|
||||
tf.read_state_file()
|
||||
assert tf.tfstate.modules[0]['path'] == ['default_backend']
|
||||
|
||||
def test_pre_load_state_data(self):
|
||||
cwd = os.path.join(current_path, 'test_tfstate_file')
|
||||
tf = Terraform(working_dir=cwd, state='tfstate.test')
|
||||
|
@ -189,7 +216,7 @@ class TestTerraform(object):
|
|||
tf.init('var_to_output')
|
||||
tf.apply('var_to_output')
|
||||
result = tf.output('test_output', **param)
|
||||
regex = re.compile('terraform output (-module=test2 -json|-json -module=test2) test_output')
|
||||
regex = re.compile("terraform output (-module=test2 -json|-json -module=test2) test_output")
|
||||
log_str = string_logger()
|
||||
if param:
|
||||
assert re.search(regex, log_str), log_str
|
||||
|
|
62
test/test_tfstate_file2/terraform.tfstate
Normal file
62
test/test_tfstate_file2/terraform.tfstate
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"version": 3,
|
||||
"terraform_version": "0.7.10",
|
||||
"serial": 0,
|
||||
"lineage": "d03ecdf7-8be0-4593-a952-1d8127875119",
|
||||
"modules": [
|
||||
{
|
||||
"path": [
|
||||
"default"
|
||||
],
|
||||
"outputs": {},
|
||||
"resources": {
|
||||
"aws_instance.ubuntu-1404": {
|
||||
"type": "aws_instance",
|
||||
"depends_on": [],
|
||||
"primary": {
|
||||
"id": "i-84d10edb",
|
||||
"attributes": {
|
||||
"ami": "ami-9abea4fb",
|
||||
"associate_public_ip_address": "true",
|
||||
"availability_zone": "us-west-2b",
|
||||
"disable_api_termination": "false",
|
||||
"ebs_block_device.#": "0",
|
||||
"ebs_optimized": "false",
|
||||
"ephemeral_block_device.#": "0",
|
||||
"iam_instance_profile": "",
|
||||
"id": "i-84d10edb",
|
||||
"instance_state": "running",
|
||||
"instance_type": "t2.micro",
|
||||
"key_name": "",
|
||||
"monitoring": "false",
|
||||
"network_interface_id": "eni-46544f07",
|
||||
"private_dns": "ip-172-31-25-244.us-west-2.compute.internal",
|
||||
"private_ip": "172.31.25.244",
|
||||
"public_dns": "ec2-35-162-30-219.us-west-2.compute.amazonaws.com",
|
||||
"public_ip": "35.162.30.219",
|
||||
"root_block_device.#": "1",
|
||||
"root_block_device.0.delete_on_termination": "true",
|
||||
"root_block_device.0.iops": "100",
|
||||
"root_block_device.0.volume_size": "8",
|
||||
"root_block_device.0.volume_type": "gp2",
|
||||
"security_groups.#": "0",
|
||||
"source_dest_check": "true",
|
||||
"subnet_id": "subnet-d2c0f0a6",
|
||||
"tags.%": "0",
|
||||
"tenancy": "default",
|
||||
"vpc_security_group_ids.#": "1",
|
||||
"vpc_security_group_ids.619359045": "sg-9fc7dcfd"
|
||||
},
|
||||
"meta": {
|
||||
"schema_version": "1"
|
||||
},
|
||||
"tainted": false
|
||||
},
|
||||
"deposed": [],
|
||||
"provider": ""
|
||||
}
|
||||
},
|
||||
"depends_on": []
|
||||
}
|
||||
]
|
||||
}
|
62
test/test_tfstate_file3/.terraform/terraform.tfstate
Normal file
62
test/test_tfstate_file3/.terraform/terraform.tfstate
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"version": 3,
|
||||
"terraform_version": "0.7.10",
|
||||
"serial": 0,
|
||||
"lineage": "d03ecdf7-8be0-4593-a952-1d8127875119",
|
||||
"modules": [
|
||||
{
|
||||
"path": [
|
||||
"default_backend"
|
||||
],
|
||||
"outputs": {},
|
||||
"resources": {
|
||||
"aws_instance.ubuntu-1404": {
|
||||
"type": "aws_instance",
|
||||
"depends_on": [],
|
||||
"primary": {
|
||||
"id": "i-84d10edb",
|
||||
"attributes": {
|
||||
"ami": "ami-9abea4fb",
|
||||
"associate_public_ip_address": "true",
|
||||
"availability_zone": "us-west-2b",
|
||||
"disable_api_termination": "false",
|
||||
"ebs_block_device.#": "0",
|
||||
"ebs_optimized": "false",
|
||||
"ephemeral_block_device.#": "0",
|
||||
"iam_instance_profile": "",
|
||||
"id": "i-84d10edb",
|
||||
"instance_state": "running",
|
||||
"instance_type": "t2.micro",
|
||||
"key_name": "",
|
||||
"monitoring": "false",
|
||||
"network_interface_id": "eni-46544f07",
|
||||
"private_dns": "ip-172-31-25-244.us-west-2.compute.internal",
|
||||
"private_ip": "172.31.25.244",
|
||||
"public_dns": "ec2-35-162-30-219.us-west-2.compute.amazonaws.com",
|
||||
"public_ip": "35.162.30.219",
|
||||
"root_block_device.#": "1",
|
||||
"root_block_device.0.delete_on_termination": "true",
|
||||
"root_block_device.0.iops": "100",
|
||||
"root_block_device.0.volume_size": "8",
|
||||
"root_block_device.0.volume_type": "gp2",
|
||||
"security_groups.#": "0",
|
||||
"source_dest_check": "true",
|
||||
"subnet_id": "subnet-d2c0f0a6",
|
||||
"tags.%": "0",
|
||||
"tenancy": "default",
|
||||
"vpc_security_group_ids.#": "1",
|
||||
"vpc_security_group_ids.619359045": "sg-9fc7dcfd"
|
||||
},
|
||||
"meta": {
|
||||
"schema_version": "1"
|
||||
},
|
||||
"tainted": false
|
||||
},
|
||||
"deposed": [],
|
||||
"provider": ""
|
||||
}
|
||||
},
|
||||
"depends_on": []
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in a new issue