mux_tf 0.12.0 → 0.13.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: 924f8b4cc0e90b4c636e3ffcc806c0e48036380ac81ee83b1dc21a437950e76e
4
- data.tar.gz: 3bea784dc0db5091efa2dcba48c653b812d618b88c8dc04b997b59cc8e810e0c
3
+ metadata.gz: ab68c9969748e0246a48cb2bf53876b0b2f47993b021017e1e9bb31782a7e4b4
4
+ data.tar.gz: 63fb2194ba923f3c30c9480f50946c550e85ea9e7e1988fdcc1764cbe7666f6a
5
5
  SHA512:
6
- metadata.gz: 3dbd88ebf68ae0bb27116ce5b553a04668a746cfdd89ed1e646fc6f75961cf8c61d66f2a4ba46d0baaa17c6fdaa3b99704485632a5787a492037e75707e06b1a
7
- data.tar.gz: bd9bf482078f080477f1871b02c256ad06b7bcb4e323adb8ee35fe056b9f8fd34bec4993c6b1611fac161b09b80db11c3d36395fdb42bef0d174e3ec666f8745
6
+ metadata.gz: e8d1442c6cff1c7c0828347176b1cc0a49fa28fb5a27e0212e730e3bf176fc4a072ba247e9cbf6d248c488fb1c62ffc6a71181dbde47e0b35a759b798ded6886
7
+ data.tar.gz: 3d85fc7be2b58bd815060145c33bccf7dc5814a5ee3a052f5adc6a4c7512792e290f0b1bb26faa07d4f950aab81036bcf9c09b91c2a3a20878fcd70601bd2044
@@ -4,7 +4,7 @@ require "bundler"
4
4
 
5
5
  module MuxTf
6
6
  module Cli
7
- module Current
7
+ module Current # rubocop:disable Metrics/ModuleLength
8
8
  extend TerraformHelpers
9
9
  extend PiotrbCliUtils::Util
10
10
  extend PiotrbCliUtils::CriCommandSupport
@@ -32,19 +32,16 @@ module MuxTf
32
32
  return launch_cmd_loop(:error) unless upgrade_status == :ok
33
33
  end
34
34
 
35
- plan_status, @plan_meta = create_plan(plan_filename)
35
+ plan_status = run_plan
36
36
 
37
37
  case plan_status
38
38
  when :ok
39
- log "no changes, exiting", depth: 1
39
+ log "exiting", depth: 1
40
40
  when :error
41
- log "something went wrong", depth: 1
42
41
  launch_cmd_loop(plan_status)
43
- when :changes
44
- log "Printing Plan Summary ...", depth: 1
45
- pretty_plan_summary(plan_filename)
42
+ when :changes # rubocop:disable Lint/DuplicateBranch
46
43
  launch_cmd_loop(plan_status)
47
- when :unknown
44
+ when :unknown # rubocop:disable Lint/DuplicateBranch
48
45
  launch_cmd_loop(plan_status)
49
46
  end
50
47
  end
@@ -68,28 +65,45 @@ module MuxTf
68
65
 
69
66
  def run_validate
70
67
  remedies = PlanFormatter.process_validation(validate)
71
- process_remedies(remedies)
68
+ status, _results = process_remedies(remedies)
69
+ status
72
70
  end
73
71
 
74
- def process_remedies(remedies)
72
+ def process_remedies(remedies, retry_count: 0) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
73
+ results = {}
74
+ if retry_count > 5
75
+ log "giving up because retry_count: #{retry_count}", depth: 1
76
+ log "unprocessed remedies: #{remedies.to_a}", depth: 1
77
+ return [false, results]
78
+ end
75
79
  if remedies.delete? :init
76
- log "Running terraform init ...", depth: 2
80
+ log "[remedy] Running terraform init ...", depth: 2
77
81
  remedies = PlanFormatter.init_status_to_remedies(*PlanFormatter.run_tf_init)
78
- if process_remedies(remedies)
82
+ status, r_results = process_remedies(remedies)
83
+ results.merge!(r_results)
84
+ if status
79
85
  remedies = PlanFormatter.process_validation(validate)
