stackup 1.4.5 → 1.7.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 +4 -4
- data/CHANGES.md +20 -0
- data/README.md +39 -7
- data/bin/stackup +2 -535
- data/lib/stackup/change_set.rb +10 -2
- data/lib/stackup/main_command.rb +561 -0
- data/lib/stackup/source.rb +1 -1
- data/lib/stackup/stack.rb +19 -8
- data/lib/stackup/version.rb +1 -1
- data/spec/stackup/main_command_spec.rb +56 -0
- data/spec/stackup/rake_tasks_spec.rb +3 -3
- data/spec/stackup/source_spec.rb +10 -0
- data/spec/stackup/stack_spec.rb +69 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 60e50e255335b7924e71a6fed889c946c3b182c2b95277056f88e2e420915f79
|
4
|
+
data.tar.gz: 816b9a71283680668fe9d3cf35c54a3a1a2516a68f0251a1771e4bba17e2cce2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be56a2ae96045ace217de36cd7f82592eb2588178b314f8d68d51e6b6d16a1e66c60557e78fa5f524cb710aa42f0d64372629fe4d6b9aad65c3ddec11fe051f4
|
7
|
+
data.tar.gz: 79974fadb7ac89a76a92fa424c4cc2ef63deeec8cfd55b6eda352e965ecd84e832322eddafaaf9eb57fd3640c3744646a484954664f15cfa9bbfeda4b0eb03c8
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
# CHANGES
|
2
2
|
|
3
|
+
## 1.7.0 (2020-11-25)
|
4
|
+
|
5
|
+
* Feature: --no-fail-on-empty-change-set
|
6
|
+
|
7
|
+
## 1.6.0 (2020-11-19)
|
8
|
+
|
9
|
+
* Feature: Support --service-role-arn on "change-set create"
|
10
|
+
|
11
|
+
## 1.5.1 (2020-11-16)
|
12
|
+
|
13
|
+
* Fix: Recognise that template is in S3 when URL is formatted like `s3.REGION.amazonaws.com`
|
14
|
+
|
15
|
+
## 1.5.0 (2020-04-21)
|
16
|
+
|
17
|
+
* Feature: --preserve-template-formatting
|
18
|
+
|
19
|
+
## 1.4.6 (2019-12-09)
|
20
|
+
|
21
|
+
* Fix: Don't error out when receiving tags in AWS style (array of hashes) from YAML or JSON file
|
22
|
+
|
3
23
|
## 1.4.5 (2019-10-03)
|
4
24
|
|
5
25
|
* Fix: Tags in 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
|
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,10 @@ 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
|
+
|
221
|
+
It is impossible to create a change set with no changes. By default, stackup will only return successfully if a change set was actually created, and will otherwise fail. If the `--no-fail-on-empty-change-set` option is provided, stackup will return successfully if a change set was created _or_ if no change set was created because no changes were needed.
|
222
|
+
|
197
223
|
## Programmatic usage
|
198
224
|
|
199
225
|
Get a handle to a `Stack` object as follows:
|
@@ -278,18 +304,24 @@ This policy grants the principal all actions required by `stackup up` for any cl
|
|
278
304
|
}
|
279
305
|
```
|
280
306
|
|
281
|
-
##
|
307
|
+
## Development
|
308
|
+
|
309
|
+
### Running tests
|
310
|
+
|
311
|
+
`auto/test` will run the tests in a Docker container.
|
312
|
+
|
313
|
+
### Releasing
|
282
314
|
|
283
|
-
The release process will push tags to GitHub, push the gem to rubygems and push the docker image to DockerHub.
|
315
|
+
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
316
|
|
285
317
|
Prerequisites:
|
286
318
|
|
287
|
-
* logged into
|
288
|
-
*
|
319
|
+
* You must be logged into docker hub via `docker login`. Your user must have permission to push to `realestate/stackup`
|
320
|
+
* You must have a rubygems account with permission to push to the `stackup` gem. (`auto/release` will ask for your username and password)
|
321
|
+
* 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
322
|
|
290
323
|
To release:
|
291
324
|
|
292
325
|
```
|
293
|
-
bundle install
|
294
326
|
auto/release
|
295
327
|
```
|
data/bin/stackup
CHANGED
@@ -2,542 +2,9 @@
|
|
2
2
|
|
3
3
|
$LOAD_PATH << File.expand_path("../lib", __dir__)
|
4
4
|
|
5
|
-
require "
|
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
|
-
|
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
|