Class: AWSBeanstalkTunkki

Inherits:
Object
  • Object
show all
Defined in:
lib/aws_beanstalk_tunkki.rb

Instance Method Summary collapse

Constructor Details

#initializeAWSBeanstalkTunkki

Returns a new instance of AWSBeanstalkTunkki.



8
9
10
# File 'lib/aws_beanstalk_tunkki.rb', line 8

def initialize
  init_deploy_variables()
end

Instance Method Details

#check_bs_envObject



93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/aws_beanstalk_tunkki.rb', line 93

def check_bs_env
  env_name = "#{@app}-#{@bs_env}"
  puts "Checking if Beanstalk environment '#{env_name}' in application '#{@app}' exists."
  application_environments = @elasticbeanstalk.describe_environments({environment_names: [env_name]})
  if is_environment_found?(application_environments.environments)
    puts "Environment '#{env_name}' already exists in application '#{@app}'."
    true
  else
    puts "Environment '#{env_name}' does not exist."
    false
  end
end

#create_app_version(version_label, s3_upload_details) ⇒ Object



161
162
163
164
165
166
167
168
169
170
171
# File 'lib/aws_beanstalk_tunkki.rb', line 161

def create_app_version
  version_label = generate_version_label()
  response = @elasticbeanstalk.create_application_version(
    {
      application_name: @app,
      version_label: version_label,
    })
  version_label
rescue => e
  raise "Application version launch failed. Error: #{e}"
end

#create_bs_env(configuration_templates) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/aws_beanstalk_tunkki.rb', line 110

def create_bs_env(configuration_templates)
  puts "Searching for Beanstalk configuration template.."

  app_bs_env_simple = "#{@app}-#{@bs_env_simple}"
  app_default       = "#{@app}-default"
  app_bs_env        = "#{@app}-#{@bs_env}"

  conf_template = find_configuration_template(configuration_templates)
  puts "Using '#{conf_template}' configuration template."
  puts "Launching new environment '#{@app}-#{@bs_env}' to application '#{@app}'."

  begin
    @elasticbeanstalk.create_environment(
      {
        application_name: @app,
        environment_name: app_bs_env,
        cname_prefix:     app_bs_env,
        template_name:    conf_template,
      })
    if (poll_for_environment_changes(app_bs_env) { |env| env.status != 'Launching' })
      puts "Created environment '#{app_bs_env}'!"
    else
      raise "Timeout when creating environment."
    end
  rescue => e
    raise "Environment launch failed, unable to proceed. Error: #{e}"
  end
end

#deploy_app(version_label) ⇒ Object



173
174
175
176
177
178
# File 'lib/aws_beanstalk_tunkki.rb', line 173

def deploy_app(version_label)
  puts "Deploying application to Beanstalk environment."
  s3_upload_details = upload_version_to_s3(version_label)
  create_app_version(version_label, s3_upload_details)
  update_environment(version_label)
end

#find_configuration_template(configuration_templates) ⇒ Object



139
140
141
142
143
144
# File 'lib/aws_beanstalk_tunkki.rb', line 139

def find_configuration_template(configuration_templates)
  configuration_templates.find do |tpl|
    /(?<conf_app_stub>[\w-]+)-(?<conf_env_stub>\w+)-\w+\z/ =~ tpl
    conf_app_stub && conf_env_stub && @app.match(conf_app_stub) && @bs_env_simple.match(conf_env_stub)
  end or raise "No configuration templates found, can't create environment."
end

#generate_version_labelObject



248
249
250
251
# File 'lib/aws_beanstalk_tunkki.rb', line 248

def generate_version_label
  time = Time.now
  "#{@app}-#{@bs_env_simple}-#{time.strftime('%Y-%m-%d-%H%M%S')}"
end

#get_aws_keysObject



78
79
80
81
82
83
84
85
# File 'lib/aws_beanstalk_tunkki.rb', line 78

def get_aws_keys
  case @environment
  when 'prod', 'st'
    [ENV['AWS_ACCESS_KEY_ID_PROD'], ENV['AWS_SECRET_ACCESS_KEY_PROD']]
  when 'dev', 'ft'
    [ENV['AWS_ACCESS_KEY_ID_DEV'], ENV['AWS_SECRET_ACCESS_KEY_DEV']]
  end
end

#get_bs_appObject



87
88
89
90
91
# File 'lib/aws_beanstalk_tunkki.rb', line 87

def get_bs_app
  application_description = @elasticbeanstalk.describe_applications({application_names: [@app]})
  raise "Application '#{@app}' not found." if application_description.applications.empty?
  application_description.applications.first
end

#init_deploy_variablesObject



20
21
22
23
24
# File 'lib/aws_beanstalk_tunkki.rb', line 20

def init_deploy_variables
  parse_command_line_options()
  set_application_environment()
  set_aws_clients()
end

#is_environment_found?(environments) ⇒ Boolean

Returns:

  • (Boolean)


106
107
108
# File 'lib/aws_beanstalk_tunkki.rb', line 106

def is_environment_found?(environments)
  !environments.empty? && !environments.all? { |env| env.status == 'Terminated' }
end

#make_zip_file(version_label) ⇒ Object



195
196
197
198
199
200
201
202
203
# File 'lib/aws_beanstalk_tunkki.rb', line 195

def make_zip_file(version_label)
  print 'Zipping application files ... '
  zip_name = "#{version_label}.zip"
  `cd #{@dir}; zip #{zip_name} -r ./ -x *.git* *.log*`
  raise "Creating ZIP failed!" if $?.exitstatus != 0
  FileUtils.mv("#{@dir}/#{zip_name}", './')
  print "Done!\n"
  zip_name
