prima-twig 0.13.1 → 0.13.37

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.
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require_relative '../lib/prima_twig.rb'
5
+ require_relative '../lib/prima_aws_client.rb'
6
+ require 'launchy'
7
+ require 'json'
8
+ require 'aws-sdk-s3'
9
+
10
+ class TwigUpdateAmi
11
+ include Command
12
+ include PrimaAwsClient
13
+ def initialize
14
+ output 'Controllo se ci sono aggiornamenti da fare...'
15
+ exec "gem update prima-twig && twig update-ami #{ARGV.join ' '}" unless `gem outdated`.lines.grep(/^prima-twig \(.*\)/).empty?
16
+ @s3 = Aws::S3::Client.new
17
+ @s3_bucket = 'prima-deploy'
18
+ @templates_base_url = "https://s3-eu-west-1.amazonaws.com"
19
+ end
20
+
21
+ def execute!(args)
22
+ update_amis(args[0], args[1], args[2], args[3], args[4])
23
+ end
24
+
25
+ private
26
+
27
+ def update_amis(ami_template, ami_id, ami_name, ami_description, env)
28
+ output "updating instance definition #{ami_template}".light_green
29
+ Dir.chdir 'ami'
30
+ update_instance_name(ami_id, ami_name, ami_description, ami_template)
31
+ output 'running packer update (this could take some time)'.light_green
32
+ new_ami_id = update_packer(ami_template, env)
33
+ # new_ami_id = 'ami-026890988d91ee8c6'
34
+ Dir.chdir '..'
35
+ stop_if(new_ami_id.to_s.empty?, 'Failed to generate AMI!'.red)
36
+ output "new ami id: #{new_ami_id}"
37
+
38
+ output 'searching for ami to update...'
39
+ ami_mappings = JSON.parse(@s3.get_object(bucket: @s3_bucket, key: "ami/ami-mappings.json")["body"].read())
40
+ old_amis = update_ami_mappings(ami_mappings, ami_template, env, new_ami_id)
41
+ stop_if(old_amis.empty?, "No ami to update! No #{ami_template} in env #{env}, exiting".yellow)
42
+
43
+ output "retrieving stacks that uses old ami ids: #{old_amis}"
44
+ exports = list_exports()
45
+ stacks = get_stacks_from_exports(exports, old_amis)
46
+ stop_if(stacks.empty?, "No stack to update found! This means that ami-mapping file is not in sync, please check manually")
47
+
48
+ stacks.each do |stack|
49
+ output "processing stack #{stack}"
50
+ if stack.include?('qa')
51
+ output "skipping stack #{stack} because is a qa"
52
+ next
53
+ else
54
+ stack_tags = tags_to_hashes(get_stack_tags(stack))
55
+ stack_tags['TemplateVersion'] = stack_tags['TemplateVersion'].to_i + 1
56
+
57
+ if stack.include?('batch')
58
+ stack_parameters = update_stack_parameters(get_stack_parameters(stack),
59
+ [
60
+ { parameter_key: 'AMIID', parameter_value: new_ami_id },
61
+ { parameter_key: 'TemplateVersion', parameter_value: stack_tags['TemplateVersion'].to_s }
62
+ ]
63
+ )
64
+ if stack.include?('offsite-backups')
65
+ stack_template = File.read("./cloudformation/stacks/batch/compute-environment-offsite-backups.yml")
66
+ else
67
+ stack_template = File.read("./cloudformation/stacks/batch/compute-environment.yml")
68
+ end
69
+ else
70
+ stack_parameters = update_stack_parameters(get_stack_parameters(stack),
71
+ [
72
+ { parameter_key: 'AMIID', parameter_value: new_ami_id },
73
+ { parameter_key: 'DesiredCapacity', parameter_value: get_desired_capacity(stack).to_s },
74
+ { parameter_key: 'TemplateVersion', parameter_value: stack_tags['TemplateVersion'].to_s }
75
+ ]
76
+ )
77
+ stack_template = File.read("./cloudformation/stacks/asg/#{stack.to_s.split("/")[1]}.yml")
78
+ end
79
+ update_stack(stack, stack_template, stack_parameters, hashes_to_tags(stack_tags))
80
+ end
81
+ end
82
+
83
+ stacks.each do |stack|
84
+ if stack.include?('qa')
85
+ next
86
+ end
87
+ wait_for_stack_ready(stack, ['CREATE_FAILED', 'ROLLBACK_IN_PROGRESS', 'ROLLBACK_FAILED', 'DELETE_IN_PROGRESS', 'DELETE_FAILED', 'DELETE_COMPLETE', 'UPDATE_ROLLBACK_FAILED', 'UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS', 'UPDATE_ROLLBACK_COMPLETE', 'ROLLBACK_COMPLETE'])
88
+ end
89
+
90
+ output 'writing new ami mapping'
91
+ File.open("ami/ami-mappings.json", 'w+') do |f|
92
+ mapping_file = JSON.pretty_generate(ami_mappings)
93
+ f.write(mapping_file)
94
+ @s3.put_object(bucket: @s3_bucket, key: "ami/ami-mappings.json", body: mapping_file)
95
+ end
96
+
97
+ output 'Update finished! ( ͡° ͜ʖ ͡°)'
98
+ end
99
+
100
+ def get_stacks_from_exports(exports, old_amis)
101
+ stacks = []
102
+ old_amis.each do |old_ami|
103
+ exports.each do |export|
104
+ if export.value.eql?(old_ami)
105
+ stacks.insert(0,export.exporting_stack_id)
106
+ end
107
+ end
108
+ end
109
+ stacks
110
+ end
111
+
112
+ def update_ami_mappings(mappings, ami_template, env, new_ami_id)
113
+ old_values = []
114
+ mappings.each do |item|
115
+ if item['ami_template'].eql?(ami_template) and item['env'].eql?(env)
116
+ old_values.insert(0,item['ami_id'])
117
+ item['ami_id'] = new_ami_id
118
+ end
119
+ end
120
+ old_values.uniq
121
+ end
122
+
123
+ def update_stack_parameters(stack_parameters, new_parameters)
124
+ new_parameters.each do |new_param|
125
+ stack_parameters.reject{ |k| k["parameter_key"] == new_param["parameter_key"] }
126
+ stack_parameters.push(new_param)
127
+ end
128
+ stack_parameters
129
+ end
130
+
131
+ def update_instance_name(ami_id, ami_name, ami_description, ecs_json_path)
132
+ ecs_json = JSON.parse File.read(ecs_json_path)
133
+
134
+ ecs_json['builders'][0]['source_ami'] = ami_id
135
+ ecs_json['builders'][0]['ami_name'] = ami_name
136
+ ecs_json['builders'][0]['ami_description'] = ami_description
137
+
138
+ File.open ecs_json_path, 'w' do |f|
139
+ f.write(JSON.pretty_generate(ecs_json))
140
+ end
141
+ end
142
+
143
+ def get_desired_capacity(stack_name)
144
+ stack_outputs = get_stack_outputs(stack_name)
145
+ stack_outputs.each do |out|
146
+ if out.export_name.include?('EC2Fleet') or out.export_name.include?('AutoScalingGroup')
147
+ return get_autoscaling_capacity(out.output_value)
148
+ end
149
+ end
150
+ end
151
+
152
+ def update_packer(json_filename, env)
153
+ execute_command "AWS_MAX_ATTEMPTS=90 AWS_POLL_DELAY_SECONDS=60 packer build -var datadog_apikey=`biscuit get -f ../configs/secrets/common.yml common_production_apikey_datadog` -var github_token=`biscuit get -f ../configs/secrets/common.yml common_private_repo_github_token` -var drone_key=\"`biscuit get -f ../configs/secrets/common.yml drone_license_key`\" -var env=#{env} -machine-readable ./#{json_filename} | tee build.log"
154
+ `grep 'artifact,0,id' build.log | cut -d, -f6 | cut -d: -f2`.sub(/\n/, '')
155
+ end
156
+
157
+ def help_content
158
+ <<-HELP
159
+
160
+ twig-update-ami
161
+ ===========
162
+
163
+ Updates ami version and applies it to stacks on cloudformation
164
+
165
+ Synopsis
166
+ --------
167
+
168
+ twig-update-ami
169
+
170
+ Description
171
+ -----------
172
+
173
+ from artemide main folder run
174
+ `twig-update-ami ${AMI_TEMPLATE} ${AMI_ID} ${AMI_NAME} ${AMI_DESCRIPTION} ${ENV}`
175
+
176
+ Subcommand for Twig: <http://rondevera.github.io/twig/>
177
+ Author: Eugenio Laghi <https://github.com/eugeniolaghi>
178
+
179
+ HELP
180
+ end
181
+
182
+ class ::Hash
183
+ def deep_merge(second)
184
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
185
+ self.merge(second.to_h, &merger)
186
+ end
187
+ end
188
+
189
+ args = ARGV.dup
190
+
191
+ if args.include?('--help')
192
+ puts help_content
193
+ exit
194
+ end
195
+
196
+ TwigUpdateAmi.new.execute!(args)
197
+ end
@@ -1,60 +1,73 @@
1
1
  require 'colorize'
