stackup 1.4.4 → 1.6.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: d7780d13ca055272558fc31b1e074d1a6721698dec9b5d8feb63cf737dcff8af
4
- data.tar.gz: 04bb0928aeb6b22510873ee5d9898c31a7ea891bce2c5cf0828061dfafe03b71
3
+ metadata.gz: 281a4cc419891f70b5a8251d52c080a678e21d2a76a57a0e715b478987c5d967
4
+ data.tar.gz: e1848f7cccad92a7a6883f3947daa68e70d1b72ad00bfa72727f0def2590ee89
5
5
  SHA512:
6
- metadata.gz: b9fcf5e6ae3dabfb016c12d88f40ef99f351d9474f2a2788f24c1b7fdc75f61144a4c93cdd6dbbdb43db952e27a6b282670d1a66873e3d99547cdf70da6bc63f
7
- data.tar.gz: 985b673ed7b8b500f341f275cec37bedf47b8ead6e1ba1d627baeb41ba89b4a5da2ea9307f55d4741b831ff5d6617a2576a05325b1941b9b1351890bd115cfea
6
+ metadata.gz: 59cd63007ce56b284e7176c82f354174bb78dec2fba84443e2b1a28465df5ed68cd0bc1ccd2474dab7572c61ebb85d9de328e12216395d0e68c8101863b0110a
7
+ data.tar.gz: 3acf8d1d83307ef2851001558c7880c4d43692f740254531369713e4cb7d38f3595398195cfd8f67460b10c1bd94cd7dca08a833fdf51371ef0a1c914addcffc
data/CHANGES.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # CHANGES
2
2
 
3
+ ## 1.6.0 (2020-11-19)
4
+
5
+ * Feature: Support --service-role-arn on "change-set create"
6
+
7
+ ## 1.5.1 (2020-11-16)
8
+
9
+ * Fix: Recognise that template is in S3 when URL is formatted like `s3.REGION.amazonaws.com`
10
+
11
+ ## 1.5.0 (2020-04-21)
12
+
13
+ * Feature: --preserve-template-formatting
14
+
15
+ ## 1.4.6 (2019-12-09)
16
+
17
+ * Fix: Don't error out when receiving tags in AWS style (array of hashes) from YAML or JSON file
18
+
19
+ ## 1.4.5 (2019-10-03)
20
+
21
+ * Fix: Tags in RakeTasks
22
+
3
23
  ## 1.4.4 (2019-10-03)
4
24
 
5
25
  * Fix: Loading RakeTasks
data/README.md CHANGED
@@ -78,10 +78,12 @@ For more details on usage, see
78
78
 
79
79
  ### Specifying parameters
80
80
 
81
- Stack parameters can be be read from a file, e.g.
81
+ Stack parameters can be read from a file, e.g.
82
82
 
83
83
  $ stackup myapp-test up -t template.json -p parameters.json
84
84
 
