mux_tf 0.15.0 → 0.16.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5eb20c09b5f5f2fcccfc4ed93018fe52ec1aaf75f5e60981fd0eff0bba895466
4
- data.tar.gz: 8122a0889cf0da6fa0e237ab94ecac94920daf4f863a218dba8df9bf0cfcc5a1
3
+ metadata.gz: 8f2bcb9ac31603cfab773175a4d81a7ab85e8cd44124dbdf7287234dd70537db
4
+ data.tar.gz: e50b7671fabc4ca94703452daef5fa3a0322f2f4fce103d2a1f02b05012e80a1
5
5
  SHA512:
6
- metadata.gz: 020b9f50b507c0f91b6b64bcfbf027010d3a39de8e47829e5fb9dc0407af2e0c850c7fc1942b23e64febf603e028c608d2f0ce86324a7bac410c4c05cfddeea6
7
- data.tar.gz: aacb58be2f7c86984354d1aad771b898eb7f6979a25df2465448233730ad7db5178914e5b76add063de42536b2358e4c2ef8c4b763819158f079a41d349f2671
6
+ metadata.gz: 5bf42a828264de913553c309f8acf9274175a7a2a2e9bc71f04534e91649ca6e91727ccf21677406f52d71331771935ead9f2011c4bb6b4780988174b7860767
7
+ data.tar.gz: abaaff54837201173a5c2ccaa796e2c501d48f4f8d72c46687b6275b6541037284be57dd8090c88d4d88c3eeee8983f68b03ce85e6a6bea4d98df914d1e62dbd
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MuxTf
4
+ module Cli
5
+ module Current
6
+ class PlanCommand
7
+ include TerraformHelpers
8
+ include PiotrbCliUtils::CriCommandSupport
9
+ extend PiotrbCliUtils::Util
10
+
11
+ def plan_cmd
12
+ define_cmd("plan", summary: "Re-run plan") do |_opts, _args, _cmd|
13
+ run_validate && run_plan
14
+ end
15
+ end
16
+
17
+ # returns boolean true if succeeded
18
+ def run_validate(level: 1)
19
+ Current.remedy_retry_helper(from: :validate, level: level) do
20
+ validation_info = validate
21
+ PlanFormatter.print_validation_errors(validation_info)
22
+ remedies = PlanFormatter.process_validation(validation_info)
23
+ [remedies, validation_info]
24
+ end
25
+ end
26
+
27
+ def run_plan(targets: [], level: 1, retry_count: 0)
28
+ plan_status, = Current.remedy_retry_helper(from: :plan, level: level, attempt: retry_count) {
29
+ @last_lock_info = nil
30
+
31
+ plan_filename = PlanFilenameGenerator.for_path
32
+
33
+ plan_status, meta = create_plan(plan_filename, targets: targets)
34
+
35
+ Current.print_errors_and_warnings(meta)
36
+
37
+ remedies = detect_remedies_from_plan(meta)
38
+
39
+ if remedies.include?(:unlock)
40
+ @last_lock_info = extract_lock_info(meta)
41
+ throw :abort, [plan_status, meta]
42
+ end
43
+
44
+ throw :abort, [plan_status, meta] if remedies.include?(:auth)
45
+
46
+ [remedies, plan_status, meta]
47
+ }
48
+
49
+ case plan_status
50
+ when :ok
51
+ log "no changes", depth: 1
52
+ when :error
53
+ log "something went wrong", depth: 1
54
+ when :changes
55
+ unless ENV["JSON_PLAN"]
56
+ log "Printing Plan Summary ...", depth: 1
57
+ plan_filename = PlanFilenameGenerator.for_path
58
+ pretty_plan_summary(plan_filename)
59
+ end
60
+ puts plan_summary_text if ENV["JSON_PLAN"]
61
+ when :unknown
62
+ # nothing
63
+ end
64
+
65
+ plan_status
66
+ end
67
+
68
+ private
69
+
70
+ def validate
71
+ log "Validating module ...", depth: 1
72
+ tf_validate # from Terraform Helpers
73
+ end
74
+
75
+ def create_plan(filename, targets: [])
76
+ log "Preparing Plan ...", depth: 1
77
+ exit_code, meta = PlanFormatter.pretty_plan(filename, targets: targets)
78
+ case exit_code
79
+ when 0
80
+ [:ok, meta]
81
+ when 1
82
+ [:error, meta]
83
+ when 2
84
+ [:changes, meta]
85
+ else
86
+ log pastel.yellow("terraform plan exited with an unknown exit code: #{exit_code}")
87
+ [:unknown, meta]
88
+ end
89
+ end
90
+
91
+ def detect_remedies_from_plan(meta)
92
+ remedies = Set.new
93
+ meta[:errors]&.each do |error|
94
+ remedies << :plan if error[:message].include?("timeout while waiting for plugin to start")
95
+ end
96
+ remedies << :unlock if lock_error?(meta)
97
+ remedies << :auth if meta[:need_auth]
98
+ remedies
99
+ end
100
+
101
+ def lock_error?(meta)
102
+ meta && meta["error"] == "lock"
103
+ end
104
+
105
+ def extract_lock_info(meta)
106
+ {
107
+ lock_id: meta["ID"],
108
+ operation: meta["Operation"],
109
+ who: meta["Who"],
110
+ created: meta["Created"]
111
+ }
112
+ end
113
+
114
+ def pretty_plan_summary(filename)
115
+ plan = PlanSummaryHandler.from_file(filename)
116
+ plan.simple_summary do |line|
117
+ log line, depth: 2
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -11,10 +11,19 @@ module MuxTf
11
11
  extend PiotrbCliUtils::CmdLoop
