kumo_keisei 2.1.1 → 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.1.1
1
+ 2.2.1
@@ -147,7 +147,7 @@ module KumoKeisei
147
147
  rescue Aws::CloudFormation::Errors::ValidationError => ex
148
148
  raise ex unless ex.message == "No updates are to be performed."
149
149
  ConsoleJockey.write_line "No changes need to be applied for #{@stack_name}."
150
- rescue Aws::Waiters::Errors::FailureStateError => ex
150
+ rescue Aws::Waiters::Errors::FailureStateError
151
151
  ConsoleJockey.write_line "Failed to apply the environment update. The stack has been rolled back. It is still safe to apply updates."
152
152
  ConsoleJockey.write_line "Find error details in the AWS CloudFormation console: #{stack_events_url}"
153
153
  raise UpdateError.new("Stack update failed for #{@stack_name}.")
@@ -5,118 +5,121 @@ require 'yaml'
5
5
  require_relative 'file_loader'
6
6
  require_relative 'parameter_builder'
7
7
 
8
- class KumoKeisei::EnvironmentConfig
9
- LOGGER = Logger.new(STDOUT)
10
-
11
- attr_reader :app_name, :env_name
12
-
13
- def initialize(options, logger = LOGGER)
14
- @app_name = options[:app_name]
15
- @env_name = options[:env_name]
16
- @config_dir_path = options[:config_dir_path]
17
- @params_template_file_path = options[:params_template_file_path]
18
- @injected_config = options[:injected_config] || {}
19
- @file_loader = KumoKeisei::FileLoader.new(options)
20
-
21
- @log = logger
22
- end
23
-
24
- def get_binding
25
- binding
26
- end
8
+ module KumoKeisei
9
+ # Environment Configuration for a cloud formation stack
10
+ class EnvironmentConfig
11
+ LOGGER = Logger.new(STDOUT)
12
+
13
+ attr_reader :app_name, :env_name
14
+
15
+ def initialize(options, logger = LOGGER)
16
+ @app_name = options[:app_name]
17
+ @env_name = options[:env_name]
18
+ @config_dir_path = options[:config_dir_path]
19
+ @params_template_file_path = options[:params_template_file_path]
20
+ @injected_config = options[:injected_config] || {}
21
+ @file_loader = KumoKeisei::FileLoader.new(options)
22
+
23
+ @log = logger
24
+ end
27
25
 
28
- def production?
29
- env_name == "production"
30
- end
26
+ def production?
27
+ env_name == 'production'
28
+ end
31
29
 
32
- def development?
33
- !(%w(production staging).include?(env_name))
34
- end
30
+ def development?
31
+ !%w(production staging).include? env_name
32
+ end
35
33
 
36
- def plain_text_secrets
37
- @plain_text_secrets ||= decrypt_secrets(encrypted_secrets)
38
- end
34
+ def plain_text_secrets
35
+ @plain_text_secrets ||= decrypt_secrets(encrypted_secrets)
36
+ end
39
37
 
40
- def config
41
- @config ||= common_config.merge(env_config).merge(@injected_config)
42
- end
38
+ def config
39
+ # a hash of all settings that apply to this environment
40
+ @config ||= common_config.merge(env_config).merge(@injected_config)
41
+ end
43
42
 
44
- def cf_params
45
- return [] unless params_template
46
- KumoKeisei::ParameterBuilder.new(get_stack_params(params_template)).params
47
- end
43
+ def cf_params
44
+ # returns a list of Cfn friendly paramater_value, paramater_key pairs for
45
+ # consumption by cloudformation.
46
+ return [] unless params
47
+ config
48
48
 
49
- private
49
+ stack_params = YAML.load(params.result(binding))
50
+ KumoKeisei::ParameterBuilder.new(stack_params).params
51
+ end
50
52
 
51
- def kms
52
- @kms ||= KumoKi::KMS.new
53
- end
53
+ private
54
54
 
55
- def get_stack_params(params_template)
56
- YAML.load(ERB.new(params_template).result(get_binding))
57
- end
55
+ def kms
56
+ @kms ||= KumoKi::KMS.new
57
+ end
58
58
 
59
- def params_template
60
- return nil unless @params_template_file_path
59
+ def params
60
+ return nil unless @params_template_file_path
61
+ @file_loader.load_erb(@params_template_file_path)
62
+ end
61
63
 
