terraform: patch state 'planned' outputs and perform minor refactor (#52004)

* patch state 'planned' outputs and perform minor refactor

* reconcile with pep8 and pylint

* remove a space...

* update changed to only flip when >0 added, changed, or destroyed is detected

* add state dependecy to conditional change check

* fix typo

* reconcile with pep8[E317]

* add a changelog fragment
This commit is contained in:
Dawson Mortenson 2019-02-26 03:39:13 -08:00 committed by John R Barker
parent 48187d5659
commit 6f585b6ee9
2 changed files with 26 additions and 29 deletions

View file

@ -0,0 +1,2 @@
bugfixes:
- terraform - fixed issue where state "planned" wouldn't return an output and the project_path had to exist in two places (https://github.com/ansible/ansible/issues/39689)

View file

@ -241,28 +241,28 @@ def remove_workspace(bin_path, project_path, workspace):
_workspace_cmd(bin_path, project_path, 'delete', workspace) _workspace_cmd(bin_path, project_path, 'delete', workspace)
def build_plan(bin_path, project_path, variables_args, state_file, targets, plan_path=None): def build_plan(command, project_path, variables_args, state_file, targets, state, plan_path=None):
if plan_path is None: if plan_path is None:
f, plan_path = tempfile.mkstemp(suffix='.tfplan') f, plan_path = tempfile.mkstemp(suffix='.tfplan')
command = [bin_path, 'plan', '-input=false', '-no-color', '-detailed-exitcode', '-out', plan_path] plan_command = [command[0], 'plan', '-input=false', '-no-color', '-detailed-exitcode', '-out', plan_path]
for t in (targets or []): for t in (module.params.get('targets') or []):
command.extend(['-target', t]) plan_command.extend(['-target', t])
command.extend(_state_args(state_file)) plan_command.extend(_state_args(state_file))
rc, out, err = module.run_command(command + variables_args, cwd=project_path, use_unsafe_shell=True) rc, out, err = module.run_command(plan_command + variables_args, cwd=project_path, use_unsafe_shell=True)
if rc == 0: if rc == 0:
# no changes # no changes
return plan_path, False return plan_path, False, out, err, plan_command if state == 'planned' else command
elif rc == 1: elif rc == 1:
# failure to plan # failure to plan
module.fail_json(msg='Terraform plan could not be created\r\nSTDOUT: {0}\r\n\r\nSTDERR: {1}'.format(out, err)) module.fail_json(msg='Terraform plan could not be created\r\nSTDOUT: {0}\r\n\r\nSTDERR: {1}'.format(out, err))
elif rc == 2: elif rc == 2:
# changes, but successful # changes, but successful
return plan_path, True return plan_path, True, out, err, plan_command if state == 'planned' else command
module.fail_json(msg='Terraform plan failed with unexpected exit code {0}. \r\nSTDOUT: {1}\r\n\r\nSTDERR: {2}'.format(rc, out, err)) module.fail_json(msg='Terraform plan failed with unexpected exit code {0}. \r\nSTDOUT: {1}\r\n\r\nSTDERR: {2}'.format(rc, out, err))
@ -317,6 +317,11 @@ def main():
else: else:
select_workspace(command[0], project_path, workspace) select_workspace(command[0], project_path, workspace)
if state == 'present':
command.extend(APPLY_ARGS)
elif state == 'absent':
command.extend(DESTROY_ARGS)
variables_args = [] variables_args = []
for k, v in variables.items(): for k, v in variables.items():
variables_args.extend([ variables_args.extend([
@ -328,11 +333,6 @@ def main():
preflight_validation(command[0], project_path, variables_args) preflight_validation(command[0], project_path, variables_args)
if state == 'present':
command.extend(APPLY_ARGS)
elif state == 'absent':
command.extend(DESTROY_ARGS)
if module.params.get('lock') is not None: if module.params.get('lock') is not None:
if module.params.get('lock'): if module.params.get('lock'):
command.append('-lock=true') command.append('-lock=true')
@ -345,35 +345,30 @@ def main():
command.extend(['-target', t]) command.extend(['-target', t])
# we aren't sure if this plan will result in changes, so assume yes # we aren't sure if this plan will result in changes, so assume yes
needs_application, changed = True, True needs_application, changed = True, False
if state == 'planned':
plan_file, needs_application = build_plan(command[0], project_path, variables_args, state_file, module.params.get('targets'), plan_file)
if state == 'absent': if state == 'absent':
# deleting cannot use a statefile
needs_application = True
# add variables settings to destroy command
command.extend(variables_args) command.extend(variables_args)
elif plan_file and os.path.exists(plan_file): elif state == 'present' and plan_file:
command.append(plan_file) if os.path.exists(project_path + "/" + plan_file):
elif plan_file and not os.path.exists(plan_file): command.append(plan_file)
module.fail_json(msg='Could not find plan_file "{0}", check the path and try again.'.format(plan_file)) else:
module.fail_json(msg='Could not find plan_file "{0}", check the path and try again.'.format(plan_file))
else: else:
plan_file, needs_application = build_plan(command[0], project_path, variables_args, state_file, module.params.get('targets'), plan_file) plan_file, needs_application, out, err, command = build_plan(command, project_path, variables_args, state_file,
module.params.get('targets'), state, plan_file)
command.append(plan_file) command.append(plan_file)
if needs_application and not module.check_mode and not state == 'planned': if needs_application and not module.check_mode and not state == 'planned':
rc, out, err = module.run_command(command, cwd=project_path) rc, out, err = module.run_command(command, cwd=project_path)
if state == 'absent' and 'Resources: 0' in out: # checks out to decide if changes were made during execution
changed = False if '0 added, 0 changed' not in out and not state == "absent" or '0 destroyed' not in out:
changed = True
if rc != 0: if rc != 0:
module.fail_json( module.fail_json(
msg="Failure when executing Terraform command. Exited {0}.\nstdout: {1}\nstderr: {2}".format(rc, out, err), msg="Failure when executing Terraform command. Exited {0}.\nstdout: {1}\nstderr: {2}".format(rc, out, err),
command=' '.join(command) command=' '.join(command)
) )
else:
changed = False
out, err = '', ''
outputs_command = [command[0], 'output', '-no-color', '-json'] + _state_args(state_file) outputs_command = [command[0], 'output', '-no-color', '-json'] + _state_args(state_file)
rc, outputs_text, outputs_err = module.run_command(outputs_command, cwd=project_path) rc, outputs_text, outputs_err = module.run_command(outputs_command, cwd=project_path)