stack_master 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.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +208 -0
  7. data/Rakefile +11 -0
  8. data/apply_demo.gif +0 -0
  9. data/bin/stack_master +16 -0
  10. data/example/simple/Gemfile +3 -0
  11. data/example/simple/parameters/myapp_vpc.yml +1 -0
  12. data/example/simple/parameters/myapp_web.yml +2 -0
  13. data/example/simple/stack_master.yml +13 -0
  14. data/example/simple/templates/myapp_vpc.rb +39 -0
  15. data/example/simple/templates/myapp_web.rb +16 -0
  16. data/features/apply.feature +241 -0
  17. data/features/delete.feature +43 -0
  18. data/features/diff.feature +191 -0
  19. data/features/events.feature +38 -0
  20. data/features/init.feature +6 -0
  21. data/features/outputs.feature +49 -0
  22. data/features/region_aliases.feature +66 -0
  23. data/features/resources.feature +45 -0
  24. data/features/stack_defaults.feature +88 -0
  25. data/features/status.feature +124 -0
  26. data/features/step_definitions/stack_steps.rb +50 -0
  27. data/features/support/env.rb +14 -0
  28. data/lib/stack_master.rb +81 -0
  29. data/lib/stack_master/aws_driver/cloud_formation.rb +56 -0
  30. data/lib/stack_master/cli.rb +164 -0
  31. data/lib/stack_master/command.rb +13 -0
  32. data/lib/stack_master/commands/apply.rb +104 -0
  33. data/lib/stack_master/commands/delete.rb +53 -0
  34. data/lib/stack_master/commands/diff.rb +31 -0
  35. data/lib/stack_master/commands/events.rb +39 -0
  36. data/lib/stack_master/commands/init.rb +109 -0
  37. data/lib/stack_master/commands/list_stacks.rb +16 -0
  38. data/lib/stack_master/commands/outputs.rb +27 -0
  39. data/lib/stack_master/commands/resources.rb +33 -0
  40. data/lib/stack_master/commands/status.rb +47 -0
  41. data/lib/stack_master/commands/validate.rb +17 -0
  42. data/lib/stack_master/config.rb +86 -0
  43. data/lib/stack_master/ctrl_c.rb +4 -0
  44. data/lib/stack_master/parameter_loader.rb +17 -0
  45. data/lib/stack_master/parameter_resolver.rb +45 -0
  46. data/lib/stack_master/parameter_resolvers/secret.rb +42 -0
  47. data/lib/stack_master/parameter_resolvers/security_group.rb +20 -0
  48. data/lib/stack_master/parameter_resolvers/sns_topic_name.rb +29 -0
  49. data/lib/stack_master/parameter_resolvers/stack_output.rb +53 -0
  50. data/lib/stack_master/prompter.rb +14 -0
  51. data/lib/stack_master/security_group_finder.rb +29 -0
  52. data/lib/stack_master/sns_topic_finder.rb +27 -0
  53. data/lib/stack_master/stack.rb +96 -0
  54. data/lib/stack_master/stack_definition.rb +49 -0
  55. data/lib/stack_master/stack_differ.rb +80 -0
  56. data/lib/stack_master/stack_events/fetcher.rb +45 -0
  57. data/lib/stack_master/stack_events/presenter.rb +27 -0
  58. data/lib/stack_master/stack_events/streamer.rb +55 -0
  59. data/lib/stack_master/stack_states.rb +34 -0
  60. data/lib/stack_master/template_compiler.rb +21 -0
  61. data/lib/stack_master/test_driver/cloud_formation.rb +139 -0
  62. data/lib/stack_master/testing.rb +7 -0
  63. data/lib/stack_master/utils.rb +31 -0
  64. data/lib/stack_master/validator.rb +25 -0
  65. data/lib/stack_master/version.rb +3 -0
  66. data/logo.png +0 -0
  67. data/script/buildkite/bundle.sh +5 -0
  68. data/script/buildkite/clean.sh +3 -0
  69. data/script/buildkite_rspec.sh +27 -0
  70. data/spec/fixtures/parameters/myapp_vpc.yml +1 -0
  71. data/spec/fixtures/stack_master.yml +35 -0
  72. data/spec/fixtures/templates/myapp_vpc.json +1 -0
  73. data/spec/spec_helper.rb +99 -0
  74. data/spec/stack_master/commands/apply_spec.rb +92 -0
  75. data/spec/stack_master/commands/delete_spec.rb +40 -0
  76. data/spec/stack_master/commands/init_spec.rb +17 -0
  77. data/spec/stack_master/commands/status_spec.rb +38 -0
  78. data/spec/stack_master/commands/validate_spec.rb +26 -0
  79. data/spec/stack_master/config_spec.rb +81 -0
  80. data/spec/stack_master/parameter_loader_spec.rb +81 -0
  81. data/spec/stack_master/parameter_resolver_spec.rb +58 -0
  82. data/spec/stack_master/parameter_resolvers/secret_spec.rb +66 -0
  83. data/spec/stack_master/parameter_resolvers/security_group_spec.rb +17 -0
  84. data/spec/stack_master/parameter_resolvers/sns_topic_name_spec.rb +43 -0
  85. data/spec/stack_master/parameter_resolvers/stack_output_spec.rb +77 -0
  86. data/spec/stack_master/security_group_finder_spec.rb +49 -0
  87. data/spec/stack_master/sns_topic_finder_spec.rb +25 -0
  88. data/spec/stack_master/stack_definition_spec.rb +37 -0
  89. data/spec/stack_master/stack_differ_spec.rb +34 -0
  90. data/spec/stack_master/stack_events/fetcher_spec.rb +65 -0
  91. data/spec/stack_master/stack_events/presenter_spec.rb +18 -0
  92. data/spec/stack_master/stack_events/streamer_spec.rb +33 -0
  93. data/spec/stack_master/stack_spec.rb +157 -0
  94. data/spec/stack_master/template_compiler_spec.rb +48 -0
  95. data/spec/stack_master/test_driver/cloud_formation_spec.rb +24 -0
  96. data/spec/stack_master/utils_spec.rb +30 -0
  97. data/spec/stack_master/validator_spec.rb +38 -0
  98. data/stack_master.gemspec +38 -0
  99. data/stacktemplates/parameter_region.yml +3 -0
  100. data/stacktemplates/parameter_stack_name.yml +3 -0
  101. data/stacktemplates/stack.json.erb +20 -0
  102. data/stacktemplates/stack_master.yml.erb +6 -0
  103. metadata +427 -0
