MovableInkAWS 2.0.1 → 2.2.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: 9748092eb913e843641b0ac9d68106cb8ae3dc141c5f3ef0cf2794c7b52a41f5
4
- data.tar.gz: 1bcbc2de4d41898654cf8df5e09fb1a43465d338f73b64a36fcf785db882fb15
3
+ metadata.gz: eb083b13e12b669f0843ea32ea2b8b4280ccce9b5ac6055964d9ca1214d36f60
4
+ data.tar.gz: 967f30f88ebef0135620f12930075da3bdeb05d4b449c2e5612a267f3197ff73
5
5
  SHA512:
6
- metadata.gz: ed19423e404a705e9caf74762f6bd1cd08b5f552af5b24d6c355364d59c422d2cfba5f225e10cd021a79e77ae5d65ee8773c86eda5340e32d69d088cfb4789c4
7
- data.tar.gz: 6c5edc3a00e2d798140c30d2cce8279c03099e9b30aa1c1630e14a10ae075756328d9327ebfab94171bea47394503d4f9a592c9a8d5e706be37fe6c1dfbbd7f6
6
+ metadata.gz: 687824a79299c74f15b6c9d1f3fded4663020670ae533619045e2097b99a38782348c3572d51bcb0d130d6b0e4e84b0ee78b778795792ba375b45118a94aaba9
7
+ data.tar.gz: 85feb71050191506f20429f851fca622597090365fe6817b54091daf539f7c84918466b20ba8566d2c948c1326188f4a33bdc8b3989d2f80e9b82cb6ffeebedf
@@ -0,0 +1,39 @@
1
+ name: test
2
+
3
+ on:
4
+ push:
5
+ branches: ['**']
6
+ tags: ['v*']
7
+ pull_request:
8
+ branches: [$default-branch]
9
+
10
+ jobs:
11
+ test:
12
+
13
+ runs-on: ubuntu-18.04
14
+
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+
18
+ - name: Install ruby
19
+ uses: ruby/setup-ruby@v1
20
+ with:
21
+ ruby-version: 2.6
22
+
23
+ - name: Install dependencies
24
+ run: bundle install --retry=3
25
+
26
+ - name: Run tests
27
+ run: bundle exec rspec spec/
28
+
29
+ - name: Publish to RubyGems
30
+ if: startsWith(github.ref, 'refs/tags/')
31
+ run: |
32
+ mkdir -p $HOME/.gem
33
+ touch $HOME/.gem/credentials
34
+ chmod 0600 $HOME/.gem/credentials
35
+ printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
36
+ gem build *.gemspec
37
+ gem push *.gem
38
+ env:
39
+ GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- MovableInkAWS (2.0.1)
4
+ MovableInkAWS (2.2.1)
5
5
  aws-sdk-athena (~> 1)
6
6
  aws-sdk-autoscaling (~> 1)
7
7
  aws-sdk-cloudwatch (~> 1)
@@ -23,51 +23,51 @@ GEM
23
23
  specs:
24
24
  addressable (2.7.0)
25
25
  public_suffix (>= 2.0.2, < 5.0)
26
- aws-eventstream (1.1.0)
27
- aws-partitions (1.390.0)
28
- aws-sdk-athena (1.33.0)
29
- aws-sdk-core (~> 3, >= 3.109.0)
26
+ aws-eventstream (1.1.1)
27
+ aws-partitions (1.433.0)
28
+ aws-sdk-athena (1.37.0)
29
+ aws-sdk-core (~> 3, >= 3.112.0)
30
30
  aws-sigv4 (~> 1.1)
31
- aws-sdk-autoscaling (1.48.0)
32
- aws-sdk-core (~> 3, >= 3.109.0)
31
+ aws-sdk-autoscaling (1.58.0)
32
+ aws-sdk-core (~> 3, >= 3.112.0)
33
33
  aws-sigv4 (~> 1.1)
