prima-twig 1.2.6 → 1.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 665845adb115e9091a4d63673e3386d7b50e2043350ee342daee36122a6807f1
4
- data.tar.gz: d92dc143955f44ae455eb20bead9668737183bfab7fa7f62c9b116e279c9538e
3
+ metadata.gz: 94183c3b380822569fa755c6cd914da1eb15d36378d93a8faf827cf8b2a8a119
4
+ data.tar.gz: 865c21d79254b4b4ad2e8034d665f74301585fd50f4fd20b019e0c6bde977183
5
5
  SHA512:
6
- metadata.gz: 27bcc7a32bc312e359015843c05ad59872d54582ce342587894489407a6c48dbbce6997a7cbc5be2e7d246aead9dc0f235996658e450810e4409b829a670428a
7
- data.tar.gz: 2474944753cd11b793107b6b8de274e7460ea2f54a2587ee43f3bef89ca2ef2278b40f861e5198a79c4fb8acce32a1e14c6bc684b479e0a5b188417b2c939462
6
+ metadata.gz: 7604f33c35162fde446fb5e91e92451745d7eab126d09f417c36c835254949f2690c77c3f4ebbd8248c885c6c19b7786498da0cfbfe44adcb798b5641e53613f
7
+ data.tar.gz: 24f093ea1a89deef5debd86afdf69b5dbb43f5f1a6aef9bc68f8a8a0b9e2e938baee20ba897116d8a2d66e5e8d7b8d60a9e1f6af267973003e7357c34d138884
@@ -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
@@ -1,164 +1,105 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'rubygems'
4
- require_relative '../lib/prima_twig.rb'
5
- require_relative '../lib/prima_aws_client.rb'
4
+ require_relative '../lib/prima_twig'
5
+ require_relative '../lib/prima_aws_client'
6
6
  require 'launchy'
7
- require 'json'
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 '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"
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
- 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
- ami_mappings = JSON.parse(@s3.get_object(bucket: @s3_bucket, key: "ami/ami-mappings.json")["body"].read())
33
-
34
- ami_mapping = nil
35
- ami_mappings.each do |item|
36
- if item['ami_template'].eql?(ami_template) and item['env'].eql?(env)
37
- ami_mapping = item
38
- end
39
- end
40
-
41
- stop_if(ami_mapping == nil, "No AMI found with parameters #{ami_template} and #{env}")
22
+ old_ami = args[0]
23
+ new_ami = args[1]
42
24
 
43
- new_ami_id = update_packer(ami_template, env, ami_mapping['teams_mapping'], ami_mapping['country'])
44
- # new_ami_id = 'ami-0d8488b68731e8756'
45
- Dir.chdir '..'
46
- stop_if(new_ami_id.to_s.empty?, 'Failed to generate AMI!'.red)
47
- output "new ami id: #{new_ami_id}"
48
-
49
- output 'searching for ami to update...'
50
- old_amis = update_ami_mappings(ami_mappings, ami_template, env, new_ami_id)
51
- stop_if(old_amis.empty?, "No ami to update! No #{ami_template} in env #{env}, exiting".yellow)
52
-
53
- output "retrieving stacks that uses old ami ids: #{old_amis}"
54
- exports = list_exports()
55
- stacks = get_stacks_from_exports(exports, old_amis)
56
- 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)
57
28
 
58
29
  stacks.each do |stack|
59
- output "processing stack #{stack}"
30
+ output "processing stack #{stack}".light_green
60
31
  if stack.include?('qa')
61
- output "skipping stack #{stack} because is a qa"
32
+ output "skipping stack #{stack} because is a qa".yellow
62
33
  next
63
34
  else
64
35
  stack_parameters = get_stack_parameters(stack)
65
- stack_parameters = update_stack_parameters(stack_parameters, "AMIID", new_ami_id)
66
- if stack.include?('batch')
67
- if stack.include?('offsite-backups')
68
- stack_template = File.read("./cloudformation/stacks/batch/compute-environment-offsite-backups.yml")
69
- else
70
- stack_template = File.read("./cloudformation/stacks/batch/compute-environment.yml")
71
- end
72
- else
73
- stack_parameters = update_stack_parameters(stack_parameters, "DesiredCapacity", get_desired_capacity(stack).to_s)
74
- 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)
75
39
  end
76
- update_stack(stack, stack_template, stack_parameters)
40
+
41
+ update_stack_reuse_template(stack, stack_parameters)
77
42
  end
78
43
  end
79
44
 
80
45
  stacks.each do |stack|
81
- if stack.include?('qa')
82
- next
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
83
59
  end
84
- 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'])
85
60
  end
86
61
 
87
- output 'writing new ami mapping'
88
- File.open("ami/ami-mappings.json", 'w+') do |f|
89
- mapping_file = JSON.pretty_generate(ami_mappings)
90
- f.write(mapping_file)
91
- @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)
92
65
  end
93
66
 
94
- output 'Update finished! ( ͡° ͜ʖ ͡°)'
67
+ output 'Update finished! ( ͡° ͜ʖ ͡°)'.light_green
95
68
  end
96
69
 
97
- def get_stacks_from_exports(exports, old_amis)
70
+ private
71
+
72
+ def get_stacks_from_exports(exports, old_ami)
98
73
  stacks = []
99
- old_amis.each do |old_ami|
100
- exports.each do |export|
101
- if export.value.eql?(old_ami)
102
- stacks.insert(0,export.exporting_stack_id)
103
- end
104
- end
74
+ exports.each do |export|
75
+ stacks.insert(0, export.exporting_stack_id) if export.value.eql?(old_ami)
105
76
  end
106
77
  stacks