80
- return false unless process_remedies(remedies)
86
+ return [false, results] unless process_remedies(remedies)
81
87
  end
82
88
  end
89
+ if remedies.delete?(:plan)
90
+ log "[remedy] Running terraform plan ...", depth: 2
91
+ plan_status = run_plan(retry_count: retry_count)
92
+ results[:plan_status] = plan_status
93
+ return [false, results] unless [:ok, :changes].include?(plan_status)
94
+ end
83
95
  if remedies.delete? :reconfigure
84
- log "Running terraform init ...", depth: 2
96
+ log "[remedy] Running terraform init ...", depth: 2
85
97
  remedies = PlanFormatter.init_status_to_remedies(*PlanFormatter.run_tf_init(reconfigure: true))
86
- return false unless process_remedies(remedies)
98
+ status, r_results = process_remedies(remedies)
99
+ results.merge!(r_results)
100
+ return [false, results] unless status
87
101
  end
88
102
  unless remedies.empty?
89
103
  log "unprocessed remedies: #{remedies.to_a}", depth: 1
90
- return false
104
+ return [false, results]
91
105
  end
92
- true
106
+ [true, results]
93
107
  end
94
108
 
95
109
  def validate
@@ -268,20 +282,68 @@ module MuxTf
268
282
  end
269
283
  end
270
284
 
271
- def run_plan(targets: [])
285
+ def print_errors_and_warnings # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
286
+ message = []
287
+ message << Paint["#{@plan_meta[:warnings].length} Warnings", :yellow] if @plan_meta[:warnings]
288
+ message << Paint["#{@plan_meta[:errors].length} Errors", :red] if @plan_meta[:errors]
289
+ if message.length.positive?
290
+ log ""
291
+ log "Encountered: #{message.join(' and ')}"
292
+ log ""
293
+ end
294
+
295
+ @plan_meta[:warnings]&.each do |warning|
296
+ log "-" * 20
297
+ log Paint["Warning: #{warning[:message]}", :yellow]
298
+ warning[:body]&.each do |line|
299
+ log Paint[line, :yellow], depth: 1
300
+ end
301
+ log ""
302
+ end
303
+
304
+ @plan_meta[:errors]&.each do |error|
305
+ log "-" * 20
306
+ log Paint["Error: #{error[:message]}", :red]
307
+ error[:body]&.each do |line|
308
+ log Paint[line, :red], depth: 1
309
+ end
310
+ log ""
311
+ end
312
+
313
+ return unless message.length.positive?
314
+
315
+ log ""
316
+ end
317
+
318
+ def detect_remedies_from_plan
319
+ remedies = Set.new
320
+ @plan_meta[:errors]&.each do |error|
321
+ remedies << :plan if error[:message].include?("timeout while waiting for plugin to start")
322
+ end
323
+ remedies
324
+ end
325
+
326
+ def run_plan(targets: [], retry_count: 0)
272
327
  plan_status, @plan_meta = create_plan(plan_filename, targets: targets)
273
328
 
274
329
  case plan_status
275
330
  when :ok
276
331
  log "no changes", depth: 1
277
332
  when :error
278
- log "something went wrong", depth: 1
333
+ # log "something went wrong", depth: 1
334
+ print_errors_and_warnings
335
+ remedies = detect_remedies_from_plan
336
+ status, results = process_remedies(remedies, retry_count: retry_count)
337
+ plan_status = results[:plan_status] if status
279
338
  when :changes
280
339
  log "Printing Plan Summary ...", depth: 1
281
340
  pretty_plan_summary(plan_filename)
282
341
  when :unknown
283
342
  # nothing
284
343
  end
344
+
345
+ print_errors_and_warnings
346
+
285
347
  plan_status
286
348
  end
287
349
 
@@ -292,8 +354,6 @@ module MuxTf
292
354
  [:ok, meta]
293
355
  when 1
294
356
  [:error, meta]
295
- # when 2
296
- # [:changes, meta]
297
357
  else
298
358
  log Paint["terraform init upgrade exited with an unknown exit code: #{exit_code}", :yellow]