2
+ require 'English'
2
3
  require 'optparse'
3
4
 
4
- module Command
5
- def exec_step command, output = nil
5
+ module Command
6
+ # executes command and exits if status != 0
7
+ def exec_step(command, output = nil)
6
8
  if output
7
9
  puts output
8
10
  else
9
- puts "exec > ".green + "#{command}".yellow
11
+ puts 'exec > '.green + command.to_s.yellow
10
12
  end
11
13
  `#{command}`
12
- exit($?.exitstatus) unless 0 === $?.exitstatus
14
+ exit($CHILD_STATUS.exitstatus) unless $CHILD_STATUS.exitstatus.zero?
13
15
  end
14
16
 
15
- def output msg
16
- puts "twig binaries > ".black + msg
17
+ def output(msg)
18
+ puts 'twig binaries > '.black + msg
17
19
  end
18
20
 
19
- def stop_if check, msg
21
+ def stop_if(check, msg, command = '')
20
22
  if check
21
- output_msg = case msg
22
- when Symbol
23
- symbol_message msg
24
- when Array
25
- array_message msg
26
- else
27
- msg
28
- end
29
- puts "there was a problem > ".red + output_msg
23
+ output_msg =
24
+ case msg
25
+ when Symbol
26
+ symbol_message msg
27
+ when Array
28
+ array_message msg
29
+ else
30
+ msg
31
+ end
32
+ command.empty? || exec(command)
33
+ puts 'there was a problem > '.red + output_msg
30
34
  exit(1)
