stack_master 0.6.0 → 0.7.0

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +22 -0
  3. data/features/apply.feature +1 -1
  4. data/features/apply_with_s3.feature +106 -0
  5. data/features/step_definitions/stack_steps.rb +5 -0
  6. data/features/support/env.rb +1 -0
  7. data/lib/stack_master.rb +78 -58
  8. data/lib/stack_master/aws_driver/s3.rb +66 -0
  9. data/lib/stack_master/cli.rb +1 -0
  10. data/lib/stack_master/commands/apply.rb +52 -5
  11. data/lib/stack_master/commands/init.rb +2 -0
  12. data/lib/stack_master/commands/list_stacks.rb +2 -0
  13. data/lib/stack_master/commands/outputs.rb +2 -0
  14. data/lib/stack_master/commands/status.rb +3 -0
  15. data/lib/stack_master/parameter_resolver.rb +1 -1
  16. data/lib/stack_master/parameter_resolvers/secret.rb +2 -0
  17. data/lib/stack_master/stack.rb +20 -13
  18. data/lib/stack_master/stack_definition.rb +63 -13
  19. data/lib/stack_master/stack_differ.rb +2 -0
  20. data/lib/stack_master/stack_events/fetcher.rb +1 -1
  21. data/lib/stack_master/stack_events/presenter.rb +1 -1
  22. data/lib/stack_master/stack_events/streamer.rb +1 -1
  23. data/lib/stack_master/template_compiler.rb +3 -1
  24. data/lib/stack_master/template_compilers/cfndsl.rb +4 -2
  25. data/lib/stack_master/template_compilers/json.rb +4 -0
  26. data/lib/stack_master/template_compilers/sparkle_formation.rb +5 -0
  27. data/lib/stack_master/template_compilers/yaml.rb +4 -0
  28. data/lib/stack_master/test_driver/cloud_formation.rb +50 -36
  29. data/lib/stack_master/test_driver/s3.rb +34 -0
  30. data/lib/stack_master/testing.rb +1 -0
  31. data/lib/stack_master/utils.rb +19 -0
  32. data/lib/stack_master/version.rb +1 -1
  33. data/spec/fixtures/stack_master.yml +4 -1
  34. data/spec/stack_master/aws_driver/s3_spec.rb +130 -0
  35. data/spec/stack_master/commands/apply_spec.rb +26 -0
  36. data/spec/stack_master/config_spec.rb +8 -1
  37. data/spec/stack_master/parameter_resolver_spec.rb +5 -0
  38. data/spec/stack_master/sparkle_formation/user_data_file_spec.rb +2 -0
  39. data/spec/stack_master/stack_events/presenter_spec.rb +1 -1
  40. data/spec/stack_master/stack_spec.rb +23 -5
  41. data/spec/stack_master/template_compiler_spec.rb +1 -0
  42. data/spec/stack_master/template_compilers/cfndsl_spec.rb +2 -0
  43. data/spec/stack_master/test_driver/s3_spec.rb +17 -0
  44. data/stack_master.gemspec +0 -1
  45. metadata +10 -16
@@ -1,3 +1,3 @@
1
1
  module StackMaster
2
- VERSION = "0.6.0"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -4,6 +4,9 @@ region_aliases:
4
4
  stack_defaults:
5
5
  tags:
6
6
  application: my-awesome-blog
7
+ s3:
8
+ bucket: my-bucket
9
+ region: us-east-1
7
10
  template_compilers:
8
11
  rb: ruby_dsl
9
12
  region_defaults:
@@ -37,4 +40,4 @@ stacks:
37
40
  myapp_web:
38
41
  template: myapp_web
39
42
  tags:
