dpl-elastic_beanstalk 1.9.0

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