stackup 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2e09ac6d57442b2d3c00a71be836fd7763a2fb05
4
- data.tar.gz: 403250844ca02c15cc39cd49b7e5f26763ef463b
3
+ metadata.gz: bd4fb7b89612636d7c12bf5de9df53b33d426706
4
+ data.tar.gz: 0efb356c797b510c3c73be0feb46f64b883ca5d7
5
5
  SHA512:
6
- metadata.gz: f4dc9a1390519780f2c42e131827ec5b7c634653cd9ff95df6acc7f308b865c1334976330bcf0c826a14f6965b78f6d5a025a828360ddc4d6e18fc53fbdd284d
7
- data.tar.gz: 383e9d2680d21408badbee0f00de41a46c2ba1b75cb71f2ade798f486049f11dac3a972e26f09b12c3154a0295137404c4c0792f7a794b08756eecd6bd8f297b
6
+ metadata.gz: 6820c01014c27659f323943935b8914c17f2d77be132d5e407c696216a90dce0cc188ed8d6f1e20aaebc4fcf1142b2ced902be47de5c091677c7f1f074e24131
7
+ data.tar.gz: 386304c7efb13945816d072f1348ce13fc01da0d3c42a551c637c57ecadcc43baa0a3e90b83b8ad0234c71762cd550edf08a5c6dc73b2a3fb9be3344b89370f3
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- stackup (0.0.8)
4
+ stackup (0.0.9)
5
5
  aws-sdk (~> 2.0)
6
6
  clamp (~> 1.0)
7
7
 
data/bin/stackup CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
- require "clamp"
2
+
3
3
  $LOAD_PATH << File.expand_path("../../lib", __FILE__)
4
- require File.expand_path("../../lib/stackup", __FILE__)
5
- require File.expand_path("../../lib/stackup/cli", __FILE__)
4
+
5
+ require "stackup/cli"
6
6
 
7
7
  Stackup::CLI.run
data/lib/stackup.rb CHANGED
@@ -1,4 +1 @@
1
- require "aws-sdk"
2
- require_relative "stackup/monitor"
3
- require_relative "stackup/stack"
4
- require "json"
1
+ require "stackup/stack"
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 ["stack"], "Manage a stack." do
5
- parameter "STACK-NAME", "Name of stack", :attribute_name => :stack_name
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
- parameter "TEMPLATE", "CloudFormation template (.json)", :attribute_name => :template
9
- parameter "PARAMETERS", "CloudFormation parameters (.json)", :attribute_name => :parameters
28
+
29
+ parameter "TEMPLATE", "CloudFormation template (.json)", :attribute_name => :template_file
10
30
 
11
31
  def execute
12
- params = JSON.parse(parameters)
13
- stack = Stackup::Stack.new(stack_name)
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
@@ -1,3 +1,5 @@
1
+ require "aws-sdk-core"
2
+
1
3
  module Stackup
2
4
  class Monitor
3
5
 
@@ -18,7 +20,7 @@ module Stackup
18
20
  private
19
21
 
20
22
  def seen?(event)
21
- event_id = event.event_id
23
+ event_id = event.id
22
24
  if events.include?(event_id)
23
25
  true
24
26
  else
data/lib/stackup/stack.rb CHANGED
@@ -1,32 +1,53 @@
1
- require "set"
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
- response = cf.create_stack(:stack_name => name,
20
- :template_body => template,
21
- :disable_rollback => true,
22
- :capabilities => ["CAPABILITY_IAM"],
23
- :parameters => parameters)
24
- wait_for_events
25
- !response[:stack_id].nil?
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 deployed?
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
- response = cf.update_stack(:stack_name => name, :template_body => template, :parameters => parameters, :capabilities => ["CAPABILITY_IAM"])
39
- wait_for_events
40
- !response[:stack_id].nil?
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 deployed?
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 deployed?
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
- status = stack.stack_status
89
- return status if status =~ /_(COMPLETE|FAILED)$/
90
- sleep(5)
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
- let(:stack) { Stackup::Stack.new("name") }
5
- let(:monitor) { Stackup::Monitor.new(stack) }
6
- let(:event) { double(Aws::CloudFormation::Event.new(:id => "1")) }
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
@@ -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(response).to receive(:[]).with(:stack_id).and_return("1")
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(response).to receive(:[]).with(:stack_id).and_return(nil)
40
+ allow(stack).to receive(:wait_for_events).and_return("CREATE_FAILED")
39
41
  end
40
- it { expect(created).to be false }
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(:deployed?).and_return(false)
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(:deployed?).and_return(true)
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(true)
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(response).to receive(:[]).with(:stack_id).and_return(nil)
75
+ allow(stack).to receive(:wait_for_events).and_return("UPDATE_FAILED")
77
76
  end
78
- it { expect(updated).to be false }
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(:deployed?).and_return(true)
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(:deployed?).and_return(false)
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(:deployed?).and_return false
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(:deployed?).and_return true
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.deployed?).to be true
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.deployed?).to be false
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.8"
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.8
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-05 00:00:00.000000000 Z
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