MovableInkAWS 1.3.1 → 2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd2763ea3f172fa051b72e65e9a3ef722ef961e67ca43fac2195f5a9acfeb881
4
- data.tar.gz: 7100767e992eacbd67763ea5ab3c221cf70787d6e36aa9732ee6372d0a27937e
3
+ metadata.gz: cf12982879cf6c6fbbd4a1027aa6de717c3084431339ad583cd4ae38d934bc91
4
+ data.tar.gz: e1baa1af0dc4a999bcba1ee421edc4204465afaadbc72a3190e97c57a948c1de
5
5
  SHA512:
6
- metadata.gz: 64807199ef1e1aba116ebd9bfac37a0395e32935cca99172a0a82865249b6a4586f452ffe9ccd45733e9354cedef6479967f95d35375c4baf530835eb6b4823c
7
- data.tar.gz: aeb957ad8595c852a7f97ca771d766f9f943663dc7db3eb2cdb2e0c9189c627bac4c197609ef2706b499dd33f11deedf9f45a19b99633d9b77261235ded530e5
6
+ metadata.gz: 567ef63e82f5d5bbf5b57b9b2aa5a853a63af0d572f710e813c322d135fef345456fc9cc3e3f103666f05065d244f67055ea78350f9b4ecec111b159663d2616
7
+ data.tar.gz: 2712730cb9852affc31a1586d36eba62b76a621d8e82dad39d8439bb0f47db72e897147666e5ea01ca2f75c7c4aeb3f5363f60b531dabd07f90e96813a846cb1
@@ -1,7 +1,6 @@
1
1
  language: ruby
2
2
 
3
3
  rvm:
4
- - 2.5
5
4
  - 2.6
6
5
 
7
6
  bundler_args: "--retry=3"
data/Gemfile CHANGED
@@ -3,4 +3,5 @@ gemspec
3
3
 
4
4
  group :test do
5
5
  gem 'rspec', '~> 3.6'
6
+ gem 'webmock'
6
7
  end
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- MovableInkAWS (1.3.1)
4
+ MovableInkAWS (2.1.0)
5
5
  aws-sdk-athena (~> 1)
6
6
  aws-sdk-autoscaling (~> 1)
7
7
  aws-sdk-cloudwatch (~> 1)
@@ -15,66 +15,80 @@ PATH
15
15
  aws-sdk-sns (~> 1)
16
16
  aws-sdk-ssm (~> 1)
17
17
  aws-sigv4 (~> 1.1)
18
+ diplomat (= 2.4.2)
18
19
  httparty (= 0.16.3)
19
20
 
20
21
  GEM
21
22
  remote: https://rubygems.org/
22
23
  specs:
24
+ addressable (2.7.0)
25
+ public_suffix (>= 2.0.2, < 5.0)
23
26
  aws-eventstream (1.1.0)
24
- aws-partitions (1.378.0)
27
+ aws-partitions (1.416.0)
25
28
  aws-sdk-athena (1.33.0)
26
29
  aws-sdk-core (~> 3, >= 3.109.0)
27
30
  aws-sigv4 (~> 1.1)
28
- aws-sdk-autoscaling (1.47.0)
31
+ aws-sdk-autoscaling (1.53.0)
29
32
  aws-sdk-core (~> 3, >= 3.109.0)
30
33
  aws-sigv4 (~> 1.1)
31
- aws-sdk-cloudwatch (1.45.0)
34
+ aws-sdk-cloudwatch (1.47.0)
32
35
  aws-sdk-core (~> 3, >= 3.109.0)
33
36
  aws-sigv4 (~> 1.1)
34
- aws-sdk-core (3.109.0)
37
+ aws-sdk-core (3.111.0)
35
38
  aws-eventstream (~> 1, >= 1.0.2)
36
39
  aws-partitions (~> 1, >= 1.239.0)
37
40
  aws-sigv4 (~> 1.1)
38
41
  jmespath (~> 1.0)
39
- aws-sdk-ec2 (1.198.0)
42
+ aws-sdk-ec2 (1.220.0)
40
43
  aws-sdk-core (~> 3, >= 3.109.0)