62
- @file_loader.load_config!(@params_template_file_path)
63
- end
64
+ def decrypt_secrets(secrets)
65
+ Hash[
66
+ secrets.map do |name, cipher_text|
67
+ @log.debug "Decrypting '#{name}'"
68
+ decrypt_cipher name, cipher_text
69
+ end
70
+ ]
71
+ end
64
72
 
65
- def decrypt_secrets(secrets)
66
- Hash[
67
- secrets.map do |name, cipher_text|
68
- @log.debug "Decrypting '#{name}'"
69
- if cipher_text.start_with? '[ENC,'
70
- begin
71
- [name, "#{kms.decrypt cipher_text[5,cipher_text.size]}"]
72
- rescue
73
- @log.error "Error decrypting secret '#{name}'"
74
- raise
75
- end
76
- else
77
- [name, cipher_text]
73
+ def decrypt_cipher(name, cipher_text)
74
+ if cipher_text.start_with? '[ENC,'
75
+ begin
76
+ [name, kms.decrypt(cipher_text[5, cipher_text.size]).to_s]
77
+ rescue
78
+ @log.error "Error decrypting secret '#{name}'"
79
+ raise
78
80
  end
81
+ else
82
+ [name, cipher_text]
79
83
  end
80
- ]
81
- end
84
+ end
82
85
 
83
- def env_config_file_name
84
- "#{env_name}.yml"
85
- end
86
+ def env_config_file_name
87
+ "#{env_name}.yml"
88
+ end
86
89
 
87
- def env_secrets_file_name
88
- "#{env_name}_secrets.yml"
89
- end
90
+ def env_secrets_file_name
91
+ "#{env_name}_secrets.yml"
92
+ end
90
93
 
91
- def encrypted_secrets
92
- encrypted_common_secrets.merge(encrypted_env_secrets)
93
- end
94
+ def encrypted_secrets
95
+ encrypted_common_secrets.merge(encrypted_env_secrets)
96
+ end
94
97
 
95
- def encrypted_common_secrets
96
- @file_loader.load_config('common_secrets.yml')
97
- end
98
+ def encrypted_common_secrets
99
+ @file_loader.load_hash('common_secrets.yml')
100
+ end
98
101
 
99
- def encrypted_env_secrets
100
- secrets = @file_loader.load_config(env_secrets_file_name)
102
+ def encrypted_env_secrets
103
+ secrets = @file_loader.load_hash(env_secrets_file_name)
101
104
 
102
- if !secrets.empty?
103
- secrets
104
- else
105
- @file_loader.load_config('development_secrets.yml')
105
+ if !secrets.empty?
106
+ secrets
107
+ else
108
+ @file_loader.load_hash('development_secrets.yml')
109
+ end
106
110
  end
107
- end
108
111
 
109
- def common_config
110
- @file_loader.load_config('common.yml')
111
- end
112
-
113
- def env_config
114
- config = @file_loader.load_config(env_config_file_name)
112
+ def common_config
113
+ @file_loader.load_hash('common.yml')
114
+ end
115
115
 
116
- if !config.empty?
117
- config
118
- else
119
- @file_loader.load_config('development.yml')
116
+ def env_config
117
+ config = @file_loader.load_hash(env_config_file_name)
118
+ if !config.empty?
119
+ config
120
+ else
121
+ @file_loader.load_hash('development.yml')
122
+ end
120
123
  end
121
124
  end
122
125
  end
@@ -4,15 +4,27 @@ module KumoKeisei
4
4
  @config_dir_path = options[:config_dir_path]
5
5
  end
6
6
 
7
- def load_config!(file_name, context = nil)
8
- erb_result = ERB.new(File.read(file_path(file_name))).result(context)
9
- YAML.load(erb_result)
7
+ def load_hash(file_name, optional = true)
8
+ # reads a file presuming it's a yml in form of key: value, returning it as a hash
9
+ path = file_path(file_name)
10
+
11
+ begin
12
+ YAML::load(File.read(path))
13
+ rescue Errno::ENOENT => ex
14
+ # file not found, return empty dictionary if that is ok
15
+ return {} if optional
16
+ raise ex
17
+ rescue StandardError => ex
18
+ # this is an error we weren't expecting
19
+ raise ex
20
+ end
10
21
  end
11
22
 
12
- def load_config(file_name)
23
+ def load_erb(file_name)
24
+ # loads a file, constructs an ERB object from it and returns the ERB object
25
+ # DOES NOT RENDER A RESULT!!
13
26
  path = file_path(file_name)
14
- return {} unless File.exist?(path)
15
- load_config!(file_name)
27
+ ERB.new(File.read(path))
16
28
  end
