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
1716See documentation in the TerraformTest class for usage. Terraform wrapping
4140from pathlib import Path
4241from 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
5250TerraformStateResource = 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
611612class 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