Skip to content

Commit d7373c7

Browse files
gamovPair
authored andcommitted
Changed interface and implementation to accept an optional target_env and
cf manifest name. Improved the tests Signed-off-by: Natalie Tay <ntay@pivotal.io>
1 parent 137094e commit d7373c7

File tree

8 files changed

+126
-89
lines changed

8 files changed

+126
-89
lines changed

README.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -335,13 +335,18 @@ To upload your local values to Heroku you could ran `bundle exec rake config:her
335335

336336
### Working with Cloud Foundry
337337

338-
Cloud Foundry integration will generate a manifest adding to your CF manifest.yml the defined ENV variables under the `env` section of specified app in the yaml file.
339-
You must specify the app name and optionally the name of your CF manifest file:
338+
Cloud Foundry integration will generate a manifest from your CF manifest with the defined ENV variables added
339+
under the `env` section. **ENV variables will be added to all applications specified in the manifest.** By default,
340+
it uses `manifest.yml` and the current `Rails.env`:
340341

341-
bundle exec rake config:cf[app_name, cf_manifest.yml]
342+
bundle exec rake config:cf
342343

343-
The result of this command will have the manifest file name suffixed with the environment you ran the task in. You can then push your app with the generated manifest.
344+
You may optionally pass target environment _and_ the name of your CF manifest file (in that case, both are compulsory):
344345

346+
bundle exec rake config:cf[target_env, your_manifest.yml]
347+
348+
The result of this command will create a new manifest file, name suffixed with '-merged'. You can then push your app
349+
with the generated manifest.
345350

346351
### Fine-tuning
347352

lib/config/integrations/cloud_foundry.rb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@
44

55
module Config
66
module Integrations
7-
class CloudFoundry < Struct.new(:app_name, :file_path)
7+
class CloudFoundry < Struct.new(:target_env, :file_path)
88

99
def invoke
10-
manifest_path = file_path || 'manifest.yml'
10+
11+
manifest_path = file_path
1112
file_name, _ext = manifest_path.split('.yml')
1213

1314
manifest_hash = YAML.load(IO.read(File.join(::Rails.root, manifest_path)))
1415

1516
puts "Generating manifest... (base cf manifest: #{manifest_path})"
1617

17-
merged_hash = Config::CFManifestMerger.new(app_name, manifest_hash).add_to_env
18+
merged_hash = Config::CFManifestMerger.new(target_env, manifest_hash).add_to_env
1819

19-
target_manifest_path = File.join(::Rails.root, "#{file_name}-#{::Rails.env}.yml")
20+
target_manifest_path = File.join(::Rails.root, "#{file_name}-merged.yml")
2021
IO.write(target_manifest_path, merged_hash.to_yaml)
2122

2223
puts "File #{target_manifest_path} generated."

lib/config/integrations/helpers/cf_manifest_merger.rb

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,25 @@ module Config
44
class CFManifestMerger
55
include Integrations::Helpers
66

7-
def initialize(app_name, manifest_hash)
8-
@app_name = app_name
9-
@manifest_hash = manifest_hash
10-
raise ArgumentError.new("Manifest path & app name must be specified") unless @app_name && @manifest_hash
11-
end
12-
13-
def add_to_env
7+
def initialize(target_env, manifest_hash)
8+
@manifest_hash = manifest_hash.dup
149

15-
settings_hash = Config.const_get(Config.const_name).to_hash.stringify_keys
10+
raise ArgumentError.new('Target environment & manifest path must be specified') unless target_env && @manifest_hash
1611

17-
prefix_keys_with_const_name_hash = to_dotted_hash(settings_hash, namespace: Config.const_name)
12+
config_setting_files = Config.setting_files(Rails.root, target_env)
13+
@settings_hash = Config.load_files(config_setting_files).to_hash.stringify_keys
14+
end
1815

19-
app_hash = @manifest_hash['applications'].detect { |hash| hash['name'] == @app_name }
16+
def add_to_env
2017

21-
raise ArgumentError, "Application '#{@app_name}' is not specified in your manifest" if app_hash.nil?
18+
prefix_keys_with_const_name_hash = to_dotted_hash(@settings_hash, namespace: Config.const_name)
2219

23-
check_conflicting_keys(app_hash['env'], settings_hash)
20+
apps = @manifest_hash['applications']
2421

25-
app_hash['env'].merge!(prefix_keys_with_const_name_hash)
22+
apps.each do |app|
23+
check_conflicting_keys(app['env'], @settings_hash)
24+
app['env'].merge!(prefix_keys_with_const_name_hash)
25+
end
2626

2727
@manifest_hash
2828
end

lib/config/tasks/cloud_foundry.rake

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@ require 'config/integrations/cloud_foundry'
33
namespace 'config' do
44

55
desc 'Create a cf manifest with the env variables defined by config under current environment'
6-
task :'cf', [:app_name, :file_path] => :environment do |_, args|
7-
Config::Integrations::CloudFoundry.new(args[:app_name], args[:file_path]).invoke
6+
task 'cf', [:target_env, :file_path] => [:environment] do |_, args|
7+
8+
raise ArgumentError, 'Both target_env and file_path arguments must be specified' if args.length == 1
9+
10+
default_args = {:target_env => Rails.env, :file_path => 'manifest.yml'}
11+
merged_args = default_args.merge(args)
12+
13+
Config::Integrations::CloudFoundry.new(*merged_args.values).invoke
814
end
915

1016
end
File renamed without changes.
File renamed without changes.

spec/integrations/helpers/cf_manifest_merger_spec.rb

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,61 +3,67 @@
33

44
describe Config::CFManifestMerger do
55

6-
after do
7-
Settings.reload_from_files("#{fixture_path}/settings.yml")
8-
end
9-
10-
it 'raises an argument error if you do not specify an app name' do
11-
expect {
12-
Config::CFManifestMerger.new(nil, load_manifest('cf_manifest.yml'))
13-
}.to raise_error(ArgumentError, 'Manifest path & app name must be specified')
14-
end
6+
let(:mocked_rails_root_path) { "#{fixture_path}/cf/" }
7+
let(:manifest_hash) { load_manifest('cf_manifest.yml') }
158

16-
it 'raises an argument error if the application name is not found in the manifest' do
9+
it 'raises an argument error if you do not specify a target environment' do
1710
expect {
18-
Config::CFManifestMerger.new('undefined', load_manifest('cf_manifest.yml')).add_to_env
19-
}.to raise_error(ArgumentError, "Application 'undefined' is not specified in your manifest")
11+
Config::CFManifestMerger.new(nil, manifest_hash)
12+
}.to raise_error(ArgumentError, 'Target environment & manifest path must be specified')
2013
end
2114

22-
it 'returns the cf manifest template if no settings available' do
23-
merger = Config::CFManifestMerger.new('app_name', load_manifest('cf_manifest.yml'))
24-
Config.load_and_set_settings ''
15+
it 'returns the cf manifest unmodified if no settings are available' do
16+
merger = Config::CFManifestMerger.new('test', manifest_hash)
2517

2618
resulting_hash = merger.add_to_env
27-
expect(resulting_hash).to eq(load_manifest('cf_manifest.yml'))
19+
expect(resulting_hash).to eq(manifest_hash)
2820
end
2921

30-
it 'merges the given YAML file with the cf manifest YAML file' do
31-
merger = Config::CFManifestMerger.new('some-cf-app', load_manifest('cf_manifest.yml'))
32-
Config.load_and_set_settings "#{fixture_path}/cf/cf_multilevel.yml"
22+
it 'adds the settings for the target_env to the manifest_hash' do
23+
allow(Rails).to receive(:root).and_return(mocked_rails_root_path)
24+
25+
merger = Config::CFManifestMerger.new('multilevel_settings', manifest_hash)
3326

3427
resulting_hash = merger.add_to_env
3528
expect(resulting_hash).to eq({
36-
"applications" => [
29+
'applications' => [
3730
{
38-
"name" => "some-cf-app",
39-
"instances" => 1,
40-
"env" => {
41-
"DEFAULT_HOST" => "host",
42-
"DEFAULT_PORT" => "port",
43-
"FOO" => "BAR",
44-
"Settings.world.capitals.europe.germany" => "Berlin",
45-
"Settings.world.capitals.europe.poland" => "Warsaw",
46-
"Settings.world.array.0.name" => "Alan",
47-
"Settings.world.array.1.name" => "Gam",
48-
"Settings.world.array_with_index.0.name" => "Bob",
49-
"Settings.world.array_with_index.1.name" => "William"
31+
'name' => 'some-cf-app',
32+
'instances' => 1,
33+
'env' => {
34+
'DEFAULT_HOST' => 'host',
35+
'DEFAULT_PORT' => 'port',
36+
'FOO' => 'BAR',
37+
'Settings.world.capitals.europe.germany' => 'Berlin',
38+
'Settings.world.capitals.europe.poland' => 'Warsaw',
39+
'Settings.world.array.0.name' => 'Alan',
40+
'Settings.world.array.1.name' => 'Gam',
41+
'Settings.world.array_with_index.0.name' => 'Bob',
42+
'Settings.world.array_with_index.1.name' => 'William'
5043
}
5144
},
52-
{"name"=>"app_name", "env"=>{"DEFAULT_HOST"=>"host"}}
45+
{
46+
'name' => 'app_name',
47+
'env' => {
48+
'DEFAULT_HOST' => 'host',
49+
'Settings.world.capitals.europe.germany' => 'Berlin',
50+
'Settings.world.capitals.europe.poland' => 'Warsaw',
51+
'Settings.world.array.0.name' => 'Alan',
52+
'Settings.world.array.1.name' => 'Gam',
53+
'Settings.world.array_with_index.0.name' => 'Bob',
54+
'Settings.world.array_with_index.1.name' => 'William'
55+
}
56+
}
5357
]
5458
})
5559
end
5660

5761
it 'raises an exception if there is conflicting keys' do
58-
merger = Config::CFManifestMerger.new('some-cf-app', load_manifest('cf_manifest.yml'))
59-
Config.load_and_set_settings "#{fixture_path}/cf/cf_conflict.yml"
62+
allow(Rails).to receive(:root).and_return(mocked_rails_root_path)
63+
64+
merger = Config::CFManifestMerger.new('conflict_settings', manifest_hash)
6065

66+
# Config.load_and_set_settings "#{fixture_path}/cf/conflict_settings.yml"
6167
expect {
6268
merger.add_to_env
6369
}.to raise_error(ArgumentError, 'Conflicting keys: DEFAULT_HOST, DEFAULT_PORT')

spec/tasks/cloud_foundry_spec.rb

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
include_context 'rake'
55

66
before :all do
7-
load File.expand_path("../../../lib/config/tasks/cloud_foundry.rake", __FILE__)
7+
load File.expand_path('../../../lib/config/tasks/cloud_foundry.rake', __FILE__)
88
Rake::Task.define_task(:environment)
99
end
1010

@@ -14,52 +14,71 @@
1414
Settings.reload_from_files("#{fixture_path}/settings.yml")
1515
end
1616

17-
it 'creates the merge manifest file for cf' do
18-
Config.load_and_set_settings "#{fixture_path}/cf/cf_multilevel.yml"
17+
it 'raises an error if the manifest file is missing' do
18+
expect {
19+
Rake::Task['config:cf'].execute
20+
}.to raise_error(SystemCallError)
21+
end
22+
23+
it 'raises an error if the settings file is missing' do
24+
expect {
25+
Rake::Task['config:cf'].execute({target_env: 'not_existing_env', file_path: 'manifest.yml'})
26+
}.to raise_error(SystemCallError)
27+
end
1928

20-
orig_rails_root = Rails.root
29+
describe 'without arguments' do
30+
it 'creates the merged manifest file with the settings env included for all applications' do
31+
allow(Rails).to receive(:root).and_return(Pathname.new Dir.mktmpdir)
2132

22-
begin
23-
Rails.application.config.root = Dir.mktmpdir
33+
test_settings_file = "#{fixture_path}/cf/settings/multilevel_settings.yml"
34+
test_manifest_file = "#{fixture_path}/cf/cf_manifest.yml"
2435

25-
FileUtils.cp("#{fixture_path}/cf/cf_manifest.yml", File.join(Rails.root, 'manifest.yml'))
36+
FileUtils.mkdir(Rails.root.join('settings'))
37+
FileUtils.cp(test_settings_file, Rails.root.join('settings','test.yml'))
2638

27-
Rake::Task['config:cf'].execute({:app_name => 'app_name'})
39+
FileUtils.cp(test_manifest_file, Rails.root.join('manifest.yml'))
2840

29-
target_file_path = File.join(Rails.root, 'manifest-test.yml')
30-
target_file_contents = YAML.load(IO.read(target_file_path))
41+
Rake::Task['config:cf'].execute
3142

32-
expect(target_file_contents["applications"][1]["name"]).to eq "app_name"
33-
expect(target_file_contents["applications"][1]["env"]["DEFAULT_HOST"]).to eq "host"
34-
expect(target_file_contents["applications"][1]["env"]["Settings.world.array.0.name"]).to eq "Alan"
35-
ensure
36-
Rails.application.config.root = orig_rails_root
43+
merged_manifest_file = File.join(Rails.root, 'manifest-merged.yml')
44+
merged_manifest_file_contents = YAML.load(IO.read(merged_manifest_file))
45+
46+
expect(merged_manifest_file_contents['applications'][0]['name']).to eq 'some-cf-app'
47+
expect(merged_manifest_file_contents['applications'][0]['env']['DEFAULT_HOST']).to eq 'host'
48+
expect(merged_manifest_file_contents['applications'][0]['env']['Settings.world.array.0.name']).to eq 'Alan'
49+
50+
expect(merged_manifest_file_contents['applications'][1]['name']).to eq 'app_name'
51+
expect(merged_manifest_file_contents['applications'][1]['env']['Settings.world.array.0.name']).to eq 'Alan'
3752
end
3853
end
3954

40-
it 'handles a custom manifest name' do
55+
describe 'with arguments' do
56+
it 'raises an error if only one argument is provided' do
57+
expect {
58+
Rake::Task['config:cf'].execute({target_env:'target_env_name'})
59+
}.to raise_error(ArgumentError)
60+
end
4161

42-
orig_rails_root = Rails.root
62+
it 'takes in account the provided arguments' do
63+
allow(Rails).to receive(:root).and_return(Pathname.new Dir.mktmpdir)
4364

44-
begin
45-
Rails.application.config.root = Dir.mktmpdir
65+
test_settings_file = "#{fixture_path}/cf/settings/multilevel_settings.yml"
66+
development_settings_file = "#{fixture_path}/development.yml"
67+
test_manifest_file = "#{fixture_path}/cf/cf_manifest.yml"
4668

47-
FileUtils.cp("#{fixture_path}/cf/cf_manifest.yml", File.join(Rails.root, 'cf_manifest.yml'))
69+
FileUtils.cp(test_manifest_file, Rails.root.join('cf_manifest.yml'))
4870

49-
Rake::Task['config:cf'].execute({app_name: 'app_name', file_path: 'cf_manifest.yml'})
71+
settings_dir = Rails.root.join('settings')
72+
settings_dir.mkpath
73+
FileUtils.cp(development_settings_file, settings_dir.join('development.yml'))
74+
FileUtils.cp(test_settings_file, settings_dir.join('test.yml'))
5075

51-
target_file_path = File.join(Rails.root, 'cf_manifest-test.yml')
76+
Rake::Task['config:cf'].execute({target_env: 'development', file_path: 'cf_manifest.yml'})
5277

53-
expect(File.size? target_file_path).to be
78+
merged_manifest_file = File.join(Rails.root, 'cf_manifest-merged.yml')
79+
merged_manifest_file_contents = YAML.load(IO.read(merged_manifest_file))
5480

55-
ensure
56-
Rails.application.config.root = orig_rails_root
81+
expect(merged_manifest_file_contents['applications'][0]['env']['Settings.size']).to eq 2
5782
end
5883
end
59-
60-
it 'raises an error if the specified file is missing' do
61-
expect {
62-
Rake::Task['config:cf'].execute({app_name: 'app_name', file_path: 'null.yml'})
63-
}.to raise_error(SystemCallError)
64-
end
6584
end

0 commit comments

Comments
 (0)