17
29
 
18
30
  private
@@ -21,11 +21,11 @@ module KumoKeisei
21
21
  end
22
22
 
23
23
  def parsed_file_params
24
- return [] unless (@file_path && File.exists?(@file_path))
24
+ return [] unless (@file_path && File.exist?(@file_path))
25
25
 
26
26
  file_contents = JSON.parse(File.read(@file_path))
27
27
 
28
- file_contents.map do |param|
28
+ file_contents.map do |param|
29
29
  {
30
30
  parameter_key: param["ParameterKey"],
31
31
  parameter_value: param["ParameterValue"]
@@ -11,170 +11,227 @@ describe KumoKeisei::EnvironmentConfig do
11
11
  }
12
12
  end
13
13
  let(:file_loader) { instance_double(KumoKeisei::FileLoader) }
14
- let(:parameter_template) { "stack_name: <%= config['stack_name'] %>" }
14
+ let(:parameters) { ERB.new("") }
15
15
  let(:params_template_file_path) { '/junk.txt' }
16
16
  let(:environment_config_file_name) { "#{env_name}.yml" }
17
17
  let(:kms) { instance_double(KumoKi::KMS) }
18
18
  let(:logger) { double(:test_logger, debug: nil) }
19
+ let(:environment_config) { described_class.new(options, logger) }
19
20
 
20
21
  before do
21
22
  allow(KumoKeisei::FileLoader).to receive(:new).and_return(file_loader)
22
23
  allow(KumoKi::KMS).to receive(:new).and_return(kms)
23
- allow(file_loader).to receive(:load_config!).with(params_template_file_path).and_return(parameter_template)
24
+ allow(file_loader).to receive(:load_erb).with(params_template_file_path).and_return(parameters)
24
25
  end
25
26
 
26
- describe '#cf_params' do
27
- subject { described_class.new(options, logger).cf_params }
27
+ context 'unit tests' do
28
+ let(:fake_environment_binding) { binding }
28
29
 
29
- context 'params template file path is not provided' do
30
- let(:options) do
31
- {
32
- env_name: env_name,
33
- config_dir_path: config_dir_path
34
- }
35
- end
36
-
37
- it 'creates an empty array' do
38
- expect(subject).to eq([])
39
- end
40
- end
30
+ describe '#cf_params' do
31
+ subject { environment_config.cf_params }
41
32
 
42
- context 'params file is empty' do
43
- let(:parameter_template) { nil }
33
+ context 'params template file path is not provided' do
34
+ let(:options) do
35
+ {
36
+ env_name: env_name,
37
+ config_dir_path: config_dir_path
38
+ }
39
+ end
44
40
 
45
- it 'creates an empty array' do
46
- expect(subject).to eq([])
41
+ it 'creates an empty array' do
42
+ expect(subject).to eq([])
43
+ end
47
44
  end
48
- end
49
45
 
50
- context 'a hard-coded param' do
51
- let(:parameter_template) { "parameter_key: <%= 'parameter_value' %>" }
46
+ context 'params is empty' do
47
+ let(:parameters) { nil }
52
48
 
53
- it 'creates a array containing an aws formatted parameter hash' do
54
- expect(subject).to eq([{parameter_key: "parameter_key", parameter_value: "parameter_value"}])
49
+ it 'creates an empty array' do
50
+ expect(subject).to eq([])
51
+ end
55
52
  end
56
- end
57
53
 
58
- context 'templated params' do
59
- let(:environment_parameters) { { "stack_name" => "okonomiyaki" } }
54
+ context 'a hard-coded param' do
55
+ let(:parameters) { ERB.new("stack_name: \"foo-stack\"") }
56
+ let(:parameters) { ERB.new("parameter_key: \"parameter_value\"") }
60
57
 
61
- context 'environment params' do
62
- it 'creates a array containing an aws formatted parameter hash' do
63
- allow(file_loader).to receive(:load_config).with('common.yml').and_return({})
64
- allow(file_loader).to receive(:load_config).with(environment_config_file_name).and_return(environment_parameters)
58
+ before do
59
+ allow(file_loader).to receive(:load_hash).with('common.yml').and_return({})
60
+ allow(file_loader).to receive(:load_hash).with('the_jungle.yml').and_return({})
61
+ allow(file_loader).to receive(:load_hash).with('development.yml').and_return({})
62
+ end
65
63
 
