mux_tf 0.2.4 → 0.4.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/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
|