85
+ These files can be either JSON or YAML format, see [YAML support](#yaml-support) for more information.
86
+
85
87
  Parameters can be specified as simple key-value pairs:
86
88
 
87
89
  ```json
@@ -116,6 +118,22 @@ Or, you can specify one or more override parameters on the command-line, using `
116
118
  -o IndexDoc=index-override.html
117
119
  -o ContentDoc=content-override.html
118
120
 
121
+ ### Specifying tags
122
+
123
+ Stack tags can be read from a file, e.g.
124
+
125
+ $ stackup myapp-test up -t template.json --tags tags.json
126
+
127
+ These files can be either JSON or YAML format, see [YAML support](#yaml-support) for more information.
128
+
129
+ Tags are specified as simple key-value pairs:
130
+
131
+ ```json
132
+ {
133
+ "environment": "dev"
134
+ }
135
+ ```
136
+
119
137
  ### Acknowledging Capabilities
120
138
 
121
139
  CloudFormation requires that some stacks explicitly acknowledge certain capabilities before creation. This helps to prevent the creation of stacks with unintended privileges.
@@ -137,7 +155,11 @@ This is to provide backwards compatibility with previously deployed stacks and m
137
155
 
138
156
  `stackup` supports input files (template, parameters, tags) in YAML format, as well as JSON.
139
157
 
140
- It also supports the [abbreviated YAML syntax for Cloudformation functions](https://aws.amazon.com/blogs/aws/aws-cloudformation-update-yaml-cross-stack-references-simplified-substitution/), though unlike the [AWS CLI](https://aws.amazon.com/cli/), Stackup normalises YAML input to JSON before invoking CloudFormation APIs.
158
+ It also supports the [abbreviated YAML syntax for Cloudformation functions](https://aws.amazon.com/blogs/aws/aws-cloudformation-update-yaml-cross-stack-references-simplified-substitution/), though unlike the [AWS CLI](https://aws.amazon.com/cli/), Stackup (by default) normalises YAML input to JSON before invoking CloudFormation APIs.
159
+
160
+ If you don't want normalisation of the YAML input to JSON, then use the `--preserve-template-formatting` flag to the `up` or `change-set create` commands.
161
+
162
+ Note: normalisation of S3 / HTTP URL stored templates is never done, as Cloudformation collects these directly.
141
163
 
142
164
  ### AWS credentials
143
165
 
@@ -194,6 +216,8 @@ You can also create, list, inspect, apply and delete [change sets](http://docs.a
194
216
 
195
217
  The change-set name defaults to "pending", but can be overridden using `--name`.
196
218
 
219
+ The `change-set create` subcommand, like the `up` command, supports `--service-role-arn` to specify a service role.
220
+
197
221
  ## Programmatic usage
198
222
 
199
223
  Get a handle to a `Stack` object as follows:
@@ -278,18 +302,24 @@ This policy grants the principal all actions required by `stackup up` for any cl
278
302
  }
279
303
  ```
280
304
 
281
- ## Releasing
305
+ ## Development
306
+
307
+ ### Running tests
308
+
309
+ `auto/test` will run the tests in a Docker container.
310
+
311
+ ### Releasing
282
312
 
283
- The release process will push tags to GitHub, push the gem to rubygems and push the docker image to DockerHub.
313
+ Releasing is done manually, not by CI. The release process will push tags to GitHub, push the gem to rubygems and push the docker image to DockerHub.
284
314
 
285
315
  Prerequisites:
286
316
 
287
- * logged into dockerhub via `docker login`. Your user must have permission to push to `realestate/stackup`
288
- * logged into rubygems via `gem push`. Your user must have permission to push to the `stackup` gem.
317
+ * You must be logged into docker hub via `docker login`. Your user must have permission to push to `realestate/stackup`
318
+ * You must have a rubygems account with permission to push to the `stackup` gem. (`auto/release` will ask for your username and password)
319
+ * You must have cloned this repo via HTTPS and have a github account with permission to push. (`auto/release` will ask for your username and a GitHub personal access token)
289
320
 
290
321
  To release:
291
322
 
292
323
  ```
293
- bundle install
294
324
  auto/release
295
325
  ```
@@ -2,542 +2,9 @@
2
2
 
3
3
  $LOAD_PATH << File.expand_path("../lib", __dir__)
4
4
 
5
- require "clamp"
6
- require "console_logger"
7
- require "multi_json"
8
- require "securerandom"
9
- require "stackup"
10
- require "stackup/differ"
11
- require "stackup/source"
12
- require "stackup/version"
13
- require "stackup/yaml"
5
+ require "stackup/main_command"
14
6
 
15
7
  $stdout.sync = true
16
8
  $stderr.sync = true
17
9
 
18
- Clamp do
19
-
20
- option ["-L", "--list"], :flag, "list stacks" do
21
- list_stacks
22
- exit 0
23
- end
24
-
25
- option ["-Y", "--yaml"], :flag, "output data in YAML format"
26
-
27
- option ["--region"], "REGION", "set region" do |arg|
28
- raise ArgumentError, "#{arg.inspect} doesn't look like a region" unless arg =~ /^[a-z]{2}-[a-z]+-\d$/
29
-
30
- arg
31
- end
32
-
33
- option ["--with-role"], "ROLE_ARN", "assume this role",
34
- :attribute_name => :role_arn
35
-
36
- option ["--retry-limit"], "N", "maximum number of retries for API calls",
37
- :environment_variable => "AWS_API_RETRY_LIMIT" do |arg|
38
- Integer(arg)
39
- end
40
-
41
- option ["--[no-]wait"], :flag, "wait for stack updates to complete",
42
- :default => true
43
-
44
- option ["--wait-poll-interval"], "N", "polling interval while waiting for updates",
45
- :default => 5, &method(:Integer)
46
-
47
- option "--debug", :flag, "enable debugging"
48
-
49
- option ["--version"], :flag, "display version" do
50
- puts "stackup v#{Stackup::VERSION}"
51
- exit 0
52
- end
53
-
54
- parameter "NAME", "Name of stack", :attribute_name => :stack_name
55
-
56
- def run(arguments)
57
- super(arguments)
58
- rescue Stackup::Source::ReadError => e
59
- signal_error e.message
60
- rescue Stackup::ServiceError => e
61
- signal_error e.message
62
- rescue Aws::Errors::MissingCredentialsError
63
- signal_error "no credentials provided"
64
- rescue Aws::Errors::ServiceError => e
65
- signal_error e.message
66
- end
67
-
68
- private
69
-
70
- def logger
71
- @logger ||= ConsoleLogger.new($stdout, debug?)
72
- end
73
-
74
- def format_data(data)
75
- if yaml?
76
- YAML.dump(data)
77
- else
78
- MultiJson.dump(data, :pretty => true)
79
- end
80
- end
81
-
82
- def display_data(data)
83
- puts format_data(data)
84
- end
85
-
86
- def role_arn=(arg)
87
- raise ArgumentError, "#{arg.inspect} doesn't look like a role ARN" unless arg =~ %r{^arn:aws:iam::\d+:role/}
88
-
89
- @role_arn = arg
90
- end
91
-
92
- def stackup
93
- Stackup(aws_config)
94
- end
95
-
96
- def base_aws_config
97
- {
98
- :log_level => :debug,
99
- :logger => logger,
100
- :region => region,
101
- :retry_limit => retry_limit
102
- }.reject { |_k, v| v.nil? }
103
- end
104
-
105
- def aws_config
106
- return base_aws_config unless role_arn
107
-
108
- assumed_credentials = Aws::AssumeRoleCredentials.new(
109
- :client => Aws::STS::Client.new(base_aws_config),
110
- :role_arn => role_arn,
111
- :role_session_name => "stackup-#{SecureRandom.hex(8)}"
112
- )
113
- base_aws_config.merge(:credentials => assumed_credentials)
114
- end
115
-
116
- def stack
117
- stackup.stack(stack_name, :wait => wait?, :wait_poll_interval => wait_poll_interval)
118
- end
119
-
120
- def list_stacks
121
- stackup.stack_names.each do |name|
122
- puts name
123
- end
124
- end
125
-
126
- def report_change
127
- final_status = yield
128
- puts final_status unless final_status.nil?
129
- end
130
-
131
- subcommand "status", "Print stack status." do
132
-
133
- def execute
134
- puts stack.status
135
- end
136
-
137
- end
138
-
139
- module HasParameters
140
-
141
- extend Clamp::Option::Declaration
142
-
143
- option ["-p", "--parameters"], "FILE", "parameters file (last wins)",
144
- :multivalued => true,
145
- :attribute_name => :parameter_sources,
146
- &Stackup::Source.method(:new)
147
-
148
- option ["-o", "--override"], "PARAM=VALUE", "parameter overrides",
149
- :multivalued => true,
150
- :attribute_name => :override_list
151
-
152
- private
153
-
154
- def parameters
155
- parameters_from_files.merge(parameter_overrides)
156
- end
157
-
158
- def parameters_from_files
159
- parameter_sources.map do |src|
160
- Stackup::Parameters.new(src.data).to_hash
161
- end.inject({}, :merge)
162
- end
163
-
164
- def parameter_overrides
165
- {}.tap do |result|
166
- override_list.each do |override|
167
- key, value = override.split("=", 2)
168
- result[key] = value
169
- end
170
- end
171
- end
172
-
173
- end
174
-
175
- subcommand "up", "Create/update the stack." do
176
-
177
- option ["-t", "--template"], "FILE", "template source",
178
- :attribute_name => :template_source,
179
- &Stackup::Source.method(:new)
180
-
181
- option ["-T", "--use-previous-template"], :flag,
182
- "reuse the existing template"
183
-
184
- include HasParameters
185
-
186
- option "--tags", "FILE", "stack tags file",
187
- :attribute_name => :tag_source,
188
- &Stackup::Source.method(:new)
189
-
190
- option "--policy", "FILE", "stack policy file",
191
- :attribute_name => :policy_source,
192
- &Stackup::Source.method(:new)
193
-
194
- option "--service-role-arn", "SERVICE_ROLE_ARN", "cloudformation service role ARN" do |arg|
195
- raise ArgumentError, "#{arg.inspect} doesn't look like a role ARN" unless arg =~ %r{^arn:aws:iam::\d+:role/}
196
-
197
- arg
198
- end
199
-
200
- option "--on-failure", "ACTION",
201
- "when stack creation fails: DO_NOTHING, ROLLBACK, or DELETE",
202
- :default => "ROLLBACK"
203
-
204
- option "--capability", "CAPABILITY", "cloudformation capability",
205
- :multivalued => true, :default => ["CAPABILITY_NAMED_IAM"]
206
-
207
- def execute
208
- unless template_source || use_previous_template?
209
- signal_usage_error "Specify either --template or --use-previous-template"
210
- end
211
- options = {}
212
- if template_source
213
- if template_source.s3?
214
- options[:template_url] = template_source.location
215
- else
216
- options[:template] = template_source.data
217
- end
218
- end
219
- options[:on_failure] = on_failure
220
- options[:parameters] = parameters
221
- options[:tags] = tag_source.data if tag_source
222
- if policy_source
223
- if policy_source.s3?
224
- options[:stack_policy_url] = policy_source.location
225
- else
226
- options[:stack_policy] = policy_source.data
227
- end
228
- end
229
- options[:role_arn] = service_role_arn if service_role_arn
230
- options[:use_previous_template] = use_previous_template?
231
- options[:capabilities] = capability_list
232
- report_change do
233
- stack.create_or_update(options)
234
- end
235
- end
236
-
237
- end
238
-
239
- subcommand ["change-sets"], "List change-sets." do
240
-
241
- def execute
242
- stack.change_set_summaries.each do |change_set|
243
- puts [
244
- pad(change_set.change_set_name, 36),
245
- pad(change_set.status, 20),
246
- pad(change_set.execution_status, 24)
247
- ].join(" ")
248
- end
249
- end
250
-
251
- private
252
-
253
- def pad(s, width)
254
- (s || "").ljust(width)
255
- end
256
-
257
- end
258
-
259
- subcommand ["change-set"], "Change-set operations." do
260
-
261
- option "--name", "NAME", "Name of change-set",
262
- :attribute_name => :change_set_name,
263
- :default => "pending"
264
-
265
- subcommand "create", "Create a change-set." do
266
-
267
- option ["-d", "--description"], "DESC",
268
- "Change-set description"
269
-
270
- option ["-t", "--template"], "FILE", "template source",
271
- :attribute_name => :template_source,
272
- &Stackup::Source.method(:new)
273
-
274
- option ["-T", "--use-previous-template"], :flag,
275
- "reuse the existing template"
276
-
277
- option ["--force"], :flag,
278
- "replace existing change-set of the same name"
279
-
280
- include HasParameters
281
-
282
- option "--tags", "FILE", "stack tags file",
283
- :attribute_name => :tag_source,
284
- &Stackup::Source.method(:new)
285
-
286
- option "--capability", "CAPABILITY", "cloudformation capability",
287
- :multivalued => true, :default => ["CAPABILITY_NAMED_IAM"]
288
-
289
- def execute
290
- unless template_source || use_previous_template?
291
- signal_usage_error "Specify either --template or --use-previous-template"
292
- end
293
- options = {}
294
- if template_source
295
- if template_source.s3?
296
- options[:template_url] = template_source.location
297
- else
298
- options[:template] = template_source.data
299
- end
300
- end
301
- options[:parameters] = parameters
302
- options[:description] = description if description
303
- options[:tags] = tag_source.data if tag_source
304
- options[:use_previous_template] = use_previous_template?
305
- options[:force] = force?
306
- options[:capabilities] = capability_list
307
- report_change do
308
- change_set.create(options)
309
- end
310
- end
311
-
312
- end
313
-
314
- subcommand "changes", "Describe the change-set." do
315
-
316
- def execute
317
- display_data(change_set.describe.changes.map(&:to_h))
318
- end
319
-
320
- end
321
-
322
- subcommand "inspect", "Show full change-set details." do
323
-
324
- def execute
325
- display_data(change_set.describe.to_h)
326
- end
327
-
328
- end
329
-
330
- subcommand ["apply", "execute"], "Apply the change-set." do
331
-
332
- def execute
333
- report_change do
334
- change_set.execute
335
- end
336
- end
337
-
338
- end
339
-
340
- subcommand "delete", "Delete the change-set." do
341
-
342
- def execute
343
- report_change do
344
- change_set.delete
345
- end
346
- end
347
-
348
- end
349
-
350
- private
351
-
352
- def change_set
353
- stack.change_set(change_set_name)
354
- end
355
-
356
- end
357
-
358
- subcommand "diff", "Compare template/params to current stack." do
359
-
360
- option "--diff-format", "FORMAT", "'text', 'color', or 'html'", :default => "color"
361
-
362
- option ["-C", "--context-lines"], "LINES", "number of lines of context to show", :default => 10_000
363
-
364
- option ["-t", "--template"], "FILE", "template source",
365
- :attribute_name => :template_source,
366
- &Stackup::Source.method(:new)
367
-
368
- include HasParameters
369
-
370
- option "--tags", "FILE", "stack tags file",
371
- :attribute_name => :tag_source,
372
- &Stackup::Source.method(:new)
373
-
374
- def execute
375
- current = {}
376
- planned = {}
377
- if template_source
378
- current["Template"] = stack.template
379
- planned["Template"] = template_source.data
380
- end
381
- unless parameter_sources.empty?
382
- current["Parameters"] = existing_parameters.sort.to_h
383
- planned["Parameters"] = new_parameters.sort.to_h
384
- end
385
- if tag_source
386
- current["Tags"] = stack.tags.sort.to_h
387
- planned["Tags"] = tag_source.data.sort.to_h
388
- end
389
- signal_usage_error "specify '--template' or '--parameters'" if planned.empty?
390
- puts differ.diff(current, planned, context_lines)
391
- end
392
-
393
- private
394
-
395
- def differ
396
- Stackup::Differ.new(diff_format, &method(:format_data))
397
- end
398
-
399
- def existing_parameters
400
- @existing_parameters ||= stack.parameters
401
- end
402
-
403
- def new_parameters
404
- existing_parameters.merge(parameters)
405
- end
406
-
407
- end
408
-
409
- subcommand ["down", "delete"], "Remove the stack." do
410
-
411
- def execute
412
- report_change do
413
- stack.delete
414
- end
415
- end
416
-
417
- end
418
-
419
- subcommand "cancel-update", "Cancel the update in-progress." do
420
-
421
- def execute
422
- report_change do
423
- stack.cancel_update
424
- end
425
- end
426
-
427
- end
428
-
429
- subcommand "wait", "Wait until stack is stable." do
430
-
431
- def execute
432
- puts stack.wait
433
- end
434
-
435
- end
436
-
437
- subcommand "events", "List stack events." do
438
-
439
- option ["-f", "--follow"], :flag, "follow new events"
440
- option ["--data"], :flag, "display events as data"
441
-
442
- def execute
443
- stack.watch(false) do |watcher|
444
- loop do
445
- watcher.each_new_event do |event|
446
- display_event(event)
447
- end
448
- break unless follow?
449
-
450
- sleep 5
451
- end
452
- end
453
- end
454
-
455
- private
456
-
457
- def display_event(e)
458
- if data?
459
- display_data(event_data(e))
460
- else
461
- puts event_summary(e)
462
- end
463
- end
464
-
465
- def event_data(e)
466
- {
467
- "timestamp" => e.timestamp.localtime,
468
- "logical_resource_id" => e.logical_resource_id,
469
- "physical_resource_id" => e.physical_resource_id,
470
- "resource_status" => e.resource_status,
471
- "resource_status_reason" => e.resource_status_reason
472
- }.reject { |_k, v| blank?(v) }
473
- end
474
-
475
- def blank?(v)
476
- v.nil? || v.respond_to?(:empty?) && v.empty?
477
- end
478
-
479
- def event_summary(e)
480
- summary = "[#{e.timestamp.localtime.iso8601}] #{e.logical_resource_id}"
481
- summary += " - #{e.resource_status}"
482
- summary += " - #{e.resource_status_reason}" if e.resource_status_reason
483
- summary
484
- end
485
-
486
- end
487
-
488
- subcommand "template", "Display stack template." do
489
-
490
- def execute
491
- display_data(stack.template)
492
- end
493
-
494
- end
495
-
496
- subcommand ["parameters", "params"], "Display stack parameters." do
497
-
498
- def execute
499
- display_data(stack.parameters)
500
- end
501
-
502
- end
503
-
504
- subcommand "tags", "Display stack tags." do
505
-
506
- def execute
507
- display_data(stack.tags)
508
- end
509
-
510
- end
511
-
512
- subcommand "resources", "Display stack resources." do
513
-
514
- def execute
515
- display_data(stack.resources)
516
- end
517
-
518
- end
519
-
520
- subcommand "outputs", "Display stack outputs." do
521
-
522
- def execute
523
- display_data(stack.outputs)
524
- end
525
-
526
- end
527
-
528
- subcommand "inspect", "Display stack particulars." do
529
-
530
- def execute
531
- data = {
532
- "Status" => stack.status,
533
- "Parameters" => stack.parameters,
534
- "Tags" => stack.tags,
535
- "Resources" => stack.resources,
536
- "Outputs" => stack.outputs
537
- }
538
- display_data(data)
539
- end
540
-
541
- end
542
-
543
- end
10
+ Stackup::MainCommand.run