66
- expect(subject).to eq([{parameter_key: "stack_name", parameter_value: "okonomiyaki"}])
64
+ it 'creates a array containing an aws formatted parameter hash' do
65
+ expect(subject).to eq([{parameter_key: "parameter_key", parameter_value: "parameter_value"}])
67
66
  end
68
67
  end
69
- end
70
- end
71
68
 
72
- describe "#config" do
73
- subject { described_class.new(options, logger).config }
69
+ describe "#config" do
70
+ subject { described_class.new(options, logger).config }
74
71
 
75
- context 'injected config' do
72
+ context 'injected config' do
76
73
 
77
- let(:options) do
78
- {
79
- env_name: env_name,
80
- config_dir_path: config_dir_path,
81
- params_template_file_path: params_template_file_path,
82
- injected_config: { "injected" => "yes" }
83
- }
84
- end
74
+ let(:options) do
75
+ {
76
+ env_name: env_name,
77
+ config_dir_path: config_dir_path,
78
+ params_template_file_path: params_template_file_path,
79
+ injected_config: { "injected" => "yes" }
80
+ }
81
+ end
85
82
 
86
- let(:common_parameters) { { "stack_name" => "okonomiyaki" } }
87
- it 'adds injected config to the config hash' do
88
- allow(file_loader).to receive(:load_config).with('common.yml').and_return(common_parameters)
89
- allow(file_loader).to receive(:load_config).with(environment_config_file_name).and_return({})
90
- allow(file_loader).to receive(:load_config).with('development.yml').and_return({})
83
+ let(:common_parameters) { { "stack_name" => "okonomiyaki" } }
84
+ it 'adds injected config to the config hash' do
85
+ expect(file_loader).to receive(:load_hash).with('common.yml').and_return(common_parameters)
86
+ expect(file_loader).to receive(:load_hash).with(environment_config_file_name).and_return({})
87
+ expect(file_loader).to receive(:load_hash).with("development.yml").and_return({})
91
88
 
92
- expect(subject).to eq({ "stack_name" => "okonomiyaki", "injected" => "yes" })
93
- end
94
- end
89
+ expect(subject).to eq({ "stack_name" => "okonomiyaki", "injected" => "yes" })
90
+ end
91
+ end
95
92
 
96
- context 'common config' do
97
- let(:common_parameters) { { "stack_name" => "okonomiyaki" } }
93
+ context 'common config' do
94
+ let(:common_parameters) { { "stack_name" => "okonomiyaki" } }
98
95
 
99
- it 'creates a array containing an aws formatted parameter hash' do
100
- allow(file_loader).to receive(:load_config).with('common.yml').and_return(common_parameters)
101
- allow(file_loader).to receive(:load_config).with(environment_config_file_name).and_return({})
102
- allow(file_loader).to receive(:load_config).with('development.yml').and_return({})
96
+ it 'creates a array containing an aws formatted parameter hash' do
97
+ expect(file_loader).to receive(:load_hash).with('common.yml').and_return(common_parameters)
98
+ expect(file_loader).to receive(:load_hash).with(environment_config_file_name).and_return({})
99
+ expect(file_loader).to receive(:load_hash).with("development.yml").and_return({})
103
100
 
104
- expect(subject).to eq('stack_name' => 'okonomiyaki')
101
+ expect(subject).to eq('stack_name' => 'okonomiyaki')
102
+ end
103
+ end
104
+
105
+ context 'merging common and environment specific configurations' do
106
+ let(:environment_config) { {'image' => 'ami-5678'} }
107
+ let(:development_config) { {'image' => 'ami-9999'} }
108
+
109
+ context 'with environmental overrides' do
110
+ let(:parameter_template) { "image: <%= config['image'] %>" }
111
+ let(:common_config) { {'image' => 'ami-1234'} }
112
+ let(:env_name) { 'development' }
113
+
114
+ it 'replaces the common value with the env value' do
115
+ expect(file_loader).to receive(:load_hash).with('common.yml').and_return(common_config)
116
+ expect(file_loader).to receive(:load_hash).with(environment_config_file_name).and_return(environment_config)
117
+ expect(subject).to eq('image' => 'ami-5678')
118
+ end
119
+ end
120
+
121
+ it 'falls back to a default environment if the requested one does not exist' do
122
+ expect(file_loader).to receive(:load_hash).with('common.yml').and_return({})
123
+ expect(file_loader).to receive(:load_hash).with("#{env_name}.yml").and_return({})
124
+ expect(file_loader).to receive(:load_hash).with("development.yml").and_return(development_config)
125
+
126
+ expect(subject).to eq('image' => 'ami-9999')
127
+ end
128
+ end
105
129
  end
