From adb1fa06a41b18976491aa7322a959af64e29815 Mon Sep 17 00:00:00 2001 From: jem Date: Fri, 25 Jun 2021 16:50:06 +0200 Subject: [PATCH] add refresh & option to output latest cmd --- build/lib/python_terraform/__init__.py | 8 + build/lib/python_terraform/terraform.py | 509 ++++++++++++++++++ build/lib/python_terraform/tfstate.py | 33 ++ dist/dda-python-terraform-0.14.0.tar.gz | Bin 0 -> 11493 bytes dist/dda-python-terraform-1.0.0.dev0.tar.gz | Bin 0 -> 11524 bytes ...thon_terraform-0.14.0-py2.py3-none-any.whl | Bin 0 -> 7983 bytes ..._terraform-1.0.0.dev0-py2.py3-none-any.whl | Bin 0 -> 8061 bytes python_terraform/terraform.py | 26 + setup.py | 4 +- 9 files changed, 578 insertions(+), 2 deletions(-) create mode 100644 build/lib/python_terraform/__init__.py create mode 100644 build/lib/python_terraform/terraform.py create mode 100644 build/lib/python_terraform/tfstate.py create mode 100644 dist/dda-python-terraform-0.14.0.tar.gz create mode 100644 dist/dda-python-terraform-1.0.0.dev0.tar.gz create mode 100644 dist/dda_python_terraform-0.14.0-py2.py3-none-any.whl create mode 100644 dist/dda_python_terraform-1.0.0.dev0-py2.py3-none-any.whl diff --git a/build/lib/python_terraform/__init__.py b/build/lib/python_terraform/__init__.py new file mode 100644 index 0000000..c2e2c10 --- /dev/null +++ b/build/lib/python_terraform/__init__.py @@ -0,0 +1,8 @@ +from .terraform import ( + IsFlagged, + IsNotFlagged, + Terraform, + TerraformCommandError, + VariableFiles, +) +from .tfstate import Tfstate diff --git a/build/lib/python_terraform/terraform.py b/build/lib/python_terraform/terraform.py new file mode 100644 index 0000000..3f61a1b --- /dev/null +++ b/build/lib/python_terraform/terraform.py @@ -0,0 +1,509 @@ +import json +import logging +import os +import subprocess +import sys +import tempfile +from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, Union + +from python_terraform.tfstate import Tfstate + +logger = logging.getLogger(__name__) + +COMMAND_WITH_SUBCOMMANDS = {"workspace"} + + +class TerraformFlag: + pass + + +class IsFlagged(TerraformFlag): + pass + + +class IsNotFlagged(TerraformFlag): + pass + + +CommandOutput = Tuple[Optional[int], Optional[str], Optional[str]] + + +class TerraformCommandError(subprocess.CalledProcessError): + def __init__(self, ret_code: int, cmd: str, out: Optional[str], err: Optional[str]): + super(TerraformCommandError, self).__init__(ret_code, cmd) + self.out = out + self.err = err + logger.error("Error with command %s. Reason: %s", self.cmd, self.err) + + +class Terraform: + """Wrapper of terraform command line tool. + + https://www.terraform.io/ + """ + + def __init__( + self, + working_dir: Optional[str] = None, + targets: Optional[Sequence[str]] = None, + state: Optional[str] = None, + variables: Optional[Dict[str, str]] = None, + parallelism: Optional[str] = None, + var_file: Optional[str] = None, + terraform_bin_path: Optional[str] = None, + is_env_vars_included: bool = True, + ): + """ + :param working_dir: the folder of the working folder, if not given, + will be current working folder + :param targets: list of target + as default value of apply/destroy/plan command + :param state: path of state file relative to working folder, + as a default value of apply/destroy/plan command + :param variables: default variables for apply/destroy/plan command, + will be override by variable passing by apply/destroy/plan method + :param parallelism: default parallelism value for apply/destroy command + :param var_file: passed as value of -var-file option, + could be string or list, list stands for multiple -var-file option + :param terraform_bin_path: binary path of terraform + :type is_env_vars_included: bool + :param is_env_vars_included: included env variables when calling terraform cmd + """ + self.is_env_vars_included = is_env_vars_included + self.working_dir = working_dir + self.state = state + self.targets = [] if targets is None else targets + self.variables = dict() if variables is None else variables + self.parallelism = parallelism + self.terraform_bin_path = ( + terraform_bin_path if terraform_bin_path else "terraform" + ) + self.var_file = var_file + self.temp_var_files = VariableFiles() + + # store the tfstate data + self.tfstate = None + self.read_state_file(self.state) + + self.latest_cmd = '' + + def __getattr__(self, item: str) -> Callable: + def wrapper(*args, **kwargs): + cmd_name = str(item) + if cmd_name.endswith("_cmd"): + cmd_name = cmd_name[:-4] + logger.debug("called with %r and %r", args, kwargs) + return self.cmd(cmd_name, *args, **kwargs) + + return wrapper + + def apply( + self, + dir_or_plan: Optional[str] = None, + input: bool = False, + skip_plan: bool = True, + no_color: Type[TerraformFlag] = IsFlagged, + **kwargs, + ) -> CommandOutput: + """Refer to https://terraform.io/docs/commands/apply.html + + no-color is flagged by default + :param no_color: disable color of stdout + :param input: disable prompt for a missing variable + :param dir_or_plan: folder relative to working folder + :param skip_plan: force apply without plan (default: false) + :param kwargs: same as kwags in method 'cmd' + :returns return_code, stdout, stderr + """ + if not skip_plan: + return self.plan(dir_or_plan=dir_or_plan, **kwargs) + default = kwargs.copy() + default["input"] = input + default["no_color"] = no_color + default["auto-approve"] = True # a False value will require an input + option_dict = self._generate_default_options(default) + args = self._generate_default_args(dir_or_plan) + return self.cmd("apply", *args, **option_dict) + + def refresh( + self, + dir_or_plan: Optional[str] = None, + input: bool = False, + no_color: Type[TerraformFlag] = IsFlagged, + **kwargs, + ) -> CommandOutput: + """Refer to https://terraform.io/docs/commands/refresh.html + + no-color is flagged by default + :param no_color: disable color of stdout + :param input: disable prompt for a missing variable + :param dir_or_plan: folder relative to working folder + :param kwargs: same as kwags in method 'cmd' + :returns return_code, stdout, stderr + """ + default = kwargs.copy() + default["input"] = input + default["no_color"] = no_color + option_dict = self._generate_default_options(default) + args = self._generate_default_args(dir_or_plan) + return self.cmd("refresh", *args, **option_dict) + + def _generate_default_args(self, dir_or_plan: Optional[str]) -> Sequence[str]: + return [dir_or_plan] if dir_or_plan else [] + + def _generate_default_options( + self, input_options: Dict[str, Any] + ) -> Dict[str, Any]: + return { + "state": self.state, + "target": self.targets, + "var": self.variables, + "var_file": self.var_file, + "parallelism": self.parallelism, + "no_color": IsFlagged, + "input": False, + **input_options, + } + + def destroy( + self, + dir_or_plan: Optional[str] = None, + force: Type[TerraformFlag] = IsFlagged, + **kwargs, + ) -> CommandOutput: + """Refer to https://www.terraform.io/docs/commands/destroy.html + + force/no-color option is flagged by default + :return: ret_code, stdout, stderr + """ + default = kwargs.copy() + default["force"] = force + options = self._generate_default_options(default) + args = self._generate_default_args(dir_or_plan) + return self.cmd("destroy", *args, **options) + + def plan( + self, + dir_or_plan: Optional[str] = None, + detailed_exitcode: Type[TerraformFlag] = IsFlagged, + **kwargs, + ) -> CommandOutput: + """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 + :return: ret_code, stdout, stderr + """ + options = kwargs.copy() + options["detailed_exitcode"] = detailed_exitcode + options = self._generate_default_options(options) + args = self._generate_default_args(dir_or_plan) + return self.cmd("plan", *args, **options) + + def init( + self, + dir_or_plan: Optional[str] = None, + backend_config: Optional[Dict[str, str]] = None, + reconfigure: Type[TerraformFlag] = IsFlagged, + backend: bool = True, + **kwargs, + ) -> CommandOutput: + """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 dir_or_plan: relative path to the folder want to init + :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 backend: whether or not to use backend settings for init + :param kwargs: options + :return: ret_code, stdout, stderr + """ + options = kwargs.copy() + options.update( + { + "backend_config": backend_config, + "reconfigure": reconfigure, + "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: str, *args, **kwargs) -> List[str]: + """For any generate_cmd_string doesn't written as public method of Terraform + + examples: + 1. call import command, + ref to https://www.terraform.io/docs/commands/import.html + --> generate_cmd_string call: + terraform import -input=true aws_instance.foo i-abcd1234 + --> python call: + tf.generate_cmd_string('import', 'aws_instance.foo', 'i-abcd1234', input=True) + + 2. call apply command, + --> generate_cmd_string call: + terraform apply -var='a=b' -var='c=d' -no-color the_folder + --> python call: + tf.generate_cmd_string('apply', the_folder, no_color=IsFlagged, var={'a':'b', 'c':'d'}) + + :param cmd: command and sub-command of terraform, seperated with space + refer to https://www.terraform.io/docs/commands/index.html + :param args: arguments of a command + :param kwargs: same as kwags in method 'cmd' + :return: string of valid terraform command + """ + cmds = cmd.split() + 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(): + if "_" in option: + option = option.replace("_", "-") + + if isinstance(value, list): + for sub_v in value: + cmds += [f"-{option}={sub_v}"] + continue + + if isinstance(value, dict): + if "backend-config" in option: + for bk, bv in value.items(): + cmds += [f"-backend-config={bk}={bv}"] + continue + + # since map type sent in string won't work, create temp var file for + # variables, and clean it up later + elif option == "var": + # 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 + if value: + filename = self.temp_var_files.create(value) + cmds += [f"-var-file={filename}"] + + continue + + # simple flag, + if value is IsFlagged: + cmds += [f"-{option}"] + continue + + if value is None or value is IsNotFlagged: + continue + + if isinstance(value, bool): + value = "true" if value else "false" + + cmds += [f"-{option}={value}"] + + cmds += args + self.latest_cmd = ' '.join(cmds) + return cmds + + def cmd( + self, + cmd: str, + *args, + capture_output: Union[bool, str] = True, + raise_on_error: bool = True, + synchronous: bool = True, + **kwargs, + ) -> CommandOutput: + """Run a terraform command, if success, will try to read state file + + :param cmd: command and sub-command of terraform, seperated with space + refer to https://www.terraform.io/docs/commands/index.html + :param args: arguments of a command + :param kwargs: any option flag with key value without prefixed dash character + if there's a dash in the option name, use under line instead of dash, + ex. -no-color --> no_color + if it's a simple flag with no value, value should be IsFlagged + ex. cmd('taint', allow_missing=IsFlagged) + if it's a boolean value flag, assign True or false + if it's a flag could be used multiple times, assign list to it's value + if it's a "var" variable flag, assign dictionary to it + if a value is None, will skip this option + if the option 'capture_output' is passed (with any value other than + True), terraform output will be printed to stdout/stderr and + "None" will be returned as out and err. + if the option 'raise_on_error' is passed (with any value that evaluates to True), + and the terraform command returns a nonzerop return code, then + a TerraformCommandError exception will be raised. The exception object will + have the following properties: + returncode: The command's return code + out: The captured stdout, or None if not captured + err: The captured stderr, or None if not captured + :return: ret_code, out, err + """ + if capture_output is True: + stderr = subprocess.PIPE + stdout = subprocess.PIPE + elif capture_output == "framework": + stderr = None + stdout = None + else: + stderr = sys.stderr + stdout = sys.stdout + + cmds = self.generate_cmd_string(cmd, *args, **kwargs) + logger.info("Command: %s", " ".join(cmds)) + + working_folder = self.working_dir if self.working_dir else None + + environ_vars = {} + if self.is_env_vars_included: + environ_vars = os.environ.copy() + + p = subprocess.Popen( + cmds, stdout=stdout, stderr=stderr, cwd=working_folder, env=environ_vars + ) + + if not synchronous: + return None, None, None + + out, err = p.communicate() + ret_code = p.returncode + logger.info("output: %s", out) + + if ret_code == 0: + self.read_state_file() + else: + logger.warning("error: %s", err) + + self.temp_var_files.clean_up() + if capture_output is True: + out = out.decode() + err = err.decode() + else: + out = None + err = None + + if ret_code and raise_on_error: + raise TerraformCommandError(ret_code, " ".join(cmds), out=out, err=err) + + return ret_code, out, err + + def output( + self, *args, capture_output: bool = True, **kwargs + ) -> Union[None, str, Dict[str, str], Dict[str, Dict[str, str]]]: + """Refer 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 the "output" command with the standard convention, + call "output_cmd" instead of "output". + + :param args: Positional arguments. There is one optional positional + argument NAME; if supplied, the returned output text + will be the json for a single named output value. + :param kwargs: Named options, passed to the command. In addition, + 'full_value': If True, and NAME is provided, then + the return value will be a dict with + "value', 'type', and 'sensitive' + properties. + :return: None, if an error occured + Output value as a string, if NAME is provided and full_value + is False or not provided + Output value as a dict with 'value', 'sensitive', and 'type' if + NAME is provided and full_value is True. + dict of named dicts each with 'value', 'sensitive', and 'type', + if NAME is not provided + """ + kwargs["json"] = IsFlagged + if capture_output is False: + raise ValueError("capture_output is required for this method") + + ret, out, _ = self.output_cmd(*args, **kwargs) + + if ret: + return None + + return json.loads(out.lstrip()) + + def read_state_file(self, file_path=None) -> None: + """Read .tfstate file + + :param file_path: relative path to working dir + :return: states file in dict type + """ + + working_dir = self.working_dir or "" + + file_path = file_path or self.state or "" + + if not file_path: + backend_path = os.path.join(file_path, ".terraform", "terraform.tfstate") + + 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) + + def set_workspace(self, workspace, *args, **kwargs) -> CommandOutput: + """Set workspace + + :param workspace: the desired workspace. + :return: status + """ + return self.cmd("workspace", "select", workspace, *args, **kwargs) + + def create_workspace(self, workspace, *args, **kwargs) -> CommandOutput: + """Create workspace + + :param workspace: the desired workspace. + :return: status + """ + return self.cmd("workspace", "new", workspace, *args, **kwargs) + + def delete_workspace(self, workspace, *args, **kwargs) -> CommandOutput: + """Delete workspace + + :param workspace: the desired workspace. + :return: status + """ + return self.cmd("workspace", "delete", workspace, *args, **kwargs) + + def show_workspace(self, **kwargs) -> CommandOutput: + """Show workspace, this command does not need the [DIR] part + + :return: workspace + """ + return self.cmd("workspace", "show", **kwargs) + + def __exit__(self, exc_type, exc_value, traceback) -> None: + self.temp_var_files.clean_up() + + +class VariableFiles: + def __init__(self): + self.files = [] + + def create(self, variables: Dict[str, str]) -> str: + with tempfile.NamedTemporaryFile( + "w+t", suffix=".tfvars.json", delete=False + ) as temp: + logger.debug("%s is created", temp.name) + self.files.append(temp) + logger.debug("variables wrote to tempfile: %s", variables) + temp.write(json.dumps(variables)) + file_name = temp.name + + return file_name + + def clean_up(self): + for f in self.files: + os.unlink(f.name) + + self.files = [] diff --git a/build/lib/python_terraform/tfstate.py b/build/lib/python_terraform/tfstate.py new file mode 100644 index 0000000..f39af0f --- /dev/null +++ b/build/lib/python_terraform/tfstate.py @@ -0,0 +1,33 @@ +import json +import logging +import os +from typing import Dict, Optional + +logger = logging.getLogger(__name__) + + +class Tfstate: + def __init__(self, data: Optional[Dict[str, str]] = None): + self.tfstate_file: Optional[str] = None + self.native_data = data + if data: + self.__dict__ = data + + @staticmethod + def load_file(file_path: str) -> "Tfstate": + """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) + if os.path.exists(file_path): + with open(file_path) as f: + json_data = json.load(f) + + tf_state = Tfstate(json_data) + tf_state.tfstate_file = file_path + return tf_state + + logger.debug("%s does not exist", file_path) + + return Tfstate() diff --git a/dist/dda-python-terraform-0.14.0.tar.gz b/dist/dda-python-terraform-0.14.0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..0b16b0ebfc429f279a00e998b5c62c65daf65ff2 GIT binary patch literal 11493 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 literal 0 HcmV?d00001 diff --git a/dist/dda-python-terraform-1.0.0.dev0.tar.gz b/dist/dda-python-terraform-1.0.0.dev0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..233130e3fa226476264258817bc7384d278bb691 GIT binary patch literal 11524 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 literal 0 HcmV?d00001 diff --git a/dist/dda_python_terraform-1.0.0.dev0-py2.py3-none-any.whl b/dist/dda_python_terraform-1.0.0.dev0-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..00f3d6c2aee3424f3a9e09fd01779fe48e4974c5 GIT binary patch literal 8061 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 Callable: def wrapper(*args, **kwargs): cmd_name = str(item) @@ -123,6 +125,29 @@ class Terraform: args = self._generate_default_args(dir_or_plan) return self.cmd("apply", *args, **option_dict) + def refresh( + self, + dir_or_plan: Optional[str] = None, + input: bool = False, + no_color: Type[TerraformFlag] = IsFlagged, + **kwargs, + ) -> CommandOutput: + """Refer to https://terraform.io/docs/commands/refresh.html + + no-color is flagged by default + :param no_color: disable color of stdout + :param input: disable prompt for a missing variable + :param dir_or_plan: folder relative to working folder + :param kwargs: same as kwags in method 'cmd' + :returns return_code, stdout, stderr + """ + default = kwargs.copy() + default["input"] = input + default["no_color"] = no_color + option_dict = self._generate_default_options(default) + args = self._generate_default_args(dir_or_plan) + return self.cmd("refresh", *args, **option_dict) + def _generate_default_args(self, dir_or_plan: Optional[str]) -> Sequence[str]: return [dir_or_plan] if dir_or_plan else [] @@ -281,6 +306,7 @@ class Terraform: cmds += [f"-{option}={value}"] cmds += args + self.latest_cmd = ' '.join(cmds) return cmds def cmd( diff --git a/setup.py b/setup.py index ad38e19..e0315cb 100644 --- a/setup.py +++ b/setup.py @@ -21,14 +21,14 @@ except IOError: setup( name=module_name, - version="0.14.0", + version="1.0.0-dev", url="https://github.com/DomainDrivenArchitecture/python-terraform", license="MIT", author="Freddy Tan", author_email="beelit94@gmail.com", description=short_description, long_description=long_description, - packages=["dda_python_terraform"], + packages=["python_terraform"], package_data={}, platforms="any", install_requires=dependencies,