stackup 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://travis-ci.org/realestate-com-au/stackup.svg?branch=master)](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
|