106
- end
107
130
 
108
- context 'merging common and environment specific configurations' do
109
- let(:environment_config) { {'image' => 'ami-5678'} }
131
+ describe "#plain_text_secrets" do
132
+ subject { described_class.new(options, logger).plain_text_secrets }
110
133
 
111
- context 'with environmental overrides' do
112
- let(:parameter_template) { "image: <%= config['image'] %>" }
113
- let(:common_config) { {'image' => 'ami-1234'} }
114
- let(:env_name) { 'development' }
134
+ let(:crypted_password) { 'lookatmyencryptedpasswords' }
135
+ let(:plain_text_password) { 'plain_text_password' }
136
+ let(:secrets) { { 'secret_password' => "[ENC,#{crypted_password}" } }
115
137
 
116
- it 'replaces the common value with the env value' do
117
- allow(file_loader).to receive(:load_config).with('common.yml').and_return(common_config)
118
- allow(file_loader).to receive(:load_config).with('development.yml').and_return(environment_config)
138
+ let(:crypted_env_password) { 'cryptedenvpassword' }
139
+ let(:plain_text_env_password) { 'plain_text_env_password' }
140
+ let(:env_secrets) { { 'secret_password' => "[ENC,#{crypted_env_password}"}}
119
141
 
120
- expect(subject).to eq('image' => 'ami-5678')
142
+ before do
143
+ allow(kms).to receive(:decrypt).with(crypted_password).and_return(plain_text_password)
121
144
  end
122
- end
123
-
124
- it 'falls back to a default environment if the requested one does not exist' do
125
- allow(file_loader).to receive(:load_config).with('common.yml').and_return({})
126
- allow(file_loader).to receive(:load_config).with("#{env_name}.yml").and_return({})
127
- expect(file_loader).to receive(:load_config).with("development.yml").and_return(environment_config)
128
145
 
129
- expect(subject).to eq('image' => 'ami-5678')
130
- end
131
- end
132
- end
146
+ it 'decrypts common secrets' do
147
+ allow(file_loader).to receive(:load_hash).with('common_secrets.yml').and_return(secrets)
148
+ allow(file_loader).to receive(:load_hash).with("#{env_name}_secrets.yml").and_return({})
149
+ allow(file_loader).to receive(:load_hash).with("development_secrets.yml").and_return({})
133
150
 
134
- describe "#plain_text_secrets" do
135
- subject { described_class.new(options, logger).plain_text_secrets }
136
-
137
- let(:crypted_password) { 'lookatmyencryptedpasswords' }
138
- let(:plain_text_password) { 'plain_text_password' }
139
- let(:secrets) { { 'secret_password' => "[ENC,#{crypted_password}" } }
151
+ expect(subject).to eq('secret_password' => plain_text_password)
152
+ end
140
153
 
141
- let(:crypted_env_password) { 'cryptedenvpassword' }
142
- let(:plain_text_env_password) { 'plain_text_env_password' }
143
- let(:env_secrets) { { 'secret_password' => "[ENC,#{crypted_env_password}"}}
154
+ it 'decrypts environment secrets' do
155
+ allow(file_loader).to receive(:load_hash).with('common_secrets.yml').and_return({})
156
+ allow(file_loader).to receive(:load_hash).with("#{env_name}_secrets.yml").and_return(secrets)
144
157
 
145
- before do
146
- allow(kms).to receive(:decrypt).with(crypted_password).and_return(plain_text_password)
147
- end
158
+ expect(subject).to eq('secret_password' => plain_text_password)
159
+ end
148
160
 
149
- it 'decrypts common secrets' do
150
- allow(file_loader).to receive(:load_config).with('common_secrets.yml').and_return(secrets)
151
- allow(file_loader).to receive(:load_config).with("#{env_name}_secrets.yml").and_return({})
152
- allow(file_loader).to receive(:load_config).with("development_secrets.yml").and_return({})
161
+ it 'gives preference to environment secrets' do
162
+ allow(file_loader).to receive(:load_hash).with('common_secrets.yml').and_return(secrets)
163
+ allow(file_loader).to receive(:load_hash).with("#{env_name}_secrets.yml").and_return(env_secrets)
164
+ allow(kms).to receive(:decrypt).with(crypted_env_password).and_return(plain_text_env_password)
153
165
 