41
44
  aws-sigv4 (~> 1.1)
42
- aws-sdk-eks (1.44.0)
45
+ aws-sdk-eks (1.46.0)
43
46
  aws-sdk-core (~> 3, >= 3.109.0)
44
47
  aws-sigv4 (~> 1.1)
45
- aws-sdk-elasticache (1.43.0)
48
+ aws-sdk-elasticache (1.50.0)
46
49
  aws-sdk-core (~> 3, >= 3.109.0)
47
50
  aws-sigv4 (~> 1.1)
48
- aws-sdk-kms (1.39.0)
51
+ aws-sdk-kms (1.41.0)
49
52
  aws-sdk-core (~> 3, >= 3.109.0)
50
53
  aws-sigv4 (~> 1.1)
51
- aws-sdk-rds (1.101.0)
54
+ aws-sdk-rds (1.110.0)
52
55
  aws-sdk-core (~> 3, >= 3.109.0)
53
56
  aws-sigv4 (~> 1.1)
54
- aws-sdk-route53 (1.44.0)
57
+ aws-sdk-route53 (1.45.0)
55
58
  aws-sdk-core (~> 3, >= 3.109.0)
56
59
  aws-sigv4 (~> 1.1)
57
- aws-sdk-s3 (1.82.0)
60
+ aws-sdk-s3 (1.87.0)
58
61
  aws-sdk-core (~> 3, >= 3.109.0)
59
62
  aws-sdk-kms (~> 1)
60
63
  aws-sigv4 (~> 1.1)
61
- aws-sdk-sns (1.32.0)
64
+ aws-sdk-sns (1.36.0)
62
65
  aws-sdk-core (~> 3, >= 3.109.0)
63
66
  aws-sigv4 (~> 1.1)
64
- aws-sdk-ssm (1.93.0)
67
+ aws-sdk-ssm (1.102.0)
65
68
  aws-sdk-core (~> 3, >= 3.109.0)
66
69
  aws-sigv4 (~> 1.1)
67
70
  aws-sigv4 (1.2.2)
68
71
  aws-eventstream (~> 1, >= 1.0.2)
72
+ crack (0.4.3)
73
+ safe_yaml (~> 1.0.0)
74
+ deep_merge (1.2.1)
69
75
  diff-lcs (1.3)
76
+ diplomat (2.4.2)
77
+ deep_merge (~> 1.0, >= 1.0.1)
78
+ faraday (>= 0.9, < 1.1.0)
79
+ faraday (1.0.1)
80
+ multipart-post (>= 1.2, < 3)
81
+ hashdiff (1.0.0)
70
82
  httparty (0.16.3)
71
83
  mime-types (~> 3.0)
72
84
  multi_xml (>= 0.5.2)
73
85
  jmespath (1.4.0)
74
86
  mime-types (3.3.1)
75
87
  mime-types-data (~> 3.2015)
76
- mime-types-data (3.2020.0512)
88
+ mime-types-data (3.2020.1104)
77
89
  multi_xml (0.6.0)
90
+ multipart-post (2.1.1)
91
+ public_suffix (4.0.5)
78
92
  rspec (3.9.0)
79
93
  rspec-core (~> 3.9.0)
80
94
  rspec-expectations (~> 3.9.0)
@@ -88,6 +102,11 @@ GEM
88
102
  diff-lcs (>= 1.2.0, < 2.0)
89
103
  rspec-support (~> 3.9.0)
90
104
  rspec-support (3.9.3)
105
+ safe_yaml (1.0.5)
106
+ webmock (3.7.6)
107
+ addressable (>= 2.3.6)
108
+ crack (>= 0.3.2)
109
+ hashdiff (>= 0.4.0, < 2.0.0)
91
110
 
92
111
  PLATFORMS
93
112
  ruby
@@ -95,6 +114,7 @@ PLATFORMS
95
114
  DEPENDENCIES
96
115
  MovableInkAWS!
97
116
  rspec (~> 3.6)
117
+ webmock
98
118
 
99
119
  BUNDLED WITH
100
120
  2.1.3