34
- aws-sdk-cloudwatch (1.46.0)
35
- aws-sdk-core (~> 3, >= 3.109.0)
34
+ aws-sdk-cloudwatch (1.50.0)
35
+ aws-sdk-core (~> 3, >= 3.112.0)
36
36
  aws-sigv4 (~> 1.1)
37
- aws-sdk-core (3.109.2)
37
+ aws-sdk-core (3.113.0)
38
38
  aws-eventstream (~> 1, >= 1.0.2)
39
39
  aws-partitions (~> 1, >= 1.239.0)
40
40
  aws-sigv4 (~> 1.1)
41
41
  jmespath (~> 1.0)
42
- aws-sdk-ec2 (1.207.0)
43
- aws-sdk-core (~> 3, >= 3.109.0)
42
+ aws-sdk-ec2 (1.227.0)
43
+ aws-sdk-core (~> 3, >= 3.112.0)
44
44
  aws-sigv4 (~> 1.1)
45
- aws-sdk-eks (1.45.0)
46
- aws-sdk-core (~> 3, >= 3.109.0)
45
+ aws-sdk-eks (1.51.0)
46
+ aws-sdk-core (~> 3, >= 3.112.0)
47
47
  aws-sigv4 (~> 1.1)
48
- aws-sdk-elasticache (1.45.0)
49
- aws-sdk-core (~> 3, >= 3.109.0)
48
+ aws-sdk-elasticache (1.54.0)
49
+ aws-sdk-core (~> 3, >= 3.112.0)
50
50
  aws-sigv4 (~> 1.1)
51
- aws-sdk-kms (1.39.0)
52
- aws-sdk-core (~> 3, >= 3.109.0)
51
+ aws-sdk-kms (1.43.0)
52
+ aws-sdk-core (~> 3, >= 3.112.0)
53
53
  aws-sigv4 (~> 1.1)
54
- aws-sdk-rds (1.105.0)
55
- aws-sdk-core (~> 3, >= 3.109.0)
54
+ aws-sdk-rds (1.117.0)
55
+ aws-sdk-core (~> 3, >= 3.112.0)
56
56
  aws-sigv4 (~> 1.1)
57
- aws-sdk-route53 (1.44.0)
58
- aws-sdk-core (~> 3, >= 3.109.0)
57
+ aws-sdk-route53 (1.47.0)
58
+ aws-sdk-core (~> 3, >= 3.112.0)
59
59
  aws-sigv4 (~> 1.1)
60
- aws-sdk-s3 (1.83.1)
61
- aws-sdk-core (~> 3, >= 3.109.0)
60
+ aws-sdk-s3 (1.91.0)
61
+ aws-sdk-core (~> 3, >= 3.112.0)
62
62
  aws-sdk-kms (~> 1)
63
63
  aws-sigv4 (~> 1.1)
64
- aws-sdk-sns (1.35.0)
65
- aws-sdk-core (~> 3, >= 3.109.0)
64
+ aws-sdk-sns (1.39.0)
65
+ aws-sdk-core (~> 3, >= 3.112.0)
66
66
  aws-sigv4 (~> 1.1)
67
- aws-sdk-ssm (1.95.0)
68
- aws-sdk-core (~> 3, >= 3.109.0)
67
+ aws-sdk-ssm (1.106.0)
68
+ aws-sdk-core (~> 3, >= 3.112.0)
69
69
  aws-sigv4 (~> 1.1)
70
- aws-sigv4 (1.2.2)
70
+ aws-sigv4 (1.2.3)
71
71
  aws-eventstream (~> 1, >= 1.0.2)
72
72
  crack (0.4.3)
73
73
  safe_yaml (~> 1.0.0)
@@ -85,7 +85,7 @@ GEM
85
85
  jmespath (1.4.0)
86
86
  mime-types (3.3.1)
87
87
  mime-types-data (~> 3.2015)
