prima-twig 1.2.2 → 1.3.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 +4 -4
- data/bin/twig-feature +3 -3
- data/bin/twig-packer-builder +84 -0
- data/bin/twig-update-ami +48 -104
- data/lib/command.rb +2 -2
- data/lib/prima_aws_client.rb +146 -146
- data/lib/prima_twig.rb +2 -2
- metadata +13 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dca7d3d6359bd512ce34021d35d023a917507ae1004de5f758abe3f70f6f1c10
|
4
|
+
data.tar.gz: 14221958e4695b6ff58ac68a3c332f9b735ad2ca99093f72eaad8d1ffe4989a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e2d5a682591fee449f6e62e2c7dc07bb9cb5b9fe6cf3ad265698daf5f484df20b6d77d84ca4edd279191bab807849ee7a395ed734cc4e34a55ad03d9fb1233f8
|
7
|
+
data.tar.gz: 754189d1268adc751f5d44fa20422bba0fb712e5024560234049bf417e8d59c62d7a1fd6c02caad8e740e948fd58e448aabec7112e98336637654070d3314f71
|
data/bin/twig-feature
CHANGED
@@ -326,7 +326,7 @@ class Release
|
|
326
326
|
tags.push tag_keep_data
|
327
327
|
end
|
328
328
|
|
329
|
-
update_cluster_stack(cluster_stack_name, tags)
|
329
|
+
update_cluster_stack(cluster_stack_name, tags, get_stack_parameters(cluster_stack_name))
|
330
330
|
|
331
331
|
output "Finito!".green
|
332
332
|
end
|
@@ -581,9 +581,9 @@ class Release
|
|
581
581
|
resp.load_balancers[0].dns_name
|
582
582
|
end
|
583
583
|
|
584
|
-
def update_cluster_stack(stack_name, tags = [])
|
584
|
+
def update_cluster_stack(stack_name, tags = [], parameters = [])
|
585
585
|
stack_body = get_stack_template(stack_name)
|
586
|
-
update_stack(stack_name, stack_body,
|
586
|
+
update_stack(stack_name, stack_body, parameters, tags)
|
587
587
|
end
|
588
588
|
|
589
589
|
def choose_branch_to_deploy(project_name, select_master = false)
|
@@ -0,0 +1,84 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require_relative '../lib/prima_twig'
|
5
|
+
require 'launchy'
|
6
|
+
require 'yaml'
|
7
|
+
|
8
|
+
class TwigPackerBuilder
|
9
|
+
include Command
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
output 'Checking for new gem releases...'
|
13
|
+
unless `gem outdated`.lines.grep(/^prima-twig \(.*\)/).empty?
|
14
|
+
exec "gem update prima-twig && twig update-ami #{ARGV.join ' '}"
|
15
|
+
end
|
16
|
+
@templates_path = "#{Dir.pwd}/amis"
|
17
|
+
end
|
18
|
+
|
19
|
+
def execute!(args)
|
20
|
+
@template_name = args[0]
|
21
|
+
@env = args[1]
|
22
|
+
@country = args[2]
|
23
|
+
|
24
|
+
File.open("#{Dir.pwd}/ami_state.yml", 'r') do |f|
|
25
|
+
@config = YAML.load(f.read)
|
26
|
+
end
|
27
|
+
|
28
|
+
@country_conf = @config['countries'].detect { |country| country['name'] == @country }
|
29
|
+
stop_if(@country_conf.nil?, "Cannot find country #{@country} inside ami_state.yml, exiting...".red)
|
30
|
+
|
31
|
+
@ami_conf = @country_conf['amis'].detect { |ami| ami['template'] == @template_name and ami['env'] == @env }
|
32
|
+
stop_if(@ami_conf.nil?, "Cannot find an ami in #{@country} generated from #{@template_name} and env #{@env} inside ami_state.yml, exiting...".red)
|
33
|
+
|
34
|
+
output "Starting update process with #{@template_name} in env #{@env} in country #{@country}".light_green
|
35
|
+
output 'running packer (this could take some time)'.light_green
|
36
|
+
new_ami_id = execute_packer
|
37
|
+
stop_if(new_ami_id.to_s.empty?, 'Failed to generate AMI!'.red)
|
38
|
+
|
39
|
+
output "new ami id: #{new_ami_id}".light_green
|
40
|
+
output "ami id that can be replaced: #{@ami_conf['id']}".light_green
|
41
|
+
output "you can run \"aws-vault exec YOUR_CONTRY_PROFILE -- twig-update-ami #{@ami_conf['id']} #{new_ami_id}\" to update all stacks".light_green
|
42
|
+
output 'Done'.light_green
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def execute_packer
|
48
|
+
execute_command "AWS_MAX_ATTEMPTS=90 AWS_POLL_DELAY_SECONDS=60 packer build -var env=#{@env} -var teams_mapping=#{@ami_conf['teams_mapping']} -var country=#{@country} -var aws_account=#{@country_conf['aws_account']} -machine-readable #{@templates_path}/#{@template_name} | tee build.log"
|
49
|
+
|
50
|
+
`grep 'artifact,0,id' build.log | cut -d, -f6 | cut -d: -f2`.sub(/\n/, '')
|
51
|
+
end
|
52
|
+
|
53
|
+
def help_content
|
54
|
+
<<-HELP
|
55
|
+
|
56
|
+
twig-packer-builder
|
57
|
+
===========
|
58
|
+
|
59
|
+
Creates AMIs using umami's templates
|
60
|
+
|
61
|
+
Synopsis
|
62
|
+
--------
|
63
|
+
|
64
|
+
twig-packer-builder
|
65
|
+
|
66
|
+
Description
|
67
|
+
-----------
|
68
|
+
|
69
|
+
from umami main folder run
|
70
|
+
`aws-vault exec AWS_PROFILE_WITH_ACCESS_TO_COMMON_ACCOUNT -- twig-update-ami ${AMI_TEMPLATE} ${ENV} ${COUNTRY}`
|
71
|
+
|
72
|
+
Subcommand for Twig: <http://rondevera.github.io/twig/>
|
73
|
+
HELP
|
74
|
+
end
|
75
|
+
|
76
|
+
args = ARGV.dup
|
77
|
+
|
78
|
+
if args.include?('--help')
|
79
|
+
puts help_content
|
80
|
+
exit
|
81
|
+
end
|
82
|
+
|
83
|
+
TwigPackerBuilder.new.execute!(args)
|
84
|
+
end
|
data/bin/twig-update-ami
CHANGED
@@ -1,154 +1,105 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
|
-
require_relative '../lib/prima_twig
|
5
|
-
require_relative '../lib/prima_aws_client
|
4
|
+
require_relative '../lib/prima_twig'
|
5
|
+
require_relative '../lib/prima_aws_client'
|
6
6
|
require 'launchy'
|
7
|
-
require '
|
8
|
-
require 'aws-sdk-s3'
|
7
|
+
require 'yaml'
|
9
8
|
|
10
9
|
class TwigUpdateAmi
|
11
10
|
include Command
|
12
11
|
include PrimaAwsClient
|
12
|
+
|
13
13
|
def initialize
|
14
|
-
output '
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
@
|
14
|
+
output 'Checking for new gem releases...'
|
15
|
+
unless `gem outdated`.lines.grep(/^prima-twig \(.*\)/).empty?
|
16
|
+
exec "gem update prima-twig && twig update-ami #{ARGV.join ' '}"
|
17
|
+
end
|
18
|
+
@state_path = "#{Dir.pwd}/ami_state.yml"
|
19
19
|
end
|
20
20
|
|
21
21
|
def execute!(args)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
private
|
22
|
+
old_ami = args[0]
|
23
|
+
new_ami = args[1]
|
26
24
|
|
27
|
-
|
28
|
-
|
29
|
-
|
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-0d8488b68731e8756'
|
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")
|
25
|
+
output "retrieving stacks that uses old ami #{old_ami}".light_green
|
26
|
+
stacks = get_stacks_from_exports(list_exports, old_ami)
|
27
|
+
stop_if(stacks.empty?, 'No stack to update! Please check manually'.red)
|
47
28
|
|
48
29
|
stacks.each do |stack|
|
49
|
-
output "processing stack #{stack}"
|
30
|
+
output "processing stack #{stack}".light_green
|
50
31
|
if stack.include?('qa')
|
51
|
-
output "skipping stack #{stack} because is a qa"
|
32
|
+
output "skipping stack #{stack} because is a qa".yellow
|
52
33
|
next
|
53
34
|
else
|
54
35
|
stack_parameters = get_stack_parameters(stack)
|
55
|
-
stack_parameters = update_stack_parameters(stack_parameters,
|
56
|
-
|
57
|
-
|
58
|
-
stack_template = File.read("./cloudformation/stacks/batch/compute-environment-offsite-backups.yml")
|
59
|
-
else
|
60
|
-
stack_template = File.read("./cloudformation/stacks/batch/compute-environment.yml")
|
61
|
-
end
|
62
|
-
else
|
63
|
-
stack_parameters = update_stack_parameters(stack_parameters, "DesiredCapacity", get_desired_capacity(stack).to_s)
|
64
|
-
stack_template = File.read("./cloudformation/stacks/asg/#{stack.to_s.split("/")[1]}.yml")
|
36
|
+
stack_parameters = update_stack_parameters(stack_parameters, 'AMIID', new_ami)
|
37
|
+
unless stack.include?('batch')
|
38
|
+
stack_parameters = update_stack_parameters(stack_parameters, 'DesiredCapacity', get_desired_capacity(stack).to_s)
|
65
39
|
end
|
66
|
-
|
40
|
+
|
41
|
+
update_stack_reuse_template(stack, stack_parameters)
|
67
42
|
end
|
68
43
|
end
|
69
44
|
|
70
45
|
stacks.each do |stack|
|
71
|
-
if stack.include?('qa')
|
72
|
-
|
46
|
+
next if stack.include?('qa')
|
47
|
+
|
48
|
+
wait_for_stack_ready(stack, %w[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])
|
49
|
+
end
|
50
|
+
|
51
|
+
output 'Saving new ami in ami_state.yml'.light_green
|
52
|
+
File.open(@state_path, 'r') do |f|
|
53
|
+
@config = YAML.load(f.read)
|
54
|
+
end
|
55
|
+
|
56
|
+
@config['countries'].each do |country|
|
57
|
+
country['amis'].each do |ami|
|
58
|
+
ami['id'] = new_ami if ami['id'] == old_ami
|
73
59
|
end
|
74
|
-
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'])
|
75
60
|
end
|
76
61
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
f.write(mapping_file)
|
81
|
-
@s3.put_object(bucket: @s3_bucket, key: "ami/ami-mappings.json", body: mapping_file)
|
62
|
+
File.open(@state_path, 'w+') do |f|
|
63
|
+
yaml = YAML.dump(@config)
|
64
|
+
f.write(yaml)
|
82
65
|
end
|
83
66
|
|
84
|
-
output 'Update finished! ( ͡° ͜ʖ ͡°)'
|
67
|
+
output 'Update finished! ( ͡° ͜ʖ ͡°)'.light_green
|
85
68
|
end
|
86
69
|
|
87
|
-
|
70
|
+
private
|
71
|
+
|
72
|
+
def get_stacks_from_exports(exports, old_ami)
|
88
73
|
stacks = []
|
89
|
-
|
90
|
-
|
91
|
-
if export.value.eql?(old_ami)
|
92
|
-
stacks.insert(0,export.exporting_stack_id)
|
93
|
-
end
|
94
|
-
end
|
74
|
+
exports.each do |export|
|
75
|
+
stacks.insert(0, export.exporting_stack_id) if export.value.eql?(old_ami)
|
95
76
|
end
|
96
77
|
stacks
|
97
78
|
end
|
98
79
|
|
99
|
-
def update_ami_mappings(mappings, ami_template, env, new_ami_id)
|
100
|
-
old_values = []
|
101
|
-
mappings.each do |item|
|
102
|
-
if item['ami_template'].eql?(ami_template) and item['env'].eql?(env)
|
103
|
-
old_values.insert(0,item['ami_id'])
|
104
|
-
item['ami_id'] = new_ami_id
|
105
|
-
end
|
106
|
-
end
|
107
|
-
old_values.uniq
|
108
|
-
end
|
109
|
-
|
110
80
|
def update_stack_parameters(stack_parameters, key, value)
|
111
81
|
stack_parameters.each do |param|
|
112
|
-
if param.parameter_key == key
|
113
|
-
param.parameter_value = value
|
114
|
-
end
|
82
|
+
param.parameter_value = value if param.parameter_key == key
|
115
83
|
end
|
116
84
|
stack_parameters
|
117
85
|
end
|
118
86
|
|
119
|
-
def update_instance_name(ami_id, ami_name, ami_description, ecs_json_path)
|
120
|
-
ecs_json = JSON.parse File.read(ecs_json_path)
|
121
|
-
|
122
|
-
ecs_json['builders'][0]['source_ami'] = ami_id
|
123
|
-
ecs_json['builders'][0]['ami_name'] = ami_name
|
124
|
-
ecs_json['builders'][0]['ami_description'] = ami_description
|
125
|
-
|
126
|
-
File.open ecs_json_path, 'w' do |f|
|
127
|
-
f.write(JSON.pretty_generate(ecs_json))
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
87
|
def get_desired_capacity(stack_name)
|
132
88
|
stack_outputs = get_stack_outputs(stack_name)
|
133
89
|
stack_outputs.each do |out|
|
134
|
-
if out.export_name.include?('EC2Fleet')
|
90
|
+
if out.export_name.include?('EC2Fleet') || out.export_name.include?('AutoScalingGroup') || out.export_name.include?('NodeGroup')
|
135
91
|
return get_autoscaling_capacity(out.output_value)
|
136
92
|
end
|
137
93
|
end
|
138
94
|
end
|
139
95
|
|
140
|
-
def update_packer(json_filename, env)
|
141
|
-
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"
|
142
|
-
`grep 'artifact,0,id' build.log | cut -d, -f6 | cut -d: -f2`.sub(/\n/, '')
|
143
|
-
end
|
144
|
-
|
145
96
|
def help_content
|
146
97
|
<<-HELP
|
147
98
|
|
148
99
|
twig-update-ami
|
149
100
|
===========
|
150
101
|
|
151
|
-
Updates
|
102
|
+
Updates AutoScalingGroups/BatchComputeEnvs with a new AMI
|
152
103
|
|
153
104
|
Synopsis
|
154
105
|
--------
|
@@ -158,8 +109,8 @@ class TwigUpdateAmi
|
|
158
109
|
Description
|
159
110
|
-----------
|
160
111
|
|
161
|
-
from
|
162
|
-
`twig-update-ami ${
|
112
|
+
from tsunami main folder run
|
113
|
+
`aws-vault exec AWS_PROFILE_WITH_ACCESS_TO_COUNTRY_ACCOUNT -- twig-update-ami ${OLD_AMI} ${NEW_AMI}`
|
163
114
|
|
164
115
|
Subcommand for Twig: <http://rondevera.github.io/twig/>
|
165
116
|
Author: Eugenio Laghi <https://github.com/eugeniolaghi>
|
@@ -167,13 +118,6 @@ class TwigUpdateAmi
|
|
167
118
|
HELP
|
168
119
|
end
|
169
120
|
|
170
|
-
class ::Hash
|
171
|
-
def deep_merge(second)
|
172
|
-
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 }
|
173
|
-
self.merge(second.to_h, &merger)
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
121
|
args = ARGV.dup
|
178
122
|
|
179
123
|
if args.include?('--help')
|
data/lib/command.rb
CHANGED
@@ -64,10 +64,10 @@ module Command
|
|
64
64
|
|
65
65
|
# executes command and returns properly colored output
|
66
66
|
def execute_command(cmd)
|
67
|
-
output "
|
67
|
+
output "Executing #{cmd}".yellow
|
68
68
|
res = `#{cmd}`
|
69
69
|
color = $CHILD_STATUS.exitstatus.zero? ? 'green' : 'red'
|
70
70
|
output res.send color
|
71
|
-
stop_if (color == 'red'),
|
71
|
+
stop_if (color == 'red'), 'Error'.red
|
72
72
|
end
|
73
73
|
end
|
data/lib/prima_aws_client.rb
CHANGED
@@ -7,7 +7,6 @@ require 'aws-sdk-ecs'
|
|
7
7
|
require 'aws-sdk-elasticloadbalancingv2'
|
8
8
|
require 'aws-sdk-s3'
|
9
9
|
require 'colorize'
|
10
|
-
#
|
11
10
|
module PrimaAwsClient
|
12
11
|
def s3_client
|
13
12
|
@s3 ||= Aws::S3::Client.new
|
@@ -48,6 +47,7 @@ module PrimaAwsClient
|
|
48
47
|
end
|
49
48
|
stacks += resp.stacks
|
50
49
|
break unless resp.next_token
|
50
|
+
|
51
51
|
next_token = resp.next_token
|
52
52
|
end
|
53
53
|
puts '.'.yellow; STDOUT.flush
|
@@ -70,6 +70,7 @@ module PrimaAwsClient
|
|
70
70
|
end
|
71
71
|
stacks += resp.stacks
|
72
72
|
break unless resp.next_token
|
73
|
+
|
73
74
|
next_token = resp.next_token
|
74
75
|
end
|
75
76
|
puts '.'.yellow; STDOUT.flush
|
@@ -92,6 +93,7 @@ module PrimaAwsClient
|
|
92
93
|
end
|
93
94
|
exports += resp.exports
|
94
95
|
break unless resp.next_token
|
96
|
+
|
95
97
|
next_token = resp.next_token
|
96
98
|
end
|
97
99
|
puts '.'.yellow; STDOUT.flush
|
@@ -108,9 +110,7 @@ module PrimaAwsClient
|
|
108
110
|
on_failure: 'ROLLBACK'
|
109
111
|
}
|
110
112
|
|
111
|
-
|
112
|
-
cf_args.merge!(role_arn: role)
|
113
|
-
end
|
113
|
+
cf_args.merge!(role_arn: role) unless role.nil?
|
114
114
|
|
115
115
|
begin
|
116
116
|
cf_client.create_stack(cf_args)
|
@@ -119,7 +119,7 @@ module PrimaAwsClient
|
|
119
119
|
sleep 15
|
120
120
|
create_stack(stack_name, stack_body, parameters = [], tags = [])
|
121
121
|
else
|
122
|
-
output "
|
122
|
+
output "CloudFormation stack #{stack_name} creation started".green
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
@@ -132,9 +132,7 @@ module PrimaAwsClient
|
|
132
132
|
capabilities: ['CAPABILITY_IAM']
|
133
133
|
}
|
134
134
|
|
135
|
-
|
136
|
-
cf_args.merge!(role_arn: role)
|
137
|
-
end
|
135
|
+
cf_args.merge!(role_arn: role) unless role.nil?
|
138
136
|
|
139
137
|
begin
|
140
138
|
cf_client.update_stack(cf_args)
|
@@ -145,7 +143,7 @@ module PrimaAwsClient
|
|
145
143
|
rescue Aws::CloudFormation::Errors::ValidationError => e
|
146
144
|
raise e
|
147
145
|
else
|
148
|
-
output "
|
146
|
+
output "CloudFormation stack #{stack_name} update started".green
|
149
147
|
end
|
150
148
|
end
|
151
149
|
|
@@ -158,9 +156,7 @@ module PrimaAwsClient
|
|
158
156
|
capabilities: ['CAPABILITY_IAM']
|
159
157
|
}
|
160
158
|
|
161
|
-
|
162
|
-
cf_args.merge!(role_arn: role)
|
163
|
-
end
|
159
|
+
cf_args.merge!(role_arn: role) unless role.nil?
|
164
160
|
|
165
161
|
begin
|
166
162
|
cf_client.update_stack(cf_args)
|
@@ -171,64 +167,87 @@ module PrimaAwsClient
|
|
171
167
|
rescue Aws::CloudFormation::Errors::ValidationError => e
|
172
168
|
raise e
|
173
169
|
else
|
174
|
-
output "
|
170
|
+
output "CloudFormation stack #{stack_name} update started".green
|
175
171
|
end
|
176
172
|
end
|
177
173
|
|
178
|
-
def
|
174
|
+
def update_stack_reuse_template(stack_name, parameters = [], tags = [], role = nil)
|
175
|
+
cf_args = {
|
176
|
+
stack_name: stack_name,
|
177
|
+
use_previous_template: true,
|
178
|
+
parameters: parameters,
|
179
|
+
tags: tags,
|
180
|
+
capabilities: ['CAPABILITY_IAM']
|
181
|
+
}
|
182
|
+
|
183
|
+
cf_args.merge!(role_arn: role) unless role.nil?
|
184
|
+
|
179
185
|
begin
|
180
|
-
cf_client.
|
186
|
+
cf_client.update_stack(cf_args)
|
181
187
|
rescue Aws::CloudFormation::Errors::Throttling => e
|
182
188
|
output 'Throttling, retrying in 15 seconds'.red
|
183
189
|
sleep 15
|
184
|
-
|
190
|
+
update_stack(stack_name, template_body, parameters = [], tags = [])
|
185
191
|
rescue Aws::CloudFormation::Errors::ValidationError => e
|
186
|
-
return false if e.message.include? 'does not exist'
|
187
192
|
raise e
|
188
193
|
else
|
189
|
-
|
194
|
+
output "CloudFormation stack #{stack_name} update started".green
|
190
195
|
end
|
191
196
|
end
|
192
197
|
|
198
|
+
def stack_exists?(stack_name)
|
199
|
+
cf_client.describe_stacks(stack_name: stack_name)
|
200
|
+
rescue Aws::CloudFormation::Errors::Throttling => e
|
201
|
+
output 'Throttling, retrying in 15 seconds'.red
|
202
|
+
sleep 15
|
203
|
+
stack_exists?(stack_name)
|
204
|
+
rescue Aws::CloudFormation::Errors::ValidationError => e
|
205
|
+
return false if e.message.include? 'does not exist'
|
206
|
+
|
207
|
+
raise e
|
208
|
+
else
|
209
|
+
true
|
210
|
+
end
|
211
|
+
|
193
212
|
def delete_stack(stack_name)
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
output "Stack #{stack_name} spenta con successo\n".green
|
202
|
-
end
|
213
|
+
cf_client.delete_stack(stack_name: stack_name)
|
214
|
+
rescue Aws::CloudFormation::Errors::Throttling => e
|
215
|
+
output 'Throttling, retrying in 15 seconds'.red
|
216
|
+
sleep 15
|
217
|
+
delete_stack(stack_name)
|
218
|
+
else
|
219
|
+
output "Stack #{stack_name} shutdown succesfully\n".green
|
203
220
|
end
|
204
221
|
|
205
|
-
def wait_for_stack_ready(stack_name, failed_statuses = [
|
222
|
+
def wait_for_stack_ready(stack_name, failed_statuses = %w[CREATE_FAILED ROLLBACK_IN_PROGRESS ROLLBACK_FAILED DELETE_IN_PROGRESS DELETE_FAILED DELETE_COMPLETE UPDATE_ROLLBACK_FAILED UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS])
|
206
223
|
ready = false
|
207
224
|
sleep_seconds = 13
|
208
|
-
output "
|
209
|
-
|
225
|
+
output "Waiting for stack #{stack_name}...\n".yellow
|
226
|
+
until ready
|
210
227
|
ready = true if stack_ready?(stack_name, failed_statuses)
|
211
228
|
seconds_elapsed = 0
|
212
229
|
loop do
|
213
230
|
break if seconds_elapsed >= sleep_seconds
|
231
|
+
|
214
232
|
print '.'.yellow; STDOUT.flush
|
215
233
|
sleep 1
|
216
234
|
seconds_elapsed += 1
|
217
235
|
end
|
218
236
|
end
|
219
|
-
output "\nStack #{stack_name}
|
237
|
+
output "\nStack #{stack_name} ready!\n".green
|
220
238
|
end
|
221
239
|
|
222
240
|
def wait_for_stack_removal(stack_name)
|
223
241
|
ready = false
|
224
242
|
sleep_seconds = 13
|
225
243
|
sleep 10
|
226
|
-
output "
|
227
|
-
|
244
|
+
output "Waiting for stack #{stack_name}...\n".yellow
|
245
|
+
until ready
|
228
246
|
ready = true if stack_deleted?(stack_name)
|
229
247
|
seconds_elapsed = 0
|
230
248
|
loop do
|
231
249
|
break if seconds_elapsed >= sleep_seconds
|
250
|
+
|
232
251
|
print '.'.yellow; STDOUT.flush
|
233
252
|
sleep 1
|
234
253
|
seconds_elapsed += 1
|
@@ -238,55 +257,46 @@ module PrimaAwsClient
|
|
238
257
|
end
|
239
258
|
|
240
259
|
def get_stack_tags(name)
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
resp.stacks[0].tags
|
249
|
-
end
|
260
|
+
resp = cf_client.describe_stacks(stack_name: name)
|
261
|
+
rescue Aws::CloudFormation::Errors::Throttling => e
|
262
|
+
output 'Throttling, retrying in 15 seconds'.red
|
263
|
+
sleep 15
|
264
|
+
get_stack_tags(name)
|
265
|
+
else
|
266
|
+
resp.stacks[0].tags
|
250
267
|
end
|
251
268
|
|
252
269
|
def get_stack_parameters(name)
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
resp.stacks[0].parameters
|
261
|
-
end
|
270
|
+
resp = cf_client.describe_stacks(stack_name: name)
|
271
|
+
rescue Aws::CloudFormation::Errors::Throttling => e
|
272
|
+
output 'Throttling, retrying in 15 seconds'.red
|
273
|
+
sleep 15
|
274
|
+
get_stack_parameters(name)
|
275
|
+
else
|
276
|
+
resp.stacks[0].parameters
|
262
277
|
end
|
263
278
|
|
264
279
|
def get_stack_outputs(name)
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
resp.stacks[0].outputs
|
273
|
-
end
|
280
|
+
resp = cf_client.describe_stacks(stack_name: name)
|
281
|
+
rescue Aws::CloudFormation::Errors::Throttling => e
|
282
|
+
output 'Throttling, retrying in 15 seconds'.red
|
283
|
+
sleep 15
|
284
|
+
get_stack_outputs(name)
|
285
|
+
else
|
286
|
+
resp.stacks[0].outputs
|
274
287
|
end
|
275
288
|
|
276
289
|
def get_stack_template(name)
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
resp.template_body
|
285
|
-
end
|
286
|
-
|
290
|
+
resp = cf_client.get_template(stack_name: name)
|
291
|
+
rescue Aws::CloudFormation::Errors::Throttling => e
|
292
|
+
output 'Throttling, retrying in 15 seconds'.red
|
293
|
+
sleep 15
|
294
|
+
get_stack_template(name)
|
295
|
+
else
|
296
|
+
resp.template_body
|
287
297
|
end
|
288
298
|
|
289
|
-
def stack_ready?(stack_name, failed_statuses = [
|
299
|
+
def stack_ready?(stack_name, failed_statuses = %w[CREATE_FAILED ROLLBACK_IN_PROGRESS ROLLBACK_FAILED DELETE_IN_PROGRESS DELETE_FAILED DELETE_COMPLETE UPDATE_ROLLBACK_FAILED UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS])
|
290
300
|
begin
|
291
301
|
resp = cf_client.describe_stacks(
|
292
302
|
stack_name: stack_name
|
@@ -297,10 +307,11 @@ module PrimaAwsClient
|
|
297
307
|
return false
|
298
308
|
end
|
299
309
|
raise "The stack #{stack_name} errored out" if failed_statuses.include? stack_status
|
300
|
-
|
310
|
+
|
311
|
+
%w[CREATE_COMPLETE UPDATE_COMPLETE UPDATE_ROLLBACK_COMPLETE ROLLBACK_COMPLETE].include? stack_status
|
301
312
|
end
|
302
313
|
|
303
|
-
def stack_deleted?(stack_name, failed_statuses = [
|
314
|
+
def stack_deleted?(stack_name, failed_statuses = %w[ROLLBACK_IN_PROGRESS ROLLBACK_FAILED DELETE_FAILED UPDATE_ROLLBACK_FAILED UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS])
|
304
315
|
begin
|
305
316
|
resp = cf_client.describe_stacks(
|
306
317
|
stack_name: stack_name
|
@@ -314,6 +325,7 @@ module PrimaAwsClient
|
|
314
325
|
return true
|
315
326
|
end
|
316
327
|
raise "The stack #{stack_name} errored out" if failed_statuses.include? stack_status
|
328
|
+
|
317
329
|
['DELETE_COMPLETE'].include? stack_status
|
318
330
|
end
|
319
331
|
|
@@ -326,38 +338,39 @@ module PrimaAwsClient
|
|
326
338
|
!resp.contents.empty?
|
327
339
|
end
|
328
340
|
|
329
|
-
def upload_artifact(source_path, destination_path, bucket_name_override=nil)
|
330
|
-
output "
|
341
|
+
def upload_artifact(source_path, destination_path, bucket_name_override = nil)
|
342
|
+
output "Uploading artifact (#{(File.size(source_path).to_f / 2**20).round(2)} MiB)\n".yellow
|
331
343
|
s3 = Aws::S3::Resource.new
|
332
|
-
s3_bucket =
|
344
|
+
s3_bucket = !bucket_name_override.nil? ? bucket_name_override : @s3_bucket
|
333
345
|
puts s3_bucket
|
334
346
|
obj = s3.bucket(s3_bucket).object(destination_path)
|
335
347
|
obj.upload_file(source_path)
|
336
348
|
|
337
|
-
output "#{s3_bucket}/#{destination_path}
|
349
|
+
output "#{s3_bucket}/#{destination_path} upload success!\n".green
|
338
350
|
end
|
339
351
|
|
340
352
|
def wait_for_artifact(bucket, path)
|
341
353
|
ready = artifact_exists?(bucket, path)
|
342
354
|
sleep_seconds = 13
|
343
|
-
output "
|
355
|
+
output "Wating that artifact #{path} becomes ready...\n".yellow
|
344
356
|
retries = 0
|
345
|
-
|
357
|
+
until ready
|
346
358
|
ready = true if artifact_exists?(bucket, path)
|
347
359
|
seconds_elapsed = 0
|
348
360
|
loop do
|
349
361
|
break if seconds_elapsed >= sleep_seconds
|
362
|
+
|
350
363
|
print '.'.yellow; STDOUT.flush
|
351
364
|
sleep 1
|
352
365
|
seconds_elapsed += 1
|
353
366
|
end
|
354
367
|
retries += 1
|
355
368
|
if retries > 150
|
356
|
-
output "\n
|
369
|
+
output "\n Artifact #{path} timed out\n".red
|
357
370
|
exit
|
358
371
|
end
|
359
372
|
end
|
360
|
-
output "\
|
373
|
+
output "\nArtifact #{path} created!\n".green
|
361
374
|
end
|
362
375
|
|
363
376
|
def list_import_stacks(export_name)
|
@@ -365,7 +378,7 @@ module PrimaAwsClient
|
|
365
378
|
next_token = ''
|
366
379
|
loop do
|
367
380
|
print '.'.yellow; STDOUT.flush
|
368
|
-
options = next_token != '' ? { export_name: export_name, next_token: next_token } : {export_name: export_name}
|
381
|
+
options = next_token != '' ? { export_name: export_name, next_token: next_token } : { export_name: export_name }
|
369
382
|
begin
|
370
383
|
resp = cf_client.list_imports(options)
|
371
384
|
rescue Aws::CloudFormation::Errors::Throttling => e
|
@@ -375,94 +388,81 @@ module PrimaAwsClient
|
|
375
388
|
end
|
376
389
|
stacks += resp.imports
|
377
390
|
break unless resp.next_token
|
391
|
+
|
378
392
|
next_token = resp.next_token
|
379
393
|
end
|
380
394
|
stacks
|
381
395
|
end
|
382
396
|
|
383
397
|
def describe_stack_resource(cluster_stack_name, logical_resource_id)
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
resp = describe_stack_resource(cluster_stack_name, logical_resource_id)
|
390
|
-
end
|
398
|
+
resp = cf_client.describe_stack_resource({ stack_name: cluster_stack_name, logical_resource_id: logical_resource_id })
|
399
|
+
rescue Aws::CloudFormation::Errors::Throttling => e
|
400
|
+
output 'Throttling, retrying in 15 seconds'.red
|
401
|
+
sleep 15
|
402
|
+
resp = describe_stack_resource(cluster_stack_name, logical_resource_id)
|
391
403
|
end
|
392
404
|
|
393
405
|
def describe_instances(instance_ids)
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
resp = describe_instances(instance_ids)
|
400
|
-
end
|
406
|
+
resp = ec2_client.describe_instances({ instance_ids: instance_ids })
|
407
|
+
rescue Aws::CloudFormation::Errors::Throttling => e
|
408
|
+
output 'Throttling, retrying in 15 seconds'.red
|
409
|
+
sleep 15
|
410
|
+
resp = describe_instances(instance_ids)
|
401
411
|
end
|
402
412
|
|
403
413
|
def describe_auto_scaling_groups(auto_scaling_group_names, max_records)
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
resp = describe_auto_scaling_groups(auto_scaling_group_names, max_records)
|
413
|
-
end
|
414
|
+
resp = asg_client.describe_auto_scaling_groups({
|
415
|
+
auto_scaling_group_names: auto_scaling_group_names,
|
416
|
+
max_records: max_records
|
417
|
+
})
|
418
|
+
rescue Aws::CloudFormation::Errors::Throttling => e
|
419
|
+
output 'Throttling, retrying in 15 seconds'.red
|
420
|
+
sleep 15
|
421
|
+
resp = describe_auto_scaling_groups(auto_scaling_group_names, max_records)
|
414
422
|
end
|
415
423
|
|
416
424
|
def describe_load_balancers(load_balancer_arns)
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
resp = describe_load_balancers(load_balancer_arns)
|
423
|
-
end
|
425
|
+
resp = alb_client.describe_load_balancers({ load_balancer_arns: load_balancer_arns })
|
426
|
+
rescue Aws::ElasticLoadBalancingV2::Errors::Throttling => e
|
427
|
+
output 'Throttling, retrying in 15 seconds'.red
|
428
|
+
sleep 15
|
429
|
+
resp = describe_load_balancers(load_balancer_arns)
|
424
430
|
end
|
425
431
|
|
426
432
|
def update_ecs_service(cluster, service, deployment_configuration)
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
resp = update_ecs_service(cluster, service, deployment_configuration)
|
437
|
-
end
|
433
|
+
resp = ecs_client.update_service(
|
434
|
+
cluster: cluster,
|
435
|
+
service: service,
|
436
|
+
deployment_configuration: deployment_configuration
|
437
|
+
)
|
438
|
+
rescue Aws::CloudFormation::Errors::Throttling => e
|
439
|
+
output 'Throttling, retrying in 15 seconds'.red
|
440
|
+
sleep 15
|
441
|
+
resp = update_ecs_service(cluster, service, deployment_configuration)
|
438
442
|
end
|
439
443
|
|
440
444
|
def describe_ecs_tasks(cluster, tasks)
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
resp = describe_ecs_tasks(cluster, tasks)
|
450
|
-
end
|
445
|
+
resp = ecs_client.describe_tasks({
|
446
|
+
cluster: cluster,
|
447
|
+
tasks: tasks
|
448
|
+
})
|
449
|
+
rescue Aws::CloudFormation::Errors::Throttling => e
|
450
|
+
output 'Throttling, retrying in 15 seconds'.red
|
451
|
+
sleep 15
|
452
|
+
resp = describe_ecs_tasks(cluster, tasks)
|
451
453
|
end
|
452
454
|
|
453
455
|
def run_ecs_task(cluster, task_definition, overrides, count)
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
resp = run_ecs_task(cluster, task_definition, overrides, count)
|
465
|
-
end
|
456
|
+
resp = ecs_client.run_task({
|
457
|
+
cluster: cluster,
|
458
|
+
task_definition: task_definition,
|
459
|
+
overrides: overrides,
|
460
|
+
count: count
|
461
|
+
})
|
462
|
+
rescue Aws::CloudFormation::Errors::Throttling => e
|
463
|
+
output 'Throttling, retrying in 15 seconds'.red
|
464
|
+
sleep 15
|
465
|
+
resp = run_ecs_task(cluster, task_definition, overrides, count)
|
466
466
|
end
|
467
467
|
|
468
468
|
def get_autoscaling_capacity(asg_name)
|
data/lib/prima_twig.rb
CHANGED
@@ -8,8 +8,8 @@ require 'aws-sdk-core'
|
|
8
8
|
require 'rubyflare'
|
9
9
|
|
10
10
|
class Prima
|
11
|
-
CONFIG_KEYS=['github', 'cloudflare_email', 'cloudflare_apikey'
|
12
|
-
attr_reader :gh, :twig, :config, :rugged
|
11
|
+
CONFIG_KEYS=['github', 'cloudflare_email', 'cloudflare_apikey']
|
12
|
+
attr_reader :gh, :twig, :config, :rugged
|
13
13
|
|
14
14
|
def initialize
|
15
15
|
@twig = Twig.new(:read_options => true, :max_days_old => 30)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prima-twig
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matteo Giachino
|
@@ -14,7 +14,7 @@ authors:
|
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
date: 2020-
|
17
|
+
date: 2020-11-18 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: aws-sdk-autoscaling
|
@@ -73,21 +73,21 @@ dependencies:
|
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: '1'
|
75
75
|
- !ruby/object:Gem::Dependency
|
76
|
-
name: aws-sdk-
|
76
|
+
name: aws-sdk-core
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|
78
78
|
requirements:
|
79
79
|
- - "~>"
|
80
80
|
- !ruby/object:Gem::Version
|
81
|
-
version: '
|
81
|
+
version: '3'
|
82
82
|
type: :runtime
|
83
83
|
prerelease: false
|
84
84
|
version_requirements: !ruby/object:Gem::Requirement
|
85
85
|
requirements:
|
86
86
|
- - "~>"
|
87
87
|
- !ruby/object:Gem::Version
|
88
|
-
version: '
|
88
|
+
version: '3'
|
89
89
|
- !ruby/object:Gem::Dependency
|
90
|
-
name: aws-sdk-
|
90
|
+
name: aws-sdk-ec2
|
91
91
|
requirement: !ruby/object:Gem::Requirement
|
92
92
|
requirements:
|
93
93
|
- - "~>"
|
@@ -101,7 +101,7 @@ dependencies:
|
|
101
101
|
- !ruby/object:Gem::Version
|
102
102
|
version: '1'
|
103
103
|
- !ruby/object:Gem::Dependency
|
104
|
-
name: aws-sdk-
|
104
|
+
name: aws-sdk-ecs
|
105
105
|
requirement: !ruby/object:Gem::Requirement
|
106
106
|
requirements:
|
107
107
|
- - "~>"
|
@@ -115,7 +115,7 @@ dependencies:
|
|
115
115
|
- !ruby/object:Gem::Version
|
116
116
|
version: '1'
|
117
117
|
- !ruby/object:Gem::Dependency
|
118
|
-
name: aws-sdk-
|
118
|
+
name: aws-sdk-elasticloadbalancingv2
|
119
119
|
requirement: !ruby/object:Gem::Requirement
|
120
120
|
requirements:
|
121
121
|
- - "~>"
|
@@ -129,19 +129,19 @@ dependencies:
|
|
129
129
|
- !ruby/object:Gem::Version
|
130
130
|
version: '1'
|
131
131
|
- !ruby/object:Gem::Dependency
|
132
|
-
name: aws-sdk-
|
132
|
+
name: aws-sdk-s3
|
133
133
|
requirement: !ruby/object:Gem::Requirement
|
134
134
|
requirements:
|
135
135
|
- - "~>"
|
136
136
|
- !ruby/object:Gem::Version
|
137
|
-
version: '
|
137
|
+
version: '1'
|
138
138
|
type: :runtime
|
139
139
|
prerelease: false
|
140
140
|
version_requirements: !ruby/object:Gem::Requirement
|
141
141
|
requirements:
|
142
142
|
- - "~>"
|
143
143
|
- !ruby/object:Gem::Version
|
144
|
-
version: '
|
144
|
+
version: '1'
|
145
145
|
- !ruby/object:Gem::Dependency
|
146
146
|
name: colorize
|
147
147
|
requirement: !ruby/object:Gem::Requirement
|
@@ -286,11 +286,13 @@ description: Our tools to manage git and github
|
|
286
286
|
email: matteo.giachino@prima.it
|
287
287
|
executables:
|
288
288
|
- twig-feature
|
289
|
+
- twig-packer-builder
|
289
290
|
- twig-update-ami
|
290
291
|
extensions: []
|
291
292
|
extra_rdoc_files: []
|
292
293
|
files:
|
293
294
|
- bin/twig-feature
|
295
|
+
- bin/twig-packer-builder
|
294
296
|
- bin/twig-update-ami
|
295
297
|
- lib/command.rb
|
296
298
|
- lib/prima_aws_client.rb
|