@@ -23,6 +23,7 @@ Gem::Specification.new do |s|
23
23
  s.add_runtime_dependency 'aws-sdk-ssm', '~> 1'
24
24
  s.add_runtime_dependency 'aws-sigv4', '~> 1.1'
25
25
  s.add_runtime_dependency 'httparty', '0.16.3'
26
+ s.add_runtime_dependency 'diplomat', '2.4.2'
26
27
 
27
28
  all_files = `git ls-files`.split("\n")
28
29
  test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -10,6 +10,7 @@ require_relative 'aws/s3'
10
10
  require_relative 'aws/eks'
11
11
  require_relative 'aws/elasticache'
12
12
  require_relative 'aws/api_gateway'
13
+ require_relative 'consul/consul'
13
14
  require 'aws-sdk-cloudwatch'
14
15
 
15
16
 
@@ -1,4 +1,5 @@
1
1
  require 'aws-sdk-ec2'
2
+ require 'diplomat'
2
3
 
3
4
  module MovableInk
4
5
  class AWS
@@ -75,7 +76,7 @@ module MovableInk
75
76
  @me ||= all_instances.select{|instance| instance.instance_id == instance_id}.first rescue nil
76
77
  end
77
78
 
78
- def instances(role:, exclude_roles: [], region: my_region, availability_zone: nil, exact_match: false, use_cache: true)
79
+ def instances_with_ec2_discovery(role:, exclude_roles: [], region: my_region, availability_zone: nil, exact_match: false, use_cache: true)
79
80
  roles = role.split(/\s*,\s*/)
80
81
  if use_cache == false
