1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2026-04-20 18:59:08 +00:00

Reformat everything.

This commit is contained in:
Felix Fontein 2025-11-01 12:08:41 +01:00
parent 3f2213791a
commit 340ff8586d
1008 changed files with 61301 additions and 58309 deletions

View file

@ -291,22 +291,26 @@ module = None
def get_version(bin_path):
extract_version = module.run_command([bin_path, 'version', '-json'])
terraform_version = (json.loads(extract_version[1]))['terraform_version']
extract_version = module.run_command([bin_path, "version", "-json"])
terraform_version = (json.loads(extract_version[1]))["terraform_version"]
return terraform_version
def preflight_validation(bin_path, project_path, version, variables_args=None, plan_file=None, no_color=True):
if project_path is None or '/' not in project_path:
if project_path is None or "/" not in project_path:
module.fail_json(msg="Path for Terraform project can not be None or ''.")
if not os.path.exists(bin_path):
module.fail_json(msg=f"Path for Terraform binary '{bin_path}' doesn't exist on this host - check the path and try again please.")
module.fail_json(
msg=f"Path for Terraform binary '{bin_path}' doesn't exist on this host - check the path and try again please."
)
if not os.path.isdir(project_path):
module.fail_json(msg=f"Path for Terraform project '{project_path}' doesn't exist on this host - check the path and try again please.")
cmd = [bin_path, 'validate']
module.fail_json(
msg=f"Path for Terraform project '{project_path}' doesn't exist on this host - check the path and try again please."
)
cmd = [bin_path, "validate"]
if no_color:
cmd.append('-no-color')
if LooseVersion(version) < LooseVersion('0.15.0'):
cmd.append("-no-color")
if LooseVersion(version) < LooseVersion("0.15.0"):
module.run_command(cmd + variables_args, check_rc=True, cwd=project_path)
else:
module.run_command(cmd, check_rc=True, cwd=project_path)
@ -316,81 +320,92 @@ def _state_args(state_file):
if not state_file:
return []
if not os.path.exists(state_file):
module.warn(f'Could not find state_file "{state_file}", the process will not destroy any resources, please check your state file path.')
return ['-state', state_file]
module.warn(
f'Could not find state_file "{state_file}", the process will not destroy any resources, please check your state file path.'
)
return ["-state", state_file]
def init_plugins(bin_path, project_path, backend_config, backend_config_files, init_reconfigure, provider_upgrade, plugin_paths, workspace, no_color=True):
command = [bin_path, 'init', '-input=false']
def init_plugins(
bin_path,
project_path,
backend_config,
backend_config_files,
init_reconfigure,
provider_upgrade,
plugin_paths,
workspace,
no_color=True,
):
command = [bin_path, "init", "-input=false"]
if no_color:
command.append('-no-color')
command.append("-no-color")
if backend_config:
for key, val in backend_config.items():
command.extend([
'-backend-config',
f'{key}={val}'
])
command.extend(["-backend-config", f"{key}={val}"])
if backend_config_files:
for f in backend_config_files:
command.extend(['-backend-config', f])
command.extend(["-backend-config", f])
if init_reconfigure:
command.extend(['-reconfigure'])
command.extend(["-reconfigure"])
if provider_upgrade:
command.extend(['-upgrade'])
command.extend(["-upgrade"])
if plugin_paths:
for plugin_path in plugin_paths:
command.extend(['-plugin-dir', plugin_path])
rc, out, err = module.run_command(command, check_rc=True, cwd=project_path, environ_update={"TF_WORKSPACE": workspace})
command.extend(["-plugin-dir", plugin_path])
rc, out, err = module.run_command(
command, check_rc=True, cwd=project_path, environ_update={"TF_WORKSPACE": workspace}
)
def get_workspace_context(bin_path, project_path, no_color=True):
workspace_ctx = {"current": "default", "all": []}
command = [bin_path, 'workspace', 'list']
command = [bin_path, "workspace", "list"]
if no_color:
command.append('-no-color')
command.append("-no-color")
rc, out, err = module.run_command(command, cwd=project_path)
if rc != 0:
module.warn(f"Failed to list Terraform workspaces:\n{err}")
for item in out.split('\n'):
for item in out.split("\n"):
stripped_item = item.strip()
if not stripped_item:
continue
elif stripped_item.startswith('* '):
workspace_ctx["current"] = stripped_item.replace('* ', '')
workspace_ctx["all"].append(stripped_item.replace('* ', ''))
elif stripped_item.startswith("* "):
workspace_ctx["current"] = stripped_item.replace("* ", "")
workspace_ctx["all"].append(stripped_item.replace("* ", ""))
else:
workspace_ctx["all"].append(stripped_item)
return workspace_ctx
def _workspace_cmd(bin_path, project_path, action, workspace, no_color=True):
command = [bin_path, 'workspace', action, workspace]
command = [bin_path, "workspace", action, workspace]
if no_color:
command.append('-no-color')
command.append("-no-color")
rc, out, err = module.run_command(command, check_rc=True, cwd=project_path)
return rc, out, err
def create_workspace(bin_path, project_path, workspace, no_color=True):
_workspace_cmd(bin_path, project_path, 'new', workspace, no_color)
_workspace_cmd(bin_path, project_path, "new", workspace, no_color)
def select_workspace(bin_path, project_path, workspace, no_color=True):
_workspace_cmd(bin_path, project_path, 'select', workspace, no_color)
_workspace_cmd(bin_path, project_path, "select", workspace, no_color)
def remove_workspace(bin_path, project_path, workspace, no_color=True):
_workspace_cmd(bin_path, project_path, 'delete', workspace, no_color)
_workspace_cmd(bin_path, project_path, "delete", workspace, no_color)
def build_plan(command, project_path, variables_args, state_file, targets, state, args, plan_path=None, no_color=True):
if plan_path is None:
f, plan_path = tempfile.mkstemp(suffix='.tfplan')
f, plan_path = tempfile.mkstemp(suffix=".tfplan")
local_command = command[:]
plan_command = [command[0], 'plan']
plan_command = [command[0], "plan"]
if state == "planned":
for c in local_command[1:]:
@ -406,13 +421,13 @@ def build_plan(command, project_path, variables_args, state_file, targets, state
for a in args:
plan_command.append(a)
plan_options = ['-input=false', '-detailed-exitcode', '-out', plan_path]
plan_options = ["-input=false", "-detailed-exitcode", "-out", plan_path]
if no_color:
plan_options.insert(0, '-no-color')
plan_options.insert(0, "-no-color")
plan_command.extend(plan_options)
for t in targets:
plan_command.extend(['-target', t])
plan_command.extend(["-target", t])
plan_command.extend(_state_args(state_file))
@ -420,30 +435,34 @@ def build_plan(command, project_path, variables_args, state_file, targets, state
if rc == 0:
# no changes
return plan_path, False, out, err, plan_command if state == 'planned' else command
return plan_path, False, out, err, plan_command if state == "planned" else command
elif rc == 1:
# failure to plan
module.fail_json(
msg=(f"Terraform plan could not be created\nSTDOUT: {out}\nSTDERR: {err}\n"
f"COMMAND: {' '.join(plan_command)} {' '.join([shlex_quote(arg) for arg in variables_args])}")
msg=(
f"Terraform plan could not be created\nSTDOUT: {out}\nSTDERR: {err}\n"
f"COMMAND: {' '.join(plan_command)} {' '.join([shlex_quote(arg) for arg in variables_args])}"
)
)
elif rc == 2:
# changes, but successful
return plan_path, True, out, err, plan_command if state == 'planned' else command
return plan_path, True, out, err, plan_command if state == "planned" else command
module.fail_json(msg=f"Terraform plan failed with unexpected exit code {rc}.\nSTDOUT: {out}\nSTDERR: {err}\n"
f"COMMAND: {' '.join(plan_command)} {' '.join([shlex_quote(arg) for arg in variables_args])}")
module.fail_json(
msg=f"Terraform plan failed with unexpected exit code {rc}.\nSTDOUT: {out}\nSTDERR: {err}\n"
f"COMMAND: {' '.join(plan_command)} {' '.join([shlex_quote(arg) for arg in variables_args])}"
)
def get_diff(diff_output):
def get_tf_resource_address(e):
return e['resource']
return e["resource"]
diff_json_output = json.loads(diff_output)
# Ignore diff if resource_changes does not exists in tfplan
if 'resource_changes' in diff_json_output:
tf_reosource_changes = diff_json_output['resource_changes']
if "resource_changes" in diff_json_output:
tf_reosource_changes = diff_json_output["resource_changes"]
else:
module.warn("Cannot find resource_changes in terraform plan, diff/check ignored")
return False, {}
@ -452,20 +471,20 @@ def get_diff(diff_output):
diff_before = []
changed = False
for item in tf_reosource_changes:
item_change = item['change']
tf_before_state = {'resource': item['address'], 'change': item['change']['before']}
tf_after_state = {'resource': item['address'], 'change': item['change']['after']}
item_change = item["change"]
tf_before_state = {"resource": item["address"], "change": item["change"]["before"]}
tf_after_state = {"resource": item["address"], "change": item["change"]["after"]}
if item_change['actions'] == ['update'] or item_change['actions'] == ['delete', 'create']:
if item_change["actions"] == ["update"] or item_change["actions"] == ["delete", "create"]:
diff_before.append(tf_before_state)
diff_after.append(tf_after_state)
changed = True
if item_change['actions'] == ['delete']:
if item_change["actions"] == ["delete"]:
diff_before.append(tf_before_state)
changed = True
if item_change['actions'] == ['create']:
if item_change["actions"] == ["create"]:
diff_after.append(tf_after_state)
changed = True
@ -473,8 +492,8 @@ def get_diff(diff_output):
diff_after.sort(key=get_tf_resource_address)
return changed, dict(
before=({'data': diff_before}),
after=({'data': diff_after}),
before=({"data": diff_before}),
after=({"data": diff_after}),
)
@ -482,79 +501,89 @@ def main():
global module
module = AnsibleModule(
argument_spec=dict(
project_path=dict(required=True, type='path'),
binary_path=dict(type='path'),
plugin_paths=dict(type='list', elements='path'),
workspace=dict(type='str', default='default'),
purge_workspace=dict(type='bool', default=False),
state=dict(default='present', choices=['present', 'absent', 'planned']),
variables=dict(type='dict'),
complex_vars=dict(type='bool', default=False),
variables_files=dict(aliases=['variables_file'], type='list', elements='path'),
plan_file=dict(type='path'),
state_file=dict(type='path'),
targets=dict(type='list', elements='str', default=[]),
lock=dict(type='bool', default=True),
lock_timeout=dict(type='int'),
force_init=dict(type='bool', default=False),
backend_config=dict(type='dict'),
backend_config_files=dict(type='list', elements='path'),
init_reconfigure=dict(type='bool', default=False),
overwrite_init=dict(type='bool', default=True),
check_destroy=dict(type='bool', default=False),
parallelism=dict(type='int'),
provider_upgrade=dict(type='bool', default=False),
no_color=dict(type='bool', default=True),
project_path=dict(required=True, type="path"),
binary_path=dict(type="path"),
plugin_paths=dict(type="list", elements="path"),
workspace=dict(type="str", default="default"),
purge_workspace=dict(type="bool", default=False),
state=dict(default="present", choices=["present", "absent", "planned"]),
variables=dict(type="dict"),
complex_vars=dict(type="bool", default=False),
variables_files=dict(aliases=["variables_file"], type="list", elements="path"),
plan_file=dict(type="path"),
state_file=dict(type="path"),
targets=dict(type="list", elements="str", default=[]),
lock=dict(type="bool", default=True),
lock_timeout=dict(type="int"),
force_init=dict(type="bool", default=False),
backend_config=dict(type="dict"),
backend_config_files=dict(type="list", elements="path"),
init_reconfigure=dict(type="bool", default=False),
overwrite_init=dict(type="bool", default=True),
check_destroy=dict(type="bool", default=False),
parallelism=dict(type="int"),
provider_upgrade=dict(type="bool", default=False),
no_color=dict(type="bool", default=True),
),
required_if=[('state', 'planned', ['plan_file'])],
required_if=[("state", "planned", ["plan_file"])],
supports_check_mode=True,
)
project_path = module.params.get('project_path')
bin_path = module.params.get('binary_path')
plugin_paths = module.params.get('plugin_paths')
workspace = module.params.get('workspace')
purge_workspace = module.params.get('purge_workspace')
state = module.params.get('state')
variables = module.params.get('variables') or {}
complex_vars = module.params.get('complex_vars')
variables_files = module.params.get('variables_files')
plan_file = module.params.get('plan_file')
state_file = module.params.get('state_file')
force_init = module.params.get('force_init')
backend_config = module.params.get('backend_config')
backend_config_files = module.params.get('backend_config_files')
init_reconfigure = module.params.get('init_reconfigure')
overwrite_init = module.params.get('overwrite_init')
check_destroy = module.params.get('check_destroy')
provider_upgrade = module.params.get('provider_upgrade')
no_color = module.params.get('no_color')
project_path = module.params.get("project_path")
bin_path = module.params.get("binary_path")
plugin_paths = module.params.get("plugin_paths")
workspace = module.params.get("workspace")
purge_workspace = module.params.get("purge_workspace")
state = module.params.get("state")
variables = module.params.get("variables") or {}
complex_vars = module.params.get("complex_vars")
variables_files = module.params.get("variables_files")
plan_file = module.params.get("plan_file")
state_file = module.params.get("state_file")
force_init = module.params.get("force_init")
backend_config = module.params.get("backend_config")
backend_config_files = module.params.get("backend_config_files")
init_reconfigure = module.params.get("init_reconfigure")
overwrite_init = module.params.get("overwrite_init")
check_destroy = module.params.get("check_destroy")
provider_upgrade = module.params.get("provider_upgrade")
no_color = module.params.get("no_color")
if bin_path is not None:
command = [bin_path]
else:
command = [module.get_bin_path('terraform', required=True)]
command = [module.get_bin_path("terraform", required=True)]
checked_version = get_version(command[0])
if LooseVersion(checked_version) < LooseVersion('0.15.0'):
if LooseVersion(checked_version) < LooseVersion("0.15.0"):
if no_color:
DESTROY_ARGS = ('destroy', '-no-color', '-force')
APPLY_ARGS = ('apply', '-no-color', '-input=false', '-auto-approve=true')
DESTROY_ARGS = ("destroy", "-no-color", "-force")
APPLY_ARGS = ("apply", "-no-color", "-input=false", "-auto-approve=true")
else:
DESTROY_ARGS = ('destroy', '-force')
APPLY_ARGS = ('apply', '-input=false', '-auto-approve=true')
DESTROY_ARGS = ("destroy", "-force")
APPLY_ARGS = ("apply", "-input=false", "-auto-approve=true")
else:
if no_color:
DESTROY_ARGS = ('destroy', '-no-color', '-auto-approve')
APPLY_ARGS = ('apply', '-no-color', '-input=false', '-auto-approve')
DESTROY_ARGS = ("destroy", "-no-color", "-auto-approve")
APPLY_ARGS = ("apply", "-no-color", "-input=false", "-auto-approve")
else:
DESTROY_ARGS = ('destroy', '-auto-approve')
APPLY_ARGS = ('apply', '-input=false', '-auto-approve')
DESTROY_ARGS = ("destroy", "-auto-approve")
APPLY_ARGS = ("apply", "-input=false", "-auto-approve")
if force_init:
if overwrite_init or not os.path.isfile(os.path.join(project_path, ".terraform", "terraform.tfstate")):
init_plugins(command[0], project_path, backend_config, backend_config_files, init_reconfigure, provider_upgrade, plugin_paths, workspace, no_color)
init_plugins(
command[0],
project_path,
backend_config,
backend_config_files,
init_reconfigure,
provider_upgrade,
plugin_paths,
workspace,
no_color,
)
workspace_ctx = get_workspace_context(command[0], project_path, no_color)
if workspace_ctx["current"] != workspace:
@ -563,24 +592,24 @@ def main():
else:
select_workspace(command[0], project_path, workspace, no_color)
if state == 'present':
if state == "present":
command.extend(APPLY_ARGS)
elif state == 'absent':
elif state == "absent":
command.extend(DESTROY_ARGS)
if state == 'present' and module.params.get('parallelism') is not None:
if state == "present" and module.params.get("parallelism") is not None:
command.append(f"-parallelism={module.params.get('parallelism')}")
def format_args(vars):
if vars is None:
return 'null'
return "null"
elif isinstance(vars, str):
return '"{string}"'.format(string=vars.replace('\\', '\\\\').replace('"', '\\"')).replace('\n', '\\n')
return '"{string}"'.format(string=vars.replace("\\", "\\\\").replace('"', '\\"')).replace("\n", "\\n")
elif isinstance(vars, bool):
if vars:
return 'true'
return "true"
else:
return 'false'
return "false"
return str(vars)
def process_complex_args(vars):
@ -588,14 +617,16 @@ def main():
if isinstance(vars, dict):
for k, v in vars.items():
if isinstance(v, dict):
ret_out.append(f'{k}={{{process_complex_args(v)}}}')
ret_out.append(f"{k}={{{process_complex_args(v)}}}")
elif isinstance(v, list):
ret_out.append(f"{k}={process_complex_args(v)}")
elif isinstance(v, (int, float, str, bool)) or v is None:
ret_out.append(f'{k}={format_args(v)}')
ret_out.append(f"{k}={format_args(v)}")
else:
# only to handle anything unforeseen
module.fail_json(msg="Supported types are, dictionaries, lists, strings, integers, boolean and float.")
module.fail_json(
msg="Supported types are, dictionaries, lists, strings, integers, boolean and float."
)
if isinstance(vars, list):
l_out = []
for item in vars:
@ -607,7 +638,9 @@ def main():
l_out.append(format_args(item))
else:
# only to handle anything unforeseen
module.fail_json(msg="Supported types are, dictionaries, lists, strings, integers, boolean and float.")
module.fail_json(
msg="Supported types are, dictionaries, lists, strings, integers, boolean and float."
)
ret_out.append(f"[{','.join(l_out)}]")
return ",".join(ret_out)
@ -616,136 +649,154 @@ def main():
if complex_vars:
for k, v in variables.items():
if isinstance(v, dict):
variables_args.extend([
'-var',
f'{k}={{{process_complex_args(v)}}}'
])
variables_args.extend(["-var", f"{k}={{{process_complex_args(v)}}}"])
elif isinstance(v, list):
variables_args.extend([
'-var',
f'{k}={process_complex_args(v)}'
])
variables_args.extend(["-var", f"{k}={process_complex_args(v)}"])
# on the top-level we need to pass just the python string with necessary
# terraform string escape sequences
elif isinstance(v, str):
variables_args.extend([
'-var',
f"{k}={v}"
])
variables_args.extend(["-var", f"{k}={v}"])
else:
variables_args.extend([
'-var',
f'{k}={format_args(v)}'
])
variables_args.extend(["-var", f"{k}={format_args(v)}"])
else:
for k, v in variables.items():
variables_args.extend([
'-var',
f'{k}={v}'
])
variables_args.extend(["-var", f"{k}={v}"])
if variables_files:
for f in variables_files:
variables_args.extend(['-var-file', f])
variables_args.extend(["-var-file", f])
preflight_validation(command[0], project_path, checked_version, variables_args, plan_file, no_color)
if module.params.get('lock') is not None:
if module.params.get('lock'):
command.append('-lock=true')
if module.params.get("lock") is not None:
if module.params.get("lock"):
command.append("-lock=true")
else:
command.append('-lock=false')
if module.params.get('lock_timeout') is not None:
command.append("-lock=false")
if module.params.get("lock_timeout") is not None:
command.append(f"-lock-timeout={module.params.get('lock_timeout')}s")
for t in (module.params.get('targets') or []):
command.extend(['-target', t])
for t in module.params.get("targets") or []:
command.extend(["-target", t])
# we aren't sure if this plan will result in changes, so assume yes
needs_application, changed = True, False
out, err = '', ''
out, err = "", ""
if state == 'absent':
if state == "absent":
command.extend(variables_args)
elif state == 'present' and plan_file:
elif state == "present" and plan_file:
if any([os.path.isfile(f"{project_path}/{plan_file}"), os.path.isfile(plan_file)]):
command.append(plan_file)
else:
module.fail_json(msg=f'Could not find plan_file "{plan_file}", check the path and try again.')
else:
plan_file, needs_application, out, err, command = build_plan(command, project_path, variables_args, state_file,
module.params.get('targets'), state, APPLY_ARGS, plan_file, no_color)
if state == 'present' and check_destroy and '- destroy' in out:
module.fail_json(msg="Aborting command because it would destroy some resources. "
"Consider switching the 'check_destroy' to false to suppress this error")
plan_file, needs_application, out, err, command = build_plan(
command,
project_path,
variables_args,
state_file,
module.params.get("targets"),
state,
APPLY_ARGS,
plan_file,
no_color,
)
if state == "present" and check_destroy and "- destroy" in out:
module.fail_json(
msg="Aborting command because it would destroy some resources. "
"Consider switching the 'check_destroy' to false to suppress this error"
)
command.append(plan_file)
result_diff = dict()
if module._diff or module.check_mode:
if state == 'absent':
plan_absent_args = ['-destroy']
plan_file, needs_application, out, err, command = build_plan(command, project_path, variables_args, state_file,
module.params.get('targets'), state, plan_absent_args, plan_file, no_color)
diff_command = [command[0], 'show', '-json', plan_file]
if state == "absent":
plan_absent_args = ["-destroy"]
plan_file, needs_application, out, err, command = build_plan(
command,
project_path,
variables_args,
state_file,
module.params.get("targets"),
state,
plan_absent_args,
plan_file,
no_color,
)
diff_command = [command[0], "show", "-json", plan_file]
rc, diff_output, err = module.run_command(diff_command, check_rc=False, cwd=project_path)
changed, result_diff = get_diff(diff_output)
if rc != 0:
if workspace_ctx["current"] != workspace:
select_workspace(command[0], project_path, workspace_ctx["current"], no_color)
module.fail_json(msg=err.rstrip(), rc=rc, stdout=out,
stdout_lines=out.splitlines(), stderr=err,
stderr_lines=err.splitlines(),
cmd=' '.join(command))
module.fail_json(
msg=err.rstrip(),
rc=rc,
stdout=out,
stdout_lines=out.splitlines(),
stderr=err,
stderr_lines=err.splitlines(),
cmd=" ".join(command),
)
if needs_application and not module.check_mode and state != 'planned':
if needs_application and not module.check_mode and state != "planned":
rc, out, err = module.run_command(command, check_rc=False, cwd=project_path)
if rc != 0:
if workspace_ctx["current"] != workspace:
select_workspace(command[0], project_path, workspace_ctx["current"], no_color)
module.fail_json(msg=err.rstrip(), rc=rc, stdout=out,
stdout_lines=out.splitlines(), stderr=err,
stderr_lines=err.splitlines(),
cmd=' '.join(command))
module.fail_json(
msg=err.rstrip(),
rc=rc,
stdout=out,
stdout_lines=out.splitlines(),
stderr=err,
stderr_lines=err.splitlines(),
cmd=" ".join(command),
)
# checks out to decide if changes were made during execution
if ' 0 added, 0 changed' not in out and not state == "absent" or ' 0 destroyed' not in out:
if " 0 added, 0 changed" not in out and not state == "absent" or " 0 destroyed" not in out:
changed = True
if no_color:
outputs_command = [command[0], 'output', '-no-color', '-json'] + _state_args(state_file)
outputs_command = [command[0], "output", "-no-color", "-json"] + _state_args(state_file)
else:
outputs_command = [command[0], 'output', '-json'] + _state_args(state_file)
outputs_command = [command[0], "output", "-json"] + _state_args(state_file)
rc, outputs_text, outputs_err = module.run_command(outputs_command, cwd=project_path)
outputs = {}
if rc == 1:
module.warn(f"Could not get Terraform outputs. This usually means none have been defined.\nstdout: {outputs_text}\nstderr: {outputs_err}")
module.warn(
f"Could not get Terraform outputs. This usually means none have been defined.\nstdout: {outputs_text}\nstderr: {outputs_err}"
)
elif rc != 0:
module.fail_json(
msg=f"Failure when getting Terraform outputs. Exited {rc}.\nstdout: {outputs_text}\nstderr: {outputs_err}",
command=' '.join(outputs_command))
command=" ".join(outputs_command),
)
else:
outputs = json.loads(outputs_text)
# Restore the Terraform workspace found when running the module
if workspace_ctx["current"] != workspace:
select_workspace(command[0], project_path, workspace_ctx["current"], no_color)
if state == 'absent' and workspace != 'default' and purge_workspace is True:
if state == "absent" and workspace != "default" and purge_workspace is True:
remove_workspace(command[0], project_path, workspace, no_color)
result = {
'state': state,
'workspace': workspace,
'outputs': outputs,
'stdout': out,
'stderr': err,
'command': ' '.join(command),
'changed': changed,
'diff': result_diff,
"state": state,
"workspace": workspace,
"outputs": outputs,
"stdout": out,
"stderr": err,
"command": " ".join(command),
"changed": changed,
"diff": result_diff,
}
module.exit_json(**result)
if __name__ == '__main__':
if __name__ == "__main__":
main()