cluster-discovery 0.1.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.
Files changed (32) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +9 -0
  5. data/.rubocop_todo.yml +62 -0
  6. data/Gemfile +26 -0
  7. data/Gemfile.lock +141 -0
  8. data/Guardfile +47 -0
  9. data/LICENSE +22 -0
  10. data/README.md +89 -0
  11. data/Rakefile +51 -0
  12. data/Thorfile +3 -0
  13. data/cluster-discovery.gemspec +26 -0
  14. data/lib/cluster/discovery/consul.rb +46 -0
  15. data/lib/cluster/discovery/ec2/auto_scaling.rb +27 -0
  16. data/lib/cluster/discovery/ec2/tag.rb +64 -0
  17. data/lib/cluster/discovery/errors.rb +21 -0
  18. data/lib/cluster/discovery/version.rb +6 -0
  19. data/lib/cluster/discovery.rb +38 -0
  20. data/spec/cassettes/Cluster_Discovery/_discover/can_discover_nodes/discovery_with_consul.yml +38 -0
  21. data/spec/cassettes/Cluster_Discovery_Consul/_discover/can_discover_all_nodes/can_discover_nodes_with_tags.yml +38 -0
  22. data/spec/cassettes/Cluster_Discovery_Consul/_discover/can_discover_all_nodes/can_discover_nodes_without_tags.yml +38 -0
  23. data/spec/cassettes/Cluster_Discovery_Consul/_discover/can_discover_leader_node/can_discover_nodes_with_tags.yml +38 -0
  24. data/spec/cassettes/Cluster_Discovery_Consul/_discover/can_discover_leader_node/can_discover_nodes_without_tags.yml +38 -0
  25. data/spec/cassettes/Cluster_Discovery_EC2_AutoScaling/_discover/can_reuse_the_tag_provider/can_find_instances_by_aws_auto_scaling_group.yml +191 -0
  26. data/spec/cassettes/Cluster_Discovery_EC2_Tag/_discover/passing_aws_region_arg/can_find_instances_by_tag.yml +862 -0
  27. data/spec/lib/cluster/discovery/consul_spec.rb +119 -0
  28. data/spec/lib/cluster/discovery/ec2/auto_scaling_spec.rb +19 -0
  29. data/spec/lib/cluster/discovery/ec2/tag_spec.rb +81 -0
  30. data/spec/lib/cluster/discovery_spec.rb +23 -0
  31. data/spec/spec_helper.rb +111 -0
  32. metadata +144 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 52dee8af31d3c92628213fc8dc3466aff557c9a1
