Permalink
Browse files

Improve cf-runtime extensibility for adding service types

- Delegate VCAP_SERVICES parsing for each service
to a class named "CFRuntime::<Servicetype>Parser"

- If class does not exist, CloudApp.service_properties
returns all service props from VCAP_SERVICES with
symbolized keys

Change-Id: I5d3c1b2c285f8aa7cad486de5b7a08e8ce3e986b
  • Loading branch information...
1 parent cc58e9c commit d127f8a836b6b3edf062e4c09839939963ff9d42 Jennifer Hickey committed Sep 19, 2012
@@ -27,10 +27,10 @@
# Some parts adapted from
# http://golang.org/src/pkg/json/decode.go and
# http://golang.org/src/pkg/utf8/utf8.go
-module CFRuntime::OkJson
+module CFRuntime
+ module OkJson
extend self
-
# Decodes a json document in string s and
# returns the corresponding ruby value.
# String s must be valid UTF-8. If you have
@@ -593,4 +593,5 @@ class Error < ::StandardError
Spc = ' '[0]
Unesc = {?b=>?\b, ?f=>?\f, ?n=>?\n, ?r=>?\r, ?t=>?\t}
+end
end
@@ -0,0 +1,6 @@
+require "cfruntime/parser/default_parser"
+require "cfruntime/parser/mongodb_parser"
+require "cfruntime/parser/mysql_parser"
+require "cfruntime/parser/postgresql_parser"
+require "cfruntime/parser/rabbitmq_parser"
+require "cfruntime/parser/redis_parser"
@@ -0,0 +1,21 @@
+module CFRuntime
+ class DefaultParser
+ # Default parsing behavior simply returns
+ # the passed-in map with all keys converted
+ # to symbols
+ def self.parse(svc)
+ symbolize_keys(svc)
+ end
+
+ private
+ def self.symbolize_keys(hash)
+ if hash.is_a? Hash
+ new_hash = {}
+ hash.each {|k, v| new_hash[k.to_sym] = symbolize_keys(v) }
+ new_hash
+ else
+ hash
+ end
+ end
+ end
+end
@@ -0,0 +1,16 @@
+module CFRuntime
+ class MongodbParser
+ def self.parse(svc)
+ serviceopts = {}
+ { :username => :username,
+ :password => :password,
+ :hostname => :host,
+ :port => :port,
+ :db => :db
+ }.each do |from, to|
+ serviceopts[to] = svc["credentials"][from.to_s]
+ end
+ serviceopts
+ end
+ end
+end
@@ -0,0 +1,16 @@
+module CFRuntime
+ class MysqlParser
+ def self.parse(svc)
+ serviceopts = {}
+ { :username => :username,
+ :password => :password,
+ :hostname => :host,
+ :port => :port,
+ :name => :database
+ }.each do |from, to|
+ serviceopts[to] = svc["credentials"][from.to_s]
+ end
+ serviceopts
+ end
+ end
+end
@@ -0,0 +1,16 @@
+module CFRuntime
+ class PostgresqlParser
+ def self.parse(svc)
+ serviceopts = {}
+ { :username => :username,
+ :password => :password,
+ :hostname => :host,
+ :port => :port,
+ :name => :database
+ }.each do |from, to|
+ serviceopts[to] = svc["credentials"][from.to_s]
+ end
+ serviceopts
+ end
+ end
+end
@@ -0,0 +1,33 @@
+module CFRuntime
+ class RabbitmqParser
+ def self.parse(svc)
+ serviceopts = {}
+ cred = svc["credentials"]
+ if cred["url"]
+ #The RabbitMQ default vhost
+ vhost = '/'
+ # The new "srs" credentials format
+ uri=URI.parse(cred["url"])
+ user=URI.unescape(uri.user) if uri.user
+ passwd=URI.unescape(uri.password) if uri.password
+ host=uri.host
+ port=uri.port
+ if uri.path =~ %r{^/(.*)}
+ raise ArgumentError.new("multiple segments in path of amqp URI: #{uri}") if $1.index('/')
+ vhost = URI.unescape($1)
+ end
+ serviceopts[:url] = cred["url"]
+ else
+ # The "old" credentials format
+ user,passwd,host,port,vhost = %w(user pass hostname port vhost).map {|key|
+ cred[key]}
+ end
+ serviceopts[:username] = user
+ serviceopts[:password] = passwd
+ serviceopts[:host] = host
+ serviceopts[:port] = port
+ serviceopts[:vhost] = vhost
+ serviceopts
+ end
+ end
+end
@@ -0,0 +1,16 @@
+module CFRuntime
+ class RedisParser
+ def self.parse(svc)
+ serviceopts = {}
+ { :username => :username,
+ :password => :password,
+ :hostname => :host,
+ :port => :port,
+ :name => :database
+ }.each do |from, to|
+ serviceopts[to] = svc["credentials"][from.to_s]
+ end
+ serviceopts
+ end
+ end
+end
@@ -1,10 +1,9 @@
-module CFRuntime
-
- require 'uri'
- require File.join(File.dirname(__FILE__), 'okjson')
+require "uri"
+require "cfruntime/okjson"
+require "cfruntime/parser"
+module CFRuntime
class CloudApp
-
class << self
# Returns true if this code is running on Cloud Foundry
def running_in_cloud?()
@@ -29,54 +28,21 @@ def port
# of a specified type are found.
def service_props(service_name)
registered_svcs = {}
- if ENV['VCAP_SERVICES']
- svcs = CFRuntime::OkJson.decode(ENV['VCAP_SERVICES'])
- else
- svcs = {}
- end
+ svcs = ENV['VCAP_SERVICES'] ? CFRuntime::OkJson.decode(ENV['VCAP_SERVICES']) : {}
svcs.each do |key,list|
label, version = key.split('-')
+ begin
+ parser = Object.const_get("CFRuntime").const_get("#{label.capitalize}Parser")
+ rescue NameError
+ parser = Object.const_get("CFRuntime").const_get("DefaultParser")
+ end
list.each do |svc|
name = svc["name"]
serviceopts = {}
serviceopts[:label] = label
serviceopts[:version] = version
serviceopts[:name] = name
- cred = svc["credentials"]
- if label =~ /rabbitmq/
- if cred['url']
- #The RabbitMQ default vhost
- vhost = '/'
- # The new "srs" credentials format
- uri=URI.parse(cred['url'])
- user=URI.unescape(uri.user) if uri.user
- passwd=URI.unescape(uri.password) if uri.password
- host=uri.host
- port=uri.port
- if uri.path =~ %r{^/(.*)}
- raise ArgumentError.new("multiple segments in path of amqp URI: #{uri}") if $1.index('/')
- vhost = URI.unescape($1)
- end
- serviceopts[:url] = cred['url']
- else
- # The "old" credentials format
- user,passwd,host,port,vhost = %w(user pass hostname port vhost).map {|key|
- cred[key]}
- end
- serviceopts[:vhost] = vhost
- else
- user,passwd,host,port,dbname,db = %w(username password hostname port name db).map {|key|
- cred[key]}
- if label == "mongodb"
- serviceopts[:db] = db
- else
- serviceopts[:database] = dbname
- end
- end
- serviceopts[:username] = user
- serviceopts[:password] = passwd
- serviceopts[:host] = host
- serviceopts[:port] = port
+ serviceopts.merge!(parser.parse(svc))
registered_svcs[name] = serviceopts
if list.count == 1
registered_svcs[label] = serviceopts
@@ -118,7 +84,5 @@ def service_names_of_type(type)
service_names
end
end
-
end
-
-end
+end
@@ -0,0 +1,24 @@
+require 'spec_helper'
+require 'cfruntime/properties'
+
+describe 'CFRuntime::DefaultParser' do
+ it 'returns the specified service info with symbolized map keys' do
+ svcs = { "filesystem-1.0" => ["{\"name\":\"filesystem-d460f\",\"label\":" +
+ "\"filesystem-1.0\",\"plan\":\"free\",\"tags\":[\"Persistent filesystem service\",\"filesystem-1.0\"," +
+ "\"filesystem\"],\"credentials\":{\"internal\":{\"fs_type\":\"local\",\"name\":\"fc662d2b-59ef-4de3-9912-90809ec5d080\"," +
+ "\"local_path\":\"/var/vcap/store/fss_backend1\"}}}"]
+ }
+ with_vcap_services(svcs) do
+ expected = { :label => "filesystem-1.0",
+ :version => "1.0",
+ :name => "filesystem-d460f",
+ :plan => "free",
+ :tags => ["Persistent filesystem service", "filesystem-1.0", "filesystem"],
+ :credentials => { :internal => { :fs_type=>"local",
+ :name=>"fc662d2b-59ef-4de3-9912-90809ec5d080",
+ :local_path=>"/var/vcap/store/fss_backend1" }}
+ }
+ CFRuntime::CloudApp.service_props('filesystem').should == expected
+ end
+ end
+end
@@ -0,0 +1,22 @@
+require 'spec_helper'
+require 'cfruntime/properties'
+
+describe 'CFRuntime::MongoParser' do
+ it 'parses a mongo service' do
+ svcs = {
+ "mongodb-#{mongo_version}" => [create_mongo_service('mongo-test')]
+ }
+ with_vcap_services(svcs) do
+ expected = { :label => "mongodb",
+ :version => "#{mongo_version}",
+ :name => "mongo-test",
+ :username => "testuser",
+ :password => "testpw",
+ :host => SOME_SERVER,
+ :port => SOME_SERVICE_PORT,
+ :db => "db"
+ }
+ CFRuntime::CloudApp.service_props('mongodb').should == expected
+ end
+ end
+end
@@ -0,0 +1,22 @@
+require 'spec_helper'
+require 'cfruntime/properties'
+
+describe 'CFRuntime::MysqlParser' do
+ it 'parses a mysql service' do
+ svcs = {
+ "mysql-#{mysql_version}" => [create_mysql_service('mysql-test')]
+ }
+ with_vcap_services(svcs) do
+ expected = { :label => "mysql",
+ :version => "#{mysql_version}",
+ :name => "mysql-test",
+ :username => "testuser",
+ :password => "testpw",
+ :host => SOME_SERVER,
+ :port => SOME_SERVICE_PORT,
+ :database => "mysqldatabase"
+ }
+ CFRuntime::CloudApp.service_props('mysql').should == expected
+ end
+ end
+end
@@ -0,0 +1,22 @@
+require 'spec_helper'
+require 'cfruntime/properties'
+
+describe 'CFRuntime::PostgresqlParser' do
+ it 'parses a postgres service' do
+ svcs = {
+ "postgresql-#{postgres_version}" => [create_postgres_service('pg-test')]
+ }
+ with_vcap_services(svcs) do
+ expected = { :label => "postgresql",
+ :version => "#{postgres_version}",
+ :name => "pg-test",
+ :username => "testuser",
+ :password => "testpw",
+ :host => SOME_SERVER,
+ :port => SOME_SERVICE_PORT,
+ :database => "pgdatabase"
+ }
+ CFRuntime::CloudApp.service_props('postgresql').should == expected
+ end
+ end
+end
@@ -0,0 +1,60 @@
+require 'spec_helper'
+require 'cfruntime/properties'
+
+describe 'CFRuntime::RabbitmqParser' do
+ it 'parses a rabbitmq service (old format)' do
+ svcs = {
+ "rabbitmq-#{rabbit_version}" => [create_rabbit_service('rabbit-test','testvhost')]
+ }
+ with_vcap_services(svcs) do
+ expected = { :label => "rabbitmq",
+ :version => "#{rabbit_version}",
+ :name => "rabbit-test",
+ :username => "rabbituser",
+ :password => "rabbitpass",
+ :host => SOME_SERVER,
+ :port => SOME_SERVICE_PORT,
+ :vhost => "testvhost"
+ }
+ CFRuntime::CloudApp.service_props('rabbitmq').should == expected
+ end
+ end
+
+ it 'parses a rabbitmq service (new format)' do
+ svcs = {
+ "rabbitmq-#{rabbit_version}" => [create_rabbit_srs_service('rabbit-test','testvhost')]
+ }
+ with_vcap_services(svcs) do
+ expected = { :label => "rabbitmq",
+ :version => "#{rabbit_version}",
+ :name => "rabbit-test",
+ :username => "rabbituser",
+ :password => "rabbitpass",
+ :host => SOME_SERVER,
+ :port => SOME_SERVICE_PORT,
+ :vhost => "testvhost",
+ :url => "amqp://rabbituser:rabbitpass@#{SOME_SERVER}:#{SOME_SERVICE_PORT}/testvhost"
+ }
+ CFRuntime::CloudApp.service_props('rabbitmq').should == expected
+ end
+ end
+
+ it 'parses a rabbitmq service without vhost (new format)' do
+ svcs = {
+ "rabbitmq-#{rabbit_version}" => [create_rabbit_srs_service('rabbit-test')]
+ }
+ with_vcap_services(svcs) do
+ expected = { :label => "rabbitmq",
+ :version => "#{rabbit_version}",
+ :name => "rabbit-test",
+ :username => "rabbituser",
+ :password => "rabbitpass",
+ :host => SOME_SERVER,
+ :port => SOME_SERVICE_PORT,
+ :vhost => "/",
+ :url => "amqp://rabbituser:rabbitpass@#{SOME_SERVER}:#{SOME_SERVICE_PORT}"
+ }
+ CFRuntime::CloudApp.service_props('rabbitmq').should == expected
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit d127f8a

Please sign in to comment.