12
12
  include Coloring
13
13
 
14
- class << self # rubocop:disable Metrics/ClassLength
14
+ class << self
15
+ attr_accessor :plan_command
16
+
15
17
  def run(args)
16
18
  version_check
17
19
 
20
+ self.plan_command = PlanCommand.new
21
+
22
+ ENV["TF_IN_AUTOMATION"] = "1"
23
+ ENV["TF_INPUT"] = "0"
24
+ ENV["TERRAGRUNT_JSON_LOG"] = "1"
25
+ ENV["TERRAGRUNT_FORWARD_TF_STDOUT"] = "1"
26
+
18
27
  if args[0] == "cli"
19
28
  cmd_loop
20
29
  return
@@ -36,17 +45,14 @@ module MuxTf
36
45
  folder_name = File.basename(Dir.getwd)
37
46
  log "Processing #{pastel.cyan(folder_name)} ..."
38
47
 
39
- ENV["TF_IN_AUTOMATION"] = "1"
40
- ENV["TF_INPUT"] = "0"
41
-
42
- return launch_cmd_loop(:error) unless run_validate
48
+ return launch_cmd_loop(:error) unless @plan_command.run_validate
43
49
 
44
50
  if ENV["TF_UPGRADE"]
45
51
  upgrade_status, _upgrade_meta = run_upgrade
46
52
  return launch_cmd_loop(:error) unless upgrade_status == :ok
47
53
  end
48
54
 
49
- plan_status = run_plan
55
+ plan_status = @plan_command.run_plan
50
56
 
51
57
  case plan_status
52
58
  when :ok
@@ -60,23 +66,6 @@ module MuxTf
60
66
  end
61
67
  end
62
68
 
63
- def plan_filename
64
- PlanFilenameGenerator.for_path
65
- end
66
-
67
- private
68
-
69
- def version_check
70
- return unless VersionCheck.has_updates?
71
-
72
- log pastel.yellow("=" * 80)
73
- log "New version of #{pastel.cyan('mux_tf')} is available!"
74
- log "You are currently on version: #{pastel.yellow(VersionCheck.current_gem_version)}"
75
- log "Latest version found is: #{pastel.green(VersionCheck.latest_gem_version)}"
76
- log "Run `#{pastel.green('gem install mux_tf')}` to update!"
77
- log pastel.yellow("=" * 80)
78
- end
79
-
80
69
  # block is expected to return a touple, the first element is a list of remedies
81
70
  # the rest are any additional results
82
71
  def remedy_retry_helper(from:, level: 1, attempt: 0, &block)
@@ -86,24 +75,61 @@ module MuxTf
86
75
  remedies, *results = block.call
