jcf 0.0.11 → 0.0.12
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/CHANGELOG.md +6 -0
- data/lib/jcf/cf/base.rb +12 -1
- data/lib/jcf/cli/commands/cf/metrics.rb +57 -52
- data/lib/jcf/cli/commands/cf/organizations.rb +6 -3
- data/lib/jcf/cli/commands/plugins.rb +24 -0
- data/lib/jcf/cli/commands.rb +1 -0
- data/lib/jcf/cli/output_formatters/csv.rb +5 -17
- data/lib/jcf/cli/output_formatters/json.rb +4 -6
- data/lib/jcf/cli/output_formatters/text.rb +21 -8
- data/lib/jcf/cli.rb +46 -0
- data/lib/jcf/metric.rb +13 -0
- data/lib/jcf/plugins/aws_rds.rb +111 -0
- data/lib/jcf/plugins/aws_s3.rb +61 -0
- data/lib/jcf/version.rb +1 -1
- metadata +7 -4
- data/lib/jcf/cf/metric.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 33389a4011279ad183dc738318b71355a50601dde4f602d867cb060b305e7a49
|
4
|
+
data.tar.gz: 42f129b8aa7ab515bd51fac1f2c8f78ee6785f8750debb5778c88fa2d062dca1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35548f0ce298af159e532eb6b0517594d8e990857d8c5f77aab5e0b770920758e2a11f74ca063c113342adc1a46df120ed2d4577684ae69b60db90578a4a5d8d
|
7
|
+
data.tar.gz: 1fd422e325af12931d444728c4aca883eceaceab60bea50bc33e143bc62e70d783b1ed4103a2c181302fc6d5e7b44eeff70aa4ff53278cb12710e7b6ec806ee6
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## [0.0.12] - 2023-10-19
|
2
|
+
|
3
|
+
- Refactored broker backend querying for metrics to use a plugin system that can be extended to support other brokers. Currently only the AWS and S3 broker is supported, but it should be easy to add others.
|
4
|
+
- Add a `jcf plugins` command to list available plugins (currently only ones in the `lib/jcf/plugins` directory)
|
5
|
+
- Refactored specs for output formatters
|
6
|
+
|
1
7
|
## [0.0.11] - 2023-10-12
|
2
8
|
|
3
9
|
- Add a tree output for a new command `jcf services NAME` that will show all the offerings and instances for that broker. In a tree. Because trees are cool.
|
data/lib/jcf/cf/base.rb
CHANGED
@@ -23,6 +23,14 @@ module JCF
|
|
23
23
|
{ name: name, guid: guid, relationships: relationships }
|
24
24
|
end
|
25
25
|
|
26
|
+
def keys
|
27
|
+
attributes.keys
|
28
|
+
end
|
29
|
+
|
30
|
+
def values
|
31
|
+
attributes.values
|
32
|
+
end
|
33
|
+
|
26
34
|
def initialize(name: nil, guid: nil, relationships: nil)
|
27
35
|
@name = name
|
28
36
|
@guid = guid
|
@@ -32,12 +40,14 @@ module JCF
|
|
32
40
|
class << self
|
33
41
|
attr_accessor :endpoint
|
34
42
|
|
43
|
+
def keys; new.keys; end
|
44
|
+
|
35
45
|
def find_by(attrs)
|
36
46
|
objects = all
|
37
47
|
objects.find_all do |obj|
|
38
|
-
# only find by attributes we have and our object has
|
39
48
|
keys = obj.attributes.keys & attrs.keys
|
40
49
|
keys.all? do |key|
|
50
|
+
return true if attrs[key].nil?
|
41
51
|
obj.attributes[key].include? attrs[key]
|
42
52
|
end
|
43
53
|
end
|
@@ -51,6 +61,7 @@ module JCF
|
|
51
61
|
new(guid: guid).populate!
|
52
62
|
end
|
53
63
|
|
64
|
+
# TODO: make this less greedy
|
54
65
|
def all(params = {})
|
55
66
|
params.compact!
|
56
67
|
|
@@ -14,38 +14,51 @@ module JCF
|
|
14
14
|
module Commands
|
15
15
|
module CF
|
16
16
|
class Metrics < Command
|
17
|
-
argument :
|
18
|
-
|
17
|
+
argument :broker, required: true,
|
18
|
+
desc: "Choose a service instance offering to query. Get the broker name from the command: jcf sb"
|
19
|
+
argument :iaas_plugin, aliases: ["-m"], required: true, type: :string,
|
20
|
+
values: ::JCF::Plugins.plugins.keys.collect(&:to_s),
|
21
|
+
desc: "Select a IaaS plugin this broker is backed by"
|
19
22
|
|
20
23
|
option :template, aliases: ["-t"], required: true, type: :string,
|
21
|
-
|
24
|
+
desc: "Template for backend service instance names e.g. \"rdsbroker-{guid}-{name}\""
|
22
25
|
option :values, aliases: ["-v"], required: false, type: :string, default: "",
|
23
|
-
|
26
|
+
desc: "Values for the template. 'guid' is the service instance guid e.g. \"name=test\""
|
24
27
|
option :org, aliases: ["-o"], required: true, type: :string,
|
25
|
-
|
28
|
+
desc: "Choose an organization (can be multiple comma-separated)"
|
26
29
|
option :name, aliases: ["-n"], type: :string, desc: "Choose a service instance name"
|
27
30
|
|
28
31
|
def call(*_args, **options)
|
29
32
|
validate_options(options)
|
33
|
+
JCF.plugin options[:iaas_plugin].to_sym
|
34
|
+
plugin = JCF::Plugins.plugins[options[:iaas_plugin].to_sym]
|
35
|
+
|
30
36
|
orgs = options[:org].include?(",") ? options[:org].split(",") : [options[:org]]
|
31
37
|
|
38
|
+
brokers = service_brokers.select { |b| b.name == options[:broker] }
|
39
|
+
# find the offerings for those brokers
|
40
|
+
offerings = service_offerings.select do |o|
|
41
|
+
brokers.find { |b| b.guid == o.relationships.service_broker.guid }
|
42
|
+
end
|
43
|
+
if offerings && offerings.empty?
|
44
|
+
err.puts "No offerings found for broker #{options[:broker]}"
|
45
|
+
exit(1)
|
46
|
+
end
|
47
|
+
err.puts "Found #{offerings.count} offerings"
|
48
|
+
|
32
49
|
orgs.each do |org|
|
33
50
|
org_guid = organizations.find { |o| o.name == org }.guid
|
34
51
|
err.puts "Found org guid: #{org_guid}"
|
35
52
|
|
36
|
-
|
37
|
-
err.puts "Found offering guid: #{offering_guid}"
|
38
|
-
|
39
|
-
unless offering_guid
|
40
|
-
err.puts "No offerings found for type #{options[:offering]}"
|
41
|
-
exit(1)
|
42
|
-
end
|
43
|
-
|
44
|
-
# find the plans for those gatherings
|
53
|
+
# find the plans for those offerings
|
45
54
|
plan_guids = service_plans.find_all do |plan|
|
46
|
-
plan.relationships.service_offering.guid
|
55
|
+
offerings.collect(&:guid).include? plan.relationships.service_offering.guid
|
47
56
|
end.collect(&:guid)
|
48
57
|
err.puts "Found plan guids: #{plan_guids.count}"
|
58
|
+
if plan_guids.empty?
|
59
|
+
err.puts "No plans found for offerings"
|
60
|
+
exit(1)
|
61
|
+
end
|
49
62
|
|
50
63
|
# look up the instances that match the plans and org
|
51
64
|
# "/v3/service_instances?organization_guids=${org_guids}&service_plan_guids=${plan_guids}"
|
@@ -56,11 +69,9 @@ module JCF
|
|
56
69
|
instances.select! { |i| i.name.include? options[:name] } if options[:name]
|
57
70
|
err.puts "Found instances: #{instances.count}"
|
58
71
|
|
59
|
-
|
60
|
-
values = []
|
72
|
+
values = {}
|
61
73
|
|
62
74
|
Thread.abort_on_exception = true
|
63
|
-
# use a the number of processors as the number of threads
|
64
75
|
instances.each_slice(Concurrent.processor_count) do |slice|
|
65
76
|
slice.collect do |instance|
|
66
77
|
service_plan = instance.relationships.service_plan.populate!
|
@@ -68,42 +79,42 @@ module JCF
|
|
68
79
|
service_broker = service_offering.relationships.service_broker.populate!
|
69
80
|
|
70
81
|
Thread.new do
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
82
|
+
metrics = {}
|
83
|
+
metrics[:name] = (instance.name || "")
|
84
|
+
metrics[:instance_guid] = instance.guid
|
85
|
+
err.puts "Getting metrics for #{instance.name}"
|
86
|
+
metrics[:region] = ENV["AWS_REGION"]
|
87
|
+
metrics[:organization] = org
|
88
|
+
metrics[:organization_guid] = org_guid
|
78
89
|
space = spaces.find { |s| s.guid == instance.relationships.space.guid }
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
90
|
+
metrics[:space] = space.name
|
91
|
+
metrics[:space_guid] = space.guid
|
92
|
+
metrics[:service_broker_name] = service_broker.name
|
93
|
+
metrics[:service_broker_guid] = service_broker.guid
|
94
|
+
metrics[:service_offering] = service_offering.name
|
95
|
+
metrics[:service_plan] = service_plan.name
|
85
96
|
|
86
97
|
template = options[:template]
|
87
98
|
t_values = parse_values(options[:values], instance.guid)
|
88
99
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
m.storage_free = to_gb(cw.storage_free(name: JCF::CLI.template_parser(template, t_values)) || "")
|
93
|
-
m.iops = (cw.iops(name: JCF::CLI.template_parser(template, t_values)).to_fs(:rounded, precision: 0) || "")
|
94
|
-
m.cpu = (cw.cpu(name: JCF::CLI.template_parser(template, t_values)).to_fs(:rounded, precision: 0) || "")
|
95
|
-
end
|
96
|
-
|
97
|
-
if service_offering.name == "aws-s3-bucket"
|
98
|
-
m.storage_used = to_gb(cw.s3_storage_used(name: JCF::CLI.template_parser(template, t_values)) || "")
|
100
|
+
t = JCF::CLI.template_parser(template, t_values)
|
101
|
+
plugin.new(name: t).metrics.each do |k, v|
|
102
|
+
metrics[k] = v
|
99
103
|
end
|
100
|
-
values
|
104
|
+
values[instance.guid] = metrics
|
101
105
|
end
|
102
|
-
end.
|
106
|
+
end.each(&:join)
|
103
107
|
end
|
104
108
|
|
105
|
-
values
|
106
|
-
|
109
|
+
# values = { guid: { name: "name", space: "space" }, guid2: { name: "name2", space: "space2" } }
|
110
|
+
output = Hash.new { |hash, key| hash[key] = [] }
|
111
|
+
values.each do |guid, metrics|
|
112
|
+
metrics.each do |k, v|
|
113
|
+
output[k] << v
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
out.puts formatter.format(data: output)
|
107
118
|
err.puts "Done."
|
108
119
|
end
|
109
120
|
end
|
@@ -141,13 +152,7 @@ module JCF
|
|
141
152
|
end
|
142
153
|
|
143
154
|
def service_brokers
|
144
|
-
JCF::CF::ServiceBroker.all
|
145
|
-
end
|
146
|
-
|
147
|
-
def to_gb(bytes)
|
148
|
-
Filesize.from("#{bytes} b").to("GB").to_fs(:rounded, precision: 2)
|
149
|
-
rescue ArgumentError
|
150
|
-
0
|
155
|
+
@service_brokers ||= JCF::CF::ServiceBroker.all
|
151
156
|
end
|
152
157
|
end
|
153
158
|
end
|
@@ -8,14 +8,17 @@ module JCF
|
|
8
8
|
module Commands
|
9
9
|
module CF
|
10
10
|
class Organizations < Command
|
11
|
+
include JCF::CF
|
12
|
+
|
11
13
|
argument :name, required: false, desc: "Organization name"
|
12
14
|
|
13
15
|
def call(name: nil, **)
|
14
|
-
if name
|
15
|
-
|
16
|
+
values = if name
|
17
|
+
Organization.find_by(name: name).collect(&:values)
|
16
18
|
else
|
17
|
-
|
19
|
+
Organization.all.collect(&:values)
|
18
20
|
end
|
21
|
+
out.puts formatter.format(headers: Organization.keys, values: values)
|
19
22
|
end
|
20
23
|
end
|
21
24
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JCF
|
4
|
+
module CLI
|
5
|
+
module Commands
|
6
|
+
class Plugins < Command
|
7
|
+
desc "Show JCF plugins"
|
8
|
+
|
9
|
+
def call(*)
|
10
|
+
out.puts "JCF Plugins:"
|
11
|
+
plugin_dir = File.join(JCF.root, "jcf", "plugins", "*.rb")
|
12
|
+
pp "plugin dir: #{plugin_dir}" if ENV["DEBUG"]
|
13
|
+
Dir[plugin_dir].each do |plugin|
|
14
|
+
out.puts " file: #{File.basename(plugin, ".rb")}" if ENV["DEBUG"]
|
15
|
+
JCF::Plugins.load_plugin(File.basename(plugin, ".rb").to_sym)
|
16
|
+
end
|
17
|
+
JCF::Plugins.plugins.each do |plugin|
|
18
|
+
out.puts " #{plugin}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/jcf/cli/commands.rb
CHANGED
@@ -7,6 +7,7 @@ module JCF
|
|
7
7
|
|
8
8
|
def self.register_commands!
|
9
9
|
register "version", Commands::Version, aliases: ["v", "-v", "--version"]
|
10
|
+
register "plugins", Commands::Plugins
|
10
11
|
register "metrics", Commands::CF::Metrics, aliases: ["m"]
|
11
12
|
|
12
13
|
register "organizations", Commands::CF::Organizations, aliases: %w[o orgs organization]
|
@@ -7,24 +7,12 @@ module JCF
|
|
7
7
|
module OutputFormatters
|
8
8
|
class CSV
|
9
9
|
class << self
|
10
|
-
def format(data)
|
11
|
-
|
12
|
-
in Array
|
13
|
-
array = data.collect(&:serializable_hash)
|
14
|
-
generate_csv(headers: array.first&.keys, values: array)
|
15
|
-
in Hash
|
16
|
-
generate_csv(headers: data.keys.sort, values: [data.serializable_hash])
|
17
|
-
else
|
18
|
-
generate_csv(headers: [], values: [])
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
10
|
+
def format(data:)
|
11
|
+
return "" if data.nil? || data.empty?
|
23
12
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
csv << hash.values
|
13
|
+
::CSV.generate(headers: data.keys, write_headers: true) do |csv|
|
14
|
+
data.values.transpose.each do |value|
|
15
|
+
csv << value
|
28
16
|
end
|
29
17
|
end
|
30
18
|
end
|
@@ -6,12 +6,10 @@ module JCF
|
|
6
6
|
module CLI
|
7
7
|
module OutputFormatters
|
8
8
|
class JSON
|
9
|
-
def self.format(data)
|
10
|
-
if data.
|
11
|
-
|
12
|
-
|
13
|
-
::JSON.generate data.serializable_hash
|
14
|
-
end
|
9
|
+
def self.format(data:)
|
10
|
+
return "{}" if data.nil? || data.empty?
|
11
|
+
|
12
|
+
data.to_json
|
15
13
|
end
|
16
14
|
end
|
17
15
|
end
|
@@ -7,8 +7,8 @@ module JCF
|
|
7
7
|
module OutputFormatters
|
8
8
|
class Text
|
9
9
|
class << self
|
10
|
-
def format(data
|
11
|
-
return "" if data.nil?
|
10
|
+
def format(data:, tree: false)
|
11
|
+
return "" if data.nil? || data.empty?
|
12
12
|
|
13
13
|
if tree
|
14
14
|
render_tree(data)
|
@@ -17,6 +17,8 @@ module JCF
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
+
private
|
21
|
+
|
20
22
|
def render_data(data)
|
21
23
|
keys = collect_keys(data)
|
22
24
|
values = collect_values(data)
|
@@ -29,19 +31,30 @@ module JCF
|
|
29
31
|
TTY::Tree.new(data).render
|
30
32
|
end
|
31
33
|
|
32
|
-
def
|
34
|
+
def collect_keys(data)
|
35
|
+
return ["Empty result"] if !data || data.empty?
|
36
|
+
|
33
37
|
if data.is_a?(Array)
|
34
|
-
data.
|
38
|
+
data.first.keys.collect(&:to_s)
|
39
|
+
elsif data.is_a?(Hash)
|
40
|
+
data.keys.collect(&:to_s) || ["Empty result"]
|
35
41
|
else
|
36
|
-
|
42
|
+
data.keys.collect(&:to_s)
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
40
|
-
|
46
|
+
# Hash:
|
47
|
+
# values: [["name1", "name2"], ["space1", "space2"]]
|
48
|
+
# output: [["name1", "space1"], ["name2", "space2"]]
|
49
|
+
def collect_values(data)
|
50
|
+
return [["Empty result"]] if !data || data.empty?
|
51
|
+
|
41
52
|
if data.is_a?(Array)
|
42
|
-
data.
|
53
|
+
data.map { |d| d.attributes.values.collect(&:to_s) }
|
54
|
+
elsif data.is_a?(Hash)
|
55
|
+
data.values.transpose
|
43
56
|
else
|
44
|
-
data.attributes.
|
57
|
+
[data.attributes.values.collect(&:to_s)]
|
45
58
|
end
|
46
59
|
end
|
47
60
|
end
|
data/lib/jcf/cli.rb
CHANGED
@@ -14,6 +14,50 @@ module JCF
|
|
14
14
|
@cache ||= MiniCache::Store.new
|
15
15
|
end
|
16
16
|
|
17
|
+
def self.plugin(plugin)
|
18
|
+
plugin = JCF::Plugins.load_plugin(plugin) if plugin.is_a?(Symbol)
|
19
|
+
validate_plugin!(plugin)
|
20
|
+
plugin.load_dependencies(self, *args, &block) if plugin.respond_to?(:load_dependencies)
|
21
|
+
plugin.configure(self, *args, &block) if plugin.respond_to?(:configure)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.validate_plugin!(plugin)
|
25
|
+
puts "Validating plugin (#{plugin}) implementation conforms to interface" if ENV["DEBUG"]
|
26
|
+
|
27
|
+
%i[metrics names values].each do |method|
|
28
|
+
raise "Plugin does not conform to interface (missing method \"#{method.to_s}\")" \
|
29
|
+
unless plugin.new(name: nil).respond_to?(method)
|
30
|
+
end
|
31
|
+
rescue JCF::CLI::NotLoggedInError => e
|
32
|
+
puts e.message
|
33
|
+
exit 1
|
34
|
+
end
|
35
|
+
|
36
|
+
module Plugins
|
37
|
+
@plugins = {}
|
38
|
+
|
39
|
+
def self.plugins
|
40
|
+
@plugins
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.load_plugin(name)
|
44
|
+
return @plugins[name] if @plugins[name]
|
45
|
+
|
46
|
+
puts "Loading plugin #{name}" if ENV["DEBUG"]
|
47
|
+
require "jcf/plugins/#{name}"
|
48
|
+
raise "Plugin didn't correctly register itself" unless @plugins[name]
|
49
|
+
@plugins[name]
|
50
|
+
end
|
51
|
+
|
52
|
+
# Plugins need to call this method to register themselves:
|
53
|
+
#
|
54
|
+
# JCF::Plugins.register_plugin :render, Render
|
55
|
+
def self.register_plugin(name, mod)
|
56
|
+
puts "Registering plugin #{name}" if ENV["DEBUG"]
|
57
|
+
@plugins[name] = mod
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
17
61
|
module CLI
|
18
62
|
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
19
63
|
def self.loader
|
@@ -59,7 +103,9 @@ module JCF
|
|
59
103
|
|
60
104
|
extend Dry::CLI::Registry
|
61
105
|
|
106
|
+
puts "Loading formatters..." if ENV["DEBUG"]
|
62
107
|
register_formatters!
|
108
|
+
puts "Loading commands..." if ENV["DEBUG"]
|
63
109
|
register_commands!
|
64
110
|
end
|
65
111
|
end
|
data/lib/jcf/metric.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "aws-sdk-cloudwatch"
|
4
|
+
require "aws-sdk-rds"
|
5
|
+
require "active_support/core_ext/integer/time"
|
6
|
+
require "active_support/isolated_execution_state"
|
7
|
+
require "filesize"
|
8
|
+
|
9
|
+
module JCF
|
10
|
+
module Plugins
|
11
|
+
class AwsRds
|
12
|
+
def initialize(name:)
|
13
|
+
@name = name
|
14
|
+
@metrics = {}
|
15
|
+
check_token!
|
16
|
+
end
|
17
|
+
|
18
|
+
def names
|
19
|
+
%w[storage_used storage_allocated storage_free iops cpu]
|
20
|
+
end
|
21
|
+
|
22
|
+
def values
|
23
|
+
names.map { |name| send(name.to_sym) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def metrics
|
27
|
+
@metrics[:storage_used] = to_gb(storage_used(name: @name || ""))
|
28
|
+
@metrics[:storage_allocated] = to_gb(storage_allocated(name: @name) || "")
|
29
|
+
@metrics[:storage_free] = to_gb(storage_free(name: @name) || "")
|
30
|
+
@metrics[:iops] = (iops(name: @name).to_fs(:rounded, precision: 0) || "")
|
31
|
+
@metrics[:cpu] = (cpu(name: @name).to_fs(:rounded, precision: 0) || "")
|
32
|
+
@metrics
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
metrics.to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def check_token!
|
42
|
+
Aws::STS::Client.new.get_caller_identity({})
|
43
|
+
rescue Aws::Errors::MissingCredentialsError
|
44
|
+
raise JCF::CLI::NotLoggedInError, "Your AWS token has expired. Please log in again."
|
45
|
+
end
|
46
|
+
|
47
|
+
METRICS = {
|
48
|
+
free_storage: "FreeStorageSpace",
|
49
|
+
allocated_storage: "AllocatedStorage",
|
50
|
+
iops: "WriteIOPS",
|
51
|
+
cpu: "CPUUtilization"
|
52
|
+
}.freeze
|
53
|
+
|
54
|
+
def storage_free(name:)
|
55
|
+
storage_free = cloudwatch(name: name, metric: "FreeStorageSpace")
|
56
|
+
storage_free.to_i
|
57
|
+
end
|
58
|
+
|
59
|
+
def storage_allocated(name:)
|
60
|
+
rds = Aws::RDS::Client.new
|
61
|
+
size = rds
|
62
|
+
.describe_db_instances(db_instance_identifier: name)
|
63
|
+
.db_instances.first.allocated_storage
|
64
|
+
Filesize.from("#{size} GB").to_i
|
65
|
+
end
|
66
|
+
|
67
|
+
def storage_used(name:)
|
68
|
+
free = storage_free(name: name)
|
69
|
+
allocated = storage_allocated(name: name)
|
70
|
+
allocated - free
|
71
|
+
end
|
72
|
+
|
73
|
+
def iops(name:)
|
74
|
+
cloudwatch(name: name, metric: :iops)
|
75
|
+
end
|
76
|
+
|
77
|
+
def cpu(name:)
|
78
|
+
cloudwatch(name: name, metric: :cpu)
|
79
|
+
end
|
80
|
+
|
81
|
+
# rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
82
|
+
def cloudwatch(name:, metric:)
|
83
|
+
cloudwatch = Aws::CloudWatch::Client.new
|
84
|
+
res = cloudwatch.get_metric_statistics({
|
85
|
+
namespace: "AWS/RDS",
|
86
|
+
metric_name: metric,
|
87
|
+
dimensions: [{ name: "DBInstanceIdentifier", value: name }],
|
88
|
+
start_time: 1.day.ago,
|
89
|
+
end_time: Time.now,
|
90
|
+
period: 86_400,
|
91
|
+
statistics: ["Average"]
|
92
|
+
})
|
93
|
+
|
94
|
+
pp res if ENV["DEBUG"]
|
95
|
+
|
96
|
+
res.datapoints.first&.average || 0
|
97
|
+
rescue Aws::Errors::MissingCredentialsError
|
98
|
+
puts "You are not logged in to an AWS shell. 'gds aws <ACCOUNT> -s'"
|
99
|
+
end
|
100
|
+
# rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
101
|
+
|
102
|
+
def to_gb(bytes)
|
103
|
+
Filesize.from("#{bytes} b").to("GB").to_fs(:rounded, precision: 2)
|
104
|
+
rescue ArgumentError
|
105
|
+
0
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
register_plugin :aws_rds, AwsRds
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "aws-sdk-s3"
|
4
|
+
require "active_support/core_ext/integer/time"
|
5
|
+
require "active_support/isolated_execution_state"
|
6
|
+
require "filesize"
|
7
|
+
|
8
|
+
module JCF
|
9
|
+
module Plugins
|
10
|
+
class AwsS3
|
11
|
+
def initialize(name:)
|
12
|
+
@name = name
|
13
|
+
@metrics = {}
|
14
|
+
check_token!
|
15
|
+
end
|
16
|
+
|
17
|
+
def names
|
18
|
+
%w[storage_used]
|
19
|
+
end
|
20
|
+
|
21
|
+
def values
|
22
|
+
names.map { |name| send(name.to_sym) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def metrics
|
26
|
+
@metrics[:storage_used] = to_gb(s3_storage_used(name: @name || ""))
|
27
|
+
@metrics
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
metrics.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def s3_storage_used(name:)
|
37
|
+
s3 = Aws::S3::Client.new
|
38
|
+
total_size = 0
|
39
|
+
response = s3.list_objects_v2(bucket: name)
|
40
|
+
response.contents.each do |object|
|
41
|
+
total_size += object.size
|
42
|
+
end
|
43
|
+
total_size
|
44
|
+
end
|
45
|
+
|
46
|
+
def check_token!
|
47
|
+
Aws::STS::Client.new.get_caller_identity({})
|
48
|
+
rescue Aws::Errors::MissingCredentialsError
|
49
|
+
raise JCF::CLI::NotLoggedInError, "Your AWS token has expired. Please log in again."
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_gb(bytes)
|
53
|
+
Filesize.from("#{bytes} b").to("GB").to_fs(:rounded, precision: 2)
|
54
|
+
rescue ArgumentError
|
55
|
+
0
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
register_plugin :aws_s3, AwsS3
|
60
|
+
end
|
61
|
+
end
|
data/lib/jcf/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jcf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jamie van Dyke
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-10-
|
11
|
+
date: 2023-10-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -279,7 +279,6 @@ files:
|
|
279
279
|
- lib/jcf/cf.rb
|
280
280
|
- lib/jcf/cf/.DS_Store
|
281
281
|
- lib/jcf/cf/base.rb
|
282
|
-
- lib/jcf/cf/metric.rb
|
283
282
|
- lib/jcf/cf/organization.rb
|
284
283
|
- lib/jcf/cf/quota.rb
|
285
284
|
- lib/jcf/cf/relationships.rb
|
@@ -304,12 +303,16 @@ files:
|
|
304
303
|
- lib/jcf/cli/commands/cf/services.rb
|
305
304
|
- lib/jcf/cli/commands/cf/spaces.rb
|
306
305
|
- lib/jcf/cli/commands/cf/users.rb
|
306
|
+
- lib/jcf/cli/commands/plugins.rb
|
307
307
|
- lib/jcf/cli/commands/version.rb
|
308
308
|
- lib/jcf/cli/errors.rb
|
309
309
|
- lib/jcf/cli/output_formatters.rb
|
310
310
|
- lib/jcf/cli/output_formatters/csv.rb
|
311
311
|
- lib/jcf/cli/output_formatters/json.rb
|
312
312
|
- lib/jcf/cli/output_formatters/text.rb
|
313
|
+
- lib/jcf/metric.rb
|
314
|
+
- lib/jcf/plugins/aws_rds.rb
|
315
|
+
- lib/jcf/plugins/aws_s3.rb
|
313
316
|
- lib/jcf/version.rb
|
314
317
|
- sig/jcf.rbs
|
315
318
|
homepage: https://github.com/fearoffish/paas-org-metric-gathering-gem
|
@@ -336,7 +339,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
336
339
|
- !ruby/object:Gem::Version
|
337
340
|
version: '0'
|
338
341
|
requirements: []
|
339
|
-
rubygems_version: 3.4.
|
342
|
+
rubygems_version: 3.4.20
|
340
343
|
signing_key:
|
341
344
|
specification_version: 4
|
342
345
|
summary: Gather metrics from AWS for CloudFoundry installations
|
data/lib/jcf/cf/metric.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "base"
|
4
|
-
|
5
|
-
module JCF
|
6
|
-
module CF
|
7
|
-
class Metric < Base
|
8
|
-
attr_accessor :name, :region, :deploy_env, :organization, :organization_guid, :space, :space_guid,
|
9
|
-
:service_broker_name, :service_broker_guid, :service_offering, :service_plan,
|
10
|
-
:storage_allocated, :storage_used, :storage_free, :iops, :cpu
|
11
|
-
|
12
|
-
# rubocop:disable Metrics/MethodLength
|
13
|
-
def attributes
|
14
|
-
{
|
15
|
-
name: name,
|
16
|
-
region: region,
|
17
|
-
organization: organization,
|
18
|
-
organization_guid: organization_guid,
|
19
|
-
space: space,
|
20
|
-
space_guid: space_guid,
|
21
|
-
service_broker_name: service_broker_name,
|
22
|
-
service_broker_guid: service_broker_guid,
|
23
|
-
service_offering: service_offering,
|
24
|
-
service_plan: service_plan,
|
25
|
-
storage_used: storage_used,
|
26
|
-
storage_allocated: storage_allocated,
|
27
|
-
storage_free: storage_free,
|
28
|
-
iops: iops,
|
29
|
-
cpu: cpu
|
30
|
-
}
|
31
|
-
end
|
32
|
-
# rubocop:enable Metrics/MethodLength
|
33
|
-
|
34
|
-
def to_s
|
35
|
-
"#{name}: used(#{storage_used}) allocated(#{storage_allocated}) free(#{storage_free}) iops(#{iops}) cpu(#{cpu})"
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|