stackup 0.0.1

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 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: []