mux_tf 0.8.4 → 0.9.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.
@@ -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