stack_master 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +208 -0
- data/Rakefile +11 -0
- data/apply_demo.gif +0 -0
- data/bin/stack_master +16 -0
- data/example/simple/Gemfile +3 -0
- data/example/simple/parameters/myapp_vpc.yml +1 -0
- data/example/simple/parameters/myapp_web.yml +2 -0
- data/example/simple/stack_master.yml +13 -0
- data/example/simple/templates/myapp_vpc.rb +39 -0
- data/example/simple/templates/myapp_web.rb +16 -0
- data/features/apply.feature +241 -0
- data/features/delete.feature +43 -0
- data/features/diff.feature +191 -0
- data/features/events.feature +38 -0
- data/features/init.feature +6 -0
- data/features/outputs.feature +49 -0
- data/features/region_aliases.feature +66 -0
- data/features/resources.feature +45 -0
- data/features/stack_defaults.feature +88 -0
- data/features/status.feature +124 -0
- data/features/step_definitions/stack_steps.rb +50 -0
- data/features/support/env.rb +14 -0
- data/lib/stack_master.rb +81 -0
- data/lib/stack_master/aws_driver/cloud_formation.rb +56 -0
- data/lib/stack_master/cli.rb +164 -0
- data/lib/stack_master/command.rb +13 -0
- data/lib/stack_master/commands/apply.rb +104 -0
- data/lib/stack_master/commands/delete.rb +53 -0
- data/lib/stack_master/commands/diff.rb +31 -0
- data/lib/stack_master/commands/events.rb +39 -0
- data/lib/stack_master/commands/init.rb +109 -0
- data/lib/stack_master/commands/list_stacks.rb +16 -0
- data/lib/stack_master/commands/outputs.rb +27 -0
- data/lib/stack_master/commands/resources.rb +33 -0
- data/lib/stack_master/commands/status.rb +47 -0
- data/lib/stack_master/commands/validate.rb +17 -0
- data/lib/stack_master/config.rb +86 -0
- data/lib/stack_master/ctrl_c.rb +4 -0
- data/lib/stack_master/parameter_loader.rb +17 -0
- data/lib/stack_master/parameter_resolver.rb +45 -0
- data/lib/stack_master/parameter_resolvers/secret.rb +42 -0
- data/lib/stack_master/parameter_resolvers/security_group.rb +20 -0
- data/lib/stack_master/parameter_resolvers/sns_topic_name.rb +29 -0
- data/lib/stack_master/parameter_resolvers/stack_output.rb +53 -0
- data/lib/stack_master/prompter.rb +14 -0
- data/lib/stack_master/security_group_finder.rb +29 -0
- data/lib/stack_master/sns_topic_finder.rb +27 -0
- data/lib/stack_master/stack.rb +96 -0
- data/lib/stack_master/stack_definition.rb +49 -0
- data/lib/stack_master/stack_differ.rb +80 -0
- data/lib/stack_master/stack_events/fetcher.rb +45 -0
- data/lib/stack_master/stack_events/presenter.rb +27 -0
- data/lib/stack_master/stack_events/streamer.rb +55 -0
- data/lib/stack_master/stack_states.rb +34 -0
- data/lib/stack_master/template_compiler.rb +21 -0
- data/lib/stack_master/test_driver/cloud_formation.rb +139 -0
- data/lib/stack_master/testing.rb +7 -0
- data/lib/stack_master/utils.rb +31 -0
- data/lib/stack_master/validator.rb +25 -0
- data/lib/stack_master/version.rb +3 -0
- data/logo.png +0 -0
- data/script/buildkite/bundle.sh +5 -0
- data/script/buildkite/clean.sh +3 -0
- data/script/buildkite_rspec.sh +27 -0
- data/spec/fixtures/parameters/myapp_vpc.yml +1 -0
- data/spec/fixtures/stack_master.yml +35 -0
- data/spec/fixtures/templates/myapp_vpc.json +1 -0
- data/spec/spec_helper.rb +99 -0
- data/spec/stack_master/commands/apply_spec.rb +92 -0
- data/spec/stack_master/commands/delete_spec.rb +40 -0
- data/spec/stack_master/commands/init_spec.rb +17 -0
- data/spec/stack_master/commands/status_spec.rb +38 -0
- data/spec/stack_master/commands/validate_spec.rb +26 -0
- data/spec/stack_master/config_spec.rb +81 -0
- data/spec/stack_master/parameter_loader_spec.rb +81 -0
- data/spec/stack_master/parameter_resolver_spec.rb +58 -0
- data/spec/stack_master/parameter_resolvers/secret_spec.rb +66 -0
- data/spec/stack_master/parameter_resolvers/security_group_spec.rb +17 -0
- data/spec/stack_master/parameter_resolvers/sns_topic_name_spec.rb +43 -0
- data/spec/stack_master/parameter_resolvers/stack_output_spec.rb +77 -0
- data/spec/stack_master/security_group_finder_spec.rb +49 -0
- data/spec/stack_master/sns_topic_finder_spec.rb +25 -0
- data/spec/stack_master/stack_definition_spec.rb +37 -0
- data/spec/stack_master/stack_differ_spec.rb +34 -0
- data/spec/stack_master/stack_events/fetcher_spec.rb +65 -0
- data/spec/stack_master/stack_events/presenter_spec.rb +18 -0
- data/spec/stack_master/stack_events/streamer_spec.rb +33 -0
- data/spec/stack_master/stack_spec.rb +157 -0
- data/spec/stack_master/template_compiler_spec.rb +48 -0
- data/spec/stack_master/test_driver/cloud_formation_spec.rb +24 -0
- data/spec/stack_master/utils_spec.rb +30 -0
- data/spec/stack_master/validator_spec.rb +38 -0
- data/stack_master.gemspec +38 -0
- data/stacktemplates/parameter_region.yml +3 -0
- data/stacktemplates/parameter_stack_name.yml +3 -0
- data/stacktemplates/stack.json.erb +20 -0
- data/stacktemplates/stack_master.yml.erb +6 -0
- metadata +427 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
module StackMaster
|
2
|
+
module Utils
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def hash_to_aws_parameters(params)
|
6
|
+
params.inject([]) do |params, (key, value)|
|
7
|
+
params << { parameter_key: key, parameter_value: value }
|
8
|
+
params
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def hash_to_aws_tags(tags)
|
13
|
+
return [] if tags.nil?
|
14
|
+
tags.inject([]) do |aws_tags, (key, value)|
|
15
|
+
aws_tags << { key: key, value: value }
|
16
|
+
aws_tags
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def underscore_to_hyphen(string)
|
21
|
+
string.to_s.gsub('_', '-')
|
22
|
+
end
|
23
|
+
|
24
|
+
def underscore_keys_to_hyphen(hash)
|
25
|
+
hash.inject({}) do |hash, (key, value)|
|
26
|
+
hash[underscore_to_hyphen(key)] = value
|
27
|
+
hash
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module StackMaster
|
2
|
+
class Validator
|
3
|
+
include Command
|
4
|
+
|
5
|
+
def initialize(stack_definition)
|
6
|
+
@stack_definition = stack_definition
|
7
|
+
end
|
8
|
+
|
9
|
+
def perform
|
10
|
+
template_body = TemplateCompiler.compile(@stack_definition.template_file_path)
|
11
|
+
cf.validate_template(template_body: template_body)
|
12
|
+
StackMaster.stdout.puts "Valid"
|
13
|
+
rescue Aws::CloudFormation::Errors::ValidationError => e
|
14
|
+
$stderr.puts "Validation Failed"
|
15
|
+
$stderr.puts e.message
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def cf
|
21
|
+
@cf ||= StackMaster.cloud_formation_driver
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
data/logo.png
ADDED
Binary file
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
echo
|
6
|
+
echo "--- Cleaning up working tree"
|
7
|
+
echo
|
8
|
+
|
9
|
+
time ./script/buildkite/clean.sh
|
10
|
+
|
11
|
+
echo
|
12
|
+
echo "--- Bundling"
|
13
|
+
echo
|
14
|
+
|
15
|
+
time ./script/buildkite/bundle.sh
|
16
|
+
|
17
|
+
echo
|
18
|
+
echo "+++ Running rspec"
|
19
|
+
echo
|
20
|
+
|
21
|
+
time bundle exec rspec
|
22
|
+
|
23
|
+
echo
|
24
|
+
echo "+++ Running cucumber"
|
25
|
+
echo
|
26
|
+
|
27
|
+
time bundle exec cucumber
|
@@ -0,0 +1 @@
|
|
1
|
+
param_1: 'hello'
|
@@ -0,0 +1,35 @@
|
|
1
|
+
region_aliases:
|
2
|
+
production: us-east-1
|
3
|
+
staging: ap-southeast-2
|
4
|
+
stack_defaults:
|
5
|
+
tags:
|
6
|
+
application: my-awesome-blog
|
7
|
+
region_defaults:
|
8
|
+
us_east_1:
|
9
|
+
tags:
|
10
|
+
environment: production
|
11
|
+
notification_arns:
|
12
|
+
- test_arn
|
13
|
+
secret_file: production.yml.gpg
|
14
|
+
stack_policy_file: my_policy.json
|
15
|
+
staging:
|
16
|
+
tags:
|
17
|
+
environment: staging
|
18
|
+
notification_arns:
|
19
|
+
- test_arn_3
|
20
|
+
secret_file: staging.yml.gpg
|
21
|
+
stacks:
|
22
|
+
us-east-1:
|
23
|
+
myapp_vpc:
|
24
|
+
template: myapp_vpc.json
|
25
|
+
notification_arns:
|
26
|
+
- test_arn_2
|
27
|
+
myapp_web:
|
28
|
+
template: myapp_web.rb
|
29
|
+
ap-southeast-2:
|
30
|
+
myapp_vpc:
|
31
|
+
template: myapp_vpc.rb
|
32
|
+
notification_arns:
|
33
|
+
- test_arn_4
|
34
|
+
myapp_web:
|
35
|
+
template: myapp_web
|
@@ -0,0 +1 @@
|
|
1
|
+
{}
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'stack_master'
|
2
|
+
require 'timecop'
|
3
|
+
require 'pry'
|
4
|
+
Aws.config[:stub_responses] = true
|
5
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
6
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
7
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
8
|
+
# this file to always be loaded, without a need to explicitly require it in any
|
9
|
+
# files.
|
10
|
+
#
|
11
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
12
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
13
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
14
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
15
|
+
# a separate helper file that requires the additional dependencies and performs
|
16
|
+
# the additional setup, and require it from the spec files that actually need
|
17
|
+
# it.
|
18
|
+
#
|
19
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
20
|
+
# users commonly want.
|
21
|
+
#
|
22
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
23
|
+
RSpec.configure do |config|
|
24
|
+
config.before do
|
25
|
+
StackMaster.cloud_formation_driver = nil
|
26
|
+
end
|
27
|
+
# rspec-expectations config goes here. You can use an alternate
|
28
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
29
|
+
# assertions if you prefer.
|
30
|
+
config.expect_with :rspec do |expectations|
|
31
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
32
|
+
# and `failure_message` of custom matchers include text for helper methods
|
33
|
+
# defined using `chain`, e.g.:
|
34
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
35
|
+
# # => "be bigger than 2 and smaller than 4"
|
36
|
+
# ...rather than:
|
37
|
+
# # => "be bigger than 2"
|
38
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
39
|
+
end
|
40
|
+
|
41
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
42
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
43
|
+
config.mock_with :rspec do |mocks|
|
44
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
45
|
+
# a real object. This is generally recommended, and will default to
|
46
|
+
# `true` in RSpec 4.
|
47
|
+
mocks.verify_partial_doubles = true
|
48
|
+
end
|
49
|
+
|
50
|
+
# These two settings work together to allow you to limit a spec run
|
51
|
+
# to individual examples or groups you care about by tagging them with
|
52
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
53
|
+
# get run.
|
54
|
+
config.filter_run :focus
|
55
|
+
config.run_all_when_everything_filtered = true
|
56
|
+
|
57
|
+
# Allows RSpec to persist some state between runs in order to support
|
58
|
+
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
59
|
+
# you configure your source control system to ignore this file.
|
60
|
+
config.example_status_persistence_file_path = "spec/examples.txt"
|
61
|
+
|
62
|
+
# Limits the available syntax to the non-monkey patched syntax that is
|
63
|
+
# recommended. For more details, see:
|
64
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
65
|
+
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
66
|
+
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
67
|
+
config.disable_monkey_patching!
|
68
|
+
|
69
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
70
|
+
# be too noisy due to issues in dependencies.
|
71
|
+
# config.warnings = true
|
72
|
+
|
73
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
74
|
+
# file, and it's useful to allow more verbose output when running an
|
75
|
+
# individual spec file.
|
76
|
+
if config.files_to_run.one?
|
77
|
+
# Use the documentation formatter for detailed output,
|
78
|
+
# unless a formatter has already been configured
|
79
|
+
# (e.g. via a command-line flag).
|
80
|
+
config.default_formatter = 'doc'
|
81
|
+
end
|
82
|
+
|
83
|
+
# Print the 10 slowest examples and example groups at the
|
84
|
+
# end of the spec run, to help surface which specs are running
|
85
|
+
# particularly slow.
|
86
|
+
config.profile_examples = 10
|
87
|
+
|
88
|
+
# Run specs in random order to surface order dependencies. If you find an
|
89
|
+
# order dependency and want to debug it, you can fix the order by providing
|
90
|
+
# the seed, which is printed after each run.
|
91
|
+
# --seed 1234
|
92
|
+
config.order = :random
|
93
|
+
|
94
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
95
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
96
|
+
# test failures related to randomization by passing the same `--seed` value
|
97
|
+
# as the one that triggered the failure.
|
98
|
+
Kernel.srand config.seed
|
99
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
RSpec.describe StackMaster::Commands::Apply do
|
2
|
+
let(:cf) { instance_double(Aws::CloudFormation::Client) }
|
3
|
+
let(:region) { 'us-east-1' }
|
4
|
+
let(:stack_name) { 'myapp-vpc' }
|
5
|
+
let(:config) { double(find_stack: stack_definition) }
|
6
|
+
let(:notification_arn) { 'test_arn' }
|
7
|
+
let(:stack_definition) { StackMaster::StackDefinition.new(base_dir: '/base_dir', region: region, stack_name: stack_name) }
|
8
|
+
let(:template_body) { '{}' }
|
9
|
+
let(:proposed_stack) { StackMaster::Stack.new(template_body: template_body, tags: { 'environment' => 'production' } , parameters: { 'param_1' => 'hello' }, notification_arns: [notification_arn], stack_policy_body: stack_policy_body ) }
|
10
|
+
let(:stack_policy_body) { '{}' }
|
11
|
+
|
12
|
+
before do
|
13
|
+
allow(StackMaster::Stack).to receive(:find).with(region, stack_name).and_return(stack)
|
14
|
+
allow(StackMaster::Stack).to receive(:generate).with(stack_definition, config).and_return(proposed_stack)
|
15
|
+
allow(Aws::CloudFormation::Client).to receive(:new).and_return(cf)
|
16
|
+
allow(cf).to receive(:update_stack)
|
17
|
+
allow(cf).to receive(:create_stack)
|
18
|
+
allow(StackMaster::StackDiffer).to receive(:new).with(proposed_stack, stack).and_return double.as_null_object
|
19
|
+
allow(STDOUT).to receive(:print)
|
20
|
+
allow(STDIN).to receive(:getch).and_return('y')
|
21
|
+
allow(StackMaster::StackEvents::Streamer).to receive(:stream)
|
22
|
+
end
|
23
|
+
|
24
|
+
def apply
|
25
|
+
StackMaster::Commands::Apply.perform(config, stack_definition)
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'the stack exist' do
|
29
|
+
let(:stack) { StackMaster::Stack.new(stack_id: '1') }
|
30
|
+
|
31
|
+
it 'calls the update stack API method' do
|
32
|
+
apply
|
33
|
+
expect(cf).to have_received(:update_stack).with(
|
34
|
+
stack_name: stack_name,
|
35
|
+
template_body: proposed_stack.template_body,
|
36
|
+
parameters: [
|
37
|
+
{ parameter_key: 'param_1', parameter_value: 'hello' }
|
38
|
+
],
|
39
|
+
capabilities: ['CAPABILITY_IAM'],
|
40
|
+
notification_arns: [notification_arn],
|
41
|
+
stack_policy_body: stack_policy_body
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'streams events' do
|
46
|
+
Timecop.freeze(Time.local(1990)) do
|
47
|
+
apply
|
48
|
+
expect(StackMaster::StackEvents::Streamer).to have_received(:stream).with(stack_name, region, io: STDOUT, from: Time.now)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'the stack does not exist' do
|
54
|
+
let(:stack) { nil }
|
55
|
+
|
56
|
+
it 'calls the create stack API method' do
|
57
|
+
apply
|
58
|
+
expect(cf).to have_received(:create_stack).with(
|
59
|
+
stack_name: stack_name,
|
60
|
+
template_body: proposed_stack.template_body,
|
61
|
+
parameters: [
|
62
|
+
{ parameter_key: 'param_1', parameter_value: 'hello' }
|
63
|
+
],
|
64
|
+
tags: [
|
65
|
+
{
|
66
|
+
key: 'environment',
|
67
|
+
value: 'production'
|
68
|
+
}],
|
69
|
+
capabilities: ['CAPABILITY_IAM'],
|
70
|
+
notification_arns: [notification_arn],
|
71
|
+
stack_policy_body: stack_policy_body
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'the stack is too large' do
|
76
|
+
let(:big_string) { 'x' * 60000 }
|
77
|
+
let(:template_body) do
|
78
|
+
"{\"a\":\"#{big_string}\"}"
|
79
|
+
end
|
80
|
+
it 'exits with a message' do
|
81
|
+
expect { apply }.to output(/The \(space compressed\) stack is larger than the limit set by AWS/).to_stdout
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'streams events' do
|
86
|
+
Timecop.freeze(Time.local(1990)) do
|
87
|
+
apply
|
88
|
+
expect(StackMaster::StackEvents::Streamer).to have_received(:stream).with(stack_name, region, io: STDOUT, from: Time.now)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
RSpec.describe StackMaster::Commands::Delete do
|
2
|
+
|
3
|
+
subject(:delete) { described_class.new(stack_name, region) }
|
4
|
+
let(:cf) { Aws::CloudFormation::Client.new }
|
5
|
+
let(:region) { 'us-east-1' }
|
6
|
+
let(:stack_name) { 'mystack' }
|
7
|
+
|
8
|
+
before do
|
9
|
+
StackMaster.cloud_formation_driver.set_region(region)
|
10
|
+
allow(Aws::CloudFormation::Client).to receive(:new).with(region: region).and_return(cf)
|
11
|
+
allow(delete).to receive(:ask?).and_return('y')
|
12
|
+
allow(StackMaster::StackEvents::Streamer).to receive(:stream)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#perform" do
|
16
|
+
context "The stack exists" do
|
17
|
+
before do
|
18
|
+
cf.stub_responses(:describe_stacks, stacks: [{ stack_id: "ABC", stack_name: stack_name, creation_time: Time.now, stack_status: 'UPDATE_COMPLETE', parameters: []}])
|
19
|
+
|
20
|
+
end
|
21
|
+
it "deletes the stack and tails the events" do
|
22
|
+
expect(cf).to receive(:delete_stack).with({:stack_name => region})
|
23
|
+
expect(StackMaster::StackEvents::Streamer).to receive(:stream)
|
24
|
+
delete.perform
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "The stack does not exist" do
|
29
|
+
before do
|
30
|
+
cf.stub_responses(:describe_stacks, Aws::CloudFormation::Errors::ValidationError.new("x", "y"))
|
31
|
+
end
|
32
|
+
it "raises an error" do
|
33
|
+
expect(StackMaster::StackEvents::Streamer).to_not receive(:stream)
|
34
|
+
expect(cf).to_not receive(:delete_stack)
|
35
|
+
delete.perform
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
RSpec.describe StackMaster::Commands::Init do
|
2
|
+
|
3
|
+
subject(:init_command) { described_class.new(false, region, stack_name) }
|
4
|
+
let(:region) { "us-east-1" }
|
5
|
+
let(:stack_name) { "test-stack" }
|
6
|
+
|
7
|
+
describe "#perform" do
|
8
|
+
it "creates all the expected files" do
|
9
|
+
expect(IO).to receive(:write).with("stack_master.yml", "stacks:\n us-east-1:\n test-stack:\n template: test-stack.json\n tags:\n environment: production\n")
|
10
|
+
expect(IO).to receive(:write).with("parameters/test_stack.yml", "# Add parameters here:\n# param1: value1\n# param2: value2\n")
|
11
|
+
expect(IO).to receive(:write).with("parameters/us-east-1/test_stack.yml", "# Add parameters here:\n# param1: value1\n# param2: value2\n")
|
12
|
+
expect(IO).to receive(:write).with("templates/test-stack.json", "{\n \"AWSTemplateFormatVersion\" : \"2010-09-09\",\n \"Description\" : \"Cloudformation stack for test-stack\",\n\n \"Parameters\" : {\n \"InstanceType\" : {\n \"Description\" : \"EC2 instance type\",\n \"Type\" : \"String\"\n }\n },\n\n \"Mappings\" : {\n },\n\n \"Resources\" : {\n },\n\n \"Outputs\" : {\n }\n}\n")
|
13
|
+
init_command.perform()
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
RSpec.describe StackMaster::Commands::Status do
|
2
|
+
subject(:status) { described_class.new(config) }
|
3
|
+
let(:config) { instance_double(StackMaster::Config, stacks: stacks) }
|
4
|
+
let(:stacks) { [stack_definition_1, stack_definition_2] }
|
5
|
+
let(:stack_definition_1) { double(:stack_definition_1, region: 'us-east-1', stack_name: 'stack1') }
|
6
|
+
let(:stack_definition_2) { double(:stack_definition_2, region: 'us-east-1', stack_name: 'stack2', stack_status: 'CREATE_COMPLETE') }
|
7
|
+
let(:cf) { Aws::CloudFormation::Client.new(region: 'us-east-1') }
|
8
|
+
|
9
|
+
before do
|
10
|
+
allow(Aws::CloudFormation::Client).to receive(:new).with(region: 'us-east-1').and_return cf
|
11
|
+
allow(StackMaster::Stack).to receive(:find).and_return stack1, stack2
|
12
|
+
allow(StackMaster::Stack).to receive(:generate).and_return proposed_stack1, proposed_stack2
|
13
|
+
end
|
14
|
+
|
15
|
+
context "#perform" do
|
16
|
+
context "some parameters are different" do
|
17
|
+
let(:stack1) { double(:stack1, template_hash: {}, parameters_with_defaults: {a: 1}, stack_status: 'UPDATE_COMPLETE') }
|
18
|
+
let(:stack2) { double(:stack2, template_hash: {}, parameters_with_defaults: {a: 2}, stack_status: 'CREATE_COMPLETE') }
|
19
|
+
let(:proposed_stack1) { double(:proposed_stack1, template_body: "{}", parameters_with_defaults: {a: 1}) }
|
20
|
+
let(:proposed_stack2) { double(:proposed_stack2, template_body: "{}", parameters_with_defaults: {a: 1}) }
|
21
|
+
it "returns the status of call stacks" do
|
22
|
+
out = "REGION | STACK_NAME | STACK_STATUS | DIFFERENT\n----------|------------|-----------------|----------\nus-east-1 | stack1 | UPDATE_COMPLETE | No \nus-east-1 | stack2 | CREATE_COMPLETE | Yes \n"
|
23
|
+
expect { status.perform }.to output(out).to_stdout
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "some templates are different" do
|
28
|
+
let(:stack1) { double(:stack1, template_hash: {foo: 'bar'}, parameters_with_defaults: {a: 1}, stack_status: 'UPDATE_COMPLETE') }
|
29
|
+
let(:stack2) { double(:stack2, template_hash: {}, parameters_with_defaults: {a: 1}, stack_status: 'CREATE_COMPLETE') }
|
30
|
+
let(:proposed_stack1) { double(:proposed_stack1, template_body: "{}", parameters_with_defaults: {a: 1}) }
|
31
|
+
let(:proposed_stack2) { double(:proposed_stack2, template_body: "{}", parameters_with_defaults: {a: 1}) }
|
32
|
+
it "returns the status of call stacks" do
|
33
|
+
out = "REGION | STACK_NAME | STACK_STATUS | DIFFERENT\n----------|------------|-----------------|----------\nus-east-1 | stack1 | UPDATE_COMPLETE | Yes \nus-east-1 | stack2 | CREATE_COMPLETE | No \n"
|
34
|
+
expect { status.perform }.to output(out).to_stdout
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|