lsaws 0.2.0 → 0.3.1

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: e2b87621670d3747c4c1b6cb9979c7401f7e736ceb8c257819a42dc31b190471
4
+ data.tar.gz: ab3227e58e62f14a718858242bd2cdd5ffd8f6a5f15acc38550605a01aea2352
5
5
  SHA512:
6
- metadata.gz: df5f2f83c723a75be975af1c96e38b91e14661d920b62407817ee05a8cce33da9e6be2b59bce23bc0d5638f5bd6be821e2ea674df8991f6902233e22b5d3b2f2
7
- data.tar.gz: 7e07bfb922870d91b12cd8f94ef9ddb354817e921cb34e269cf6a4d85d3c84737fa69dd69eec104d18cffd6709a2a77767500613312d0caf0dde4430f6d9cef6
6
+ metadata.gz: 4eeb107b1267e40dadb27fccb5dc619f53807410c68c3b85cda9c5e63e3c57da9ab731b422f1c236a0b3bc7a2701e4bffee989257676a9e3cff20cfc9ce2b07c
7
+ data.tar.gz: a725ba9efec0bf251516ca30a8841dac825ca3dacaf2adf00dcef453f2806e22d946512a6b4f6e70bb6f72547f2204e474065f623f86bc258e0a48624c8e9aef
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/README.md CHANGED
@@ -23,6 +23,88 @@ User-friendly AWS resources listing tool
23
23
  -A, --all List all entity types within SDK
24
24
 
25
25
 
26
+ ### Listing installed SDKs
27
+
28
+ # lsaws -L
29
+
30
+ accessanalyzer
31
+ account
32
+ acm
33
+ acmpca
34
+ alexaforbusiness
35
+ amplify
36
+ amplifybackend
37
+ amplifyuibuilder
38
+ apigateway
39
+ apigatewaymanagementapi
40
+ apigatewayv2
41
+ appconfig
42
+ appconfigdata
43
+ appflow
44
+ appintegrationsservice
45
+ applicationautoscaling
46
+ applicationcostprofiler
47
+ applicationdiscoveryservice
48
+ applicationinsights
49
+ appmesh
50
+ appregistry
51
+ apprunner
52
+ appstream
53
+ appsync
54
+ arczonalshift
55
+ ...
56
+
57
+
58
+ ### Listing listable entities from EC2 sdk
59
+
60
+ # lsaws ec2 -L
61
+
62
+ account_attributes
63
+ address_transfers
64
+ addresses
65
+ availability_zones
66
+ aws_network_performance_metric_subscriptions
67
+ bundle_tasks
68
+ capacity_reservation_fleets
69
+ capacity_reservations
70
+ carrier_gateways
71
+ classic_link_instances
72
+ client_vpn_endpoints
73
+ coip_pools
74
+ conversion_tasks
75
+ customer_gateways
76
+ dhcp_options
77
+ egress_only_internet_gateways
78
+ elastic_gpus
79
+ export_image_tasks
80
+ export_tasks
81
+ fast_launch_images
82
+ fast_snapshot_restores
83
+ fleets
84
+ flow_logs
85
+ fpga_images
86
+ host_reservation_offerings
87
+ ...
88
+
89
+
90
+ ### Listing EC2 instances
91
+
92
+ # lsaws ec2
93
+
94
+ ┌─────────────────────┬─────────────┬───────────────────────┬────────────────────┬─────────────────────────┐
95
+ │ instance_id │ name │ vpc_id │ private_ip_address │ launch_time │
96
+ ├─────────────────────┼─────────────┼───────────────────────┼────────────────────┼─────────────────────────┤
97
+ │ i-1234567890abcdef0 │ my-instance │ vpc-1234567890abcdef0 │ 10-0-0-157 │ 2022-11-15 10:48:59 UTC │
98
+ └─────────────────────┴─────────────┴───────────────────────┴────────────────────┴─────────────────────────┘
99
+
100
+ ### Listing specific columns of EC2 images as json-stream
101
+
102
+ # lsaws ec2 images -c image_id,creation_date -o js
103
+
104
+ {"image_id":"ami-1234e6197567838b4","creation_date":"2023-01-11T13:02:00.000Z"}
105
+ {"image_id":"ami-1234f58a167898416","creation_date":"2023-01-11T02:02:00.000Z"}
106
+ {"image_id":"ami-12341a1fd567897a8","creation_date":"2023-01-01T01:02:00.000Z"}
107
+
26
108
  ## License
27
109
 
28
110
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/README.md.tpl CHANGED
@@ -6,6 +6,32 @@ User-friendly AWS resources listing tool
6
6
 
7
7
  % lsaws
