mux_tf 0.11.0 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|