mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Terraform: add workspace support for switching and removing workspaces (#43489)
From terraform documentation: ``` The persistent data stored in the backend belongs to a workspace. Initially the backend has only one workspace, called "default", and thus there is only one Terraform state associated with that configuration. Certain backends support multiple named workspaces, allowing multiple states to be associated with a single configuration. The configuration still has only one backend, but multiple distinct instances of that configuration to be deployed without configuring a new backend or changing authentication credentials. ``` This patch introduces the `workspace` parameter in the terraform module. The module will select the workspace is it does not exists, or simply select it if it exists. Fixes #43134 Add 'purge_workspace' parameter and handle the workspace context The `purge_workspace` parameter allows to remove a workspace when asking for state = absent. It allows to leave a clean state file without empty workspaces if the parameter is true. Also adding the support of a workspace context that allows to restore the workspace when that was active when the module started.
This commit is contained in:
parent
09ae1ec308
commit
0461620b2d
1 changed files with 71 additions and 2 deletions
|
@ -38,6 +38,21 @@ options:
|
|||
- The path to the root of the Terraform directory with the
|
||||
vars.tf/main.tf/etc to use.
|
||||
required: true
|
||||
workspace:
|
||||
description:
|
||||
- The terraform workspace to work with.
|
||||
required: false
|
||||
default: default
|
||||
version_added: 2.7
|
||||
purge_workspace:
|
||||
description:
|
||||
- Only works with state = absent
|
||||
- If true, the workspace will be deleted after the "terraform destroy" action.
|
||||
- The 'default' workspace will not be deleted.
|
||||
required: false
|
||||
default: false
|
||||
type: bool
|
||||
version_added: 2.7
|
||||
plan_file:
|
||||
description:
|
||||
- The path to an existing Terraform plan file to apply. If this is not
|
||||
|
@ -166,6 +181,43 @@ def init_plugins(bin_path, project_path):
|
|||
module.fail_json(msg="Failed to initialize Terraform modules:\r\n{0}".format(err))
|
||||
|
||||
|
||||
def get_workspace_context(bin_path, project_path):
|
||||
workspace_ctx = {"current": "default", "all": []}
|
||||
command = [bin_path, 'workspace', 'list']
|
||||
rc, out, err = module.run_command(command, cwd=project_path)
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Failed to list Terraform workspaces:\r\n{0}".format(err))
|
||||
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('* ', '')
|
||||
else:
|
||||
workspace_ctx["all"].append(stripped_item)
|
||||
return workspace_ctx
|
||||
|
||||
|
||||
def _workspace_cmd(bin_path, project_path, action, workspace):
|
||||
command = [bin_path, 'workspace', action, workspace]
|
||||
rc, out, err = module.run_command(command, cwd=project_path)
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Failed to {0} workspace:\r\n{1}".format(action, err))
|
||||
return rc, out, err
|
||||
|
||||
|
||||
def create_workspace(bin_path, project_path, workspace):
|
||||
_workspace_cmd(bin_path, project_path, 'new', workspace)
|
||||
|
||||
|
||||
def select_workspace(bin_path, project_path, workspace):
|
||||
_workspace_cmd(bin_path, project_path, 'select', workspace)
|
||||
|
||||
|
||||
def remove_workspace(bin_path, project_path, workspace):
|
||||
_workspace_cmd(bin_path, project_path, 'delete', workspace)
|
||||
|
||||
|
||||
def build_plan(bin_path, project_path, variables_args, state_file, targets, plan_path=None):
|
||||
if plan_path is None:
|
||||
f, plan_path = tempfile.mkstemp(suffix='.tfplan')
|
||||
|
@ -198,6 +250,8 @@ def main():
|
|||
argument_spec=dict(
|
||||
project_path=dict(required=True, type='path'),
|
||||
binary_path=dict(type='path'),
|
||||
workspace=dict(required=False, type='str', default='default'),
|
||||
purge_workspace=dict(type='bool', default=False),
|
||||
state=dict(default='present', choices=['present', 'absent', 'planned']),
|
||||
variables=dict(type='dict'),
|
||||
variables_file=dict(type='path'),
|
||||
|
@ -206,7 +260,7 @@ def main():
|
|||
targets=dict(type='list', default=[]),
|
||||
lock=dict(type='bool', default=True),
|
||||
lock_timeout=dict(type='int',),
|
||||
force_init=dict(type='bool', default=False)
|
||||
force_init=dict(type='bool', default=False),
|
||||
),
|
||||
required_if=[('state', 'planned', ['plan_file'])],
|
||||
supports_check_mode=True,
|
||||
|
@ -214,6 +268,8 @@ def main():
|
|||
|
||||
project_path = module.params.get('project_path')
|
||||
bin_path = module.params.get('binary_path')
|
||||
workspace = module.params.get('workspace')
|
||||
purge_workspace = module.params.get('purge_workspace')
|
||||
state = module.params.get('state')
|
||||
variables = module.params.get('variables') or {}
|
||||
variables_file = module.params.get('variables_file')
|
||||
|
@ -229,6 +285,13 @@ def main():
|
|||
if force_init:
|
||||
init_plugins(command[0], project_path)
|
||||
|
||||
workspace_ctx = get_workspace_context(command[0], project_path)
|
||||
if workspace_ctx["current"] != workspace:
|
||||
if workspace not in workspace_ctx["all"]:
|
||||
create_workspace(command[0], project_path, workspace)
|
||||
else:
|
||||
select_workspace(command[0], project_path, workspace)
|
||||
|
||||
variables_args = []
|
||||
for k, v in variables.items():
|
||||
variables_args.extend([
|
||||
|
@ -300,7 +363,13 @@ def main():
|
|||
else:
|
||||
outputs = json.loads(outputs_text)
|
||||
|
||||
module.exit_json(changed=changed, state=state, outputs=outputs, stdout=out, stderr=err, command=' '.join(command))
|
||||
# Restore the Terraform workspace found when running the module
|
||||
if workspace_ctx["current"] != workspace:
|
||||
select_workspace(command[0], project_path, workspace_ctx["current"])
|
||||
if state == 'absent' and workspace != 'default' and purge_workspace is True:
|
||||
remove_workspace(command[0], project_path, workspace)
|
||||
|
||||
module.exit_json(changed=changed, state=state, workspace=workspace, outputs=outputs, stdout=out, stderr=err, command=' '.join(command))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
Loading…
Reference in a new issue