stackup 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 198c45027f791cb13d1ba1de4643fda60a97a18d
4
+ data.tar.gz: 7aab71076441135004a10109bda7a452ec15d75d
5
+ SHA512:
6
+ metadata.gz: ff9a5468160fd18e6b4f80697457e76c653e5d5a755b776cd2873ee230e91148c9cd02dc9d402457e6c7ce468d8e016e6f0377b9512399c74394cf0e1d24cae2
7
+ data.tar.gz: 4d13507cc1022c2073b6feb0c13eea683b8983e2468489d470b5b5c40f9fea394340fe733d592283c9d852ebce45d6d8e1c87469b64c710aa321bca607ba7b4f
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org" do
2
+ gemspec
3
+
4
+ gem "rake", "~> 10.0"
5
+
6
+ group :test do
7
+ gem "rspec", "~> 3.3"
8
+ gem "byebug"
9
+ end
10
+
11
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,48 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ stackup (0.0.1)
5
+ aws-sdk (~> 2.0)
6
+ clamp (~> 1.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ aws-sdk (2.1.20)
12
+ aws-sdk-resources (= 2.1.20)
13
+ aws-sdk-core (2.1.20)
14
+ jmespath (~> 1.0)
15
+ aws-sdk-resources (2.1.20)
16
+ aws-sdk-core (= 2.1.20)
17
+ byebug (6.0.2)
18
+ clamp (1.0.0)
19
+ diff-lcs (1.2.5)
20
+ jmespath (1.0.2)
21
+ multi_json (~> 1.0)
22
+ multi_json (1.11.2)
23
+ rake (10.4.2)
24
+ rspec (3.3.0)
25
+ rspec-core (~> 3.3.0)
26
+ rspec-expectations (~> 3.3.0)
27
+ rspec-mocks (~> 3.3.0)
28
+ rspec-core (3.3.2)
29
+ rspec-support (~> 3.3.0)
30
+ rspec-expectations (3.3.1)
31
+ diff-lcs (>= 1.2.0, < 2.0)
32
+ rspec-support (~> 3.3.0)
33
+ rspec-mocks (3.3.2)
34
+ diff-lcs (>= 1.2.0, < 2.0)
35
+ rspec-support (~> 3.3.0)
36
+ rspec-support (3.3.0)
37
+
38
+ PLATFORMS
39
+ ruby
40
+
41
+ DEPENDENCIES
42
+ byebug
43
+ rake (~> 10.0)
44
+ rspec (~> 3.3)
45
+ stackup!
46
+
47
+ BUNDLED WITH
48
+ 1.10.6
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # stackup
2
+
3
+ Stackup attempts to simplify AWS Cloudformation stack creation process in
4
+ ruby projects by providing executable to perform common operations such
5
+ as apply(create/update), delete, recreate on stack along with validations on
6
+ templates. Task executions which enforce a stack change will wait until
7
+ ROLLBACK/COMPLETE or DELETE is signalled on the stack (useful in continuous
8
+ deployment environments to wait until deployment is successful).
9
+
10
+ ## Installation
11
+
12
+ $ gem install stackup
13
+
14
+ ## Usage
15
+
16
+ The entry-point is the "stackup" command.
17
+
18
+ The "stack" subcommand lists stacks:
19
+
20
+ $ stackup stacks
21
+
22
+ Most other commands operate in the context of a named stack:
23
+
24
+ $ stackup stack STACK-NAME ...
25
+
26
+ To create or update a stack, based on a template, use "apply":
27
+
28
+ $ stackup stack myapp-test apply
29
+
30
+ This will:
31
+
32
+ * update (or create) the named CloudFormation stack, using the specified template
33
+ * monitor events until the stack update is complete
34
+ * print any stack "outputs"
35
+
36
+ Other stack subcommands include:
37
+
38
+ $ stackup stack myapp-test outputs
39
+ $ stackup stack myapp-test resources
40
+ $ stackup stack myapp-test delete
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new do |t|
6
+ t.pattern = 'spec/**/*_spec.rb'
7
+ t.rspec_opts = ["--colour", "--format", "documentation"]
8
+ end
9
+
10
+ task "default" => "spec"
data/lib/stackup.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'aws-sdk'
2
+ require_relative 'stackup/monitor'
3
+ require_relative 'stackup/stack'
@@ -0,0 +1,29 @@
1
+ module Stackup
2
+ class Monitor
3
+ attr_accessor :stack, :events
4
+ def initialize(stack)
5
+ @stack = stack
6
+ @events = Set.new
7
+ end
8
+
9
+ def new_events
10
+ stack.events.take_while do |event|
11
+ !seen?(event)
12
+ end.reverse
13
+ rescue ::Aws::CloudFormation::Errors::ValidationError => e
14
+ []
15
+ end
16
+
17
+ private
18
+ def seen?(event)
19
+ event_id = event.event_id
20
+ if events.include?(event_id)
21
+ true
22
+ else
23
+ events.add(event_id)
24
+ false
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,48 @@
1
+ require 'set'
2
+
3
+ module Stackup
4
+ class Stack
5
+ attr_reader :stack, :name, :cf, :template, :monitor
6
+ SUCESS_STATES = ["CREATE_COMPLETE", "UPDATE_COMPLETE"]
7
+ FAILURE_STATES = ["CREATE_FAILED", "DELETE_FAILED", "UPDATE_ROLLBACK_FAILED", "ROLLBACK_FAILED", "ROLLBACK_COMPLETE","ROLLBACK_FAILED","UPDATE_ROLLBACK_COMPLETE","UPDATE_ROLLBACK_FAILED"]
8
+ END_STATES = SUCESS_STATES + FAILURE_STATES
9
+
10
+ def initialize(name, template)
11
+ @cf = Aws::CloudFormation::Client.new
12
+ @stack = Aws::CloudFormation::Stack.new(name: name, client: cf)
13
+ @monitor = Stackup::Monitor.new(@stack)
14
+ @template = template
15
+ @name = name
16
+ end
17
+
18
+ def create
19
+ response = cf.create_stack({
20
+ stack_name: name,
21
+ template_body: template,
22
+ disable_rollback: true
23
+ })
24
+ !response[:stack_id].nil?
25
+ stack.wait_until(max_attempts: 1000, delay: 10) { |resource| display_events ; END_STATES.include?(resource.stack_status) }
26
+ end
27
+
28
+ def display_events
29
+ monitor.new_events.each do |e|
30
+ ts = e.timestamp.localtime.strftime("%H:%M:%S")
31
+ fields = [e.logical_resource_id, e.resource_status, e.resource_status_reason]
32
+ puts("[#{ts}] #{fields.compact.join(' - ')}")
33
+ end
34
+ end
35
+
36
+ def deployed?
37
+ !stack.stack_status.nil?
38
+ rescue Aws::CloudFormation::Errors::ValidationError => e
39
+ false
40
+ end
41
+
42
+ def valid?
43
+ response = cf.validate_template(template)
44
+ response[:code].nil?
45
+ end
46
+
47
+ end
48
+ end
Binary file
@@ -0,0 +1,5 @@
1
+ require File.expand_path('../../lib/stackup', __FILE__)
2
+
3
+ RSpec.configure do |c|
4
+ c.mock_with :rspec
5
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Stackup::Monitor do
4
+ let(:stack) { Stackup::Stack.new('name', 'template') }
5
+ let(:monitor) { Stackup::Monitor.new(stack) }
6
+ let(:event) { double(Aws::CloudFormation::Event.new(id: '1')) }
7
+ let(:events) { [ event ] }
8
+
9
+ it 'should add the event if it is non-existent' do
10
+ allow(event).to receive(:event_id).and_return('1')
11
+ allow(stack).to receive(:events).and_return(events)
12
+ expect(monitor.new_events.size).to eq(1)
13
+ end
14
+
15
+ it 'should skip the event if it has been shown' do
16
+ allow(event).to receive(:event_id).and_return('1')
17
+ allow(stack).to receive(:events).and_return(events)
18
+ expect(monitor.new_events.size).to eq(1)
19
+ expect(monitor.new_events.size).to eq(0)
20
+ end
21
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe Stackup::Stack do
4
+ let(:stack) { Stackup::Stack.new('stack_name', double(String)) }
5
+ let(:cf_stack) { double(Aws::CloudFormation::Stack) }
6
+ let(:cf) {double(Aws::CloudFormation::Client)}
7
+
8
+ before do
9
+ allow(Aws::CloudFormation::Client).to receive(:new).and_return(cf)
10
+ allow(Aws::CloudFormation::Stack).to receive(:new).and_return(cf_stack)
11
+ end
12
+
13
+ context 'create' do
14
+ let(:response) {Seahorse::Client::Http::Response.new}
15
+ it 'should create stack if all is well' do
16
+ allow(response).to receive(:[]).with(:stack_id).and_return('1')
17
+ allow(cf).to receive(:create_stack).and_return(response)
18
+ allow(cf_stack).to receive(:wait_until).and_return(true)
19
+ expect(stack.create).to be true
20
+ end
21
+
22
+ it 'should return nil if stack was not created' do
23
+ allow(response).to receive(:[]).with(:stack_id).and_return(nil)
24
+ allow(cf).to receive(:create_stack).and_return(response)
25
+ allow(cf_stack).to receive(:wait_until).and_return(false)
26
+ expect(stack.create).to be false
27
+ end
28
+ end
29
+
30
+ context 'validate' do
31
+ it 'should be valid if cf validate say so' do
32
+ allow(cf).to receive(:validate_template).and_return({})
33
+ expect(stack.valid?).to be true
34
+ end
35
+
36
+ it 'should be invalid if cf validate say so' do
37
+ allow(cf).to receive(:validate_template).and_return({code: '404'})
38
+ expect(stack.valid?).to be false
39
+ end
40
+
41
+ end
42
+
43
+ context 'deployed' do
44
+ it 'should be true if it is already deployed' do
45
+ allow(cf_stack).to receive(:stack_status).and_return('CREATE_COMPLETE')
46
+ expect(stack.deployed?).to be true
47
+ end
48
+
49
+ it 'should be false if it is not deployed' do
50
+ allow(cf_stack).to receive(:stack_status).and_raise(Aws::CloudFormation::Errors::ValidationError.new('1', '2'))
51
+ expect(stack.deployed?).to be false
52
+ end
53
+ end
54
+ end
data/stackup.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+
7
+ spec.name = "stackup"
8
+ spec.version = '0.0.1'
9
+ spec.authors = ["Arvind Kunday", "Mike Williams"]
10
+ spec.email = ["arvind.kunday@rea-group.com", "mike.williams@rea-group.com"]
11
+ spec.summary = "Tools for deployment to AWS"
12
+ spec.homepage = "https://github.com/realestate-com-au/stackup"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = Dir["**/*"].reject { |f| File.directory?(f) }
16
+ spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) }
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_dependency "aws-sdk", "~> 2.0"
20
+ spec.add_dependency "clamp", "~> 1.0"
21
+
22
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stackup
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Arvind Kunday
8
+ - Mike Williams
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-09-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: aws-sdk
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '2.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '2.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: clamp
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.0'
42
+ description:
43
+ email:
44
+ - arvind.kunday@rea-group.com
45
+ - mike.williams@rea-group.com
46
+ executables: []
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - Gemfile
51
+ - Gemfile.lock
52
+ - README.md
53
+ - Rakefile
54
+ - lib/stackup.rb
55
+ - lib/stackup/monitor.rb
56
+ - lib/stackup/stack.rb
57
+ - pkg/stackup-0.0.1.gem
58
+ - spec/spec_helper.rb
59
+ - spec/stackup/monitor_spec.rb
60
+ - spec/stackup/stack_spec.rb
61
+ - stackup.gemspec
62
+ homepage: https://github.com/realestate-com-au/stackup
63
+ licenses:
64
+ - MIT
65
+ metadata: {}
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project:
82
+ rubygems_version: 2.4.5.1
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: Tools for deployment to AWS
86
+ test_files: []