Ejemplo n.º 1
0
    def test_execute_command(self):
        """Test executing a command successfully"""
        self.mock_process.poll.return_value = 0
        exit_code, stdout = execute_command(['echo', '1 '])

        self.assertEqual(self.mock_popen.call_count, 1)
        self.assertEqual(exit_code, 0)
        self.assertEqual(stdout, [])
Ejemplo n.º 2
0
    def test_execute_command_retry(self, mock_open, mock_network_error):
        """Test retrying execution because of network errors"""
        self.mock_process.poll.side_effect = [1, 1, 1, 0]
        mock_network_error.side_effect = [["Throttling"], []]
        mock_stdout_read = mock_open.return_value
        mock_stdout_read.readline.return_value = b''

        exit_code, stdout = execute_command(['echo', '1'], retry=True)

        self.assertEqual(self.mock_popen.call_count, 2)
        self.assertEqual(exit_code, 0)
        self.assertEqual(stdout, [])
Ejemplo n.º 3
0
    def test_execute_command_max_retry(self, mock_open, mock_network_error):
        """Test retrying execution because of network errors up to 5 times"""
        self.mock_process.poll.return_value = 255
        mock_network_error.side_effect = [["Throttling"], ["unexpected EOF"],
                                          ["Throttling"], ["unexpected EOF"],
                                          ["Throttling"], ["unexpected EOF"]]
        mock_stdout_read = mock_open.return_value
        mock_stdout_read.readline.return_value = b''

        exit_code, stdout = execute_command(['echo', '1'], retry=True)

        self.assertEqual(self.mock_popen.call_count, MAX_RETRIES)
        self.assertEqual(exit_code, 255)
        self.assertEqual(stdout, [])
Ejemplo n.º 4
0
    def execute(self,
                operation: str,
                debug: bool = False) -> Tuple[int, List[str], bool]:
        """
        Function for executing this pipeline entry.
        :param operation: The Terraform operation to execute. IE: apply, plan
        :param debug: True if Terraform debug info should be printed.
        :return: A tuple of the exit code, output of the command, and whether changes were detected.
        """
        command_env = os.environ.copy()
        command_env.update(self.envvars)

        if debug:
            command_env["TF_LOG"] = "DEBUG"

        # pylint: disable=unused-variable
        plan_file, plan_file_name = tempfile.mkstemp(suffix=".tfplan")

        # We're using --no-resolve-envvars here because we've already resolved the environment variables in
        # the constructor. We are then passing in those environment variables explicitly in the
        # execute_command call below.
        base_args = ["tf", "--no-resolve-envvars", self.path]
        init_args = base_args + ["init"] + self.variables
        plan_args = base_args + [
            "plan", "-detailed-exitcode", f"-out={plan_file_name}"
        ] + self.variables
        operation_args = base_args + [operation] + self.variables

        if operation in ["apply", "destroy"]:
            operation_args += ["-auto-approve"]

        init_exit_code, output = execute_command(
            init_args,
            print_output=False,
            capture_stderr=True,
            env=command_env,
        )

        if init_exit_code != 0:
            return init_exit_code, output, True

        # We need to plan first before we apply so we can actually see a plan... Thanks Hashicorp
        if operation in ["apply"]:
            plan_exit_code, plan_stdout = execute_command(plan_args,
                                                          print_output=False,
                                                          capture_stderr=True,
                                                          env=command_env)

            output += ["\n"] + plan_stdout

            if plan_exit_code != 2:
                return (
                    plan_exit_code,
                    output,
                    False,
                )

            operation_args += [plan_file_name]

        if operation in ["plan"]:
            operation_args += ["--detailed-exitcode"]

        operation_exit_code, operation_stdout = execute_command(
            operation_args,
            print_output=False,
            capture_stderr=True,
            env=command_env)

        output += ["\n"] + operation_stdout

        changes_detected = True
        if operation in ["plan"]:
            if operation_exit_code == 2:
                # Set exit code to 0 so the command doesn't fail out
                operation_exit_code = 0
            elif operation_exit_code != 2:
                changes_detected = False

        return (
            operation_exit_code,
            output,
            changes_detected,
        )
