cluster-discovery 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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