cf-runtime 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +12737 -0
- data/README.md +28 -0
- data/lib/cfruntime.rb +26 -0
- data/lib/cfruntime/amqp.rb +43 -0
- data/lib/cfruntime/carrot.rb +44 -0
- data/lib/cfruntime/mongodb.rb +86 -0
- data/lib/cfruntime/mysql.rb +44 -0
- data/lib/cfruntime/postgres.rb +50 -0
- data/lib/cfruntime/properties.rb +124 -0
- data/lib/cfruntime/redis.rb +44 -0
- data/lib/cfruntime/version.rb +3 -0
- metadata +266 -0
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#cf-runtime
|
2
|
+
|
3
|
+
A library for interacting with Cloud Foundry services. Provides methods for obtaining pre-configured connection objects and connection properties.
|
4
|
+
|
5
|
+
_Copyright (c) 2011-2012 VMware, Inc. Please see the LICENSE file._
|
6
|
+
|
7
|
+
require 'cfruntime'
|
8
|
+
|
9
|
+
#Connect to mysql service named 'mysql-test'
|
10
|
+
client = CFRuntime::Mysql2Client.create_from_svc 'mysql-test'
|
11
|
+
|
12
|
+
#Connect to a single service of type MongoDB
|
13
|
+
connection = CFRuntime::MongoClient.create
|
14
|
+
db = connection.db
|
15
|
+
|
16
|
+
#Obtain connection properties for 'myservice'
|
17
|
+
if CFRuntime::CloudApp.running_in_cloud?
|
18
|
+
service_props = CFRuntime::CloudApp.service_props 'myservice'
|
19
|
+
end
|
20
|
+
|
21
|
+
#Obtain connection properties for single service of type MySQL
|
22
|
+
service_props = CFRuntime::CloudApp.service_props 'mysql'
|
23
|
+
|
24
|
+
#Other handy methods
|
25
|
+
CFRuntime::CloudApp.host
|
26
|
+
CFRuntime::CloudApp.port
|
27
|
+
CFRuntime::CloudApp.service_names
|
28
|
+
CFRuntime::CloudApp.service_names_of_type 'mysql'
|
data/lib/cfruntime.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
begin
|
2
|
+
require 'cfruntime/amqp'
|
3
|
+
rescue LoadError
|
4
|
+
end
|
5
|
+
begin
|
6
|
+
require 'cfruntime/carrot'
|
7
|
+
rescue LoadError
|
8
|
+
end
|
9
|
+
begin
|
10
|
+
require 'cfruntime/mongodb'
|
11
|
+
rescue LoadError
|
12
|
+
end
|
13
|
+
begin
|
14
|
+
require 'cfruntime/mysql'
|
15
|
+
rescue LoadError
|
16
|
+
end
|
17
|
+
begin
|
18
|
+
require 'cfruntime/postgres'
|
19
|
+
rescue LoadError
|
20
|
+
end
|
21
|
+
begin
|
22
|
+
require 'cfruntime/redis'
|
23
|
+
rescue LoadError
|
24
|
+
end
|
25
|
+
require 'cfruntime/properties'
|
26
|
+
require 'cfruntime/version'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'amqp'
|
2
|
+
module CFRuntime
|
3
|
+
class AMQPClient
|
4
|
+
|
5
|
+
# Creates and returns an +AMQP+ connection to a single rabbitmq service.
|
6
|
+
# Passes optional other_options and block arguments to +AMQP.connect+.
|
7
|
+
# Raises +ArgumentError+ If zero or multiple rabbitmq services are found.
|
8
|
+
def self.create(other_options = {}, &block)
|
9
|
+
service_names = CloudApp.service_names_of_type('rabbitmq')
|
10
|
+
if service_names.length != 1
|
11
|
+
raise ArgumentError.new("Expected 1 service of rabbitmq type, " +
|
12
|
+
"but found #{service_names.length}. " +
|
13
|
+
"Consider using create_from_svc(service_name) instead.")
|
14
|
+
end
|
15
|
+
create_from_svc(service_names[0],other_options,&block)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Creates and returns an +AMQP+ connection to a rabbitmq service with the
|
19
|
+
# specified name.
|
20
|
+
# Passes optional other_options and block arguments to +AMQP.connect+.
|
21
|
+
# Raises +ArgumentError+ If specified rabbitmq service is not found.
|
22
|
+
def self.create_from_svc(service_name, other_options = {}, &block)
|
23
|
+
AMQP.connect(options_for_svc(service_name), other_options, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Merges provided options with connection options for specified rabbitmq service.
|
27
|
+
# Returns merged Hash containing (:user, :pass, :vhost, :host, :port).
|
28
|
+
# Raises +ArgumentError+ If specified rabbitmq service is not found.
|
29
|
+
def self.options_for_svc(service_name,options={})
|
30
|
+
service_props = CFRuntime::CloudApp.service_props(service_name)
|
31
|
+
if service_props.nil?
|
32
|
+
raise ArgumentError.new("Service with name #{service_name} not found")
|
33
|
+
end
|
34
|
+
cfoptions = options
|
35
|
+
cfoptions[:host] = service_props[:host]
|
36
|
+
cfoptions[:port] = service_props[:port]
|
37
|
+
cfoptions[:user] = service_props[:username]
|
38
|
+
cfoptions[:pass] = service_props[:password]
|
39
|
+
cfoptions[:vhost] = service_props[:vhost]
|
40
|
+
cfoptions
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'carrot'
|
2
|
+
require 'cfruntime/properties'
|
3
|
+
module CFRuntime
|
4
|
+
class CarrotClient
|
5
|
+
|
6
|
+
# Creates and returns a +Carrot+ instance connected to a single rabbitmq service.
|
7
|
+
# Passes optional Hash of non-connection-related options to +Carrot.new+.
|
8
|
+
# Raises +ArgumentError+ If zero or multiple rabbitmq services are found.
|
9
|
+
def self.create(options={})
|
10
|
+
service_names = CloudApp.service_names_of_type('rabbitmq')
|
11
|
+
if service_names.length != 1
|
12
|
+
raise ArgumentError.new("Expected 1 service of rabbitmq type, " +
|
13
|
+
"but found #{service_names.length}. " +
|
14
|
+
"Consider using create_from_svc(service_name) instead.")
|
15
|
+
end
|
16
|
+
create_from_svc(service_names[0],options)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Creates and returns a +Carrot+ instance connected to a rabbitmq service with the
|
20
|
+
# specified name.
|
21
|
+
# Passes optional Hash of non-connection-related options to +Carrot.new+.
|
22
|
+
# Raises +ArgumentError+ If specified rabbitmq service is not found.
|
23
|
+
def self.create_from_svc(service_name, options={})
|
24
|
+
Carrot.new(options_for_svc(service_name,options))
|
25
|
+
end
|
26
|
+
|
27
|
+
# Merges provided options with connection options for specified rabbitmq service.
|
28
|
+
# Returns merged Hash containing (:user, :pass, :vhost, :host, :port).
|
29
|
+
# Raises +ArgumentError+ If specified rabbitmq service is not found.
|
30
|
+
def self.options_for_svc(service_name,options={})
|
31
|
+
service_props = CFRuntime::CloudApp.service_props(service_name)
|
32
|
+
if service_props.nil?
|
33
|
+
raise ArgumentError.new("Service with name #{service_name} not found")
|
34
|
+
end
|
35
|
+
cfoptions = options
|
36
|
+
cfoptions[:host] = service_props[:host]
|
37
|
+
cfoptions[:port] = service_props[:port]
|
38
|
+
cfoptions[:user] = service_props[:username]
|
39
|
+
cfoptions[:pass] = service_props[:password]
|
40
|
+
cfoptions[:vhost] = service_props[:vhost]
|
41
|
+
cfoptions
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'mongo'
|
2
|
+
require 'cfruntime/properties'
|
3
|
+
module CFRuntime
|
4
|
+
class MongoClient
|
5
|
+
|
6
|
+
# Creates and returns a Mongo +Connection+ to a single mongodb service.
|
7
|
+
# Passes optional Hash of non-connection-related options to +Mongo::Connection.new+.
|
8
|
+
# The connection is wrapped in a proxy that adds a no-argument db method to gain access
|
9
|
+
# to the database created by CloudFoundry without having to specify the name.
|
10
|
+
# Raises +ArgumentError+ If zero or multiple mongodb services are found.
|
11
|
+
def self.create(options={})
|
12
|
+
service_names = CloudApp.service_names_of_type('mongodb')
|
13
|
+
if service_names.length != 1
|
14
|
+
raise ArgumentError.new("Expected 1 service of mongodb type, " +
|
15
|
+
"but found #{service_names.length}. " +
|
16
|
+
"Consider using create_from_svc(service_name) instead.")
|
17
|
+
end
|
18
|
+
create_from_svc(service_names[0],options)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Creates and returns a Mongo +Connection+ to a mongodb service with the
|
22
|
+
# specified name.
|
23
|
+
# Passes optional Hash of non-connection-related options to +Mongo::Connection.new+.
|
24
|
+
# The connection is wrapped in a proxy that adds a no-argument db method to gain access
|
25
|
+
# to the database created by CloudFoundry without having to specify the name.
|
26
|
+
# Raises +ArgumentError+ If specified mongodb service is not found.
|
27
|
+
def self.create_from_svc(service_name,options={})
|
28
|
+
service_props = CFRuntime::CloudApp.service_props(service_name)
|
29
|
+
if service_props.nil?
|
30
|
+
raise ArgumentError.new("Service with name #{service_name} not found")
|
31
|
+
end
|
32
|
+
uri = "mongodb://#{service_props[:username]}:#{service_props[:password]}@#{service_props[:host]}:#{service_props[:port]}/#{service_props[:db]}"
|
33
|
+
conn = Mongo::Connection.from_uri(uri, options)
|
34
|
+
MongoConnection.new(conn, service_props[:db])
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the db_name for a single mongodb service.
|
38
|
+
# Raises +ArgumentError+ If zero or multiple mongodb services are found.
|
39
|
+
def self.db_name()
|
40
|
+
service_names = CloudApp.service_names_of_type('mongodb')
|
41
|
+
if service_names.length != 1
|
42
|
+
raise ArgumentError.new("Expected 1 service of mongodb type, " +
|
43
|
+
"but found #{service_names.length}. " +
|
44
|
+
"Consider using db_name_from_svc(service_name) instead.")
|
45
|
+
end
|
46
|
+
db_name_from_svc(service_names[0])
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns the db_name for the mongodb service with the specified name.
|
50
|
+
# Raises +ArgumentError+ If specified mongodb service is not found.
|
51
|
+
def self.db_name_from_svc(service_name)
|
52
|
+
service_props = CFRuntime::CloudApp.service_props(service_name)
|
53
|
+
if service_props.nil?
|
54
|
+
raise ArgumentError.new("Service with name #{service_name} not found")
|
55
|
+
end
|
56
|
+
service_props[:db]
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
class MongoConnection
|
62
|
+
|
63
|
+
instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval|object_id/ }
|
64
|
+
|
65
|
+
def initialize(connection, db_name)
|
66
|
+
@target = connection
|
67
|
+
@dbname = db_name
|
68
|
+
end
|
69
|
+
|
70
|
+
def db(db_name=@dbname, opts={})
|
71
|
+
@target.send('db', db_name, opts)
|
72
|
+
end
|
73
|
+
|
74
|
+
def target()
|
75
|
+
@target
|
76
|
+
end
|
77
|
+
|
78
|
+
protected
|
79
|
+
|
80
|
+
def method_missing(method, *args, &block)
|
81
|
+
@target.send(method, *args, &block)
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'mysql2'
|
2
|
+
require 'cfruntime/properties'
|
3
|
+
module CFRuntime
|
4
|
+
class Mysql2Client
|
5
|
+
|
6
|
+
# Creates and returns a Mysql2 +Client+ instance connected to a single mysql service.
|
7
|
+
# Passes optional Hash of non-connection-related options to +Mysql2::Client.new+.
|
8
|
+
# Raises +ArgumentError+ If zero or multiple mysql services are found.
|
9
|
+
def self.create(options={})
|
10
|
+
service_names = CloudApp.service_names_of_type('mysql')
|
11
|
+
if service_names.length != 1
|
12
|
+
raise ArgumentError.new("Expected 1 service of mysql type, " +
|
13
|
+
"but found #{service_names.length}. " +
|
14
|
+
"Consider using create_from_svc(service_name) instead.")
|
15
|
+
end
|
16
|
+
create_from_svc(service_names[0],options)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Creates and returns a Mysql2 +Client+ instance connected to a mysql service with the
|
20
|
+
# specified name.
|
21
|
+
# Passes optional Hash of non-connection-related options to +Mysql2::Client.new+.
|
22
|
+
# Raises +ArgumentError+ If specified mysql service is not found.
|
23
|
+
def self.create_from_svc(service_name, options={})
|
24
|
+
Mysql2::Client.new(options_for_svc(service_name,options))
|
25
|
+
end
|
26
|
+
|
27
|
+
# Merges provided options with connection options for specified mysql service.
|
28
|
+
# Returns merged Hash containing (:username, :password, :database, :host, :port).
|
29
|
+
# Raises +ArgumentError+ If specified mysql service is not found.
|
30
|
+
def self.options_for_svc(service_name,options={})
|
31
|
+
service_props = CFRuntime::CloudApp.service_props(service_name)
|
32
|
+
if service_props.nil?
|
33
|
+
raise ArgumentError.new("Service with name #{service_name} not found")
|
34
|
+
end
|
35
|
+
cfoptions = options
|
36
|
+
cfoptions[:host] = service_props[:host]
|
37
|
+
cfoptions[:port] = service_props[:port]
|
38
|
+
cfoptions[:username] = service_props[:username]
|
39
|
+
cfoptions[:password] = service_props[:password]
|
40
|
+
cfoptions[:database] = service_props[:database]
|
41
|
+
cfoptions
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'pg'
|
2
|
+
require 'cfruntime/properties'
|
3
|
+
module CFRuntime
|
4
|
+
class PGClient
|
5
|
+
|
6
|
+
# Creates and returns a +PGconn+ connecting to a single postgresql service.
|
7
|
+
# Passes optional Hash of non-connection-related options to +PGconn.open+.
|
8
|
+
# Raises +ArgumentError+ If zero or multiple postgresql services are found.
|
9
|
+
def self.create(options={})
|
10
|
+
service_names = CloudApp.service_names_of_type('postgresql')
|
11
|
+
if service_names.length != 1
|
12
|
+
raise ArgumentError.new("Expected 1 service of postgresql type, " +
|
13
|
+
"but found #{service_names.length}. " +
|
14
|
+
"Consider using create_from_svc(service_name) instead.")
|
15
|
+
end
|
16
|
+
cfoptions = options_for_svc(service_names[0],options)
|
17
|
+
#Pass back the options for verification
|
18
|
+
yield cfoptions if block_given?
|
19
|
+
PGconn.open(cfoptions)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Creates and returns a +PGconn+ connecting to a postgresql service with the
|
23
|
+
# specified name.
|
24
|
+
# Passes optional Hash of non-connection-related options to +PGconn.open+.
|
25
|
+
# Raises +ArgumentError+ If specified postgresql service is not found.
|
26
|
+
def self.create_from_svc(service_name,options={})
|
27
|
+
cfoptions = options_for_svc(service_name,options)
|
28
|
+
#Pass back the options for verification
|
29
|
+
yield cfoptions if block_given?
|
30
|
+
PGconn.open(cfoptions)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Merges provided options with connection options for specified postgresql service.
|
34
|
+
# Returns merged Hash containing (:user, :password, :dbname, :host, :port).
|
35
|
+
# Raises +ArgumentError+ If specified postgresql service is not found.
|
36
|
+
def self.options_for_svc(service_name,options={})
|
37
|
+
service_props = CFRuntime::CloudApp.service_props(service_name)
|
38
|
+
if service_props.nil?
|
39
|
+
raise ArgumentError.new("Service with name #{service_name} not found")
|
40
|
+
end
|
41
|
+
cfoptions = options
|
42
|
+
cfoptions[:host] = service_props[:host]
|
43
|
+
cfoptions[:port] = service_props[:port]
|
44
|
+
cfoptions[:user] = service_props[:username]
|
45
|
+
cfoptions[:password] = service_props[:password]
|
46
|
+
cfoptions[:dbname] = service_props[:database]
|
47
|
+
cfoptions
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module CFRuntime
|
2
|
+
|
3
|
+
require 'crack/json'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
class CloudApp
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# Returns true if this code is running on Cloud Foundry
|
10
|
+
def running_in_cloud?()
|
11
|
+
!ENV['VCAP_APPLICATION'].nil?
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns the application host name
|
15
|
+
def host
|
16
|
+
ENV['VCAP_APP_HOST']
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the port bound to the application
|
20
|
+
def port
|
21
|
+
ENV['VCAP_APP_PORT']
|
22
|
+
end
|
23
|
+
|
24
|
+
# Parses the VCAP_SERVICES environment variable and returns a Hash of properties
|
25
|
+
# for the specified service name. If only one service of a particular type is bound
|
26
|
+
# to the application, service_props(type) will also work.
|
27
|
+
# Example: service_props('mysql').
|
28
|
+
# Returns nil if service with specified name is not found or if zero or multiple services
|
29
|
+
# of a specified type are found.
|
30
|
+
def service_props(service_name)
|
31
|
+
registered_svcs = {}
|
32
|
+
if ENV['VCAP_SERVICES']
|
33
|
+
svcs = Crack::JSON.parse(ENV['VCAP_SERVICES'])
|
34
|
+
else
|
35
|
+
svcs = {}
|
36
|
+
end
|
37
|
+
svcs.each do |key,list|
|
38
|
+
label, version = key.split('-')
|
39
|
+
list.each do |svc|
|
40
|
+
name = svc["name"]
|
41
|
+
serviceopts = {}
|
42
|
+
serviceopts[:label] = label
|
43
|
+
serviceopts[:version] = version
|
44
|
+
serviceopts[:name] = name
|
45
|
+
cred = svc["credentials"]
|
46
|
+
if label =~ /rabbitmq/
|
47
|
+
if cred['url']
|
48
|
+
#The RabbitMQ default vhost
|
49
|
+
vhost = '/'
|
50
|
+
# The new "srs" credentials format
|
51
|
+
uri=URI.parse(cred['url'])
|
52
|
+
user=URI.unescape(uri.user) if uri.user
|
53
|
+
passwd=URI.unescape(uri.password) if uri.password
|
54
|
+
host=uri.host
|
55
|
+
port=uri.port
|
56
|
+
if uri.path =~ %r{^/(.*)}
|
57
|
+
raise ArgumentError.new("multiple segments in path of amqp URI: #{uri}") if $1.index('/')
|
58
|
+
vhost = URI.unescape($1)
|
59
|
+
end
|
60
|
+
serviceopts[:url] = cred['url']
|
61
|
+
else
|
62
|
+
# The "old" credentials format
|
63
|
+
user,passwd,host,port,vhost = %w(user pass hostname port vhost).map {|key|
|
64
|
+
cred[key]}
|
65
|
+
end
|
66
|
+
serviceopts[:vhost] = vhost
|
67
|
+
else
|
68
|
+
user,passwd,host,port,dbname,db = %w(username password hostname port name db).map {|key|
|
69
|
+
cred[key]}
|
70
|
+
if label == "mongodb"
|
71
|
+
serviceopts[:db] = db
|
72
|
+
else
|
73
|
+
serviceopts[:database] = dbname
|
74
|
+
end
|
75
|
+
end
|
76
|
+
serviceopts[:username] = user
|
77
|
+
serviceopts[:password] = passwd
|
78
|
+
serviceopts[:host] = host
|
79
|
+
serviceopts[:port] = port
|
80
|
+
registered_svcs[name] = serviceopts
|
81
|
+
if list.count == 1
|
82
|
+
registered_svcs[label] = serviceopts
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
registered_svcs[service_name]
|
87
|
+
end
|
88
|
+
|
89
|
+
# Parses the VCAP_SERVICES environment variable and returns an array of Service
|
90
|
+
# names bound to the current application.
|
91
|
+
def service_names
|
92
|
+
service_names = []
|
93
|
+
if ENV['VCAP_SERVICES']
|
94
|
+
Crack::JSON.parse(ENV['VCAP_SERVICES']).each do |key,list|
|
95
|
+
list.each do |svc|
|
96
|
+
service_names << svc["name"]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
service_names
|
101
|
+
end
|
102
|
+
|
103
|
+
# Parses the VCAP_SERVICES environment variable and returns an array of Service
|
104
|
+
# names of the specified type bound to the current application.
|
105
|
+
# Example: service_names_of_type('mysql')
|
106
|
+
def service_names_of_type(type)
|
107
|
+
service_names = []
|
108
|
+
if ENV['VCAP_SERVICES']
|
109
|
+
Crack::JSON.parse(ENV['VCAP_SERVICES']).each do |key,list|
|
110
|
+
label, version = key.split('-')
|
111
|
+
list.each do |svc|
|
112
|
+
if label == type
|
113
|
+
service_names << svc["name"]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
service_names
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|