31
35
  end
32
36
  end
33
37
 
34
- def symbol_message s
38
+ def symbol_message(s)
35
39
  case s
36
40
  when :clean
37
- "hai dei file non committati...non posso continuare'"
41
+ 'hai dei file non committati...non posso continuare'
38
42
  when :detached_head
39
43
  "repo in stato 'head detached'"
40
44
  when :wrong_args
41
- "argomento non corretto"
45
+ 'argomento non corretto'
42
46
  end
43
47
  end
44
48
 
45
- def array_message arr
49
+ def array_message(arr)
46
50
  case arr[0]
47
51
  when :wrong_args
48
52
  msg = symbol_message arr[0]
49
53
  msg += "\n"
50
- msg += " valore possibile: "
51
- values = "[ " + arr[1].join(" | ").yellow + " ]"
54
+ msg += ' valore possibile: '
55
+ values = '[ ' + arr[1].join(' | ').yellow + ' ]'
52
56
  msg += values
53
57
  msg
54
58
  end
55
59
  end
56
60
 
57
- def stop_unless check, msg
58
- stop_if (not check), msg
61
+ def stop_unless(check, msg, command = '')
62
+ stop_if !check, msg, command
63
+ end
64
+
65
+ # executes command and returns properly colored output
66
+ def execute_command(cmd)
67
+ output "Eseguo #{cmd}".yellow
68
+ res = `#{cmd}`
69
+ color = $CHILD_STATUS.exitstatus.zero? ? 'green' : 'red'
70
+ output res.send color
71
+ stop_if (color == 'red'), "Errore durante la build dell'artifact".red
59
72
  end
