prima-twig 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c85bc796545f4a0f38fa6be4380dec88e6c2fb771e8fe39d5044c57e9c0a6ee
4
- data.tar.gz: 4eef557dba95dc99c88f3f07951bb6219aa2fe63eb928c12e9c6a743954400e6
3
+ metadata.gz: 94183c3b380822569fa755c6cd914da1eb15d36378d93a8faf827cf8b2a8a119
4
+ data.tar.gz: 865c21d79254b4b4ad2e8034d665f74301585fd50f4fd20b019e0c6bde977183
5
5
  SHA512:
6
- metadata.gz: 4d2472dfc0c866aab6563b9cca251fd606256dc05c55bd450abbd65e5db86371327626bbb8001b06a3e15f50355f6d9a26fb1760513bc3a04698c043b9cbf572
7
- data.tar.gz: 4e86ab32ba1df95732055f29da69469fdd2c71d197233c5db726e18780910141206e9eb6ab5d94d2db621b3be8199d0bf22bc69bc8fdc24c26204eef6a0f1c19
6
+ metadata.gz: 7604f33c35162fde446fb5e91e92451745d7eab126d09f417c36c835254949f2690c77c3f4ebbd8248c885c6c19b7786498da0cfbfe44adcb798b5641e53613f
7
+ data.tar.gz: 24f093ea1a89deef5debd86afdf69b5dbb43f5f1a6aef9bc68f8a8a0b9e2e938baee20ba897116d8a2d66e5e8d7b8d60a9e1f6af267973003e7357c34d138884
@@ -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, [], tags)
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
@@ -1,166 +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
22
+ old_ami = args[0]
23
+ new_ami = args[1]
26
24
 
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")
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
- 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")
35
+ stack_parameters = get_stack_parameters(stack)
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)
78
39
  end
79
- update_stack(stack, stack_template, stack_parameters, hashes_to_tags(stack_tags))
40
+
41
+ update_stack_reuse_template(stack, stack_parameters)
80
42
  end
81
43
  end
82
44
 
83
45
  stacks.each do |stack|
84
- if stack.include?('qa')
85
- 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
86
59
  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
60
  end
89
61
 
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)
62
+ File.open(@state_path, 'w+') do |f|
63
+ yaml = YAML.dump(@config)
64
+ f.write(yaml)
95
65
  end
96
66
 
97
- output 'Update finished! ( ͡° ͜ʖ ͡°)'
67
+ output 'Update finished! ( ͡° ͜ʖ ͡°)'.light_green
98
68
  end
99
69
 
100
- def get_stacks_from_exports(exports, old_amis)
70
+ private
71
+
72
+ def get_stacks_from_exports(exports, old_ami)
101
73
  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
74
+ exports.each do |export|
75
+ stacks.insert(0, export.exporting_stack_id) if export.value.eql?(old_ami)
108
76
  end
109
77
  stacks
110
78
  end
111
79
 
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)
80
+ def update_stack_parameters(stack_parameters, key, value)
81
+ stack_parameters.each do |param|
82
+ param.parameter_value = value if param.parameter_key == key
127
83
  end
128
84
  stack_parameters
129
85
  end
130
86
 
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
87
  def get_desired_capacity(stack_name)
144
88
  stack_outputs = get_stack_outputs(stack_name)
145
89
  stack_outputs.each do |out|
146
- if out.export_name.include?('EC2Fleet') or out.export_name.include?('AutoScalingGroup')
90
+ if out.export_name.include?('EC2Fleet') || out.export_name.include?('AutoScalingGroup') || out.export_name.include?('NodeGroup')
147
91
  return get_autoscaling_capacity(out.output_value)
148
92
  end
149
93
  end
150
94
  end
151
95
 
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
96
  def help_content
158
97
  <<-HELP
159
98
 
160
99
  twig-update-ami
161
100
  ===========
162
101
 
163
- Updates ami version and applies it to stacks on cloudformation
102
+ Updates AutoScalingGroups/BatchComputeEnvs with a new AMI
164
103
 
165
104
  Synopsis
166
105
  --------
@@ -170,8 +109,8 @@ class TwigUpdateAmi
170
109
  Description
171
110
  -----------
172
111
 
173
- from artemide main folder run
174
- `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}`
175
114
 
176
115
  Subcommand for Twig: <http://rondevera.github.io/twig/>
177
116
  Author: Eugenio Laghi <https://github.com/eugeniolaghi>
@@ -179,13 +118,6 @@ class TwigUpdateAmi
179
118
  HELP
180
119
  end
181
120
 
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
121
  args = ARGV.dup
190
122
 
191
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)
@@ -474,21 +500,4 @@ module PrimaAwsClient
474
500
  resp = ec2_client.describe_spot_fleet_requests(spot_fleet_request_ids: [fleet_arn])
475
501
  resp.spot_fleet_request_configs[0].spot_fleet_request_config.target_capacity
476
502
  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
503
  end
@@ -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', 'aws_username', 'aws_password', 'prima_apikey']
12
- attr_reader :gh, :twig, :config, :rugged, :aws
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.2.1
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-07-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