Skip to content

Commit c2d4c45

Browse files
authored
Fix Terraform workspace destroy (#336)
Fix bug in terraform destroy when using workspaces and add e2e test coverage. Fix some pylint warning and remove missing docstring exception.
1 parent 9c64558 commit c2d4c45

5 files changed

Lines changed: 60 additions & 4 deletions

File tree

scripts/qesap/lib/cmds.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,10 @@ def cmd_terraform(configure_data, base_project, dryrun, workspace='default', des
176176
cmds = []
177177
terraform_common_cmd = [config.get_terraform_bin(), f"-chdir={cfg_paths['provider']}"]
178178
if destroy:
179+
cmds.append(terraform_common_cmd + ['destroy', '-auto-approve', '-no-color'])
179180
if workspace != 'default':
180181
cmds.append(terraform_common_cmd + ['workspace', 'select', 'default', '-no-color'])
181182
cmds.append(terraform_common_cmd + ['workspace', 'delete', workspace, '-no-color'])
182-
cmds.append(terraform_common_cmd + ['destroy', '-auto-approve', '-no-color'])
183183
else:
184184
cmds.append(terraform_common_cmd + ['init', '-no-color'])
185185
if workspace != 'default':
@@ -260,7 +260,7 @@ def ansible_command_sequence(configure_data_ansible, base_project, sequence, ver
260260
if 'roles_path' in configure_data_ansible:
261261
original_env['ANSIBLE_ROLES_PATH'] = configure_data_ansible['roles_path']
262262

263-
# 2. Verify that needed binary are usable
263+
# 2. Verify that the two needed binaries are usable
264264
ansible_bin_paths = {}
265265
for ansible_bin in ['ansible', 'ansible-playbook']:
266266
binpath = shutil.which(ansible_bin)
@@ -292,6 +292,16 @@ def ansible_command_sequence(configure_data_ansible, base_project, sequence, ver
292292
ansible_cmd_seq.append({'cmd': ['mkdir', '-p', junit]})
293293

294294
ssh_share = ansible_common.copy()
295+
296+
# This is to avoid any manual intervention during first connection.
297+
# Without this code it is usually needed to interactively
298+
# accept the ssh host fingerprint.
299+
# It is implemented using https://docs.ansible.com/ansible/latest/command_guide/intro_adhoc.html
300+
# - the binary used is 'ansible' instead of 'ansible-playbook'
301+
# - option 'all' runs the same command on all hosts in the inventory (that comes from ansible_common)
302+
# - '-a' is for running a single command remotely,
303+
# - 'true' is just the simplest possible command as th epoint is not what we run but establishing a first connection
304+
# to have the fingerprint saved in the local known_host file.
295305
ssh_share[0] = ansible_bin_paths['ansible']
296306
# Don't set '--ssh-extra-args="..."' but 'ssh-extra-args=...'
297307
# for avoiding the ansible ssh connection failure introduced by

scripts/qesap/lib/config.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ def __init__(self, configure_data):
7373
self.conf = configure_data
7474

7575
def get_terraform_bin(self):
76+
"""
77+
Allow to specify a custom binary to be used in place of terraform.
78+
Could be maybe used to give a try to opentofu.
79+
"""
7680
if 'bin' in self.conf['terraform']:
7781
return self.conf['terraform']['bin']
7882
# The user does not specify any custom binary in its config.yaml
@@ -126,6 +130,9 @@ def terraform_yml(self):
126130
return True
127131

128132
def has_tfvar_template(self):
133+
"""
134+
Search for terraform template conf.yaml region
135+
"""
129136
if 'terraform' not in self.conf or self.conf['terraform'] is None:
130137
log.info("No 'terraform' in the config.yaml")
131138
return False
@@ -207,6 +214,9 @@ def validate(self):
207214
return True
208215

209216
def has_ansible(self):
217+
"""
218+
Check if the ansible conf.yaml region is present
219+
"""
210220
return 'ansible' in self.conf
211221

212222
@staticmethod
@@ -254,6 +264,9 @@ def has_ansible_playbooks(self, sequence):
254264
return True
255265

256266
def get_playbooks(self, sequence):
267+
"""
268+
Get list of playbooks
269+
"""
257270
return self.conf['ansible'][sequence]
258271

259272
def validate_ansible_config(self, sequence):

scripts/qesap/lib/status.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class Status(int):
1919
msg = ""
2020

2121
def __new__(cls, str_or_int):
22+
value = None
2223
if isinstance(str_or_int, str):
2324
value = 0 if str_or_int == "ok" else 1
2425
elif isinstance(str_or_int, int):

scripts/qesap/pylint.rc

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,6 @@ confidence=HIGH,
154154
# use-symbolic-message-instead
155155

156156
disable=line-too-long,
157-
missing-class-docstring,
158-
missing-function-docstring,
159157
missing-module-docstring,
160158
too-many-branches,
161159
unused-argument,

scripts/qesap/test/e2e/test.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,40 @@ test_file "${TEST_TERRAFORM_TFSTATE}"
268268
[[ ! -f "${TEST_PROVIDER}/foo.bar" ]] || test_die "Resource ${TEST_PROVIDER}/foo.bar has not to be deleted when calling terraform destroy"
269269
rm "${TEST_TERRAFORM_TFSTATE}"
270270

271+
#######################################################################
272+
test_step "[${QESAP_CFG}] Run dryrun Terraform workspace"
273+
# correct execution of terraform with workspaces in dryrun mode:
274+
THIS_LOG="${QESAPROOT}/test_3_terraform.log"
275+
qesap.py --verbose -b ${QESAPROOT} -c ${QESAP_CFG} --dryrun terraform -w DONALDUCK || test_die "${QESAP_CFG} fail on dryrun terraform workspace"
276+
TEST_TERRAFORM_TFSTATE="${TEST_PROVIDER}/terraform.tfstate"
277+
[[ ! -f "${TEST_TERRAFORM_TFSTATE}" ]] || test_die "File ${TEST_TERRAFORM_TFVARS} has been generated but it should not be in dryrun mode!"
278+
279+
test_split
280+
echo "Run the script again collecting the output"
281+
qesap.py -b ${QESAPROOT} -c ${QESAP_CFG} --dryrun terraform -w DONALDUCK |& tee "${THIS_LOG}"
282+
for t_step in init workspace plan apply; do
283+
echo "***Detect ${t_step} command***"
284+
grep -qE "terraform.*${t_step}" \
285+
"${THIS_LOG}" || test_die "${QESAP_CFG} terraform workspace dryrun does not have expected ${t_step} command in the output"
286+
done
287+
rm ${THIS_LOG}
288+
289+
#######################################################################
290+
test_step "[${QESAP_CFG}] Run Terraform with workspaces"
291+
292+
# Test is using an empty main.tf placed in the right provider folder
293+
qesap.py --verbose -b ${QESAPROOT} -c ${QESAP_CFG} terraform -w DONALDUCK || test_die "${QESAP_CFG} fail on terraform with workspace"
294+
ls -lai "${TEST_PROVIDER}"
295+
296+
[[ ! -f "${TEST_TERRAFORM_TFSTATE}" ]] || test_die "File ${TEST_TERRAFORM_TFVARS} has been generated but it should not"
297+
[[ -d "${TEST_TERRAFORM_TFSTATE}.d" ]] || test_die "Folder ${TEST_TERRAFORM_TFVARS}.d has not been generated"
298+
299+
#######################################################################
300+
test_step "[${QESAP_CFG}] Run Terraform destroy with workspaces"
301+
302+
qesap.py --verbose -b ${QESAPROOT} -c ${QESAP_CFG} terraform -w DONALDUCK -d || test_die "${QESAP_CFG} fail on terraform destroy with workspace"
303+
rm -rf "${TEST_TERRAFORM_TFSTATE}.d"
304+
271305
#######################################################################
272306
test_step "[${QESAP_CFG}] test stdout for terraform PASS"
273307
# run `qesap.py terraform` both with and without `--verbose`

0 commit comments

Comments
 (0)