diff --git a/terrat_runner/work_apply.py b/terrat_runner/work_apply.py index 48629a32..533f6a71 100644 --- a/terrat_runner/work_apply.py +++ b/terrat_runner/work_apply.py @@ -1,69 +1,13 @@ -import base64 -import hashlib -import json import logging import os -import string import tempfile -import cmd import repo_config as rc -import requests_retry import work_exec import workflow_step import workflow_step_terrateam_ssh_key_setup -def _load_plan(state, work_token, api_base_url, dir_path, workspace, plan_path): - res = requests_retry.get(api_base_url + '/v1/work-manifests/' + work_token + '/plans', - params={'path': dir_path, 'workspace': workspace}) - - if res.status_code != 200: - return (False, 'Could not load plan from backend') - - plan_data = base64.b64decode(res.json()['data']) - - try: - plan_data = json.loads(plan_data) - - if plan_data['method'] == 'terrateam': - plan_data_encoded = plan_data['data'] - plan_data_raw = base64.b64decode(plan_data_encoded) - logging.debug('APPLY : LOAD_PLAN : dir_path=%s : workspace=%s : md5=%s', - dir_path, - workspace, - hashlib.md5(plan_data_raw).hexdigest()) - - with open(plan_path, 'wb') as f: - f.write(plan_data_raw) - - return (True, None) - elif plan_data['method'] == 'cmd': - tmpl_vars = { - 'plan_dst_path': plan_path - } - fetch_cmd = [string.Template(s).safe_substitute(tmpl_vars) for s in plan_data['fetch']] - proc = cmd.run(state, {'cmd': fetch_cmd}) - if proc.returncode != 0: - return (False, 'Failed to fetch plan, see action logs for more details') - if plan_data.get('delete'): - cmd.run(state, {'cmd': plan_data['delete']}) - return (True, None) - else: - raise Exception('Unknown method') - except json.JSONDecodeError: - plan_data_raw = plan_data - logging.debug('APPLY : LOAD_PLAN : dir_path=%s : workspace=%s : md5=%s', - dir_path, - workspace, - hashlib.md5(plan_data_raw).hexdigest()) - - with open(plan_path, 'wb') as f: - f.write(plan_data_raw) - - return (True, None) - - class Exec(work_exec.ExecInterface): def pre_hooks(self, state): pre_hooks = rc.get_all_hooks(state.repo_config)['pre'] @@ -94,77 +38,51 @@ def exec(self, state, d): workspace = d['workspace'] workflow_idx = d.get('workflow') - (success, output) = _load_plan(state, - state.work_token, - state.api_base_url, - path, - workspace, - os.path.join(tmpdir, 'plan')) - - if not success: - outputs = [ - { - 'success': False, - 'outputs': {'text': output}, - 'workflow_step': { - 'type': 'init' - } - } - ] - state = state._replace(failed=True) - result = { - 'path': path, - 'workspace': workspace, - 'success': not state.failed, - 'outputs': outputs, - } - return (state, result) + env = state.env.copy() + env['TERRATEAM_PLAN_FILE'] = os.path.join(tmpdir, 'plan') + env['TERRATEAM_DIR'] = path + env['TERRATEAM_WORKSPACE'] = workspace + env['TERRATEAM_TMPDIR'] = tmpdir + + if workflow_idx is None: + workflow = rc.get_default_workflow(state.repo_config) else: - env = state.env.copy() - env['TERRATEAM_PLAN_FILE'] = os.path.join(tmpdir, 'plan') - env['TERRATEAM_DIR'] = path - env['TERRATEAM_WORKSPACE'] = workspace - env['TERRATEAM_TMPDIR'] = tmpdir - - if workflow_idx is None: - workflow = rc.get_default_workflow(state.repo_config) - else: - workflow = rc.get_workflow(state.repo_config, workflow_idx) - - create_and_select_workspace = rc.get_create_and_select_workspace( - state.repo_config, - path) - - logging.info('APPLY : CREATE_AND_SELECT_WORKSPACE : %s : %r', - path, - create_and_select_workspace) - - logging.info('APPLY : CDKTF : %s : %r', - path, - workflow['cdktf']) - - if not workflow['cdktf'] and create_and_select_workspace: - env['TF_WORKSPACE'] = workspace - - env['TERRATEAM_TERRAFORM_VERSION'] = work_exec.determine_tf_version( - state.working_dir, - os.path.join(state.working_dir, path), - workflow['terraform_version']) - - state = state._replace(env=env) - - state = workflow_step.run_steps( - state._replace(working_dir=os.path.join(state.working_dir, path), - path=path, - workspace=workspace, - workflow=workflow), - workflow['apply']) - - result = { - 'path': path, - 'workspace': workspace, - 'success': not state.failed, - 'outputs': state.outputs.copy(), - } - - return (state, result) + workflow = rc.get_workflow(state.repo_config, workflow_idx) + + create_and_select_workspace = rc.get_create_and_select_workspace( + state.repo_config, + path) + + logging.info('APPLY : CREATE_AND_SELECT_WORKSPACE : %s : %r', + path, + create_and_select_workspace) + + logging.info('APPLY : CDKTF : %s : %r', + path, + workflow['cdktf']) + + if not workflow['cdktf'] and create_and_select_workspace: + env['TF_WORKSPACE'] = workspace + + env['TERRATEAM_TERRAFORM_VERSION'] = work_exec.determine_tf_version( + state.working_dir, + os.path.join(state.working_dir, path), + workflow['terraform_version']) + + state = state._replace(env=env) + + state = workflow_step.run_steps( + state._replace(working_dir=os.path.join(state.working_dir, path), + path=path, + workspace=workspace, + workflow=workflow), + workflow['apply']) + + result = { + 'path': path, + 'workspace': workspace, + 'success': not state.failed, + 'outputs': state.outputs.copy(), + } + + return (state, result) diff --git a/terrat_runner/workflow_step_apply.py b/terrat_runner/workflow_step_apply.py index 308d9b87..192135af 100644 --- a/terrat_runner/workflow_step_apply.py +++ b/terrat_runner/workflow_step_apply.py @@ -1,9 +1,67 @@ +import base64 +import hashlib +import json +import logging +import string + +import cmd import repo_config as rc +import requests_retry import retry import workflow import workflow_step_terraform +def _load_plan(state, work_token, api_base_url, dir_path, workspace, plan_path): + res = requests_retry.get(api_base_url + '/v1/work-manifests/' + work_token + '/plans', + params={'path': dir_path, 'workspace': workspace}) + + if res.status_code != 200: + return (False, 'Could not load plan from backend') + + plan_data = base64.b64decode(res.json()['data']) + + try: + plan_data = json.loads(plan_data) + + if plan_data['method'] == 'terrateam': + plan_data_encoded = plan_data['data'] + plan_data_raw = base64.b64decode(plan_data_encoded) + logging.debug('APPLY : LOAD_PLAN : dir_path=%s : workspace=%s : md5=%s', + dir_path, + workspace, + hashlib.md5(plan_data_raw).hexdigest()) + + with open(plan_path, 'wb') as f: + f.write(plan_data_raw) + + return (True, None) + elif plan_data['method'] == 'cmd': + tmpl_vars = { + 'plan_dst_path': plan_path + } + fetch_cmd = [string.Template(s).safe_substitute(tmpl_vars) for s in plan_data['fetch']] + proc = cmd.run(state, {'cmd': fetch_cmd}) + if proc.returncode != 0: + return (False, 'Failed to fetch plan, see action logs for more details') + if plan_data.get('delete'): + cmd.run(state, {'cmd': plan_data['delete']}) + return (True, None) + else: + raise Exception('Unknown method') + except json.JSONDecodeError: + plan_data_raw = plan_data + logging.debug('APPLY : LOAD_PLAN : dir_path=%s : workspace=%s : md5=%s', + dir_path, + workspace, + hashlib.md5(plan_data_raw).hexdigest()) + + with open(plan_path, 'wb') as f: + f.write(plan_data_raw) + + return (True, None) + + def _test_success_update_config(config): def _f(ret): if ret.failed: @@ -18,6 +76,19 @@ def run(state, config): config['args'] = ['apply', '$TERRATEAM_PLAN_FILE'] config['output_key'] = 'apply' + (success, output) = _load_plan(state, + state.work_token, + state.api_base_url, + state.path, + state.workspace, + state.env['TERRATEAM_PLAN_FILE']) + + if not success: + return workflow.Result(failed=True, + state=state, + workflow_step={'type': 'apply'}, + outputs={'text': output}) + retry_config = rc.get_retry(config) tries = retry_config['enabled'] and retry_config['tries'] or 1 result = retry.run(