4
+ data.tar.gz: 0d3d7aef4253ee018c4be0f05c8b08fba9a24a88
5
+ SHA512:
6
+ metadata.gz: 74624cd578d290a6023685cd4161272640fd8de962877cbe4941838218c62a462d2e5bcc5c7045ca00ed8aeef53beaa0453ffc9bebe1c57f3b019abc450c366e
7
+ data.tar.gz: 90b73cbcdb2f4c9af9eb5b5c1f404267966bcc27946c0f058ece3b00c9ed48a9826a6e19d7dfea2f30a3a3e560bcf235de70853c2d1ef22dd31ccfd40e1331fd
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.bundle
10
+ *.so
11
+ *.o
12
+ *.a
13
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,9 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ Metrics/MethodLength:
4
+ Exclude:
5
+ - 'Rakefile'
6
+
7
+ Metrics/LineLength:
8
+ Exclude:
9
+ - 'Rakefile'
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,62 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2016-01-06 15:20:00 -0500 using RuboCop version 0.35.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ # Cop supports --auto-correct.
11
+
12
+ Style/BlockComments:
13
+ Exclude:
14
+ - 'spec/spec_helper.rb'
15
+
16
+ # Offense count: 2
17
+ # Configuration parameters: Exclude.
18
+ Style/Documentation:
19
+ Exclude:
20
+ - 'spec/**/*'
21
+ - 'test/**/*'
22
+ - 'lib/cluster/discovery.rb'
23
+ - 'lib/cluster/discovery/version.rb'
24
+ - 'lib/**/*'
25
+
26
+ # Offense count: 2
27
+ # Cop supports --auto-correct.
28
+ Style/LeadingCommentSpace:
29
+ Exclude:
30
+ - 'Gemfile'
31
+
32
+ # Offense count: 2
33
+ # Cop supports --auto-correct.
34
+ # Configuration parameters: PreferredDelimiters.
35
+ Style/PercentLiteralDelimiters:
36
+ Exclude:
37
+ - 'cluster-discovery.gemspec'
38
+
39
+ # Offense count: 1
40
+ # Cop supports --auto-correct.
41
+ # Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
42
+ Style/RegexpLiteral:
43
+ Exclude:
44
+ - 'Guardfile'
45
+
46
+ # Offense count: 1
47
+ # Cop supports --auto-correct.
48
+ Style/RescueModifier:
49
+ Exclude:
50
+ - 'lib/cluster/discovery/version.rb'
51
+
52
+ # Offense count: 3
53
+ # Cop supports --auto-correct.
54
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
55
+ #Style/StringLiterals:
56
+ # Enabled: false
57
+
58
+ # Offense count: 2
59
+ # Cop supports --auto-correct.
60
+ Style/UnneededPercentQ:
61
+ Exclude:
62
+ - 'cluster-discovery.gemspec'
data/Gemfile ADDED
@@ -0,0 +1,26 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cluster-discovery.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem 'yard', '~> 0.8'
8
+ gem 'thor', '~> 0.19.1'
9
+ gem 'thor-scmversion', '= 1.7.0'
10
+
11
+ group :guard do
12
+ gem 'guard', '~> 2.13'
13
+ gem 'guard-rspec', '~> 4.6'
14
+ gem 'guard-rubocop', '~> 1.2'
15
+ gem 'terminal-notifier-guard', '~> 1.6'
16
+ end
17
+ end
18
+
19
+ group :development, :test do
20
+ gem 'rubocop', '~> 0.34'
21
+ gem 'simplecov', '~> 0.11'
22
+ gem 'vcr', '~> 3.0'
23
+ gem 'webmock', '~> 1.22'
24
+ gem 'rspec', '~> 3.4'
25
+ gem 'httparty', '~> 0.13'
26
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,141 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ cluster-discovery (0.0.0)
5
+ aws-sdk (~> 2.2)
6
+ diplomat (~> 0.15)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ addressable (2.3.8)
12
+ ast (2.2.0)
13
+ astrolabe (1.3.1)
14
+ parser (~> 2.2)
15
+ aws-sdk (2.2.9)
16
+ aws-sdk-resources (= 2.2.9)
17
+ aws-sdk-core (2.2.9)
18
+ jmespath (~> 1.0)
19
+ aws-sdk-resources (2.2.9)
20
+ aws-sdk-core (= 2.2.9)
21
+ coderay (1.1.0)
22
+ crack (0.4.3)
23
+ safe_yaml (~> 1.0.0)
24
+ diff-lcs (1.2.5)
25
+ diplomat (0.15.0)
26
+ faraday (~> 0.9)
27
+ json (~> 1.8)
28
+ docile (1.1.5)
29
+ faraday (0.9.2)
30
+ multipart-post (>= 1.2, < 3)
31
+ ffi (1.9.10)
32
+ formatador (0.2.5)
33
+ guard (2.13.0)
34
+ formatador (>= 0.2.4)
35
+ listen (>= 2.7, <= 4.0)
36
+ lumberjack (~> 1.0)
37
+ nenv (~> 0.1)
38
+ notiffany (~> 0.0)
39
+ pry (>= 0.9.12)
40
+ shellany (~> 0.0)
41
+ thor (>= 0.18.1)
42
+ guard-compat (1.2.1)
43
+ guard-rspec (4.6.4)
44
+ guard (~> 2.1)
45
+ guard-compat (~> 1.1)
46
+ rspec (>= 2.99.0, < 4.0)
47
+ guard-rubocop (1.2.0)
48
+ guard (~> 2.0)
49
+ rubocop (~> 0.20)
50
+ hashdiff (0.2.3)
51
+ httparty (0.13.7)
52
+ json (~> 1.8)
53
+ multi_xml (>= 0.5.2)
54
+ jmespath (1.1.3)
55
+ json (1.8.3)
56
+ listen (3.0.5)
57
+ rb-fsevent (>= 0.9.3)
58
+ rb-inotify (>= 0.9)
59
+ lumberjack (1.0.10)
60
+ method_source (0.8.2)
61
+ mixlib-shellout (2.2.5)
62
+ multi_xml (0.5.5)
63
+ multipart-post (2.0.0)
64
+ nenv (0.2.0)
65
+ notiffany (0.0.8)
66
+ nenv (~> 0.1)
67
+ shellany (~> 0.0)
68
+ parser (2.2.3.0)
69
+ ast (>= 1.1, < 3.0)
70
+ powerpack (0.1.1)
71
+ pry (0.10.3)
72
+ coderay (~> 1.1.0)
73
+ method_source (~> 0.8.1)
74
+ slop (~> 3.4)
75
+ rainbow (2.0.0)
76
+ rake (10.4.2)
77
+ rb-fsevent (0.9.7)
78
+ rb-inotify (0.9.5)
79
+ ffi (>= 0.5.0)
80
+ rspec (3.4.0)
81
+ rspec-core (~> 3.4.0)
82
+ rspec-expectations (~> 3.4.0)
83
+ rspec-mocks (~> 3.4.0)
84
+ rspec-core (3.4.1)
85
+ rspec-support (~> 3.4.0)
86
+ rspec-expectations (3.4.0)
87
+ diff-lcs (>= 1.2.0, < 2.0)
88
+ rspec-support (~> 3.4.0)
89
+ rspec-mocks (3.4.0)
90
+ diff-lcs (>= 1.2.0, < 2.0)
91
+ rspec-support (~> 3.4.0)
92
+ rspec-support (3.4.1)
93
+ rubocop (0.35.1)
94
+ astrolabe (~> 1.3)
95
+ parser (>= 2.2.3.0, < 3.0)
96
+ powerpack (~> 0.1)
97
+ rainbow (>= 1.99.1, < 3.0)
98
+ ruby-progressbar (~> 1.7)
99
+ tins (<= 1.6.0)
100
+ ruby-progressbar (1.7.5)
101
+ safe_yaml (1.0.4)
102
+ shellany (0.0.1)
103
+ simplecov (0.11.1)
104
+ docile (~> 1.1.0)
105
+ json (~> 1.8)
106
+ simplecov-html (~> 0.10.0)
107
+ simplecov-html (0.10.0)
108
+ slop (3.6.0)
109
+ terminal-notifier-guard (1.6.4)
110
+ thor (0.19.1)
111
+ thor-scmversion (1.7.0)
112
+ mixlib-shellout
113
+ thor
114
+ tins (1.6.0)
115
+ vcr (3.0.1)
116
+ webmock (1.22.5)
117
+ addressable (< 2.4.0)
118
+ crack (>= 0.3.2)
119
+ hashdiff
120
+ yard (0.8.7.6)
121
+
122
+ PLATFORMS
123
+ ruby
124
+
125
+ DEPENDENCIES
126
+ bundler (~> 1.7)
127
+ cluster-discovery!
128
+ guard (~> 2.13)
129
+ guard-rspec (~> 4.6)
130
+ guard-rubocop (~> 1.2)
131
+ httparty (~> 0.13)
132
+ rake (~> 10.0)
133
+ rspec (~> 3.4)
134
+ rubocop (~> 0.34)
135
+ simplecov (~> 0.11)
136
+ terminal-notifier-guard (~> 1.6)
137
+ thor (~> 0.19.1)
138
+ thor-scmversion (= 1.7.0)
139
+ vcr (~> 3.0)
140
+ webmock (~> 1.22)
141
+ yard (~> 0.8)
data/Guardfile ADDED
@@ -0,0 +1,47 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ # Note: The cmd option is now required due to the increasing number of ways
19
+ # rspec may be run, below are examples of the most common uses.
20
+ # * bundler: 'bundle exec rspec'
21
+ # * bundler binstubs: 'bin/rspec'
22
+ # * spring: 'bin/rspec' (This will use spring if running and you have
23
+ # installed the spring binstubs per the docs)
24
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
25
+ # * 'just' rspec: 'rspec'
26
+
27
+ guard :rspec, cmd: 'bundle exec rspec' do
28
+ require 'guard/rspec/dsl'
29
+ dsl = Guard::RSpec::Dsl.new(self)
30
+
31
+ # Feel free to open issues for suggestions and improvements
32
+
33
+ # RSpec files
34
+ rspec = dsl.rspec
35
+ watch(rspec.spec_helper) { rspec.spec_dir }
36
+ watch(rspec.spec_support) { rspec.spec_dir }
37
+ watch(rspec.spec_files)
38
+
39
+ # Ruby files
40
+ ruby = dsl.ruby
41
+ dsl.watch_spec_files_for(ruby.lib_files)
42
+ end
43
+
44
+ guard :rubocop do
45
+ watch(%r{.+\.rb$})
46
+ watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
47
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 Andrew Thompson, Rapid7 LLC.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # Cluster::Discovery
2
+
3
+ This is a library that provides a generic interface to multiple cluster providers.
4
+
5
+ ## Cluster Providers
6
+ Cluster providers are something that provides metadata about a collection of hosts in a cluster. Supported providers are as follows:
7
+
8
+ * [x] EC2 Tags
9
+ * [x] EC2 AutoScaling Groups
10
+ * [ ] Consul Services
11
+ * [ ] Static Lists
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ gem 'cluster-discovery'
19
+ ```
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install cluster-discovery
28
+
29
+ ## Usage
30
+
31
+ ### EC2 Tags
32
+
33
+ To discover cluster instances with EC2 Tags use something like the following example. The keys `aws_region`, `aws_tags`(`key`, `value`) are all required.
34
+
35
+ This returns an Array of EC2 Instance Objects
36
+
37
+ ```ruby
38
+ instances = Cluster::Discovery.discover(
39
+ 'ec2_tag',
40
+ aws_region: 'us-east-1',
41
+ aws_tags: [{ key: 'Service', value: 'router' }])
42
+
43
+ instances.map(&:instance_id)
44
+ ```
45
+
46
+ ### AutoScaling Groups
47
+
48
+ To discover cluster instances by AutoScaling Group use something like the following example. The keys `aws_region`, `aws_asg` are all required.
49
+
50
+ ```ruby
51
+ instances = Cluster::Discovery.discover(
52
+ 'ec2_asg',
53
+ aws_region: 'us-east-1',
54
+ aws_asg: 'foo-prod-v000')
55
+
56
+ instances.map(&:instance_id)
57
+ ```
58
+
59
+ ### Consul
60
+
61
+ To discover cluster instances using Consul use something like the following example. The keys `consul_url` and `consul_service` are required, `leader` and `tags` are optional.
62
+
63
+ ```ruby
64
+ instances = Cluster::Discovery.discover(
65
+ 'consul',
66
+ consul_url: 'http://my.consul.cluster:8500',
67
+ consul_service: 'redis',
68
+ leader: true,
69
+ tags: 'master')
70
+ instances.map(&:Address)
71
+ ```
72
+
73
+ ## Contributing
74
+
75
+ ### Running the tests
76
+
77
+ ```bash
78
+ TEST_CONSUL_HOST=my.consul.cluster rake spec
79
+ ```
80
+ Or just:
81
+ ```bash
82
+ rake spec
83
+ ```
84
+
85
+ 1. Fork it ( https://github.com/rapid7/cluster-discovery/fork )
86
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
87
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
88
+ 4. Push to the branch (`git push origin my-new-feature`)
89
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rubocop/rake_task'
3
+ require 'rspec/core/rake_task'
4
+ require 'httparty'
5
+ require 'logger'
6
+
7
+ @my_logger = Logger.new(STDOUT)
8
+
9
+ RuboCop::RakeTask.new
10
+ RSpec::Core::RakeTask.new(:spec)
11
+
12
+ def register_service(id:, ip:, tag:, status:)
13
+ json_doc = format(%(
14
+ {
15
+ "Datacenter": "dc1",
16
+ "Node": "foobar%{id}",
17
+ "Address": "%{ip}",
18
+ "Service": {
19
+ "ID": "redis%{id}",
20
+ "Service": "redis",
21
+ "Tags": ["%{tag}", "v1"],
22
+ "Address": "127.0.0.1",
23
+ "Port": 8000
24
+ },
25
+ "Check": {
26
+ "Node": "foobar%{id}",
27
+ "CheckID": "service:redis1",
28
+ "Name": "Redis health check",
29
+ "Notes": "Script based health check",
30
+ "Status": "%{status}",
31
+ "ServiceID": "redis%{id}"
32
+ }
33
+ }
34
+ ), id: id, ip: ip, tag: tag, status: status)
35
+
36
+ consul_host = ENV['TEST_CONSUL_HOST']
37
+ options = { logger: @my_logger, log_level: :debug, log_format: :curl }
38
+ options[:body] = json_doc
39
+ HTTParty.put("http://#{consul_host}:8500/v1/catalog/register", options)
40
+ end
41
+
42
+ task :refresh_fixtures do
43
+ register_service(id: '1', ip: '192.168.10.10', tag: 'master', status: 'passing')
44
+ register_service(id: '2', ip: '192.168.10.11', tag: 'slave', status: 'passing')
45
+ register_service(id: '3', ip: '192.168.10.12', tag: 'master', status: 'passing')
46
+ register_service(id: '4', ip: '192.168.10.13', tag: 'slave', status: 'passing')
47
+ register_service(id: '5', ip: '192.168.10.14', tag: 'master', status: 'passing')
48
+ register_service(id: '6', ip: '192.168.10.15', tag: 'slave', status: 'passing')
49
+ end
50
+
51
+ task default: [:spec, :rubocop]
data/Thorfile ADDED
@@ -0,0 +1,3 @@
1
+ require 'bundler'
2
+ require 'bundler/setup'
3
+ require 'thor/scmversion'
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cluster/discovery/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'cluster-discovery'
8
+ spec.version = Cluster::Discovery::VERSION
9
+ spec.authors = ['Andrew Thompson']
10
+ spec.email = ['Andrew_Thompson@rapid7.com']
11
+ spec.summary = 'Cluster Discovery Library'
12
+ spec.description = %q{This is a library that provides a generic interface to multiple cluster providers. Cluster providers include: EC2 Tags, EC2 AutoScaling Groups, and Consul}
13
+ spec.homepage = ''
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.7'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+
24
+ spec.add_dependency 'aws-sdk', '~> 2.2'
25
+ spec.add_dependency 'diplomat', '~> 0.15'
26
+ end
@@ -0,0 +1,46 @@
1
+ module Cluster
2
+ module Discovery
3
+ class Consul
4
+ def initialize(consul_url: nil)
5
+ unless consul_url.nil?
6
+ Diplomat.configure do |config|
7
+ config.url = consul_url
8
+ end
9
+ end
10
+ @configuration = Diplomat.configuration
11
+ end
12
+
13
+ # Discovery nodes using a Consul cluster
14
+ #
15
+ # @param [String] consul_service: Name of the Consul service
16
+ # @param [Boolean] leader: (false) Fetch :all or or :first(the leader)
17
+ # @param [Array] tags: ([]) List of tags to limit discovery by
18
+ # @return [Struct] Object representing the Node in the service
19
+ def discover(consul_service:, leader: false, tags: [])
20
+ scope, options = build_extra_opts(leader: leader, tags: tags)
21
+ nodes = Diplomat::Service.get(
22
+ consul_service,
23
+ scope,
24
+ options
25
+ )
26
+ [nodes].flatten
27
+ end
28
+
29
+ # Build args for Diplomat::Service.get
30
+ #
31
+ # @param [Hash] args the options to pass to Diplomat::Service.get
32
+ # @option opts [Symbol] :leader Wheather or not to get only the leader
33
+ # @option opts [Symbol] :tags The tags to discover by
34
+ # @return [Array] The options to pass to Diplomat::Service.get
35
+ def build_extra_opts(args)
36
+ opts = []
37
+ leader = args[:leader] == true ? :first : :all
38
+ opts << leader
39
+ opts << { tag: args[:tags] } unless args[:tags].empty?
40
+ opts
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ require 'diplomat'
@@ -0,0 +1,27 @@
1
+ module Cluster
2
+ module Discovery
3
+ module EC2
4
+ class AutoScaling
5
+ def initialize(aws_region:)
6
+ @aws_region = aws_region
7
+ end
8
+
9
+ # Discover EC2 Instances by AutoScaling Group
10
+ #
11
+ # @param [String] aws_auto_scaling_group AutoScaling Group to limit
12
+ # discovery by
13
+ # @return [Array<Aws::EC2::Types::Instance>] Discovery result
14
+ def discover(aws_asg:)
15
+ fail EmptyASGError if aws_asg.nil?
16
+ tags = Cluster::Discovery::EC2::Tag.new(aws_region: @aws_region)
17
+ tags.discover(aws_tags: [
18
+ {
19
+ key: 'aws:autoscaling:groupName',
20
+ value: aws_asg
21
+ }
22
+ ])
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,64 @@
1
+ module Cluster
2
+ module Discovery
3
+ module EC2
4
+ class Tag
5
+ # @!attribute [r]
6
+ # The EC2 Client Object
7
+ attr_reader :ec2_client
8
+
9
+ # Initialize the EC2 Client object
10
+ #
11
+ # @param [String] aws_region: The aws region
12
+ # @return [Aws::EC2::Client] The EC2 Client object
13
+ def initialize(aws_region:)
14
+ @ec2_client ||= Aws::EC2::Client.new(region: aws_region)
15
+ end
16
+
17
+ # Discover EC2 Instances by Tag
18
+ #
19
+ # @param [Array<Hash>] aws_tags AWS Tags to limit discovery
20
+ # @option aws_tags [String] :key The name of the tag
21
+ # @option aws_tags [Array<String>] :values The value(s) of the tag
22
+ # @example For example, aws_tags might look something like this:
23
+ # [
24
+ # { key: "tag:Service", values: ["MyService"] },
25
+ # { key: "tag:Purpose", values: ["MyPurpose"] }
26
+ # ]
27
+ # @return [Array<Aws::EC2::Types::Instance>] Array of EC2
28
+ # Instance objects
29
+ def discover(aws_tags: [])
30
+ fail EmptyTagsError if aws_tags.empty?
31
+ discover_instances_by_tags(build_tags(aws_tags))
32
+ end
33
+
34
+ private
35
+
36
+ def default_tags
37
+ { name: 'instance-state-name', values: ['running'] }
38
+ end
39
+
40
+ def build_tags(tags = [])
41
+ unless tags.empty?
42
+ tag_keys = tags.map(&:keys).flatten.uniq
43
+ fail MalformedTagsError unless [:key, :value] == tag_keys
44
+ tags.map! do |t|
45
+ { name: "tag:#{t[:key]}", values: [t[:value]] }
46
+ end
47
+ end
48
+ tags << default_tags
49
+ tags.flatten
50
+ end
51
+
52
+ def discover_instances_by_tags(tags)
53
+ instances = ec2_client.describe_instances(
54
+ filters: tags).inject([]) do |a, page|
55
+ a << page.reservations.map(&:instances)
56
+ end
57
+ instances.flatten.compact
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ require 'aws-sdk'
@@ -0,0 +1,21 @@
1
+ module Cluster
2
+ module Discovery
3
+ class EmptyTagsError < RuntimeError
4
+ def message
5
+ 'Tags cannot be empty'
6
+ end
7
+ end
8
+
9
+ class MalformedTagsError < RuntimeError
10
+ def message
11
+ 'Missing or invald keys'
12
+ end
13
+ end
14
+
15
+ class EmptyASGError < RuntimeError
16
+ def message
17
+ 'Must be a vaild AutoScaling Group'
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,6 @@
1
+ module Cluster
2
+ module Discovery
3
+ PATH = File.expand_path('../../../../VERSION', __FILE__)
4
+ VERSION = IO.read(PATH) rescue '0.0.1'
5
+ end
6
+ end