60
73
  end
@@ -0,0 +1,494 @@
1
+ require 'aws-sdk-autoscaling'
2
+ require 'aws-sdk-batch'
3
+ require 'aws-sdk-cloudformation'
4
+ require 'aws-sdk-cloudfront'
5
+ require 'aws-sdk-ec2'
6
+ require 'aws-sdk-ecs'
7
+ require 'aws-sdk-elasticloadbalancingv2'
8
+ require 'aws-sdk-s3'
9
+ require 'colorize'
10
+ #
11
+ module PrimaAwsClient
12
+ def s3_client
13
+ @s3 ||= Aws::S3::Client.new
14
+ end
15
+
16
+ def cf_client
17
+ @cf ||= Aws::CloudFormation::Client.new
18
+ end
19
+
20
+ def asg_client
21
+ @asg ||= Aws::AutoScaling::Client.new
22
+ end
23
+
24
+ def ec2_client
25
+ @ec2 ||= Aws::EC2::Client.new
26
+ end
27
+
28
+ def alb_client
29
+ @alb ||= Aws::ElasticLoadBalancingV2::Client.new
30
+ end
31
+
32
+ def ecs_client
33
+ @ecs ||= Aws::ECS::Client.new
34
+ end
35
+
36
+ def stack_list
37
+ stacks = []
38
+ next_token = ''
39
+ loop do
40
+ print '.'.yellow; STDOUT.flush
41
+ options = next_token != '' ? { next_token: next_token } : {}
42
+ begin
43
+ resp = cf_client.describe_stacks(options)
44
+ rescue Aws::CloudFormation::Errors::Throttling => e
45
+ output 'Throttling, retrying in 15 seconds'.red
46
+ sleep 15
47
+ resp = cf_client.describe_stacks(options)
48
+ end
49
+ stacks += resp.stacks
50
+ break unless resp.next_token
51
+ next_token = resp.next_token
52
+ end
53
+ puts '.'.yellow; STDOUT.flush
54
+ stacks.keep_if { |stack| stack.stack_name.include? '-qa-' }
55
+ stacks
56
+ end
57
+
58
+ def cluster_list
59
+ stacks = []
60
+ next_token = ''
61
+ loop do
62
+ print '.'.yellow; STDOUT.flush
63
+ options = next_token != '' ? { next_token: next_token } : {}
64
+ begin
65
+ resp = cf_client.describe_stacks(options)
66
+ rescue Aws::CloudFormation::Errors::Throttling => e
67
+ output 'Throttling, retrying in 15 seconds'.red
68
+ sleep 15
69
+ resp = cf_client.describe_stacks(options)
70
+ end
71
+ stacks += resp.stacks
72
+ break unless resp.next_token
73
+ next_token = resp.next_token
74
+ end
75
+ puts '.'.yellow; STDOUT.flush
76
+ stacks.keep_if { |stack| stack.stack_name.include? 'ecs-cluster-qa-' }
77
+ stacks
78
+ end
79
+
80
+ def list_exports
81
+ exports = []
82
+ next_token = ''
83
+ loop do
84
+ print '.'.yellow; STDOUT.flush
85
+ options = next_token != '' ? { next_token: next_token } : {}
86
+ begin
87
+ resp = cf_client.list_exports(options)
88
+ rescue Aws::CloudFormation::Errors::Throttling => e
89
+ output 'Throttling, retrying in 15 seconds'.red
90
+ sleep 15
91
+ resp = cf_client.list_exports(options)
92
+ end
93
+ exports += resp.exports
94
+ break unless resp.next_token
95
+ next_token = resp.next_token
96
+ end
97
+ puts '.'.yellow; STDOUT.flush
98
+ exports
99
+ end
100
+
101
+ def create_stack(stack_name, stack_body, parameters = [], tags = [], role = nil)
102
+ cf_args = {
103
+ stack_name: stack_name,
104
+ template_body: stack_body,
105
+ parameters: parameters,
106
+ tags: tags,
107
+ capabilities: ['CAPABILITY_IAM'],
108
+ on_failure: 'ROLLBACK'
109
+ }
110
+
111
+ if role != nil then
112
+ cf_args.merge!(role_arn: role)
113
+ end
114
+
115
+ begin
116
+ cf_client.create_stack(cf_args)
117
+ rescue Aws::CloudFormation::Errors::Throttling, Aws::CloudFormation::Errors::LimitExcedeedException => e
118
+ output 'Throttling, retrying in 15 seconds'.red
119
+ sleep 15
120
+ create_stack(stack_name, stack_body, parameters = [], tags = [])
121
+ else
122
+ output "La creazione dello stack #{stack_name} è stata avviata".green
123
+ end
124
+ end
125
+
126
+ def update_stack(stack_name, template_body, parameters = [], tags = [], role = nil)
127
+ cf_args = {
128
+ stack_name: stack_name,
129
+ template_body: template_body,
130
+ parameters: parameters,
131
+ tags: tags,
132
+ capabilities: ['CAPABILITY_IAM']
133
+ }
134
+
135
+ if role != nil then
136
+ cf_args.merge!(role_arn: role)
137
+ end
138
+
139
+ begin
140
+ cf_client.update_stack(cf_args)
141
+ rescue Aws::CloudFormation::Errors::Throttling => e
142
+ output 'Throttling, retrying in 15 seconds'.red
143
+ sleep 15
144
+ update_stack(stack_name, template_body, parameters = [], tags = [])
145
+ rescue Aws::CloudFormation::Errors::ValidationError => e
146
+ raise e
147
+ else
148
+ output "L'update dello stack #{stack_name} è stato avviato".green
149
+ end
150
+ end
151
+
152
+ def update_stack_url(stack_name, template_url, parameters = [], tags = [], role = nil)
153
+ cf_args = {
154
+ stack_name: stack_name,
155
+ template_url: template_url,
156
+ parameters: parameters,
157
+ tags: tags,
158
+ capabilities: ['CAPABILITY_IAM']
159
+ }
160
+
161
+ if role != nil then
162
+ cf_args.merge!(role_arn: role)
163
+ end
164
+
165
+ begin
166
+ cf_client.update_stack(cf_args)
167
+ rescue Aws::CloudFormation::Errors::Throttling => e
168
+ output 'Throttling, retrying in 15 seconds'.red
169
+ sleep 15
170
+ update_stack_url(stack_name, template_url, parameters = [], tags = [])
171
+ rescue Aws::CloudFormation::Errors::ValidationError => e
172
+ raise e
173
+ else
174
+ output "L'update dello stack #{stack_name} è stato avviato".green
175
+ end
176
+ end
177
+
178
+ def stack_exists?(stack_name)
179
+ begin
180
+ cf_client.describe_stacks(stack_name: stack_name)
181
+ rescue Aws::CloudFormation::Errors::Throttling => e
182
+ output 'Throttling, retrying in 15 seconds'.red
183
+ sleep 15
184
+ stack_exists?(stack_name)
185
+ rescue Aws::CloudFormation::Errors::ValidationError => e
186
+ return false if e.message.include? 'does not exist'
187
+ raise e
188
+ else
189
+ true
190
+ end
191
+ end
192
+
193
+ def delete_stack(stack_name)
194
+ begin
195
+ cf_client.delete_stack(stack_name: stack_name)
196
+ rescue Aws::CloudFormation::Errors::Throttling => e
197
+ output 'Throttling, retrying in 15 seconds'.red
198
+ sleep 15
199
+ delete_stack(stack_name)
200
+ else
201
+ output "Stack #{stack_name} spenta con successo\n".green
202
+ end
203
+ end
204
+
205
+ def wait_for_stack_ready(stack_name, failed_statuses = ['CREATE_FAILED', 'ROLLBACK_IN_PROGRESS', 'ROLLBACK_FAILED', 'DELETE_IN_PROGRESS', 'DELETE_FAILED', 'DELETE_COMPLETE', 'UPDATE_ROLLBACK_FAILED', 'UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS'])
206
+ ready = false
207
+ sleep_seconds = 13
208
+ output "Attendo che lo stack #{stack_name} finisca di essere inizializzato...\n".yellow
209
+ while !ready
210
+ ready = true if stack_ready?(stack_name, failed_statuses)
211
+ seconds_elapsed = 0
212
+ loop do
213
+ break if seconds_elapsed >= sleep_seconds
214
+ print '.'.yellow; STDOUT.flush
215
+ sleep 1
216
+ seconds_elapsed += 1
217
+ end
218
+ end
219
+ output "\nStack #{stack_name} pronto!\n".green
220
+ end
221
+
222
+ def wait_for_stack_removal(stack_name)
223
+ ready = false
224
+ sleep_seconds = 13
225
+ sleep 10
226
+ output "Attendo che lo stack #{stack_name} finisca di essere cancellato...\n".yellow
227
+ while !ready
228
+ ready = true if stack_deleted?(stack_name)
229
+ seconds_elapsed = 0
230
+ loop do
231
+ break if seconds_elapsed >= sleep_seconds
232
+ print '.'.yellow; STDOUT.flush
233
+ sleep 1
234
+ seconds_elapsed += 1
235
+ end
236
+ end
237
+ output "\nStack #{stack_name} eliminato!\n".green
238
+ end
239
+
240
+ def get_stack_tags(name)
241
+ begin
242
+ resp = cf_client.describe_stacks(stack_name: name)
243
+ rescue Aws::CloudFormation::Errors::Throttling => e
244
+ output 'Throttling, retrying in 15 seconds'.red
245
+ sleep 15
246
+ get_stack_tags(name)
247
+ else
248
+ resp.stacks[0].tags
249
+ end
250
+ end
251
+
252
+ def get_stack_parameters(name)
253
+ begin
254
+ resp = cf_client.describe_stacks(stack_name: name)
255
+ rescue Aws::CloudFormation::Errors::Throttling => e
256
+ output 'Throttling, retrying in 15 seconds'.red
257
+ sleep 15
258
+ get_stack_parameters(name)
259
+ else
260
+ resp.stacks[0].parameters
261
+ end
262
+ end
263
+
264
+ def get_stack_outputs(name)
265
+ begin
266
+ resp = cf_client.describe_stacks(stack_name: name)
267
+ rescue Aws::CloudFormation::Errors::Throttling => e
268
+ output 'Throttling, retrying in 15 seconds'.red
269
+ sleep 15
270
+ get_stack_outputs(name)
271
+ else
272
+ resp.stacks[0].outputs
273
+ end
274
+ end
275
+
276
+ def get_stack_template(name)
277
+ begin
278
+ resp = cf_client.get_template(stack_name: name)
279
+ rescue Aws::CloudFormation::Errors::Throttling => e
280
+ output 'Throttling, retrying in 15 seconds'.red
281
+ sleep 15
282
+ get_stack_template(name)
283
+ else
284
+ resp.template_body
285
+ end
286
+
287
+ end
288
+
289
+ def stack_ready?(stack_name, failed_statuses = ['CREATE_FAILED', 'ROLLBACK_IN_PROGRESS', 'ROLLBACK_FAILED', 'DELETE_IN_PROGRESS', 'DELETE_FAILED', 'DELETE_COMPLETE', 'UPDATE_ROLLBACK_FAILED', 'UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS'])
290
+ begin
291
+ resp = cf_client.describe_stacks(
292
+ stack_name: stack_name
293
+ )
294
+ stack_status = resp.stacks[0].stack_status
295
+ rescue Aws::CloudFormation::Errors::Throttling => e
296
+ print 'Throttling'.red; STDOUT.flush
297
+ return false
298
+ end
299
+ raise "The stack #{stack_name} errored out" if failed_statuses.include? stack_status
300
+ ['CREATE_COMPLETE', 'UPDATE_COMPLETE', 'UPDATE_ROLLBACK_COMPLETE', 'ROLLBACK_COMPLETE'].include? stack_status
301
+ end
302
+
303
+ def stack_deleted?(stack_name, failed_statuses = ['ROLLBACK_IN_PROGRESS', 'ROLLBACK_FAILED', 'DELETE_FAILED', 'UPDATE_ROLLBACK_FAILED', 'UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS'])
304
+ begin
305
+ resp = cf_client.describe_stacks(
306
+ stack_name: stack_name
307
+ )
308
+ stack_status = resp.stacks[0].stack_status
309
+ rescue Aws::CloudFormation::Errors::Throttling => e
310
+ print 'Throttling'.red; STDOUT.flush
311
+ return false
312
+ rescue Aws::CloudFormation::Errors::ValidationError => e
313
+ print 'Stack deleted'
314
+ return true
315
+ end
316
+ raise "The stack #{stack_name} errored out" if failed_statuses.include? stack_status
317
+ ['DELETE_COMPLETE'].include? stack_status
318
+ end
319
+
320
+ def artifact_exists?(bucket, path)
321
+ resp = s3_client.list_objects(
322
+ bucket: bucket,
323
+ max_keys: 1,
324
+ prefix: path
325
+ )
326
+ !resp.contents.empty?
327
+ end
328
+
329
+ def upload_artifact(source_path, destination_path, bucket_name_override=nil)
330
+ output "Upload dell'artifact in corso (#{(File.size(source_path).to_f / 2**20).round(2)} MiB)\n".yellow
331
+ s3 = Aws::S3::Resource.new
332
+ s3_bucket = if !bucket_name_override.nil? then bucket_name_override else @s3_bucket end
333
+ puts s3_bucket
334
+ obj = s3.bucket(s3_bucket).object(destination_path)
335
+ obj.upload_file(source_path)
336
+
337
+ output "#{s3_bucket}/#{destination_path} uploadato con successo!\n".green
338
+ end
339
+
340
+ def wait_for_artifact(bucket, path)
341
+ ready = artifact_exists?(bucket, path)
342
+ sleep_seconds = 13
343
+ output "Attendo che sia pronto l'artefatto #{path}...\n".yellow
344
+ retries = 0
345
+ while !ready
346
+ ready = true if artifact_exists?(bucket, path)
347
+ seconds_elapsed = 0
348
+ loop do
349
+ break if seconds_elapsed >= sleep_seconds
350
+ print '.'.yellow; STDOUT.flush
351
+ sleep 1
352
+ seconds_elapsed += 1
353
+ end
354
+ retries += 1
355
+ if retries > 150
356
+ output "\n Timeout raggiunto aspettando #{path}\n".red
357
+ exit
358
+ end
359
+ end
360
+ output "\nArtefatto #{path} creato!\n".green
361
+ end
362
+
363
+ def list_import_stacks(export_name)
364
+ stacks = []
365
+ next_token = ''
366
+ loop do
367
+ print '.'.yellow; STDOUT.flush
368
+ options = next_token != '' ? { export_name: export_name, next_token: next_token } : {export_name: export_name}
369
+ begin
370
+ resp = cf_client.list_imports(options)
371
+ rescue Aws::CloudFormation::Errors::Throttling => e
372
+ output 'Throttling, retrying in 15 seconds'.red
373
+ sleep 15
374
+ resp = cf_client.list_imports(options)
375
+ end
376
+ stacks += resp.imports
377
+ break unless resp.next_token
378
+ next_token = resp.next_token
379
+ end
380
+ stacks
381
+ end
382
+
383
+ def describe_stack_resource(cluster_stack_name, logical_resource_id)
384
+ begin
385
+ resp = cf_client.describe_stack_resource({stack_name: cluster_stack_name, logical_resource_id: logical_resource_id})
386
+ rescue Aws::CloudFormation::Errors::Throttling => e
387
+ output 'Throttling, retrying in 15 seconds'.red
388
+ sleep 15
389
+ resp = describe_stack_resource(cluster_stack_name, logical_resource_id)
390
+ end
391
+ end
392
+
393
+ def describe_instances(instance_ids)
394
+ begin
395
+ resp = ec2_client.describe_instances({instance_ids: instance_ids})
396
+ rescue Aws::CloudFormation::Errors::Throttling => e
397
+ output 'Throttling, retrying in 15 seconds'.red
398
+ sleep 15
399
+ resp = describe_instances(instance_ids)
400
+ end
401
+ end
402
+
403
+ def describe_auto_scaling_groups(auto_scaling_group_names, max_records)
404
+ begin
405
+ resp = asg_client.describe_auto_scaling_groups({
406
+ auto_scaling_group_names: auto_scaling_group_names,
407
+ max_records: max_records
408
+ })
409
+ rescue Aws::CloudFormation::Errors::Throttling => e
410
+ output 'Throttling, retrying in 15 seconds'.red
411
+ sleep 15
412
+ resp = describe_auto_scaling_groups(auto_scaling_group_names, max_records)
413
+ end
414
+ end
415
+
416
+ def describe_load_balancers(load_balancer_arns)
417
+ begin
418
+ resp = alb_client.describe_load_balancers({load_balancer_arns: load_balancer_arns})
419
+ rescue Aws::CloudFormation::Errors::Throttling => e
420
+ output 'Throttling, retrying in 15 seconds'.red
421
+ sleep 15
422
+ resp = describe_load_balancers(load_balancer_arns)
423
+ end
424
+ end
425
+
426
+ def update_ecs_service(cluster, service, deployment_configuration)
427
+ begin
428
+ resp = ecs_client.update_service(
429
+ cluster: cluster,
430
+ service: service,
431
+ deployment_configuration: deployment_configuration
432
+ )
433
+ rescue Aws::CloudFormation::Errors::Throttling => e
434
+ output 'Throttling, retrying in 15 seconds'.red
435
+ sleep 15
436
+ resp = update_ecs_service(cluster, service, deployment_configuration)
437
+ end
438
+ end
439
+
440
+ def describe_ecs_tasks(cluster, tasks)
441
+ begin
442
+ resp = ecs_client.describe_tasks({
443
+ cluster: cluster,
444
+ tasks: tasks
445
+ })
446
+ rescue Aws::CloudFormation::Errors::Throttling => e
447
+ output 'Throttling, retrying in 15 seconds'.red
448
+ sleep 15
449
+ resp = describe_ecs_tasks(cluster, tasks)
450
+ end
451
+ end
452
+
453
+ def run_ecs_task(cluster, task_definition, overrides, count)
454
+ begin
455
+ resp = ecs_client.run_task({
456
+ cluster: cluster,
457
+ task_definition: task_definition,
458
+ overrides: overrides,
459
+ count: count
460
+ })
461
+ rescue Aws::CloudFormation::Errors::Throttling => e
462
+ output 'Throttling, retrying in 15 seconds'.red
463
+ sleep 15
464
+ resp = run_ecs_task(cluster, task_definition, overrides, count)
465
+ end
466
+ end
467
+
468
+ def get_autoscaling_capacity(asg_name)
469
+ resp = asg_client.describe_auto_scaling_groups(auto_scaling_group_names: [asg_name])
470
+ resp.auto_scaling_groups[0].desired_capacity
471
+ end
472
+
473
+ def get_spotfleet_capacity(fleet_arn)
474
+ resp = ec2_client.describe_spot_fleet_requests(spot_fleet_request_ids: [fleet_arn])
475
+ resp.spot_fleet_request_configs[0].spot_fleet_request_config.target_capacity
476
+ end
477
+
478
+ def hashes_to_tags(hashes)
479
+ tags = []
480
+ hkeys = hashes.keys
481
+ hkeys.each do |hkey|
482
+ tags.insert(0, { key: hkey, value: hashes[hkey].to_s })
483
+ end
484
+ tags
485
+ end
486
+
487
+ def tags_to_hashes(tags)
488
+ hash = Hash.new
489
+ tags.each do |tags_obj|
490
+ hash[tags_obj.key] = tags_obj.value
491
+ end
492
+ hash
493
+ end
494
+ end