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 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