8
8
 
9
+ ### Listing installed SDKs
10
+
11
+ % lsaws -L
12
+
13
+ ### Listing listable entities from EC2 sdk
14
+
15
+ % lsaws ec2 -L
16
+
17
+ ### Listing EC2 instances
18
+
19
+ # lsaws ec2
20
+
21
+ ┌─────────────────────┬─────────────┬───────────────────────┬────────────────────┬─────────────────────────┐
22
+ │ instance_id │ name │ vpc_id │ private_ip_address │ launch_time │
23
+ ├─────────────────────┼─────────────┼───────────────────────┼────────────────────┼─────────────────────────┤
24
+ │ i-1234567890abcdef0 │ my-instance │ vpc-1234567890abcdef0 │ 10-0-0-157 │ 2022-11-15 10:48:59 UTC │
25
+ └─────────────────────┴─────────────┴───────────────────────┴────────────────────┴─────────────────────────┘
26
+
27
+ ### Listing specific columns of EC2 images as json-stream
28
+
29
+ # lsaws ec2 images -c image_id,creation_date -o js
30
+
31
+ {"image_id":"ami-0245e6197bf3138b4","creation_date":"2023-01-19T12:03:38.000Z"}
32
+ {"image_id":"ami-0bc3f58a172118416","creation_date":"2023-01-20T09:37:32.000Z"}
33
+ {"image_id":"ami-07e71a1fdf49957a8","creation_date":"2023-02-01T02:06:29.000Z"}
34
+
9
35
  ## License
10
36
 
11
37
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/lib/lsaws/lister.rb CHANGED
@@ -3,20 +3,31 @@
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
+ end
17
+
18
+ def _abort_with_list(sdk, type)
19
+ if type == "default"
20
+ warn "[!] no default entity type set for #{sdk.inspect} SDK"
21
+ else
22
+ warn "[!] #{sdk.inspect} SDK does not have #{type.inspect} entity type"
23
+ end
24
+ puts "Known entity types are:"
25
+ list_entity_types(sdk)
26
+ exit 1
16
27
  end
17
28
 
18
29
  def _prepare_entities(sdk, type, &block)
19
- edef = CONFIG.dig(sdk, type)
30
+ edef = Lsaws.config.dig(sdk, type)
20
31
  return _prepare_entities(sdk, edef, &block) if edef.is_a?(String) # redirect like 'default' -> 'instances'
21
32
 
22
33
  edef ||= {}
@@ -33,38 +44,43 @@ module Lsaws
33
44
  sdkp = SDKParser.new(sdk)
34
45
  client_class = edef["client_class"] || sdkp.client_class_name
35
46
  client = Kernel.const_get(client_class).new
36
- method_name = edef["method"] || (client.respond_to?("describe_#{type}") ? "describe_#{type}" : "list_#{type}")
37
- unless client.respond_to?(method_name)
38
- if type == "default"
39
- warn "[!] no default entity type set for #{sdk.inspect} SDK"
40
- else
41
- warn "[!] #{sdk.inspect} SDK does not have #{type.inspect} entity type"
42
- end
43
- puts "Known entity types are:"
44
- list_entity_types(sdk)
45
- exit 1
46
- end
47
+ method_name = edef["method"] || sdkp.etype2method(type)
48
+ _abort_with_list(sdk, type) unless method_name
49
+ warn "[d] #{method_name} #{params}" if @options[:debug]
47
50
  results = client.send(method_name, params)
48
51
 
49
- pp results if @options[:debug]
52
+ warn "[d] #{File.basename(__FILE__)}:#{__LINE__} results:\n#{results.pretty_inspect}" if @options[:debug]
50
53
 
51
54
  if !edef["result_keys"] && results.any?
52
55
  r = results.first
53
56
  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
57
+ edef["result_keys"] =
58
+ if r.respond_to?(type)
59
+ [type]
60
+ elsif NEXT_PAGE_FIELDS.any? { |t| r.respond_to?(t) }
61
+ data_members = r.members - NEXT_PAGE_FIELDS
62
+ if data_members.size == 1
63
+ [data_members[0]]
64
+ else
65
+ # XXX what if there's more than one array?
66
+ [data_members.find { |key| r[key].is_a?(Array) }].compact
67
+ end
68
+ else
69
+ []
70
+ end
59
71
  end
72
+
60
73
  edef["result_keys"].each do |key|
61
74
  results = if results.is_a?(Array)
62
- results.map(&key.to_sym).flatten
75
+ results.map(&key.to_sym).flatten # TODO: is flatten necessary?
63
76
  else
64
- results.send(key)
77
+ results[key]
65
78
  end
