stackup 1.4.2 → 1.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +5 -0
- data/bin/stackup +13 -0
- data/lib/stackup/change_set.rb +1 -1
- data/lib/stackup/differ.rb +1 -1
- data/lib/stackup/parameters.rb +1 -0
- data/lib/stackup/rake_tasks.rb +58 -29
- data/lib/stackup/source.rb +1 -1
- data/lib/stackup/stack.rb +3 -0
- data/lib/stackup/stack_watcher.rb +1 -0
- data/lib/stackup/version.rb +1 -1
- data/lib/stackup/yaml.rb +1 -0
- data/spec/stackup/stack_spec.rb +63 -0
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 01e367ca4b2044872b8bffc12075970ad962f5542e143a3d8f47cc6cd9ec6773
|
4
|
+
data.tar.gz: a3b4dd683a84e01885ffdc60ec306e18d212e24fefddec660cd451b8ca8c5d23
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1fc1090d8365094a4ec26df52d8f4ba745d5ff9dc275001630d4ebb013a7673105d658749ee0d3214da89c1559c0acbaa3a5f9ee1cb17ab8e643ab16436d0ac0
|
7
|
+
data.tar.gz: 578e511731086edfe83a22245bde31c50ec8c194504772f6f4ae3cd2ac56fe5819e2adfc2c11e34b576e07071207f70b2f3f2c38f61d16a0ca74f639e1668333
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# CHANGES
|
2
2
|
|
3
|
+
## 1.4.3 (2019-09-05)
|
4
|
+
|
5
|
+
* Add support for CloudFormation capabilities via CLI and Rake
|
6
|
+
* Fix: Prevent failure when creating a stack with change sets
|
7
|
+
|
3
8
|
## 1.4.2 (2019-01-21)
|
4
9
|
|
5
10
|
* Fix #64: Create and Update functions should not mutate the options parameter
|
data/bin/stackup
CHANGED
@@ -26,6 +26,7 @@ Clamp do
|
|
26
26
|
|
27
27
|
option ["--region"], "REGION", "set region" do |arg|
|
28
28
|
raise ArgumentError, "#{arg.inspect} doesn't look like a region" unless arg =~ /^[a-z]{2}-[a-z]+-\d$/
|
29
|
+
|
29
30
|
arg
|
30
31
|
end
|
31
32
|
|
@@ -84,6 +85,7 @@ Clamp do
|
|
84
85
|
|
85
86
|
def role_arn=(arg)
|
86
87
|
raise ArgumentError, "#{arg.inspect} doesn't look like a role ARN" unless arg =~ %r{^arn:aws:iam::\d+:role/}
|
88
|
+
|
87
89
|
@role_arn = arg
|
88
90
|
end
|
89
91
|
|
@@ -102,6 +104,7 @@ Clamp do
|
|
102
104
|
|
103
105
|
def aws_config
|
104
106
|
return base_aws_config unless role_arn
|
107
|
+
|
105
108
|
assumed_credentials = Aws::AssumeRoleCredentials.new(
|
106
109
|
:client => Aws::STS::Client.new(base_aws_config),
|
107
110
|
:role_arn => role_arn,
|
@@ -190,6 +193,7 @@ Clamp do
|
|
190
193
|
|
191
194
|
option "--service-role-arn", "SERVICE_ROLE_ARN", "cloudformation service role ARN" do |arg|
|
192
195
|
raise ArgumentError, "#{arg.inspect} doesn't look like a role ARN" unless arg =~ %r{^arn:aws:iam::\d+:role/}
|
196
|
+
|
193
197
|
arg
|
194
198
|
end
|
195
199
|
|
@@ -197,6 +201,9 @@ Clamp do
|
|
197
201
|
"when stack creation fails: DO_NOTHING, ROLLBACK, or DELETE",
|
198
202
|
:default => "ROLLBACK"
|
199
203
|
|
204
|
+
option "--capability", "CAPABILITY", "cloudformation capability",
|
205
|
+
:multivalued => true, :default => ["CAPABILITY_NAMED_IAM"]
|
206
|
+
|
200
207
|
def execute
|
201
208
|
unless template_source || use_previous_template?
|
202
209
|
signal_usage_error "Specify either --template or --use-previous-template"
|
@@ -221,6 +228,7 @@ Clamp do
|
|
221
228
|
end
|
222
229
|
options[:role_arn] = service_role_arn if service_role_arn
|
223
230
|
options[:use_previous_template] = use_previous_template?
|
231
|
+
options[:capabilities] = capability_list
|
224
232
|
report_change do
|
225
233
|
stack.create_or_update(options)
|
226
234
|
end
|
@@ -275,6 +283,9 @@ Clamp do
|
|
275
283
|
:attribute_name => :tag_source,
|
276
284
|
&Stackup::Source.method(:new)
|
277
285
|
|
286
|
+
option "--capability", "CAPABILITY", "cloudformation capability",
|
287
|
+
:multivalued => true, :default => ["CAPABILITY_NAMED_IAM"]
|
288
|
+
|
278
289
|
def execute
|
279
290
|
unless template_source || use_previous_template?
|
280
291
|
signal_usage_error "Specify either --template or --use-previous-template"
|
@@ -292,6 +303,7 @@ Clamp do
|
|
292
303
|
options[:tags] = tag_source.data if tag_source
|
293
304
|
options[:use_previous_template] = use_previous_template?
|
294
305
|
options[:force] = force?
|
306
|
+
options[:capabilities] = capability_list
|
295
307
|
report_change do
|
296
308
|
change_set.create(options)
|
297
309
|
end
|
@@ -434,6 +446,7 @@ Clamp do
|
|
434
446
|
display_event(event)
|
435
447
|
end
|
436
448
|
break unless follow?
|
449
|
+
|
437
450
|
sleep 5
|
438
451
|
end
|
439
452
|
end
|
data/lib/stackup/change_set.rb
CHANGED
@@ -87,7 +87,7 @@ module Stackup
|
|
87
87
|
# @raise [Stackup::StackUpdateError] if operation fails
|
88
88
|
#
|
89
89
|
def execute
|
90
|
-
modify_stack(
|
90
|
+
modify_stack(/(CREATE|UPDATE)_COMPLETE/, "update failed") do
|
91
91
|
cf_client.execute_change_set(:stack_name => stack.name, :change_set_name => name)
|
92
92
|
end
|
93
93
|
end
|
data/lib/stackup/differ.rb
CHANGED
@@ -20,7 +20,7 @@ module Stackup
|
|
20
20
|
def diff(existing_data, pending_data, context_lines = nil)
|
21
21
|
existing = format(normalize_data(existing_data)) + "\n"
|
22
22
|
pending = format(normalize_data(pending_data)) + "\n"
|
23
|
-
diff = Diffy::Diff.new(existing, pending, context
|
23
|
+
diff = Diffy::Diff.new(existing, pending, :context => context_lines).to_s(diff_style)
|
24
24
|
diff unless diff =~ /\A\s*\Z/
|
25
25
|
end
|
26
26
|
|
data/lib/stackup/parameters.rb
CHANGED
data/lib/stackup/rake_tasks.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require "rake/tasklib"
|
2
2
|
require "tempfile"
|
3
3
|
require "yaml"
|
4
|
+
require "Pathname"
|
4
5
|
|
5
6
|
module Stackup
|
6
7
|
|
@@ -13,6 +14,7 @@ module Stackup
|
|
13
14
|
attr_accessor :template
|
14
15
|
attr_accessor :parameters
|
15
16
|
attr_accessor :tags
|
17
|
+
attr_accessor :capabilities
|
16
18
|
|
17
19
|
alias namespace= name=
|
18
20
|
|
@@ -23,6 +25,7 @@ module Stackup
|
|
23
25
|
yield self if block_given?
|
24
26
|
raise ArgumentError, "no name provided" unless @name
|
25
27
|
raise ArgumentError, "no template provided" unless @template
|
28
|
+
|
26
29
|
define
|
27
30
|
end
|
28
31
|
|
@@ -36,14 +39,15 @@ module Stackup
|
|
36
39
|
def define
|
37
40
|
namespace(name) do
|
38
41
|
|
39
|
-
data_options =
|
40
|
-
data_options
|
41
|
-
data_options
|
42
|
-
data_options
|
42
|
+
data_options = []
|
43
|
+
data_options << DataOption.for("--template", template)
|
44
|
+
data_options << DataOption.for("--parameters", parameters) if parameters
|
45
|
+
data_options << DataOption.for("--tags", tags) if tag
|
43
46
|
|
44
47
|
desc "Update #{stack} stack"
|
45
|
-
task "up" => data_options.
|
46
|
-
|
48
|
+
task "up" => data_options.grep(DataOptionFile).map(&:argument) do
|
49
|
+
data_options << DataOption.for("--capability", capabilities) if capabilities
|
50
|
+
stackup "up", *data_options.map(&:to_a).flatten
|
47
51
|
end
|
48
52
|
|
49
53
|
desc "Cancel update of #{stack} stack"
|
@@ -52,8 +56,8 @@ module Stackup
|
|
52
56
|
end
|
53
57
|
|
54
58
|
desc "Show pending changes to #{stack} stack"
|
55
|
-
task "diff" => data_options.
|
56
|
-
stackup "diff", *data_options.to_a
|
59
|
+
task "diff" => data_options.grep(DataOptionFile).map(&:argument) do
|
60
|
+
stackup "diff", *data_options.map(&:to_a).flatten
|
57
61
|
end
|
58
62
|
|
59
63
|
desc "Show #{stack} stack outputs and resources"
|
@@ -74,41 +78,66 @@ module Stackup
|
|
74
78
|
end
|
75
79
|
end
|
76
80
|
|
77
|
-
#
|
78
|
-
|
79
|
-
class DataOptions
|
80
|
-
|
81
|
-
def initialize
|
82
|
-
@options = {}
|
83
|
-
end
|
81
|
+
# A flag with optional argument that will be passed to stackup
|
82
|
+
class DataOption
|
84
83
|
|
85
|
-
def
|
86
|
-
@
|
84
|
+
def initialize(flag, argument)
|
85
|
+
@flag = flag
|
86
|
+
@argument = argument
|
87
87
|
end
|
88
88
|
|
89
|
-
def
|
90
|
-
@
|
89
|
+
def to_a
|
90
|
+
[@flag, @argument]
|
91
91
|
end
|
92
92
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
93
|
+
# Factory method for initialising DataOptions based on class
|
94
|
+
def self.for(flag, argument)
|
95
|
+
if argument.is_a?(Hash)
|
96
|
+
DataOptionHash.new(flag, argument)
|
97
|
+
elsif argument.is_a?(Array)
|
98
|
+
DataOptionArray.new(flag, argument)
|
99
|
+
elsif argument.is_a?(String) && File.exist?(argument)
|
100
|
+
DataOptionFile.new(flag, argument)
|
101
|
+
else
|
102
|
+
DataOption.new(flag, argument)
|
99
103
|
end
|
100
104
|
end
|
101
105
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
+
attr_reader :flag
|
107
|
+
attr_reader :argument
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
# An option with a Hash argument
|
112
|
+
# Hash content is stored in a temporary file upon conversion
|
113
|
+
class DataOptionHash < DataOption
|
114
|
+
|
115
|
+
def as_tempfile(filename, data)
|
116
|
+
tempfile = Tempfile.new([filename, ".yml"])
|
117
|
+
tempfile.write(YAML.dump(data))
|
106
118
|
tempfile.close
|
107
119
|
tempfile.path.to_s
|
108
120
|
end
|
109
121
|
|
122
|
+
def to_a
|
123
|
+
[@flag, as_tempfile(@flag[2..-1], @argument)]
|
124
|
+
end
|
125
|
+
|
110
126
|
end
|
111
127
|
|
128
|
+
# An option with an Array argument
|
129
|
+
# Flag is repeated for every Array member upon conversion
|
130
|
+
class DataOptionArray < DataOption
|
131
|
+
|
132
|
+
def to_a
|
133
|
+
@argument.map { |a| [@flag, a] }.flatten
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
# An option with a File argument
|
139
|
+
class DataOptionFile < DataOption; end
|
140
|
+
|
112
141
|
end
|
113
142
|
|
114
143
|
end
|
data/lib/stackup/source.rb
CHANGED
data/lib/stackup/stack.rb
CHANGED
@@ -41,6 +41,7 @@ module Stackup
|
|
41
41
|
def on_event(event_handler = nil, &block)
|
42
42
|
event_handler ||= block
|
43
43
|
raise ArgumentError, "no event_handler provided" if event_handler.nil?
|
44
|
+
|
44
45
|
@event_handler = event_handler
|
45
46
|
end
|
46
47
|
|
@@ -324,6 +325,7 @@ module Stackup
|
|
324
325
|
if wait?
|
325
326
|
status = modify_stack_synchronously(&block)
|
326
327
|
raise StackUpdateError, failure_message unless target_status === status
|
328
|
+
|
327
329
|
status
|
328
330
|
else
|
329
331
|
modify_stack_asynchronously(&block)
|
@@ -344,6 +346,7 @@ module Stackup
|
|
344
346
|
status = self.status
|
345
347
|
logger.debug("stack_status=#{status}")
|
346
348
|
return status if status.nil? || status =~ /_(COMPLETE|FAILED)$/
|
349
|
+
|
347
350
|
sleep(wait_poll_interval)
|
348
351
|
end
|
349
352
|
end
|
data/lib/stackup/version.rb
CHANGED
data/lib/stackup/yaml.rb
CHANGED
data/spec/stackup/stack_spec.rb
CHANGED
@@ -280,6 +280,22 @@ describe Stackup::Stack do
|
|
280
280
|
|
281
281
|
end
|
282
282
|
|
283
|
+
context "with :capabilities as an Array" do
|
284
|
+
|
285
|
+
before do
|
286
|
+
options[:capabilities] = %w[CAPABILITY_IAM CAPABILITY_NAMED_IAM]
|
287
|
+
end
|
288
|
+
|
289
|
+
it "passes them through" do
|
290
|
+
expected_capabilities = %w[CAPABILITY_IAM CAPABILITY_NAMED_IAM]
|
291
|
+
create_or_update
|
292
|
+
expect(cf_client).to have_received(:create_stack) do |options|
|
293
|
+
expect(options[:capabilities]).to eq(expected_capabilities)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
end
|
298
|
+
|
283
299
|
end
|
284
300
|
|
285
301
|
describe "#change_set#create" do
|
@@ -360,6 +376,53 @@ describe Stackup::Stack do
|
|
360
376
|
|
361
377
|
end
|
362
378
|
|
379
|
+
context "with :capabilities as an Array" do
|
380
|
+
|
381
|
+
before do
|
382
|
+
options[:capabilities] = %w[CAPABILITY_IAM CAPABILITY_NAMED_IAM]
|
383
|
+
end
|
384
|
+
|
385
|
+
it "passes them through" do
|
386
|
+
expected_capabilities = %w[CAPABILITY_IAM CAPABILITY_NAMED_IAM]
|
387
|
+
create_change_set
|
388
|
+
expect(cf_client).to have_received(:create_change_set) do |options|
|
389
|
+
expect(options[:capabilities]).to eq(expected_capabilities)
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
end
|
394
|
+
|
395
|
+
end
|
396
|
+
|
397
|
+
describe "#change_set#execute" do
|
398
|
+
|
399
|
+
let(:change_set_name) { "create-it" }
|
400
|
+
|
401
|
+
let(:describe_stacks_responses) do
|
402
|
+
[
|
403
|
+
stack_description("CREATE_IN_PROGRESS"),
|
404
|
+
stack_description("CREATE_COMPLETE")
|
405
|
+
]
|
406
|
+
end
|
407
|
+
|
408
|
+
def execute_change_set
|
409
|
+
stack.change_set(change_set_name).execute
|
410
|
+
end
|
411
|
+
|
412
|
+
it "calls :execute_change_set" do
|
413
|
+
execute_change_set
|
414
|
+
expected_args = {
|
415
|
+
:stack_name => stack_name,
|
416
|
+
:change_set_name => change_set_name
|
417
|
+
}
|
418
|
+
expect(cf_client).to have_received(:execute_change_set)
|
419
|
+
.with(hash_including(expected_args))
|
420
|
+
end
|
421
|
+
|
422
|
+
it "returns status" do
|
423
|
+
expect(execute_change_set).to eq("CREATE_COMPLETE")
|
424
|
+
end
|
425
|
+
|
363
426
|
end
|
364
427
|
|
365
428
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stackup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Williams
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-
|
12
|
+
date: 2019-09-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: aws-sdk-cloudformation
|
@@ -134,8 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
134
|
- !ruby/object:Gem::Version
|
135
135
|
version: '0'
|
136
136
|
requirements: []
|
137
|
-
|
138
|
-
rubygems_version: 2.7.6
|
137
|
+
rubygems_version: 3.0.3
|
139
138
|
signing_key:
|
140
139
|
specification_version: 4
|
141
140
|
summary: Manage CloudFormation stacks
|