stackup 0.2.0 → 0.3.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/Gemfile.lock +2 -2
- data/README.md +17 -7
- data/bin/stackup +44 -13
- data/lib/stackup/error_handling.rb +32 -0
- data/lib/stackup/errors.rb +9 -6
- data/lib/stackup/stack.rb +118 -14
- data/lib/stackup/stack_watcher.rb +1 -1
- data/pkg/stackup-0.2.0.gem +0 -0
- data/spec/stackup/stack_spec.rb +54 -22
- data/stackup.gemspec +3 -3
- data/woollyams/parameters.json +6 -0
- data/woollyams/template.yml +31 -0
- metadata +8 -5
- data/lib/stackup/error_mapping_proxy.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ff49d4bc734ae1d359d0727319dfd73bafde86f9
|
4
|
+
data.tar.gz: 79b3705ee2af79a3a268e74b0a589ab2fbe8f107
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ea9f0efed3b79d32fc842a5a726114a7b5d70731626c355072ebc510e0ff016361e34db90f225ff3ec952cd229411a5d4dabd505901831393c825bc8753d0b7
|
7
|
+
data.tar.gz: d6a435c32e8203114d2f671ac56e66764d044da56b6e1f5c20227a6b80cfc6de2b511079f03538aed79b592efb866d6ddce843de1ac2a7f8b699aefa693895fd
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -2,11 +2,21 @@
|
|
2
2
|
|
3
3
|
[](https://travis-ci.org/realestate-com-au/stackup)
|
4
4
|
|
5
|
-
Stackup
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
Stackup provides a CLI and a simplified Ruby API for dealing with
|
6
|
+
AWS CloudFormation stacks.
|
7
|
+
|
8
|
+
## Why?
|
9
|
+
|
10
|
+
Stackup provides some advantages over using `awscli` or `aws-sdk` directly:
|
11
|
+
|
12
|
+
- It treats stack changes as synchronous, streaming stack events until the
|
13
|
+
stack reaches a stable state.
|
14
|
+
|
15
|
+
- A `Stack#up` facade for `create`/`update` frees you from having to know
|
16
|
+
whether your stack already exists or not.
|
17
|
+
|
18
|
+
- "No-op" operations - deleting a stack that doesn't exist, or updating
|
19
|
+
without a template change - are handled gracefully (i.e. without error).
|
10
20
|
|
11
21
|
## Installation
|
12
22
|
|
@@ -20,9 +30,9 @@ Most commands operate in the context of a named stack:
|
|
20
30
|
|
21
31
|
$ stackup STACK-NAME ...
|
22
32
|
|
23
|
-
Called
|
33
|
+
Called with `--list`, it will list stacks:
|
24
34
|
|
25
|
-
$ stackup
|
35
|
+
$ stackup --list
|
26
36
|
foo-bar-test
|
27
37
|
zzz-production
|
28
38
|
|
data/bin/stackup
CHANGED
@@ -10,15 +10,15 @@ require "yaml"
|
|
10
10
|
|
11
11
|
Clamp do
|
12
12
|
|
13
|
-
option "--debug", :flag, "enable debugging"
|
14
|
-
|
15
|
-
option ["-f", "--format"], "FORMAT", "output format", :default => "yaml"
|
16
|
-
|
17
13
|
option ["-L", "--list"], :flag, "list stacks" do
|
18
14
|
list_stacks
|
19
15
|
exit 0
|
20
16
|
end
|
21
17
|
|
18
|
+
option ["-Y", "--yaml"], :flag, "output data in YAML format"
|
19
|
+
|
20
|
+
option "--debug", :flag, "enable debugging"
|
21
|
+
|
22
22
|
parameter "NAME", "Name of stack", :attribute_name => :stack_name
|
23
23
|
|
24
24
|
def run(arguments)
|
@@ -38,11 +38,10 @@ Clamp do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def format_data(data)
|
41
|
-
|
42
|
-
when "json"
|
43
|
-
MultiJson.dump(data, :pretty => true)
|
44
|
-
when "yaml"
|
41
|
+
if yaml?
|
45
42
|
YAML.dump(data)
|
43
|
+
else
|
44
|
+
MultiJson.dump(data, :pretty => true)
|
46
45
|
end
|
47
46
|
end
|
48
47
|
|
@@ -89,8 +88,8 @@ Clamp do
|
|
89
88
|
|
90
89
|
def execute
|
91
90
|
options = {}
|
92
|
-
options[:
|
93
|
-
options[:
|
91
|
+
options[:template] = template
|
92
|
+
options[:parameters] = parameters if parameters
|
94
93
|
options[:disable_rollback] = disable_rollback?
|
95
94
|
report_change do
|
96
95
|
stack.create_or_update(options)
|
@@ -99,12 +98,20 @@ Clamp do
|
|
99
98
|
|
100
99
|
private
|
101
100
|
|
102
|
-
def
|
103
|
-
|
101
|
+
def load_data(file)
|
102
|
+
YAML.load_file(file)
|
104
103
|
rescue Errno::ENOENT
|
105
104
|
signal_error "no such file: #{file.inspect}"
|
106
105
|
end
|
107
106
|
|
107
|
+
def template
|
108
|
+
@template ||= load_data(template_file)
|
109
|
+
end
|
110
|
+
|
111
|
+
def parameters
|
112
|
+
@parameters ||= load_data(parameters_file) if parameters_file
|
113
|
+
end
|
114
|
+
|
108
115
|
end
|
109
116
|
|
110
117
|
subcommand ["down", "delete"], "Remove the stack." do
|
@@ -127,7 +134,23 @@ Clamp do
|
|
127
134
|
|
128
135
|
end
|
129
136
|
|
130
|
-
subcommand "
|
137
|
+
subcommand "template", "Display stack template." do
|
138
|
+
|
139
|
+
def execute
|
140
|
+
display_data(stack.template)
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
subcommand "parameters", "Display stack parameters." do
|
146
|
+
|
147
|
+
def execute
|
148
|
+
display_data(stack.parameters)
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
subcommand "outputs", "Display stack outputs." do
|
131
154
|
|
132
155
|
def execute
|
133
156
|
display_data(stack.outputs)
|
@@ -135,4 +158,12 @@ Clamp do
|
|
135
158
|
|
136
159
|
end
|
137
160
|
|
161
|
+
subcommand "resources", "Display stack resources." do
|
162
|
+
|
163
|
+
def execute
|
164
|
+
display_data(stack.resources)
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
138
169
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "stackup/errors"
|
2
|
+
|
3
|
+
module Stackup
|
4
|
+
|
5
|
+
# Handle +Aws::CloudFormation::Errors::ValidationError+.
|
6
|
+
#
|
7
|
+
module ErrorHandling
|
8
|
+
|
9
|
+
# Invoke an Aws::CloudFormation operation
|
10
|
+
#
|
11
|
+
# If a +ValidationError+ is raised, check the message; there's often
|
12
|
+
# useful information is hidden inside. If that's the case, convert it to
|
13
|
+
# an appropriate Stackup exception.
|
14
|
+
#
|
15
|
+
def handling_validation_error
|
16
|
+
yield
|
17
|
+
rescue Aws::CloudFormation::Errors::ValidationError => e
|
18
|
+
case e.message
|
19
|
+
when /Stack .* does not exist/
|
20
|
+
raise NoSuchStack, "no such stack"
|
21
|
+
when "No updates are to be performed."
|
22
|
+
raise NoUpdateRequired, "no updates are required"
|
23
|
+
when / can ?not be /
|
24
|
+
raise InvalidStateError, e.message
|
25
|
+
else
|
26
|
+
raise ValidationError, e.message
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/lib/stackup/errors.rb
CHANGED
@@ -3,16 +3,19 @@ module Stackup
|
|
3
3
|
# Base Stackup Exception class
|
4
4
|
class ServiceError < StandardError; end
|
5
5
|
|
6
|
-
# Raised when
|
7
|
-
class
|
6
|
+
# Raised when something else dodgy happened
|
7
|
+
class ValidationError < ServiceError; end
|
8
8
|
|
9
|
-
# Raised
|
10
|
-
class
|
9
|
+
# Raised when the specified stack does not exist
|
10
|
+
class NoSuchStack < ValidationError; end
|
11
11
|
|
12
12
|
# Raised if we can't perform that operation now
|
13
|
-
class InvalidStateError <
|
13
|
+
class InvalidStateError < ValidationError; end
|
14
14
|
|
15
15
|
# Raised when a stack is already up-to-date
|
16
|
-
class NoUpdateRequired <
|
16
|
+
class NoUpdateRequired < ValidationError; end
|
17
|
+
|
18
|
+
# Raised to indicate a problem updating a stack
|
19
|
+
class StackUpdateError < ServiceError; end
|
17
20
|
|
18
21
|
end
|
data/lib/stackup/stack.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require "aws-sdk-resources"
|
2
2
|
require "logger"
|
3
|
-
require "
|
3
|
+
require "multi_json"
|
4
|
+
require "stackup/error_handling"
|
4
5
|
require "stackup/stack_watcher"
|
5
6
|
|
6
7
|
module Stackup
|
@@ -20,7 +21,7 @@ module Stackup
|
|
20
21
|
|
21
22
|
attr_reader :name, :cf_client, :watcher
|
22
23
|
|
23
|
-
# Register a handler for reporting of stack events
|
24
|
+
# Register a handler for reporting of stack events.
|
24
25
|
# @param [Proc] event_handler
|
25
26
|
#
|
26
27
|
def on_event(event_handler = nil, &block)
|
@@ -29,11 +30,15 @@ module Stackup
|
|
29
30
|
@event_handler = event_handler
|
30
31
|
end
|
31
32
|
|
33
|
+
include ErrorHandling
|
34
|
+
|
32
35
|
# @return [String] the current stack status
|
33
36
|
# @raise [Stackup::NoSuchStack] if the stack doesn't exist
|
34
37
|
#
|
35
38
|
def status
|
36
|
-
|
39
|
+
handling_validation_error do
|
40
|
+
cf_stack.stack_status
|
41
|
+
end
|
37
42
|
end
|
38
43
|
|
39
44
|
# @return [boolean] true iff the stack exists
|
@@ -51,11 +56,48 @@ module Stackup
|
|
51
56
|
# accepts a superset of the options supported by
|
52
57
|
# +Aws::CloudFormation::Stack#update+
|
53
58
|
# (see http://docs.aws.amazon.com/sdkforruby/api/Aws/CloudFormation/Stack.html#update-instance_method)
|
54
|
-
# @
|
59
|
+
# @option options [Array<String>] :capabilities (CAPABILITY_IAM)
|
60
|
+
# list of capabilities required for stack template
|
61
|
+
# @option options [boolean] :disable_rollback (false)
|
62
|
+
# if true, disable rollback if stack creation fails
|
63
|
+
# @option options [String] :notification_arns
|
64
|
+
# ARNs for the Amazon SNS topics associated with this stack
|
65
|
+
# @option options [String] :on_failure (DO_NOTHING)
|
66
|
+
# if stack creation fails: DO_NOTHING, ROLLBACK, or DELETE
|
67
|
+
# @option options [Hash, Array<Hash>] :parameters
|
68
|
+
# stack parameters, either as a Hash, or as an Array of
|
69
|
+
# +Aws::CloudFormation::Types::Parameter+ structures
|
70
|
+
# @option options [Array<String>] :resource_types
|
71
|
+
# resource types that you have permissions to work with
|
72
|
+
# @option options [Hash] :template
|
73
|
+
# stack template, as Ruby data
|
74
|
+
# @option options [String] :stack_policy_body
|
75
|
+
# stack policy, as JSON
|
76
|
+
# @option options [String] :stack_policy_url
|
77
|
+
# location of stack policy
|
78
|
+
# @option options [String] :stack_policy_during_update_body
|
79
|
+
# temporary stack policy, as JSON
|
80
|
+
# @option options [String] :stack_policy_during_update_url
|
81
|
+
# location of temporary stack policy
|
82
|
+
# @option options [String] :template_body
|
83
|
+
# stack template, as JSON
|
84
|
+
# @option options [String] :template_url
|
85
|
+
# location of stack template
|
86
|
+
# @option options [Integer] :timeout_in_minutes
|
87
|
+
# stack creation timeout
|
88
|
+
# @option options [boolean] :use_previous_template
|
89
|
+
# if true, reuse the existing template
|
90
|
+
# @return [Symbol] +:created+ or +:updated+ if successful
|
55
91
|
# @raise [Stackup::StackUpdateError] if operation fails
|
56
92
|
#
|
57
93
|
def create_or_update(options)
|
58
94
|
options = options.dup
|
95
|
+
if (template_data = options.delete(:template))
|
96
|
+
options[:template_body] = MultiJson.dump(template_data)
|
97
|
+
end
|
98
|
+
if (parameters = options[:parameters])
|
99
|
+
options[:parameters] = normalise_parameters(parameters)
|
100
|
+
end
|
59
101
|
options[:capabilities] ||= ["CAPABILITY_IAM"]
|
60
102
|
delete if ALMOST_DEAD_STATUSES.include?(status)
|
61
103
|
update(options)
|
@@ -71,12 +113,14 @@ module Stackup
|
|
71
113
|
#
|
72
114
|
# @param [String] template template JSON
|
73
115
|
# @param [Array<Hash>] parameters template parameters
|
74
|
-
# @return [Symbol]
|
116
|
+
# @return [Symbol] +:deleted+ if successful
|
75
117
|
# @raise [Stackup::StackUpdateError] if operation fails
|
76
118
|
#
|
77
119
|
def delete
|
78
120
|
begin
|
79
|
-
@stack_id =
|
121
|
+
@stack_id = handling_validation_error do
|
122
|
+
cf_stack.stack_id
|
123
|
+
end
|
80
124
|
rescue NoSuchStack
|
81
125
|
return nil
|
82
126
|
end
|
@@ -93,7 +137,7 @@ module Stackup
|
|
93
137
|
|
94
138
|
# Cancel update in-progress.
|
95
139
|
#
|
96
|
-
# @return [Symbol]
|
140
|
+
# @return [Symbol] +:update_cancelled+ if successful
|
97
141
|
# @raise [Stackup::StackUpdateError] if operation fails
|
98
142
|
#
|
99
143
|
def cancel_update
|
@@ -106,16 +150,60 @@ module Stackup
|
|
106
150
|
nil
|
107
151
|
end
|
108
152
|
|
153
|
+
# Get the current template.
|
154
|
+
#
|
155
|
+
# @return [Hash] current stack template, as Ruby data
|
156
|
+
# @raise [Stackup::NoSuchStack] if the stack doesn't exist
|
157
|
+
#
|
158
|
+
def template
|
159
|
+
handling_validation_error do
|
160
|
+
template_json = cf_client.get_template(:stack_name => name).template_body
|
161
|
+
MultiJson.load(template_json)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Get the current parameters.
|
166
|
+
#
|
167
|
+
# @return [Hash] current stack parameters
|
168
|
+
# @raise [Stackup::NoSuchStack] if the stack doesn't exist
|
169
|
+
#
|
170
|
+
def parameters
|
171
|
+
handling_validation_error do
|
172
|
+
{}.tap do |h|
|
173
|
+
cf_stack.parameters.each do |p|
|
174
|
+
h[p.parameter_key] = p.parameter_value
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Get stack outputs.
|
181
|
+
#
|
182
|
+
# @return [Hash<String, String>] stack outputs
|
183
|
+
# @raise [Stackup::NoSuchStack] if the stack doesn't exist
|
184
|
+
#
|
185
|
+
def outputs
|
186
|
+
handling_validation_error do
|
187
|
+
{}.tap do |h|
|
188
|
+
cf_stack.outputs.each do |o|
|
189
|
+
h[o.output_key] = o.output_value
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
109
195
|
# Get stack outputs.
|
110
196
|
#
|
111
197
|
# @return [Hash<String, String>]
|
112
198
|
# mapping of logical resource-name to physical resource-name
|
113
199
|
# @raise [Stackup::NoSuchStack] if the stack doesn't exist
|
114
200
|
#
|
115
|
-
def
|
116
|
-
|
117
|
-
|
118
|
-
|
201
|
+
def resources
|
202
|
+
handling_validation_error do
|
203
|
+
{}.tap do |h|
|
204
|
+
cf_stack.resource_summaries.each do |r|
|
205
|
+
h[r.logical_resource_id] = r.physical_resource_id
|
206
|
+
end
|
119
207
|
end
|
120
208
|
end
|
121
209
|
end
|
@@ -124,8 +212,10 @@ module Stackup
|
|
124
212
|
|
125
213
|
def create(options)
|
126
214
|
options[:stack_name] = name
|
215
|
+
options.delete(:stack_policy_during_update_body)
|
216
|
+
options.delete(:stack_policy_during_update_url)
|
127
217
|
status = modify_stack do
|
128
|
-
|
218
|
+
cf.create_stack(options)
|
129
219
|
end
|
130
220
|
fail StackUpdateError, "stack creation failed" unless status == "CREATE_COMPLETE"
|
131
221
|
:created
|
@@ -133,6 +223,8 @@ module Stackup
|
|
133
223
|
|
134
224
|
def update(options)
|
135
225
|
options.delete(:disable_rollback)
|
226
|
+
options.delete(:on_failure)
|
227
|
+
options.delete(:timeout_in_minutes)
|
136
228
|
status = modify_stack do
|
137
229
|
cf_stack.update(options)
|
138
230
|
end
|
@@ -153,7 +245,7 @@ module Stackup
|
|
153
245
|
|
154
246
|
def cf_stack
|
155
247
|
id_or_name = @stack_id || name
|
156
|
-
|
248
|
+
cf.stack(id_or_name)
|
157
249
|
end
|
158
250
|
|
159
251
|
def event_handler
|
@@ -170,7 +262,9 @@ module Stackup
|
|
170
262
|
def modify_stack
|
171
263
|
watcher = Stackup::StackWatcher.new(cf_stack)
|
172
264
|
watcher.zero
|
173
|
-
|
265
|
+
handling_validation_error do
|
266
|
+
yield
|
267
|
+
end
|
174
268
|
loop do
|
175
269
|
watcher.each_new_event(&event_handler)
|
176
270
|
status = self.status
|
@@ -180,6 +274,16 @@ module Stackup
|
|
180
274
|
end
|
181
275
|
end
|
182
276
|
|
277
|
+
def normalise_parameters(arg)
|
278
|
+
return arg unless arg.is_a?(Hash)
|
279
|
+
arg.map do |key, value|
|
280
|
+
{
|
281
|
+
:parameter_key => key,
|
282
|
+
:parameter_value => value
|
283
|
+
}
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
183
287
|
end
|
184
288
|
|
185
289
|
end
|
@@ -18,6 +18,7 @@ module Stackup
|
|
18
18
|
# Yield all events since the last call
|
19
19
|
#
|
20
20
|
def each_new_event
|
21
|
+
# rubocop:disable Lint/HandleExceptions
|
21
22
|
buffer = []
|
22
23
|
stack.events.each do |event|
|
23
24
|
break if @processed_event_ids.include?(event.event_id)
|
@@ -28,7 +29,6 @@ module Stackup
|
|
28
29
|
@processed_event_ids.add(event.event_id)
|
29
30
|
end
|
30
31
|
rescue Aws::CloudFormation::Errors::ValidationError
|
31
|
-
# okay
|
32
32
|
end
|
33
33
|
|
34
34
|
# Consume all new events
|
Binary file
|
data/spec/stackup/stack_spec.rb
CHANGED
@@ -76,18 +76,24 @@ describe Stackup::Stack do
|
|
76
76
|
|
77
77
|
let(:template) { "stack template" }
|
78
78
|
|
79
|
+
let(:options) do
|
80
|
+
{ :template_body => template }
|
81
|
+
end
|
82
|
+
|
79
83
|
def create_or_update
|
80
|
-
stack.create_or_update(
|
84
|
+
stack.create_or_update(options)
|
81
85
|
end
|
82
86
|
|
83
|
-
|
87
|
+
let(:describe_stacks_responses) do
|
88
|
+
super() + [
|
89
|
+
stack_description("CREATE_IN_PROGRESS"),
|
90
|
+
stack_description(final_status)
|
91
|
+
]
|
92
|
+
end
|
84
93
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
stack_description("CREATE_COMPLETE")
|
89
|
-
]
|
90
|
-
end
|
94
|
+
let(:final_status) { "CREATE_COMPLETE" }
|
95
|
+
|
96
|
+
context "successful" do
|
91
97
|
|
92
98
|
it "calls :create_stack" do
|
93
99
|
expected_args = {
|
@@ -107,12 +113,7 @@ describe Stackup::Stack do
|
|
107
113
|
|
108
114
|
context "unsuccessful" do
|
109
115
|
|
110
|
-
let(:
|
111
|
-
super() + [
|
112
|
-
stack_description("CREATE_IN_PROGRESS"),
|
113
|
-
stack_description("CREATE_FAILED")
|
114
|
-
]
|
115
|
-
end
|
116
|
+
let(:final_status) { "CREATE_FAILED" }
|
116
117
|
|
117
118
|
it "raises a StackUpdateError" do
|
118
119
|
expect { create_or_update }
|
@@ -121,6 +122,41 @@ describe Stackup::Stack do
|
|
121
122
|
|
122
123
|
end
|
123
124
|
|
125
|
+
context "with :template as data" do
|
126
|
+
|
127
|
+
let(:options) do
|
128
|
+
{ :template => { "foo" => "bar" } }
|
129
|
+
end
|
130
|
+
|
131
|
+
it "converts the template to JSON" do
|
132
|
+
create_or_update
|
133
|
+
expect(cf_client).to have_received(:create_stack)
|
134
|
+
.with(hash_including(:template_body))
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
context "with :parameters as Hash" do
|
140
|
+
|
141
|
+
before do
|
142
|
+
options[:parameters] = { "foo" => "bar" }
|
143
|
+
end
|
144
|
+
|
145
|
+
it "converts them to an Array" do
|
146
|
+
expected_parameters = [
|
147
|
+
{
|
148
|
+
:parameter_key => "foo",
|
149
|
+
:parameter_value => "bar"
|
150
|
+
}
|
151
|
+
]
|
152
|
+
create_or_update
|
153
|
+
expect(cf_client).to have_received(:create_stack) do |options|
|
154
|
+
expect(options[:parameters]).to eq(expected_parameters)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
124
160
|
end
|
125
161
|
|
126
162
|
end
|
@@ -202,13 +238,11 @@ describe Stackup::Stack do
|
|
202
238
|
let(:describe_stacks_responses) do
|
203
239
|
super() + [
|
204
240
|
stack_description("UPDATE_IN_PROGRESS"),
|
205
|
-
|
241
|
+
stack_description(final_status)
|
206
242
|
]
|
207
243
|
end
|
208
244
|
|
209
|
-
let(:
|
210
|
-
stack_description("UPDATE_COMPLETE")
|
211
|
-
end
|
245
|
+
let(:final_status) { "UPDATE_COMPLETE" }
|
212
246
|
|
213
247
|
it "calls :update_stack" do
|
214
248
|
expected_args = {
|
@@ -245,9 +279,7 @@ describe Stackup::Stack do
|
|
245
279
|
|
246
280
|
context "unsuccessful" do
|
247
281
|
|
248
|
-
let(:
|
249
|
-
stack_description("UPDATE_ROLLBACK_COMPLETE")
|
250
|
-
end
|
282
|
+
let(:final_status) { "UPDATE_ROLLBACK_COMPLETE" }
|
251
283
|
|
252
284
|
it "raises a StackUpdateError" do
|
253
285
|
expect { create_or_update }.to raise_error(Stackup::StackUpdateError)
|
@@ -300,7 +332,7 @@ describe Stackup::Stack do
|
|
300
332
|
)
|
301
333
|
end
|
302
334
|
|
303
|
-
it "calls :delete_stack, then :create_stack
|
335
|
+
it "calls :delete_stack, then :create_stack" do
|
304
336
|
create_or_update
|
305
337
|
expect(cf_client).to have_received(:delete_stack)
|
306
338
|
expect(cf_client).to have_received(:create_stack)
|
data/stackup.gemspec
CHANGED
@@ -4,9 +4,9 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
Gem::Specification.new do |spec|
|
5
5
|
|
6
6
|
spec.name = "stackup"
|
7
|
-
spec.version = "0.
|
8
|
-
spec.authors = ["
|
9
|
-
spec.email = ["
|
7
|
+
spec.version = "0.3.0"
|
8
|
+
spec.authors = ["Mike Williams", "Arvind Kunday"]
|
9
|
+
spec.email = ["mike.williams@rea-group.com", "arvind.kunday@rea-group.com"]
|
10
10
|
spec.summary = "Manage CloudFormation stacks"
|
11
11
|
spec.homepage = "https://github.com/realestate-com-au/stackup"
|
12
12
|
spec.license = "MIT"
|
@@ -0,0 +1,31 @@
|
|
1
|
+
---
|
2
|
+
AWSTemplateFormatVersion: '2010-09-09'
|
3
|
+
Description: 'AWS CloudFormation Sample Template S3_Website_Bucket_With_Retain_On_Delete:
|
4
|
+
Sample template showing how to create a publicly accessible S3 bucket configured
|
5
|
+
for website access with a deletion policy of retail on delete. **WARNING** This
|
6
|
+
template creates an S3 bucket that will NOT be deleted when the stack is deleted.
|
7
|
+
You will be billed for the AWS resources used if you create a stack from this template.'
|
8
|
+
Resources:
|
9
|
+
S3Bucket:
|
10
|
+
Type: AWS::S3::Bucket
|
11
|
+
Properties:
|
12
|
+
AccessControl: PublicRead
|
13
|
+
WebsiteConfiguration:
|
14
|
+
IndexDocument: index.html
|
15
|
+
ErrorDocument: error.html
|
16
|
+
Outputs:
|
17
|
+
WebsiteURL:
|
18
|
+
Value:
|
19
|
+
Fn::GetAtt:
|
20
|
+
- S3Bucket
|
21
|
+
- WebsiteURL
|
22
|
+
Description: URL for website hosted on S3
|
23
|
+
S3BucketSecureURL:
|
24
|
+
Value:
|
25
|
+
Fn::Join:
|
26
|
+
- ''
|
27
|
+
- - https://
|
28
|
+
- Fn::GetAtt:
|
29
|
+
- S3Bucket
|
30
|
+
- DomainName
|
31
|
+
Description: Name of S3 bucket to hold website content
|
metadata
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stackup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Arvind Kunday
|
8
7
|
- Mike Williams
|
8
|
+
- Arvind Kunday
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
@@ -69,8 +69,8 @@ dependencies:
|
|
69
69
|
version: '0'
|
70
70
|
description:
|
71
71
|
email:
|
72
|
-
- arvind.kunday@rea-group.com
|
73
72
|
- mike.williams@rea-group.com
|
73
|
+
- arvind.kunday@rea-group.com
|
74
74
|
executables:
|
75
75
|
- stackup
|
76
76
|
extensions: []
|
@@ -106,16 +106,19 @@ files:
|
|
106
106
|
- doc/method_list.html
|
107
107
|
- doc/top-level-namespace.html
|
108
108
|
- lib/stackup.rb
|
109
|
-
- lib/stackup/
|
109
|
+
- lib/stackup/error_handling.rb
|
110
110
|
- lib/stackup/errors.rb
|
111
111
|
- lib/stackup/service.rb
|
112
112
|
- lib/stackup/stack.rb
|
113
113
|
- lib/stackup/stack_watcher.rb
|
114
|
+
- pkg/stackup-0.2.0.gem
|
114
115
|
- spec/spec_helper.rb
|
115
116
|
- spec/stackup/stack_spec.rb
|
116
117
|
- spec/stackup/stack_watcher_spec.rb
|
117
118
|
- stackup.gemspec
|
119
|
+
- woollyams/parameters.json
|
118
120
|
- woollyams/template.json
|
121
|
+
- woollyams/template.yml
|
119
122
|
homepage: https://github.com/realestate-com-au/stackup
|
120
123
|
licenses:
|
121
124
|
- MIT
|
@@ -136,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
136
139
|
version: '0'
|
137
140
|
requirements: []
|
138
141
|
rubyforge_project:
|
139
|
-
rubygems_version: 2.4.
|
142
|
+
rubygems_version: 2.4.5.1
|
140
143
|
signing_key:
|
141
144
|
specification_version: 4
|
142
145
|
summary: Manage CloudFormation stacks
|
@@ -1,37 +0,0 @@
|
|
1
|
-
require "stackup/errors"
|
2
|
-
|
3
|
-
module Stackup
|
4
|
-
|
5
|
-
# An error-mapping proxy for Aws::CloudFormation models.
|
6
|
-
#
|
7
|
-
# It exists to convert certain types of `ValidationError`, where useful
|
8
|
-
# information is hidden inside the "message", to Stackup exceptions.
|
9
|
-
#
|
10
|
-
class ErrorMappingProxy
|
11
|
-
|
12
|
-
def initialize(delegate)
|
13
|
-
@delegate = delegate
|
14
|
-
end
|
15
|
-
|
16
|
-
def method_missing(*args)
|
17
|
-
@delegate.send(*args)
|
18
|
-
rescue Aws::CloudFormation::Errors::ValidationError => e
|
19
|
-
case e.message
|
20
|
-
when "No updates are to be performed."
|
21
|
-
raise NoUpdateRequired, "no updates are required"
|
22
|
-
when /Stack .* does not exist$/
|
23
|
-
raise NoSuchStack, "no such stack"
|
24
|
-
when / can ?not be /
|
25
|
-
raise InvalidStateError, e.message
|
26
|
-
else
|
27
|
-
raise e
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def respond_to?(method)
|
32
|
-
@delegate.respond_to?(method)
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|