lsaws 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09f8ec552161ab82ab45a5c57e50e7756b434974088dadd90e8417d97c317ecc'
4
- data.tar.gz: c77880939cc6058a9f8120e95f3df65392ddfe4105783fac7183277edd19c31a
3
+ metadata.gz: fc348c9f2fd3c7a4f3159329804809b7800307aa42eedfdc01b17d9c8e220021
4
+ data.tar.gz: b558293f705f7df368d75aeb41a6bc133fd7bb3a366ddef2f790f99267c019c5
5
5
  SHA512:
6
- metadata.gz: df5f2f83c723a75be975af1c96e38b91e14661d920b62407817ee05a8cce33da9e6be2b59bce23bc0d5638f5bd6be821e2ea674df8991f6902233e22b5d3b2f2
7
- data.tar.gz: 7e07bfb922870d91b12cd8f94ef9ddb354817e921cb34e269cf6a4d85d3c84737fa69dd69eec104d18cffd6709a2a77767500613312d0caf0dde4430f6d9cef6
6
+ metadata.gz: 2c2a9d0c17689697c13aa3be30e05b6406a7d0031102342e3bddfcd56a486b73e23c57cdfcae8fa9653443e66fefb8b5d962c57abd044f103ecf26e85a69d7a4
7
+ data.tar.gz: 2dc1db342c3d646d9b5e7504b01cf2fdd9f83ad9e90298bde50f69eaf9b5adffce0405a0b8b86ea7890617fb484262780397a9ee08bb0e43a9a652d22d8672e8
data/Gemfile CHANGED
@@ -8,8 +8,8 @@ gemspec
8
8
  group :development, :test do
9
9
  gem "activesupport", "~> 7.0"
10
10
  gem "aws-sdk"
11
+ gem "pry"
11
12
  gem "rake", "~> 13.0"
12
13
  gem "rspec", "~> 3.0"
13
14
  gem "rubocop", "~> 1.21"
14
- gem "debug"
15
15
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lsaws (0.1.0)
4
+ lsaws (0.3.0)
5
5
  aws-sdk-core (~> 3.131)
6
6
  tabulo (~> 2.8)
7
7
 
@@ -1379,27 +1379,24 @@ GEM
1379
1379
  aws-sigv2 (1.1.0)
1380
1380
  aws-sigv4 (1.5.2)
1381
1381
  aws-eventstream (~> 1, >= 1.0.2)
1382
+ coderay (1.1.3)
1382
1383
  concurrent-ruby (1.2.0)
1383
- debug (1.7.1)
1384
- irb (>= 1.5.0)
1385
- reline (>= 0.3.1)
1386
1384
  diff-lcs (1.5.0)
1387
1385
  i18n (1.12.0)
1388
1386
  concurrent-ruby (~> 1.0)
1389
- io-console (0.6.0)
1390
- irb (1.6.2)
1391
- reline (>= 0.3.0)
1392
1387
  jmespath (1.6.2)
1393
1388
  json (2.6.3)
1389
+ method_source (1.0.0)
1394
1390
  minitest (5.17.0)
1395
1391
  parallel (1.22.1)
1396
1392
  parser (3.2.0.0)
1397
1393
  ast (~> 2.4.1)
1394
+ pry (0.14.2)
1395
+ coderay (~> 1.1)
1396
+ method_source (~> 1.0)
1398
1397
  rainbow (3.1.1)
1399
1398
  rake (13.0.6)
1400
1399
  regexp_parser (2.6.2)
1401
- reline (0.3.2)
1402
- io-console (~> 0.5)
1403
1400
  rexml (3.2.5)
1404
1401
  rspec (3.12.0)
1405
1402
  rspec-core (~> 3.12.0)
@@ -1441,8 +1438,8 @@ PLATFORMS
1441
1438
  DEPENDENCIES
1442
1439
  activesupport (~> 7.0)
1443
1440
  aws-sdk
1444
- debug
1445
1441
  lsaws!
1442
+ pry
1446
1443
  rake (~> 13.0)
1447
1444
  rspec (~> 3.0)
