stack_master 0.10.1 → 0.10.2
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 +4 -4
- data/lib/stack_master.rb +1 -0
- data/lib/stack_master/commands/apply.rb +2 -2
- data/lib/stack_master/stack.rb +10 -32
- data/lib/stack_master/stack_differ.rb +1 -1
- data/lib/stack_master/template_utils.rb +31 -0
- data/lib/stack_master/validator.rb +2 -2
- data/lib/stack_master/version.rb +1 -1
- data/spec/fixtures/parameters/myapp_vpc_with_secrets.yml +3 -0
- data/spec/fixtures/stack_master.yml +2 -0
- data/spec/stack_master/commands/status_spec.rb +4 -4
- data/spec/stack_master/config_spec.rb +2 -2
- data/spec/stack_master/stack_spec.rb +0 -20
- data/spec/stack_master/template_utils_spec.rb +21 -0
- data/spec/stack_master/validator_spec.rb +14 -1
- metadata +7 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dac9ea54e5fb5f551c15e0d077816eb62b79cb7a
|
|
4
|
+
data.tar.gz: f9e58ade11de8c75902ffc8ed83a03d623b02a83
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 18f4ad8e75146c81a63b33aade1e97ac14d133863def43e62f78e969120122eeba7ec33f90253d7582904443d9cedcab444d54c57e43637e5800981ff0a4000d
|
|
7
|
+
data.tar.gz: 311a35d9e1976033c4ab45a588199596b8be674af0669abd0dc27c77ec7f17f9a28b09d050ab0586b72fc578c02afab99aec9acf5112f6ac57fdbe9df988e574
|
data/lib/stack_master.rb
CHANGED
|
@@ -24,6 +24,7 @@ module StackMaster
|
|
|
24
24
|
autoload :ResolverArray, 'stack_master/resolver_array'
|
|
25
25
|
autoload :Resolver, 'stack_master/resolver_array'
|
|
26
26
|
autoload :Utils, 'stack_master/utils'
|
|
27
|
+
autoload :TemplateUtils, 'stack_master/template_utils'
|
|
27
28
|
autoload :Config, 'stack_master/config'
|
|
28
29
|
autoload :PagedResponseAccumulator, 'stack_master/paged_response_accumulator'
|
|
29
30
|
autoload :StackDefinition, 'stack_master/stack_definition'
|
|
@@ -103,7 +103,7 @@ module StackMaster
|
|
|
103
103
|
if use_s3?
|
|
104
104
|
s3.url(bucket: @s3_config['bucket'], prefix: @s3_config['prefix'], region: @s3_config['region'], template: @stack_definition.s3_template_file_name)
|
|
105
105
|
else
|
|
106
|
-
proposed_stack.
|
|
106
|
+
proposed_stack.template
|
|
107
107
|
end
|
|
108
108
|
end
|
|
109
109
|
|
|
@@ -112,7 +112,7 @@ module StackMaster
|
|
|
112
112
|
@stack_definition.s3_files.tap do |files|
|
|
113
113
|
files[@stack_definition.s3_template_file_name] = {
|
|
114
114
|
path: @stack_definition.template_file_path,
|
|
115
|
-
body: proposed_stack.
|
|
115
|
+
body: proposed_stack.template
|
|
116
116
|
}
|
|
117
117
|
end
|
|
118
118
|
end
|
data/lib/stack_master/stack.rb
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
module StackMaster
|
|
2
2
|
class Stack
|
|
3
|
-
MAX_TEMPLATE_SIZE = 51200
|
|
4
|
-
MAX_S3_TEMPLATE_SIZE = 460800
|
|
5
|
-
|
|
6
3
|
attr_reader :stack_name,
|
|
7
4
|
:region,
|
|
8
5
|
:stack_id,
|
|
@@ -18,25 +15,8 @@ module StackMaster
|
|
|
18
15
|
|
|
19
16
|
include Utils::Initializable
|
|
20
17
|
|
|
21
|
-
def template_hash
|
|
22
|
-
return unless template_body
|
|
23
|
-
@template_hash ||= case template_format
|
|
24
|
-
when :json
|
|
25
|
-
JSON.parse(template_body)
|
|
26
|
-
when :yaml
|
|
27
|
-
YAML.load(template_body)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def maybe_compressed_template_body
|
|
32
|
-
# Do not compress the template if it's not JSON because parsing YAML as a hash ignores
|
|
33
|
-
# CloudFormation-specific tags such as !Ref
|
|
34
|
-
return template_body if template_body.size <= MAX_TEMPLATE_SIZE || template_format != :json
|
|
35
|
-
@compressed_template_body ||= JSON.dump(template_hash)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
18
|
def template_default_parameters
|
|
39
|
-
template_hash.fetch('Parameters', {}).inject({}) do |result, (parameter_name, description)|
|
|
19
|
+
TemplateUtils.template_hash(template).fetch('Parameters', {}).inject({}) do |result, (parameter_name, description)|
|
|
40
20
|
result[parameter_name] = description['Default']
|
|
41
21
|
result
|
|
42
22
|
end
|
|
@@ -61,7 +41,7 @@ module StackMaster
|
|
|
61
41
|
params_hash
|
|
62
42
|
end
|
|
63
43
|
template_body ||= cf.get_template(stack_name: stack_name).template_body
|
|
64
|
-
template_format = identify_template_format(template_body)
|
|
44
|
+
template_format = TemplateUtils.identify_template_format(template_body)
|
|
65
45
|
stack_policy_body ||= cf.get_stack_policy(stack_name: stack_name).stack_policy_body
|
|
66
46
|
outputs = cf_stack.outputs
|
|
67
47
|
|
|
@@ -82,7 +62,7 @@ module StackMaster
|
|
|
82
62
|
def self.generate(stack_definition, config)
|
|
83
63
|
parameter_hash = ParameterLoader.load(stack_definition.parameter_files)
|
|
84
64
|
template_body = TemplateCompiler.compile(config, stack_definition.template_file_path)
|
|
85
|
-
template_format = identify_template_format(template_body)
|
|
65
|
+
template_format = TemplateUtils.identify_template_format(template_body)
|
|
86
66
|
parameters = ParameterResolver.resolve(config, stack_definition, parameter_hash)
|
|
87
67
|
stack_policy_body = if stack_definition.stack_policy_file_path
|
|
88
68
|
File.read(stack_definition.stack_policy_file_path)
|
|
@@ -97,19 +77,13 @@ module StackMaster
|
|
|
97
77
|
stack_policy_body: stack_policy_body)
|
|
98
78
|
end
|
|
99
79
|
|
|
100
|
-
def self.identify_template_format(template_body)
|
|
101
|
-
return :json if template_body =~ /^{/x # ignore leading whitespaces
|
|
102
|
-
:yaml
|
|
103
|
-
end
|
|
104
|
-
private_class_method :identify_template_format
|
|
105
|
-
|
|
106
80
|
def max_template_size(use_s3)
|
|
107
|
-
return MAX_S3_TEMPLATE_SIZE if use_s3
|
|
108
|
-
MAX_TEMPLATE_SIZE
|
|
81
|
+
return TemplateUtils::MAX_S3_TEMPLATE_SIZE if use_s3
|
|
82
|
+
TemplateUtils::MAX_TEMPLATE_SIZE
|
|
109
83
|
end
|
|
110
84
|
|
|
111
85
|
def too_big?(use_s3 = false)
|
|
112
|
-
|
|
86
|
+
template.size > max_template_size(use_s3)
|
|
113
87
|
end
|
|
114
88
|
|
|
115
89
|
def aws_parameters
|
|
@@ -119,5 +93,9 @@ module StackMaster
|
|
|
119
93
|
def aws_tags
|
|
120
94
|
Utils.hash_to_aws_tags(tags)
|
|
121
95
|
end
|
|
96
|
+
|
|
97
|
+
def template
|
|
98
|
+
@template ||= TemplateUtils.maybe_compressed_template_body(template_body)
|
|
99
|
+
end
|
|
122
100
|
end
|
|
123
101
|
end
|
|
@@ -15,7 +15,7 @@ module StackMaster
|
|
|
15
15
|
def current_template
|
|
16
16
|
return '' unless @current_stack
|
|
17
17
|
return @current_stack.template_body unless @current_stack.template_format == :json
|
|
18
|
-
JSON.pretty_generate(@current_stack.
|
|
18
|
+
JSON.pretty_generate(TemplateUtils.template_hash(@current_stack.template_body))
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def current_parameters
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module StackMaster
|
|
2
|
+
module TemplateUtils
|
|
3
|
+
MAX_TEMPLATE_SIZE = 51200
|
|
4
|
+
MAX_S3_TEMPLATE_SIZE = 460800
|
|
5
|
+
|
|
6
|
+
extend self
|
|
7
|
+
|
|
8
|
+
def identify_template_format(template_body)
|
|
9
|
+
return :json if template_body =~ /^{/x # ignore leading whitespaces
|
|
10
|
+
:yaml
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def template_hash(template_body=nil)
|
|
14
|
+
return unless template_body
|
|
15
|
+
template_format = identify_template_format(template_body)
|
|
16
|
+
case template_format
|
|
17
|
+
when :json
|
|
18
|
+
JSON.parse(template_body)
|
|
19
|
+
when :yaml
|
|
20
|
+
YAML.load(template_body)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def maybe_compressed_template_body(template_body)
|
|
25
|
+
# Do not compress the template if it's not JSON because parsing YAML as a hash ignores
|
|
26
|
+
# CloudFormation-specific tags such as !Ref
|
|
27
|
+
return template_body if template_body.size <= MAX_TEMPLATE_SIZE || identify_template_format(template_body) != :json
|
|
28
|
+
JSON.dump(template_hash(template_body))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -11,8 +11,8 @@ module StackMaster
|
|
|
11
11
|
|
|
12
12
|
def perform
|
|
13
13
|
StackMaster.stdout.print "#{@stack_definition.stack_name}: "
|
|
14
|
-
template_body =
|
|
15
|
-
cf.validate_template(template_body: template_body)
|
|
14
|
+
template_body = TemplateCompiler.compile(@config, @stack_definition.template_file_path)
|
|
15
|
+
cf.validate_template(template_body: TemplateUtils.maybe_compressed_template_body(template_body))
|
|
16
16
|
StackMaster.stdout.puts "valid"
|
|
17
17
|
true
|
|
18
18
|
rescue Aws::CloudFormation::Errors::ValidationError => e
|
data/lib/stack_master/version.rb
CHANGED
|
@@ -17,8 +17,8 @@ RSpec.describe StackMaster::Commands::Status do
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
context "some parameters are different" do
|
|
20
|
-
let(:stack1) { double(:stack1, template_hash: {}, template_format: :json, parameters_with_defaults: {a: 1}, stack_status: 'UPDATE_COMPLETE') }
|
|
21
|
-
let(:stack2) { double(:stack2, template_hash: {}, template_format: :json, parameters_with_defaults: {a: 2}, stack_status: 'CREATE_COMPLETE') }
|
|
20
|
+
let(:stack1) { double(:stack1, template_body: '{}', template_hash: {}, template_format: :json, parameters_with_defaults: {a: 1}, stack_status: 'UPDATE_COMPLETE') }
|
|
21
|
+
let(:stack2) { double(:stack2, template_body: '{}', template_hash: {}, template_format: :json, parameters_with_defaults: {a: 2}, stack_status: 'CREATE_COMPLETE') }
|
|
22
22
|
let(:proposed_stack1) { double(:proposed_stack1, template_body: "{}", template_format: :json, parameters_with_defaults: {a: 1}) }
|
|
23
23
|
let(:proposed_stack2) { double(:proposed_stack2, template_body: "{}", template_format: :json, parameters_with_defaults: {a: 1}) }
|
|
24
24
|
|
|
@@ -29,8 +29,8 @@ RSpec.describe StackMaster::Commands::Status do
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
context "some templates are different" do
|
|
32
|
-
let(:stack1) { double(:stack1, template_hash: {foo: 'bar'}, template_format: :json, parameters_with_defaults: {a: 1}, stack_status: 'UPDATE_COMPLETE') }
|
|
33
|
-
let(:stack2) { double(:stack2, template_hash: {}, template_format: :json, parameters_with_defaults: {a: 1}, stack_status: 'CREATE_COMPLETE') }
|
|
32
|
+
let(:stack1) { double(:stack1, template_body: '{"foo": "bar"}', template_hash: {foo: 'bar'}, template_format: :json, parameters_with_defaults: {a: 1}, stack_status: 'UPDATE_COMPLETE') }
|
|
33
|
+
let(:stack2) { double(:stack2, template_body: '{}', template_hash: {}, template_format: :json, parameters_with_defaults: {a: 1}, stack_status: 'CREATE_COMPLETE') }
|
|
34
34
|
let(:proposed_stack1) { double(:proposed_stack1, template_body: "{}", template_format: :json, parameters_with_defaults: {a: 1}) }
|
|
35
35
|
let(:proposed_stack2) { double(:proposed_stack2, template_body: "{}", template_format: :json, parameters_with_defaults: {a: 1}) }
|
|
36
36
|
|
|
@@ -53,12 +53,12 @@ RSpec.describe StackMaster::Config do
|
|
|
53
53
|
|
|
54
54
|
it 'can filter by region only' do
|
|
55
55
|
stacks = loaded_config.filter('us-east-1')
|
|
56
|
-
expect(stacks.size).to eq
|
|
56
|
+
expect(stacks.size).to eq 3
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
it 'can return all stack definitions with no filters' do
|
|
60
60
|
stacks = loaded_config.filter
|
|
61
|
-
expect(stacks.size).to eq
|
|
61
|
+
expect(stacks.size).to eq 5
|
|
62
62
|
end
|
|
63
63
|
end
|
|
64
64
|
|
|
@@ -124,26 +124,6 @@ RSpec.describe StackMaster::Stack do
|
|
|
124
124
|
end
|
|
125
125
|
end
|
|
126
126
|
|
|
127
|
-
describe "#maybe_compressed_template_body" do
|
|
128
|
-
subject(:maybe_compressed_template_body) do
|
|
129
|
-
stack.maybe_compressed_template_body
|
|
130
|
-
end
|
|
131
|
-
context "undersized json" do
|
|
132
|
-
let(:stack) { described_class.new(template_body: '{ }' ) }
|
|
133
|
-
|
|
134
|
-
it "leaves the json alone if it's not too large" do
|
|
135
|
-
expect(maybe_compressed_template_body).to eq('{ }')
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
context "oversized json" do
|
|
140
|
-
let(:stack) { described_class.new(template_body: "{#{' ' * 60000}}", template_format: :json) }
|
|
141
|
-
it "compresses the json when it's overly bulbous" do
|
|
142
|
-
expect(maybe_compressed_template_body).to eq('{}')
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
|
|
147
127
|
describe '#too_big?' do
|
|
148
128
|
let(:big_stack) { described_class.new(template_body: "{\"a\":\"#{'x' * 500000}\"}") }
|
|
149
129
|
let(:medium_stack) { described_class.new(template_body: "{\"a\":\"#{'x' * 60000}\"}") }
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
RSpec.describe StackMaster::TemplateUtils do
|
|
2
|
+
describe "#maybe_compressed_template_body" do
|
|
3
|
+
subject(:maybe_compressed_template_body) do
|
|
4
|
+
described_class.maybe_compressed_template_body(template_body)
|
|
5
|
+
end
|
|
6
|
+
context "undersized json" do
|
|
7
|
+
let(:template_body) { '{ }' }
|
|
8
|
+
|
|
9
|
+
it "leaves the json alone if it's not too large" do
|
|
10
|
+
expect(maybe_compressed_template_body).to eq('{ }')
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context "oversized json" do
|
|
15
|
+
let(:template_body) { "{#{' ' * 60000}}" }
|
|
16
|
+
it "compresses the json when it's overly bulbous" do
|
|
17
|
+
expect(maybe_compressed_template_body).to eq('{}')
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -2,10 +2,11 @@ RSpec.describe StackMaster::Validator do
|
|
|
2
2
|
|
|
3
3
|
subject(:validator) { described_class.new(stack_definition, config) }
|
|
4
4
|
let(:config) { StackMaster::Config.new({'stacks' => {}}, '/base_dir') }
|
|
5
|
+
let(:stack_name) { 'myapp_vpc' }
|
|
5
6
|
let(:stack_definition) do
|
|
6
7
|
StackMaster::StackDefinition.new(
|
|
7
8
|
region: 'us-east-1',
|
|
8
|
-
stack_name:
|
|
9
|
+
stack_name: stack_name,
|
|
9
10
|
template: 'myapp_vpc.json',
|
|
10
11
|
tags: { 'environment' => 'production' },
|
|
11
12
|
base_dir: File.expand_path('spec/fixtures'),
|
|
@@ -34,6 +35,18 @@ RSpec.describe StackMaster::Validator do
|
|
|
34
35
|
expect { validator.perform }.to output(/myapp_vpc: invalid/).to_stdout
|
|
35
36
|
end
|
|
36
37
|
end
|
|
38
|
+
|
|
39
|
+
context "validate is called from from a continuous integration system with no access to secrets" do
|
|
40
|
+
let(:stack_name) { 'myapp_vpc_with_secrets' }
|
|
41
|
+
let(:secret) { instance_double(StackMaster::ParameterResolvers::Secret) }
|
|
42
|
+
before do
|
|
43
|
+
allow(StackMaster::ParameterResolvers::Secret).to receive(:new).and_return(secret)
|
|
44
|
+
end
|
|
45
|
+
it "does not prompt for the secret key" do
|
|
46
|
+
expect(secret).not_to receive(:resolve)
|
|
47
|
+
validator.perform
|
|
48
|
+
end
|
|
49
|
+
end
|
|
37
50
|
end
|
|
38
51
|
|
|
39
52
|
end
|
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.10.
|
|
4
|
+
version: 0.10.2
|
|
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-11-
|
|
12
|
+
date: 2016-11-24 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: bundler
|
|
@@ -362,6 +362,7 @@ files:
|
|
|
362
362
|
- lib/stack_master/template_compilers/json.rb
|
|
363
363
|
- lib/stack_master/template_compilers/sparkle_formation.rb
|
|
364
364
|
- lib/stack_master/template_compilers/yaml.rb
|
|
365
|
+
- lib/stack_master/template_utils.rb
|
|
365
366
|
- lib/stack_master/test_driver/cloud_formation.rb
|
|
366
367
|
- lib/stack_master/test_driver/s3.rb
|
|
367
368
|
- lib/stack_master/testing.rb
|
|
@@ -373,6 +374,7 @@ files:
|
|
|
373
374
|
- script/buildkite/clean.sh
|
|
374
375
|
- script/buildkite_rspec.sh
|
|
375
376
|
- spec/fixtures/parameters/myapp_vpc.yml
|
|
377
|
+
- spec/fixtures/parameters/myapp_vpc_with_secrets.yml
|
|
376
378
|
- spec/fixtures/stack_master.yml
|
|
377
379
|
- spec/fixtures/templates/json/valid_myapp_vpc.json
|
|
378
380
|
- spec/fixtures/templates/myapp_vpc.json
|
|
@@ -417,6 +419,7 @@ files:
|
|
|
417
419
|
- spec/stack_master/template_compilers/json_spec.rb
|
|
418
420
|
- spec/stack_master/template_compilers/sparkle_formation_spec.rb
|
|
419
421
|
- spec/stack_master/template_compilers/yaml_spec.rb
|
|
422
|
+
- spec/stack_master/template_utils_spec.rb
|
|
420
423
|
- spec/stack_master/test_driver/cloud_formation_spec.rb
|
|
421
424
|
- spec/stack_master/test_driver/s3_spec.rb
|
|
422
425
|
- spec/stack_master/utils_spec.rb
|
|
@@ -468,6 +471,7 @@ test_files:
|
|
|
468
471
|
- features/support/env.rb
|
|
469
472
|
- features/validate.feature
|
|
470
473
|
- spec/fixtures/parameters/myapp_vpc.yml
|
|
474
|
+
- spec/fixtures/parameters/myapp_vpc_with_secrets.yml
|
|
471
475
|
- spec/fixtures/stack_master.yml
|
|
472
476
|
- spec/fixtures/templates/json/valid_myapp_vpc.json
|
|
473
477
|
- spec/fixtures/templates/myapp_vpc.json
|
|
@@ -512,6 +516,7 @@ test_files:
|
|
|
512
516
|
- spec/stack_master/template_compilers/json_spec.rb
|
|
513
517
|
- spec/stack_master/template_compilers/sparkle_formation_spec.rb
|
|
514
518
|
- spec/stack_master/template_compilers/yaml_spec.rb
|
|
519
|
+
- spec/stack_master/template_utils_spec.rb
|
|
515
520
|
- spec/stack_master/test_driver/cloud_formation_spec.rb
|
|
516
521
|
- spec/stack_master/test_driver/s3_spec.rb
|
|
517
522
|
- spec/stack_master/utils_spec.rb
|