stackup 0.0.8 → 0.0.9
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 +1 -1
- data/bin/stackup +3 -3
- data/lib/stackup.rb +1 -4
- data/lib/stackup/cli.rb +29 -9
- data/lib/stackup/monitor.rb +3 -1
- data/lib/stackup/stack.rb +52 -28
- data/pkg/stackup-0.0.8.gem +0 -0
- data/spec/stackup/monitor_spec.rb +6 -5
- data/spec/stackup/stack_spec.rb +18 -19
- data/stackup.gemspec +1 -1
- data/woollyams/sample-template.json +39 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bd4fb7b89612636d7c12bf5de9df53b33d426706
|
4
|
+
data.tar.gz: 0efb356c797b510c3c73be0feb46f64b883ca5d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6820c01014c27659f323943935b8914c17f2d77be132d5e407c696216a90dce0cc188ed8d6f1e20aaebc4fcf1142b2ced902be47de5c091677c7f1f074e24131
|
7
|
+
data.tar.gz: 386304c7efb13945816d072f1348ce13fc01da0d3c42a551c637c57ecadcc43baa0a3e90b83b8ad0234c71762cd550edf08a5c6dc73b2a3fb9be3344b89370f3
|
data/Gemfile.lock
CHANGED
data/bin/stackup
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
2
|
+
|
3
3
|
$LOAD_PATH << File.expand_path("../../lib", __FILE__)
|
4
|
-
|
5
|
-
require
|
4
|
+
|
5
|
+
require "stackup/cli"
|
6
6
|
|
7
7
|
Stackup::CLI.run
|
data/lib/stackup.rb
CHANGED
data/lib/stackup/cli.rb
CHANGED
@@ -1,34 +1,54 @@
|
|
1
|
+
require "clamp"
|
2
|
+
require "json"
|
3
|
+
require "stackup/stack"
|
4
|
+
|
1
5
|
module Stackup
|
6
|
+
|
2
7
|
class CLI < Clamp::Command
|
3
8
|
|
4
|
-
subcommand
|
5
|
-
|
9
|
+
subcommand "stack", "Manage a stack." do
|
10
|
+
|
11
|
+
parameter "NAME", "Name of stack", :attribute_name => :stack_name
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def stack
|
16
|
+
Stackup::Stack.new(stack_name)
|
17
|
+
end
|
18
|
+
|
19
|
+
subcommand "status", "Print stack status." do
|
20
|
+
|
21
|
+
def execute
|
22
|
+
puts stack.status
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
6
26
|
|
7
27
|
subcommand "deploy", "Create/update the stack" do
|
8
|
-
|
9
|
-
parameter "
|
28
|
+
|
29
|
+
parameter "TEMPLATE", "CloudFormation template (.json)", :attribute_name => :template_file
|
10
30
|
|
11
31
|
def execute
|
12
|
-
|
13
|
-
stack
|
14
|
-
stack.create(template, params)
|
32
|
+
template = File.read(template_file)
|
33
|
+
stack.deploy(template)
|
15
34
|
end
|
35
|
+
|
16
36
|
end
|
17
37
|
|
18
38
|
subcommand "delete", "Remove the stack." do
|
19
39
|
def execute
|
20
|
-
stack = Stackup::Stack.new(stack_name)
|
21
40
|
stack.delete
|
22
41
|
end
|
23
42
|
end
|
24
43
|
|
25
44
|
subcommand "outputs", "Stack outputs." do
|
26
45
|
def execute
|
27
|
-
stack = Stackup::Stack.new(stack_name)
|
28
46
|
stack.outputs
|
29
47
|
end
|
30
48
|
end
|
49
|
+
|
31
50
|
end
|
32
51
|
|
33
52
|
end
|
53
|
+
|
34
54
|
end
|
data/lib/stackup/monitor.rb
CHANGED
data/lib/stackup/stack.rb
CHANGED
@@ -1,32 +1,53 @@
|
|
1
|
-
require "
|
1
|
+
require "aws-sdk-resources"
|
2
|
+
require "stackup/monitor"
|
2
3
|
|
3
4
|
module Stackup
|
4
5
|
class Stack
|
5
6
|
|
6
|
-
attr_reader :stack, :name, :cf, :monitor
|
7
7
|
SUCESS_STATES = ["CREATE_COMPLETE", "DELETE_COMPLETE", "UPDATE_COMPLETE"]
|
8
8
|
FAILURE_STATES = ["CREATE_FAILED", "DELETE_FAILED", "ROLLBACK_COMPLETE", "ROLLBACK_FAILED", "UPDATE_ROLLBACK_COMPLETE", "UPDATE_ROLLBACK_FAILED"]
|
9
9
|
END_STATES = SUCESS_STATES + FAILURE_STATES
|
10
10
|
|
11
|
-
def initialize(name)
|
11
|
+
def initialize(name, client_options = {})
|
12
12
|
@cf = Aws::CloudFormation::Client.new
|
13
13
|
@stack = Aws::CloudFormation::Stack.new(:name => name, :client => cf)
|
14
14
|
@monitor = Stackup::Monitor.new(@stack)
|
15
|
+
@monitor.new_events # drain previous events
|
15
16
|
@name = name
|
16
17
|
end
|
17
18
|
|
19
|
+
attr_reader :stack, :name, :cf, :monitor
|
20
|
+
|
21
|
+
def status
|
22
|
+
stack.stack_status
|
23
|
+
rescue Aws::CloudFormation::Errors::ValidationError
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def exists?
|
28
|
+
!!status
|
29
|
+
end
|
30
|
+
|
18
31
|
def create(template, parameters)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
wait_for_events
|
25
|
-
|
32
|
+
cf.create_stack(:stack_name => name,
|
33
|
+
:template_body => template,
|
34
|
+
:disable_rollback => true,
|
35
|
+
:capabilities => ["CAPABILITY_IAM"],
|
36
|
+
:parameters => parameters)
|
37
|
+
status = wait_for_events
|
38
|
+
|
39
|
+
fail CreateError, "stack creation failed" unless status == "CREATE_COMPLETE"
|
40
|
+
true
|
41
|
+
|
42
|
+
rescue ::Aws::CloudFormation::Errors::ValidationError
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
|
46
|
+
class CreateError < StandardError
|
26
47
|
end
|
27
48
|
|
28
49
|
def update(template, parameters)
|
29
|
-
return false unless
|
50
|
+
return false unless exists?
|
30
51
|
if stack.stack_status == "CREATE_FAILED"
|
31
52
|
puts "Stack is in CREATE_FAILED state so must be manually deleted before it can be updated"
|
32
53
|
return false
|
@@ -35,13 +56,25 @@ module Stackup
|
|
35
56
|
deleted = delete
|
36
57
|
return false if !deleted
|
37
58
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
59
|
+
cf.update_stack(:stack_name => name, :template_body => template, :parameters => parameters, :capabilities => ["CAPABILITY_IAM"])
|
60
|
+
|
61
|
+
status = wait_for_events
|
62
|
+
fail UpdateError, "stack update failed" unless status == "UPDATE_COMPLETE"
|
63
|
+
true
|
64
|
+
|
65
|
+
rescue ::Aws::CloudFormation::Errors::ValidationError => e
|
66
|
+
if e.message == "No updates are to be performed."
|
67
|
+
puts e.message
|
68
|
+
return false
|
69
|
+
end
|
70
|
+
raise e
|
71
|
+
end
|
72
|
+
|
73
|
+
class UpdateError < StandardError
|
41
74
|
end
|
42
75
|
|
43
76
|
def delete
|
44
|
-
return false unless
|
77
|
+
return false unless exists?
|
45
78
|
cf.delete_stack(:stack_name => name)
|
46
79
|
status = wait_for_events
|
47
80
|
fail UpdateError, "stack delete failed" unless status == "DELETE_COMPLETE"
|
@@ -51,7 +84,7 @@ module Stackup
|
|
51
84
|
end
|
52
85
|
|
53
86
|
def deploy(template, parameters = [])
|
54
|
-
if
|
87
|
+
if exists?
|
55
88
|
update(template, parameters)
|
56
89
|
else
|
57
90
|
create(template, parameters)
|
@@ -64,20 +97,11 @@ module Stackup
|
|
64
97
|
puts stack.outputs.flat_map { |output| "#{output.output_key} - #{output.output_value}" }
|
65
98
|
end
|
66
99
|
|
67
|
-
def deployed?
|
68
|
-
!stack.stack_status.nil?
|
69
|
-
rescue Aws::CloudFormation::Errors::ValidationError => e
|
70
|
-
false
|
71
|
-
end
|
72
|
-
|
73
100
|
def valid?(template)
|
74
101
|
response = cf.validate_template(template)
|
75
102
|
response[:code].nil?
|
76
103
|
end
|
77
104
|
|
78
|
-
class UpdateError < StandardError
|
79
|
-
end
|
80
|
-
|
81
105
|
private
|
82
106
|
|
83
107
|
# Wait (displaying stack events) until the stack reaches a stable state.
|
@@ -85,9 +109,9 @@ module Stackup
|
|
85
109
|
def wait_for_events
|
86
110
|
loop do
|
87
111
|
display_new_events
|
88
|
-
|
89
|
-
return status if status =~ /_(COMPLETE|FAILED)$/
|
90
|
-
sleep(
|
112
|
+
stack.reload
|
113
|
+
return status if status.nil? || status =~ /_(COMPLETE|FAILED)$/
|
114
|
+
sleep(2)
|
91
115
|
end
|
92
116
|
end
|
93
117
|
|
Binary file
|
@@ -1,15 +1,15 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe Stackup::Monitor do
|
4
|
-
|
5
|
-
let(:
|
6
|
-
let(:
|
4
|
+
|
5
|
+
let(:stack) { instance_double(Aws::CloudFormation::Stack, :events => events) }
|
6
|
+
let(:monitor) { described_class.new(stack) }
|
7
|
+
|
8
|
+
let(:event) { instance_double(Aws::CloudFormation::Event, :id => "1") }
|
7
9
|
let(:events) { [event] }
|
8
10
|
|
9
11
|
before do
|
10
|
-
Aws.config[:region] = "ap-southeast-2"
|
11
12
|
allow(event).to receive(:event_id).and_return("1")
|
12
|
-
allow(stack).to receive(:events).and_return(events)
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should add the event if it is non-existent" do
|
@@ -20,4 +20,5 @@ describe Stackup::Monitor do
|
|
20
20
|
expect(monitor.new_events.size).to eq(1)
|
21
21
|
expect(monitor.new_events.size).to eq(0)
|
22
22
|
end
|
23
|
+
|
23
24
|
end
|
data/spec/stackup/stack_spec.rb
CHANGED
@@ -4,17 +4,20 @@ describe Stackup::Stack do
|
|
4
4
|
|
5
5
|
let(:stack) { described_class.new("stack_name") }
|
6
6
|
|
7
|
-
let(:cf_stack) { instance_double("Aws::CloudFormation::Stack"
|
7
|
+
let(:cf_stack) { instance_double("Aws::CloudFormation::Stack",
|
8
|
+
:stack_status => stack_status) }
|
8
9
|
let(:cf_client) { instance_double("Aws::CloudFormation::Client") }
|
9
10
|
|
10
11
|
let(:template) { double(String) }
|
11
12
|
let(:parameters) { [] }
|
13
|
+
let(:stack_status) { nil }
|
12
14
|
|
13
15
|
let(:response) { Seahorse::Client::Http::Response.new }
|
14
16
|
|
15
17
|
before do
|
16
18
|
allow(Aws::CloudFormation::Client).to receive(:new).and_return(cf_client)
|
17
19
|
allow(Aws::CloudFormation::Stack).to receive(:new).and_return(cf_stack)
|
20
|
+
allow(cf_stack).to receive(:events).and_return([])
|
18
21
|
end
|
19
22
|
|
20
23
|
describe "#create" do
|
@@ -23,21 +26,20 @@ describe Stackup::Stack do
|
|
23
26
|
|
24
27
|
before do
|
25
28
|
allow(cf_client).to receive(:create_stack).and_return(response)
|
26
|
-
allow(stack).to receive(:wait_for_events).and_return(true)
|
27
29
|
end
|
28
30
|
|
29
31
|
context "when stack gets successfully created" do
|
30
32
|
before do
|
31
|
-
allow(
|
33
|
+
allow(stack).to receive(:wait_for_events).and_return("CREATE_COMPLETE")
|
32
34
|
end
|
33
35
|
it { expect(created).to be true }
|
34
36
|
end
|
35
37
|
|
36
38
|
context "when stack creation fails" do
|
37
39
|
before do
|
38
|
-
allow(
|
40
|
+
allow(stack).to receive(:wait_for_events).and_return("CREATE_FAILED")
|
39
41
|
end
|
40
|
-
it { expect
|
42
|
+
it { expect{ created }.to raise_error Stackup::Stack::CreateError }
|
41
43
|
end
|
42
44
|
|
43
45
|
end
|
@@ -47,16 +49,16 @@ describe Stackup::Stack do
|
|
47
49
|
|
48
50
|
context "when there is no existing stack" do
|
49
51
|
before do
|
50
|
-
allow(stack).to receive(:
|
52
|
+
allow(stack).to receive(:exists?).and_return(false)
|
51
53
|
end
|
52
54
|
it { expect(updated).to be false }
|
53
55
|
end
|
54
56
|
|
55
57
|
context "when there is an existing stack" do
|
56
58
|
before do
|
57
|
-
allow(stack).to receive(:
|
59
|
+
allow(stack).to receive(:exists?).and_return(true)
|
58
60
|
allow(cf_client).to receive(:update_stack).and_return(response)
|
59
|
-
allow(stack).to receive(:wait_for_events).and_return(
|
61
|
+
allow(stack).to receive(:wait_for_events).and_return("UPDATE_COMPLETE")
|
60
62
|
end
|
61
63
|
|
62
64
|
context "in a successfully deployed state" do
|
@@ -65,17 +67,14 @@ describe Stackup::Stack do
|
|
65
67
|
end
|
66
68
|
|
67
69
|
context "when stack gets successfully updated" do
|
68
|
-
before do
|
69
|
-
allow(response).to receive(:[]).with(:stack_id).and_return("1")
|
70
|
-
end
|
71
70
|
it { expect(updated).to be true }
|
72
71
|
end
|
73
72
|
|
74
73
|
context "when stack update fails" do
|
75
74
|
before do
|
76
|
-
allow(
|
75
|
+
allow(stack).to receive(:wait_for_events).and_return("UPDATE_FAILED")
|
77
76
|
end
|
78
|
-
it { expect
|
77
|
+
it { expect{ updated }.to raise_error Stackup::Stack::UpdateError }
|
79
78
|
end
|
80
79
|
end
|
81
80
|
|
@@ -144,7 +143,7 @@ describe Stackup::Stack do
|
|
144
143
|
context "when stack already exists" do
|
145
144
|
|
146
145
|
before do
|
147
|
-
allow(stack).to receive(:
|
146
|
+
allow(stack).to receive(:exists?).and_return(true)
|
148
147
|
allow(cf_stack).to receive(:stack_status).and_return("CREATE_COMPLETE")
|
149
148
|
allow(cf_client).to receive(:update_stack).and_return({ stack_id: "stack-name" })
|
150
149
|
end
|
@@ -158,7 +157,7 @@ describe Stackup::Stack do
|
|
158
157
|
context "when stack does not exist" do
|
159
158
|
|
160
159
|
before do
|
161
|
-
allow(stack).to receive(:
|
160
|
+
allow(stack).to receive(:exists?).and_return(false)
|
162
161
|
allow(cf_client).to receive(:create_stack).and_return({ stack_id: "stack-name" })
|
163
162
|
end
|
164
163
|
|
@@ -176,7 +175,7 @@ describe Stackup::Stack do
|
|
176
175
|
|
177
176
|
context "there is no existing stack" do
|
178
177
|
before do
|
179
|
-
allow(stack).to receive(:
|
178
|
+
allow(stack).to receive(:exists?).and_return false
|
180
179
|
end
|
181
180
|
|
182
181
|
it { expect(deleted).to be false }
|
@@ -188,7 +187,7 @@ describe Stackup::Stack do
|
|
188
187
|
|
189
188
|
context "there is an existing stack" do
|
190
189
|
before do
|
191
|
-
allow(stack).to receive(:
|
190
|
+
allow(stack).to receive(:exists?).and_return true
|
192
191
|
allow(cf_client).to receive(:delete_stack)
|
193
192
|
end
|
194
193
|
|
@@ -225,12 +224,12 @@ describe Stackup::Stack do
|
|
225
224
|
context "deployed" do
|
226
225
|
it "should be true if it is already deployed" do
|
227
226
|
allow(cf_stack).to receive(:stack_status).and_return("CREATE_COMPLETE")
|
228
|
-
expect(stack.
|
227
|
+
expect(stack.exists?).to be true
|
229
228
|
end
|
230
229
|
|
231
230
|
it "should be false if it is not deployed" do
|
232
231
|
allow(cf_stack).to receive(:stack_status).and_raise(Aws::CloudFormation::Errors::ValidationError.new("1", "2"))
|
233
|
-
expect(stack.
|
232
|
+
expect(stack.exists?).to be false
|
234
233
|
end
|
235
234
|
end
|
236
235
|
end
|
data/stackup.gemspec
CHANGED
@@ -4,7 +4,7 @@ $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.0.
|
7
|
+
spec.version = "0.0.9"
|
8
8
|
spec.authors = ["Arvind Kunday", "Mike Williams"]
|
9
9
|
spec.email = ["arvind.kunday@rea-group.com", "mike.williams@rea-group.com"]
|
10
10
|
spec.summary = "Tools for deployment to AWS"
|
@@ -0,0 +1,39 @@
|
|
1
|
+
{
|
2
|
+
"AWSTemplateFormatVersion": "2010-09-09",
|
3
|
+
"Description": "A sample template",
|
4
|
+
"Resources": {
|
5
|
+
"role": {
|
6
|
+
"Type": "AWS::IAM::Role",
|
7
|
+
"Properties": {
|
8
|
+
"AssumeRolePolicyDocument": {
|
9
|
+
"Version" : "2012-10-17",
|
10
|
+
"Statement": [
|
11
|
+
{
|
12
|
+
"Effect": "Allow",
|
13
|
+
"Principal": {
|
14
|
+
"Service": [ "ec2.amazonaws.com" ]
|
15
|
+
},
|
16
|
+
"Action": [ "sts:AssumeRole" ]
|
17
|
+
}
|
18
|
+
]
|
19
|
+
},
|
20
|
+
"Path": "/",
|
21
|
+
"Policies": [
|
22
|
+
{
|
23
|
+
"PolicyName": "rooty",
|
24
|
+
"PolicyDocument": {
|
25
|
+
"Version" : "2012-10-17",
|
26
|
+
"Statement": [
|
27
|
+
{
|
28
|
+
"Effect": "Allow",
|
29
|
+
"Action": "*",
|
30
|
+
"Resource": "*"
|
31
|
+
}
|
32
|
+
]
|
33
|
+
}
|
34
|
+
}
|
35
|
+
]
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
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: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arvind Kunday
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-10-
|
12
|
+
date: 2015-10-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: aws-sdk
|
@@ -58,10 +58,12 @@ files:
|
|
58
58
|
- lib/stackup/monitor.rb
|
59
59
|
- lib/stackup/stack.rb
|
60
60
|
- pkg/stackup-0.0.1.gem
|
61
|
+
- pkg/stackup-0.0.8.gem
|
61
62
|
- spec/spec_helper.rb
|
62
63
|
- spec/stackup/monitor_spec.rb
|
63
64
|
- spec/stackup/stack_spec.rb
|
64
65
|
- stackup.gemspec
|
66
|
+
- woollyams/sample-template.json
|
65
67
|
homepage: https://github.com/realestate-com-au/stackup
|
66
68
|
licenses:
|
67
69
|
- MIT
|