Skip to content

Commit 61ca131

Browse files
committed
tag v1.7.2
1 parent b136ea6 commit 61ca131

3 files changed

Lines changed: 102 additions & 93 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
# Changelog
22

33
All notable changes to this project will be documented in this file.
4+
<!-- markdownlint-disable MD024 -->
45

56
## [Unreleased]
67

78
<!-- None < 2022-08-04 11:44:18+00:00 -->
89

10+
- [[#56](https://github.com/GoogleCloudPlatform/terraform-python-testing-helper/pull/56)] add ability to pickle tftest instance objects ([marshall7m](https://github.com/marshall7m)) <!-- 2022-09-15 05:22:57+00:00 -->
11+
912
## [1.7.1] - 2022-08-04
1013

1114
<!-- 2022-08-04 11:44:18+00:00 < 2022-07-11 12:09:43+00:00 -->

tftest.py

Lines changed: 71 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
1514
"""Simple Python wrapper for Terraform test fixtures.
1615
1716
See documentation in the TerraformTest class for usage. Terraform wrapping
@@ -41,13 +40,12 @@
4140
from pathlib import Path
4241
from typing import List
4342

44-
__version__ = '1.7.1'
43+
__version__ = '1.7.2'
4544

4645
_LOGGER = logging.getLogger('tftest')
4746

48-
49-
TerraformCommandOutput = collections.namedtuple(
50-
'TerraformCommandOutput', 'retcode out err')
47+
TerraformCommandOutput = collections.namedtuple('TerraformCommandOutput',
48+
'retcode out err')
5149

5250
TerraformStateResource = collections.namedtuple(
5351
'TerraformStateResource', 'key provider type attributes depends_on raw')
@@ -73,7 +71,6 @@ def cmd_error(self):
7371
'ignore_external_dependencies',
7472
]
7573

76-
7774
_TG_KV_ARGS = [
7875
"iam_role",
7976
"config",
@@ -101,17 +98,20 @@ def parse_args(init_vars=None, tf_vars=None, targets=None, **kw):
10198
"""
10299
cmd_args = []
103100

104-
cmd_args += [f'--terragrunt-{arg.replace("_", "-")}'
105-
for arg in _TG_BOOL_ARGS if kw.get(f"tg_{arg}")]
101+
cmd_args += [
102+
f'--terragrunt-{arg.replace("_", "-")}' for arg in _TG_BOOL_ARGS
103+
if kw.get(f"tg_{arg}")
104+
]
106105
for arg in _TG_KV_ARGS:
107106
if kw.get(f"tg_{arg}"):
108-
cmd_args += [f'--terragrunt-{arg.replace("_", "-")}',
109-
kw[f"tg_{arg}"]]
107+
cmd_args += [f'--terragrunt-{arg.replace("_", "-")}', kw[f"tg_{arg}"]]
110108
if kw.get('tg_parallelism'):
111109
cmd_args.append(f'--terragrunt-parallelism {kw["tg_parallelism"]}')
112110
if isinstance(kw.get('tg_override_attr'), dict):
113-
cmd_args += ['--terragrunt-override-attr={}={}'.format(k, v)
114-
for k, v in kw.get('tg_override_attr').items()]
111+
cmd_args += [
112+
'--terragrunt-override-attr={}={}'.format(k, v)
113+
for k, v in kw.get('tg_override_attr').items()
114+
]
115115

116116
if kw.get('auto_approve'):
117117
cmd_args.append('-auto-approve')
@@ -134,14 +134,15 @@ def parse_args(init_vars=None, tf_vars=None, targets=None, **kw):
134134
if kw.get('upgrade'):
135135
cmd_args.append('-upgrade')
136136
if isinstance(init_vars, dict):
137-
cmd_args += ['-backend-config={}={}'.format(k, v)
138-
for k, v in init_vars.items()]
137+
cmd_args += [
138+
'-backend-config={}={}'.format(k, v) for k, v in init_vars.items()
139+
]
139140
elif isinstance(init_vars, str):
140141
cmd_args += ['-backend-config', '{}'.format(init_vars)]
141142
if tf_vars:
142-
cmd_args += list(itertools.chain.from_iterable(
143-
("-var", "{}={}".format(k, v)) for k, v in tf_vars.items()
144-
))
143+
cmd_args += list(
144+
itertools.chain.from_iterable(
145+
("-var", "{}={}".format(k, v)) for k, v in tf_vars.items()))
145146
if targets:
146147
cmd_args += [("-target={}".format(t)) for t in targets]
147148
if kw.get('tf_var_file'):
@@ -201,8 +202,9 @@ def __init__(self, raw):
201202
@property
202203
def child_modules(self):
203204
if self._modules is None:
204-
self._modules = dict((mod['address'][self._strip:], TerraformPlanModule(
205-
mod)) for mod in self._raw.get('child_modules', {}))
205+
self._modules = dict(
206+
(mod['address'][self._strip:], TerraformPlanModule(mod))
207+
for mod in self._raw.get('child_modules', {}))
206208
return self._modules
207209

208210
@property
@@ -225,11 +227,11 @@ class TerraformPlanOutput(TerraformJSONBase):
225227
def __init__(self, raw):
226228
super(TerraformPlanOutput, self).__init__(raw)
227229
planned_values = raw.get('planned_values', {})
228-
self.root_module = TerraformPlanModule(
229-
planned_values.get('root_module', {}))
230+
self.root_module = TerraformPlanModule(planned_values.get(
231+
'root_module', {}))
230232
self.outputs = TerraformValueDict(planned_values.get('outputs', {}))
231-
self.resource_changes = dict((v['address'], v)
232-
for v in self._raw.get('resource_changes', {}))
233+
self.resource_changes = dict(
234+
(v['address'], v) for v in self._raw.get('resource_changes', {}))
233235
# there might be no variables defined
234236
self.variables = TerraformValueDict(raw.get('variables', {}))
235237

@@ -264,8 +266,8 @@ def resources(self):
264266
if not self._resources:
265267
resources = {}
266268
for res in self._raw['resources']:
267-
name = '%s.%s.%s' % (
268-
res.get('module'), res.get('type'), res.get('name'))
269+
name = '%s.%s.%s' % (res.get('module'), res.get('type'),
270+
res.get('name'))
269271
resources[name] = res
270272
self._resources = resources
271273
return self._resources
@@ -315,8 +317,7 @@ def __init__(self, tfdir, basedir=None, binary='terraform', env=None):
315317
self.env = os.environ.copy()
316318
self.tg_run_all = False
317319
self._plan_formatter = lambda out: TerraformPlanOutput(json.loads(out))
318-
self._output_formatter = lambda out: TerraformValueDict(
319-
json.loads(out))
320+
self._output_formatter = lambda out: TerraformValueDict(json.loads(out))
320321
if env is not None:
321322
self.env.update(env)
322323

@@ -328,6 +329,7 @@ def remove_readonly(func, path, excinfo):
328329
_LOGGER.warning(f'Issue deleting file {path}, caused by {excinfo}')
329330
os.chmod(path, stat.S_IWRITE)
330331
func(path)
332+
331333
_LOGGER.debug('cleaning up %s %s', tfdir, filenames)
332334
for filename in filenames:
333335
path = os.path.join(tfdir, filename)
@@ -344,13 +346,11 @@ def remove_readonly(func, path, excinfo):
344346
for tg_dir in glob.glob(path, recursive=True):
345347
if os.path.isdir(tg_dir):
346348
shutil.rmtree(tg_dir, onerror=remove_readonly)
347-
_LOGGER.debug(
348-
'Restoring original TF files after prevent destroy changes')
349+
_LOGGER.debug('Restoring original TF files after prevent destroy changes')
349350
if restore_files:
350351
for bkp_file in Path(tfdir).rglob('*.bkp'):
351352
try:
352-
shutil.copy(str(bkp_file),
353-
f'{str(bkp_file).strip(".bkp")}')
353+
shutil.copy(str(bkp_file), f'{str(bkp_file).strip(".bkp")}')
354354
except (IOError, OSError):
355355
_LOGGER.exception(
356356
f'Unable to restore terraform file {bkp_file.resolve()}')
@@ -404,14 +404,15 @@ def setup(self, extra_files=None, plugin_dir=None, init_vars=None,
404404
with open(tf_file, 'r') as src:
405405
terraform = src.read()
406406
with open(tf_file, 'w') as src:
407-
terraform = re.sub(
408-
r'prevent_destroy\s+=\s+true', 'prevent_destroy = false', terraform)
407+
terraform = re.sub(r'prevent_destroy\s+=\s+true',
408+
'prevent_destroy = false', terraform)
409409
src.write(terraform)
410410
except (OSError, IOError):
411411
_LOGGER.exception(
412412
f'Unable to update prevent_destroy in file {tf_file.resolve()}')
413413
raise TerraformTestError(
414-
f'Unable to update prevent_destroy in file ({tf_file.resolve()}) failed')
414+
f'Unable to update prevent_destroy in file ({tf_file.resolve()}) failed'
415+
)
415416

416417
# link extra files inside dir
417418
filenames = []
@@ -432,11 +433,11 @@ def setup(self, extra_files=None, plugin_dir=None, init_vars=None,
432433
_LOGGER.debug('linked %s', link_src)
433434
else:
434435
_LOGGER.warning('no such file {}'.format(link_src))
435-
self._finalizer = weakref.finalize(
436-
self, self._cleanup, self.tfdir, filenames, deep=cleanup_on_exit,
437-
restore_files=disable_prevent_destroy)
438-
setup_output = self.init(plugin_dir=plugin_dir,
439-
init_vars=init_vars, backend=backend, **kw)
436+
self._finalizer = weakref.finalize(self, self._cleanup, self.tfdir,
437+
filenames, deep=cleanup_on_exit,
438+
restore_files=disable_prevent_destroy)
439+
setup_output = self.init(plugin_dir=plugin_dir, init_vars=init_vars,
440+
backend=backend, **kw)
440441
if workspace_name:
441442
setup_output += self.workspace(name=workspace_name)
442443
return setup_output
@@ -453,7 +454,11 @@ def workspace(self, name=None):
453454
"""Run Terraform workspace command."""
454455
raw_ws_out = self.execute_command('workspace', *['list']).out
455456
cmd_args = ['select', name]
456-
if name not in [ws.replace('*', '').strip() for ws in raw_ws_out.split('\n') if len(ws) > 0]:
457+
if name not in [
458+
ws.replace('*', '').strip()
459+
for ws in raw_ws_out.split('\n')
460+
if len(ws) > 0
461+
]:
457462
cmd_args = ['new', name]
458463
return self.execute_command('workspace', *cmd_args).out
459464

@@ -472,28 +477,27 @@ def plan(self, input=False, color=False, refresh=True, tf_vars=None,
472477
output: Determines if output will be returned.
473478
tf_var_file: Path to terraform variable configuration file relative to `self.tfdir`.
474479
"""
475-
cmd_args = parse_args(input=input, color=color,
476-
refresh=refresh, tf_vars=tf_vars,
477-
targets=targets, tf_var_file=tf_var_file, **kw)
480+
cmd_args = parse_args(input=input, color=color, refresh=refresh,
481+
tf_vars=tf_vars, targets=targets,
482+
tf_var_file=tf_var_file, **kw)
478483
if not output:
479484
return self.execute_command('plan', *cmd_args).out
480485
with tempfile.NamedTemporaryFile() as fp:
481486
fp.close()
482487
# for tg we need to specify a temp name that is relative for the output to go into each
483488
# of the .terragrunt-cache, then plan / show would work, otherwise it overwrites each other!
484-
temp_file = fp.name if len(
485-
self._tg_ra()) == 0 else os.path.basename(fp.name)
489+
temp_file = fp.name if len(self._tg_ra()) == 0 else os.path.basename(
490+
fp.name)
486491
cmd_args.append('-out={}'.format(temp_file))
487492
self.execute_command('plan', *cmd_args)
488493
result = self.execute_command('show', '-no-color', '-json', temp_file)
489494
try:
490495
return self._plan_formatter(result.out)
491496
except json.JSONDecodeError as e:
492-
raise TerraformTestError(
493-
'Error decoding plan output: {}'.format(e))
497+
raise TerraformTestError('Error decoding plan output: {}'.format(e))
494498

495-
def apply(self, input=False, color=False, auto_approve=True,
496-
tf_vars=None, targets=None, tf_var_file=None, **kw):
499+
def apply(self, input=False, color=False, auto_approve=True, tf_vars=None,
500+
targets=None, tf_var_file=None, **kw):
497501
"""
498502
Run Terraform apply command.
499503
@@ -506,9 +510,9 @@ def apply(self, input=False, color=False, auto_approve=True,
506510
and its dependencies
507511
tf_var_file: Path to terraform variable configuration file relative to `self.tfdir`.
508512
"""
509-
cmd_args = parse_args(input=input, color=color,
510-
auto_approve=auto_approve, tf_vars=tf_vars,
511-
targets=targets, tf_var_file=tf_var_file, **kw)
513+
cmd_args = parse_args(input=input, color=color, auto_approve=auto_approve,
514+
tf_vars=tf_vars, targets=targets,
515+
tf_var_file=tf_var_file, **kw)
512516
return self.execute_command('apply', *cmd_args).out
513517

514518
def output(self, name=None, color=False, json_format=True, **kw):
@@ -526,17 +530,18 @@ def output(self, name=None, color=False, json_format=True, **kw):
526530
_LOGGER.warning('error decoding output: {}'.format(e))
527531
return output
528532

529-
def destroy(self, color=False, auto_approve=True, tf_vars=None, targets=None, tf_var_file=None, **kw):
533+
def destroy(self, color=False, auto_approve=True, tf_vars=None, targets=None,
534+
tf_var_file=None, **kw):
530535
"""Run Terraform destroy command."""
531536
cmd_args = parse_args(color=color, auto_approve=auto_approve,
532537
tf_vars=tf_vars, targets=targets,
533-
tf_var_file=tf_var_file, **kw)
538+
tf_var_file=tf_var_file, **kw)
534539
return self.execute_command('destroy', *cmd_args).out
535540

536-
def refresh(self, color=False, lock=False, tf_vars=None, targets=None, **kw):
541+
def refresh(self, color=False, lock=False, tf_vars=None, targets=None, **kw):
537542
"""Run Terraform refresh command."""
538-
cmd_args = parse_args(color=color, lock=lock,
539-
tf_vars=tf_vars, targets=targets, **kw)
543+
cmd_args = parse_args(color=color, lock=lock, tf_vars=tf_vars,
544+
targets=targets, **kw)
540545
return self.execute_command('refresh', *cmd_args).out
541546

542547
def state_pull(self):
@@ -557,13 +562,9 @@ def execute_command(self, cmd, *cmd_args):
557562
retcode = None
558563
full_output_lines = []
559564
try:
560-
p = subprocess.Popen(cmdline,
561-
stdout=subprocess.PIPE,
562-
stderr=subprocess.PIPE,
563-
cwd=self.tfdir,
564-
env=self.env,
565-
universal_newlines=True,
566-
encoding='utf-8',
565+
p = subprocess.Popen(cmdline, stdout=subprocess.PIPE,
566+
stderr=subprocess.PIPE, cwd=self.tfdir, env=self.env,
567+
universal_newlines=True, encoding='utf-8',
567568
errors='ignore')
568569
while True:
569570
output = p.stdout.readline()
@@ -610,7 +611,8 @@ def _parse_run_all_out(output: str, formatter: TerraformJSONBase) -> str:
610611

611612
class TerragruntTest(TerraformTest):
612613

613-
def __init__(self, tfdir, basedir=None, binary='terragrunt', env=None, tg_run_all=False):
614+
def __init__(self, tfdir, basedir=None, binary='terragrunt', env=None,
615+
tg_run_all=False):
614616
"""A helper class that could be used for testing terragrunt
615617
616618
Most operations that apply to :func:`~TerraformTest` also apply to this class.
@@ -631,7 +633,7 @@ def __init__(self, tfdir, basedir=None, binary='terragrunt', env=None, tg_run_al
631633
TerraformTest.__init__(self, tfdir, basedir, binary, env)
632634
self.tg_run_all = tg_run_all
633635
if self.tg_run_all:
634-
self._plan_formatter = partial(
635-
_parse_run_all_out, formatter=TerraformPlanOutput)
636-
self._output_formatter = partial(
637-
_parse_run_all_out, formatter=TerraformValueDict)
636+
self._plan_formatter = partial(_parse_run_all_out,
637+
formatter=TerraformPlanOutput)
638+
self._output_formatter = partial(_parse_run_all_out,
639+
formatter=TerraformValueDict)

0 commit comments

Comments
 (0)