stackup 1.5.0 → 1.7.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/CHANGES.md +17 -0
- data/README.md +21 -12
- data/bin/stackup +3 -545
- data/lib/stackup/change_set.rb +24 -13
- data/lib/stackup/differ.rb +2 -0
- data/lib/stackup/error_handling.rb +2 -0
- data/lib/stackup/errors.rb +2 -0
- data/lib/stackup/main_command.rb +551 -0
- data/lib/stackup/parameters.rb +8 -6
- data/lib/stackup/rake_tasks.rb +3 -2
- data/lib/stackup/service.rb +2 -0
- data/lib/stackup/source.rb +3 -1
- data/lib/stackup/stack_watcher.rb +4 -4
- data/lib/stackup/utils.rb +2 -0
- data/lib/stackup/version.rb +3 -1
- data/lib/stackup/yaml.rb +2 -0
- data/lib/stackup.rb +2 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/stackup/main_command_spec.rb +61 -0
- data/spec/stackup/parameters_spec.rb +2 -0
- data/spec/stackup/rake_tasks_spec.rb +2 -0
- data/spec/stackup/source_spec.rb +12 -0
- data/spec/stackup/stack_spec.rb +51 -0
- data/spec/stackup/stack_watcher_spec.rb +2 -0
- data/spec/stackup/utils_spec.rb +2 -0
- data/spec/stackup/yaml_spec.rb +2 -0
- metadata +12 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f74cb61004ec9220d92f5f1d1ff292607b216233bc5aed7668d31027e4e2d2b3
|
|
4
|
+
data.tar.gz: 18cce0dfddda4b0870affec164d0d0c9cdb30e2e25f1ea675e3b64bd3385323e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 38a0621de917ddd7bc2163756c854ad89bae2f79e5b8f1d784e04042b55f4a5a9ea5a84bed89f305c4604efc7c9ccb01ec46c32ec3635e3d03f3148b5b3db0ed
|
|
7
|
+
data.tar.gz: ac2b19120472ed87471053394d1383d860939a562aebdf5e17fa2a3fc1290b48397c1dae1d9b6c752d6a0a4fd041716b9c753be42f135661303863e2e471397d
|
data/CHANGES.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# CHANGES
|
|
2
2
|
|
|
3
|
+
## 1.7.1 (2021-08-31)
|
|
4
|
+
|
|
5
|
+
* Dependency upgrades. Uplift of release process.
|
|
6
|
+
* M1/arm64 support for docker image releases.
|
|
7
|
+
|
|
8
|
+
## 1.7.0 (2020-11-25)
|
|
9
|
+
|
|
10
|
+
* Feature: --no-fail-on-empty-change-set
|
|
11
|
+
|
|
12
|
+
## 1.6.0 (2020-11-19)
|
|
13
|
+
|
|
14
|
+
* Feature: Support --service-role-arn on "change-set create"
|
|
15
|
+
|
|
16
|
+
## 1.5.1 (2020-11-16)
|
|
17
|
+
|
|
18
|
+
* Fix: Recognise that template is in S3 when URL is formatted like `s3.REGION.amazonaws.com`
|
|
19
|
+
|
|
3
20
|
## 1.5.0 (2020-04-21)
|
|
4
21
|
|
|
5
22
|
* Feature: --preserve-template-formatting
|
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# stackup
|
|
2
2
|
|
|
3
3
|
[](http://badge.fury.io/rb/stackup)
|
|
4
|
-
[](https://travis-ci.com/realestate-com-au/stackup)
|
|
5
5
|
|
|
6
6
|
Stackup provides a CLI and a simplified Ruby API for dealing with
|
|
7
7
|
AWS CloudFormation stacks.
|
|
@@ -191,7 +191,7 @@ Where a template URL references an object in S3, `stackup` leverages [CloudForma
|
|
|
191
191
|
Non-S3 URLs are also supported, though in that case `stackup` must fetch the content itself:
|
|
192
192
|
|
|
193
193
|
$ stackup mystack up \
|
|
194
|
-
-t https://raw.githubusercontent.com/realestate-com-au/stackup/
|
|
194
|
+
-t https://raw.githubusercontent.com/realestate-com-au/stackup/main/examples/template.yml
|
|
195
195
|
|
|
196
196
|
### Stack deletion
|
|
197
197
|
|
|
@@ -216,6 +216,10 @@ You can also create, list, inspect, apply and delete [change sets](http://docs.a
|
|
|
216
216
|
|
|
217
217
|
The change-set name defaults to "pending", but can be overridden using `--name`.
|
|
218
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
|
+
|
|
219
223
|
## Programmatic usage
|
|
220
224
|
|
|
221
225
|
Get a handle to a `Stack` object as follows:
|
|
@@ -300,18 +304,23 @@ This policy grants the principal all actions required by `stackup up` for any cl
|
|
|
300
304
|
}
|
|
301
305
|
```
|
|
302
306
|
|
|
303
|
-
##
|
|
307
|
+
## Development
|
|
304
308
|
|
|
305
|
-
|
|
309
|
+
### Running tests
|
|
306
310
|
|
|
307
|
-
|
|
311
|
+
`auto/test` will run the tests in a Docker container.
|
|
312
|
+
`auto/lint` will run the linter in a Docker container.
|
|
308
313
|
|
|
309
|
-
|
|
310
|
-
* logged into rubygems via `gem push`. Your user must have permission to push to the `stackup` gem.
|
|
314
|
+
### Releasing
|
|
311
315
|
|
|
312
|
-
|
|
316
|
+
Releasing is done mostly by CI. The automated process will push tags to GitHub, push the docker images to DockerHub. It won't yet push the gem to `rubygems.org`, but we're working ok it..
|
|
313
317
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
auto/release
|
|
317
|
-
|
|
318
|
+
Prerequisites:
|
|
319
|
+
|
|
320
|
+
* You must have a rubygems account with permission to push to the `stackup` gem. (`auto/release-gem` will ask for your username and password)
|
|
321
|
+
* You have bumped the version number in `lib/stackup/version.rb`.
|
|
322
|
+
* You have checked the `CHANGES.md` file to make sure it is reasonable and has the changes for this new version.
|
|
323
|
+
|
|
324
|
+
To release the rubygem.
|
|
325
|
+
1. `auto/release-gem`
|
|
326
|
+
2. On REA's internal CI tool of choice, find the build for `stackup-ci` and trigger a new build from `HEAD`. This will always grab the latest code from here and release the rest of the parts.
|
data/bin/stackup
CHANGED
|
@@ -1,553 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
$LOAD_PATH << File.expand_path("../lib", __dir__)
|
|
4
5
|
|
|
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"
|
|
6
|
+
require "stackup/main_command"
|
|
14
7
|
|
|
15
8
|
$stdout.sync = true
|
|
16
9
|
$stderr.sync = true
|
|
17
10
|
|
|
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 (in seconds) 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
|
-
option ["-P", "--preserve-template-formatting"], :flag,
|
|
185
|
-
"do not normalise the template when calling the Cloudformation APIs; useful for preserving YAML and comments"
|
|
186
|
-
|
|
187
|
-
include HasParameters
|
|
188
|
-
|
|
189
|
-
option "--tags", "FILE", "stack tags file",
|
|
190
|
-
:attribute_name => :tag_source,
|
|
191
|
-
&Stackup::Source.method(:new)
|
|
192
|
-
|
|
193
|
-
option "--policy", "FILE", "stack policy file",
|
|
194
|
-
:attribute_name => :policy_source,
|
|
195
|
-
&Stackup::Source.method(:new)
|
|
196
|
-
|
|
197
|
-
option "--service-role-arn", "SERVICE_ROLE_ARN", "cloudformation service role ARN" do |arg|
|
|
198
|
-
raise ArgumentError, "#{arg.inspect} doesn't look like a role ARN" unless arg =~ %r{^arn:aws:iam::\d+:role/}
|
|
199
|
-
|
|
200
|
-
arg
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
option "--on-failure", "ACTION",
|
|
204
|
-
"when stack creation fails: DO_NOTHING, ROLLBACK, or DELETE",
|
|
205
|
-
:default => "ROLLBACK"
|
|
206
|
-
|
|
207
|
-
option "--capability", "CAPABILITY", "cloudformation capability",
|
|
208
|
-
:multivalued => true, :default => ["CAPABILITY_NAMED_IAM"]
|
|
209
|
-
|
|
210
|
-
def execute
|
|
211
|
-
unless template_source || use_previous_template?
|
|
212
|
-
signal_usage_error "Specify either --template or --use-previous-template"
|
|
213
|
-
end
|
|
214
|
-
options = {}
|
|
215
|
-
if template_source
|
|
216
|
-
if template_source.s3?
|
|
217
|
-
options[:template_url] = template_source.location
|
|
218
|
-
else
|
|
219
|
-
options[:template] = template_source.data
|
|
220
|
-
options[:template_orig] = template_source.body
|
|
221
|
-
end
|
|
222
|
-
end
|
|
223
|
-
options[:on_failure] = on_failure
|
|
224
|
-
options[:parameters] = parameters
|
|
225
|
-
options[:tags] = tag_source.data if tag_source
|
|
226
|
-
if policy_source
|
|
227
|
-
if policy_source.s3?
|
|
228
|
-
options[:stack_policy_url] = policy_source.location
|
|
229
|
-
else
|
|
230
|
-
options[:stack_policy] = policy_source.data
|
|
231
|
-
end
|
|
232
|
-
end
|
|
233
|
-
options[:role_arn] = service_role_arn if service_role_arn
|
|
234
|
-
options[:use_previous_template] = use_previous_template?
|
|
235
|
-
options[:capabilities] = capability_list
|
|
236
|
-
options[:preserve] = preserve_template_formatting?
|
|
237
|
-
report_change do
|
|
238
|
-
stack.create_or_update(options)
|
|
239
|
-
end
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
subcommand ["change-sets"], "List change-sets." do
|
|
245
|
-
|
|
246
|
-
def execute
|
|
247
|
-
stack.change_set_summaries.each do |change_set|
|
|
248
|
-
puts [
|
|
249
|
-
pad(change_set.change_set_name, 36),
|
|
250
|
-
pad(change_set.status, 20),
|
|
251
|
-
pad(change_set.execution_status, 24)
|
|
252
|
-
].join(" ")
|
|
253
|
-
end
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
private
|
|
257
|
-
|
|
258
|
-
def pad(s, width)
|
|
259
|
-
(s || "").ljust(width)
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
subcommand ["change-set"], "Change-set operations." do
|
|
265
|
-
|
|
266
|
-
option "--name", "NAME", "Name of change-set",
|
|
267
|
-
:attribute_name => :change_set_name,
|
|
268
|
-
:default => "pending"
|
|
269
|
-
|
|
270
|
-
subcommand "create", "Create a change-set." do
|
|
271
|
-
|
|
272
|
-
option ["-d", "--description"], "DESC",
|
|
273
|
-
"Change-set description"
|
|
274
|
-
|
|
275
|
-
option ["-t", "--template"], "FILE", "template source",
|
|
276
|
-
:attribute_name => :template_source,
|
|
277
|
-
&Stackup::Source.method(:new)
|
|
278
|
-
|
|
279
|
-
option ["-T", "--use-previous-template"], :flag,
|
|
280
|
-
"reuse the existing template"
|
|
281
|
-
|
|
282
|
-
option ["-P", "--preserve-template-formatting"], :flag,
|
|
283
|
-
"do not normalise the template when calling the Cloudformation APIs; useful for preserving YAML and comments"
|
|
284
|
-
|
|
285
|
-
option ["--force"], :flag,
|
|
286
|
-
"replace existing change-set of the same name"
|
|
287
|
-
|
|
288
|
-
include HasParameters
|
|
289
|
-
|
|
290
|
-
option "--tags", "FILE", "stack tags file",
|
|
291
|
-
:attribute_name => :tag_source,
|
|
292
|
-
&Stackup::Source.method(:new)
|
|
293
|
-
|
|
294
|
-
option "--capability", "CAPABILITY", "cloudformation capability",
|
|
295
|
-
:multivalued => true, :default => ["CAPABILITY_NAMED_IAM"]
|
|
296
|
-
|
|
297
|
-
def execute
|
|
298
|
-
unless template_source || use_previous_template?
|
|
299
|
-
signal_usage_error "Specify either --template or --use-previous-template"
|
|
300
|
-
end
|
|
301
|
-
options = {}
|
|
302
|
-
if template_source
|
|
303
|
-
if template_source.s3?
|
|
304
|
-
options[:template_url] = template_source.location
|
|
305
|
-
else
|
|
306
|
-
options[:template] = template_source.data
|
|
307
|
-
options[:template_orig] = template_source.body
|
|
308
|
-
end
|
|
309
|
-
end
|
|
310
|
-
options[:parameters] = parameters
|
|
311
|
-
options[:description] = description if description
|
|
312
|
-
options[:tags] = tag_source.data if tag_source
|
|
313
|
-
options[:use_previous_template] = use_previous_template?
|
|
314
|
-
options[:force] = force?
|
|
315
|
-
options[:capabilities] = capability_list
|
|
316
|
-
options[:preserve] = preserve_template_formatting?
|
|
317
|
-
report_change do
|
|
318
|
-
change_set.create(options)
|
|
319
|
-
end
|
|
320
|
-
end
|
|
321
|
-
|
|
322
|
-
end
|
|
323
|
-
|
|
324
|
-
subcommand "changes", "Describe the change-set." do
|
|
325
|
-
|
|
326
|
-
def execute
|
|
327
|
-
display_data(change_set.describe.changes.map(&:to_h))
|
|
328
|
-
end
|
|
329
|
-
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
subcommand "inspect", "Show full change-set details." do
|
|
333
|
-
|
|
334
|
-
def execute
|
|
335
|
-
display_data(change_set.describe.to_h)
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
end
|
|
339
|
-
|
|
340
|
-
subcommand ["apply", "execute"], "Apply the change-set." do
|
|
341
|
-
|
|
342
|
-
def execute
|
|
343
|
-
report_change do
|
|
344
|
-
change_set.execute
|
|
345
|
-
end
|
|
346
|
-
end
|
|
347
|
-
|
|
348
|
-
end
|
|
349
|
-
|
|
350
|
-
subcommand "delete", "Delete the change-set." do
|
|
351
|
-
|
|
352
|
-
def execute
|
|
353
|
-
report_change do
|
|
354
|
-
change_set.delete
|
|
355
|
-
end
|
|
356
|
-
end
|
|
357
|
-
|
|
358
|
-
end
|
|
359
|
-
|
|
360
|
-
private
|
|
361
|
-
|
|
362
|
-
def change_set
|
|
363
|
-
stack.change_set(change_set_name)
|
|
364
|
-
end
|
|
365
|
-
|
|
366
|
-
end
|
|
367
|
-
|
|
368
|
-
subcommand "diff", "Compare template/params to current stack." do
|
|
369
|
-
|
|
370
|
-
option "--diff-format", "FORMAT", "'text', 'color', or 'html'", :default => "color"
|
|
371
|
-
|
|
372
|
-
option ["-C", "--context-lines"], "LINES", "number of lines of context to show", :default => 10_000
|
|
373
|
-
|
|
374
|
-
option ["-t", "--template"], "FILE", "template source",
|
|
375
|
-
:attribute_name => :template_source,
|
|
376
|
-
&Stackup::Source.method(:new)
|
|
377
|
-
|
|
378
|
-
include HasParameters
|
|
379
|
-
|
|
380
|
-
option "--tags", "FILE", "stack tags file",
|
|
381
|
-
:attribute_name => :tag_source,
|
|
382
|
-
&Stackup::Source.method(:new)
|
|
383
|
-
|
|
384
|
-
def execute
|
|
385
|
-
current = {}
|
|
386
|
-
planned = {}
|
|
387
|
-
if template_source
|
|
388
|
-
current["Template"] = stack.template
|
|
389
|
-
planned["Template"] = template_source.data
|
|
390
|
-
end
|
|
391
|
-
unless parameter_sources.empty?
|
|
392
|
-
current["Parameters"] = existing_parameters.sort.to_h
|
|
393
|
-
planned["Parameters"] = new_parameters.sort.to_h
|
|
394
|
-
end
|
|
395
|
-
if tag_source
|
|
396
|
-
current["Tags"] = stack.tags.sort.to_h
|
|
397
|
-
planned["Tags"] = tag_source.data.sort.to_h
|
|
398
|
-
end
|
|
399
|
-
signal_usage_error "specify '--template' or '--parameters'" if planned.empty?
|
|
400
|
-
puts differ.diff(current, planned, context_lines)
|
|
401
|
-
end
|
|
402
|
-
|
|
403
|
-
private
|
|
404
|
-
|
|
405
|
-
def differ
|
|
406
|
-
Stackup::Differ.new(diff_format, &method(:format_data))
|
|
407
|
-
end
|
|
408
|
-
|
|
409
|
-
def existing_parameters
|
|
410
|
-
@existing_parameters ||= stack.parameters
|
|
411
|
-
end
|
|
412
|
-
|
|
413
|
-
def new_parameters
|
|
414
|
-
existing_parameters.merge(parameters)
|
|
415
|
-
end
|
|
416
|
-
|
|
417
|
-
end
|
|
418
|
-
|
|
419
|
-
subcommand ["down", "delete"], "Remove the stack." do
|
|
420
|
-
|
|
421
|
-
def execute
|
|
422
|
-
report_change do
|
|
423
|
-
stack.delete
|
|
424
|
-
end
|
|
425
|
-
end
|
|
426
|
-
|
|
427
|
-
end
|
|
428
|
-
|
|
429
|
-
subcommand "cancel-update", "Cancel the update in-progress." do
|
|
430
|
-
|
|
431
|
-
def execute
|
|
432
|
-
report_change do
|
|
433
|
-
stack.cancel_update
|
|
434
|
-
end
|
|
435
|
-
end
|
|
436
|
-
|
|
437
|
-
end
|
|
438
|
-
|
|
439
|
-
subcommand "wait", "Wait until stack is stable." do
|
|
440
|
-
|
|
441
|
-
def execute
|
|
442
|
-
puts stack.wait
|
|
443
|
-
end
|
|
444
|
-
|
|
445
|
-
end
|
|
446
|
-
|
|
447
|
-
subcommand "events", "List stack events." do
|
|
448
|
-
|
|
449
|
-
option ["-f", "--follow"], :flag, "follow new events"
|
|
450
|
-
option ["--data"], :flag, "display events as data"
|
|
451
|
-
|
|
452
|
-
def execute
|
|
453
|
-
stack.watch(false) do |watcher|
|
|
454
|
-
loop do
|
|
455
|
-
watcher.each_new_event do |event|
|
|
456
|
-
display_event(event)
|
|
457
|
-
end
|
|
458
|
-
break unless follow?
|
|
459
|
-
|
|
460
|
-
sleep 5
|
|
461
|
-
end
|
|
462
|
-
end
|
|
463
|
-
end
|
|
464
|
-
|
|
465
|
-
private
|
|
466
|
-
|
|
467
|
-
def display_event(e)
|
|
468
|
-
if data?
|
|
469
|
-
display_data(event_data(e))
|
|
470
|
-
else
|
|
471
|
-
puts event_summary(e)
|
|
472
|
-
end
|
|
473
|
-
end
|
|
474
|
-
|
|
475
|
-
def event_data(e)
|
|
476
|
-
{
|
|
477
|
-
"timestamp" => e.timestamp.localtime,
|
|
478
|
-
"logical_resource_id" => e.logical_resource_id,
|
|
479
|
-
"physical_resource_id" => e.physical_resource_id,
|
|
480
|
-
"resource_status" => e.resource_status,
|
|
481
|
-
"resource_status_reason" => e.resource_status_reason
|
|
482
|
-
}.reject { |_k, v| blank?(v) }
|
|
483
|
-
end
|
|
484
|
-
|
|
485
|
-
def blank?(v)
|
|
486
|
-
v.nil? || v.respond_to?(:empty?) && v.empty?
|
|
487
|
-
end
|
|
488
|
-
|
|
489
|
-
def event_summary(e)
|
|
490
|
-
summary = "[#{e.timestamp.localtime.iso8601}] #{e.logical_resource_id}"
|
|
491
|
-
summary += " - #{e.resource_status}"
|
|
492
|
-
summary += " - #{e.resource_status_reason}" if e.resource_status_reason
|
|
493
|
-
summary
|
|
494
|
-
end
|
|
495
|
-
|
|
496
|
-
end
|
|
497
|
-
|
|
498
|
-
subcommand "template", "Display stack template." do
|
|
499
|
-
|
|
500
|
-
def execute
|
|
501
|
-
display_data(stack.template)
|
|
502
|
-
end
|
|
503
|
-
|
|
504
|
-
end
|
|
505
|
-
|
|
506
|
-
subcommand ["parameters", "params"], "Display stack parameters." do
|
|
507
|
-
|
|
508
|
-
def execute
|
|
509
|
-
display_data(stack.parameters)
|
|
510
|
-
end
|
|
511
|
-
|
|
512
|
-
end
|
|
513
|
-
|
|
514
|
-
subcommand "tags", "Display stack tags." do
|
|
515
|
-
|
|
516
|
-
def execute
|
|
517
|
-
display_data(stack.tags)
|
|
518
|
-
end
|
|
519
|
-
|
|
520
|
-
end
|
|
521
|
-
|
|
522
|
-
subcommand "resources", "Display stack resources." do
|
|
523
|
-
|
|
524
|
-
def execute
|
|
525
|
-
display_data(stack.resources)
|
|
526
|
-
end
|
|
527
|
-
|
|
528
|
-
end
|
|
529
|
-
|
|
530
|
-
subcommand "outputs", "Display stack outputs." do
|
|
531
|
-
|
|
532
|
-
def execute
|
|
533
|
-
display_data(stack.outputs)
|
|
534
|
-
end
|
|
535
|
-
|
|
536
|
-
end
|
|
537
|
-
|
|
538
|
-
subcommand "inspect", "Display stack particulars." do
|
|
539
|
-
|
|
540
|
-
def execute
|
|
541
|
-
data = {
|
|
542
|
-
"Status" => stack.status,
|
|
543
|
-
"Parameters" => stack.parameters,
|
|
544
|
-
"Tags" => stack.tags,
|
|
545
|
-
"Resources" => stack.resources,
|
|
546
|
-
"Outputs" => stack.outputs
|
|
547
|
-
}
|
|
548
|
-
display_data(data)
|
|
549
|
-
end
|
|
550
|
-
|
|
551
|
-
end
|
|
552
|
-
|
|
553
|
-
end
|
|
11
|
+
Stackup::MainCommand.run
|