87
76
  return results if remedies.empty?
88
77
 
89
- remedy_status, _remedy_results = process_remedies(remedies, from: from, level: level)
78
+ remedy_status, remedy_results = process_remedies(remedies, from: from, level: level)
79
+ throw :abort, false if remedy_results[:user_error]
90
80
  return remedy_status if remedy_status
91
81
  end
92
82
  log "!! giving up because attempt: #{attempt}"
93
83
  end
94
84
  end
95
85
 
96
- # returns boolean true if succeeded
97
- def run_validate(level: 1)
98
- remedy_retry_helper(from: :validate, level: level) do
99
- validation_info = validate
100
- PlanFormatter.print_validation_errors(validation_info)
101
- remedies = PlanFormatter.process_validation(validation_info)
102
- [remedies, validation_info]
86
+ def print_errors_and_warnings(meta)
87
+ message = []
88
+ message << pastel.yellow("#{meta[:warnings].length} Warnings") if meta[:warnings]
89
+ message << pastel.red("#{meta[:errors].length} Errors") if meta[:errors]
90
+ if message.length.positive?
91
+ log ""
92
+ log "Encountered: #{message.join(' and ')}"
93
+ log ""
94
+ end
95
+
96
+ meta[:warnings]&.each do |warning|
97
+ log "-" * 20
98
+ log pastel.yellow("Warning: #{warning[:message]}")
99
+ warning[:body]&.each do |line|
100
+ log pastel.yellow(line), depth: 1
101
+ end
102
+ log ""
103
103
  end
104
+
105
+ meta[:errors]&.each do |error|
106
+ log "-" * 20
107
+ log pastel.red("Error: #{error[:message]}")
108
+ error[:body]&.each do |line|
109
+ log pastel.red(line), depth: 1
110
+ end
111
+ log ""
112
+ end
113
+
114
+ return unless message.length.positive?
115
+
116
+ log ""
104
117
  end
105
118
 
106
- def process_remedies(remedies, from: nil, level: 1, retry_count: 0) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
119
+ private
120
+
121
+ def version_check
122
+ return unless VersionCheck.has_updates?
123
+
124
+ log pastel.yellow("=" * 80)
125
+ log "New version of #{pastel.cyan('mux_tf')} is available!"
126
+ log "You are currently on version: #{pastel.yellow(VersionCheck.current_gem_version)}"
127
+ log "Latest version found is: #{pastel.green(VersionCheck.latest_gem_version)}"
128
+ log "Run `#{pastel.green('gem install mux_tf')}` to update!"
129
+ log pastel.yellow("=" * 80)
130
+ end
131
+
132
+ def process_remedies(remedies, from: nil, level: 1, retry_count: 0)
107
133
  remedies = remedies.dup
108
134
  remedy = nil
109
135
  wrap_log = lambda do |msg, color: nil|
@@ -136,7 +162,7 @@ module MuxTf
136
162
  if remedies.delete?(:plan)
137
163
  remedy = :plan
138
164
  log wrap_log["Running terraform plan ..."], depth: 2
139
- plan_status = run_plan(retry_count: retry_count)
165
+ plan_status = @plan_command.run_plan(retry_count: retry_count)
140
166
  results[:plan_status] = plan_status
141
167
  return [false, results] unless [:ok, :changes].include?(plan_status)
142
168
  end
@@ -160,6 +186,7 @@ module MuxTf
160
186
  log wrap_log["-" * 40, color: :red]
161
187
  log wrap_log["!! User Error, Please fix the issue and try again", color: :red]
162
188
  log wrap_log["-" * 40, color: :red]
189
+ results[:user_error] = true
163
190
  return [false, results]
164
191
  end
165
192
  if remedies.delete? :auth
@@ -182,27 +209,6 @@ module MuxTf
182
209
  [true, results]
183
210
  end
184
211
 