Ejemplo n.º 5
0
    def execute(self,
                operation: str,
                debug: bool = False) -> Tuple[int, List[str], bool]:
        """
        Function for executing this Graph Entry.
        :param operation: The Terraform operation to execute. IE: apply, plan
        :param debug: True if Terraform debug info should be printed.
        :return: A tuple of the exit code, output of the command, and whether changes were detected.
        """
        print(f"Executing {self.abs_path} {operation} ...")
        self.state = "Executing"
        command_env = os.environ.copy()
        command_env.update(self.envvars)

        if debug:
            command_env["TF_LOG"] = "DEBUG"

        # We're using --no-resolve-envvars here because we've already resolved the environment variables in
        # the constructor. We are then passing in those environment variables explicitly in the
        # execute_command call below.
        base_args = ["tf", "--no-resolve-envvars", self.abs_path]
        init_args = base_args + ["init"] + self.variables
        operation_args = base_args + [operation] + self.variables

        init_exit_code, init_stdout = execute_command(
            init_args,
            print_output=False,
            capture_stderr=True,
            env=command_env,
            audit_api_url=self.wrapper_config.audit_api_url,
            cwd=self.abs_path)
        if init_exit_code != 0:
            self.state = "Failed"
            return init_exit_code, init_stdout, True

        shell = False
        if operation in ["apply", "destroy"]:
            operation_args = ["yes", "yes", "|"] + operation_args
            shell = True

        operation_exit_code, operation_stdout = execute_command(
            " ".join(operation_args) if shell else operation_args,
            print_output=False,
            capture_stderr=True,
            env=command_env,
            shell=shell,
            audit_api_url=self.wrapper_config.audit_api_url,
            cwd=self.path)

        if operation_exit_code == 0:
            self.state = "Success"
        else:
            self.state = "Failed"

        changes_detected = True
        if any("Resources: 0 added, 0 changed, 0 destroyed" in line
               for line in operation_stdout):
            changes_detected = False

        print(f"\nFinished executing {self.abs_path} {operation} ...")
        return (
            operation_exit_code,
            init_stdout + ["\n"] + operation_stdout,
            changes_detected,
        )
Ejemplo n.º 6
0
    def execute(self,
                operation: str,
                debug: bool = False) -> Tuple[int, List[str], bool]:
        """
        Function for executing this Graph Entry.
        :param operation: The Terraform operation to execute. IE: apply, plan
        :param debug: True if Terraform debug info should be printed.
        :return: A tuple of the exit code, output of the command, and whether changes were detected.
        """
        self.state = "Executing"
        command_env = os.environ.copy()
        command_env.update(self.envvars)

        if debug:
            command_env["TF_LOG"] = "DEBUG"

        # pylint: disable=unused-variable
        plan_file, plan_file_name = tempfile.mkstemp(suffix=".tfplan")

        # We're using --no-resolve-envvars here because we've already resolved the environment variables in
        # the constructor. We are then passing in those environment variables explicitly in the
        # execute_command call below.
        base_args = ["tf", "--no-resolve-envvars", self.path]
        init_args = base_args + ["init"] + self.variables
        plan_args = base_args + [
            "plan", "-detailed-exitcode",
            "-out=%s" % plan_file_name
        ] + self.variables
        operation_args = base_args + [operation] + self.variables

        if operation in ["apply", "destroy"]:
            operation_args += ["-auto-approve"]

        init_exit_code, init_stdout = execute_command(
            init_args,
            print_output=False,
            capture_stderr=True,
            env=command_env,
        )
        if init_exit_code != 0:
            self.state = "Failed"
            return init_exit_code, init_stdout, True
        if operation in ["apply"]:
            plan_exit_code, plan_stdout = execute_command(plan_args,
                                                          print_output=False,
                                                          capture_stderr=True,
                                                          env=command_env)
            operation_args += [plan_file_name]
        else:
            plan_exit_code = 0
            plan_stdout = []

        changes_detected = plan_exit_code != 0
        if plan_exit_code in (0, 2):
            self.state = "Success"
        else:
            self.state = "Failed"

        if plan_exit_code != 2:
            return (
                plan_exit_code,
                init_stdout + ["\n"] + plan_stdout,
                changes_detected,
            )

        operation_exit_code, operation_stdout = execute_command(
            operation_args,
            print_output=False,
            capture_stderr=True,
            env=command_env)

        if operation_exit_code == 0:
            self.state = "Success"

        else:
            self.state = "Failed"

        return (
            operation_exit_code,
            init_stdout + ["\n"] + plan_stdout + ["\n"] + operation_stdout,
            changes_detected,
        )