cf-runtime 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|