@@ -0,0 +1,49 @@
1
+ RSpec.describe StackMaster::SecurityGroupFinder do
2
+ subject(:finder) { described_class.new(region) }
3
+ let(:region) { 'us-east-1' }
4
+ let(:group_name) { "our-api-BeanstalkSg-T4RKD99YOY2F" }
5
+ let(:filter) do
6
+ {
7
+ filters: [
8
+ {
9
+ name: "group-name",
10
+ values: [group_name],
11
+ },
12
+ ],
13
+ }
14
+ end
15
+
16
+ describe "#find" do
17
+ before do
18
+ allow_any_instance_of(Aws::EC2::Resource).to receive(:security_groups).with(filter).and_return(security_groups)
19
+ end
20
+
21
+ context "one sg match" do
22
+ let(:security_groups) { [
23
+ double(id: 'sg-a7d2ccc0')
24
+ ] }
25
+ it "returns the id" do
26
+ expect(finder.find(group_name)).to eq 'sg-a7d2ccc0'
27
+ end
28
+ end
29
+
30
+ context "more than one sg matches" do
31
+ let(:security_groups) { [
32
+ double(id: 'sg-a7d2ccc0'),
33
+ double(id: 'sg-a7d2ccc2'),
34
+ ] }
35
+ it "returns the id" do
36
+ err = StackMaster::SecurityGroupFinder::MultipleSecurityGroupsFound
37
+ expect { finder.find(group_name) }.to raise_error(err)
38
+ end
39
+ end
40
+
41
+ context "no matches" do
42
+ let(:security_groups) { [] }
43
+ it "returns the id" do
44
+ err = StackMaster::SecurityGroupFinder::SecurityGroupNotFound
45
+ expect { finder.find(group_name) }.to raise_error(err)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,25 @@
1
+ RSpec.describe StackMaster::SnsTopicFinder do
2
+
3
+ subject(:finder) { described_class.new(region) }
4
+ let(:region) { 'us-east-1' }
5
+ let(:topics) do
6
+ [
7
+ double(arn: 'arn:aws:sns:us-east-1:581634149801:topic1name'),
8
+ double(arn: 'arn:aws:sns:us-east-1:581634149801:topic2name'),
9
+ ]
10
+ end
11
+ before do
12
+ allow_any_instance_of(Aws::SNS::Resource).to receive(:topics).and_return topics
13
+ end
14
+
15
+ describe '#find' do
16
+ it 'finds the topics that exist' do
17
+ expect(finder.find('topic1name')).to eq 'arn:aws:sns:us-east-1:581634149801:topic1name'
18
+ expect(finder.find('topic2name')).to eq 'arn:aws:sns:us-east-1:581634149801:topic2name'
19
+ end
20
+
21
+ it 'raises an exception for topics that do not exist' do
22
+ expect { finder.find('unknowntopics') }.to raise_error StackMaster::SnsTopicFinder::TopicNotFound
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,37 @@
1
+ RSpec.describe StackMaster::StackDefinition do
2
+ subject(:stack_definition) do
3
+ StackMaster::StackDefinition.new(
4
+ region: region,
5
+ stack_name: stack_name,
6
+ template: template,
7
+ tags: tags,
8
+ base_dir: base_dir)
9
+ end
10
+
11
+ let(:region) { 'us-east-1' }
12
+ let(:stack_name) { 'stack_name' }
13
+ let(:template) { 'template.json' }
14
+ let(:tags) { {'environment' => 'production'} }
15
+ let(:base_dir) { '/base_dir' }
16
+
17
+ it 'has default and region specific parameter file locations' do
18
+ expect(stack_definition.parameter_files).to eq([
19
+ "/base_dir/parameters/#{stack_name}.yml",
20
+ "/base_dir/parameters/#{region}/#{stack_name}.yml"
21
+ ])
22
+ end
23
+
24
+ context 'with additional parameter lookup dirs' do
25
+ before do
26
+ stack_definition.send(:additional_parameter_lookup_dirs=, ['production'])
27
+ end
28
+
29
+ it 'includes a parameter lookup dir for it' do
30
+ expect(stack_definition.parameter_files).to eq([
31
+ "/base_dir/parameters/#{stack_name}.yml",
32
+ "/base_dir/parameters/#{region}/#{stack_name}.yml",
33
+ "/base_dir/parameters/production/#{stack_name}.yml"
34
+ ])
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,34 @@
1
+ RSpec.describe StackMaster::StackDiffer do
2
+ subject(:differ) { described_class.new(proposed_stack, stack) }
3
+ let(:stack) { StackMaster::Stack.new(stack_name: stack_name,
4
+ region: region,
5
+ stack_id: 123,
6
+ template_body: '{}',
7
+ parameters: {}) }
8
+ let(:proposed_stack) { StackMaster::Stack.new(stack_name: stack_name,
9
+ region: region,
10
+ parameters: { 'param1' => 'hello'},
11
+ template_body: "{\"a\": 1}") }
12
+ let(:stack_name) { 'myapp-vpc' }
13
+ let(:region) { 'us-east-1' }
14
+
15
+ describe "#output_diff" do
16
+ context "entirely new stack" do
17
+ let(:stack) { nil }
18
+
19
+ it "outputs the entire stack" do
20
+ expect { differ.output_diff }.to output(/\+ \"a\"\: 1/).to_stdout
21
+ expect { differ.output_diff }.to output(/param1\: hello/).to_stdout
22
+ expect { differ.output_diff }.to output(/No stack found/).to_stdout
23
+ end
24
+ end
25
+
26
+ context "stack update" do
27
+ it "outputs the stack diff" do
28
+ expect { differ.output_diff }.to output(/\+ \"a\"\: 1/).to_stdout
29
+ expect { differ.output_diff }.to output(/param1\: hello/).to_stdout
30
+ expect { differ.output_diff }.to_not output(/No stack found/).to_stdout
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,65 @@
1
+ RSpec.describe StackMaster::StackEvents::Fetcher do
2
+ let(:cf) { Aws::CloudFormation::Client.new }
3
+
4
+ before do
5
+ allow(Aws::CloudFormation::Client).to receive(:new).and_return(cf)
6
+ allow(StackMaster::StackEvents::Streamer).to receive(:stream)
7
+ end
8
+
9
+ context 'with 2 stack events' do
10
+ let(:events) { [
11
+ { event_id: '1', stack_id: '1', stack_name: 'blah', timestamp: Time.now},
12
+ { event_id: '2', stack_id: '1', stack_name: 'blah', timestamp: Time.now}
13
+ ] }
14
+
15
+ before do
16
+ cf.stub_responses(:describe_stack_events, stack_events: events)
17
+ end
18
+
19
+ it 'returns stack events' do
20
+ events = StackMaster::StackEvents::Fetcher.fetch('blah', 'us-east-1')
21
+ expect(events.count).to eq 2
22
+ end
23
+ end
24
+
25
+ context 'when more than one page is present' do
26
+ let(:page_one_events) { [
27
+ { event_id: '1', stack_id: '1', stack_name: 'blah', timestamp: Time.now},
28
+ { event_id: '2', stack_id: '1', stack_name: 'blah', timestamp: Time.now}
29
+ ] }
30
+ let(:page_two_events) { [
31
+ { event_id: '3', stack_id: '1', stack_name: 'blah', timestamp: Time.now}
32
+ ] }
33
+
34
+ before do
35
+ cf.stub_responses(:describe_stack_events, { stack_events: page_one_events, next_token: 'blah' }, { stack_events: page_two_events } )
36
+ end
37
+
38
+ it 'returns all the stack events combined' do
39
+ events = StackMaster::StackEvents::Fetcher.fetch('blah', 'us-east-1')
40
+ expect(events.count).to eq 3
41
+ end
42
+ end
43
+
44
+ context 'filtering with a from timestamp' do
45
+ let(:two_pm) { Time.parse('2015-10-27 14:00') }
46
+ let(:three_pm) { Time.parse('2015-10-27 15:00') }
47
+ let(:four_pm) { Time.parse('2015-10-27 16:00') }
48
+
49
+ let(:events) {
50
+ [
51
+ { event_id: '1', stack_id: '1', stack_name: 'blah', timestamp: two_pm },
52
+ { event_id: '2', stack_id: '1', stack_name: 'blah', timestamp: four_pm },
53
+ ]
54
+ }
55
+
56
+ before do
57
+ cf.stub_responses(:describe_stack_events, stack_events: events)
58
+ end
59
+
60
+ it 'only returns events after the timestamp' do
61
+ events = StackMaster::StackEvents::Fetcher.fetch('blah', 'us-east-1', from: three_pm)
62
+ expect(events.map(&:event_id)).to eq ['2']
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,18 @@
1
+ RSpec.describe StackMaster::StackEvents::Presenter do
2
+ describe "#print_event" do
3
+ let(:time) { Time.new(2001,1,1,2,2,2) }
4
+ let(:event) do
5
+ double(:event,
6
+ timestamp: time,
7
+ logical_resource_id: 'MyAwesomeQueue',
8
+ resource_type: 'AWS::SQS::Queue',
9
+ resource_status: 'CREATE_IN_PROGRESS',
10
+ resource_status_reason: 'Resource creation Initiated')
11
+ end
12
+ subject(:print_event) { described_class.print_event($stdout, event) }
13
+
14
+ it "nicely presents event data" do
15
+ expect { print_event }.to output("\e[0;33;49m2001-01-01 02:02:02 +1100 MyAwesomeQueue AWS::SQS::Queue CREATE_IN_PROGRESS Resource creation Initiated\e[0m\n").to_stdout
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,33 @@
1
+ RSpec.describe StackMaster::StackEvents::Streamer do
2
+ let(:events_first_call) { [
3
+ OpenStruct.new(event_id: '1', resource_status: 'BLAH', timestamp: Time.now),
4
+ OpenStruct.new(event_id: '2', resource_status: 'BLAH', timestamp: Time.now),
5
+ OpenStruct.new(event_id: '3', resource_status: 'BLAH', timestamp: Time.now),
6
+ ] }
7
+ let(:events_second_call) {
8
+ events_first_call + [
9
+ OpenStruct.new(event_id: '4', resource_status: 'UPDATE_COMPLETE', resource_type: 'AWS::CloudFormation::Stack', logical_resource_id: stack_name, timestamp: Time.now)
10
+ ]
11
+ }
12
+ let(:stack_name) { 'stack-name' }
13
+ let(:region) { 'us-east-1' }
14
+ let(:now) { Time.now }
15
+
16
+ before do
17
+ allow(StackMaster::StackEvents::Fetcher).to receive(:fetch).with(stack_name, region, from: now).and_return(events_first_call, events_second_call)
18
+ allow(Time).to receive(:now).and_return(now)
19
+ end
20
+
21
+ it 'returns after seeing a finish state' do
22
+ events = []
23
+ StackMaster::StackEvents::Streamer.stream(stack_name, region, sleep_between_fetches: 0) do |event|
24
+ events << event
25
+ end
26
+ end
27
+
28
+ it 'streams events to an io object' do
29
+ io = StringIO.new
30
+ StackMaster::StackEvents::Streamer.stream(stack_name, region, sleep_between_fetches: 0, io: io)
31
+ expect(io.string).to include('UPDATE_COMPLETE')
32
+ end
33
+ end
@@ -0,0 +1,157 @@
1
+ RSpec.describe StackMaster::Stack do
2
+ let(:region) { 'us-east-1' }
3
+ let(:stack_name) { 'myapp_vpc' }
4
+ let(:stack_id) { '1' }
5
+ let(:stack_policy_body) { '{}' }
6
+ let(:cf) { Aws::CloudFormation::Client.new }
7
+ subject(:stack) { StackMaster::Stack.find(region, stack_name) }
8
+
9
+ before do
10
+ allow(Aws::CloudFormation::Client).to receive(:new).and_return(cf)
11
+ end
12
+
13
+ describe '.find' do
14
+ context 'when the stack exists in AWS' do
15
+ let(:parameters) {
16
+ [
17
+ {parameter_key: 'param1', parameter_value: 'value1'},
18
+ {parameter_key: 'param2', parameter_value: 'value2'}
19
+ ]
20
+ }
21
+ before do
22
+ cf.stub_responses(:describe_stacks, stacks: [{ stack_id: stack_id, stack_name: stack_name, creation_time: Time.now, stack_status: 'UPDATE_COMPLETE', parameters: parameters, notification_arns: ['test_arn']}])
23
+ cf.stub_responses(:get_template, template_body: "{}")
24
+ cf.stub_responses(:get_stack_policy, stack_policy_body: stack_policy_body)
25
+ end
26
+
27
+ it 'returns a stack object with a stack_id' do
28
+ expect(stack.stack_id).to eq stack_id
29
+ end
30
+
31
+ it "returns a template body" do
32
+ expect(stack.template_body).to eq "{}"
33
+ end
34
+
35
+ it 'parses parameters into a hash' do
36
+ expect(stack.parameters).to eq({'param1' => 'value1', 'param2' => 'value2'})
37
+ end
38
+
39
+ it 'sets notification_arns' do
40
+ expect(stack.notification_arns).to eq(['test_arn'])
41
+ end
42
+
43
+ it 'sets the stack policy' do
44
+ expect(stack.stack_policy_body).to eq stack_policy_body
45
+ end
46
+ end
47
+
48
+ context 'when the stack does not exist in AWS' do
49
+ before do
50
+ cf.stub_responses(:describe_stacks, Aws::CloudFormation::Errors::ValidationError.new('a', 'b'))
51
+ end
52
+
53
+ it 'returns nil' do
54
+ stack = StackMaster::Stack.find(region, stack_name)
55
+ expect(stack).to be_nil
56
+ end
57
+ end
58
+
59
+ context 'when CF returns no stacks' do
60
+ before do
61
+ cf.stub_responses(:describe_stacks, stacks: [])
62
+ end
63
+
64
+ it 'returns nil' do
65
+ stack = StackMaster::Stack.find(region, stack_name)
66
+ expect(stack).to be_nil
67
+ end
68
+ end
69
+ end
70
+
71
+ describe '.generate' do
72
+ let(:tags) { { 'tag1' => 'value1' } }
73
+ let(:stack_definition) { StackMaster::StackDefinition.new(region: region, stack_name: stack_name, tags: tags, base_dir: '/base_dir', template: template_file_name, notification_arns: ['test_arn'], stack_policy_file: 'no_replace_rds.json') }
74
+ let(:config) { StackMaster::Config.new({'stacks' => {}}, '/base_dir') }
75
+ subject(:stack) { StackMaster::Stack.generate(stack_definition, config) }
76
+ let(:parameter_hash) { { 'DbPassword' => { 'secret' => 'db_password' } } }
77
+ let(:resolved_parameters) { { 'DbPassword' => 'sdfgjkdhlfjkghdflkjghdflkjg', 'InstanceType' => 't2.medium' } }
78
+ let(:template_file_name) { 'template.rb' }
79
+ let(:template_body) { '{"Parameters": { "VpcId": { "Description": "VPC ID" }, "InstanceType": { "Description": "Instance Type", "Default": "t2.micro" }} }' }
80
+ let(:stack_policy_body) { '{}' }
81
+
82
+ before do
83
+ allow(StackMaster::ParameterLoader).to receive(:load).and_return(parameter_hash)
84
+ allow(StackMaster::ParameterResolver).to receive(:resolve).and_return(resolved_parameters)
85
+ allow(StackMaster::TemplateCompiler).to receive(:compile).with(stack_definition.template_file_path).and_return(template_body)
86
+ allow(File).to receive(:read).with(stack_definition.stack_policy_file_path).and_return(stack_policy_body)
87
+ end
88
+
89
+ it 'has the stack definitions region' do
90
+ expect(stack.region).to eq region
91
+ end
92
+
93
+ it 'has the stack definitions name' do
94
+ expect(stack.stack_name).to eq stack_name
95
+ end
96
+
97
+ it 'has the stack definitions tags' do
98
+ expect(stack.tags).to eq tags
99
+ end
100
+
101
+ it 'resolves the parameters' do
102
+ expect(stack.parameters).to eq resolved_parameters
103
+ end
104
+
105
+ it 'compiles the template body' do
106
+ expect(stack.template_body).to eq template_body
107
+ end
108
+
109
+ it 'has notification_arns' do
110
+ expect(stack.notification_arns).to eq ['test_arn']
111
+ end
112
+
113
+ it 'has the stack policy' do
114
+ expect(stack.stack_policy_body).to eq stack_policy_body
115
+ end
116
+
117
+ it 'extracts default template parameters' do
118
+ expect(stack.template_default_parameters).to eq('VpcId' => nil, 'InstanceType' => 't2.micro')
119
+ end
120
+
121
+ it 'exposes parameters with defaults taken into account' do
122
+ expect(stack.parameters_with_defaults).to eq('DbPassword' => 'sdfgjkdhlfjkghdflkjghdflkjg', 'InstanceType' => 't2.medium', 'VpcId' => nil)
123
+ end
124
+ end
125
+
126
+ describe "#maybe_compressed_template_body" do
127
+ subject(:maybe_compressed_template_body) do
128
+ stack.maybe_compressed_template_body
129
+ end
130
+ context "undersized json" do
131
+ let(:stack) { described_class.new(template_body: '{ }' ) }
132
+
133
+ it "leaves the json alone if it's not too large" do
134
+ expect(maybe_compressed_template_body).to eq('{ }')
135
+ end
136
+ end
137
+
138
+ context "oversized json" do
139
+ let(:stack) { described_class.new(template_body: "{#{' ' * 60000}}" ) }
140
+ it "compresses the json when it's overly bulbous" do
141
+ expect(maybe_compressed_template_body).to eq('{}')
142
+ end
143
+ end
144
+ end
145
+
146
+ describe '#too_big?' do
147
+ let(:big_stack) { described_class.new(template_body: "{\"a\":\"#{'x' * 60000}\"}") }
148
+ let(:little_stack) { described_class.new(template_body: "{\"a\":\"#{'x' * 1000}\"}") }
149
+
150
+ it 'returns true for big stacks' do
151
+ expect(big_stack.too_big?).to be_truthy
152
+ end
153
+ it 'returns false for small stacks' do
154
+ expect(little_stack.too_big?).to be_falsey
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,48 @@
1
+ RSpec.describe StackMaster::TemplateCompiler do
2
+ describe ".compile" do
3
+ def compile
4
+ StackMaster::TemplateCompiler.compile(template_file_path)
5
+ end
6
+
7
+ context 'json template' do
8
+ let(:template_file_path) { '/base_dir/templates/template.json' }
9
+
10
+ context "small json template" do
11
+ before do
12
+ allow(File).to receive(:read).with(template_file_path).and_return('{ }')
13
+ end
14
+
15
+ it "reads from the template file path" do
16
+ expect(compile).to eq('{ }')
17
+ end
18
+ end
19
+
20
+ context 'extra big json template' do
21
+ before do
22
+ allow(File).to receive(:read).with(template_file_path).and_return("{ #{' ' * 60000} }")
23
+ end
24
+
25
+ it "reads from the template file path" do
26
+ expect(compile).to eq('{}')
27
+ end
28
+ end
29
+ end
30
+
31
+ context 'sparkleformation template' do
32
+ let(:template_file_path) { '/base_dir/templates/template.rb' }
33
+
34
+ before do
35
+ allow(SparkleFormation).to receive(:compile).with(template_file_path).and_return({})
36
+ end
37
+
38
+ it 'compiles with sparkleformation' do
39
+ expect(compile).to eq("{\n}")
40
+ end
41
+
42
+ it 'sets the appropriate sparkle_path' do
43
+ compile
44
+ expect(SparkleFormation.sparkle_path).to eq File.dirname(template_file_path)
45
+ end
46
+ end
47
+ end
48
+ end