107
78
  end
108
79
 
109
- def update_ami_mappings(mappings, ami_template, env, new_ami_id)
110
- old_values = []
111
- mappings.each do |item|
112
- if item['ami_template'].eql?(ami_template) and item['env'].eql?(env)
113
- old_values.insert(0,item['ami_id'])
114
- item['ami_id'] = new_ami_id
115
- end
116
- end
117
- old_values.uniq
118
- end
119
-
120
80
  def update_stack_parameters(stack_parameters, key, value)
121
81
  stack_parameters.each do |param|
122
- if param.parameter_key == key
123
- param.parameter_value = value
124
- end
82
+ param.parameter_value = value if param.parameter_key == key
125
83
  end
126
84
  stack_parameters
127
85
  end
128
86
 
129
- def update_instance_name(ami_id, ami_name, ami_description, ecs_json_path)
130
- ecs_json = JSON.parse File.read(ecs_json_path)
131
-
132
- ecs_json['builders'][0]['source_ami'] = ami_id
133
- ecs_json['builders'][0]['ami_name'] = ami_name
134
- ecs_json['builders'][0]['ami_description'] = ami_description
135
-
136
- File.open ecs_json_path, 'w' do |f|
137
- f.write(JSON.pretty_generate(ecs_json))
138
- end
139
- end
140
-
141
87
  def get_desired_capacity(stack_name)
142
88
  stack_outputs = get_stack_outputs(stack_name)
143
89
  stack_outputs.each do |out|
144
- if out.export_name.include?('EC2Fleet') or out.export_name.include?('AutoScalingGroup') or out.export_name.include?('NodeGroup')
90
+ if out.export_name.include?('EC2Fleet') || out.export_name.include?('AutoScalingGroup') || out.export_name.include?('NodeGroup')
145
91
  return get_autoscaling_capacity(out.output_value)
146
92
  end
147
93
  end
148
94
  end
149
95
 
150
- def update_packer(json_filename, env, teams, country)
151
- 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} -var teams_mapping=#{teams} -var country=#{country} -machine-readable ./#{json_filename} | tee build.log"
152
- `grep 'artifact,0,id' build.log | cut -d, -f6 | cut -d: -f2`.sub(/\n/, '')
153
- end
154
-
155
96
  def help_content
156
97
  <<-HELP
157
98
 
158
99
  twig-update-ami
159
100
  ===========
160
101
 
161
- Updates ami version and applies it to stacks on cloudformation
102
+ Updates AutoScalingGroups/BatchComputeEnvs with a new AMI
162
103
 
163
104
  Synopsis
164
105
  --------
@@ -168,8 +109,8 @@ class TwigUpdateAmi
168
109
  Description
169
110
  -----------
170
111
 
171
- from artemide main folder run
172
- `twig-update-ami ${AMI_TEMPLATE} ${AMI_ID} ${AMI_NAME} ${AMI_DESCRIPTION} ${ENV}`
112
+ from tsunami main folder run
113
+ `aws-vault exec AWS_PROFILE_WITH_ACCESS_TO_COUNTRY_ACCOUNT -- twig-update-ami ${OLD_AMI} ${NEW_AMI}`
173
114
 
174
115
  Subcommand for Twig: <http://rondevera.github.io/twig/>
175
116
  Author: Eugenio Laghi <https://github.com/eugeniolaghi>
@@ -177,13 +118,6 @@ class TwigUpdateAmi
177
118
  HELP
178
119
  end
179
120
 
180
- class ::Hash
181
- def deep_merge(second)
182
- 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 }
183
- self.merge(second.to_h, &merger)
184
- end
185
- end
186
-
187
121
  args = ARGV.dup
188
122
 
189
123
  if args.include?('--help')
@@ -175,6 +175,32 @@ module PrimaAwsClient
175
175
  end
176
176
  end
177
177
 
178
+ def update_stack_reuse_template(stack_name, parameters = [], tags = [], role = nil)
179
+ cf_args = {
180
+ stack_name: stack_name,
181
+ use_previous_template: true,
182
+ parameters: parameters,
183
+ tags: tags,
184
+ capabilities: ['CAPABILITY_IAM']
185
+ }
186
+
187
+ if role != nil then
188
+ cf_args.merge!(role_arn: role)
189
+ end
190
+
191
+ begin
192
+ cf_client.update_stack(cf_args)
193
+ rescue Aws::CloudFormation::Errors::Throttling => e
194
+ output 'Throttling, retrying in 15 seconds'.red
195
+ sleep 15
196
+ update_stack(stack_name, template_body, parameters = [], tags = [])
197
+ rescue Aws::CloudFormation::Errors::ValidationError => e
198
+ raise e
199
+ else
200
+ output "L'update dello stack #{stack_name} è stato avviato".green
201
+ end
202
+ end
203
+
178
204
  def stack_exists?(stack_name)
179
205
  begin
180
206
  cf_client.describe_stacks(stack_name: stack_name)
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.2.6
4
+ version: 1.3.0
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-10-21 00:00:00.000000000 Z
17
+ date: 2020-11-16 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-ec2
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: '1'
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: '1'
88
+ version: '3'
89
89
  - !ruby/object:Gem::Dependency
90
- name: aws-sdk-ecs
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-elasticloadbalancingv2
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-s3
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-core
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: '3'
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: '3'
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
@@ -314,7 +316,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
314
316
  - !ruby/object:Gem::Version
315
317
  version: '0'
316
318
  requirements: []
317
- rubygems_version: 3.0.1
319
+ rubygems_version: 3.0.3
318
320
  signing_key:
319
321
  specification_version: 4
320
322
  summary: The Prima twig toolbelt