Skip to content

Commit 247119b

Browse files
gamovPair
authored andcommitted
Add support for Cloud Foundry
Signed-off-by: Natalie Tay <ntay@pivotal.io> Signed-off-by: Alan Yeo <ayeo@pivotal.io> Signed-off-by: Benjamin Tan <btan@pivotal.io>
1 parent 5e67861 commit 247119b

File tree

16 files changed

+383
-21
lines changed

16 files changed

+383
-21
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,21 @@ filesystem gets recreated from the git sources on each instance refresh. To use
333333

334334
To upload your local values to Heroku you could ran `bundle exec rake config:heroku`.
335335

336+
### Working with Cloud Foundry
337+
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`:
341+
342+
bundle exec rake config:cf
343+
344+
You may optionally pass target environment _and_ the name of your CF manifest file (in that case, both are compulsory):
345+
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.
350+
336351
### Fine-tuning
337352

338353
You can customize how environment variables are processed:
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
require 'bundler'
2+
require 'yaml'
3+
require_relative '../../../lib/config/integrations/helpers/cf_manifest_merger'
4+
5+
module Config
6+
module Integrations
7+
class CloudFoundry < Struct.new(:target_env, :file_path)
8+
9+
def invoke
10+
11+
manifest_path = file_path
12+
file_name, _ext = manifest_path.split('.yml')
13+
14+
manifest_hash = YAML.load(IO.read(File.join(::Rails.root, manifest_path)))
15+
16+
puts "Generating manifest... (base cf manifest: #{manifest_path})"
17+
18+
merged_hash = Config::CFManifestMerger.new(target_env, manifest_hash).add_to_env
19+
20+
target_manifest_path = File.join(::Rails.root, "#{file_name}-merged.yml")
21+
IO.write(target_manifest_path, merged_hash.to_yaml)
22+
23+
puts "File #{target_manifest_path} generated."
24+
end
25+
26+
end
27+
end
28+
end
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
require_relative 'helpers'
2+
3+
module Config
4+
class CFManifestMerger
5+
include Integrations::Helpers
6+
7+
def initialize(target_env, manifest_hash)
8+
@manifest_hash = manifest_hash.dup
9+
10+
raise ArgumentError.new('Target environment & manifest path must be specified') unless target_env && @manifest_hash
11+
12+
config_root = File.join(Rails.root, 'config')
13+
config_setting_files = Config.setting_files(config_root, target_env)
14+
@settings_hash = Config.load_files(config_setting_files).to_hash.stringify_keys
15+
end
16+
17+
def add_to_env
18+
19+
prefix_keys_with_const_name_hash = to_dotted_hash(@settings_hash, namespace: Config.const_name)
20+
21+
apps = @manifest_hash['applications']
22+
23+
apps.each do |app|
24+
check_conflicting_keys(app['env'], @settings_hash)
25+
app['env'].merge!(prefix_keys_with_const_name_hash)
26+
end
27+
28+
@manifest_hash
29+
end
30+
31+
private
32+
33+
def check_conflicting_keys(env_hash, settings_hash)
34+
conflicting_keys = env_hash.keys & settings_hash.keys
35+
raise ArgumentError.new("Conflicting keys: #{conflicting_keys.join(', ')}") if conflicting_keys.any?
36+
end
37+
38+
end
39+
end
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module Config::Integrations::Helpers
2+
3+
def to_dotted_hash(source, target: {}, namespace: nil)
4+
raise ArgumentError, "target must be a hash (given: #{target.class.name})" unless target.kind_of? Hash
5+
prefix = "#{namespace}." if namespace
6+
case source
7+
when Hash
8+
source.each do |key, value|
9+
to_dotted_hash(value, target: target, namespace: "#{prefix}#{key}")
10+
end
11+
when Array
12+
source.each_with_index do |value, index|
13+
to_dotted_hash(value, target: target, namespace: "#{prefix}#{index}")
14+
end
15+
else
16+
target[namespace] = source
17+
end
18+
target
19+
end
20+
21+
end

lib/config/integrations/heroku.rb

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
require 'bundler'
2+
require_relative 'helpers/helpers'
23

34
module Config
45
module Integrations
56
class Heroku < Struct.new(:app)
7+
include Integrations::Helpers
8+
69
def invoke
710
puts 'Setting vars...'
811
heroku_command = "config:set #{vars}"
@@ -14,13 +17,13 @@ def invoke
1417
def vars
1518
# Load only local options to Heroku
1619
Config.load_and_set_settings(
17-
Rails.root.join("config", "settings.local.yml").to_s,
18-
Rails.root.join("config", "settings", "#{environment}.local.yml").to_s,
19-
Rails.root.join("config", "environments", "#{environment}.local.yml").to_s
20+
::Rails.root.join("config", "settings.local.yml").to_s,
21+
::Rails.root.join("config", "settings", "#{environment}.local.yml").to_s,
22+
::Rails.root.join("config", "environments", "#{environment}.local.yml").to_s
2023
)
2124

2225
out = ''
23-
dotted_hash = to_dotted_hash Kernel.const_get(Config.const_name).to_hash, {}, Config.const_name
26+
dotted_hash = to_dotted_hash Kernel.const_get(Config.const_name).to_hash, namespace: Config.const_name
2427
dotted_hash.each {|key, value| out += " #{key}=#{value} "}
2528
out
2629
end
@@ -38,22 +41,6 @@ def `(command)
3841
Bundler.with_clean_env { super }
3942
end
4043

41-
def to_dotted_hash(source, target = {}, namespace = nil)
42-
prefix = "#{namespace}." if namespace
43-
case source
44-
when Hash
45-
source.each do |key, value|
46-
to_dotted_hash(value, target, "#{prefix}#{key}")
47-
end
48-
when Array
49-
source.each_with_index do |value, index|
50-
to_dotted_hash(value, target, "#{prefix}#{index}")
51-
end
52-
else
53-
target[namespace] = source
54-
end
55-
target
56-
end
5744
end
5845
end
5946
end

lib/config/integrations/rails/railtie.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def preload
1515

1616
# Load rake tasks (eg. Heroku)
1717
rake_tasks do
18-
Dir[File.join(File.dirname(__FILE__), '../tasks/*.rake')].each { |f| load f }
18+
Dir[File.join(File.dirname(__FILE__), '../../tasks/*.rake')].each { |f| load f }
1919
end
2020

2121
config.before_configuration { preload }

lib/config/tasks/cloud_foundry.rake

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
require 'config/integrations/cloud_foundry'
2+
3+
namespace 'config' do
4+
5+
desc 'Create a cf manifest with the env variables defined by config under current environment'
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
14+
end
15+
16+
end

lib/config/tasks/heroku.rake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
require 'config/integrations/heroku'
22

33
namespace 'config' do
4+
5+
desc 'Upload to Heroku all env variables defined by config under current environment'
46
task :heroku, [:app] => :environment do |_, args|
57
Config::Integrations::Heroku.new(args[:app]).invoke
68
end
9+
710
end

spec/fixtures/cf/cf_manifest.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
applications:
2+
- name: some-cf-app
3+
instances: 1
4+
env:
5+
DEFAULT_HOST: host
6+
DEFAULT_PORT: port
7+
FOO: BAR
8+
9+
- name: app_name
10+
env:
11+
DEFAULT_HOST: host
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DEFAULT_HOST: host
2+
DEFAULT_PORT: port

0 commit comments

Comments
 (0)