154
- expect(subject).to eq('secret_password' => plain_text_password)
155
- end
166
+ expect(subject).to eq('secret_password' => plain_text_env_password)
167
+ end
156
168
 
157
- it 'decrypts environment secrets' do
158
- allow(file_loader).to receive(:load_config).with('common_secrets.yml').and_return({})
159
- allow(file_loader).to receive(:load_config).with("#{env_name}_secrets.yml").and_return(secrets)
169
+ it 'falls back to a default environment if the requested one does not exist' do
170
+ allow(file_loader).to receive(:load_hash).with('common_secrets.yml').and_return({})
171
+ allow(file_loader).to receive(:load_hash).with("#{env_name}_secrets.yml").and_return({})
172
+ expect(file_loader).to receive(:load_hash).with("development_secrets.yml").and_return(secrets)
160
173
 
161
- expect(subject).to eq('secret_password' => plain_text_password)
162
- end
174
+ expect(subject).to eq('secret_password' => plain_text_password)
175
+ end
176
+ end
163
177
 
164
- it 'gives preference to environment secrets' do
165
- allow(file_loader).to receive(:load_config).with('common_secrets.yml').and_return(secrets)
166
- allow(file_loader).to receive(:load_config).with("#{env_name}_secrets.yml").and_return(env_secrets)
167
- allow(kms).to receive(:decrypt).with(crypted_env_password).and_return(plain_text_env_password)
178
+ describe '#development?' do
179
+ %w(production staging).each do |environment|
180
+ it "returns false for #{environment}" do
181
+ expect(
182
+ described_class.new({
183
+ env_name: environment,
184
+ config_dir_path: '',
185
+ params_template_file_path: ''}
186
+ ).development?).to eq false
187
+ end
188
+ end
168
189
 
169
- expect(subject).to eq('secret_password' => plain_text_env_password)
190
+ it 'returns true for anything other than production or staging' do
191
+ expect(
192
+ described_class.new({
193
+ env_name: 'fred',
194
+ config_dir_path: '',
195
+ params_template_file_path: ''}
196
+ ).development?).to eq true
197
+ end
198
+ end
170
199
  end
171
200
 
172
- it 'falls back to a default environment if the requested one does not exist' do
173
- allow(file_loader).to receive(:load_config).with('common_secrets.yml').and_return({})
174
- allow(file_loader).to receive(:load_config).with("#{env_name}_secrets.yml").and_return({})
175
- expect(file_loader).to receive(:load_config).with("development_secrets.yml").and_return(secrets)
176
-
177
- expect(subject).to eq('secret_password' => plain_text_password)
201
+ context 'integration tests' do
202
+
203
+ describe '#cf_params' do
204
+ subject { environment_config.cf_params }
205
+
206
+ context 'templated params' do
207
+ let(:parameters) { ERB.new("stack_name: \"<%= config['stack_name'] %>\"" ) }
208
+ let(:common_config) { { "stack_name" => "common"} }
209
+ let(:staging_config) { { "stack_name" => "staging" } }
210
+ let(:development_config) { { "stack_name" => "development" } }
211
+
212
+ context 'hiearchy of parameters' do
213
+ it 'will load values from the common paramater file' do
214
+ expect(file_loader).to receive(:load_hash).with('common.yml').and_return(common_config)
215
+ expect(file_loader).to receive(:load_hash).with("development.yml").and_return({})
216
+ expect(file_loader).to receive(:load_hash).with(environment_config_file_name).and_return({})
217
+ expect(subject).to eq([{parameter_key: "stack_name", parameter_value: "common"}])
218
+ end
219
+
220
+ it 'will load values from the environment specific file' do
221
+ expect(file_loader).to receive(:load_hash).with('common.yml').and_return({})
222
+ expect(file_loader).to receive(:load_hash).with(environment_config_file_name).and_return(staging_config)
223
+ expect(subject).to eq([{parameter_key: "stack_name", parameter_value: "staging"}])
224
+ end
225
+
226
+ it 'will load values from the shared development file if an environment specific file has no values' do
227
+ expect(file_loader).to receive(:load_hash).with('common.yml').and_return({})
228
+ expect(file_loader).to receive(:load_hash).with(environment_config_file_name).and_return({})
229
+ expect(file_loader).to receive(:load_hash).with("development.yml").and_return(development_config)
230
+ expect(subject).to eq([{parameter_key: "stack_name", parameter_value: "development"}])
231
+ end
232
+ end
233
+ end
234
+ end
178
235
  end
179
236
  end
180
237
  end