81
82
  filter = default_filter.push({
@@ -106,6 +107,59 @@ module MovableInk
106
107
  end
107
108
  end
108
109
 
110
+ def instances_with_consul_discovery(role:, region: my_region, availability_zone: nil)
111
+ if role == nil || role == ''
112
+ raise MovableInk::AWS::Errors::RoleNameRequiredError
113
+ end
114
+
115
+ if role.include?('_')
116
+ raise MovableInk::AWS::Errors::RoleNameInvalidError
117
+ end
118
+
119
+ consul_service_options = {
120
+ dc: datacenter(region: region),
121
+ stale: true,
122
+ cached: true,
123
+ passing: true,
124
+ }
125
+ consul_service_options[:node_meta] = "availability_zone:#{availability_zone}" unless availability_zone.nil?
126
+
127
+ Diplomat::Health.service(role, consul_service_options).map { |endpoint|
128
+ OpenStruct.new (
129
+ {
130
+ private_ip_address: endpoint.Node['Address'],
131
+ instance_id: endpoint.Node['Meta']['instance_id'],
132
+ tags: [
133
+ {
134
+ key: 'Name',
135
+ value: endpoint.Node['Node']
136
+ },
137
+ {
138
+ key: 'mi:roles',
139
+ value: endpoint.Node['Meta']['mi_roles']
140
+ },
141
+ {
142
+ key: 'mi:monitoring_roles',
143
+ value: endpoint.Node['Meta']['mi_monitoring_roles']
144
+ }
145
+ ],
146
+ placement: {
147
+ availability_zone: endpoint.Node['Meta']['availability_zone']
148
+ }
149
+ })
150
+ }
151
+ end
152
+
153
+ def instances(role:, exclude_roles: [], region: my_region, availability_zone: nil, exact_match: false, use_cache: true, discovery_type: 'ec2')
154
+ if discovery_type == 'ec2'
155
+ instances_with_ec2_discovery(role: role, exclude_roles: exclude_roles, region: region, availability_zone: availability_zone, exact_match: exact_match, use_cache: use_cache)
156
+ elsif discovery_type == 'consul'
157
+ instances_with_consul_discovery(role: role, region: region, availability_zone: availability_zone)
158
+ else
159
+ raise MovableInk::AWS::Errors::InvalidDiscoveryTypeError
160
+ end
161
+ end
162
+
109
163
  def thopter
110
164
  private_ip_addresses(thopter_instance).first
111
165
  end
@@ -5,6 +5,9 @@ module MovableInk
5
5
  class FailedWithBackoff < StandardError; end
6
6
  class EC2Required < StandardError; end
7
7
  class NoEnvironmentTagError < StandardError; end
8
+ class InvalidDiscoveryTypeError < StandardError; end
9
+ class RoleNameRequiredError < StandardError; end
10
+ class RoleNameInvalidError < StandardError; end
8
11
  end
9
12
  end
10
13
  end
@@ -11,29 +11,20 @@ module MovableInk
11
11
  end
12
12
 
13
13
  def sns_slack_topic_arn
14
- run_with_backoff do
15
- sns.list_topics.each do |resp|
16
- resp.topics.each do |topic|
17
- return topic.topic_arn if topic.topic_arn.include? "slack-aws-alerts"
18
- end
19
- end
20
- end
14
+ @sns_slack_topic_arn ||= sns_topics.detect { |topic| topic.topic_arn.include? "slack-aws-alerts" }.topic_arn rescue nil
21
15
  end
22
16
 
23
17
  def sns_pagerduty_topic_arn
24
- run_with_backoff do
25
- sns.list_topics.each do |resp|
26
- resp.topics.each do |topic|
27
- return topic.topic_arn if topic.topic_arn.include? "pagerduty-custom-alerts"
28
- end
29
- end
30
- end
18
+ @sns_pagerduty_topic_arn ||= sns_topics.detect { |topic| topic.topic_arn.include? "pagerduty-custom-alerts" }.topic_arn rescue nil
19
+ end
20
+
21
+ def sns_topics
22
+ @sns_topics ||= run_with_backoff { sns.list_topics.flat_map(&:topics) }
31
23
  end
32
24
 
33
25
  def notify_and_sleep(seconds, error_class)
34
26
  message = "Throttled by AWS. Sleeping #{seconds} seconds, (#{error_class})"
35
- notify_slack(subject: 'API Throttled',
36
- message: message)
27
+ notify_slack(subject: 'API Throttled', message: message)
37
28
  puts message
38
29
  sleep seconds
39
30
  end
@@ -0,0 +1,21 @@
1
+ require 'diplomat'
2
+
3
+ Diplomat.configure do |config|
4
+ config.url = "https://localhost:8501"
5
+ config.options = { ssl: { verify: false } }
6
+ end
7
+
8
+ module MovableInk
9
+ module Consul
10
+ module Kv
11
+ def self.get(*args)
12
+ begin
13
+ result = Diplomat::Kv.get(*args)
14
+ JSON.parse(result) if !result.nil?
15
+ rescue JSON::ParserError
16
+ result
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,5 +1,5 @@
1
1
  module MovableInk
2
2
  class AWS
3
- VERSION = '1.3.1'
3
+ VERSION = '2.1.0'
4
4
  end
5
5
  end
@@ -0,0 +1,22 @@
1
+ require_relative '../lib/movable_ink/aws'
2
+ require 'webmock/rspec'
3
+
4
+ describe MovableInk::Consul do
5
+ describe MovableInk::Consul::Kv do
6
+ it 'get returns a hash of json content' do
7
+ allow(Diplomat::Kv).to receive(:get).and_return({ hello: 'world' }.to_json)
8
+ expect(MovableInk::Consul::Kv.get('ham')).to eq({ 'hello' => 'world' })
9
+ end
10
+
11
+ it 'returns a string if the stored value is not json' do
12
+ value = 'some-database-password'
13
+ allow(Diplomat::Kv).to receive(:get).and_return(value)
14
+ expect(MovableInk::Consul::Kv.get('db_password')).to eq(value)
15
+ end
16
+
17
+ it 'forwards all arguments to Diplomat' do
18
+ expect(Diplomat::Kv).to receive(:get).with('foo/', recurse: true)
19
+ MovableInk::Consul::Kv.get('foo/', recurse: true)
20
+ end
21
+ end
22
+ end
@@ -1,4 +1,5 @@
1
1
  require_relative '../lib/movable_ink/aws'
2
+ require 'webmock/rspec'
2
3
 
3
4
  describe MovableInk::AWS::EC2 do
4
5
  context "outside EC2" do
@@ -21,6 +22,7 @@ describe MovableInk::AWS::EC2 do
21
22
  end
22
23
 
23
24
  context "inside EC2" do
25
+ WebMock.allow_net_connect!
24
26
  let(:aws) { MovableInk::AWS.new }
25
27
  let(:ec2) { Aws::EC2::Client.new(stub_responses: true) }
26
28
  let(:tag_data) { ec2.stub_data(:describe_tags, tags: [
@@ -305,6 +307,150 @@ describe MovableInk::AWS::EC2 do
305
307
  end
306
308
  end
307
309
 
310
+ context "instances with consul discovery" do
311
+ let(:my_availability_zone) { 'us-east-1a' }
312
+ let(:my_datacenter) { 'iad' }
313
+ let(:other_availability_zone) { 'us-east-1b' }
314
+ let(:consul_app_service_instances) {
315
+ [{
316
+ Node: {
317
+ Node: 'app_instance1',
318
+ Address: '10.0.0.1',
319
+ Datacenter: my_datacenter,
320
+ Meta: {
321
+ availability_zone: my_availability_zone,
322
+ instance_id: 'i-12345',
323
+ mi_monitoring_roles: 'app',
324
+ mi_roles: 'app'
325
+ },
326
+ ServiceID: 'app',
327
+ ServiceName: 'app',
328
+ ServiceTags: ['foo', 'bar'],
329
+ ServicePort: 80,
330
+ }
331
+ },
332
+ {
333
+ Node: {
334
+ Node: 'app_instance2',
335
+ Address: '10.0.0.2',
336
+ Datacenter: my_datacenter,
337
+ Meta: {
338
+ availability_zone: my_availability_zone,
339
+ instance_id: 'i-54321',
340
+ mi_monitoring_roles: 'app',
341
+ mi_roles: 'app'
342
+ },
343
+ ServiceID: 'app',
344
+ ServiceName: 'app',
345
+ ServiceTags: ['foo', 'bar'],
346
+ ServicePort: 80,
347
+ }
348
+ }]
349
+ }
350
+
351
+ let(:consul_ojos_service_instances) {
352
+ [
353
+ {
354
+ Node: {
355
+ Node: 'ojos_instance2',
356
+ Address: '10.0.0.4',
357
+ Datacenter: my_datacenter,
358
+ Meta: {
359
+ availability_zone: other_availability_zone,
360
+ instance_id: 'i-zyx987',
361
+ mi_monitoring_roles: 'ojos',
362
+ mi_roles: 'ojos'
363
+ },
364
+ ServiceID: 'ojos',
365
+ ServiceName: 'ojos',
366
+ ServiceTags: ['foo', 'bar'],
367
+ ServicePort: 2702,
368
+ },
369
+ },
370
+ {
371
+ Node: {
372
+ Node: 'ojos_instance3',
373
+ Address: '10.0.0.5',
374
+ Datacenter: my_datacenter,
375
+ Meta: {
376
+ availability_zone: other_availability_zone,
377
+ instance_id: 'i-987654',
378
+ mi_monitoring_roles: 'ojos',
379
+ mi_roles: 'ojos'
380
+ },
381
+ ServiceID: 'ojos',
382
+ ServiceName: 'ojos',
383
+ ServiceTags: ['foo', 'bar'],
384
+ ServicePort: 2702,
385
+ }
386
+ }]
387
+ }
388
+
389
+ before(:each) do
390
+ allow(aws).to receive(:mi_env).and_return('test')
391
+ allow(aws).to receive(:availability_zone).and_return(my_availability_zone)
392
+ allow(aws).to receive(:my_region).and_return('us-east-1')
393
+ allow(aws).to receive(:ec2).and_return(ec2)
394
+ end
395
+
396
+ it "returns an error if no role is defined" do
397
+ expect{ aws.instances(role: nil, discovery_type: 'consul') }.to raise_error(MovableInk::AWS::Errors::RoleNameRequiredError)
398
+ end
399
+
400
+ it "returns an error if an invalid role is passed" do
401
+ expect{ aws.instances(role: 'asset_proxy', discovery_type: 'consul') }.to raise_error(MovableInk::AWS::Errors::RoleNameInvalidError)
402
+ end
403
+
404
+ it "returns all instances matching a consul service" do
405
+ miaws = double(MovableInk::AWS)
406
+ allow(miaws).to receive(:my_region).and_return('us-east-1')
407
+
408
+ json = JSON.generate(consul_app_service_instances)
409
+ stub_request(:get, "https://localhost:8501/v1/health/service/app?cached&dc=#{my_datacenter}&passing&stale").
410
+ with(
411
+ headers: {
412
+ 'Accept'=>'*/*',
413
+ 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
414
+ 'User-Agent'=>'Faraday v1.0.1'
415
+ }).
416
+ to_return(status: 200, body: json, headers: {})
417
+
418
+ app_instances = aws.instances(role: 'app', discovery_type: 'consul')
419
+ expect(app_instances.map{|i| i.tags.first[:value]}).to eq(['app_instance1', 'app_instance2'])
420
+
421
+ json = JSON.generate(consul_ojos_service_instances)
422
+ stub_request(:get, "https://localhost:8501/v1/health/service/ojos?cached&dc=#{my_datacenter}&passing&stale").
423
+ with(
424
+ headers: {
425
+ 'Accept'=>'*/*',
426
+ 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
427
+ 'User-Agent'=>'Faraday v1.0.1'
428
+ }).
429
+ to_return(status: 200, body: json, headers: {})
430
+
431
+ ojos_instances = aws.instances(role: 'ojos', discovery_type: 'consul')
432
+ expect(ojos_instances.map{|i| i.tags.first[:value ]}).to eq(['ojos_instance2', 'ojos_instance3'])
433
+ end
434
+
435
+ it "returns all instances matching a consul service filtered by availability_zone" do
436
+ miaws = double(MovableInk::AWS)
437
+ allow(miaws).to receive(:my_region).and_return('us-east-1')
438
+
439
+ json = JSON.generate(consul_ojos_service_instances)
440
+ stub_request(:get, "https://localhost:8501/v1/health/service/ojos?cached&dc=#{my_datacenter}&node-meta=availability_zone:#{other_availability_zone}&passing&stale").
441
+ with(
442
+ headers: {
443
+ 'Accept'=>'*/*',
444
+ 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
445
+ 'User-Agent'=>'Faraday v1.0.1'
446
+ }).
447
+ to_return(status: 200, body: json, headers: {})
448
+
449
+ ojos_instances = aws.instances(role: 'ojos', availability_zone: other_availability_zone, discovery_type: 'consul')
450
+ expect(ojos_instances.map{|i| i.tags.first[:value]}).to eq(['ojos_instance2', 'ojos_instance3'])
451
+ end
452
+ end
453
+
308
454
  context "ordered roles" do
309
455
  let(:my_availability_zone) { 'us-east-1a' }
310
456
  let(:other_availability_zone) { 'us-east-1b' }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: MovableInkAWS
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Chesler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-01 00:00:00.000000000 Z
11
+ date: 2021-01-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-core
@@ -206,6 +206,20 @@ dependencies:
206
206
  - - '='
207
207
  - !ruby/object:Gem::Version
208
208
  version: 0.16.3
209
+ - !ruby/object:Gem::Dependency
210
+ name: diplomat
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - '='
214
+ - !ruby/object:Gem::Version
215
+ version: 2.4.2
216
+ type: :runtime
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - '='
221
+ - !ruby/object:Gem::Version
222
+ version: 2.4.2
209
223
  description: AWS Utility methods for MovableInk
210
224
  email: mchesler@movableink.com
211
225
  executables: []
@@ -232,9 +246,11 @@ files:
232
246
  - lib/movable_ink/aws/s3.rb
233
247
  - lib/movable_ink/aws/sns.rb
234
248
  - lib/movable_ink/aws/ssm.rb
249
+ - lib/movable_ink/consul/consul.rb
235
250
  - lib/movable_ink/version.rb
236
251
  - spec/autoscaling_spec.rb
237
252
  - spec/aws_spec.rb
253
+ - spec/consul_spec.rb
238
254
  - spec/ec2_spec.rb
239
255
  - spec/elasticache_spec.rb
240
256
  - spec/metadata_spec.rb