1448
1445
  rubocop (~> 1.21)
data/lib/lsaws/lister.rb CHANGED
@@ -3,20 +3,20 @@
3
3
  require "aws-sdk-core"
4
4
  require "json"
5
5
  require "tabulo"
6
- require "yaml"
7
6
 
8
7
  module Lsaws
9
8
  class Lister
10
- CONFIG = YAML.load_file(File.join(Lsaws.root, "lsaws.yml"))
11
-
12
9
  include Utils
13
10
 
11
+ NEXT_PAGE_FIELDS = %i[next_token next_marker next_page_token page_token].freeze
12
+
14
13
  def initialize(options)
15
14
  @options = options
15
+ @options[:max_width] = nil if @options[:max_width].to_i.zero?
16
16
  end
17
17
 
18
18
  def _prepare_entities(sdk, type, &block)
19
- edef = CONFIG.dig(sdk, type)
19
+ edef = Lsaws.config.dig(sdk, type)
20
20
  return _prepare_entities(sdk, edef, &block) if edef.is_a?(String) # redirect like 'default' -> 'instances'
21
21
 
22
22
  edef ||= {}
@@ -33,7 +33,7 @@ module Lsaws
33
33
  sdkp = SDKParser.new(sdk)
34
34
  client_class = edef["client_class"] || sdkp.client_class_name
35
35
  client = Kernel.const_get(client_class).new
36
- method_name = edef["method"] || (client.respond_to?("describe_#{type}") ? "describe_#{type}" : "list_#{type}")
36
+ method_name = edef["method"] || sdkp.etype2method(type)
37
37
  unless client.respond_to?(method_name)
38
38
  if type == "default"
39
39
  warn "[!] no default entity type set for #{sdk.inspect} SDK"
@@ -44,27 +44,41 @@ module Lsaws
44
44
  list_entity_types(sdk)
45
45
  exit 1
46
46
  end
47
+ warn "[d] #{method_name} #{params}" if @options[:debug]
47
48
  results = client.send(method_name, params)
48
49
 
49
- pp results if @options[:debug]
50
+ warn "[d] #{File.basename(__FILE__)}:#{__LINE__} results:\n#{results.pretty_inspect}" if @options[:debug]
50
51
 
51
52
  if !edef["result_keys"] && results.any?
52
53
  r = results.first
53
54
  r = r.last if r.is_a?(Array)
54
- edef["result_keys"] = if r.respond_to?(type)
55
- [type]
56
- else
57
- [(r.members - [:next_token]).first]
58
- end
55
+ edef["result_keys"] =
56
+ if r.respond_to?(type)
57
+ [type]
58
+ elsif NEXT_PAGE_FIELDS.any? { |t| r.respond_to?(t) }
59
+ data_members = r.members - NEXT_PAGE_FIELDS
60
+ if data_members.size == 1
61
+ [data_members[0]]
62
+ else
63
+ # XXX what if there's more than one array?
64
+ [data_members.find { |key| r[key].is_a?(Array) }].compact
65
+ end
66
+ else
67
+ []
68
+ end
59
69
  end
70
+
60
71
  edef["result_keys"].each do |key|
61
72
  results = if results.is_a?(Array)
62
- results.map(&key.to_sym).flatten
73
+ results.map(&key.to_sym).flatten # TODO: is flatten necessary?
63
74
  else
64
- results.send(key)
75
+ results[key]
65
76
  end
66
77
  end
78
+
79
+ warn "[d] #{File.basename(__FILE__)}:#{__LINE__} results:\n#{results.pretty_inspect}" if @options[:debug]
67
80
  edef["cols"] = @options[:show_cols] if @options[:show_cols].any?
81
+ warn "[d] edef: #{edef}" if @options[:debug]
68
82
 
69
83
  col_defs = {}
70
84
  Array(edef["cols"]).each do |r|
@@ -77,10 +91,12 @@ module Lsaws
77
91
  raise Error, "unexpected #{r.inspect}"
78
92
  end
79
93
  end
94
+ # TODO: check with all types
80
95
  col_defs["tags"] = _convert_tags_proc if @options[:show_tags] || col_defs["tags"]