40
- test_override: 2
43
+ test_override: 2
@@ -0,0 +1,130 @@
1
+ RSpec.describe StackMaster::AwsDriver::S3 do
2
+ let(:region) { 'us-east-1' }
3
+ let(:bucket) { 'bucket' }
4
+ let(:s3) { Aws::S3::Client.new(stub_responses: true) }
5
+ subject(:s3_driver) { StackMaster::AwsDriver::S3.new }
6
+
7
+ before do
8
+ allow(Aws::S3::Client).to receive(:new).and_return(s3)
9
+ end
10
+
11
+ describe '#upload_files' do
12
+ before do
13
+ allow(File).to receive(:read).and_return('file content')
14
+ end
15
+
16
+ context 'when set_region is called' do
17
+ it 'defaults to that region' do
18
+ s3_driver.set_region('default')
19
+ expect(Aws::S3::Client).to receive(:new).with(region: 'default').and_return(s3)
20
+ files = {
21
+ 'template' => {
22
+ path: 'spec/fixtures/templates/myapp_vpc.json',
23
+ body: 'file content'
24
+ }
25
+ }
26
+ s3_driver.upload_files(bucket: 'b',
27
+ files: files)
28
+ end
29
+ end
30
+
31
+ context 'when called with a prefix' do
32
+ let(:options) do
33
+ {
34
+ bucket: 'bucket',
35
+ region: 'region',
36
+ prefix: 'prefix',
37
+ files: {'template' => {
38
+ path: 'spec/fixtures/templates/myapp_vpc.json',
39
+ body: 'file content'
40
+ }
41
+ }
42
+ }
43
+ end
44
+
45
+ it 'uploads files under a prefix' do
46
+ expect(s3).to receive(:put_object).with(bucket: 'bucket',
47
+ key: 'prefix/template',
48
+ body: 'file content',
49
+ metadata: {md5: "d10b4c3ff123b26dc068d43a8bef2d23"})
50
+ s3_driver.upload_files(options)
51
+ end
52
+ end
53
+
54
+ context 'when called without a prefix' do
55
+ let(:options) do
56
+ {
57
+ bucket: 'bucket',
58
+ region: 'region',
59
+ files: {'template' => {
60
+ :path => 'spec/fixtures/templates/myapp_vpc.json',
61
+ :body => 'file content'
62
+ }
63
+ }
64
+ }
65
+ end
66
+
67
+ it 'uploads files under the bucket root' do
68
+ expect(s3).to receive(:put_object).with(bucket: 'bucket',
69
+ key: 'template',
70
+ body: 'file content',
71
+ metadata: {md5: "d10b4c3ff123b26dc068d43a8bef2d23"})
72
+ s3_driver.upload_files(options)
73
+ end
74
+ end
75
+
76
+ context 'when called with files in a subfolder' do
77
+ let(:options) do
78
+ {
79
+ bucket: 'bucket',
80
+ region: 'region',
81
+ prefix: 'prefix',
82
+ files: {'template' => {
83
+ :path => 'spec/fixtures/templates/myapp_vpc.json',
84
+ :body => 'file content'
85
+ }
86
+ }
87
+ }
88
+ end
89
+
90
+ it 'uploads files under the prefix' do
91
+ expect(s3).to receive(:put_object).with(bucket: 'bucket',
92
+ key: 'prefix/template',
93
+ body: 'file content',
94
+ metadata: {md5: "d10b4c3ff123b26dc068d43a8bef2d23"})
95
+ s3_driver.upload_files(options)
96
+ end
97
+ end
98
+
99
+ context 'when called with several files' do
100
+ let(:options) do
101
+ {
102
+ bucket: 'bucket',
103
+ region: 'region',
104
+ files: {
105
+ 'template1' => {
106
+ :path => 'spec/fixtures/templates/myapp_vpc.json',
107
+ :body => 'file content'
108
+ },
109
+ 'template2' => {
110
+ :path => 'spec/fixtures/templates/myapp_vpc.json',
111
+ :body => 'file content'
112
+ }
113
+ }
114
+ }
115
+ end
116
+
117
+ it 'uploads all the files' do
118
+ expect(s3).to receive(:put_object).with(bucket: 'bucket',
119
+ key: 'template1',
120
+ body: 'file content',
121
+ metadata: {md5: "d10b4c3ff123b26dc068d43a8bef2d23"})
122
+ expect(s3).to receive(:put_object).with(bucket: 'bucket',
123
+ key: 'template2',
124
+ body: 'file content',
125
+ metadata: {md5: "d10b4c3ff123b26dc068d43a8bef2d23"})
126
+ s3_driver.upload_files(options)
127
+ end
128
+ end
129
+ end
130
+ end
@@ -1,5 +1,6 @@
1
1
  RSpec.describe StackMaster::Commands::Apply do
