Merge pull request #57 from BNMetrics/develop
fixed a bug with var-file argument, allow workspace commands to pass flags and options
This commit is contained in:
commit
3eaaec37d0
4 changed files with 156 additions and 42 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -9,3 +9,11 @@
|
||||||
.dropbox
|
.dropbox
|
||||||
Icon
|
Icon
|
||||||
/pytestdebug.log
|
/pytestdebug.log
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# virtualenv
|
||||||
|
.virtualenv/
|
||||||
|
|
||||||
|
|
||||||
|
# Intellij
|
||||||
|
.idea/
|
||||||
|
|
|
@ -21,6 +21,7 @@ except ImportError:
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
log.addHandler(NullHandler())
|
log.addHandler(NullHandler())
|
||||||
|
|
||||||
|
COMMAND_WITH_SUBCOMMANDS = ['workspace']
|
||||||
|
|
||||||
class IsFlagged:
|
class IsFlagged:
|
||||||
pass
|
pass
|
||||||
|
@ -36,6 +37,7 @@ class TerraformCommandError(subprocess.CalledProcessError):
|
||||||
self.out = out
|
self.out = out
|
||||||
self.err = err
|
self.err = err
|
||||||
|
|
||||||
|
|
||||||
class Terraform(object):
|
class Terraform(object):
|
||||||
"""
|
"""
|
||||||
Wrapper of terraform command line tool
|
Wrapper of terraform command line tool
|
||||||
|
@ -205,6 +207,10 @@ class Terraform(object):
|
||||||
"""
|
"""
|
||||||
cmds = cmd.split()
|
cmds = cmd.split()
|
||||||
cmds = [self.terraform_bin_path] + cmds
|
cmds = [self.terraform_bin_path] + cmds
|
||||||
|
if cmd in COMMAND_WITH_SUBCOMMANDS:
|
||||||
|
args = list(args)
|
||||||
|
subcommand = args.pop(0)
|
||||||
|
cmds.append(subcommand)
|
||||||
|
|
||||||
for option, value in kwargs.items():
|
for option, value in kwargs.items():
|
||||||
if '_' in option:
|
if '_' in option:
|
||||||
|
@ -223,9 +229,13 @@ class Terraform(object):
|
||||||
|
|
||||||
# since map type sent in string won't work, create temp var file for
|
# since map type sent in string won't work, create temp var file for
|
||||||
# variables, and clean it up later
|
# variables, and clean it up later
|
||||||
else:
|
elif option == 'var':
|
||||||
filename = self.temp_var_files.create(value)
|
# We do not create empty var-files if there is no var passed.
|
||||||
cmds += ['-var-file={0}'.format(filename)]
|
# An empty var-file would result in an error: An argument or block definition is required here
|
||||||
|
if value:
|
||||||
|
filename = self.temp_var_files.create(value)
|
||||||
|
cmds += ['-var-file={0}'.format(filename)]
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# simple flag,
|
# simple flag,
|
||||||
|
@ -319,7 +329,6 @@ class Terraform(object):
|
||||||
|
|
||||||
return ret_code, out, err
|
return ret_code, out, err
|
||||||
|
|
||||||
|
|
||||||
def output(self, *args, **kwargs):
|
def output(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
https://www.terraform.io/docs/commands/output.html
|
https://www.terraform.io/docs/commands/output.html
|
||||||
|
@ -388,40 +397,40 @@ class Terraform(object):
|
||||||
|
|
||||||
self.tfstate = Tfstate.load_file(file_path)
|
self.tfstate = Tfstate.load_file(file_path)
|
||||||
|
|
||||||
def set_workspace(self, workspace):
|
def set_workspace(self, workspace, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
set workspace
|
set workspace
|
||||||
:param workspace: the desired workspace.
|
:param workspace: the desired workspace.
|
||||||
:return: status
|
:return: status
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.cmd('workspace' ,'select', workspace)
|
return self.cmd('workspace', 'select', workspace, *args, **kwargs)
|
||||||
|
|
||||||
def create_workspace(self, workspace):
|
def create_workspace(self, workspace, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
create workspace
|
create workspace
|
||||||
:param workspace: the desired workspace.
|
:param workspace: the desired workspace.
|
||||||
:return: status
|
:return: status
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.cmd('workspace', 'new', workspace)
|
return self.cmd('workspace', 'new', workspace, *args, **kwargs)
|
||||||
|
|
||||||
def delete_workspace(self, workspace):
|
def delete_workspace(self, workspace, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
delete workspace
|
delete workspace
|
||||||
:param workspace: the desired workspace.
|
:param workspace: the desired workspace.
|
||||||
:return: status
|
:return: status
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.cmd('workspace', 'delete', workspace)
|
return self.cmd('workspace', 'delete', workspace, *args, **kwargs)
|
||||||
|
|
||||||
def show_workspace(self):
|
def show_workspace(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
show workspace
|
show workspace, this command does not need the [DIR] part
|
||||||
:return: workspace
|
:return: workspace
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.cmd('workspace', 'show')
|
return self.cmd('workspace', 'show', **kwargs)
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, traceback):
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
self.temp_var_files.clean_up()
|
self.temp_var_files.clean_up()
|
||||||
|
|
|
@ -3,6 +3,7 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from python_terraform import *
|
from python_terraform import *
|
||||||
|
from contextlib import contextmanager
|
||||||
import pytest
|
import pytest
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
@ -34,7 +35,7 @@ CMD_CASES = [
|
||||||
['method', 'expected_output', 'expected_ret_code', 'expected_exception', 'expected_logs', 'folder'],
|
['method', 'expected_output', 'expected_ret_code', 'expected_exception', 'expected_logs', 'folder'],
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
lambda x: x.cmd('plan', 'var_to_output', no_color=IsFlagged, var={'test_var': 'test'}) ,
|
lambda x: x.cmd('plan', 'var_to_output', no_color=IsFlagged, var={'test_var': 'test'}),
|
||||||
# Expected output varies by terraform version
|
# Expected output varies by terraform version
|
||||||
["doesn't need to do anything", # Terraform < 0.10.7 (used in travis env)
|
["doesn't need to do anything", # Terraform < 0.10.7 (used in travis env)
|
||||||
"no\nactions need to be performed"], # Terraform >= 0.10.7
|
"no\nactions need to be performed"], # Terraform >= 0.10.7
|
||||||
|
@ -69,7 +70,16 @@ CMD_CASES = [
|
||||||
False,
|
False,
|
||||||
'',
|
'',
|
||||||
'var_to_output'
|
'var_to_output'
|
||||||
]
|
],
|
||||||
|
# test workspace command (commands with subcommand)
|
||||||
|
[
|
||||||
|
lambda x: x.cmd('workspace', 'show', no_color=IsFlagged),
|
||||||
|
'',
|
||||||
|
0,
|
||||||
|
False,
|
||||||
|
'command: terraform workspace show -no-color',
|
||||||
|
''
|
||||||
|
],
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -102,6 +112,28 @@ def string_logger(request):
|
||||||
return lambda: str(log_stream.getvalue())
|
return lambda: str(log_stream.getvalue())
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def workspace_setup_teardown():
|
||||||
|
"""
|
||||||
|
Fixture used in workspace related tests
|
||||||
|
|
||||||
|
Create and tear down a workspace
|
||||||
|
*Use as a contextmanager*
|
||||||
|
"""
|
||||||
|
@contextmanager
|
||||||
|
def wrapper(workspace_name, create=True, delete=True, *args, **kwargs):
|
||||||
|
tf = Terraform(working_dir=current_path)
|
||||||
|
tf.init()
|
||||||
|
if create:
|
||||||
|
tf.create_workspace(workspace_name, *args, **kwargs)
|
||||||
|
yield tf
|
||||||
|
if delete:
|
||||||
|
tf.set_workspace('default')
|
||||||
|
tf.delete_workspace(workspace_name)
|
||||||
|
|
||||||
|
yield wrapper
|
||||||
|
|
||||||
|
|
||||||
class TestTerraform(object):
|
class TestTerraform(object):
|
||||||
def teardown_method(self, method):
|
def teardown_method(self, method):
|
||||||
""" teardown any state that was previously setup with a setup_method
|
""" teardown any state that was previously setup with a setup_method
|
||||||
|
@ -184,6 +216,17 @@ class TestTerraform(object):
|
||||||
assert expected_output in out.replace('\n', '').replace(' ', '')
|
assert expected_output in out.replace('\n', '').replace(' ', '')
|
||||||
assert err == ''
|
assert err == ''
|
||||||
|
|
||||||
|
def test_apply_with_var_file(self, string_logger):
|
||||||
|
tf = Terraform(working_dir=current_path)
|
||||||
|
|
||||||
|
tf.init()
|
||||||
|
tf.apply(var_file=os.path.join(current_path, 'tfvar_file', 'test.tfvars'))
|
||||||
|
logs = string_logger()
|
||||||
|
logs = logs.split('\n')
|
||||||
|
for log in logs:
|
||||||
|
if log.startswith('command: terraform apply'):
|
||||||
|
assert log.count('-var-file=') == 1
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
['cmd', 'args', 'options'],
|
['cmd', 'args', 'options'],
|
||||||
[
|
[
|
||||||
|
@ -321,41 +364,94 @@ class TestTerraform(object):
|
||||||
tf.import_cmd('aws_instance.foo', 'i-abc1234', no_color=IsFlagged)
|
tf.import_cmd('aws_instance.foo', 'i-abc1234', no_color=IsFlagged)
|
||||||
assert 'command: terraform import -no-color aws_instance.foo i-abc1234' in string_logger()
|
assert 'command: terraform import -no-color aws_instance.foo i-abc1234' in string_logger()
|
||||||
|
|
||||||
def test_create_workspace(self):
|
def test_create_workspace(self, workspace_setup_teardown):
|
||||||
tf = Terraform(working_dir=current_path)
|
workspace_name = 'test'
|
||||||
tf.init()
|
with workspace_setup_teardown(workspace_name, create=False) as tf:
|
||||||
ret, out, err = tf.create_workspace('test')
|
ret, out, err = tf.create_workspace('test')
|
||||||
tf.set_workspace('default')
|
|
||||||
tf.delete_workspace('test')
|
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
assert err == ''
|
assert err == ''
|
||||||
|
|
||||||
def test_set_workspace(self):
|
def test_create_workspace_with_args(
|
||||||
tf = Terraform(working_dir=current_path)
|
self, workspace_setup_teardown, string_logger
|
||||||
tf.init()
|
):
|
||||||
tf.create_workspace('test')
|
workspace_name = 'test'
|
||||||
tf.set_workspace('test')
|
state_file_path = os.path.join(current_path, 'test_tfstate_file2', 'terraform.tfstate')
|
||||||
tf.set_workspace('default')
|
with workspace_setup_teardown(workspace_name, create=False) as tf:
|
||||||
ret, out, err = tf.delete_workspace('test')
|
ret, out, err = tf.create_workspace('test', current_path, no_color=IsFlagged)
|
||||||
|
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
assert err == ''
|
assert err == ''
|
||||||
|
|
||||||
def test_show_workspace(self):
|
logs = string_logger()
|
||||||
tf = Terraform(working_dir=current_path)
|
logs = logs.replace('\n', '')
|
||||||
tf.init()
|
expected_log = 'command: terraform workspace new -no-color test {}'.format(current_path)
|
||||||
tf.create_workspace('test')
|
assert expected_log in logs
|
||||||
ret, out, err = tf.show_workspace()
|
|
||||||
tf.set_workspace('default')
|
def test_set_workspace(self, workspace_setup_teardown):
|
||||||
tf.delete_workspace('test')
|
workspace_name = 'test'
|
||||||
|
with workspace_setup_teardown(workspace_name) as tf:
|
||||||
|
ret, out, err = tf.set_workspace(workspace_name)
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
assert err == ''
|
assert err == ''
|
||||||
|
|
||||||
def test_delete_workspace(self):
|
def test_set_workspace_with_args(
|
||||||
tf = Terraform(working_dir=current_path)
|
self, workspace_setup_teardown, string_logger):
|
||||||
tf.init()
|
workspace_name = 'test'
|
||||||
tf.create_workspace('test')
|
with workspace_setup_teardown(workspace_name) as tf:
|
||||||
tf.set_workspace('default')
|
ret, out, err = tf.set_workspace(workspace_name, current_path, no_color=IsFlagged)
|
||||||
ret, out, err = tf.delete_workspace('test')
|
|
||||||
tf.show_workspace()
|
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
assert err == ''
|
assert err == ''
|
||||||
|
|
||||||
|
logs = string_logger()
|
||||||
|
logs = logs.replace('\n', '')
|
||||||
|
expected_log = 'command: terraform workspace select -no-color test {}'.format(current_path)
|
||||||
|
assert expected_log in logs
|
||||||
|
|
||||||
|
def test_show_workspace(self, workspace_setup_teardown):
|
||||||
|
workspace_name = 'test'
|
||||||
|
with workspace_setup_teardown(workspace_name) as tf:
|
||||||
|
ret, out, err = tf.show_workspace()
|
||||||
|
assert ret == 0
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
def test_show_workspace_with_no_color(
|
||||||
|
self, workspace_setup_teardown, string_logger
|
||||||
|
):
|
||||||
|
workspace_name = 'test'
|
||||||
|
with workspace_setup_teardown(workspace_name) as tf:
|
||||||
|
ret, out, err = tf.show_workspace(no_color=IsFlagged)
|
||||||
|
|
||||||
|
assert ret == 0
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
logs = string_logger()
|
||||||
|
logs = logs.replace('\n', '')
|
||||||
|
expected_log = 'command: terraform workspace show -no-color'
|
||||||
|
assert expected_log in logs
|
||||||
|
|
||||||
|
def test_delete_workspace(self, workspace_setup_teardown):
|
||||||
|
workspace_name = 'test'
|
||||||
|
with workspace_setup_teardown(workspace_name, delete=False) as tf:
|
||||||
|
tf.set_workspace('default')
|
||||||
|
ret, out, err = tf.delete_workspace(workspace_name)
|
||||||
|
assert ret == 0
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
def test_delete_workspace_with_args(
|
||||||
|
self, workspace_setup_teardown, string_logger
|
||||||
|
):
|
||||||
|
workspace_name = 'test'
|
||||||
|
with workspace_setup_teardown(workspace_name, delete=False) as tf:
|
||||||
|
tf.set_workspace('default')
|
||||||
|
ret, out, err = tf.delete_workspace(
|
||||||
|
workspace_name, current_path, force=IsFlagged,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert ret == 0
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
logs = string_logger()
|
||||||
|
logs = logs.replace('\n', '')
|
||||||
|
expected_log = 'command: terraform workspace delete -force test {}'.format(current_path)
|
||||||
|
assert expected_log in logs
|
||||||
|
|
1
test/tfvar_files/test.tfvars
Normal file
1
test/tfvar_files/test.tfvars
Normal file
|
@ -0,0 +1 @@
|
||||||
|
test_var = "True"
|
Loading…
Reference in a new issue