end

#parse_command_line_optionsObject



26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/aws_beanstalk_tunkki.rb', line 26

def parse_command_line_options
  OptionParser.new do |opt|
    opt.on('--app APP_NAME') { |app_name| @app = app_name }
    opt.on('--branch BRANCH_NAME') { |branch_name| @branch = branch_name }
    opt.on('--dir DIR') { |dir| @dir = dir }
    opt.on('--region REGION') { |aws_region| @aws_region = aws_region }
    opt.on('--local LOCAL') { |local| @local = local }
  end.parse!
  raise "Beanstalk application (--app) required!" if @app.nil?
  raise "Git branch (--branch) required!" if @branch.nil?
  raise "App dir location (--dir) required!" if @branch.nil?
  raise "AWS Region missing (--region) required!" if @aws_region.nil?
end

#poll_for_environment_changes(env_name) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/aws_beanstalk_tunkki.rb', line 146

def poll_for_environment_changes(env_name)
  print "Making changes to environment #{env_name}"
  ten_minutes = 60 * 10
  ten_minutes.times do |i|
    if (yield(@elasticbeanstalk.describe_environments({environment_names: [env_name]}).environments.first))
      print "\n"
      return true
    else
      print '.'
      sleep(1)
    end
  end
  false
end

#run_deploymentObject



12
13
14
15
16
17
18
# File 'lib/aws_beanstalk_tunkki.rb', line 12

def run_deployment
  application = get_bs_app()
  if !check_bs_env()
    create_bs_env(application.configuration_templates)
  end
  deploy_app(generate_version_label())
end

#set_application_environmentObject



40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/aws_beanstalk_tunkki.rb', line 40

def set_application_environment
  case @branch
  when /\Aprod/
    set_application_environment_vars(environment: 'prod', env_simple: 'production')
  when /\Ast/
    set_application_environment_vars(environment: 'st', env_simple: 'staging')
  when /\Adev/
    set_application_environment_vars(environment: 'dev', env_simple: 'development')
  when /\Aft/
    set_application_environment_vars(environment: 'ft', env_simple: 'feature')
  else
    raise "Invalid deployment branch name detected! Branch name must start with prod, st, dev or ft"
  end
end

#set_application_environment_vars(environment: 'dev', env_simple: '') ⇒ Object



55
56
57
58
59
60
# File 'lib/aws_beanstalk_tunkki.rb', line 55

def set_application_environment_vars(environment: 'dev', env_simple: '')
  puts "Using branch '#{@branch}' as #{env_simple}"
  @bs_env = @branch.gsub(/[\/:;.,+_<>#]/, '-')
  @environment = environment
  @bs_env_simple = env_simple
end

#set_aws_clientsObject



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/aws_beanstalk_tunkki.rb', line 62

def set_aws_clients
  if @local == "true"
    # Deploy from local machine, requires AWS_PROFILE=default and aws-mfa based login before execution
    credentials = Aws::SharedCredentials.new()
  else
    aws_access_key_id, aws_secret_access_key = *get_aws_keys()
    raise "AWS keys are not defined!" if aws_access_key_id.nil? || aws_secret_access_key.nil?
    credentials = Aws::Credentials.new(aws_access_key_id, aws_secret_access_key)
  end
  @elasticbeanstalk = Aws::ElasticBeanstalk::Client.new(region: @aws_region, credentials: credentials)
  @s3 = Aws::S3::Client.new(region: @aws_region, credentials: credentials)
  bucket = @s3.list_buckets.buckets.find { |b| /\Aelasticbeanstalk-#{@aws_region}/ =~ b.name }
  raise "Could not resolve s3 bucket name." if bucket.nil?
  @s3_bucket_name = bucket.name
end

#update_environment(version_label) ⇒ Object



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/aws_beanstalk_tunkki.rb', line 225

def update_environment(version_label)
  sleep(5) # Environment is in an invalid state for this operation. Must be Ready. (RuntimeError)
  @elasticbeanstalk.update_environment(
    {
      environment_name: "#{@app}-#{@bs_env}",
      version_label: version_label,
      option_settings: [
        {
          namespace: "aws:elasticbeanstalk:command",
          option_name: "Timeout",
          value: "1800",
        }
      ]
    })
  if (poll_for_environment_changes("#{@app}-#{@bs_env}") { |env| env.status != 'Updating' })
    puts "Updated '#{@app}-#{@bs_env}' environment successfully."
  else
    raise "Timeout of 10 minutes reached when updating environment."
  end
rescue => e
  raise "Updating environment failed. Error: #{e}"
end

#upload_version_to_s3(version_label) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/aws_beanstalk_tunkki.rb', line 180

def upload_version_to_s3(version_label)
  zip_name = make_zip_file(version_label)
  zip_name_with_path = "#{@app}/#{zip_name}"
  print "Uploading '#{zip_name}' to S3 bucket (#{@s3_bucket_name})... "
  begin
    File.open(zip_name) do |zip_file|
      @s3.put_object(bucket: @s3_bucket_name, body: zip_file, key: zip_name_with_path)
    end
    print "Done!\n"
    {bucket: @s3_bucket_name, file: zip_name_with_path}
  rescue => e
    raise "Upload to S3 bucket (#{@s3_bucket_name}) failed. Error: #{e}"
  end
end