tgwrap 0.8.11__py3-none-any.whl → 0.8.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tgwrap/cli.py +10 -4
- tgwrap/deploy.py +54 -51
- tgwrap/main.py +15 -10
- {tgwrap-0.8.11.dist-info → tgwrap-0.8.13.dist-info}/METADATA +4 -2
- tgwrap-0.8.13.dist-info/RECORD +11 -0
- tgwrap-0.8.11.dist-info/RECORD +0 -11
- {tgwrap-0.8.11.dist-info → tgwrap-0.8.13.dist-info}/LICENSE +0 -0
- {tgwrap-0.8.11.dist-info → tgwrap-0.8.13.dist-info}/WHEEL +0 -0
- {tgwrap-0.8.11.dist-info → tgwrap-0.8.13.dist-info}/entry_points.txt +0 -0
tgwrap/cli.py
CHANGED
@@ -50,7 +50,7 @@ CLICK_CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
|
|
50
50
|
|
51
51
|
TG_COMMANDS=[
|
52
52
|
'init', 'validate', 'validate-inputs', 'plan', 'apply',
|
53
|
-
'destroy', 'info', 'output', 'show',
|
53
|
+
'destroy', 'info', 'output', 'show', 'state',
|
54
54
|
]
|
55
55
|
STAGES=['global', 'sbx', 'dev', 'qas', 'run', 'tst', 'acc', 'prd']
|
56
56
|
class DefaultGroup(click.Group):
|
@@ -107,7 +107,7 @@ def main():
|
|
107
107
|
ignore_unknown_options=True,
|
108
108
|
),
|
109
109
|
)
|
110
|
-
@click.argument('command', type=click.Choice(TG_COMMANDS + ['
|
110
|
+
@click.argument('command', type=click.Choice(TG_COMMANDS + ['render-json']))
|
111
111
|
@click.option('--verbose', '-v', is_flag=True, default=False,
|
112
112
|
help='Verbose printing',
|
113
113
|
show_default=True
|
@@ -210,6 +210,10 @@ def run(command, verbose, debug, dry_run, no_lock, update, upgrade,
|
|
210
210
|
help='Run the command step by step and stop when an error occurs (where applicable)',
|
211
211
|
show_default=True
|
212
212
|
)
|
213
|
+
@click.option('--continue-on-error', '-C', is_flag=True, default=False,
|
214
|
+
help='When running in step by step, continue when an error occurs',
|
215
|
+
show_default=True
|
216
|
+
)
|
213
217
|
@click.option('--planfile', '-p', is_flag=True, default=False,
|
214
218
|
help='Use the generated planfile when applying the changes',
|
215
219
|
show_default=True
|
@@ -244,7 +248,7 @@ def run(command, verbose, debug, dry_run, no_lock, update, upgrade,
|
|
244
248
|
@click.argument('terragrunt-args', nargs=-1, type=click.UNPROCESSED)
|
245
249
|
@click.version_option(version=__version__)
|
246
250
|
def run_all(command, verbose, debug, dry_run, no_lock, update, upgrade, exclude_external_dependencies,
|
247
|
-
step_by_step, planfile, auto_approve, clean, working_dir, start_at_step,
|
251
|
+
step_by_step, continue_on_error, planfile, auto_approve, clean, working_dir, start_at_step,
|
248
252
|
limit_parallelism, include_dir, exclude_dir, terragrunt_args):
|
249
253
|
""" Executes a terragrunt command across multiple projects """
|
250
254
|
|
@@ -260,6 +264,7 @@ def run_all(command, verbose, debug, dry_run, no_lock, update, upgrade, exclude_
|
|
260
264
|
upgrade=upgrade,
|
261
265
|
exclude_external_dependencies=exclude_external_dependencies,
|
262
266
|
step_by_step=step_by_step,
|
267
|
+
continue_on_error=continue_on_error,
|
263
268
|
planfile=planfile,
|
264
269
|
auto_approve=auto_approve,
|
265
270
|
clean=clean,
|
@@ -392,9 +397,10 @@ def run_import(address, id, verbose, dry_run, working_dir, no_lock, terragrunt_a
|
|
392
397
|
help='A glob of a directory that needs to be excluded, this option can be used multiple times. For example: -E "integrations/\*/\*"',
|
393
398
|
show_default=True,
|
394
399
|
)
|
395
|
-
@click.option('--planfile-dir', '-P', default=
|
400
|
+
@click.option('--planfile-dir', '-P', default='.terragrunt-cache/current',
|
396
401
|
help='Relative path to directory with plan file (or set TGWRAP_PLANFILE_DIR environment variable), see README for more details',
|
397
402
|
envvar='TGWRAP_PLANFILE_DIR', type=click.Path(),
|
403
|
+
show_default=True,
|
398
404
|
)
|
399
405
|
@click.argument('terragrunt-args', nargs=-1, type=click.UNPROCESSED)
|
400
406
|
@click.version_option(version=__version__)
|
tgwrap/deploy.py
CHANGED
@@ -171,66 +171,69 @@ def prepare_deploy_config(step, config, source_dir, source_config_dir, target_di
|
|
171
171
|
printer.warning(f'Source path of config file does not exist: {source_path}')
|
172
172
|
|
173
173
|
for ss, substack in substack_configs:
|
174
|
-
|
175
|
-
|
176
|
-
source_path = os.path.join(
|
177
|
-
source_dir, source_stage, substack['source'], ''
|
178
|
-
)
|
179
|
-
target_path = os.path.join(
|
180
|
-
target_dir, substack['target'], ''
|
181
|
-
)
|
182
|
-
|
183
|
-
include_modules = substack['include_modules'] if len(substack.get('include_modules', {})) > 0 else []
|
184
|
-
printer.verbose(f'Include modules: {include_modules}')
|
185
|
-
|
186
|
-
if include_modules:
|
187
|
-
# get all directories in the substack and create an exlude_modules list from that
|
188
|
-
source_directories = get_directories(source_path)
|
189
|
-
exclude_modules = list(set(source_directories) - set(include_modules))
|
190
|
-
else:
|
191
|
-
exclude_modules = substack.get('exclude_modules', [])
|
192
|
-
|
193
|
-
if os.path.exists(source_path):
|
194
|
-
deploy_actions[f'substack -> {substack["target"]}'] = {
|
195
|
-
"source": source_path,
|
196
|
-
"target": target_path,
|
197
|
-
"excludes": exclude_modules,
|
198
|
-
}
|
174
|
+
if 'applies_to_stages' in substack and target_stage not in substack['applies_to_stages']:
|
175
|
+
printer.verbose(f'Target stage {target_stage} not applicable for substack {ss}.')
|
199
176
|
else:
|
200
|
-
printer.
|
201
|
-
|
202
|
-
if len(substack.get('configs', [])) > 0:
|
203
|
-
# run some checks and sets some variables
|
204
|
-
if not source_config_dir:
|
205
|
-
raise Exception("Config files must be deployed but 'config_path' variable is not set!")
|
206
|
-
elif not config_dir:
|
207
|
-
raise Exception("Config files must be deployed but 'config_dir' variable is not set!")
|
177
|
+
printer.verbose(f'Found substack : {ss}')
|
208
178
|
|
179
|
+
source_path = os.path.join(
|
180
|
+
source_dir, source_stage, substack['source'], ''
|
181
|
+
)
|
209
182
|
target_path = os.path.join(
|
210
|
-
target_dir,
|
183
|
+
target_dir, substack['target'], ''
|
211
184
|
)
|
212
|
-
# the target path might not exist
|
213
|
-
try:
|
214
|
-
os.makedirs(target_path)
|
215
|
-
except FileExistsError:
|
216
|
-
pass
|
217
185
|
|
218
|
-
|
219
|
-
printer.verbose(f'
|
186
|
+
include_modules = substack['include_modules'] if len(substack.get('include_modules', {})) > 0 else []
|
187
|
+
printer.verbose(f'Include modules: {include_modules}')
|
220
188
|
|
221
|
-
|
222
|
-
|
223
|
-
)
|
189
|
+
if include_modules:
|
190
|
+
# get all directories in the substack and create an exlude_modules list from that
|
191
|
+
source_directories = get_directories(source_path)
|
192
|
+
exclude_modules = list(set(source_directories) - set(include_modules))
|
193
|
+
else:
|
194
|
+
exclude_modules = substack.get('exclude_modules', [])
|
224
195
|
|
225
|
-
full_target_path = os.path.dirname(os.path.join(target_path, cfg))
|
226
|
-
# print("source: ", full_source_path)
|
227
|
-
# print("target: ", full_target_path)
|
228
196
|
if os.path.exists(source_path):
|
229
|
-
deploy_actions[f
|
230
|
-
"source":
|
231
|
-
"target":
|
197
|
+
deploy_actions[f'substack -> {substack["target"]}'] = {
|
198
|
+
"source": source_path,
|
199
|
+
"target": target_path,
|
200
|
+
"excludes": exclude_modules,
|
232
201
|
}
|
233
202
|
else:
|
234
|
-
printer.warning(f'Source path of
|
203
|
+
printer.warning(f'Source path of substack does not exist: {source_path}')
|
204
|
+
|
205
|
+
if len(substack.get('configs', [])) > 0:
|
206
|
+
# run some checks and sets some variables
|
207
|
+
if not source_config_dir:
|
208
|
+
raise Exception("Config files must be deployed but 'config_path' variable is not set!")
|
209
|
+
elif not config_dir:
|
210
|
+
raise Exception("Config files must be deployed but 'config_dir' variable is not set!")
|
211
|
+
|
212
|
+
target_path = os.path.join(
|
213
|
+
target_dir, config_dir, module_name, target_stage, substack['target'], '',
|
214
|
+
)
|
215
|
+
# the target path might not exist
|
216
|
+
try:
|
217
|
+
os.makedirs(target_path)
|
218
|
+
except FileExistsError:
|
219
|
+
pass
|
220
|
+
|
221
|
+
for cfg in substack.get('configs', []):
|
222
|
+
printer.verbose(f'Found substack config file : {cfg}')
|
223
|
+
|
224
|
+
full_source_path = os.path.join(
|
225
|
+
source_config_dir, source_stage, substack['source'], cfg
|
226
|
+
)
|
227
|
+
|
228
|
+
full_target_path = os.path.dirname(os.path.join(target_path, cfg))
|
229
|
+
# print("source: ", full_source_path)
|
230
|
+
# print("target: ", full_target_path)
|
231
|
+
if os.path.exists(source_path):
|
232
|
+
deploy_actions[f"substack {substack['target']} configs -> {os.path.join(substack['source'], cfg)}"] = {
|
233
|
+
"source": full_source_path,
|
234
|
+
"target": full_target_path,
|
235
|
+
}
|
236
|
+
else:
|
237
|
+
printer.warning(f'Source path of config file does not exist: {source_path}')
|
235
238
|
|
236
239
|
return deploy_actions
|
tgwrap/main.py
CHANGED
@@ -102,7 +102,7 @@ class TgWrap():
|
|
102
102
|
'info': '{base_command} terragrunt-info --terragrunt-non-interactive {update} {upgrade} {common}',
|
103
103
|
'plan': '{base_command} {command} --terragrunt-non-interactive -out={planfile_name} {lock_level} {update} {parallelism} {common}',
|
104
104
|
'apply': '{base_command} {command} {non_interactive} {no_auto_approve} {update} {parallelism} {common} {planfile}',
|
105
|
-
'show': '{base_command} {command} --terragrunt-non-interactive {
|
105
|
+
'show': '{base_command} {command} --terragrunt-non-interactive {common} {planfile_name}',
|
106
106
|
'destroy': '{base_command} {command} {non_interactive} {no_auto_approve} {parallelism} {common} {planfile}',
|
107
107
|
}
|
108
108
|
|
@@ -656,7 +656,7 @@ class TgWrap():
|
|
656
656
|
self, command, exclude_external_dependencies, start_at_step, dry_run,
|
657
657
|
parallel_execution=False, ask_for_confirmation=False, collect_output_file=None,
|
658
658
|
backwards=False, working_dir=None, include_dirs=[], exclude_dirs=[],
|
659
|
-
use_native_terraform=False, add_to_workdir=None,
|
659
|
+
use_native_terraform=False, add_to_workdir=None, continue_on_error=False,
|
660
660
|
):
|
661
661
|
"Runs the desired command in the directories as defined in the directed graph"
|
662
662
|
|
@@ -753,7 +753,7 @@ class TgWrap():
|
|
753
753
|
progress=progress,
|
754
754
|
)
|
755
755
|
|
756
|
-
if stop_processing:
|
756
|
+
if stop_processing and not continue_on_error:
|
757
757
|
self.printer.warning(f"Processing needs to be stopped at step {step_nbr}.")
|
758
758
|
self.printer.normal(
|
759
759
|
f"After you've fixed the problem, you can continue where you left off by adding '--start-at-step {step_nbr}'."
|
@@ -884,14 +884,14 @@ class TgWrap():
|
|
884
884
|
# tgwrap state mv 'azuread_group.this["viewers"]' 'azuread_group.this["readers"]'
|
885
885
|
rc = subprocess.run(
|
886
886
|
shlex.split(cmd, posix=False),
|
887
|
-
cwd=cwd,
|
887
|
+
cwd=cwd if cwd else None,
|
888
888
|
)
|
889
889
|
self.printer.verbose(rc)
|
890
890
|
|
891
891
|
sys.exit(rc.returncode)
|
892
892
|
|
893
893
|
def run_all(self, command, debug, dry_run, no_lock, update, upgrade,
|
894
|
-
exclude_external_dependencies, step_by_step, planfile, auto_approve, clean,
|
894
|
+
exclude_external_dependencies, step_by_step, continue_on_error, planfile, auto_approve, clean,
|
895
895
|
working_dir, start_at_step, limit_parallelism, include_dirs, exclude_dirs, terragrunt_args):
|
896
896
|
""" Executes a terragrunt command across multiple modules """
|
897
897
|
|
@@ -903,10 +903,6 @@ class TgWrap():
|
|
903
903
|
modifying_command = (command.lower() in ['apply', 'destroy'])
|
904
904
|
auto_approve = auto_approve if modifying_command else True
|
905
905
|
|
906
|
-
# if the dir is not ending on '/*', add it
|
907
|
-
include_dirs = [dir.rstrip(f'.{os.path.sep}*') + f'{os.path.sep}*' for dir in include_dirs]
|
908
|
-
exclude_dirs = [dir.rstrip(f'.{os.path.sep}*') + f'{os.path.sep}*' for dir in exclude_dirs]
|
909
|
-
|
910
906
|
cmd = self._construct_command(
|
911
907
|
command=command,
|
912
908
|
allow_no_run_all=False,
|
@@ -933,11 +929,16 @@ class TgWrap():
|
|
933
929
|
f'This command will be executed for each individual module:\n$ {cmd}'
|
934
930
|
)
|
935
931
|
|
932
|
+
# if the dir is not ending on '/*', add it
|
933
|
+
include_dirs = [dir.rstrip(f'.{os.path.sep}*') + f'{os.path.sep}*' for dir in include_dirs]
|
934
|
+
exclude_dirs = [dir.rstrip(f'.{os.path.sep}*') + f'{os.path.sep}*' for dir in exclude_dirs]
|
935
|
+
|
936
936
|
self._run_di_graph(
|
937
937
|
command=cmd,
|
938
938
|
exclude_external_dependencies=exclude_external_dependencies,
|
939
939
|
working_dir=working_dir,
|
940
940
|
ask_for_confirmation=(not auto_approve),
|
941
|
+
continue_on_error=continue_on_error,
|
941
942
|
dry_run=dry_run,
|
942
943
|
start_at_step=start_at_step,
|
943
944
|
backwards=True if command.lower() in ['destroy'] else False,
|
@@ -1314,6 +1315,10 @@ class TgWrap():
|
|
1314
1315
|
# and make it unique
|
1315
1316
|
substacks = set(substacks)
|
1316
1317
|
|
1318
|
+
# the manifest file supports both `sub_stacks` and `substack` config name. Annoying to be a bit autistic when it comes to naming :-/
|
1319
|
+
substack_configs = manifest.get('sub_stacks', {})
|
1320
|
+
substack_configs.update(manifest.get('substacks', {}))
|
1321
|
+
|
1317
1322
|
for target_stage in target_stages:
|
1318
1323
|
target_dir = os.path.join(working_dir, '', target_stage)
|
1319
1324
|
self.printer.header(f'Deploy stage {target_stage} to {target_dir}...')
|
@@ -1337,7 +1342,7 @@ class TgWrap():
|
|
1337
1342
|
target_dir=target_dir,
|
1338
1343
|
target_stage=target_stage,
|
1339
1344
|
substacks=substacks,
|
1340
|
-
substack_configs=
|
1345
|
+
substack_configs=substack_configs.items(),
|
1341
1346
|
tg_file_name=self.TERRAGRUNT_FILE,
|
1342
1347
|
verbose=self.printer.print_verbose,
|
1343
1348
|
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: tgwrap
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.13
|
4
4
|
Summary: A (terragrunt) wrapper around a (terraform) wrapper around ....
|
5
5
|
Home-page: https://gitlab.com/lunadata/tgwrap
|
6
6
|
License: MIT
|
@@ -230,7 +230,7 @@ deploy: # which modules do you want to deploy
|
|
230
230
|
# - source: networking
|
231
231
|
# - target: networking-connected
|
232
232
|
|
233
|
-
|
233
|
+
substacks:
|
234
234
|
is01:
|
235
235
|
source: shared-integration/intsvc01
|
236
236
|
target: integration/is01
|
@@ -240,6 +240,8 @@ sub_stacks:
|
|
240
240
|
- my-ss-config.hcl
|
241
241
|
- ../my-ss-config-dir
|
242
242
|
is02:
|
243
|
+
applies_to_stages: # optional list of stages to include the substack in
|
244
|
+
- dev
|
243
245
|
source: shared-integration/intsvc01
|
244
246
|
target: integration/is02
|
245
247
|
|
@@ -0,0 +1,11 @@
|
|
1
|
+
tgwrap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
tgwrap/analyze.py,sha256=CsSaGv-be6ATy36z9X7x00gpKY59soJys2VbIzD-tmg,8726
|
3
|
+
tgwrap/cli.py,sha256=sWUHP4EYUeKpYP3KgU3t2Er4YLlnwfXNVQkgVwmGFPM,29342
|
4
|
+
tgwrap/deploy.py,sha256=bJiox_fz8JsoPreX4woW6-EqAebhpJWnKUVLVeGXkrI,10000
|
5
|
+
tgwrap/main.py,sha256=Dqqzqe2x7UXHgAgQL764CfoZ6V0JFbk4TphBkBcYcC8,74872
|
6
|
+
tgwrap/printer.py,sha256=dkcOCPIPB-IP6pn8QMpa06xlcqPFVaDvxnz-QEpDJV0,2663
|
7
|
+
tgwrap-0.8.13.dist-info/LICENSE,sha256=VT-AVxIXt3EQTC-7Hy1uPGnrDNJLqfcgLgJD78fiyx4,1065
|
8
|
+
tgwrap-0.8.13.dist-info/METADATA,sha256=EDgJtU6kePAZrn-aft9LWPDImK_5qEYaBoJCGhY_L3s,11616
|
9
|
+
tgwrap-0.8.13.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
10
|
+
tgwrap-0.8.13.dist-info/entry_points.txt,sha256=H8X0PMPmd4aW7Y9iyChZ0Ug6RWGXqhRUvHH-6f6Mxz0,42
|
11
|
+
tgwrap-0.8.13.dist-info/RECORD,,
|
tgwrap-0.8.11.dist-info/RECORD
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
tgwrap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
tgwrap/analyze.py,sha256=CsSaGv-be6ATy36z9X7x00gpKY59soJys2VbIzD-tmg,8726
|
3
|
-
tgwrap/cli.py,sha256=weYPXnpZ1200L28tNGzVaO_GlX7wAdLn1nQZsHKpe3k,29060
|
4
|
-
tgwrap/deploy.py,sha256=oD-H1ojdcSQTbiQHs7BZP9NnbV_MDtlpJFaDRl2-KvU,9578
|
5
|
-
tgwrap/main.py,sha256=Eocuece85XM5GHF6TA7OdhxHZqTlbSds5gu7lpZQGQA,74459
|
6
|
-
tgwrap/printer.py,sha256=dkcOCPIPB-IP6pn8QMpa06xlcqPFVaDvxnz-QEpDJV0,2663
|
7
|
-
tgwrap-0.8.11.dist-info/LICENSE,sha256=VT-AVxIXt3EQTC-7Hy1uPGnrDNJLqfcgLgJD78fiyx4,1065
|
8
|
-
tgwrap-0.8.11.dist-info/METADATA,sha256=l8Pmnql-7cijxcy8Hysw3Ka54ZQ2vYumjwDTz6TW3eQ,11529
|
9
|
-
tgwrap-0.8.11.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
10
|
-
tgwrap-0.8.11.dist-info/entry_points.txt,sha256=H8X0PMPmd4aW7Y9iyChZ0Ug6RWGXqhRUvHH-6f6Mxz0,42
|
11
|
-
tgwrap-0.8.11.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|