2
2
  let(:cf) { instance_double(Aws::CloudFormation::Client) }
3
+ let(:s3) { instance_double(Aws::S3::Client) }
3
4
  let(:region) { 'us-east-1' }
4
5
  let(:stack_name) { 'myapp-vpc' }
5
6
  let(:config) { double(find_stack: stack_definition) }
@@ -13,7 +14,9 @@ RSpec.describe StackMaster::Commands::Apply do
13
14
  before do
14
15
  allow(StackMaster::Stack).to receive(:find).with(region, stack_name).and_return(stack)
15
16
  allow(StackMaster::Stack).to receive(:generate).with(stack_definition, config).and_return(proposed_stack)
17
+ allow(config).to receive(:stack_defaults).and_return({})
16
18
  allow(Aws::CloudFormation::Client).to receive(:new).and_return(cf)
19
+ allow(Aws::S3::Client).to receive(:new).and_return(s3)
17
20
  allow(cf).to receive(:create_stack)
18
21
  allow(StackMaster::StackDiffer).to receive(:new).with(proposed_stack, stack).and_return double.as_null_object
19
22
  allow(StackMaster::StackEvents::Streamer).to receive(:stream)
@@ -55,6 +58,29 @@ RSpec.describe StackMaster::Commands::Apply do
55
58
  end
56
59
  end
57
60
 
61
+ context 'when using s3' do
62
+ before do
63
+ stack_definition.s3 = {
64
+ 'bucket' => 'my-bucket',
65
+ 'prefix' => 'my-prefix',
66
+ 'region' => 'us-east-1',
67
+ }
68
+ stack_definition.template = 'my-template.rb'
69
+ allow(s3).to receive(:list_objects).and_return([])
70
+ allow(s3).to receive(:put_object)
71
+ end
72
+
73
+ it 'uploads to the correct URL' do
74
+ apply
75
+ expect(s3).to have_received(:put_object).with(
76
+ bucket: 'my-bucket',
77
+ key: 'my-prefix/my-template.json',
78
+ body: template_body,
79
+ metadata: { md5: Digest::MD5.hexdigest(template_body).to_s }
80
+ )
81
+ end
82
+ end
83
+
58
84
  context 'the changeset failed to create' do
59
85
  before do
60
86
  allow(change_set).to receive(:failed?).and_return(true)
