mux_tf 0.8.4 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,8 +6,6 @@ module MuxTf
6
6
  include TerraformHelpers
7
7
  include PiotrbCliUtils::Util
8
8
 
9
- PLAN_FILENAME = "foo.tfplan"
10
-
11
9
  def self.from_file(file)
12
10
  data = data_from_file(file)
13
11
  new data
@@ -20,7 +18,7 @@ module MuxTf
20
18
  puts "Analyzing changes ..."
21
19
  result = tf_show(file, json: true)
22
20
  data = result.parsed_output
23
- File.open("#{file}.json", "w") { |fh| fh.write(JSON.dump(data)) }
21
+ File.write("#{file}.json", JSON.dump(data))
24
22
  data
25
23
  end
26
24
  end
@@ -29,93 +27,89 @@ module MuxTf
29
27
  new(data)
30
28
  end
31
29
 
32
- def initialize(data)
30
+ def initialize(data) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
33
31
  @parts = []
34
32
 
35
- if data["output_changes"]
36
- data["output_changes"].each do |output_name, v|
37
- case v["actions"]
38
- when ["no-op"]
39
- # do nothing
40
- when ["create"]
41
- parts << {
42
- type: "output",
43
- action: "create",
44
- after_unknown: v["after_unknown"],
45
- sensitive: [v["before_sensitive"], v["after_sensitive"]],
46
- address: output_name
47
- }
48
- when ["update"]
49
- parts << {
50
- type: "output",
51
- action: "update",
52
- after_unknown: v["after_unknown"],
53
- sensitive: [v["before_sensitive"], v["after_sensitive"]],
54
- address: output_name
55
- }
56
- when ["delete"]
57
- parts << {
58
- type: "output",
59
- action: "delete",
60
- after_unknown: v["after_unknown"],
61
- sensitive: [v["before_sensitive"], v["after_sensitive"]],
62
- address: output_name
63
- }
64
- else
65
- puts "[??] #{output_name}"
66
- puts "UNKNOWN ACTIONS: #{v["actions"].inspect}"
67
- puts "TODO: update plan_summary to support this!"
68
- end
33
+ data["output_changes"]&.each do |output_name, v|
34
+ case v["actions"]
35
+ when ["no-op"]
36
+ # do nothing
37
+ when ["create"]
38
+ parts << {
39
+ type: "output",
40
+ action: "create",
41
+ after_unknown: v["after_unknown"],
42
+ sensitive: [v["before_sensitive"], v["after_sensitive"]],
43
+ address: output_name
44
+ }
45
+ when ["update"]
46
+ parts << {
47
+ type: "output",
48
+ action: "update",
49
+ after_unknown: v["after_unknown"],
50
+ sensitive: [v["before_sensitive"], v["after_sensitive"]],
51
+ address: output_name
52
+ }
53
+ when ["delete"]
54
+ parts << {
55
+ type: "output",
56
+ action: "delete",
57
+ after_unknown: v["after_unknown"],
58
+ sensitive: [v["before_sensitive"], v["after_sensitive"]],
59
+ address: output_name
60
+ }
61
+ else
62
+ puts "[??] #{output_name}"
63
+ puts "UNKNOWN ACTIONS: #{v['actions'].inspect}"
64
+ puts "TODO: update plan_summary to support this!"
69
65
  end
70
66
  end
71
67
 