88
- mime-types-data (3.2020.1104)
88
+ mime-types-data (3.2021.0225)
89
89
  multi_xml (0.6.0)
90
90
  multipart-post (2.1.1)
91
91
  public_suffix (4.0.5)
@@ -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
 
@@ -112,50 +112,72 @@ module MovableInk
112
112
  raise MovableInk::AWS::Errors::RoleNameRequiredError
113
113
  end
114
114
 
115
- if role.include?('_')
116
- raise MovableInk::AWS::Errors::RoleNameInvalidError
117
- end
118
-
119
- Diplomat.configure do |config|
120
- config.url = "https://localhost:8501"
121
- config.options = { ssl: { verify: false } }
122
- end
123
-
124
- consul_service_options = Hash.new
125
-
126
- consul_service_options[:dc] = datacenter(region: region)
127
- consul_service_options[:stale] = true
128
- consul_service_options[:cached] = true
129
- consul_service_options[:passing] = true
130
-
115
+ consul_service_options = {
116
+ dc: datacenter(region: region),
117
+ stale: true,
118
+ cached: true,
119
+ passing: true,
120
+ }
131
121
  consul_service_options[:node_meta] = "availability_zone:#{availability_zone}" unless availability_zone.nil?
132
122
 
133
- Diplomat::Health.service(role, consul_service_options).map { |endpoint|
134
- OpenStruct.new (
135
- {
136
- private_ip_address: endpoint.Node['Address'],
137
- instance_id: endpoint.Node['Meta']['instance_id'],
138
- tags: [
139
- {
140
- key: 'Name',
141
- value: endpoint.Node['Node']
142
- },
143
- {
144
- key: 'mi:roles',
145
- value: endpoint.Node['Meta']['mi_roles']
146
- },
147
- {
148
- key: 'mi:monitoring_roles',
149
- value: endpoint.Node['Meta']['mi_monitoring_roles']
150
- }
151
- ],
152
- placement: {
153
- availability_zone: endpoint.Node['Meta']['availability_zone']
154
- }
155
- })
123
+ # We replace underscores with dashes in the role name in order to comply with
124
+ # consul service naming conventions while still retaining the role name we use
125
+ # within MI configuration
126
+ Diplomat::Health.service(role.gsub('_', '-'), consul_service_options).map { |endpoint|
127
+ if endpoint.Node.dig('Meta', 'external-source') == 'kubernetes'
128
+ map_k8s_consul_endpoint(endpoint, role)
129
+ else
130
+ map_ec2_consul_endpoint(endpoint)
131
+ end
156
132
  }
157
133
  end
158
134
 
135
+ def map_k8s_consul_endpoint(endpoint, role)
136
+ OpenStruct.new ({
137
+ private_ip_address: endpoint.Service.dig('Address'),
138
+ instance_id: nil,
139
+ tags: [
140
+ {
141
+ key: 'Name',
142
+ value: endpoint.Service.dig('ID')
143
+ },
144
+ {
145
+ key: 'mi:roles',
146
+ value: role
147
+ },
148
+ {
149
+ key: 'mi:monitoring_roles',
150
+ value: role
151
+ }
152
+ ],
153
+ placement: { availability_zone: nil }
154
+ })
155
+ end
156
+
157
+ def map_ec2_consul_endpoint(endpoint)
158
+ OpenStruct.new ({
159
+ private_ip_address: endpoint.Node['Address'],
160
+ instance_id: endpoint.Node['Meta']['instance_id'],
161
+ tags: [
162
+ {
163
+ key: 'Name',
164
+ value: endpoint.Node['Node']
165
+ },
166
+ {
167
+ key: 'mi:roles',
168
+ value: endpoint.Node['Meta']['mi_roles']
169
+ },
170
+ {
171
+ key: 'mi:monitoring_roles',
172
+ value: endpoint.Node['Meta']['mi_monitoring_roles']
173
+ }
174
+ ],
175
+ placement: {
176
+ availability_zone: endpoint.Node['Meta']['availability_zone']
177
+ }
178
+ })
179
+ end
180
+
159
181
  def instances(role:, exclude_roles: [], region: my_region, availability_zone: nil, exact_match: false, use_cache: true, discovery_type: 'ec2')
160
182
  if discovery_type == 'ec2'
161
183
  instances_with_ec2_discovery(role: role, exclude_roles: exclude_roles, region: region, availability_zone: availability_zone, exact_match: exact_match, use_cache: use_cache)
@@ -29,42 +29,27 @@ module MovableInk
29
29
  sleep seconds
30
30
  end
31
31
 
32
- def notify_nsq_can_not_be_drained
33
- notify_slack(subject: 'NSQ not drained',
34
- message: "Unable to drain NSQ for instance <https://#{my_region}.console.aws.amazon.com/ec2/v2/home?region=#{my_region}#Instances:search=#{instance_id};sort=instanceId|#{instance_id}>")
35
- notify_pagerduty(region: my_region, instance_id: instance_id)
36
- end
37
-
38
- def notify_pagerduty(region:, instance_id:)
39
- summary = "Unable to drain NSQ for instance #{instance_id} in region #{region}"
40
-
41
- # the PagerDuty integration key is added to the payload in the AWS integration
42
- json_message = {
43
- pagerduty: {
44
- event_action: 'trigger',
45
- payload: {
46
- source: 'MovableInkAWS',
47
- summary: summary,
48
- timestamp: Time.now.utc.iso8601,
49
- severity: 'error',
50
- component: 'nsq',
51
- group: 'nsq',
52
- custom_details: {
53
- InstanceId: instance_id,
54
- },
55
- },
56
- dedup_key: "nsq-not-draining-#{instance_id}",
57
- links: [{
58
- href: "https://#{region}.console.aws.amazon.com/ec2/v2/home?region=#{region}#Instances:search=#{instance_id};sort=instanceId",
59
- text: 'View Instance'
60
- }],
61
- }
62
- }.to_json
63
-
32
+ def send_alert(
33
+ source: instance_id,
34
+ links: [],
35
+ custom_details: {},
36
+ summary:,
37
+ dedup_key:
38
+ )
64
39
  run_with_backoff do
65
- sns.publish(topic_arn: sns_pagerduty_topic_arn,
66
- subject: "Unable to drain NSQ",
67
- message: json_message)
40
+ message_json = pd_message_json({
41
+ source: source,
42
+ summary: summary,
43
+ links: links,
44
+ custom_details: custom_details,
45
+ dedup_key: dedup_key,
46
+ })
47
+
48
+ sns.publish({
49
+ topic_arn: sns_pagerduty_topic_arn,
50
+ subject: summary,
51
+ message: message_json
52
+ })
68
53
  end
69
54
  end
70
55
 
@@ -78,6 +63,33 @@ module MovableInk
78
63
  message: "#{required_info}\n#{message}")
79
64
  end
80
65
  end
66
+
67
+ private
68
+
69
+ def pd_message_json(
70
+ source:,
71
+ summary:,
72
+ links:,
73
+ custom_details:,
74
+ dedup_key:
75
+ )
76
+ {
77
+ pagerduty: {
78
+ event_action: 'trigger',
79
+ payload: {
80
+ source: source,
81
+ summary: summary,
82
+ timestamp: Time.now.utc.iso8601,
83
+ severity: 'error',
84
+ component: source,
85
+ group: source,
86
+ custom_details: custom_details,
87
+ },
88
+ dedup_key: dedup_key,
89
+ links: links,
90
+ }
91
+ }.to_json
92
+ end
81
93
  end
82
94
  end
83
95
  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 = '2.0.1'
3
+ VERSION = '2.2.1'
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
data/spec/ec2_spec.rb CHANGED
@@ -386,6 +386,36 @@ describe MovableInk::AWS::EC2 do
386
386
  }]
387
387
  }
388
388
 
389
+ let(:consul_kubernetes_service_instances) do
390
+ [
391
+ {
392
+ "Node": {
393
+ "ID": "",
394
+ "Node": "k8s-sync",
395
+ "Address": "127.0.0.1",
396
+ "Datacenter": "my_datacenter",
397
+ "TaggedAddresses": nil,
398
+ "Meta": {
399
+ "external-source": "kubernetes",
400
+ "CreateIndex": 987654321,
401
+ "ModifyIndex": 123456789,
402
+ }
403
+ },
404
+ "Service": {
405
+ "ID": "pod-name-ac8f920827af",
406
+ "Service": "kubernetes-service-name",
407
+ "Tags": ["k8s"],
408
+ "Address": "10.0.0.1",
409
+ "Port": 8080,
410
+ "Meta": {
411
+ "external-source": "kubernetes",
412
+ "external-k8s-ns": "default",
413
+ }
414
+ }
415
+ }
416
+ ]
417
+ end
418
+
389
419
  before(:each) do
390
420
  allow(aws).to receive(:mi_env).and_return('test')
391
421
  allow(aws).to receive(:availability_zone).and_return(my_availability_zone)
@@ -397,10 +427,6 @@ describe MovableInk::AWS::EC2 do
397
427
  expect{ aws.instances(role: nil, discovery_type: 'consul') }.to raise_error(MovableInk::AWS::Errors::RoleNameRequiredError)
398
428
  end
399
429
 
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
430
  it "returns all instances matching a consul service" do
405
431
  miaws = double(MovableInk::AWS)
406
432
  allow(miaws).to receive(:my_region).and_return('us-east-1')
@@ -449,6 +475,33 @@ describe MovableInk::AWS::EC2 do
449
475
  ojos_instances = aws.instances(role: 'ojos', availability_zone: other_availability_zone, discovery_type: 'consul')
450
476
  expect(ojos_instances.map{|i| i.tags.first[:value]}).to eq(['ojos_instance2', 'ojos_instance3'])
451
477
  end
478
+
479
+ it "returns backends that are synced from consul-k8s" do
480
+ miaws = double(MovableInk::AWS)
481
+ allow(miaws).to receive(:my_region).and_return('us-east-1')
482
+
483
+ json = JSON.generate(consul_kubernetes_service_instances)
484
+ stub_request(:get, "https://localhost:8501/v1/health/service/kubernetes-service-name?cached&dc=#{my_datacenter}&passing&stale")
485
+ .with({
486
+ headers: {
487
+ 'Accept'=>'*/*',
488
+ 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
489
+ 'User-Agent'=>'Faraday v1.0.1'
490
+ }
491
+ })
492
+ .to_return(status: 200, body: json, headers: {})
493
+
494
+ resp_instances = aws.instances(role: 'kubernetes-service-name', discovery_type: 'consul')
495
+ backend = resp_instances.first
496
+ name_tag = backend.tags.find {|t| t[:key] == 'Name' }
497
+ roles_tag = backend.tags.find {|t| t[:key] == 'mi:roles' }
498
+
499
+ expect(name_tag[:value]).to eq('pod-name-ac8f920827af')
500
+ expect(roles_tag[:value]).to eq('kubernetes-service-name')
501
+ expect(backend.instance_id).to eq(nil)
502
+ expect(backend.private_ip_address).to eq('10.0.0.1')
503
+ expect(backend.private_ip_address).to eq('10.0.0.1')
504
+ end
452
505
  end
453
506
 
454
507
  context "ordered roles" do
data/spec/sns_spec.rb CHANGED
@@ -47,7 +47,7 @@ describe MovableInk::AWS::SNS do
47
47
  allow(aws).to receive(:sns).and_return(sns)
48
48
  allow(aws).to receive(:private_ipv4).and_return('10.0.0.1')
49
49
 
50
- expect(aws.notify_pagerduty(region: 'us-east-1', instance_id: 'i-987654321').message_id).to eq('messageId')
50
+ expect(aws.send_alert(summary: 'Something bad happened', dedup_key: 'i-987654321').message_id).to eq('messageId')
51
51
  end
52
52
 
53
53
  it "should truncate subjects longer than 100 characters" do
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: 2.0.1
4
+ version: 2.2.1
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-11-06 00:00:00.000000000 Z
11
+ date: 2021-03-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-core
@@ -227,7 +227,7 @@ extensions: []
227
227
  extra_rdoc_files: []
228
228
  files:
229
229
  - ".github/workflows/pr-lint.yaml"
230
- - ".travis.yml"
230
+ - ".github/workflows/test.yaml"
231
231
  - Gemfile
232
232
  - Gemfile.lock
233
233
  - MovableInkAWS.gemspec
@@ -246,9 +246,11 @@ files:
246
246
  - lib/movable_ink/aws/s3.rb
247
247
  - lib/movable_ink/aws/sns.rb
248
248
  - lib/movable_ink/aws/ssm.rb
249
+ - lib/movable_ink/consul/consul.rb
249
250
  - lib/movable_ink/version.rb
250
251
  - spec/autoscaling_spec.rb
251
252
  - spec/aws_spec.rb
253
+ - spec/consul_spec.rb
252
254
  - spec/ec2_spec.rb
253
255
  - spec/elasticache_spec.rb
254
256
  - spec/metadata_spec.rb
@@ -274,7 +276,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
274
276
  - !ruby/object:Gem::Version
275
277
  version: '0'
276
278
  requirements: []
277
- rubygems_version: 3.0.8
279
+ rubygems_version: 3.0.3
278
280
  signing_key:
279
281
  specification_version: 4
280
282
  summary: AWS Utility methods for MovableInk
data/.travis.yml DELETED
@@ -1,18 +0,0 @@
1
- language: ruby
2
-
3
- rvm:
4
- - 2.6
5
-
6
- bundler_args: "--retry=3"
7
-
8
- script:
9
- - rspec spec/
10
-
11
- deploy:
12
- provider: rubygems
13
- api_key:
14
- secure: NAHVFo9MwOnAYhq1NzZz22/2p34Iw/L71ZNt2WHSbWuMVnA/xyvVu7Ja/S25aQmaV/btNkxTYtbJbf83QZTO9/IwkashWFcWJtLiptZkBRdpkiunMPWX9Gw3Ap5fODUIPxLFyLYWd53huNBJppBCVQTUPwkLEcWJcQZP4MU7dkhW5QKdQgmyNTlMy8epxyMqCQe7V0n98LCwreUsHxHMToe6/V0FemysjYjpnyNvcFpGFa9kf28VQw8ZpDR0iobw9hg6qs61Xj69Y3ns9fWI3sdnk8VjY29M+V415/ksyn7kzCjYiQsGGCFerzjMffIt/4PXqX81GRvcymxmpXWwtikJYP95kiHYbLGFUNS5GJngbyVgO5+amjx7vSZfaBGSXcUCSkxLV1Ki4QfMYnA4ihogwTdQhIALSEV9GzOfFt147lUok2LgZDzNfn9nCPL+qKMBNaBRe7/YoWGuvLQG/smYjunPYhis0ynAQ1Sytc7lgtbdEtLFzfET87ITVS4Y2F9TQ+z1fxROAn/YwMmdSPyiaXVLEOIUc/gx0BjJneTl43M4l6ZId8Q6maJTNG2Fd0yK0hwLJcsN/257HbnPEiTTgkXBcg58E6/ABfZqwDnJrP3sQPem85M4BxLJIPUvfBkYzyeAEV+ALYDJx+KLaqYCCgavF9M+41VbkkNZyKU=
15
- gem: MovableInkAWS
16
- on:
17
- tags: true
18
- repo: movableink/awslib