mux_tf 0.2.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/exe/tf_current +10 -3
- data/exe/tf_mux +10 -3
- data/exe/tf_plan_summary +10 -3
- data/lib/deps.rb +5 -0
- data/lib/mux_tf.rb +28 -25
- data/lib/mux_tf/cli/current.rb +92 -65
- data/lib/mux_tf/cli/mux.rb +47 -24
- data/lib/mux_tf/cli/plan_summary.rb +20 -246
- data/lib/mux_tf/plan_formatter.rb +100 -52
- data/lib/mux_tf/plan_summary_handler.rb +258 -0
- data/lib/mux_tf/terraform_helpers.rb +30 -29
- data/lib/mux_tf/tmux.rb +13 -13
- data/lib/mux_tf/version.rb +1 -1
- data/lib/mux_tf/version_check.rb +6 -6
- data/lib/mux_tf/yaml_cache.rb +1 -1
- data/mux_tf.gemspec +24 -25
- metadata +4 -7
- data/.gitignore +0 -10
- data/Gemfile +0 -8
- data/LICENSE.txt +0 -21
- data/README.md +0 -52
- data/Rakefile +0 -4
data/lib/mux_tf/cli/mux.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "bundler"
|
4
|
+
|
3
5
|
module MuxTf
|
4
6
|
module Cli
|
5
7
|
module Mux
|
@@ -7,44 +9,63 @@ module MuxTf
|
|
7
9
|
extend PiotrbCliUtils::ShellHelpers
|
8
10
|
|
9
11
|
class << self
|
12
|
+
def with_clean_env
|
13
|
+
backup = {}
|
14
|
+
Bundler.with_original_env do
|
15
|
+
ENV.keys.grep(/^(RBENV_|RUBYLIB)/).each do |key|
|
16
|
+
backup[key] = ENV[key]
|
17
|
+
ENV.delete(key)
|
18
|
+
end
|
19
|
+
yield
|
20
|
+
end
|
21
|
+
ensure
|
22
|
+
backup.each do |k, v|
|
23
|
+
ENV[k] = v
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
10
27
|
def run(_args)
|
11
|
-
Dotenv.load(
|
28
|
+
Dotenv.load(".env.mux")
|
12
29
|
|
13
|
-
log
|
30
|
+
log "Enumerating folders ..."
|
14
31
|
dirs = enumerate_terraform_dirs
|
15
32
|
|
16
|
-
fail_with
|
33
|
+
fail_with "Error: - no subfolders detected! Aborting." if dirs.empty?
|
17
34
|
|
18
|
-
tasks = dirs.map
|
35
|
+
tasks = dirs.map { |dir|
|
19
36
|
{
|
20
37
|
name: dir,
|
21
38
|
cwd: dir,
|
22
|
-
cmd: File.expand_path(File.join(__dir__,
|
39
|
+
cmd: File.expand_path(File.join(__dir__, "..", "..", "..", "exe", "tf_current"))
|
23
40
|
}
|
24
|
-
|
41
|
+
}
|
25
42
|
|
26
43
|
project = File.basename(Dir.getwd)
|
27
44
|
|
28
|
-
if ENV[
|
29
|
-
log
|
30
|
-
words = Shellwords.shellsplit(ENV[
|
31
|
-
result = capture_shell([*words,
|
45
|
+
if ENV["MUX_TF_AUTH_WRAPPER"]
|
46
|
+
log "Warming up AWS connection ..."
|
47
|
+
words = Shellwords.shellsplit(ENV["MUX_TF_AUTH_WRAPPER"])
|
48
|
+
result = capture_shell([*words, "aws", "sts", "get-caller-identity"], raise_on_error: true)
|
32
49
|
p JSON.parse(result)
|
33
50
|
end
|
34
51
|
|
35
52
|
if Tmux.session_running?(project)
|
36
|
-
log
|
53
|
+
log "Killing existing session ..."
|
37
54
|
Tmux.kill_session(project)
|
38
55
|
end
|
39
56
|
|
40
|
-
log
|
41
|
-
|
42
|
-
|
57
|
+
log "Starting new session ..."
|
58
|
+
with_clean_env do
|
59
|
+
Tmux.new_session project
|
60
|
+
end
|
61
|
+
Tmux.select_pane "initial"
|
43
62
|
|
44
|
-
Tmux.
|
45
|
-
Tmux.set_hook 'window-pane-changed', 'select-layout tiled'
|
63
|
+
# Tmux.set "remain-on-exit", "on"
|
46
64
|
|
47
|
-
Tmux.
|
65
|
+
Tmux.set_hook "pane-exited", "select-layout tiled"
|
66
|
+
Tmux.set_hook "window-pane-changed", "select-layout tiled"
|
67
|
+
|
68
|
+
Tmux.set "mouse", "on"
|
48
69
|
|
49
70
|
window_id = Tmux.list_windows.first[:id]
|
50
71
|
|
@@ -60,17 +81,19 @@ module MuxTf
|
|
60
81
|
end
|
61
82
|
end
|
62
83
|
|
63
|
-
log
|
84
|
+
log "Almost done ..."
|
64
85
|
|
65
|
-
initial_pane = Tmux.find_pane(
|
86
|
+
initial_pane = Tmux.find_pane("initial")
|
66
87
|
Tmux.kill_pane initial_pane[:id]
|
67
88
|
Tmux.tile!
|
68
89
|
|
69
90
|
puts "\e]0;tmux: #{project}\007"
|
70
91
|
|
71
|
-
|
72
|
-
|
73
|
-
log
|
92
|
+
sleep 1
|
93
|
+
|
94
|
+
log "Attaching ..."
|
95
|
+
Tmux.attach(project, cc: !!ENV["MUXP_CC_MODE"])
|
96
|
+
log "Done!"
|
74
97
|
end
|
75
98
|
|
76
99
|
private
|
@@ -78,9 +101,9 @@ module MuxTf
|
|
78
101
|
def enumerate_terraform_dirs
|
79
102
|
ignored = []
|
80
103
|
|
81
|
-
ignored += ENV[
|
104
|
+
ignored += ENV["MUX_IGNORE"].split(",") if ENV["MUX_IGNORE"]
|
82
105
|
|
83
|
-
dirs = Dir[
|
106
|
+
dirs = Dir["**/*/.terraform"].map { |n| n.gsub(%r{/\.terraform}, "") }
|
84
107
|
dirs.reject! { |d| d.in?(ignored) }
|
85
108
|
|
86
109
|
dirs
|
@@ -14,271 +14,45 @@ module MuxTf
|
|
14
14
|
hierarchy: false
|
15
15
|
}
|
16
16
|
|
17
|
-
args = OptionParser.new
|
18
|
-
opts.on(
|
17
|
+
args = OptionParser.new { |opts|
|
18
|
+
opts.on("-i") do |v|
|
19
19
|
options[:interactive] = v
|
20
20
|
end
|
21
|
-
opts.on(
|
21
|
+
opts.on("-h") do |v|
|
22
22
|
options[:hierarchy] = v
|
23
23
|
end
|
24
|
-
|
24
|
+
}.parse!(args)
|
25
25
|
|
26
26
|
if options[:interactive]
|
27
|
-
raise
|
27
|
+
raise "must specify plan file in interactive mode" if args[0].blank?
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
else
|
33
|
-
JSON.parse(STDIN.read)
|
34
|
-
end
|
35
|
-
|
36
|
-
parts = []
|
37
|
-
|
38
|
-
data['resource_changes'].each do |v|
|
39
|
-
next unless v['change']
|
40
|
-
|
41
|
-
case v['change']['actions']
|
42
|
-
when ['no-op']
|
43
|
-
# do nothing
|
44
|
-
when ['create']
|
45
|
-
parts << {
|
46
|
-
action: 'create',
|
47
|
-
address: v['address'],
|
48
|
-
deps: find_deps(data, v['address'])
|
49
|
-
}
|
50
|
-
when ['update']
|
51
|
-
parts << {
|
52
|
-
action: 'update',
|
53
|
-
address: v['address'],
|
54
|
-
deps: find_deps(data, v['address'])
|
55
|
-
}
|
56
|
-
when ['delete']
|
57
|
-
parts << {
|
58
|
-
action: 'delete',
|
59
|
-
address: v['address'],
|
60
|
-
deps: find_deps(data, v['address'])
|
61
|
-
}
|
62
|
-
when %w[delete create]
|
63
|
-
parts << {
|
64
|
-
action: 'replace',
|
65
|
-
address: v['address'],
|
66
|
-
deps: find_deps(data, v['address'])
|
67
|
-
}
|
68
|
-
when ['read']
|
69
|
-
parts << {
|
70
|
-
action: 'read',
|
71
|
-
address: v['address'],
|
72
|
-
deps: find_deps(data, v['address'])
|
73
|
-
}
|
74
|
-
else
|
75
|
-
puts "[??] #{v['address']}"
|
76
|
-
puts "UNKNOWN ACTIONS: #{v['change']['actions'].inspect}"
|
77
|
-
puts 'TODO: update plan_summary to support this!'
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
prune_unchanged_deps(parts)
|
82
|
-
|
83
|
-
if options[:interactive]
|
84
|
-
run_interactive(parts, args[0])
|
30
|
+
plan = if args[0]
|
31
|
+
PlanSummaryHandler.from_file(args[0])
|
85
32
|
else
|
86
|
-
|
87
|
-
print_nested(parts)
|
88
|
-
else
|
89
|
-
print_flat(parts)
|
90
|
-
end
|
91
|
-
print_summary(parts)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def load_summary_from_file(file)
|
96
|
-
if File.exist?("#{file}.json") && File.mtime("#{file}.json").to_f >= File.mtime(file).to_f
|
97
|
-
JSON.parse(File.read("#{file}.json"))
|
98
|
-
else
|
99
|
-
puts 'Analyzing changes ...'
|
100
|
-
result = tf_show(file, json: true)
|
101
|
-
data = result.parsed_output
|
102
|
-
File.open("#{file}.json", 'w') { |fh| fh.write(JSON.dump(data)) }
|
103
|
-
data
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def print_summary(parts)
|
108
|
-
summary = {}
|
109
|
-
parts.each do |part|
|
110
|
-
summary[part[:action]] ||= 0
|
111
|
-
summary[part[:action]] += 1
|
112
|
-
end
|
113
|
-
pieces = summary.map do |k, v|
|
114
|
-
color = color_for_action(k)
|
115
|
-
"#{Paint[v, :yellow]} to #{Paint[k, color]}"
|
33
|
+
PlanSummaryHandler.from_data(JSON.parse(STDIN.read))
|
116
34
|
end
|
117
35
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
def print_flat(parts)
|
123
|
-
parts.each do |part|
|
124
|
-
puts "[#{format_action(part[:action])}] #{format_address(part[:address])}"
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def run_interactive(parts, _plan_name)
|
129
|
-
prompt = TTY::Prompt.new
|
130
|
-
result = prompt.multi_select('Update resources:', per_page: 99, echo: false) do |menu|
|
131
|
-
parts.each do |part|
|
132
|
-
label = "[#{format_action(part[:action])}] #{format_address(part[:address])}"
|
133
|
-
menu.choice label, part[:address]
|
36
|
+
if options[:interactive]
|
37
|
+
abort_message = catch :abort do
|
38
|
+
plan.run_interactive
|
134
39
|
end
|
135
|
-
|
136
|
-
|
137
|
-
if !result.empty?
|
138
|
-
log 'Re-running apply with the selected resources ...'
|
139
|
-
status = tf_apply(targets: result)
|
140
|
-
unless status.success?
|
141
|
-
log Paint["Failed! (#{status.status})", :red]
|
142
|
-
exit status.status
|
40
|
+
if abort_message
|
41
|
+
log Paint["Aborted: #{abort_message}", :red]
|
143
42
|
end
|
144
43
|
else
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
def print_nested(parts)
|
150
|
-
parts = parts.deep_dup
|
151
|
-
until parts.empty?
|
152
|
-
part = parts.shift
|
153
|
-
if part[:deps] == []
|
154
|
-
indent = if part[:met_deps] && !part[:met_deps].empty?
|
155
|
-
' '
|
156
|
-
else
|
157
|
-
''
|
158
|
-
end
|
159
|
-
message = "[#{format_action(part[:action])}]#{indent} #{format_address(part[:address])}"
|
160
|
-
if part[:met_deps]
|
161
|
-
message += " - (needs: #{part[:met_deps].join(', ')})"
|
162
|
-
end
|
163
|
-
puts message
|
164
|
-
parts.each do |ipart|
|
165
|
-
d = ipart[:deps].delete(part[:address])
|
166
|
-
if d
|
167
|
-
ipart[:met_deps] ||= []
|
168
|
-
ipart[:met_deps] << d
|
169
|
-
end
|
44
|
+
if options[:hierarchy]
|
45
|
+
plan.nested_summary.each do |line|
|
46
|
+
puts line
|
170
47
|
end
|
171
48
|
else
|
172
|
-
|
173
|
-
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
def prune_unchanged_deps(parts)
|
178
|
-
valid_addresses = parts.map { |part| part[:address] }
|
179
|
-
|
180
|
-
parts.each do |part|
|
181
|
-
part[:deps].select! { |dep| valid_addresses.include?(dep) }
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
def find_config(module_root, module_name, address, parent_address)
|
186
|
-
module_info = if parent_address.empty?
|
187
|
-
module_root[module_name]
|
188
|
-
elsif module_root && module_root[module_name]
|
189
|
-
module_root[module_name]['module']
|
190
|
-
else
|
191
|
-
{}
|
192
|
-
end
|
193
|
-
|
194
|
-
if m = address.match(/^module\.([^.]+)\./)
|
195
|
-
find_config(module_info['module_calls'], m[1], m.post_match, parent_address + ["module.#{m[1]}"])
|
196
|
-
else
|
197
|
-
if module_info['resources']
|
198
|
-
resource = module_info['resources'].find do |resource|
|
199
|
-
address == resource['address']
|
49
|
+
plan.flat_summary.each do |line|
|
50
|
+
puts line
|
200
51
|
end
|
201
52
|
end
|
202
|
-
|
203
|
-
|
204
|
-
end
|
205
|
-
|
206
|
-
def find_deps(data, address)
|
207
|
-
result = []
|
208
|
-
|
209
|
-
full_address = address
|
210
|
-
m = address.match(/\[(.+)\]$/)
|
211
|
-
if m
|
212
|
-
address = m.pre_match
|
213
|
-
index = m[1][0] == '"' ? m[1].gsub(/^"(.+)"$/, '\1') : m[1].to_i
|
214
|
-
end
|
215
|
-
|
216
|
-
if data['prior_state']['values']['root_module']['resources']
|
217
|
-
resource = data['prior_state']['values']['root_module']['resources'].find do |resource|
|
218
|
-
address == resource['address'] && index == resource['index']
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
result += resource['depends_on'] if resource && resource['depends_on']
|
223
|
-
|
224
|
-
resource, parent_address = find_config(data['configuration'], 'root_module', address, [])
|
225
|
-
if resource
|
226
|
-
deps = []
|
227
|
-
resource['expressions'].each do |_k, v|
|
228
|
-
deps << v['references'] if v.is_a?(Hash) && v['references']
|
229
|
-
end
|
230
|
-
result += deps.map { |s| (parent_address + [s]).join('.') }
|
231
|
-
end
|
232
|
-
|
233
|
-
result
|
234
|
-
end
|
235
|
-
|
236
|
-
def color_for_action(action)
|
237
|
-
case action
|
238
|
-
when 'create'
|
239
|
-
:green
|
240
|
-
when 'update'
|
241
|
-
:yellow
|
242
|
-
when 'delete'
|
243
|
-
:red
|
244
|
-
when 'replace'
|
245
|
-
:red
|
246
|
-
when 'read'
|
247
|
-
:cyan
|
248
|
-
else
|
249
|
-
:reset
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
def symbol_for_action(action)
|
254
|
-
case action
|
255
|
-
when 'create'
|
256
|
-
'+'
|
257
|
-
when 'update'
|
258
|
-
'~'
|
259
|
-
when 'delete'
|
260
|
-
'-'
|
261
|
-
when 'replace'
|
262
|
-
'±'
|
263
|
-
when 'read'
|
264
|
-
'>'
|
265
|
-
else
|
266
|
-
action
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
|
-
def format_action(action)
|
271
|
-
color = color_for_action(action)
|
272
|
-
symbol = symbol_for_action(action)
|
273
|
-
Paint[symbol, color]
|
274
|
-
end
|
275
|
-
|
276
|
-
def format_address(address)
|
277
|
-
parts = address.split('.')
|
278
|
-
parts.each_with_index do |part, index|
|
279
|
-
parts[index] = Paint[part, :cyan] if index.odd?
|
53
|
+
puts
|
54
|
+
puts plan.summary
|
280
55
|
end
|
281
|
-
parts.join('.')
|
282
56
|
end
|
283
57
|
end
|
284
58
|
end
|
@@ -6,13 +6,9 @@ module MuxTf
|
|
6
6
|
extend PiotrbCliUtils::Util
|
7
7
|
|
8
8
|
class << self
|
9
|
-
# include CommandHelpers
|
10
|
-
|
11
9
|
def pretty_plan(filename)
|
12
10
|
pastel = Pastel.new
|
13
11
|
|
14
|
-
plan_output = String.new
|
15
|
-
|
16
12
|
phase = :init
|
17
13
|
|
18
14
|
meta = {}
|
@@ -30,8 +26,7 @@ module MuxTf
|
|
30
26
|
|
31
27
|
parser.state(:plan_error, /^Error: /, %i[refreshing refresh_done])
|
32
28
|
|
33
|
-
status = tf_plan(out: filename, detailed_exitcode: true, compact_warnings: true)
|
34
|
-
plan_output << raw_line
|
29
|
+
status = tf_plan(out: filename, detailed_exitcode: true, compact_warnings: true) { |raw_line|
|
35
30
|
parser.parse(raw_line.rstrip) do |state, line|
|
36
31
|
case state
|
37
32
|
when :none
|
@@ -42,19 +37,19 @@ module MuxTf
|
|
42
37
|
end
|
43
38
|
when :info
|
44
39
|
if /Acquiring state lock. This may take a few moments.../.match?(line)
|
45
|
-
log
|
40
|
+
log "Acquiring state lock ...", depth: 2
|
46
41
|
else
|
47
42
|
p [state, line]
|
48
43
|
end
|
49
44
|
when :error
|
50
|
-
meta[
|
45
|
+
meta["error"] = "lock"
|
51
46
|
log Paint[line, :red], depth: 2
|
52
47
|
when :plan_error
|
53
48
|
if phase != :plan_error
|
54
49
|
puts
|
55
50
|
phase = :plan_error
|
56
51
|
end
|
57
|
-
meta[
|
52
|
+
meta["error"] = "refresh"
|
58
53
|
log Paint[line, :red], depth: 2
|
59
54
|
when :error_lock_info
|
60
55
|
if line =~ /^ ([^ ]+):\s+([^ ].+)$/
|
@@ -64,9 +59,9 @@ module MuxTf
|
|
64
59
|
when :refreshing
|
65
60
|
if phase != :refreshing
|
66
61
|
phase = :refreshing
|
67
|
-
log
|
62
|
+
log "Refreshing state ", depth: 2, newline: false
|
68
63
|
else
|
69
|
-
print
|
64
|
+
print "."
|
70
65
|
end
|
71
66
|
when :refresh_done
|
72
67
|
if phase != :refresh_done
|
@@ -83,14 +78,25 @@ module MuxTf
|
|
83
78
|
p [state, line]
|
84
79
|
end
|
85
80
|
end
|
86
|
-
|
81
|
+
}
|
87
82
|
[status.status, meta]
|
88
83
|
end
|
89
84
|
|
90
|
-
def
|
91
|
-
|
85
|
+
def init_status_to_remedies(status, meta)
|
86
|
+
remedies = Set.new
|
87
|
+
if status != 0
|
88
|
+
if meta[:need_reconfigure]
|
89
|
+
remedies << :reconfigure
|
90
|
+
else
|
91
|
+
p [status, meta]
|
92
|
+
remedies << :unknown
|
93
|
+
end
|
94
|
+
end
|
95
|
+
remedies
|
96
|
+
end
|
92
97
|
|
93
|
-
|
98
|
+
def run_tf_init(upgrade: nil, reconfigure: nil)
|
99
|
+
pastel = Pastel.new
|
94
100
|
|
95
101
|
phase = :init
|
96
102
|
|
@@ -98,62 +104,104 @@ module MuxTf
|
|
98
104
|
|
99
105
|
parser = StatefulParser.new(normalizer: pastel.method(:strip))
|
100
106
|
|
101
|
-
parser.state(:
|
102
|
-
parser.state(:
|
107
|
+
parser.state(:modules_init, /^Initializing modules\.\.\./)
|
108
|
+
parser.state(:modules_upgrade, /^Upgrading modules\.\.\./)
|
109
|
+
parser.state(:backend, /^Initializing the backend\.\.\./, [:modules_init, :modules_upgrade])
|
103
110
|
parser.state(:plugins, /^Initializing provider plugins\.\.\./, [:backend])
|
104
111
|
|
105
112
|
parser.state(:plugin_warnings, /^$/, [:plugins])
|
113
|
+
parser.state(:backend_error, /Error:/, [:backend])
|
114
|
+
|
115
|
+
status = tf_init(upgrade: upgrade, reconfigure: reconfigure) { |raw_line|
|
116
|
+
stripped_line = pastel.strip(raw_line.rstrip)
|
106
117
|
|
107
|
-
status = tf_init(upgrade: true, color: false) do |raw_line|
|
108
|
-
plan_output << raw_line
|
109
118
|
parser.parse(raw_line.rstrip) do |state, line|
|
110
119
|
case state
|
111
|
-
when :
|
120
|
+
when :modules_init
|
121
|
+
if phase != state
|
122
|
+
phase = state
|
123
|
+
log "Initializing modules ", depth: 1
|
124
|
+
next
|
125
|
+
end
|
126
|
+
case stripped_line
|
127
|
+
when ""
|
128
|
+
puts
|
129
|
+
else
|
130
|
+
p [state, stripped_line]
|
131
|
+
end
|
132
|
+
when :modules_upgrade
|
112
133
|
if phase != state
|
113
134
|
# first line
|
114
135
|
phase = state
|
115
|
-
log
|
136
|
+
log "Upgrding modules ", depth: 1, newline: false
|
116
137
|
next
|
117
138
|
end
|
118
|
-
case
|
139
|
+
case stripped_line
|
119
140
|
when /^- (?<module>[^ ]+) in (?<path>.+)$/
|
120
141
|
# info = $~.named_captures
|
121
142
|
# log "- #{info["module"]}", depth: 2
|
122
|
-
print
|
143
|
+
print "."
|
123
144
|
when /^Downloading (?<repo>[^ ]+) (?<version>[^ ]+) for (?<module>[^ ]+)\.\.\./
|
124
145
|
# info = $~.named_captures
|
125
146
|
# log "Downloading #{info["module"]} from #{info["repo"]} @ #{info["version"]}"
|
126
|
-
print
|
127
|
-
when
|
147
|
+
print "D"
|
148
|
+
when ""
|
128
149
|
puts
|
129
150
|
else
|
130
|
-
p [state,
|
151
|
+
p [state, stripped_line]
|
131
152
|
end
|
132
153
|
when :backend
|
133
154
|
if phase != state
|
134
155
|
# first line
|
135
156
|
phase = state
|
136
|
-
log
|
157
|
+
log "Initializing the backend ", depth: 1, newline: false
|
137
158
|
next
|
138
159
|
end
|
139
|
-
case
|
140
|
-
when
|
160
|
+
case stripped_line
|
161
|
+
when /^Successfully configured/
|
162
|
+
log line, depth: 2
|
163
|
+
when /unless the backend/
|
164
|
+
log line, depth: 2
|
165
|
+
when ""
|
141
166
|
puts
|
142
167
|
else
|
143
|
-
p [state,
|
168
|
+
p [state, stripped_line]
|
169
|
+
end
|
170
|
+
when :backend_error
|
171
|
+
if raw_line.match "terraform init -reconfigure"
|
172
|
+
meta[:need_reconfigure] = true
|
173
|
+
log Paint["module needs to be reconfigured", :red], depth: 2
|
144
174
|
end
|
145
175
|
when :plugins
|
146
176
|
if phase != state
|
147
177
|
# first line
|
148
178
|
phase = state
|
149
|
-
log
|
179
|
+
log "Initializing provider plugins ...", depth: 1
|
150
180
|
next
|
151
181
|
end
|
152
|
-
case
|
153
|
-
when /^-
|
182
|
+
case stripped_line
|
183
|
+
when /^- (?<module>.+) is built in to Terraform$/
|
154
184
|
info = $LAST_MATCH_INFO.named_captures
|
155
|
-
log "-
|
156
|
-
when
|
185
|
+
log "- [BUILTIN] #{info["module"]}", depth: 2
|
186
|
+
when /^- Finding (?<module>[^ ]+) versions matching "(?<version>.+)"\.\.\./
|
187
|
+
info = $LAST_MATCH_INFO.named_captures
|
188
|
+
log "- [FIND] #{info["module"]} matching #{info["version"].inspect}", depth: 2
|
189
|
+
when /^- Finding latest version of (?<module>.+)\.\.\.$/
|
190
|
+
info = $LAST_MATCH_INFO.named_captures
|
191
|
+
log "- [FIND] #{info["module"]}", depth: 2
|
192
|
+
when /^- Installing (?<module>[^ ]+) v(?<version>.+)\.\.\.$/
|
193
|
+
info = $LAST_MATCH_INFO.named_captures
|
194
|
+
log "- [INSTALLING] #{info["module"]} v#{info["version"]}", depth: 2
|
195
|
+
when /^- Installed (?<module>[^ ]+) v(?<version>.+) \(signed by( a)? (?<signed>.+)\)$/
|
196
|
+
info = $LAST_MATCH_INFO.named_captures
|
197
|
+
log "- [INSTALLED] #{info["module"]} v#{info["version"]} (#{info["signed"]})", depth: 2
|
198
|
+
when /^- Using previously-installed (?<module>[^ ]+) v(?<version>.+)$/
|
199
|
+
info = $LAST_MATCH_INFO.named_captures
|
200
|
+
log "- [USING] #{info["module"]} v#{info["version"]}", depth: 2
|
201
|
+
when /^- Downloading plugin for provider "(?<provider>[^"]+)" \((?<provider_path>[^)]+)\) (?<version>.+)\.\.\.$/
|
202
|
+
info = $LAST_MATCH_INFO.named_captures
|
203
|
+
log "- #{info["provider"]} #{info["version"]}", depth: 2
|
204
|
+
when "- Checking for available provider plugins..."
|
157
205
|
# noop
|
158
206
|
else
|
159
207
|
p [state, line]
|
@@ -170,7 +218,7 @@ module MuxTf
|
|
170
218
|
p [state, line]
|
171
219
|
end
|
172
220
|
end
|
173
|
-
|
221
|
+
}
|
174
222
|
|
175
223
|
[status.status, meta]
|
176
224
|
end
|
@@ -178,20 +226,20 @@ module MuxTf
|
|
178
226
|
def process_validation(info)
|
179
227
|
remedies = Set.new
|
180
228
|
|
181
|
-
if info[
|
182
|
-
log "Encountered #{Paint[info[
|
183
|
-
info[
|
184
|
-
color = dinfo[
|
185
|
-
log "#{Paint[dinfo[
|
186
|
-
if dinfo[
|
229
|
+
if info["error_count"] > 0 || info["warning_count"] > 0
|
230
|
+
log "Encountered #{Paint[info["error_count"], :red]} Errors and #{Paint[info["warning_count"], :yellow]} Warnings!", depth: 2
|
231
|
+
info["diagnostics"].each do |dinfo|
|
232
|
+
color = dinfo["severity"] == "error" ? :red : :yellow
|
233
|
+
log "#{Paint[dinfo["severity"].capitalize, color]}: #{dinfo["summary"]}", depth: 3
|
234
|
+
if dinfo["detail"]&.include?("terraform init")
|
187
235
|
remedies << :init
|
188
236
|
else
|
189
|
-
log dinfo[
|
190
|
-
if dinfo[
|
191
|
-
log format_validation_range(dinfo[
|
237
|
+
log dinfo["detail"], depth: 4 if dinfo["detail"]
|
238
|
+
if dinfo["range"]
|
239
|
+
log format_validation_range(dinfo["range"], color), depth: 4
|
192
240
|
end
|
193
241
|
|
194
|
-
remedies << :unknown if dinfo[
|
242
|
+
remedies << :unknown if dinfo["severity"] == "error"
|
195
243
|
end
|
196
244
|
end
|
197
245
|
end
|
@@ -214,17 +262,17 @@ module MuxTf
|
|
214
262
|
|
215
263
|
context_lines = 3
|
216
264
|
|
217
|
-
lines = range[
|
218
|
-
columns = range[
|
265
|
+
lines = range["start"]["line"]..range["end"]["line"]
|
266
|
+
columns = range["start"]["column"]..range["end"]["column"]
|
219
267
|
|
220
268
|
# on ../../../modules/pods/jane_pod/main.tf line 151, in module "jane":
|
221
269
|
# 151: jane_resources_preset = var.jane_resources_presetx
|
222
270
|
output = []
|
223
271
|
lines_info = lines.size == 1 ? "#{lines.first}:#{columns.first}" : "#{lines.first}:#{columns.first} to #{lines.last}:#{columns.last}"
|
224
|
-
output << "on: #{range[
|
272
|
+
output << "on: #{range["filename"]} line#{lines.size > 1 ? "s" : ""}: #{lines_info}"
|
225
273
|
|
226
|
-
if File.exist?(range[
|
227
|
-
file_lines = File.read(range[
|
274
|
+
if File.exist?(range["filename"])
|
275
|
+
file_lines = File.read(range["filename"]).split("\n")
|
228
276
|
extract_range = ([lines.first - context_lines, 0].max)..([lines.last + context_lines, file_lines.length - 1].min)
|
229
277
|
file_lines.each_with_index do |line, index|
|
230
278
|
if extract_range.cover?(index + 1)
|
@@ -237,7 +285,7 @@ module MuxTf
|
|
237
285
|
start_col = columns.last
|
238
286
|
end
|
239
287
|
painted_line = paint_line(line, color, start_col: start_col, end_col: end_col)
|
240
|
-
output << "#{Paint[
|
288
|
+
output << "#{Paint[">", color]} #{index + 1}: #{painted_line}"
|
241
289
|
else
|
242
290
|
output << " #{index + 1}: #{line}"
|
243
291
|
end
|