Migrate to Python 3 only
This commit is contained in:
parent
a33aebce1c
commit
fc584c2a48
3 changed files with 140 additions and 167 deletions
29
.travis.yml
29
.travis.yml
|
@ -1,8 +1,9 @@
|
||||||
language: python
|
language: python
|
||||||
python:
|
python:
|
||||||
- '2.7'
|
|
||||||
- '3.5'
|
|
||||||
- '3.6'
|
- '3.6'
|
||||||
|
- '3.7'
|
||||||
|
- '3.8'
|
||||||
|
- '3.9'
|
||||||
before_install: sudo apt-get install unzip
|
before_install: sudo apt-get install unzip
|
||||||
before_script:
|
before_script:
|
||||||
- export TFVER=0.10.0
|
- export TFVER=0.10.0
|
||||||
|
@ -26,27 +27,3 @@ branches:
|
||||||
- master
|
- master
|
||||||
- develop
|
- develop
|
||||||
- release/**
|
- release/**
|
||||||
deploy:
|
|
||||||
- provider: pypi
|
|
||||||
distributions: sdist
|
|
||||||
server: https://testpypi.python.org/pypi
|
|
||||||
user: beelit94
|
|
||||||
password:
|
|
||||||
secure: sWxc+p/gdq3k2WbUGNG2F4TukFNkTkvq6OPaiOvyfgWThYNk6/juRkMd8flmTbh0VGhcjFbpDLeSApb2kFhfiokYJSH1hOOcmXf8xzYH8/+R4DDEiGa5Y/pR9TBvYu4S8eJEfFUFfb1BBpapykj7o43hcaqMExBJIdVJU7aeoEAC1jQeTJh8wWwdJKHy2dNSM+6RVhk3e5+b0LfK7Bk5sU5P+YdEMj79MJU450J4OmZXWzJgvBN5/2QfVa5LrUD00nYuGuiBniz2lVevIHWjUYawUzpPsTa7F0s2WemG9YcV7U8u06xNjY9Ce3CTbxNhc7OIKq+TCkOgR3qZFXVJ8A87G+AT2iQ01VslQ4DJCxnJNTnpqojWnwf6MFL9O8ONioWYO32bhQFKOQ806ASHP4lNMRDKqx8hXtP5In7/r0SARKscv6Bas83rp+FESkKD5vWgkZJG+yx96LlwRLUhSVnVyb/nOJ++zt5RR3BvY2O4p9YAZY3Qt8TQihOdBQKnY3UXsMyNaE25+yvyNWpmyJiePRbTUd+cpLnycnqG9Ll8v6TpFXb6ahFMjlAFfJNQYlREfseClTHSRjZNxfsXGQCsJh6TZAq7jOB5hCk3q41eOUFWARxbyj8j59NBV8fSQrrGJJ9/VZKQeYiQlBB9KpK4PrnH84oeQ8i+VSbVr5w=
|
|
||||||
on:
|
|
||||||
branch: release/**
|
|
||||||
tags: false
|
|
||||||
condition: $TRAVIS_PYTHON_VERSION = "3.5"
|
|
||||||
- provider: pypi
|
|
||||||
distributions: sdist
|
|
||||||
user: beelit94
|
|
||||||
password:
|
|
||||||
secure: QhCiTLrBvw/Uzt3eiLEmvMP3uHnayVCETqEDA+2+Q9vFavqj0CHA76zqYonBFqnh0a3HFCRIVVt+6ynpZ10kpQ3tAObIw+pY39ZPnpAhOjSpFzzMdpIF9Bhv9A93ng2iSESAZPAOwktHzUwjFx0Zvl0lSYD9rutHgttGgdU2CajiUtwTUhCTjOAVdR2Gm+15H808vzKWnMaKflXxZt+fkt279mQTYAtz6eBWtZwIKry/uAJCSrPSWtbi50O0HsWRMXLXWH5Jn/BVjWSDSM92DssUDq0D+tQyp4M5nQXJ9EyAvEdsKNLx3cvNruznh2ohI2jmcoIjwFiS6+wrEmUiXkP86iyzCSqL/EbcOG0xUh3vbfYtMBp7jENgD405+3SEhPY4PlqUmc+HDtB7FUcHz4y7wGWJRGyQzNnjJ6Tv0Ajdz5mfJubWVIvHjcRqkxTVtUKt50o00xZ62M0ZzQkDTIHQEsZly0XeHAgSvNzWkmjt9BiBrZ9OkoWVkRpSrCBy/EcpDNPCTSfSzOQ0Nq1ePFjkkW1n8QWDW9Pdb+/7/P2y9E2S8CT+nXBkRQeQiO86Qf1Ireg7k9TA5VYisVZ6bEXEc9UV0mAojpSsC7zWhVlbAoltN6ZbjKmqy/wqn2QIcJemcSie0JigzKpdw7l8FPT2lCRyTKlYLpRyKXzSkNI=
|
|
||||||
on:
|
|
||||||
branch: master
|
|
||||||
tags: false
|
|
||||||
condition: $TRAVIS_PYTHON_VERSION = "3.5"
|
|
||||||
notifications:
|
|
||||||
email:
|
|
||||||
recipients:
|
|
||||||
- beelit94@gmail.com
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# above is for compatibility of python2.7.11
|
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
@ -11,17 +8,10 @@ import tempfile
|
||||||
from python_terraform.tfstate import Tfstate
|
from python_terraform.tfstate import Tfstate
|
||||||
|
|
||||||
|
|
||||||
try: # Python 2.7+
|
logger = logging.getLogger(__name__)
|
||||||
from logging import NullHandler
|
|
||||||
except ImportError:
|
|
||||||
class NullHandler(logging.Handler):
|
|
||||||
def emit(self, record):
|
|
||||||
pass
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
COMMAND_WITH_SUBCOMMANDS = {"workspace"}
|
||||||
log.addHandler(NullHandler())
|
|
||||||
|
|
||||||
COMMAND_WITH_SUBCOMMANDS = ['workspace']
|
|
||||||
|
|
||||||
class IsFlagged:
|
class IsFlagged:
|
||||||
pass
|
pass
|
||||||
|
@ -32,27 +22,29 @@ class IsNotFlagged:
|
||||||
|
|
||||||
|
|
||||||
class TerraformCommandError(subprocess.CalledProcessError):
|
class TerraformCommandError(subprocess.CalledProcessError):
|
||||||
def __init__(self, ret_code, cmd, out, err):
|
def __init__(self, ret_code, cmd, out, err):
|
||||||
super(TerraformCommandError, self).__init__(ret_code, cmd)
|
super(TerraformCommandError, self).__init__(ret_code, cmd)
|
||||||
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
|
|
||||||
https://www.terraform.io/
|
https://www.terraform.io/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, working_dir=None,
|
def __init__(
|
||||||
targets=None,
|
self,
|
||||||
state=None,
|
working_dir=None,
|
||||||
variables=None,
|
targets=None,
|
||||||
parallelism=None,
|
state=None,
|
||||||
var_file=None,
|
variables=None,
|
||||||
terraform_bin_path=None,
|
parallelism=None,
|
||||||
is_env_vars_included=True,
|
var_file=None,
|
||||||
):
|
terraform_bin_path=None,
|
||||||
|
is_env_vars_included=True,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
:param working_dir: the folder of the working folder, if not given,
|
:param working_dir: the folder of the working folder, if not given,
|
||||||
will be current working folder
|
will be current working folder
|
||||||
|
@ -75,8 +67,9 @@ class Terraform(object):
|
||||||
self.targets = [] if targets is None else targets
|
self.targets = [] if targets is None else targets
|
||||||
self.variables = dict() if variables is None else variables
|
self.variables = dict() if variables is None else variables
|
||||||
self.parallelism = parallelism
|
self.parallelism = parallelism
|
||||||
self.terraform_bin_path = terraform_bin_path \
|
self.terraform_bin_path = (
|
||||||
if terraform_bin_path else 'terraform'
|
terraform_bin_path if terraform_bin_path else "terraform"
|
||||||
|
)
|
||||||
self.var_file = var_file
|
self.var_file = var_file
|
||||||
self.temp_var_files = VariableFiles()
|
self.temp_var_files = VariableFiles()
|
||||||
|
|
||||||
|
@ -87,17 +80,23 @@ class Terraform(object):
|
||||||
def __getattr__(self, item):
|
def __getattr__(self, item):
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
cmd_name = str(item)
|
cmd_name = str(item)
|
||||||
if cmd_name.endswith('_cmd'):
|
if cmd_name.endswith("_cmd"):
|
||||||
cmd_name = cmd_name[:-4]
|
cmd_name = cmd_name[:-4]
|
||||||
log.debug('called with %r and %r' % (args, kwargs))
|
logger.debug("called with %r and %r", args, kwargs)
|
||||||
return self.cmd(cmd_name, *args, **kwargs)
|
return self.cmd(cmd_name, *args, **kwargs)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
def apply(self, dir_or_plan=None, input=False, skip_plan=False, no_color=IsFlagged,
|
def apply(
|
||||||
**kwargs):
|
self,
|
||||||
"""
|
dir_or_plan=None,
|
||||||
refer to https://terraform.io/docs/commands/apply.html
|
input=False,
|
||||||
|
skip_plan=False,
|
||||||
|
no_color=IsFlagged,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""Refer to https://terraform.io/docs/commands/apply.html
|
||||||
|
|
||||||
no-color is flagged by default
|
no-color is flagged by default
|
||||||
:param no_color: disable color of stdout
|
:param no_color: disable color of stdout
|
||||||
:param input: disable prompt for a missing variable
|
:param input: disable prompt for a missing variable
|
||||||
|
@ -107,58 +106,63 @@ class Terraform(object):
|
||||||
:returns return_code, stdout, stderr
|
:returns return_code, stdout, stderr
|
||||||
"""
|
"""
|
||||||
default = kwargs
|
default = kwargs
|
||||||
default['input'] = input
|
default["input"] = input
|
||||||
default['no_color'] = no_color
|
default["no_color"] = no_color
|
||||||
default['auto-approve'] = (skip_plan == True)
|
default["auto-approve"] = skip_plan is True
|
||||||
option_dict = self._generate_default_options(default)
|
option_dict = self._generate_default_options(default)
|
||||||
args = self._generate_default_args(dir_or_plan)
|
args = self._generate_default_args(dir_or_plan)
|
||||||
return self.cmd('apply', *args, **option_dict)
|
return self.cmd("apply", *args, **option_dict)
|
||||||
|
|
||||||
def _generate_default_args(self, dir_or_plan):
|
def _generate_default_args(self, dir_or_plan):
|
||||||
return [dir_or_plan] if dir_or_plan else []
|
return [dir_or_plan] if dir_or_plan else []
|
||||||
|
|
||||||
def _generate_default_options(self, input_options):
|
def _generate_default_options(self, input_options):
|
||||||
option_dict = dict()
|
option_dict = dict()
|
||||||
option_dict['state'] = self.state
|
option_dict["state"] = self.state
|
||||||
option_dict['target'] = self.targets
|
option_dict["target"] = self.targets
|
||||||
option_dict['var'] = self.variables
|
option_dict["var"] = self.variables
|
||||||
option_dict['var_file'] = self.var_file
|
option_dict["var_file"] = self.var_file
|
||||||
option_dict['parallelism'] = self.parallelism
|
option_dict["parallelism"] = self.parallelism
|
||||||
option_dict['no_color'] = IsFlagged
|
option_dict["no_color"] = IsFlagged
|
||||||
option_dict['input'] = False
|
option_dict["input"] = False
|
||||||
option_dict.update(input_options)
|
option_dict.update(input_options)
|
||||||
return option_dict
|
return option_dict
|
||||||
|
|
||||||
def destroy(self, dir_or_plan=None, force=IsFlagged, **kwargs):
|
def destroy(self, dir_or_plan=None, force=IsFlagged, **kwargs):
|
||||||
"""
|
"""Refer to https://www.terraform.io/docs/commands/destroy.html
|
||||||
refer to https://www.terraform.io/docs/commands/destroy.html
|
|
||||||
force/no-color option is flagged by default
|
force/no-color option is flagged by default
|
||||||
:return: ret_code, stdout, stderr
|
:return: ret_code, stdout, stderr
|
||||||
"""
|
"""
|
||||||
default = kwargs
|
default = kwargs
|
||||||
default['force'] = force
|
default["force"] = force
|
||||||
options = self._generate_default_options(default)
|
options = self._generate_default_options(default)
|
||||||
args = self._generate_default_args(dir_or_plan)
|
args = self._generate_default_args(dir_or_plan)
|
||||||
return self.cmd('destroy', *args, **options)
|
return self.cmd("destroy", *args, **options)
|
||||||
|
|
||||||
def plan(self, dir_or_plan=None, detailed_exitcode=IsFlagged, **kwargs):
|
def plan(self, dir_or_plan=None, detailed_exitcode=IsFlagged, **kwargs):
|
||||||
"""
|
"""Refer 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 detailed_exitcode: Return a detailed exit code when the command exits.
|
||||||
:param dir_or_plan: relative path to plan/folder
|
:param dir_or_plan: relative path to plan/folder
|
||||||
:param kwargs: options
|
:param kwargs: options
|
||||||
:return: ret_code, stdout, stderr
|
:return: ret_code, stdout, stderr
|
||||||
"""
|
"""
|
||||||
options = kwargs
|
options = kwargs
|
||||||
options['detailed_exitcode'] = detailed_exitcode
|
options["detailed_exitcode"] = detailed_exitcode
|
||||||
options = self._generate_default_options(options)
|
options = self._generate_default_options(options)
|
||||||
args = self._generate_default_args(dir_or_plan)
|
args = self._generate_default_args(dir_or_plan)
|
||||||
return self.cmd('plan', *args, **options)
|
return self.cmd("plan", *args, **options)
|
||||||
|
|
||||||
def init(self, dir_or_plan=None, backend_config=None,
|
def init(
|
||||||
reconfigure=IsFlagged, backend=True, **kwargs):
|
self,
|
||||||
"""
|
dir_or_plan=None,
|
||||||
refer to https://www.terraform.io/docs/commands/init.html
|
backend_config=None,
|
||||||
|
reconfigure=IsFlagged,
|
||||||
|
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
|
By default, this assumes you want to use backend config, and tries to
|
||||||
init fresh. The flags -reconfigure and -backend=true are default.
|
init fresh. The flags -reconfigure and -backend=true are default.
|
||||||
|
@ -166,7 +170,7 @@ class Terraform(object):
|
||||||
:param dir_or_plan: relative path to the folder want to init
|
:param dir_or_plan: relative path to the folder want to init
|
||||||
:param backend_config: a dictionary of backend config options. eg.
|
:param backend_config: a dictionary of backend config options. eg.
|
||||||
t = Terraform()
|
t = Terraform()
|
||||||
t.init(backend_config={'access_key': 'myaccesskey',
|
t.init(backend_config={'access_key': 'myaccesskey',
|
||||||
'secret_key': 'mysecretkey', 'bucket': 'mybucketname'})
|
'secret_key': 'mysecretkey', 'bucket': 'mybucketname'})
|
||||||
:param reconfigure: whether or not to force reconfiguration of backend
|
:param reconfigure: whether or not to force reconfiguration of backend
|
||||||
:param backend: whether or not to use backend settings for init
|
:param backend: whether or not to use backend settings for init
|
||||||
|
@ -174,16 +178,15 @@ class Terraform(object):
|
||||||
:return: ret_code, stdout, stderr
|
:return: ret_code, stdout, stderr
|
||||||
"""
|
"""
|
||||||
options = kwargs
|
options = kwargs
|
||||||
options['backend_config'] = backend_config
|
options["backend_config"] = backend_config
|
||||||
options['reconfigure'] = reconfigure
|
options["reconfigure"] = reconfigure
|
||||||
options['backend'] = backend
|
options["backend"] = backend
|
||||||
options = self._generate_default_options(options)
|
options = self._generate_default_options(options)
|
||||||
args = self._generate_default_args(dir_or_plan)
|
args = self._generate_default_args(dir_or_plan)
|
||||||
return self.cmd('init', *args, **options)
|
return self.cmd("init", *args, **options)
|
||||||
|
|
||||||
def generate_cmd_string(self, cmd, *args, **kwargs):
|
def generate_cmd_string(self, cmd, *args, **kwargs):
|
||||||
"""
|
"""For any generate_cmd_string doesn't written as public method of Terraform
|
||||||
for any generate_cmd_string doesn't written as public method of terraform
|
|
||||||
|
|
||||||
examples:
|
examples:
|
||||||
1. call import command,
|
1. call import command,
|
||||||
|
@ -213,50 +216,50 @@ class Terraform(object):
|
||||||
cmds.append(subcommand)
|
cmds.append(subcommand)
|
||||||
|
|
||||||
for option, value in kwargs.items():
|
for option, value in kwargs.items():
|
||||||
if '_' in option:
|
if "_" in option:
|
||||||
option = option.replace('_', '-')
|
option = option.replace("_", "-")
|
||||||
|
|
||||||
if type(value) is list:
|
if type(value) is list:
|
||||||
for sub_v in value:
|
for sub_v in value:
|
||||||
cmds += ['-{k}={v}'.format(k=option, v=sub_v)]
|
cmds += [f"-{option}={sub_v}"]
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if type(value) is dict:
|
if type(value) is dict:
|
||||||
if 'backend-config' in option:
|
if "backend-config" in option:
|
||||||
for bk, bv in value.items():
|
for bk, bv in value.items():
|
||||||
cmds += ['-backend-config={k}={v}'.format(k=bk, v=bv)]
|
cmds += [f"-backend-config={bk}={bv}"]
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 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
|
||||||
elif option == 'var':
|
elif option == "var":
|
||||||
# We do not create empty var-files if there is no var passed.
|
# We do not create empty var-files if there is no var passed.
|
||||||
# An empty var-file would result in an error: An argument or block definition is required here
|
# An empty var-file would result in an error: An argument or block definition is required here
|
||||||
if value:
|
if value:
|
||||||
filename = self.temp_var_files.create(value)
|
filename = self.temp_var_files.create(value)
|
||||||
cmds += ['-var-file={0}'.format(filename)]
|
cmds += [f"-var-file={filename}"]
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# simple flag,
|
# simple flag,
|
||||||
if value is IsFlagged:
|
if value is IsFlagged:
|
||||||
cmds += ['-{k}'.format(k=option)]
|
cmds += ["-{k}".format(k=option)]
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if value is None or value is IsNotFlagged:
|
if value is None or value is IsNotFlagged:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if type(value) is bool:
|
if type(value) is bool:
|
||||||
value = 'true' if value else 'false'
|
value = "true" if value else "false"
|
||||||
|
|
||||||
cmds += ['-{k}={v}'.format(k=option, v=value)]
|
cmds += [f"-{option}={value}"]
|
||||||
|
|
||||||
cmds += args
|
cmds += args
|
||||||
return cmds
|
return cmds
|
||||||
|
|
||||||
def cmd(self, cmd, *args, **kwargs):
|
def cmd(self, cmd, *args, **kwargs):
|
||||||
"""
|
"""Run a terraform command, if success, will try to read state file
|
||||||
run a terraform command, if success, will try to read state file
|
|
||||||
:param cmd: command and sub-command of terraform, seperated with space
|
:param cmd: command and sub-command of terraform, seperated with space
|
||||||
refer to https://www.terraform.io/docs/commands/index.html
|
refer to https://www.terraform.io/docs/commands/index.html
|
||||||
:param args: arguments of a command
|
:param args: arguments of a command
|
||||||
|
@ -281,10 +284,10 @@ class Terraform(object):
|
||||||
err: The captured stderr, or None if not captured
|
err: The captured stderr, or None if not captured
|
||||||
:return: ret_code, out, err
|
:return: ret_code, out, err
|
||||||
"""
|
"""
|
||||||
capture_output = kwargs.pop('capture_output', True)
|
capture_output = kwargs.pop("capture_output", True)
|
||||||
raise_on_error = kwargs.pop('raise_on_error', False)
|
raise_on_error = kwargs.pop("raise_on_error", False)
|
||||||
synchronous = kwargs.pop('synchronous', True)
|
synchronous = kwargs.pop("synchronous", True)
|
||||||
|
|
||||||
if capture_output is True:
|
if capture_output is True:
|
||||||
stderr = subprocess.PIPE
|
stderr = subprocess.PIPE
|
||||||
stdout = subprocess.PIPE
|
stdout = subprocess.PIPE
|
||||||
|
@ -296,7 +299,7 @@ class Terraform(object):
|
||||||
stdout = sys.stdout
|
stdout = sys.stdout
|
||||||
|
|
||||||
cmds = self.generate_cmd_string(cmd, *args, **kwargs)
|
cmds = self.generate_cmd_string(cmd, *args, **kwargs)
|
||||||
log.debug('command: {c}'.format(c=' '.join(cmds)))
|
logger.debug("Command: %s", " ".join(cmds))
|
||||||
|
|
||||||
working_folder = self.working_dir if self.working_dir else None
|
working_folder = self.working_dir if self.working_dir else None
|
||||||
|
|
||||||
|
@ -304,47 +307,46 @@ class Terraform(object):
|
||||||
if self.is_env_vars_included:
|
if self.is_env_vars_included:
|
||||||
environ_vars = os.environ.copy()
|
environ_vars = os.environ.copy()
|
||||||
|
|
||||||
p = subprocess.Popen(cmds, stdout=stdout, stderr=stderr,
|
p = subprocess.Popen(
|
||||||
cwd=working_folder, env=environ_vars)
|
cmds, stdout=stdout, stderr=stderr, cwd=working_folder, env=environ_vars
|
||||||
|
)
|
||||||
|
|
||||||
if not synchronous:
|
if not synchronous:
|
||||||
return p, None, None
|
return p, None, None
|
||||||
|
|
||||||
out, err = p.communicate()
|
out, err = p.communicate()
|
||||||
ret_code = p.returncode
|
ret_code = p.returncode
|
||||||
log.debug('output: {o}'.format(o=out))
|
logger.debug("output: %s", out)
|
||||||
|
|
||||||
if ret_code == 0:
|
if ret_code == 0:
|
||||||
self.read_state_file()
|
self.read_state_file()
|
||||||
else:
|
else:
|
||||||
log.warning('error: {e}'.format(e=err))
|
logger.warning("error: %s", err)
|
||||||
|
|
||||||
self.temp_var_files.clean_up()
|
self.temp_var_files.clean_up()
|
||||||
if capture_output is True:
|
if capture_output is True:
|
||||||
out = out.decode('utf-8')
|
out = out.decode()
|
||||||
err = err.decode('utf-8')
|
err = err.decode()
|
||||||
else:
|
else:
|
||||||
out = None
|
out = None
|
||||||
err = None
|
err = None
|
||||||
|
|
||||||
if ret_code != 0 and raise_on_error:
|
if ret_code and raise_on_error:
|
||||||
raise TerraformCommandError(
|
raise TerraformCommandError(ret_code, " ".join(cmds), out=out, err=err)
|
||||||
ret_code, ' '.join(cmds), out=out, err=err)
|
|
||||||
|
|
||||||
return ret_code, out, err
|
return ret_code, out, err
|
||||||
|
|
||||||
def output(self, *args, **kwargs):
|
def output(self, *args, **kwargs):
|
||||||
"""
|
"""Refer https://www.terraform.io/docs/commands/output.html
|
||||||
https://www.terraform.io/docs/commands/output.html
|
|
||||||
|
|
||||||
Note that this method does not conform to the (ret_code, out, err) return convention. To use
|
Note that this method does not conform to the (ret_code, out, err) return
|
||||||
the "output" command with the standard convention, call "output_cmd" instead of
|
convention. To use the "output" command with the standard convention,
|
||||||
"output".
|
call "output_cmd" instead of "output".
|
||||||
|
|
||||||
:param args: Positional arguments. There is one optional positional
|
:param args: Positional arguments. There is one optional positional
|
||||||
argument NAME; if supplied, the returned output text
|
argument NAME; if supplied, the returned output text
|
||||||
will be the json for a single named output value.
|
will be the json for a single named output value.
|
||||||
:param kwargs: Named options, passed to the command. In addition,
|
:param kwargs: Named options, passed to the command. In addition,
|
||||||
'full_value': If True, and NAME is provided, then
|
'full_value': If True, and NAME is provided, then
|
||||||
the return value will be a dict with
|
the return value will be a dict with
|
||||||
"value', 'type', and 'sensitive'
|
"value', 'type', and 'sensitive'
|
||||||
|
@ -357,15 +359,15 @@ class Terraform(object):
|
||||||
dict of named dicts each with 'value', 'sensitive', and 'type',
|
dict of named dicts each with 'value', 'sensitive', and 'type',
|
||||||
if NAME is not provided
|
if NAME is not provided
|
||||||
"""
|
"""
|
||||||
full_value = kwargs.pop('full_value', False)
|
full_value = kwargs.pop("full_value", False)
|
||||||
name_provided = (len(args) > 0)
|
name_provided = bool(len(args))
|
||||||
kwargs['json'] = IsFlagged
|
kwargs["json"] = IsFlagged
|
||||||
if not kwargs.get('capture_output', True) is True:
|
if not kwargs.get("capture_output", True) is True:
|
||||||
raise ValueError('capture_output is required for this method')
|
raise ValueError("capture_output is required for this method")
|
||||||
|
|
||||||
ret, out, err = self.output_cmd(*args, **kwargs)
|
ret, out, err = self.output_cmd(*args, **kwargs)
|
||||||
|
|
||||||
if ret != 0:
|
if ret:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
out = out.lstrip()
|
out = out.lstrip()
|
||||||
|
@ -373,68 +375,63 @@ class Terraform(object):
|
||||||
value = json.loads(out)
|
value = json.loads(out)
|
||||||
|
|
||||||
if name_provided and not full_value:
|
if name_provided and not full_value:
|
||||||
value = value['value']
|
value = value["value"]
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def read_state_file(self, file_path=None):
|
def read_state_file(self, file_path=None):
|
||||||
"""
|
"""Read .tfstate file
|
||||||
read .tfstate file
|
|
||||||
:param file_path: relative path to working dir
|
:param file_path: relative path to working dir
|
||||||
:return: states file in dict type
|
:return: states file in dict type
|
||||||
"""
|
"""
|
||||||
|
|
||||||
working_dir = self.working_dir or ''
|
working_dir = self.working_dir or ""
|
||||||
|
|
||||||
file_path = file_path or self.state or ''
|
file_path = file_path or self.state or ""
|
||||||
|
|
||||||
if not file_path:
|
if not file_path:
|
||||||
backend_path = os.path.join(file_path, '.terraform',
|
backend_path = os.path.join(file_path, ".terraform", "terraform.tfstate")
|
||||||
'terraform.tfstate')
|
|
||||||
|
|
||||||
if os.path.exists(os.path.join(working_dir, backend_path)):
|
if os.path.exists(os.path.join(working_dir, backend_path)):
|
||||||
file_path = backend_path
|
file_path = backend_path
|
||||||
else:
|
else:
|
||||||
file_path = os.path.join(file_path, 'terraform.tfstate')
|
file_path = os.path.join(file_path, "terraform.tfstate")
|
||||||
|
|
||||||
file_path = os.path.join(working_dir, file_path)
|
file_path = os.path.join(working_dir, file_path)
|
||||||
|
|
||||||
self.tfstate = Tfstate.load_file(file_path)
|
self.tfstate = Tfstate.load_file(file_path)
|
||||||
|
|
||||||
def set_workspace(self, workspace, *args, **kwargs):
|
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, *args, **kwargs)
|
||||||
return self.cmd('workspace', 'select', workspace, *args, **kwargs)
|
|
||||||
|
|
||||||
def create_workspace(self, workspace, *args, **kwargs):
|
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, *args, **kwargs)
|
||||||
return self.cmd('workspace', 'new', workspace, *args, **kwargs)
|
|
||||||
|
|
||||||
def delete_workspace(self, workspace, *args, **kwargs):
|
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, *args, **kwargs)
|
||||||
return self.cmd('workspace', 'delete', workspace, *args, **kwargs)
|
|
||||||
|
|
||||||
def show_workspace(self, **kwargs):
|
def show_workspace(self, **kwargs):
|
||||||
"""
|
"""Show workspace, this command does not need the [DIR] part
|
||||||
show workspace, this command does not need the [DIR] part
|
|
||||||
:return: workspace
|
:return: workspace
|
||||||
"""
|
"""
|
||||||
|
return self.cmd("workspace", "show", **kwargs)
|
||||||
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()
|
||||||
|
@ -445,11 +442,12 @@ class VariableFiles(object):
|
||||||
self.files = []
|
self.files = []
|
||||||
|
|
||||||
def create(self, variables):
|
def create(self, variables):
|
||||||
with tempfile.NamedTemporaryFile('w+t', suffix='.tfvars.json', delete=False) as temp:
|
with tempfile.NamedTemporaryFile(
|
||||||
log.debug('{0} is created'.format(temp.name))
|
"w+t", suffix=".tfvars.json", delete=False
|
||||||
|
) as temp:
|
||||||
|
logger.debug("%s is created", temp.name)
|
||||||
self.files.append(temp)
|
self.files.append(temp)
|
||||||
log.debug(
|
logger.debug("variables wrote to tempfile: %s", variables)
|
||||||
'variables wrote to tempfile: {0}'.format(str(variables)))
|
|
||||||
temp.write(json.dumps(variables))
|
temp.write(json.dumps(variables))
|
||||||
file_name = temp.name
|
file_name = temp.name
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# above is for compatibility of python2.7.11
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Tfstate(object):
|
class Tfstate:
|
||||||
def __init__(self, data=None):
|
def __init__(self, data=None):
|
||||||
self.tfstate_file = None
|
self.tfstate_file = None
|
||||||
self.native_data = data
|
self.native_data = data
|
||||||
|
@ -17,10 +14,11 @@ class Tfstate(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_file(file_path):
|
def load_file(file_path):
|
||||||
|
"""Read the tfstate file and load its contents.
|
||||||
|
|
||||||
|
Parses then as JSON and put the result into the object.
|
||||||
"""
|
"""
|
||||||
Read the tfstate file and load its contents, parses then as JSON and put the result into the object
|
logger.debug('read data from %s', file_path)
|
||||||
"""
|
|
||||||
log.debug('read data from {0}'.format(file_path))
|
|
||||||
if os.path.exists(file_path):
|
if os.path.exists(file_path):
|
||||||
with open(file_path) as f:
|
with open(file_path) as f:
|
||||||
json_data = json.load(f)
|
json_data = json.load(f)
|
||||||
|
@ -29,6 +27,6 @@ class Tfstate(object):
|
||||||
tf_state.tfstate_file = file_path
|
tf_state.tfstate_file = file_path
|
||||||
return tf_state
|
return tf_state
|
||||||
|
|
||||||
log.debug('{0} is not exist'.format(file_path))
|
logger.debug('%s is not exist', file_path)
|
||||||
|
|
||||||
return Tfstate()
|
return Tfstate()
|
||||||
|
|
Loading…
Reference in a new issue