81
96
 
82
97
  results ||= []
83
- if results.any? && !results.first.respond_to?(:name)
98
+ warn "[d] #{results.inspect}" if @options[:debug]
99
+ if results.respond_to?(:any?) && results.any? && !results.first.respond_to?(:name) && results.first.respond_to?(:tags)
84
100
  results.first.class.class_eval do
85
101
  def name
86
102
  tags.find { |tag| tag.key == "Name" }&.value
@@ -88,9 +104,19 @@ module Lsaws
88
104
  end
89
105
  end
90
106
 
91
- # ec2 instance_event_notification_attributes
92
- # 'Array(results)' doesn't work here
93
- results = [results] unless results.is_a?(Array)
107
+ case results
108
+ when Hash
109
+ col_defs = {
110
+ key: :first,
111
+ value: :last
112
+ }
113
+ when Array
114
+ # ok
115
+ else
116
+ # ec2 instance_event_notification_attributes
117
+ # 'Array(results)' doesn't work here
118
+ results = [results]
119
+ end
94
120
 
95
121
  if block_given?
96
122
  results.map do |entity|
@@ -106,6 +132,9 @@ module Lsaws
106
132
  if rows.is_a?(Array) && rows[0].is_a?(String)
107
133
  # sqs
108
134
  cols = { value: proc { |entity| entity } }
135
+ elsif rows.is_a?(Array) && rows[0].is_a?(Hash)
136
+ # securitylake:log_sources
137
+ cols = { value: proc { |entity| entity } }
109
138
  elsif rows.respond_to?(:members)
110
139
  rows = [rows]
111
140
  end
@@ -168,12 +197,23 @@ module Lsaws
168
197
  _list_array SDKParser.new(sdk).entity_types
169
198
  end
170
199
 
200
+ # elasticache:global_replication_groups has 'members' as a column, so cannot just use `rows.members`
201
+ # assuming row is always subclass of Struct here
202
+ def _get_cols(row)
203
+ if row.is_a?(Struct)
204
+ Struct.instance_method(:members).bind_call(row)
205
+ else
206
+ row.members
207
+ end
208
+ end
209
+
171
210
  def _tabulo_guess_max_cols(rows, _cols)
172
- max_cols = rows[0].members.size
211
+ all_cols = _get_cols(rows[0])
212
+ max_cols = all_cols.size
173
213
  return max_cols if max_cols < 4 || !@options[:max_width]
174
214
 
175
215
  4.upto(max_cols) do |ncols|
176
- tbl = Tabulo::Table.new(rows[0, 100], *rows[0].members[0, ncols])
216
+ tbl = Tabulo::Table.new(rows[0, 100], *all_cols[0, ncols])
177
217
  tbl.autosize_columns
178
218
  tbl_width = tbl.column_registry.values.map { |c| c.padded_width + 1 }.inject(:+) + 1
179
219
  return ncols - 1 if tbl_width >= @options[:max_width]
@@ -187,7 +227,7 @@ module Lsaws
187
227
  elsif type == :list
188
228
  return list_entity_types(sdk)
189
229
  elsif type == :all
190
- get_entity_types(sdk).each do |etype|
230
+ SDKParser.new(sdk).entity_types.each do |etype|
191
231
  puts "#{etype}:"
192
232
  process_command(sdk, etype)
193
233
  end
@@ -208,7 +248,7 @@ module Lsaws
208
248
  cols.each { |name, func| tbl.add_column(name, &func) }
209
249
  else
210
250
  max_cols = _tabulo_guess_max_cols(rows, cols)
211
- rows[0].members[0, max_cols].each { |col| tbl.add_column(col) }
251
+ _get_cols(rows[0])[0, max_cols].each { |col| tbl.add_column(col) }
212
252
  end
213
253
  puts tbl.pack(max_table_width: @options[:max_width])
214
254
  when :json
@@ -2,6 +2,11 @@
2
2
 
3
3
  module Lsaws
4
4
  class SDKParser