66
79
  end
80
+
81
+ warn "[d] #{File.basename(__FILE__)}:#{__LINE__} results:\n#{results.pretty_inspect}" if @options[:debug]
67
82
  edef["cols"] = @options[:show_cols] if @options[:show_cols].any?
83
+ warn "[d] edef: #{edef}" if @options[:debug]
68
84
 
69
85
  col_defs = {}
70
86
  Array(edef["cols"]).each do |r|
@@ -77,10 +93,12 @@ module Lsaws
77
93
  raise Error, "unexpected #{r.inspect}"
78
94
  end
79
95
  end
96
+ # TODO: check with all types
80
97
  col_defs["tags"] = _convert_tags_proc if @options[:show_tags] || col_defs["tags"]
81
98
 
82
99
  results ||= []
83
- if results.any? && !results.first.respond_to?(:name)
100
+ warn "[d] #{results.inspect}" if @options[:debug]
101
+ if results.respond_to?(:any?) && results.any? && !results.first.respond_to?(:name) && results.first.respond_to?(:tags)
84
102
  results.first.class.class_eval do
85
103
  def name
86
104
  tags.find { |tag| tag.key == "Name" }&.value
@@ -88,9 +106,19 @@ module Lsaws
88
106
  end
89
107
  end
90
108
 
91
- # ec2 instance_event_notification_attributes
92
- # 'Array(results)' doesn't work here
93
- results = [results] unless results.is_a?(Array)
109
+ case results
110
+ when Hash
111
+ col_defs = {
112
+ key: :first,
113
+ value: :last
114
+ }
115
+ when Array
116
+ # ok
117
+ else
118
+ # ec2 instance_event_notification_attributes
119
+ # 'Array(results)' doesn't work here
120
+ results = [results]
121
+ end
94
122
 
95
123
  if block_given?
96
124
  results.map do |entity|
@@ -106,6 +134,9 @@ module Lsaws
106
134
  if rows.is_a?(Array) && rows[0].is_a?(String)
107
135
  # sqs
108
136
  cols = { value: proc { |entity| entity } }
137
+ elsif rows.is_a?(Array) && rows[0].is_a?(Hash)
138
+ # securitylake:log_sources
139
+ cols = { value: proc { |entity| entity } }
109
140
  elsif rows.respond_to?(:members)
110
141
  rows = [rows]
111
142
  end
@@ -168,12 +199,23 @@ module Lsaws
168
199
  _list_array SDKParser.new(sdk).entity_types
169
200
  end
170
201
 
202
+ # elasticache:global_replication_groups has 'members' as a column, so cannot just use `rows.members`
203
+ # assuming row is always subclass of Struct here
204
+ def _get_cols(row)
205
+ if row.is_a?(Struct)
206
+ Struct.instance_method(:members).bind_call(row)
207
+ else
208
+ row.members
209
+ end
210
+ end
211
+
171
212
  def _tabulo_guess_max_cols(rows, _cols)
172
- max_cols = rows[0].members.size
213
+ all_cols = _get_cols(rows[0])
214
+ max_cols = all_cols.size
173
215
  return max_cols if max_cols < 4 || !@options[:max_width]
174
216
 
175
217
  4.upto(max_cols) do |ncols|
176
- tbl = Tabulo::Table.new(rows[0, 100], *rows[0].members[0, ncols])
218
+ tbl = Tabulo::Table.new(rows[0, 100], *all_cols[0, ncols])
177
219
  tbl.autosize_columns
178
220
  tbl_width = tbl.column_registry.values.map { |c| c.padded_width + 1 }.inject(:+) + 1
179
221
  return ncols - 1 if tbl_width >= @options[:max_width]
@@ -187,7 +229,7 @@ module Lsaws
187
229
  elsif type == :list
188
230
  return list_entity_types(sdk)
189
231
  elsif type == :all
190
- get_entity_types(sdk).each do |etype|
232
+ SDKParser.new(sdk).entity_types.each do |etype|
191
233
  puts "#{etype}:"
192
234
  process_command(sdk, etype)
193
235
  end
@@ -208,7 +250,7 @@ module Lsaws
208
250
  cols.each { |name, func| tbl.add_column(name, &func) }
209
251
  else
210
252
  max_cols = _tabulo_guess_max_cols(rows, cols)
211
- rows[0].members[0, max_cols].each { |col| tbl.add_column(col) }
253
+ _get_cols(rows[0])[0, max_cols].each { |col| tbl.add_column(col) }
212
254
  end
213
255
  puts tbl.pack(max_table_width: @options[:max_width])
214
256
  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) }.uniq.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.1"
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.1
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-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-core