299
359
  [:unknown, meta]
data/lib/mux_tf/cli.rb CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  module MuxTf
4
4
  module Cli
5
+ extend PiotrbCliUtils::Util
6
+
5
7
  def self.run(mode, args)
6
8
  case mode
7
9
  when :mux
@@ -13,7 +13,7 @@ module MuxTf
13
13
 
14
14
  parser = StatefulParser.new(normalizer: pastel.method(:strip))
15
15
  parser.state(:info, /^Acquiring state lock/)
16
- parser.state(:error, /(╷|Error locking state|Error:)/, [:none, :blank, :info, :reading])
16
+ parser.state(:error, /(Error locking state|Error:)/, [:none, :blank, :info, :reading])
17
17
  parser.state(:reading, /: (Reading...|Read complete after)/, [:none, :info, :reading])
18
18
  parser.state(:none, /^$/, [:reading])
19
19
  parser.state(:refreshing, /^.+: Refreshing state... \[id=/, [:none, :info, :reading])
@@ -31,10 +31,13 @@ module MuxTf
31
31
  parser.state(:plan_legend, /^Terraform used the selected providers to generate the following execution$/)
32
32
  parser.state(:none, /^$/, [:plan_legend])
33
33
 
34
- parser.state(:error_lock_info, /Lock Info/, [:error])
35
- parser.state(:error, /^$/, [:error_lock_info])
34
+ parser.state(:error_lock_info, /Lock Info/, [:plan_error_error])
36
35
 
37
- parser.state(:plan_error, /^╷|Error: /, [:refreshing, :refresh_done])
36
+ parser.state(:plan_error, /Planning failed. Terraform encountered an error while generating this plan./, [:refreshing, :refresh_done])
37
+ parser.state(:plan_error_block, /^╷/, [:plan_error, :none, :blank, :info, :reading])
38
+ parser.state(:plan_error_warning, /^│ Warning: /, [:plan_error_block])
39
+ parser.state(:plan_error_error, /^│ Error: /, [:plan_error_block])
40
+ parser.state(:plan_error, /^╵/, [:plan_error_warning, :plan_error_error, :plan_error_block, :error_lock_info])
38
41
 
39
42
  last_state = nil
40
43
 
@@ -68,16 +71,54 @@ module MuxTf
68
71
  else
69
72
  p [state, line]
70
73
  end
71
- when :error
72
- meta["error"] = "lock"
73
- log Paint[line, :red], depth: 2
74
+ when :plan_error_block
75
+ meta[:current_error] = {
76
+ type: :unknown,
77
+ body: []
78
+ }
79
+ when :plan_error_warning, :plan_error_error
80
+ clean_line = pastel.strip(line).gsub(/^│ /, "")
81
+ if clean_line =~ /^(Warning|Error): (.+)$/
82
+ meta[:current_error][:type] = $LAST_MATCH_INFO[1].downcase.to_sym
83
+ meta[:current_error][:message] = $LAST_MATCH_INFO[2]
84
+ elsif clean_line == ""
85
+ # skip double empty lines
86
+ meta[:current_error][:body] << clean_line if meta[:current_error][:body].last != ""
87
+ else
88
+ meta[:current_error][:body] ||= []
89
+ meta[:current_error][:body] << clean_line
90
+ end
74
91
  when :plan_error
75
- puts if first_in_state
76
- meta["error"] = "refresh"
77
- log Paint[line, :red], depth: 2
92
+ case pastel.strip(line)
93
+ when "" # closing of an error block
94
+ if meta[:current_error][:type] == :error
95
+ meta[:errors] ||= []
96
+ meta[:errors] << meta[:current_error]
97
+ end
98
+ if meta[:current_error][:type] == :warning
99
+ meta[:warnings] ||= []
100
+ meta[:warnings] << meta[:current_error]
101
+ end
102
+ meta.delete(:current_error)
103
+ when ""
104
+ # skip empty line
105
+ when /Releasing state lock. This may take a few moments"/
106
+ log line, depth: 2
107
+ when /Planning failed./ # rubocop:disable Lint/DuplicateBranch
108
+ log line, depth: 2
109
+ else
110
+ p [state, line]
111
+ end
78
112
  when :error_lock_info
113
+ meta["error"] = "lock"
79
114
  meta[$LAST_MATCH_INFO[1]] = $LAST_MATCH_INFO[2] if line =~ /([A-Z]+\S+)+:\s+(.+)$/
80
- log Paint[line, :red], depth: 2
115
+ clean_line = pastel.strip(line).gsub(/^│ /, "")
116
+ if clean_line == ""
117
+ meta[:current_error][:body] << clean_line if meta[:current_error][:body].last != ""
118
+ else
119
+ meta[:current_error][:body] << clean_line
120
+ end
121
+ # log Paint[line, :red], depth: 2
81
122
  when :refreshing
82
123
  if first_in_state
83
124
  log "Refreshing state ", depth: 2, newline: false
@@ -270,6 +311,8 @@ module MuxTf
270
311
  remedies << :init
271
312
  elsif /there is no package for .+ cached in/.match?(dinfo["summary"]) # rubocop:disable Lint/DuplicateBranch
272
313
  remedies << :init
314
+ elsif dinfo["detail"]&.include?("timeout while waiting for plugin to start") # rubocop:disable Lint/DuplicateBranch
315
+ remedies << :init
273
316
  else
274
317
  log dinfo["detail"], depth: 4 if dinfo["detail"]
275
318
  log format_validation_range(dinfo["range"], color), depth: 4 if dinfo["range"]
@@ -239,7 +239,7 @@ module MuxTf
239
239
  throw :abort, "nothing selected"
240
240
  else
241
241
  log "Re-running apply with the selected resources ..."
242
- run_plan(targets: result)
242
+ MuxTf::Cli::Current.run_plan(targets: result)
243
243
  end
244
244
  end
245
245
 
@@ -263,36 +263,6 @@ module MuxTf
263
263
  end
264
264
  end
265
265
 
266
- def run_plan(targets: [])
267
- plan_filename = MuxTf::Cli::Current.plan_filename
268
- plan_status, @plan_meta = create_plan(plan_filename, targets: targets)
269
-
270
- case plan_status
271
- when :ok
272
- log "no changes", depth: 1
273
- when :error
274
- log "something went wrong", depth: 1
275
- when :changes
276
- log "Printing Plan Summary ...", depth: 1
277
- pretty_plan_summary(plan_filename)
278
- when :unknown
279
- # nothing
280
- end
281
- plan_status
282
- end
283
-
284
- def pretty_plan_summary(filename)
285
- plan = PlanSummaryHandler.from_file(filename)
286
- plan.flat_summary.each do |line|
287
- log line, depth: 2
288
- end
289
- plan.output_summary.each do |line|
290
- log line, depth: 2
291
- end
292
- log "", depth: 2
293
- log plan.summary, depth: 2
294
- end
295
-
296
266
  def prune_unchanged_deps(_parts)
297
267
  valid_addresses = resource_parts.map { |part| part[:address] }
298
268
 
@@ -3,6 +3,7 @@
3
3
  module MuxTf
4
4
  module TerraformHelpers
5
5
  include PiotrbCliUtils::ShellHelpers
6
+ include PiotrbCliUtils::Util
6
7
 
7
8
  ResultStruct = Struct.new("TerraformResponse", :status, :success?, :output, :parsed_output, keyword_init: true)
8
9
 
data/lib/mux_tf/tmux.rb CHANGED
@@ -4,6 +4,8 @@ require "shellwords"
4
4
 
5
5
  module MuxTf
6
6
  module Tmux
7
+ extend PiotrbCliUtils::Util
8
+
7
9
  class << self
8
10
  def session_running?(name)
9
11
  tmux("has-session -t #{name.inspect} 2>/dev/null", raise_on_error: false)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MuxTf
4
- VERSION = "0.12.0"
4
+ VERSION = "0.13.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mux_tf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Banasik
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-07-12 00:00:00.000000000 Z
11
+ date: 2023-07-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport