dpl-elastic_beanstalk 1.9.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 22594965f777aa365113a4cb769c2876a34c6d71
4
+ data.tar.gz: 5e3149066cc30b974f1480b51b5eeb7673d79473
5
+ SHA512:
6
+ metadata.gz: f8aa3e16185e01849c5a385ecd4161f09f8d39bee447d569cfbd8f1e44f75717668fb8d244f0bcd0f547fee233f8e2e2cb7c8688d27db29bc96cf7118821e30e
7
+ data.tar.gz: 2ee266003d72e0e8ce560d29ed8364083359e01251df4872ab57bb179f503aedb250e90e42799fd512d998ecb6c4938ae9d2eb3b9ced8a2aa6563d62ff2e6aa5
@@ -0,0 +1,3 @@
1
+ require './gemspec_helper'
2
+
3
+ gemspec_for 'elastic_beanstalk', [['aws-sdk', '~> 2.0'], ['rubyzip']]
@@ -0,0 +1,194 @@
1
+ require 'time'
2
+ require 'aws-sdk'
3
+ require 'zip'
4
+
5
+ module DPL
6
+ class Provider
7
+ class ElasticBeanstalk < Provider
8
+ experimental 'AWS Elastic Beanstalk'
9
+
10
+ DEFAULT_REGION = 'us-east-1'
11
+
12
+ def needs_key?
13
+ false
14
+ end
15
+
16
+ def access_key_id
17
+ options[:access_key_id] || context.env['AWS_ACCESS_KEY_ID'] || raise(Error, "missing access_key_id")
18
+ end
19
+
20
+ def secret_access_key
21
+ options[:secret_access_key] || context.env['AWS_SECRET_ACCESS_KEY'] || raise(Error, "missing secret_access_key")
22
+ end
23
+
24
+ def check_auth
25
+ options = {
26
+ :region => region,
27
+ :credentials => Aws::Credentials.new(access_key_id, secret_access_key)
28
+ }
29
+ Aws.config.update(options)
30
+ end
31
+
32
+ def check_app
33
+ end
34
+
35
+ def only_create_app_version
36
+ options[:only_create_app_version]
37
+ end
38
+
39
+ def push_app
40
+ @start_time = Time.now
41
+ create_bucket unless bucket_exists?
42
+
43
+ if options[:zip_file]
44
+ zip_file = File.expand_path(options[:zip_file])
45
+ else
46
+ zip_file = create_zip
47
+ end
48
+
49
+ s3_object = upload(archive_name, zip_file)
50
+ sleep 5 #s3 eventual consistency
51
+ version = create_app_version(s3_object)
52
+ if !only_create_app_version
53
+ update_app(version)
54
+ wait_until_deployed if options[:wait_until_deployed]
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def app_name
61
+ option(:app)
62
+ end
63
+
64
+ def env_name
65
+ options[:env] || context.env['ELASTIC_BEANSTALK_ENV'] || raise(Error, "missing env")
66
+ end
67
+
68
+ def version_label
69
+ context.env['ELASTIC_BEANSTALK_LABEL'] || "travis-#{sha}-#{Time.now.to_i}"
70
+ end
71
+
72
+ def version_description
73
+ context.env['ELASTIC_BEANSTALK_DESCRIPTION'] || commit_msg
74
+ end
75
+
76
+ def archive_name
77
+ "#{version_label}.zip"
78
+ end
79
+
80
+ def region
81
+ options[:region] || DEFAULT_REGION
82
+ end
83
+
84
+ def bucket_name
85
+ option(:bucket_name)
86
+ end
87
+
88
+ def bucket_path
89
+ @bucket_path ||= options[:bucket_path] ? option(:bucket_path).gsub(/\/*$/,'/') : nil
90
+ end
91
+
92
+ def s3
93
+ @s3 ||= Aws::S3::Resource.new
94
+ end
95
+
96
+ def eb
97
+ @eb ||= Aws::ElasticBeanstalk::Client.new
98
+ end
99
+
100
+ def bucket_exists?
101
+ s3.bucket(bucket_name).exists?
102
+ end
103
+
104
+ def create_bucket
105
+ s3.bucket(bucket_name).create
106
+ end
107
+
108
+ def files_to_pack
109
+ `git ls-files -z`.split("\x0")
110
+ end
111
+
112
+ def create_zip
113
+ directory = Dir.pwd
114
+ zipfile_name = File.join(directory, archive_name)
115
+
116
+ Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
117
+ files_to_pack.each do |file|
118
+ relative_archive_path = File.join(directory, '/')
119
+ zipfile.add(file.sub(relative_archive_path, ''), file)
120
+ end
121
+ end
122
+ zipfile_name
123
+ end
124
+
125
+ def upload(key, file)
126
+ options = {
127
+ :body => Pathname.new(file).open
128
+ }
129
+ bucket = s3.bucket(bucket_name)
130
+ obj = bucket_path ? bucket.object("#{bucket_path}#{key}") : bucket.object(key)
131
+ obj.put(options)
132
+ obj
133
+ end
134
+
135
+ def create_app_version(s3_object)
136
+ # Elastic Beanstalk doesn't support descriptions longer than 200 characters
137
+ description = version_description[0, 200]
138
+ options = {
139
+ :application_name => app_name,
140
+ :version_label => version_label,
141
+ :description => description,
142
+ :source_bundle => {
143
+ :s3_bucket => bucket_name,
144
+ :s3_key => s3_object.key
145
+ },
146
+ :auto_create_application => false
147
+ }
148
+ eb.create_application_version(options)
149
+ end
150
+
151
+ # Wait until EB environment update finishes
152
+ def wait_until_deployed
153
+ errorEvents = 0 # errors counter, should remain 0 for successful deployment
154
+ events = []
155
+
156
+ loop do
157
+ environment = eb.describe_environments({
158
+ :application_name => app_name,
159
+ :environment_names => [env_name]
160
+ })[:environments].first
161
+
162
+ eb.describe_events({
163
+ :environment_name => env_name,
164
+ :start_time => @start_time.utc.iso8601,
165
+ })[:events].reverse.each do |event|
166
+ message = "#{event[:event_date]} [#{event[:severity]}] #{event[:message]}"
167
+ unless events.include?(message)
168
+ events.push(message)
169
+ if event[:severity] == "ERROR"
170
+ errorEvents += 1
171
+ warn(message)
172
+ else
173
+ log(message)
174
+ end
175
+ end
176
+ end
177
+
178
+ break if environment[:status] == "Ready"
179
+ sleep 5
180
+ end
181
+
182
+ if errorEvents > 0 then error("Deployment failed.") end
183
+ end
184
+
185
+ def update_app(version)
186
+ options = {
187
+ :environment_name => env_name,
188
+ :version_label => version[:application_version][:version_label]
189
+ }
190
+ eb.update_environment(options)
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,209 @@
1
+ require 'spec_helper'
2
+ require 'aws-sdk'
3
+ require 'dpl/provider'
4
+ require 'dpl/provider/elastic_beanstalk'
5
+
6
+ describe DPL::Provider::ElasticBeanstalk do
7
+
8
+ let(:access_key_id) { 'qwertyuiopasdfghjklz' }
9
+ let(:secret_access_key) { 'qwertyuiopasdfghjklzqwertyuiopasdfghjklz' }
10
+ let(:region) { 'us-west-2' }
11
+ let(:app) { 'example-app' }
12
+ let(:env) { 'live' }
13
+ let(:bucket_name) { "travis-elasticbeanstalk-test-builds-#{region}" }
14
+ let(:bucket_path) { "some/app"}
15
+ let(:only_create_app_version) { nil }
16
+ let(:wait_until_deployed) { nil }
17
+
18
+ let(:bucket_mock) do
19
+ dbl = double("bucket mock", write: nil)
20
+ allow(dbl).to receive(:objects).and_return(double("Hash", :[] => dbl))
21
+ dbl
22
+ end
23
+
24
+ let(:s3_mock) do
25
+ hash_dbl = double("Hash", :[] => bucket_mock, :map => [])
26
+ double("Aws::S3", bucket: hash_dbl, config: hash_dbl)
27
+ end
28
+
29
+ let(:io_double) do
30
+ double('IO', open: Object.new)
31
+ end
32
+
33
+ let(:s3_obj_double) do
34
+ double("Aws::S3::Object", put: Object.new)
35
+ end
36
+
37
+ subject :provider do
38
+ described_class.new(
39
+ DummyContext.new, :access_key_id => access_key_id, :secret_access_key => secret_access_key,
40
+ :region => region, :app => app, :env => env, :bucket_name => bucket_name, :bucket_path => bucket_path,
41
+ :only_create_app_version => only_create_app_version,
42
+ :wait_until_deployed => wait_until_deployed
43
+ )
44
+ end
45
+
46
+ subject :provider_without_bucket_path do
47
+ described_class.new(
48
+ DummyContext.new, :access_key_id => access_key_id, :secret_access_key => secret_access_key,
49
+ :region => region, :app => app, :env => env, :bucket_name => bucket_name
50
+ )
51
+ end
52
+
53
+ describe "#check_auth" do
54
+ example do
55
+ expect(Aws.config).to receive(:update)
56
+ provider.check_auth
57
+ end
58
+ end
59
+
60
+ describe "#push_app" do
61
+
62
+ let(:app_version) { Object.new }
63
+
64
+ before :each do
65
+ allow(io_double).to receive(:open)
66
+ allow(s3_obj_double).to receive(:put).with(anything).and_return(Object.new)
67
+ allow(s3_mock).to receive(:bucket).with(bucket_name).and_return(bucket_mock)
68
+ expect(Pathname).to receive(:new).and_return(io_double)
69
+ end
70
+
71
+ example 'bucket exists already' do
72
+ allow(s3_mock).to receive(:bucket).with(bucket_name).and_return(bucket_mock)
73
+ allow(bucket_mock).to receive(:exists?).and_return(true)
74
+ allow(bucket_mock).to receive(:object).with("some/app/file.zip").and_return(s3_obj_double)
75
+
76
+ expect(provider).to receive(:s3).and_return(s3_mock).twice
77
+ expect(provider).not_to receive(:create_bucket)
78
+ expect(provider).to receive(:create_zip).and_return('/path/to/file.zip')
79
+ expect(provider).to receive(:archive_name).and_return('file.zip')
80
+ expect(provider).to receive(:upload).with('file.zip', '/path/to/file.zip').and_call_original
81
+ expect(provider).to receive(:sleep).with(5)
82
+ expect(provider).to receive(:create_app_version).with(s3_obj_double).and_return(app_version)
83
+ expect(provider).to receive(:update_app).with(app_version)
84
+
85
+ provider.push_app
86
+ end
87
+
88
+ example 'bucket doesnt exist yet' do
89
+ allow(s3_mock).to receive(:bucket).with(bucket_name).and_return(bucket_mock)
90
+ allow(bucket_mock).to receive(:exists?).and_return(false)
91
+ allow(bucket_mock).to receive(:object).with("some/app/file.zip").and_return(s3_obj_double)
92
+
93
+ expect(provider).to receive(:s3).and_return(s3_mock).twice
94
+ expect(provider).to receive(:create_bucket)
95
+ expect(provider).to receive(:create_zip).and_return('/path/to/file.zip')
96
+ expect(provider).to receive(:archive_name).and_return('file.zip')
97
+ expect(provider).to receive(:upload).with('file.zip', '/path/to/file.zip').and_call_original
98
+ expect(provider).to receive(:sleep).with(5)
99
+ expect(provider).to receive(:create_app_version).with(s3_obj_double).and_return(app_version)
100
+ expect(provider).to receive(:update_app).with(app_version)
101
+
102
+ provider.push_app
103
+ end
104
+
105
+ context 'only creates app version' do
106
+ let(:only_create_app_version) { true }
107
+
108
+ example 'verify the app is not updated' do
109
+ allow(s3_mock).to receive(:bucket).with(bucket_name).and_return(bucket_mock)
110
+ allow(bucket_mock).to receive(:exists?).and_return(false)
111
+ allow(bucket_mock).to receive(:create)
112
+ allow(bucket_mock).to receive(:object).with("some/app/file.zip").and_return(s3_obj_double)
113
+
114
+ expect(provider).to receive(:s3).and_return(s3_mock).at_least(3).times
115
+ expect(provider).to receive(:create_zip).and_return('/path/to/file.zip')
116
+ expect(provider).to receive(:archive_name).and_return('file.zip')
117
+ expect(provider).to receive(:upload).with('file.zip', '/path/to/file.zip').and_call_original
118
+ expect(provider).to receive(:sleep).with(5)
119
+ expect(provider).to receive(:create_app_version).with(s3_obj_double).and_return(app_version)
120
+ expect(provider).not_to receive(:update_app).with(app_version)
121
+
122
+ provider.push_app
123
+ end
124
+ end
125
+
126
+ context 'When the bucket_path option is not set' do
127
+ example 'Does not prepend bucket_path to the s3 bucket' do
128
+ allow(s3_mock).to receive(:bucket).with(bucket_name).and_return(bucket_mock)
129
+ allow(bucket_mock).to receive(:object).with("file.zip").and_return(s3_obj_double)
130
+ allow(bucket_mock).to receive(:exists?).and_return(false)
131
+ allow(bucket_mock).to receive(:create)
132
+
133
+ expect(provider_without_bucket_path).to receive(:s3).and_return(s3_mock).at_least(3).times
134
+ expect(provider_without_bucket_path).to receive(:create_zip).and_return('/path/to/file.zip')
135
+ expect(provider_without_bucket_path).to receive(:archive_name).and_return('file.zip')
136
+ expect(provider_without_bucket_path).to receive(:bucket_path).and_return(nil)
137
+ expect(provider_without_bucket_path).to receive(:upload).with('file.zip', '/path/to/file.zip').and_call_original
138
+ expect(provider_without_bucket_path).to receive(:sleep).with(5)
139
+ expect(provider_without_bucket_path).to receive(:create_app_version).with(s3_obj_double).and_return(app_version)
140
+ expect(provider_without_bucket_path).to receive(:update_app).with(app_version)
141
+
142
+ provider_without_bucket_path.push_app
143
+ end
144
+ end
145
+
146
+ context 'When wait_until_deployed option is set' do
147
+ let(:wait_until_deployed) { true }
148
+
149
+ example 'Waits until deployment completes' do
150
+ allow(bucket_mock).to receive(:exists?).and_return(false)
151
+ allow(bucket_mock).to receive(:create)
152
+ allow(bucket_mock).to receive(:object).with("some/app/file.zip").and_return(s3_obj_double)
153
+
154
+ expect(provider).to receive(:s3).and_return(s3_mock).at_least(3).times
155
+ expect(provider).to receive(:create_zip).and_return('/path/to/file.zip')
156
+ expect(provider).to receive(:archive_name).and_return('file.zip')
157
+ expect(provider).to receive(:upload).with('file.zip', '/path/to/file.zip').and_call_original
158
+ expect(provider).to receive(:sleep).with(5)
159
+ expect(provider).to receive(:create_app_version).with(s3_obj_double).and_return(app_version)
160
+ expect(provider).to receive(:update_app).with(app_version)
161
+ expect(provider).to receive(:wait_until_deployed)
162
+
163
+ provider.push_app
164
+ end
165
+ end
166
+
167
+ context "when zip_file option is given" do
168
+ before :each do
169
+ allow(bucket_mock).to receive(:object).with("some/app/file.zip").and_return(s3_obj_double)
170
+ allow(bucket_mock).to receive(:exists?).and_return(false)
171
+ allow(bucket_mock).to receive(:create)
172
+ expect(provider).to receive(:s3).and_return(s3_mock).at_least(3).times
173
+ expect(provider).to receive(:archive_name).and_return('file.zip')
174
+ expect(provider).to receive(:sleep).with(5)
175
+ expect(provider).to receive(:create_app_version).with(s3_obj_double).and_return(app_version)
176
+ expect(provider).to receive(:update_app).with(app_version)
177
+ end
178
+
179
+ example 'zip_file is an absolute path' do
180
+ provider.options.update(:zip_file => '/absolute/path/to/file.zip')
181
+ expect(provider).to receive(:upload).with('file.zip', '/absolute/path/to/file.zip').and_call_original
182
+
183
+ provider.push_app
184
+ end
185
+
186
+ example 'zip_file is an relative path' do
187
+ provider.options.update(:zip_file => 'relative/path/to/file.zip')
188
+ expect(provider).to receive(:upload).with('file.zip', File.join(Dir.pwd, 'relative/path/to/file.zip')).and_call_original
189
+
190
+ provider.push_app
191
+ end
192
+
193
+ example 'zip_file is a path with ~/' do
194
+ provider.options.update(:zip_file => '~/file.zip')
195
+ expect(provider).to receive(:upload).with('file.zip', File.join(ENV['HOME'], 'file.zip')).and_call_original
196
+
197
+ provider.push_app
198
+ end
199
+
200
+ example 'zip_file is has no path' do
201
+ provider.options.update(:zip_file => 'file.zip')
202
+ expect(provider).to receive(:upload).with('file.zip', File.join(Dir.pwd, 'file.zip')).and_call_original
203
+
204
+ provider.push_app
205
+ end
206
+
207
+ end
208
+ end
209
+ end
metadata ADDED
@@ -0,0 +1,187 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dpl-elastic_beanstalk
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Konstantin Haase
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-03-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dpl
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.9.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.9.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: aws-sdk
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubyzip
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-its
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: json_pure
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: tins
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: coveralls
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: highline
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description: deploy tool abstraction for clients
154
+ email: konstantin.mailinglists@googlemail.com
155
+ executables: []
156
+ extensions: []
157
+ extra_rdoc_files: []
158
+ files:
159
+ - dpl-elastic_beanstalk.gemspec
160
+ - lib/dpl/provider/elastic_beanstalk.rb
161
+ - spec/provider/elastic_beanstalk_spec.rb
162
+ homepage: https://github.com/travis-ci/dpl
163
+ licenses:
164
+ - MIT
165
+ metadata: {}
166
+ post_install_message:
167
+ rdoc_options: []
168
+ require_paths:
169
+ - lib
170
+ required_ruby_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '2.2'
175
+ required_rubygems_version: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ requirements: []
181
+ rubyforge_project:
182
+ rubygems_version: 2.6.13
183
+ signing_key:
184
+ specification_version: 4
185
+ summary: deploy tool
186
+ test_files:
187
+ - spec/provider/elastic_beanstalk_spec.rb