aws_beanstalk_tunkki 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f226c4c68ba5bd6380f70933d8c04b449a89fdd6e27ad53333e89758f6333682
4
+ data.tar.gz: 39848528ce0ce80fc0e14ab678573d2d7ede28bb34c0031061a04a769533d31c
5
+ SHA512:
6
+ metadata.gz: f4229f11c843d1edffa590cb0f3d5e43b5d3304e952d9d2e2179466a396bd38d2e17f04a6b752e0beed12f0371f13adb2e8825f33250bfad0a1a80489dc19158
7
+ data.tar.gz: 810275c477f8ebe857f71cdd4d9865b11b147e7ee65c83bb36d952ab4ecef2afe8868855bea26a306832cbe521902e6528cf7f7b54cfd10be1ec456c30854604
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'aws_beanstalk_tunkki'
4
+ AWSBeanstalkTunkki.new.run_deployment()
@@ -0,0 +1,253 @@
1
+ require 'aws-sdk-elasticbeanstalk'
2
+ require 'aws-sdk-s3'
3
+ require 'optparse'
4
+ require 'fileutils'
5
+
6
+ class AWSBeanstalkTunkki
7
+
8
+ def initialize
9
+ init_deploy_variables()
10
+ end
11
+
12
+ def run_deployment
13
+ application = get_bs_app()
14
+ if !check_bs_env()
15
+ create_bs_env(application.configuration_templates)
16
+ end
17
+ deploy_app(generate_version_label())
18
+ end
19
+
20
+ def init_deploy_variables
21
+ parse_command_line_options()
22
+ set_application_environment()
23
+ set_aws_clients()
24
+ end
25
+
26
+ def parse_command_line_options
27
+ OptionParser.new do |opt|
28
+ opt.on('--app APP_NAME') { |app_name| @app = app_name }
29
+ opt.on('--branch BRANCH_NAME') { |branch_name| @branch = branch_name }
30
+ opt.on('--dir DIR') { |dir| @dir = dir }
31
+ opt.on('--region REGION') { |aws_region| @aws_region = aws_region }
32
+ opt.on('--local LOCAL') { |local| @local = local }
33
+ end.parse!
34
+ raise "Beanstalk application (--app) required!" if @app.nil?
35
+ raise "Git branch (--branch) required!" if @branch.nil?
36
+ raise "App dir location (--dir) required!" if @branch.nil?
37
+ raise "AWS Region missing (--region) required!" if @aws_region.nil?
38
+ end
39
+
40
+ def set_application_environment
41
+ case @branch
42
+ when /\Aprod/
43
+ set_application_environment_vars(environment: 'prod', env_simple: 'production')
44
+ when /\Ast/
45
+ set_application_environment_vars(environment: 'st', env_simple: 'staging')
46
+ when /\Adev/
47
+ set_application_environment_vars(environment: 'dev', env_simple: 'development')
48
+ when /\Aft/
49
+ set_application_environment_vars(environment: 'ft', env_simple: 'feature')
50
+ else
51
+ raise "Invalid deployment branch name detected! Branch name must start with prod, st, dev or ft"
52
+ end
53
+ end
54
+
55
+ def set_application_environment_vars(environment: 'dev', env_simple: '')
56
+ puts "Using branch '#{@branch}' as #{env_simple}"
57
+ @bs_env = @branch.gsub(/[\/:;.,+_<>#]/, '-')
58
+ @environment = environment
59
+ @bs_env_simple = env_simple
60
+ end
61
+
62
+ def set_aws_clients
63
+ if @local == "true"
64
+ # Deploy from local machine, requires AWS_PROFILE=default and aws-mfa based login before execution
65
+ credentials = Aws::SharedCredentials.new()
66
+ else
67
+ aws_access_key_id, aws_secret_access_key = *get_aws_keys()
68
+ raise "AWS keys are not defined!" if aws_access_key_id.nil? || aws_secret_access_key.nil?
69
+ credentials = Aws::Credentials.new(aws_access_key_id, aws_secret_access_key)
70
+ end
71
+ @elasticbeanstalk = Aws::ElasticBeanstalk::Client.new(region: @aws_region, credentials: credentials)
72
+ @s3 = Aws::S3::Client.new(region: @aws_region, credentials: credentials)
73
+ bucket = @s3.list_buckets.buckets.find { |b| /\Aelasticbeanstalk-#{@aws_region}/ =~ b.name }
74
+ raise "Could not resolve s3 bucket name." if bucket.nil?
75
+ @s3_bucket_name = bucket.name
76
+ end
77
+
78
+ def get_aws_keys
79
+ case @environment
80
+ when 'prod', 'st'
81
+ [ENV['AWS_ACCESS_KEY_ID_PROD'], ENV['AWS_SECRET_ACCESS_KEY_PROD']]
82
+ when 'dev', 'ft'
83
+ [ENV['AWS_ACCESS_KEY_ID_DEV'], ENV['AWS_SECRET_ACCESS_KEY_DEV']]
84
+ end
85
+ end
86
+
87
+ def get_bs_app
88
+ application_description = @elasticbeanstalk.describe_applications({application_names: [@app]})
89
+ raise "Application '#{@app}' not found." if application_description.applications.empty?
90
+ application_description.applications.first
91
+ end
92
+
93
+ def check_bs_env
94
+ env_name = "#{@app}-#{@bs_env}"
95
+ puts "Checking if Beanstalk environment '#{env_name}' in application '#{@app}' exists."
96
+ application_environments = @elasticbeanstalk.describe_environments({environment_names: [env_name]})
97
+ if is_environment_found?(application_environments.environments)
98
+ puts "Environment '#{env_name}' already exists in application '#{@app}'."
99
+ true
100
+ else
101
+ puts "Environment '#{env_name}' does not exist."
102
+ false
103
+ end
104
+ end
105
+
106
+ def is_environment_found?(environments)
107
+ !environments.empty? && !environments.all? { |env| env.status == 'Terminated' }
108
+ end
109
+
110
+ def create_bs_env(configuration_templates)
111
+ puts "Searching for Beanstalk configuration template.."
112
+
113
+ app_bs_env_simple = "#{@app}-#{@bs_env_simple}"
114
+ app_default = "#{@app}-default"
115
+ app_bs_env = "#{@app}-#{@bs_env}"
116
+
117
+ conf_template = find_configuration_template(configuration_templates)
118
+ puts "Using '#{conf_template}' configuration template."
119
+ puts "Launching new environment '#{@app}-#{@bs_env}' to application '#{@app}'."
120
+
121
+ begin
122
+ @elasticbeanstalk.create_environment(
123
+ {
124
+ application_name: @app,
125
+ environment_name: app_bs_env,
126
+ cname_prefix: app_bs_env,
127
+ template_name: conf_template,
128
+ })
129
+ if (poll_for_environment_changes(app_bs_env) { |env| env.status != 'Launching' })
130
+ puts "Created environment '#{app_bs_env}'!"
131
+ else
132
+ raise "Timeout when creating environment."
133
+ end
134
+ rescue => e
135
+ raise "Environment launch failed, unable to proceed. Error: #{e}"
136
+ end
137
+ end
138
+
139
+ def find_configuration_template(configuration_templates)
140
+ configuration_templates.find do |tpl|
141
+ /(?<conf_app_stub>[\w-]+)-(?<conf_env_stub>\w+)-\w+\z/ =~ tpl
142
+ conf_app_stub && conf_env_stub && @app.match(conf_app_stub) && @bs_env_simple.match(conf_env_stub)
143
+ end or raise "No configuration templates found, can't create environment."
144
+ end
145
+
146
+ def poll_for_environment_changes(env_name)
147
+ print "Making changes to environment #{env_name}"
148
+ ten_minutes = 60 * 10
149
+ ten_minutes.times do |i|
150
+ if (yield(@elasticbeanstalk.describe_environments({environment_names: [env_name]}).environments.first))
151
+ print "\n"
152
+ return true
153
+ else
154
+ print '.'
155
+ sleep(1)
156
+ end
157
+ end
158
+ false
159
+ end
160
+
161
+ def create_app_version
162
+ version_label = generate_version_label()
163
+ response = @elasticbeanstalk.create_application_version(
164
+ {
165
+ application_name: @app,
166
+ version_label: version_label,
167
+ })
168
+ version_label
169
+ rescue => e
170
+ raise "Application version launch failed. Error: #{e}"
171
+ end
172
+
173
+ def deploy_app(version_label)
174
+ puts "Deploying application to Beanstalk environment."
175
+ s3_upload_details = upload_version_to_s3(version_label)
176
+ create_app_version(version_label, s3_upload_details)
177
+ update_environment(version_label)
178
+ end
179
+
180
+ def upload_version_to_s3(version_label)
181
+ zip_name = make_zip_file(version_label)
182
+ zip_name_with_path = "#{@app}/#{zip_name}"
183
+ print "Uploading '#{zip_name}' to S3 bucket (#{@s3_bucket_name})... "
184
+ begin
185
+ File.open(zip_name) do |zip_file|
186
+ @s3.put_object(bucket: @s3_bucket_name, body: zip_file, key: zip_name_with_path)
187
+ end
188
+ print "Done!\n"
189
+ {bucket: @s3_bucket_name, file: zip_name_with_path}
190
+ rescue => e
191
+ raise "Upload to S3 bucket (#{@s3_bucket_name}) failed. Error: #{e}"
192
+ end
193
+ end
194
+
195
+ def make_zip_file(version_label)
196
+ print 'Zipping application files ... '
197
+ zip_name = "#{version_label}.zip"
198
+ `cd #{@dir}; zip #{zip_name} -r ./ -x *.git* *.log*`
199
+ raise "Creating ZIP failed!" if $?.exitstatus != 0
200
+ FileUtils.mv("#{@dir}/#{zip_name}", './')
201
+ print "Done!\n"
202
+ zip_name
203
+ end
204
+
205
+ def create_app_version(version_label, s3_upload_details)
206
+ if @app.length > 199
207
+ raise "Error! Beanstalk environment name max length is 200 characters! Length: #{@app.length} characters"
208
+ end
209
+ @elasticbeanstalk.create_application_version(
210
+ {
211
+ application_name: @app,
212
+ version_label: version_label,
213
+ source_bundle: {
214
+ s3_bucket: s3_upload_details[:bucket],
215
+ s3_key: s3_upload_details[:file],
216
+ },
217
+ auto_create_application: true,
218
+ process: true,
219
+ })
220
+ puts "Created application version '#{version_label}'"
221
+ rescue => e
222
+ raise "Creating application version '#{version_label}' failed. Error: #{e}"
223
+ end
224
+
225
+ def update_environment(version_label)
226
+ sleep(5) # Environment is in an invalid state for this operation. Must be Ready. (RuntimeError)
227
+ @elasticbeanstalk.update_environment(
228
+ {
229
+ environment_name: "#{@app}-#{@bs_env}",
230
+ version_label: version_label,
231
+ option_settings: [
232
+ {
233
+ namespace: "aws:elasticbeanstalk:command",
234
+ option_name: "Timeout",
235
+ value: "1800",
236
+ }
237
+ ]
238
+ })
239
+ if (poll_for_environment_changes("#{@app}-#{@bs_env}") { |env| env.status != 'Updating' })
240
+ puts "Updated '#{@app}-#{@bs_env}' environment successfully."
241
+ else
242
+ raise "Timeout of 10 minutes reached when updating environment."
243
+ end
244
+ rescue => e
245
+ raise "Updating environment failed. Error: #{e}"
246
+ end
247
+
248
+ def generate_version_label
249
+ time = Time.now
250
+ "#{@app}-#{@bs_env_simple}-#{time.strftime('%Y-%m-%d-%H%M%S')}"
251
+ end
252
+
253
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aws_beanstalk_tunkki
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Valtteri Pajunen
8
+ - Janne Saraste
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2019-12-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: aws-sdk-elasticbeanstalk
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '1.26'
21
+ - - "~>"
22
+ - !ruby/object:Gem::Version
23
+ version: '1.26'
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ version: '1.26'
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.26'
34
+ - !ruby/object:Gem::Dependency
35
+ name: aws-sdk-s3
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '1.59'
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '1.59'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '1.59'
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.59'
54
+ description: Tool for deploying your app to AWS ElasticBeanstalk.
55
+ email: IL.Tekniikka@iltalehti.fi
56
+ executables:
57
+ - aws_beanstalk_tunkki
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - bin/aws_beanstalk_tunkki
62
+ - lib/aws_beanstalk_tunkki.rb
63
+ homepage: https://github.com/almamedia/aws-beanstalk-tunkki
64
+ licenses:
65
+ - MIT
66
+ metadata: {}
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubygems_version: 3.0.3
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: AWS Beanstalk Tunkki
86
+ test_files: []