@@ -4,9 +4,11 @@ RSpec.describe StackMaster::Config do
4
4
  let(:myapp_vpc_definition) {
5
5
  StackMaster::StackDefinition.new(
6
6
  region: 'us-east-1',
7
+ region_alias: 'production',
7
8
  stack_name: 'myapp-vpc',
8
9
  template: 'myapp_vpc.json',
9
10
  tags: { 'application' => 'my-awesome-blog', 'environment' => 'production' },
11
+ s3: { 'bucket' => 'my-bucket', 'region' => 'us-east-1' },
10
12
  notification_arns: ['test_arn', 'test_arn_2'],
11
13
  base_dir: base_dir,
12
14
  secret_file: 'production.yml.gpg',
@@ -66,7 +68,8 @@ RSpec.describe StackMaster::Config do
66
68
 
67
69
  it 'loads stack defaults' do
68
70
  expect(loaded_config.stack_defaults).to eq({
69
- 'tags' => { 'application' => 'my-awesome-blog' }
71
+ 'tags' => { 'application' => 'my-awesome-blog' },
72
+ 's3' => { 'bucket' => 'my-bucket', 'region' => 'us-east-1' }
70
73
  })
71
74
  end
72
75
 
@@ -107,11 +110,13 @@ RSpec.describe StackMaster::Config do
107
110
  expect(loaded_config.find_stack('ap-southeast-2', 'myapp-vpc')).to eq(StackMaster::StackDefinition.new(
108
111
  stack_name: 'myapp-vpc',
109
112
  region: 'ap-southeast-2',
113
+ region_alias: 'staging',
110
114
  tags: {
111
115
  'application' => 'my-awesome-blog',
112
116
  'environment' => 'staging',
113
117
  'test_override' => 1
114
118
  },
119
+ s3: { 'bucket' => 'my-bucket', 'region' => 'us-east-1' },
115
120
  notification_arns: ['test_arn_3', 'test_arn_4'],
116
121
  template: 'myapp_vpc.rb',
117
122
  base_dir: base_dir,
@@ -121,11 +126,13 @@ RSpec.describe StackMaster::Config do
121
126
  expect(loaded_config.find_stack('ap-southeast-2', 'myapp-web')).to eq(StackMaster::StackDefinition.new(
122
127
  stack_name: 'myapp-web',
123
128
  region: 'ap-southeast-2',
129
+ region_alias: 'staging',
124
130
  tags: {
125
131
  'application' => 'my-awesome-blog',
126
132
  'environment' => 'staging',
127
133
  'test_override' => 2
128
134
  },
135
+ s3: { 'bucket' => 'my-bucket', 'region' => 'us-east-1' },
129
136
  notification_arns: ['test_arn_3'],
130
137
  template: 'myapp_web',
131
138
  base_dir: base_dir,
@@ -38,6 +38,11 @@ RSpec.describe StackMaster::ParameterResolver do
38
38
  expect(resolve(param1: [1, 2])).to eq(param1: '1,2')
39
39
  end
40
40
 
41
+ it 'converts boolean values to strings' do
42
+ expect(resolve(param1: true)).to eq(param1: 'true')
43
+ expect(resolve(param1: false)).to eq(param1: 'false')
44
+ end
45
+
41
46
  it 'it throws an error when the hash contains more than one key' do
42
47
  expect {
43
48
  resolve(param: { nested1: 'value1', nested2: 'value2' })
@@ -1,3 +1,5 @@
1
+ require 'stack_master/sparkle_formation/user_data_file'
2
+
1
3
  RSpec.describe SparkleFormation::SparkleAttribute::Aws, '#user_data_file!' do
2
4
  let(:user_data) do
3
5
  <<-EOS
@@ -12,7 +12,7 @@ RSpec.describe StackMaster::StackEvents::Presenter do
12
12
  subject(:print_event) { described_class.print_event($stdout, event) }
13
13
 
14
14
  it "nicely presents event data" do
15
- expect { print_event }.to output("\e[0;33;49m2001-01-01 02:02:02 #{Time.now.strftime('%z')} MyAwesomeQueue AWS::SQS::Queue CREATE_IN_PROGRESS Resource creation Initiated\e[0m\n").to_stdout
15
+ expect { print_event }.to output("\e[0;33;49m2001-01-01 02:02:02 #{time.strftime('%z')} MyAwesomeQueue AWS::SQS::Queue CREATE_IN_PROGRESS Resource creation Initiated\e[0m\n").to_stdout
16
16
  end
17
17
  end
18
18
  end
@@ -144,14 +144,32 @@ RSpec.describe StackMaster::Stack do
144
144
  end
145
145
 
146
146
  describe '#too_big?' do
147
- let(:big_stack) { described_class.new(template_body: "{\"a\":\"#{'x' * 60000}\"}") }
147
+ let(:big_stack) { described_class.new(template_body: "{\"a\":\"#{'x' * 500000}\"}") }
148
+ let(:medium_stack) { described_class.new(template_body: "{\"a\":\"#{'x' * 60000}\"}") }
148
149
  let(:little_stack) { described_class.new(template_body: "{\"a\":\"#{'x' * 1000}\"}") }
149
150
 
150
- it 'returns true for big stacks' do
151
- expect(big_stack.too_big?).to be_truthy
151
+ context 'when not using S3' do
152
+ it 'returns true for big stacks' do
153
+ expect(big_stack.too_big?).to be_truthy
154
+ end
155
+ it 'returns true for medium stacks' do
156
+ expect(medium_stack.too_big?).to be_truthy
157
+ end
158
+ it 'returns false for small stacks' do
159
+ expect(little_stack.too_big?).to be_falsey
160
+ end
152
161
  end
153
- it 'returns false for small stacks' do
154
- expect(little_stack.too_big?).to be_falsey
162
+
163
+ context 'when using S3' do
164
+ it 'returns true for big stacks' do
165
+ expect(big_stack.too_big?(true)).to be_truthy
166
+ end
167
+ it 'returns false for medium stacks' do
168
+ expect(medium_stack.too_big?(true)).to be_falsey
169
+ end
170
+ it 'returns false for small stacks' do
171
+ expect(little_stack.too_big?(true)).to be_falsey
172
+ end
155
173
  end
156
174
  end
157
175
 
@@ -4,6 +4,7 @@ RSpec.describe StackMaster::TemplateCompiler do
4
4
  let(:template_file_path) { '/base_dir/templates/template.fab' }
5
5
 
6
6
  class TestTemplateCompiler
7
+ def self.require_dependencies; end
7
8
  def self.compile(template_file_path); end
8
9
  end
9
10
 
@@ -1,4 +1,6 @@
1
1
  RSpec.describe StackMaster::TemplateCompilers::Cfndsl do
2
+ before(:all) { described_class.require_dependencies }
3
+
2
4
  describe '.compile' do
3
5
  def compile
4
6
  described_class.compile(template_file_path)
@@ -0,0 +1,17 @@
1
+ RSpec.describe StackMaster::TestDriver::S3 do
2
+ subject(:s3_driver) { described_class.new }
3
+ let(:bucket) { 'test-bucket' }
4
+ let(:prefix) { 'test-prefix' }
5
+ let(:files) { { 'test-file' => { path: 'path', body: 'body' } } }
6
+ let(:region) { 'us-east-1' }
7
+
8
+ it 'uploads and finds files' do
9
+ s3_driver.upload_files(bucket: bucket,
10
+ prefix: prefix,
11
+ region: region,
12
+ files: files)
13
+ file = s3_driver.find_file(bucket: bucket,
14
+ object_key: [prefix, 'test-file'].compact.join('/'))
15
+ expect(file).to be
16
+ end
17
+ end
data/stack_master.gemspec CHANGED
@@ -27,7 +27,6 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency "timecop"
28
28
  spec.add_dependency "ruby-progressbar"
29
29
  spec.add_dependency "commander"
30
- spec.add_dependency "virtus"
31
30
  spec.add_dependency "aws-sdk", "~> 2.2.31"
32
31
  spec.add_dependency "diffy"
33
32
  spec.add_dependency "erubis"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stack_master
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Hodgkiss
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-07-15 00:00:00.000000000 Z
12
+ date: 2016-07-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -137,20 +137,6 @@ dependencies:
137
137
  - - ">="
138
138
  - !ruby/object:Gem::Version
139
139
  version: '0'
140
- - !ruby/object:Gem::Dependency
141
- name: virtus
142
- requirement: !ruby/object:Gem::Requirement
143
- requirements:
144
- - - ">="
145
- - !ruby/object:Gem::Version
146
- version: '0'
147
- type: :runtime
148
- prerelease: false
149
- version_requirements: !ruby/object:Gem::Requirement
150
- requirements:
151
- - - ">="
152
- - !ruby/object:Gem::Version
153
- version: '0'
154
140
  - !ruby/object:Gem::Dependency
155
141
  name: aws-sdk
156
142
  requirement: !ruby/object:Gem::Requirement
@@ -316,6 +302,7 @@ files:
316
302
  - example/simple/templates/myapp_vpc.rb
317
303
  - example/simple/templates/myapp_web.rb
318
304
  - features/apply.feature
305
+ - features/apply_with_s3.feature
319
306
  - features/delete.feature
320
307
  - features/diff.feature
321
308
  - features/events.feature
@@ -330,6 +317,7 @@ files:
330
317
  - features/validate.feature
331
318
  - lib/stack_master.rb
332
319
  - lib/stack_master/aws_driver/cloud_formation.rb
320
+ - lib/stack_master/aws_driver/s3.rb
333
321
  - lib/stack_master/change_set.rb
334
322
  - lib/stack_master/cli.rb
335
323
  - lib/stack_master/command.rb
@@ -375,6 +363,7 @@ files:
375
363
  - lib/stack_master/template_compilers/sparkle_formation.rb
376
364
  - lib/stack_master/template_compilers/yaml.rb
377
365
  - lib/stack_master/test_driver/cloud_formation.rb
366
+ - lib/stack_master/test_driver/s3.rb
378
367
  - lib/stack_master/testing.rb
379
368
  - lib/stack_master/utils.rb
380
369
  - lib/stack_master/validator.rb
@@ -392,6 +381,7 @@ files:
392
381
  - spec/fixtures/templates/yml/valid_myapp_vpc.yml
393
382
  - spec/fixtures/test/.gitkeep
394
383
  - spec/spec_helper.rb
384
+ - spec/stack_master/aws_driver/s3_spec.rb
395
385
  - spec/stack_master/change_set_spec.rb
396
386
  - spec/stack_master/command_spec.rb
397
387
  - spec/stack_master/commands/apply_spec.rb
@@ -428,6 +418,7 @@ files:
428
418
  - spec/stack_master/template_compilers/sparkle_formation_spec.rb
429
419
  - spec/stack_master/template_compilers/yaml_spec.rb
430
420
  - spec/stack_master/test_driver/cloud_formation_spec.rb
421
+ - spec/stack_master/test_driver/s3_spec.rb
431
422
  - spec/stack_master/utils_spec.rb
432
423
  - spec/stack_master/validator_spec.rb
433
424
  - spec/stack_master_spec.rb
@@ -463,6 +454,7 @@ summary: StackMaster is a sure-footed way of creating, updating and keeping trac
463
454
  of Amazon (AWS) CloudFormation stacks.
464
455
  test_files:
465
456
  - features/apply.feature
457
+ - features/apply_with_s3.feature
466
458
  - features/delete.feature
467
459
  - features/diff.feature
468
460
  - features/events.feature
@@ -484,6 +476,7 @@ test_files:
484
476
  - spec/fixtures/templates/yml/valid_myapp_vpc.yml
485
477
  - spec/fixtures/test/.gitkeep
486
478
  - spec/spec_helper.rb
479
+ - spec/stack_master/aws_driver/s3_spec.rb
487
480
  - spec/stack_master/change_set_spec.rb
488
481
  - spec/stack_master/command_spec.rb
489
482
  - spec/stack_master/commands/apply_spec.rb
@@ -520,6 +513,7 @@ test_files:
520
513
  - spec/stack_master/template_compilers/sparkle_formation_spec.rb
521
514
  - spec/stack_master/template_compilers/yaml_spec.rb
522
515
  - spec/stack_master/test_driver/cloud_formation_spec.rb
516
+ - spec/stack_master/test_driver/s3_spec.rb
523
517
  - spec/stack_master/utils_spec.rb
524
518
  - spec/stack_master/validator_spec.rb
525
519
  - spec/stack_master_spec.rb