72
- if data["resource_changes"]
73
- data["resource_changes"].each do |v|
74
- next unless v["change"]
75
-
76
- case v["change"]["actions"]
77
- when ["no-op"]
78
- # do nothing
79
- when ["create"]
80
- parts << {
81
- type: "resource",
82
- action: "create",
83
- address: v["address"],
84
- deps: find_deps(data, v["address"])
85
- }
86
- when ["update"]
87
- parts << {
88
- type: "resource",
89
- action: "update",
90
- address: v["address"],
91
- deps: find_deps(data, v["address"])
92
- }
93
- when ["delete"]
94
- parts << {
95
- type: "resource",
96
- action: "delete",
97
- address: v["address"],
98
- deps: find_deps(data, v["address"])
99
- }
100
- when %w[delete create]
101
- parts << {
102
- type: "resource",
103
- action: "replace",
104
- address: v["address"],
105
- deps: find_deps(data, v["address"])
106
- }
107
- when ["read"]
108
- parts << {
109
- type: "resource",
110
- action: "read",
111
- address: v["address"],
112
- deps: find_deps(data, v["address"])
113
- }
114
- else
115
- puts "[??] #{v["address"]}"
116
- puts "UNKNOWN ACTIONS: #{v["change"]["actions"].inspect}"
117
- puts "TODO: update plan_summary to support this!"
118
- end
68
+ data["resource_changes"]&.each do |v|
69
+ next unless v["change"]
70
+
71
+ case v["change"]["actions"]
72
+ when ["no-op"]
73
+ # do nothing
74
+ when ["create"]
75
+ parts << {
76
+ type: "resource",
77
+ action: "create",
78
+ address: v["address"],
79
+ deps: find_deps(data, v["address"])
80
+ }
81
+ when ["update"]
82
+ parts << {
83
+ type: "resource",
84
+ action: "update",
85
+ address: v["address"],
86
+ deps: find_deps(data, v["address"])
87
+ }
88
+ when ["delete"]
89
+ parts << {
90
+ type: "resource",
91
+ action: "delete",
92
+ address: v["address"],
93
+ deps: find_deps(data, v["address"])
94
+ }
95
+ when %w[delete create]
96
+ parts << {
97
+ type: "resource",
98
+ action: "replace",
99
+ address: v["address"],
100
+ deps: find_deps(data, v["address"])
101
+ }
102
+ when ["read"]
103
+ parts << {
104
+ type: "resource",
105
+ action: "read",
106
+ address: v["address"],
107
+ deps: find_deps(data, v["address"])
108
+ }
109
+ else
110
+ puts "[??] #{v['address']}"
111
+ puts "UNKNOWN ACTIONS: #{v['change']['actions'].inspect}"
112
+ puts "TODO: update plan_summary to support this!"
119
113
  end
120
114
  end
121
115
 
@@ -130,7 +124,7 @@ module MuxTf
130
124
  parts.select { |part| part[:type] == "output" }
131
125
  end
132
126
 
133
- def summary
127
+ def summary # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
134
128
  # resources
135
129
  resource_summary = {}
136
130
  resource_parts.each do |part|
@@ -157,7 +151,7 @@ module MuxTf
157
151
  [
158
152
  "Plan Summary:",
159
153
  resource_pieces.any? ? resource_pieces.join(Paint[", ", :gray]) : nil,
160
- output_pieces.any? ? "Outputs: #{output_pieces.join(Paint[", ", :gray])}" : nil
154
+ output_pieces.any? ? "Outputs: #{output_pieces.join(Paint[', ', :gray])}" : nil
161
155
  ].compact.join(" ")
162
156
  else
163
157
  "Plan Summary: no changes"
@@ -172,14 +166,14 @@ module MuxTf
172
166
  result
173
167
  end
174
168
 
175
- def sensitive_summary(bv, av)
169
+ def sensitive_summary(before_value, after_value)
176
170
  # before vs after
177
- if bv && av
178
- "(#{Paint["sensitive", :yellow]})"
179
- elsif bv
180
- "(#{Paint["-sensitive", :red]})"
181
- elsif av
182
- "(#{Paint["+sensitive", :cyan]})"
171
+ if before_value && after_value
172
+ "(#{Paint['sensitive', :yellow]})"
173
+ elsif before_value
174
+ "(#{Paint['-sensitive', :red]})"
175
+ elsif after_value
176
+ "(#{Paint['+sensitive', :cyan]})"
183
177
  end
184
178
  end
185
179
 
@@ -197,19 +191,19 @@ module MuxTf
197
191
  result
198
192
  end
199
193
 
200
- def nested_summary
194
+ def nested_summary # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
201
195
  result = []
202
196
  parts = resource_parts.deep_dup
203
197
  until parts.empty?
204
198
  part = parts.shift
205
199
  if part[:deps] == []
206
200
  indent = if part[:met_deps] && !part[:met_deps].empty?
207
- " "
208
- else
209
- ""
210
- end
201
+ " "
202
+ else
203
+ ""
204
+ end
211
205
  message = "[#{format_action(part[:action])}]#{indent} #{format_address(part[:address])}"
