stackup 0.1.0 → 0.2.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 +12 -12
- data/README.md +38 -15
- data/bin/stackup +76 -34
- data/doc/Stackup/ErrorMappingProxy.html +378 -0
- data/doc/Stackup/InvalidStateError.html +142 -0
- data/doc/Stackup/NoSuchStack.html +142 -0
- data/doc/Stackup/NoUpdateRequired.html +134 -0
- data/doc/Stackup/Service.html +399 -0
- data/doc/Stackup/ServiceError.html +138 -0
- data/doc/Stackup/Stack.html +1269 -0
- data/doc/Stackup/StackUpdateError.html +142 -0
- data/doc/Stackup/StackWatcher.html +455 -0
- data/doc/Stackup.html +202 -0
- data/doc/_index.html +199 -0
- data/doc/class_list.html +58 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +57 -0
- data/doc/css/style.css +339 -0
- data/doc/file.README.html +143 -0
- data/doc/file_list.html +60 -0
- data/doc/frames.html +26 -0
- data/doc/index.html +143 -0
- data/doc/js/app.js +219 -0
- data/doc/js/full_list.js +181 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +195 -0
- data/doc/top-level-namespace.html +194 -0
- data/lib/stackup/error_mapping_proxy.rb +37 -0
- data/lib/stackup/errors.rb +9 -6
- data/lib/stackup/service.rb +39 -0
- data/lib/stackup/stack.rb +52 -59
- data/lib/stackup/stack_watcher.rb +16 -10
- data/lib/stackup.rb +26 -0
- data/spec/spec_helper.rb +15 -1
- data/spec/stackup/stack_spec.rb +114 -48
- data/spec/stackup/stack_watcher_spec.rb +20 -15
- data/stackup.gemspec +1 -1
- data/woollyams/template.json +44 -0
- metadata +29 -6
- data/pkg/stackup-0.0.1.gem +0 -0
- data/pkg/stackup-0.0.8.gem +0 -0
- data/pkg/stackup-0.0.9.gem +0 -0
- data/woollyams/sample-template.json +0 -79
@@ -0,0 +1,37 @@
|
|
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
|
data/lib/stackup/errors.rb
CHANGED
@@ -1,15 +1,18 @@
|
|
1
1
|
module Stackup
|
2
2
|
|
3
3
|
# Base Stackup Exception class
|
4
|
-
class ServiceError < StandardError
|
5
|
-
end
|
4
|
+
class ServiceError < StandardError; end
|
6
5
|
|
7
6
|
# Raised when the specified stack does not exist
|
8
|
-
class NoSuchStack < ServiceError
|
9
|
-
end
|
7
|
+
class NoSuchStack < ServiceError; end
|
10
8
|
|
11
9
|
# Raised to indicate a problem updating a stack
|
12
|
-
class StackUpdateError < ServiceError
|
13
|
-
|
10
|
+
class StackUpdateError < ServiceError; end
|
11
|
+
|
12
|
+
# Raised if we can't perform that operation now
|
13
|
+
class InvalidStateError < ServiceError; end
|
14
|
+
|
15
|
+
# Raised when a stack is already up-to-date
|
16
|
+
class NoUpdateRequired < StandardError; end
|
14
17
|
|
15
18
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "aws-sdk-core"
|
2
|
+
require "stackup/stack"
|
3
|
+
|
4
|
+
module Stackup
|
5
|
+
|
6
|
+
# A handle to CloudFormation.
|
7
|
+
#
|
8
|
+
class Service
|
9
|
+
|
10
|
+
def initialize(cf_client = {})
|
11
|
+
cf_client = Aws::CloudFormation::Client.new(cf_client) if cf_client.is_a?(Hash)
|
12
|
+
@cf_client = cf_client
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Stackup::Stack] the named stack
|
16
|
+
#
|
17
|
+
def stack(name, options = {})
|
18
|
+
Stack.new(name, cf_client, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Enumeration<String>] names of existing stacks
|
22
|
+
#
|
23
|
+
def stack_names
|
24
|
+
Enumerator.new do |y|
|
25
|
+
cf_client.describe_stacks.each do |response|
|
26
|
+
response.stacks.each do |stack|
|
27
|
+
y << stack.stack_name
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_reader :cf_client
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
data/lib/stackup/stack.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require "aws-sdk-resources"
|
2
2
|
require "logger"
|
3
|
-
require "stackup/
|
3
|
+
require "stackup/error_mapping_proxy"
|
4
4
|
require "stackup/stack_watcher"
|
5
5
|
|
6
6
|
module Stackup
|
@@ -13,7 +13,6 @@ module Stackup
|
|
13
13
|
client = Aws::CloudFormation::Client.new(client) if client.is_a?(Hash)
|
14
14
|
@name = name
|
15
15
|
@cf_client = client
|
16
|
-
@watcher = Stackup::StackWatcher.new(cf_stack)
|
17
16
|
options.each do |key, value|
|
18
17
|
public_send("#{key}=", value)
|
19
18
|
end
|
@@ -35,8 +34,6 @@ module Stackup
|
|
35
34
|
#
|
36
35
|
def status
|
37
36
|
cf_stack.stack_status
|
38
|
-
rescue Aws::CloudFormation::Errors::ValidationError => e
|
39
|
-
handle_validation_error(e)
|
40
37
|
end
|
41
38
|
|
42
39
|
# @return [boolean] true iff the stack exists
|
@@ -50,16 +47,20 @@ module Stackup
|
|
50
47
|
|
51
48
|
# Create or update the stack.
|
52
49
|
#
|
53
|
-
# @param [
|
54
|
-
#
|
50
|
+
# @param [Hash] options create/update options
|
51
|
+
# accepts a superset of the options supported by
|
52
|
+
# +Aws::CloudFormation::Stack#update+
|
53
|
+
# (see http://docs.aws.amazon.com/sdkforruby/api/Aws/CloudFormation/Stack.html#update-instance_method)
|
55
54
|
# @return [Symbol] `:created` or `:updated` if successful
|
56
55
|
# @raise [Stackup::StackUpdateError] if operation fails
|
57
56
|
#
|
58
|
-
def create_or_update(
|
57
|
+
def create_or_update(options)
|
58
|
+
options = options.dup
|
59
|
+
options[:capabilities] ||= ["CAPABILITY_IAM"]
|
59
60
|
delete if ALMOST_DEAD_STATUSES.include?(status)
|
60
|
-
update(
|
61
|
+
update(options)
|
61
62
|
rescue NoSuchStack
|
62
|
-
create(
|
63
|
+
create(options)
|
63
64
|
end
|
64
65
|
|
65
66
|
alias_method :up, :create_or_update
|
@@ -74,17 +75,37 @@ module Stackup
|
|
74
75
|
# @raise [Stackup::StackUpdateError] if operation fails
|
75
76
|
#
|
76
77
|
def delete
|
77
|
-
|
78
|
+
begin
|
79
|
+
@stack_id = cf_stack.stack_id
|
80
|
+
rescue NoSuchStack
|
81
|
+
return nil
|
82
|
+
end
|
78
83
|
status = modify_stack do
|
79
|
-
|
84
|
+
cf_stack.delete
|
80
85
|
end
|
81
|
-
fail StackUpdateError, "stack delete failed" unless status
|
82
|
-
rescue NoSuchStack
|
86
|
+
fail StackUpdateError, "stack delete failed" unless status == "DELETE_COMPLETE"
|
83
87
|
:deleted
|
88
|
+
ensure
|
89
|
+
@stack_id = nil
|
84
90
|
end
|
85
91
|
|
86
92
|
alias_method :down, :delete
|
87
93
|
|
94
|
+
# Cancel update in-progress.
|
95
|
+
#
|
96
|
+
# @return [Symbol] `:update_cancelled` if successful
|
97
|
+
# @raise [Stackup::StackUpdateError] if operation fails
|
98
|
+
#
|
99
|
+
def cancel_update
|
100
|
+
status = modify_stack do
|
101
|
+
cf_stack.cancel_update
|
102
|
+
end
|
103
|
+
fail StackUpdateError, "update cancel failed" unless status =~ /_COMPLETE$/
|
104
|
+
:update_cancelled
|
105
|
+
rescue InvalidStateError
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
|
88
109
|
# Get stack outputs.
|
89
110
|
#
|
90
111
|
# @return [Hash<String, String>]
|
@@ -97,28 +118,23 @@ module Stackup
|
|
97
118
|
h[output.output_key] = output.output_value
|
98
119
|
end
|
99
120
|
end
|
100
|
-
rescue Aws::CloudFormation::Errors::ValidationError => e
|
101
|
-
handle_validation_error(e)
|
102
121
|
end
|
103
122
|
|
104
123
|
private
|
105
124
|
|
106
|
-
def create(
|
125
|
+
def create(options)
|
126
|
+
options[:stack_name] = name
|
107
127
|
status = modify_stack do
|
108
|
-
|
109
|
-
:stack_name => name,
|
110
|
-
:template_body => template,
|
111
|
-
:capabilities => ["CAPABILITY_IAM"],
|
112
|
-
:parameters => parameters
|
113
|
-
)
|
128
|
+
ErrorMappingProxy.new(cf).create_stack(options)
|
114
129
|
end
|
115
130
|
fail StackUpdateError, "stack creation failed" unless status == "CREATE_COMPLETE"
|
116
131
|
:created
|
117
132
|
end
|
118
133
|
|
119
|
-
def update(
|
134
|
+
def update(options)
|
135
|
+
options.delete(:disable_rollback)
|
120
136
|
status = modify_stack do
|
121
|
-
|
137
|
+
cf_stack.update(options)
|
122
138
|
end
|
123
139
|
fail StackUpdateError, "stack update failed" unless status == "UPDATE_COMPLETE"
|
124
140
|
:updated
|
@@ -127,11 +143,17 @@ module Stackup
|
|
127
143
|
end
|
128
144
|
|
129
145
|
def logger
|
130
|
-
@logger ||=
|
146
|
+
@logger ||= cf_client.config[:logger]
|
147
|
+
@logger ||= Logger.new($stdout).tap { |l| l.level = Logger::INFO }
|
148
|
+
end
|
149
|
+
|
150
|
+
def cf
|
151
|
+
Aws::CloudFormation::Resource.new(:client => cf_client)
|
131
152
|
end
|
132
153
|
|
133
154
|
def cf_stack
|
134
|
-
|
155
|
+
id_or_name = @stack_id || name
|
156
|
+
ErrorMappingProxy.new(cf.stack(id_or_name))
|
135
157
|
end
|
136
158
|
|
137
159
|
def event_handler
|
@@ -146,47 +168,18 @@ module Stackup
|
|
146
168
|
# @return the final stack status
|
147
169
|
#
|
148
170
|
def modify_stack
|
171
|
+
watcher = Stackup::StackWatcher.new(cf_stack)
|
149
172
|
watcher.zero
|
150
173
|
yield
|
151
|
-
wait_until_stable
|
152
|
-
rescue Aws::CloudFormation::Errors::ValidationError => e
|
153
|
-
handle_validation_error(e)
|
154
|
-
end
|
155
|
-
|
156
|
-
# Wait (displaying stack events) until the stack reaches a stable state.
|
157
|
-
#
|
158
|
-
# @return the final stack status
|
159
|
-
#
|
160
|
-
def wait_until_stable
|
161
174
|
loop do
|
162
|
-
|
163
|
-
|
175
|
+
watcher.each_new_event(&event_handler)
|
176
|
+
status = self.status
|
177
|
+
logger.debug("stack_status=#{status}")
|
164
178
|
return status if status.nil? || status =~ /_(COMPLETE|FAILED)$/
|
165
179
|
sleep(5)
|
166
180
|
end
|
167
181
|
end
|
168
182
|
|
169
|
-
def report_new_events
|
170
|
-
watcher.new_events.each do |e|
|
171
|
-
event_handler.call(e)
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
def handle_validation_error(e)
|
176
|
-
case e.message
|
177
|
-
when "No updates are to be performed."
|
178
|
-
fail NoUpdateRequired, "no updates are required"
|
179
|
-
when / does not exist$/
|
180
|
-
fail NoSuchStack, "no such stack: #{name}"
|
181
|
-
else
|
182
|
-
raise e
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
# Raised when a stack is already up-to-date
|
187
|
-
class NoUpdateRequired < StandardError
|
188
|
-
end
|
189
|
-
|
190
183
|
end
|
191
184
|
|
192
185
|
end
|
@@ -2,6 +2,10 @@ require "aws-sdk-core"
|
|
2
2
|
|
3
3
|
module Stackup
|
4
4
|
|
5
|
+
# A stack event observer.
|
6
|
+
#
|
7
|
+
# Keeps track of previously processed events, and yields the new ones.
|
8
|
+
#
|
5
9
|
class StackWatcher
|
6
10
|
|
7
11
|
def initialize(stack)
|
@@ -13,22 +17,24 @@ module Stackup
|
|
13
17
|
|
14
18
|
# Yield all events since the last call
|
15
19
|
#
|
16
|
-
def
|
17
|
-
[]
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
@processed_event_ids.add(event.event_id)
|
22
|
-
end
|
20
|
+
def each_new_event
|
21
|
+
buffer = []
|
22
|
+
stack.events.each do |event|
|
23
|
+
break if @processed_event_ids.include?(event.event_id)
|
24
|
+
buffer.unshift(event)
|
23
25
|
end
|
24
|
-
|
25
|
-
|
26
|
+
buffer.each do |event|
|
27
|
+
yield event if block_given?
|
28
|
+
@processed_event_ids.add(event.event_id)
|
29
|
+
end
|
30
|
+
rescue Aws::CloudFormation::Errors::ValidationError
|
31
|
+
# okay
|
26
32
|
end
|
27
33
|
|
28
34
|
# Consume all new events
|
29
35
|
#
|
30
36
|
def zero
|
31
|
-
|
37
|
+
each_new_event
|
32
38
|
nil
|
33
39
|
end
|
34
40
|
|
data/lib/stackup.rb
CHANGED
@@ -1 +1,27 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require "stackup/service"
|
1
3
|
require "stackup/stack"
|
4
|
+
|
5
|
+
# Allow use of `Stackup.stacks` rather than `Stackup().stacks`
|
6
|
+
#
|
7
|
+
module Stackup
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
def service(client = {})
|
12
|
+
Stackup::Service.new(client)
|
13
|
+
end
|
14
|
+
|
15
|
+
extend Forwardable
|
16
|
+
|
17
|
+
def_delegators :service, :stack, :stack_names
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
# rubocop:disable Style/MethodName
|
24
|
+
|
25
|
+
def Stackup(*args)
|
26
|
+
Stackup.service(*args)
|
27
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,19 @@
|
|
1
|
-
require
|
1
|
+
require "console_logger"
|
2
|
+
|
3
|
+
module CfStubbing
|
4
|
+
|
5
|
+
def stub_cf_client
|
6
|
+
client_options = { :stub_responses => true }
|
7
|
+
if ENV.key?("AWS_DEBUG")
|
8
|
+
client_options[:logger] = ConsoleLogger.new(STDOUT, true)
|
9
|
+
client_options[:log_level] = :debug
|
10
|
+
end
|
11
|
+
Aws::CloudFormation::Client.new(client_options)
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
2
15
|
|
3
16
|
RSpec.configure do |c|
|
4
17
|
c.mock_with :rspec
|
18
|
+
c.include CfStubbing
|
5
19
|
end
|
data/spec/stackup/stack_spec.rb
CHANGED
@@ -1,15 +1,10 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
+
require "stackup/stack"
|
4
|
+
|
3
5
|
describe Stackup::Stack do
|
4
6
|
|
5
|
-
let(:cf_client)
|
6
|
-
client_options = { :stub_responses => true }
|
7
|
-
if ENV.key?("AWS_DEBUG")
|
8
|
-
client_options[:logger] = Logger.new(STDOUT)
|
9
|
-
client_options[:log_level] = :debug
|
10
|
-
end
|
11
|
-
Aws::CloudFormation::Client.new(client_options)
|
12
|
-
end
|
7
|
+
let(:cf_client) { stub_cf_client }
|
13
8
|
|
14
9
|
let(:stack_name) { "stack_name" }
|
15
10
|
let(:unique_stack_id) { "ID:#{stack_name}" }
|
@@ -23,24 +18,21 @@ describe Stackup::Stack do
|
|
23
18
|
allow(cf_client).to receive(:create_stack).and_call_original
|
24
19
|
allow(cf_client).to receive(:delete_stack).and_call_original
|
25
20
|
allow(cf_client).to receive(:update_stack).and_call_original
|
21
|
+
allow(cf_client).to receive(:cancel_update_stack).and_call_original
|
26
22
|
end
|
27
23
|
|
28
|
-
def
|
24
|
+
def validation_error(message)
|
29
25
|
{
|
30
26
|
:status_code => 400,
|
31
27
|
:headers => {},
|
32
|
-
:body =>
|
28
|
+
:body => [
|
29
|
+
"<ErrorResponse><Error><Code>ValidationError</Code><Message>",
|
30
|
+
message,
|
31
|
+
"</Message></Error></ErrorResponse>"
|
32
|
+
].join
|
33
33
|
}
|
34
34
|
end
|
35
35
|
|
36
|
-
def stack_does_not_exist
|
37
|
-
service_error("ValidationError", "Stack with id #{stack_name} does not exist")
|
38
|
-
end
|
39
|
-
|
40
|
-
def no_update_required
|
41
|
-
service_error("ValidationError", "No updates are to be performed.")
|
42
|
-
end
|
43
|
-
|
44
36
|
def stack_description(stack_status)
|
45
37
|
{
|
46
38
|
:stacks => [
|
@@ -58,7 +50,7 @@ describe Stackup::Stack do
|
|
58
50
|
|
59
51
|
let(:describe_stacks_responses) do
|
60
52
|
[
|
61
|
-
|
53
|
+
validation_error("Stack with id #{stack_name} does not exist")
|
62
54
|
]
|
63
55
|
end
|
64
56
|
|
@@ -85,7 +77,7 @@ describe Stackup::Stack do
|
|
85
77
|
let(:template) { "stack template" }
|
86
78
|
|
87
79
|
def create_or_update
|
88
|
-
stack.create_or_update(template)
|
80
|
+
stack.create_or_update(:template_body => template)
|
89
81
|
end
|
90
82
|
|
91
83
|
context "successful" do
|
@@ -162,14 +154,14 @@ describe Stackup::Stack do
|
|
162
154
|
let(:describe_stacks_responses) do
|
163
155
|
super() + [
|
164
156
|
stack_description("DELETE_IN_PROGRESS"),
|
165
|
-
|
157
|
+
stack_description("DELETE_COMPLETE")
|
166
158
|
]
|
167
159
|
end
|
168
160
|
|
169
161
|
it "calls delete_stack" do
|
170
162
|
stack.delete
|
171
163
|
expect(cf_client).to have_received(:delete_stack)
|
172
|
-
.with(hash_including(:stack_name =>
|
164
|
+
.with(hash_including(:stack_name => unique_stack_id))
|
173
165
|
end
|
174
166
|
|
175
167
|
it "returns :deleted" do
|
@@ -199,28 +191,36 @@ describe Stackup::Stack do
|
|
199
191
|
|
200
192
|
let(:template) { "stack template" }
|
201
193
|
|
194
|
+
let(:options) do
|
195
|
+
{ :template_body => template }
|
196
|
+
end
|
197
|
+
|
202
198
|
def create_or_update
|
203
|
-
stack.create_or_update(
|
199
|
+
stack.create_or_update(options)
|
204
200
|
end
|
205
201
|
|
206
|
-
|
202
|
+
let(:describe_stacks_responses) do
|
203
|
+
super() + [
|
204
|
+
stack_description("UPDATE_IN_PROGRESS"),
|
205
|
+
final_describe_stacks_response
|
206
|
+
]
|
207
|
+
end
|
207
208
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
stack_description("UPDATE_COMPLETE")
|
212
|
-
]
|
213
|
-
end
|
209
|
+
let(:final_describe_stacks_response) do
|
210
|
+
stack_description("UPDATE_COMPLETE")
|
211
|
+
end
|
214
212
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
213
|
+
it "calls :update_stack" do
|
214
|
+
expected_args = {
|
215
|
+
:stack_name => stack_name,
|
216
|
+
:template_body => template
|
217
|
+
}
|
218
|
+
create_or_update
|
219
|
+
expect(cf_client).to have_received(:update_stack)
|
220
|
+
.with(hash_including(expected_args))
|
221
|
+
end
|
222
|
+
|
223
|
+
context "successful" do
|
224
224
|
|
225
225
|
it "returns :updated" do
|
226
226
|
expect(create_or_update).to eq(:updated)
|
@@ -229,7 +229,10 @@ describe Stackup::Stack do
|
|
229
229
|
context "if no updates are required" do
|
230
230
|
|
231
231
|
before do
|
232
|
-
cf_client.stub_responses(
|
232
|
+
cf_client.stub_responses(
|
233
|
+
:update_stack,
|
234
|
+
validation_error("No updates are to be performed.")
|
235
|
+
)
|
233
236
|
end
|
234
237
|
|
235
238
|
it "returns nil" do
|
@@ -242,11 +245,8 @@ describe Stackup::Stack do
|
|
242
245
|
|
243
246
|
context "unsuccessful" do
|
244
247
|
|
245
|
-
let(:
|
246
|
-
|
247
|
-
stack_description("UPDATE_IN_PROGRESS"),
|
248
|
-
stack_description("UPDATE_ROLLBACK_COMPLETE")
|
249
|
-
]
|
248
|
+
let(:final_describe_stacks_response) do
|
249
|
+
stack_description("UPDATE_ROLLBACK_COMPLETE")
|
250
250
|
end
|
251
251
|
|
252
252
|
it "raises a StackUpdateError" do
|
@@ -255,6 +255,20 @@ describe Stackup::Stack do
|
|
255
255
|
|
256
256
|
end
|
257
257
|
|
258
|
+
context "with :disable_rollback" do
|
259
|
+
|
260
|
+
before do
|
261
|
+
options[:disable_rollback] = true
|
262
|
+
end
|
263
|
+
|
264
|
+
it "calls :update_stack" do
|
265
|
+
create_or_update
|
266
|
+
expect(cf_client).to have_received(:update_stack)
|
267
|
+
.with(hash_not_including(:disable_rollback))
|
268
|
+
end
|
269
|
+
|
270
|
+
end
|
271
|
+
|
258
272
|
end
|
259
273
|
|
260
274
|
%w(CREATE_FAILED ROLLBACK_COMPLETE).each do |initial_status|
|
@@ -267,20 +281,23 @@ describe Stackup::Stack do
|
|
267
281
|
let(:template) { "stack template" }
|
268
282
|
|
269
283
|
def create_or_update
|
270
|
-
stack.create_or_update(template)
|
284
|
+
stack.create_or_update(:template_body => template)
|
271
285
|
end
|
272
286
|
|
273
287
|
let(:describe_stacks_responses) do
|
274
288
|
super() + [
|
275
289
|
stack_description("DELETE_IN_PROGRESS"),
|
276
|
-
|
290
|
+
validation_error("Stack with id #{stack_name} does not exist"),
|
277
291
|
stack_description("CREATE_IN_PROGRESS"),
|
278
292
|
stack_description("CREATE_COMPLETE")
|
279
293
|
]
|
280
294
|
end
|
281
295
|
|
282
296
|
before do
|
283
|
-
cf_client.stub_responses(
|
297
|
+
cf_client.stub_responses(
|
298
|
+
:update_stack,
|
299
|
+
validation_error("Stack [woollyams-test] does not exist")
|
300
|
+
)
|
284
301
|
end
|
285
302
|
|
286
303
|
it "calls :delete_stack, then :create_stack first" do
|
@@ -294,6 +311,55 @@ describe Stackup::Stack do
|
|
294
311
|
end
|
295
312
|
end
|
296
313
|
|
314
|
+
context "when status is stable" do
|
315
|
+
|
316
|
+
before do
|
317
|
+
cf_client.stub_responses(
|
318
|
+
:cancel_update_stack,
|
319
|
+
validation_error("that cannot be called from current stack status")
|
320
|
+
)
|
321
|
+
end
|
322
|
+
|
323
|
+
describe "#cancel_update" do
|
324
|
+
|
325
|
+
it "returns nil" do
|
326
|
+
expect(stack.cancel_update).to be_nil
|
327
|
+
end
|
328
|
+
|
329
|
+
end
|
330
|
+
|
331
|
+
end
|
332
|
+
|
333
|
+
context "when status is UPDATE_IN_PROGRESS" do
|
334
|
+
|
335
|
+
let(:stack_status) { "UPDATE_IN_PROGRESS" }
|
336
|
+
|
337
|
+
describe "#cancel_update" do
|
338
|
+
|
339
|
+
let(:describe_stacks_responses) do
|
340
|
+
super() + [
|
341
|
+
stack_description("UPDATE_ROLLBACK_IN_PROGRESS"),
|
342
|
+
stack_description("UPDATE_ROLLBACK_COMPLETE")
|
343
|
+
]
|
344
|
+
end
|
345
|
+
|
346
|
+
it "calls :cancel_update_stack" do
|
347
|
+
expected_args = {
|
348
|
+
:stack_name => stack_name
|
349
|
+
}
|
350
|
+
stack.cancel_update
|
351
|
+
expect(cf_client).to have_received(:cancel_update_stack)
|
352
|
+
.with(hash_including(expected_args))
|
353
|
+
end
|
354
|
+
|
355
|
+
it "returns :update_cancelled" do
|
356
|
+
expect(stack.cancel_update).to eq(:update_cancelled)
|
357
|
+
end
|
358
|
+
|
359
|
+
end
|
360
|
+
|
361
|
+
end
|
362
|
+
|
297
363
|
end
|
298
364
|
|
299
365
|
end
|