mux_tf 0.11.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 +4 -4
- data/lib/mux_tf/cli/current.rb +81 -21
- data/lib/mux_tf/cli.rb +2 -0
- data/lib/mux_tf/plan_formatter.rb +72 -23
- data/lib/mux_tf/plan_summary_handler.rb +1 -31
- data/lib/mux_tf/terraform_helpers.rb +1 -0
- data/lib/mux_tf/tmux.rb +2 -0
- data/lib/mux_tf/version.rb +1 -1
- data/lib/mux_tf.rb +0 -1
- metadata +2 -3
- data/lib/mux_tf/once_helper.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab68c9969748e0246a48cb2bf53876b0b2f47993b021017e1e9bb31782a7e4b4
|
4
|
+
data.tar.gz: 63fb2194ba923f3c30c9480f50946c550e85ea9e7e1988fdcc1764cbe7666f6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8d1442c6cff1c7c0828347176b1cc0a49fa28fb5a27e0212e730e3bf176fc4a072ba247e9cbf6d248c488fb1c62ffc6a71181dbde47e0b35a759b798ded6886
|
7
|
+
data.tar.gz: 3d85fc7be2b58bd815060145c33bccf7dc5814a5ee3a052f5adc6a4c7512792e290f0b1bb26faa07d4f950aab81036bcf9c09b91c2a3a20878fcd70601bd2044
|
data/lib/mux_tf/cli/current.rb
CHANGED
@@ -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
|
35
|
+
plan_status = run_plan
|
36
36
|
|
37
37
|
case plan_status
|
38
38
|
when :ok
|
39
|
-
log "
|
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
|
-
|
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
|
-
|
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
|
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
@@ -9,13 +9,11 @@ module MuxTf
|
|
9
9
|
def pretty_plan(filename, targets: []) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
10
10
|
pastel = Pastel.new
|
11
11
|
|
12
|
-
once = OnceHelper.new
|
13
|
-
|
14
12
|
meta = {}
|
15
13
|
|
16
14
|
parser = StatefulParser.new(normalizer: pastel.method(:strip))
|
17
15
|
parser.state(:info, /^Acquiring state lock/)
|
18
|
-
parser.state(:error, /(
|
16
|
+
parser.state(:error, /(Error locking state|Error:)/, [:none, :blank, :info, :reading])
|
19
17
|
parser.state(:reading, /: (Reading...|Read complete after)/, [:none, :info, :reading])
|
20
18
|
parser.state(:none, /^$/, [:reading])
|
21
19
|
parser.state(:refreshing, /^.+: Refreshing state... \[id=/, [:none, :info, :reading])
|
@@ -23,19 +21,30 @@ module MuxTf
|
|
23
21
|
[:none, :blank, :info, :reading])
|
24
22
|
parser.state(:refresh_done, /^----------+$/, [:refreshing])
|
25
23
|
parser.state(:refresh_done, /^$/, [:refreshing])
|
24
|
+
|
25
|
+
parser.state(:output_info, /^Changes to Outputs:$/, [:refresh_done])
|
26
|
+
parser.state(:refresh_done, /^$/, [:output_info])
|
27
|
+
|
26
28
|
parser.state(:plan_info, /Terraform will perform the following actions:/, [:refresh_done, :none])
|
27
29
|
parser.state(:plan_summary, /^Plan:/, [:plan_info])
|
28
30
|
|
29
31
|
parser.state(:plan_legend, /^Terraform used the selected providers to generate the following execution$/)
|
30
32
|
parser.state(:none, /^$/, [:plan_legend])
|
31
33
|
|
32
|
-
parser.state(:error_lock_info, /Lock Info/, [:
|
33
|
-
|
34
|
+
parser.state(:error_lock_info, /Lock Info/, [:plan_error_error])
|
35
|
+
|
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])
|
34
41
|
|
35
|
-
|
42
|
+
last_state = nil
|
36
43
|
|
37
44
|
status = tf_plan(out: filename, detailed_exitcode: true, compact_warnings: true, targets: targets) { |raw_line|
|
38
45
|
parser.parse(raw_line.rstrip) do |state, line|
|
46
|
+
first_in_state = last_state != state
|
47
|
+
|
39
48
|
case state
|
40
49
|
when :none
|
41
50
|
if line.blank?
|
@@ -62,39 +71,77 @@ module MuxTf
|
|
62
71
|
else
|
63
72
|
p [state, line]
|
64
73
|
end
|
65
|
-
when :
|
66
|
-
meta[
|
67
|
-
|
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
|
68
91
|
when :plan_error
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
72
112
|
when :error_lock_info
|
113
|
+
meta["error"] = "lock"
|
73
114
|
meta[$LAST_MATCH_INFO[1]] = $LAST_MATCH_INFO[2] if line =~ /([A-Z]+\S+)+:\s+(.+)$/
|
74
|
-
|
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
|
75
122
|
when :refreshing
|
76
|
-
|
123
|
+
if first_in_state
|
77
124
|
log "Refreshing state ", depth: 2, newline: false
|
78
|
-
|
125
|
+
else
|
79
126
|
print "."
|
80
|
-
|
127
|
+
end
|
81
128
|
when :plan_legend
|
82
|
-
|
129
|
+
puts if first_in_state
|
83
130
|
log line, depth: 2
|
84
131
|
when :refresh_done
|
85
|
-
|
86
|
-
puts
|
87
|
-
}.otherwise {
|
88
|
-
# nothing
|
89
|
-
}
|
132
|
+
puts if first_in_state
|
90
133
|
when :plan_info # rubocop:disable Lint/DuplicateBranch
|
91
|
-
|
134
|
+
puts if first_in_state
|
135
|
+
log line, depth: 2
|
136
|
+
when :output_info # rubocop:disable Lint/DuplicateBranch
|
137
|
+
puts if first_in_state
|
92
138
|
log line, depth: 2
|
93
139
|
when :plan_summary
|
94
140
|
log line, depth: 2
|
95
141
|
else
|
96
142
|
p [state, pastel.strip(line)]
|
97
143
|
end
|
144
|
+
last_state = state
|
98
145
|
end
|
99
146
|
}
|
100
147
|
[status.status, meta]
|
@@ -264,6 +311,8 @@ module MuxTf
|
|
264
311
|
remedies << :init
|
265
312
|
elsif /there is no package for .+ cached in/.match?(dinfo["summary"]) # rubocop:disable Lint/DuplicateBranch
|
266
313
|
remedies << :init
|
314
|
+
elsif dinfo["detail"]&.include?("timeout while waiting for plugin to start") # rubocop:disable Lint/DuplicateBranch
|
315
|
+
remedies << :init
|
267
316
|
else
|
268
317
|
log dinfo["detail"], depth: 4 if dinfo["detail"]
|
269
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
|
|
data/lib/mux_tf/tmux.rb
CHANGED
data/lib/mux_tf/version.rb
CHANGED
data/lib/mux_tf.rb
CHANGED
@@ -23,7 +23,6 @@ require "dotenv"
|
|
23
23
|
|
24
24
|
require_relative "./mux_tf/version"
|
25
25
|
require_relative "./mux_tf/plan_filename_generator"
|
26
|
-
require_relative "./mux_tf/once_helper"
|
27
26
|
require_relative "./mux_tf/resource_tokenizer"
|
28
27
|
require_relative "./mux_tf/cli"
|
29
28
|
require_relative "./mux_tf/tmux"
|
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.
|
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-
|
11
|
+
date: 2023-07-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -141,7 +141,6 @@ files:
|
|
141
141
|
- lib/mux_tf/cli/current.rb
|
142
142
|
- lib/mux_tf/cli/mux.rb
|
143
143
|
- lib/mux_tf/cli/plan_summary.rb
|
144
|
-
- lib/mux_tf/once_helper.rb
|
145
144
|
- lib/mux_tf/plan_filename_generator.rb
|
146
145
|
- lib/mux_tf/plan_formatter.rb
|
147
146
|
- lib/mux_tf/plan_summary_handler.rb
|
data/lib/mux_tf/once_helper.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module MuxTf
|
4
|
-
class OnceHelper
|
5
|
-
# once = OnceHelper.new
|
6
|
-
# once.for(:phase).once { ... }.otherwise { ... }
|
7
|
-
|
8
|
-
class StateEvaluator
|
9
|
-
def initialize(once_helper, new_state)
|
10
|
-
if once_helper.state == new_state
|
11
|
-
@path = :otherwise
|
12
|
-
else
|
13
|
-
once_helper.state = new_state
|
14
|
-
@path = :once
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def once
|
19
|
-
yield if @path == :then
|
20
|
-
self
|
21
|
-
end
|
22
|
-
|
23
|
-
def otherwise
|
24
|
-
yield if @path == :otherwise
|
25
|
-
self
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def initialize
|
30
|
-
@state = nil
|
31
|
-
end
|
32
|
-
|
33
|
-
attr_accessor :state
|
34
|
-
|
35
|
-
def for(new_state)
|
36
|
-
StateEvaluator.new(self, new_state)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|