212
- message += " - (needs: #{part[:met_deps].join(", ")})" if part[:met_deps]
206
+ message += " - (needs: #{part[:met_deps].join(', ')})" if part[:met_deps]
213
207
  result << message
214
208
  parts.each do |ipart|
215
209
  d = ipart[:deps].delete(part[:address])
@@ -234,11 +228,11 @@ module MuxTf
234
228
  end
235
229
  }
236
230
 
237
- if !result.empty?
231
+ if result.empty?
232
+ throw :abort, "nothing selected"
233
+ else
238
234
  log "Re-running apply with the selected resources ..."
239
235
  run_plan(targets: result)
240
- else
241
- throw :abort, "nothing selected"
242
236
  end
243
237
  end
244
238
 
@@ -263,7 +257,8 @@ module MuxTf
263
257
  end
264
258
 
265
259
  def run_plan(targets: [])
266
- plan_status, @plan_meta = create_plan(PLAN_FILENAME, targets: targets)
260
+ plan_filename = MuxTf::Cli::Current.plan_filename
261
+ plan_status, @plan_meta = create_plan(plan_filename, targets: targets)
267
262
 
268
263
  case plan_status
269
264
  when :ok
@@ -272,7 +267,7 @@ module MuxTf
272
267
  log "something went wrong", depth: 1
273
268
  when :changes
274
269
  log "Printing Plan Summary ...", depth: 1
275
- pretty_plan_summary(PLAN_FILENAME)
270
+ pretty_plan_summary(plan_filename)
276
271
  when :unknown
277
272
  # nothing
278
273
  end
@@ -291,7 +286,7 @@ module MuxTf
291
286
  log plan.summary, depth: 2
292
287
  end
293
288
 
294
- def prune_unchanged_deps(parts)
289
+ def prune_unchanged_deps(_parts)
295
290
  valid_addresses = resource_parts.map { |part| part[:address] }
296
291
 
297
292
  resource_parts.each do |part|
@@ -299,7 +294,7 @@ module MuxTf
299
294
  end
300
295
  end
301
296
 
302
- def find_deps(data, address)
297
+ def find_deps(data, address) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
303
298
  result = []
304
299
 
305
300
  m = address.match(/\[(.+)\]$/)
@@ -309,8 +304,8 @@ module MuxTf
309
304
  end
310
305
 
311
306
  if data.dig("prior_state", "values", "root_module", "resources")
312
- resource = data["prior_state"]["values"]["root_module"]["resources"].find { |resource|
313
- address == resource["address"] && index == resource["index"]
307
+ resource = data["prior_state"]["values"]["root_module"]["resources"].find { |inner_resource|
308
+ address == inner_resource["address"] && index == inner_resource["index"]
314
309
  }
315
310
  end
316
311
 
@@ -330,19 +325,19 @@ module MuxTf
330
325
 
331
326
  def find_config(module_root, module_name, address, parent_address)
332
327
  module_info = if parent_address.empty?
333
- module_root[module_name]
334
- elsif module_root && module_root[module_name]
335
- module_root[module_name]["module"]
336
- else
337
- {}
338
- end
339
-
340
- if m = address.match(/^module\.([^.]+)\./)
328
+ module_root[module_name]
329
+ elsif module_root && module_root[module_name]
330
+ module_root[module_name]["module"]
331
+ else
332
+ {}
333
+ end
334
+
335
+ if (m = address.match(/^module\.([^.]+)\./))
341
336
  find_config(module_info["module_calls"], m[1], m.post_match, parent_address + ["module.#{m[1]}"])
342
337
  else
343
338
  if module_info["resources"]
344
- resource = module_info["resources"].find { |resource|
345
- address == resource["address"]
339
+ resource = module_info["resources"].find { |inner_resource|
340
+ address == inner_resource["address"]
346
341
  }
347
342
  end
348
343
  [resource, parent_address]
@@ -357,7 +352,7 @@ module MuxTf
357
352
  :yellow
358
353
  when "delete"
359
354
  :red
360
- when "replace"
355
+ when "replace" # rubocop:disable Lint/DuplicateBranch
361
356
  :red
362
357
  when "read"
363
358
  :cyan
@@ -395,7 +390,7 @@ module MuxTf
395
390
  parts.each_with_index do |(part_type, part_value), index|
396
391
  case part_type
397
392
  when :rt
398
- result << "." if index > 0
393
+ result << "." if index.positive?
399
394
  result << Paint[part_value, :cyan]
400
395
  when :rn
401
396
  result << "."
@@ -1,62 +1,66 @@
1
- class ResourceTokenizer
2
- def self.split(resource)
3
- tokenize(resource).map(&:last)
4
- end
1
+ # frozen_string_literal: true
5
2
 
6
- def self.tokenize(resource)
7
- result = []
8
- n = 0
9
- pn = 0
10
- state = :rt
11
- until n >= resource.length
12
- case state
13
- when :rt
14
- # looking for .
15
- if resource[n] == "."
16
- # reached the dot ..
17
- result << [:rt, resource[pn...n]]
18
- pn = n + 1
19
- state = :rn
20
- end
21
- when :rn
22
- # looking for [ or .
23
- if resource[n] == "."
24
- # reached the dot ..
25
- result << [:rn, resource[pn...n]]
26
- pn = n + 1
27
- state = :rt
28
- end
29
- if resource[n] == "["
30
- # reached the open bracket
31
- result << [:rn, resource[pn...n]]
32
- pn = n
33
- state = :ri
34
- end
35
- if n == resource.length - 1
36
- # last character .. close the current group
37
- # the last thing should only ever be an index or a name
38
- result << [:rn, resource[pn..n]]
39
- pn = n
40
- state = :done
41
- end
42
- when :ri
43
- # looking for ]
44
- if resource[n] == "]"
45
- # reached the close bracket
46
- result << [:ri, resource[pn..n]]
47
- pn = n + 1
48
- state = :rt
49
- if resource[n + 1] == "."
50
- pn = n + 2
51
- n += 1
3
+ module MuxTf
4
+ class ResourceTokenizer
5
+ def self.split(resource)
6
+ tokenize(resource).map(&:last)
7
+ end
8
+
9
+ def self.tokenize(resource) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
10
+ result = []
11
+ n = 0
12
+ pn = 0
13
+ state = :rt
14
+ until n >= resource.length
15
+ case state
16
+ when :rt
17
+ # looking for .
18
+ if resource[n] == "."
19
+ # reached the dot ..
20
+ result << [:rt, resource[pn...n]]
21
+ pn = n + 1
22
+ state = :rn
23
+ end
24
+ when :rn
25
+ # looking for [ or .
26
+ if resource[n] == "."
27
+ # reached the dot ..
28
+ result << [:rn, resource[pn...n]]
29
+ pn = n + 1
30
+ state = :rt
31
+ end
32
+ if resource[n] == "["
33
+ # reached the open bracket
34
+ result << [:rn, resource[pn...n]]
35
+ pn = n
36
+ state = :ri
37
+ end
38
+ if n == resource.length - 1
39
+ # last character .. close the current group
40
+ # the last thing should only ever be an index or a name
41
+ result << [:rn, resource[pn..n]]
42
+ pn = n
43
+ state = :done
44
+ end
45
+ when :ri
46
+ # looking for ]
47
+ if resource[n] == "]"
48
+ # reached the close bracket
49
+ result << [:ri, resource[pn..n]]
50
+ pn = n + 1
51
+ state = :rt
52
+ if resource[n + 1] == "."
53
+ pn = n + 2
54
+ n += 1
55
+ end
52
56
  end
57
+ else
58
+ warn "unhandled state: #{state.inspect}"
53
59
  end
54
- else
55
- warn "unhandled state: #{state.inspect}"
60
+ # p resource[n]
61
+ n += 1
56
62
  end
57
- # p resource[n]
58
- n += 1
63
+ result
59
64
  end
60
- result
61
65
  end
62
66
  end
@@ -4,6 +4,8 @@ module MuxTf
4
4
  module TerraformHelpers
5
5
  include PiotrbCliUtils::ShellHelpers
6
6
 
7
+ ResultStruct = Struct.new("TerraformResponse", :status, :success?, :output, :parsed_output, keyword_init: true)
8
+
7
9
  def tf_force_unlock(id:)
8
10
  run_terraform(tf_prepare_command(["force-unlock", "-force", id], need_auth: true))
9
11
  end
@@ -88,32 +90,32 @@ module MuxTf
88
90
  # return_status: false, echo_command: true, quiet: false, indent: 0
89
91
  def run_terraform(args, **_options)
90
92
  status = run_shell(args, return_status: true, echo_command: true, quiet: false)
91
- OpenStruct.new({
92
- status: status,
93
- success?: status == 0
94
- })
93
+ ResultStruct.new({
94
+ status: status,
95
+ success?: status.zero?
96
+ })
95
97
  end
96
98
 
97
99
  def stream_terraform(args, &block)
98
100
  status = run_with_each_line(args, &block)
99
101
  # status is a Process::Status
100
- OpenStruct.new({
101
- status: status.exitstatus,
102
- success?: status.exitstatus == 0
103
- })
102
+ ResultStruct.new({
103
+ status: status.exitstatus,
104
+ success?: status.exitstatus.zero?
105
+ })
104
106
  end
105
107
 
106
108
  # error: true, echo_command: true, indent: 0, raise_on_error: false, detailed_result: false
107
109
  def capture_terraform(args, json: nil)
108
110
  result = capture_shell(args, error: true, echo_command: false, raise_on_error: false, detailed_result: true)
109
111
  parsed_output = JSON.parse(result.output) if json
110
- OpenStruct.new({
111
- status: result.status,
112
- success?: result.status == 0,
113
- output: result.output,
114
- parsed_output: parsed_output
115
- })
116
- rescue JSON::ParserError => e
112
+ ResultStruct.new({
113
+ status: result.status,
114
+ success?: result.status.zero?,
115
+ output: result.output,
116
+ parsed_output: parsed_output
117
+ })
118
+ rescue JSON::ParserError
117
119
  fail_with "Execution Failed! - #{result.inspect}"
118
120
  end
119
121
  end
data/lib/mux_tf/tmux.rb CHANGED
@@ -16,7 +16,7 @@ module MuxTf
16
16
  def find_pane(name)
17
17
  panes = `tmux list-panes -F "\#{pane_id},\#{pane_title}"`.strip.split("\n").map { |row|
18
18
  x = row.split(",")
19
- return {id: x[0], name: x[1]}
19
+ { id: x[0], name: x[1] }
20
20
  }
21
21
  panes.find { |pane| pane[:name] == name }
22
22
  end
@@ -24,7 +24,7 @@ module MuxTf
24
24
  def list_windows
25
25
  `tmux list-windows -F "\#{window_id},\#{window_index},\#{window_name}"`.strip.split("\n").map do |row|
26
26
  x = row.split(",")
27
- {id: x[0], index: x[1], name: x[2]}
27
+ { id: x[0], index: x[1], name: x[2] }
28
28
  end
29
29
  end
30
30
 
@@ -49,7 +49,7 @@ module MuxTf
49
49
  end
50
50
 
51
51
  def attach(name, cc: false)
52
- tmux %(#{cc && "-CC" || ""} attach -t #{name.inspect}), raise_on_error: false
52
+ tmux %(#{(cc && '-CC') || ''} attach -t #{name.inspect}), raise_on_error: false
53
53
  end
54
54
 
55
55
  def kill_pane(pane_id)
@@ -93,9 +93,7 @@ module MuxTf
93
93
  # puts " => tmux #{cmd}"
94
94
  system("#{tmux_bin} #{cmd}")
95
95
  unless $CHILD_STATUS.success?
96
- if raise_on_error
97
- fail_with("`tmux #{cmd}' failed with code: #{$CHILD_STATUS.exitstatus}")
98
- end
96
+ fail_with("`tmux #{cmd}' failed with code: #{$CHILD_STATUS.exitstatus}") if raise_on_error
99
97
 
100
98
  return false
101
99
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MuxTf
4
- VERSION = "0.8.4"
4
+ VERSION = "0.9.0"
5
5
  end
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MuxTf
2
4
  module VersionCheck
3
- def has_updates?
5
+ def has_updates? # rubocop:disable Naming/PredicateName
4
6
  current_gem_version < latest_gem_version
5
7
  end
6
8
 
@@ -23,6 +25,6 @@ module MuxTf
23
25
  @cache ||= YamlCache.new(File.expand_path("~/.mux_tf.yaml"), default_ttl: 1.hour)
24
26
  end
25
27
 
26
- extend self
28
+ module_function :has_updates?, :latest_gem_version, :current_gem_version, :cache
27
29
  end
28
30
  end