185
- def validate
186
- log "Validating module ...", depth: 1
187
- tf_validate.parsed_output
188
- end
189
-
190
- def create_plan(filename, targets: [])
191
- log "Preparing Plan ...", depth: 1
192
- exit_code, meta = PlanFormatter.pretty_plan(filename, targets: targets)
193
- case exit_code
194
- when 0
195
- [:ok, meta]
196
- when 1
197
- [:error, meta]
198
- when 2
199
- [:changes, meta]
200
- else
201
- log pastel.yellow("terraform plan exited with an unknown exit code: #{exit_code}")
202
- [:unknown, meta]
203
- end
204
- end
205
-
206
212
  def launch_cmd_loop(status)
207
213
  return if ENV["NO_CMD"]
208
214
 
@@ -240,7 +246,7 @@ module MuxTf
240
246
  def build_root_cmd
241
247
  root_cmd = define_cmd(nil)
242
248
 
243
- root_cmd.add_command(plan_cmd)
249
+ root_cmd.add_command(@plan_command.plan_cmd)
244
250
  root_cmd.add_command(apply_cmd)
245
251
  root_cmd.add_command(shell_cmd)
246
252
  root_cmd.add_command(force_unlock_cmd)
@@ -248,6 +254,7 @@ module MuxTf
248
254
  root_cmd.add_command(reconfigure_cmd)
249
255
  root_cmd.add_command(interactive_cmd)
250
256
  root_cmd.add_command(plan_details_cmd)
257
+ root_cmd.add_command(init_cmd)
251
258
 
252
259
  root_cmd.add_command(exit_cmd)
253
260
  root_cmd.add_command(define_cmd("help", summary: "Show help for commands") { |_opts, _args, cmd| puts cmd.supercommand.help })
@@ -269,20 +276,26 @@ module MuxTf
269
276
  def plan_details_cmd
270
277
  define_cmd("details", summary: "Show Plan Details") do |_opts, _args, _cmd|
271
278
  puts plan_summary_text
272
- end
273
- end
274
279
 
275
- def plan_cmd
276
- define_cmd("plan", summary: "Re-run plan") do |_opts, _args, _cmd|
277
- run_validate && run_plan
280
+ unless ENV["JSON_PLAN"]
281
+ log "Printing Plan Summary ...", depth: 1
282
+
283
+ plan_filename = PlanFilenameGenerator.for_path
284
+ plan = PlanSummaryHandler.from_file(plan_filename)
285
+ plan.simple_summary do |line|
286
+ log line
287
+ end
288
+ end
289
+ # puts plan_summary_text if ENV["JSON_PLAN"]
278
290
  end
279
291
  end
280
292
 
281
293
  def apply_cmd
282
294
  define_cmd("apply", summary: "Apply the current plan") do |_opts, _args, _cmd|
295
+ plan_filename = PlanFilenameGenerator.for_path
283
296
  status = tf_apply(filename: plan_filename)
284
297
  if status.success?
285
- plan_status = run_plan
298
+ plan_status = @plan_command.run_plan
286
299
  throw :stop, :done if plan_status == :ok
287
300
  else
288
301
  log "Apply Failed!"
@@ -343,6 +356,17 @@ module MuxTf
343
356
  end
344
357
  end
345
358
 
359
+ def init_cmd
360
+ define_cmd("init", summary: "Re-run init") do |_opts, _args, _cmd|
361
+ exit_code, meta = PlanFormatter.run_tf_init
362
+ print_errors_and_warnings(meta)
363
+ if exit_code != 0
364
+ log meta.inspect unless meta.empty?
365
+ log "Init Failed!"
366
+ end
367
+ end
368
+ end
369
+
346
370
  def upgrade_cmd
347
371
  define_cmd("upgrade", summary: "Upgrade modules/plguins") do |_opts, _args, _cmd|
348
372
  status, meta = run_upgrade
@@ -366,13 +390,18 @@ module MuxTf
366
390
 
367
391
  def interactive_cmd
368
392
  define_cmd("interactive", summary: "Apply interactively") do |_opts, _args, _cmd|
393
+ plan_filename = PlanFilenameGenerator.for_path
369
394
  plan = PlanSummaryHandler.from_file(plan_filename)
370
395
  begin
371
- abort_message = catch(:abort) { plan.run_interactive }
396
+ abort_message = catch(:abort) {
397
+ result = plan.run_interactive
398
+ log "Re-running apply with the selected resources ..."
399
+ @plan_command.run_plan(targets: result)
400
+ }
372
401
  if abort_message
373
402
  log pastel.red("Aborted: #{abort_message}")
374
403
  else
375
- run_plan
404
+ @plan_command.run_plan
376
405
  end
377
406
  rescue Exception => e # rubocop:disable Lint/RescueException
378
407
  log e.full_message
@@ -381,102 +410,6 @@ module MuxTf
381
410
  end
382
411
  end
383
412
 
384
- def print_errors_and_warnings(meta) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
385
- message = []
386
- message << pastel.yellow("#{meta[:warnings].length} Warnings") if meta[:warnings]
387
- message << pastel.red("#{meta[:errors].length} Errors") if meta[:errors]
388
- if message.length.positive?
389
- log ""
390
- log "Encountered: #{message.join(' and ')}"
391
- log ""
392
- end
393
-
394
- meta[:warnings]&.each do |warning|
395
- log "-" * 20
396
- log pastel.yellow("Warning: #{warning[:message]}")
397
- warning[:body]&.each do |line|
398
- log pastel.yellow(line), depth: 1
399
- end
400
- log ""
401
- end
402
-
403
- meta[:errors]&.each do |error|
404
- log "-" * 20
405
- log pastel.red("Error: #{error[:message]}")
406
- error[:body]&.each do |line|
407
- log pastel.red(line), depth: 1
408
- end
409
- log ""
410
- end
411
-
412
- return unless message.length.positive?
413
-
414
- log ""
415
- end
416
-
417
- def detect_remedies_from_plan(meta)
418
- remedies = Set.new
419
- meta[:errors]&.each do |error|
420
- remedies << :plan if error[:message].include?("timeout while waiting for plugin to start")
421
- end
422
- remedies << :unlock if lock_error?(meta)
423
- remedies << :auth if meta[:need_auth]
424
- remedies
425
- end
426
-
427
- def lock_error?(meta)
428
- meta && meta["error"] == "lock"
429
- end
430
-
431
- def extract_lock_info(meta)
432
- {
433
- lock_id: meta["ID"],
434
- operation: meta["Operation"],
435
- who: meta["Who"],
436
- created: meta["Created"]
437
- }
438
- end
439
-
440
- def run_plan(targets: [], level: 1, retry_count: 0)
441
- plan_status, = remedy_retry_helper(from: :plan, level: level, attempt: retry_count) {
442
- @last_lock_info = nil
443
-
444
- plan_status, meta = create_plan(plan_filename, targets: targets)
445
-
446
- print_errors_and_warnings(meta)
447
-
448
- remedies = detect_remedies_from_plan(meta)
449
-
450
- if remedies.include?(:unlock)
451
- @last_lock_info = extract_lock_info(meta)
452
- throw :abort, [plan_status, meta]
453
- end
454
-
455
- throw :abort, [plan_status, meta] if remedies.include?(:auth)
456
-
457
- [remedies, plan_status, meta]
458
- }
459
-
460
- case plan_status
461
- when :ok
462
- log "no changes", depth: 1
463
- when :error
464
- log "something went wrong", depth: 1
465
- when :changes
466
- unless ENV["JSON_PLAN"]
467
- log "Printing Plan Summary ...", depth: 1
468
- pretty_plan_summary(plan_filename)
469
- end
470
- puts plan_summary_text if ENV["JSON_PLAN"]
471
- when :unknown
472
- # nothing
473
- end
474
-
475
- plan_status
476
- end
477
-
478
- public :run_plan
479
-
480
413
  def run_upgrade
481
414
  exit_code, meta = PlanFormatter.run_tf_init(upgrade: true)
482
415
  print_errors_and_warnings(meta)
@@ -490,18 +423,6 @@ module MuxTf
490
423
  [:unknown, meta]
491
424
  end
492
425
  end
493
-
494
- def pretty_plan_summary(filename)
495
- plan = PlanSummaryHandler.from_file(filename)
496
- plan.flat_summary.each do |line|
497
- log line, depth: 2
498
- end
499
- plan.output_summary.each do |line|
500
- log line, depth: 2
501
- end
502
- log "", depth: 2
503
- log plan.summary, depth: 2
504
- end
505
426
  end
506
427
  end
507
428
  end