5
+ IGNORED_SDKS = [
6
+ "core", "resources", # do not contain any resource listing methods
7
+ "s3control" # requires account_id param for all requests
8
+ ].freeze
9
+
5
10
  def self.get_sdks
6
11
  r = []
7
12
  Gem.path.each do |p|
@@ -12,7 +17,7 @@ module Lsaws
12
17
  a.size == 4 ? a[2] : nil
13
18
  end)
14
19
  end
15
- r.compact.uniq.sort - ["core"]
20
+ r.compact.uniq.sort - IGNORED_SDKS
16
21
  end
17
22
 
18
23
  def initialize(sdk)
@@ -23,6 +28,7 @@ module Lsaws
23
28
  def client_class_name
24
29
  @client_class_name ||=
25
30
  begin
31
+ # TODO: use constants from gems/aws-sdk-resources-x.xxx
26
32
  c = Aws.constants.find { |x| x.to_s.downcase == @sdk }
27
33
  "Aws::#{c}::Client"
28
34
  end
@@ -32,10 +38,25 @@ module Lsaws
32
38
  @client_class ||= Kernel.const_get(client_class_name)
33
39
  end
34
40
 
41
+ # order is important!
42
+ LIST_METHOD_PREFIXES = %w[list describe get].freeze
43
+
44
+ def etype2method(etype)
45
+ LIST_METHOD_PREFIXES.each do |prefix|
46
+ m = "#{prefix}_#{etype}"
47
+ return m if client_class.public_method_defined?(m)
48
+ end
49
+ nil
50
+ end
51
+
52
+ def method2etype(method)
53
+ method.to_s.sub(/^(?:#{LIST_METHOD_PREFIXES.join("|")})_/, "")
54
+ end
55
+
35
56
  def entity_types
36
57
  methods = client_class
37
58
  .instance_methods
38
- .find_all { |m| m =~ /^(describe|list)_.+s$/ && m !~ /(status|access)$/ }
59
+ .find_all { |m| m =~ /^(?:#{LIST_METHOD_PREFIXES.join("|")})_.+s$/ && m !~ /(?:status|access)$/ }
39
60
 
40
61
  return [] if methods.empty?
41
62
 
@@ -44,10 +65,10 @@ module Lsaws
44
65
  next(true) unless rdoc
45
66
 
46
67
  required_params = rdoc.scan(/^\s+# @option params \[required, (.+?)\] :(\w+)/)
47
- required_params.any?
68
+ required_params.any? || Lsaws.config.dig(@sdk, method2etype(m), "required_params")
48
69
  end
49
70
 
50
- methods.map { |m| m.to_s.sub(/^(describe|list)_/, "") }.sort
71
+ methods.map { |m| method2etype(m) }.sort
51
72
  end
52
73
 
53
74
  def get_method_rdoc(method)
@@ -64,5 +85,10 @@ module Lsaws
64
85
  end
65
86
  chunk[chunk.rindex("\n\n") + 2..]
66
87
  end
88
+
89
+ def get_method_api(method)
90
+ # calling private API!
91
+ client_class.api.operation(method)
92
+ end
67
93
  end
68
94
  end
data/lib/lsaws/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lsaws
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/lsaws.rb CHANGED
@@ -1,11 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "yaml"
4
+
3
5
  module Lsaws
4
6
  class Error < StandardError; end
5
7
 
6
8
  def self.root
7
9
  File.dirname(File.dirname(File.expand_path(__FILE__)))
8
10
  end
11
+
12
+ def self.config
13
+ @config ||= YAML.load_file(File.join(Lsaws.root, "lsaws.yml"))
14
+ end
9
15
  end
10
16
 
11
17
  require_relative "lsaws/version"
data/lsaws.yml CHANGED
@@ -1,3 +1,8 @@
1
+ cloudformation:
2
+ stack_resources:
3
+ required_params:
4
+ - [ stack_name ]
5
+ - [ physical_resource_id ]
1
6
  ec2:
2
7
  default: instances
3
8
  images:
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lsaws
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey "Zed" Zaikin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-02-10 00:00:00.000000000 Z
11
+ date: 2023-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-core