Browse files

Add cf-runtime support for blob service

- Add service properties to CloudApp

- Add client for aws-s3 connections
Change-Id: I38d42a51167b8a8efa3803b48d671eafde4934bd
  • Loading branch information...
1 parent 981f33b commit bacaa1ede64a06666f34b0ce08b0c78075ff4025 Jennifer Hickey committed Sep 28, 2012
View
3 cfruntime-tests/spec/assets/service_bindings_by_name/Gemfile
@@ -6,4 +6,5 @@ gem 'sinatra'
gem 'json'
gem 'carrot'
gem 'pg',"~> 0.11.0"
-gem 'cf-runtime'
+gem 'cf-runtime'
+gem 'aws-s3'
View
25 cfruntime-tests/spec/assets/service_bindings_by_name/app.rb
@@ -6,6 +6,7 @@
require 'carrot'
require 'uri'
require 'pg'
+require 'aws/s3'
require 'cfruntime'
get '/env' do
@@ -85,6 +86,26 @@
read_from_rabbit(params[:key], client)
end
+post '/service/blob/:bucket' do
+ load_blob
+ AWS::S3::Bucket.create(params[:bucket])
+end
+
+get '/service/blob/:bucket' do
+ load_blob
+ AWS::S3::Bucket.find(params[:bucket]).inspect
+end
+
+post '/service/blob/:bucket/:object' do
+ load_blob
+ AWS::S3::S3Object.store(params[:object], request.body, params[:bucket])
+end
+
+get '/service/blob/:bucket/:object' do
+ load_blob
+ AWS::S3::S3Object.value(params[:object], params[:bucket])
+end
+
def load_redis
CFRuntime::RedisClient.create_from_svc('test-cfruntime-svc-test-redis')
end
@@ -107,6 +128,10 @@ def load_postgresql
client
end
+def load_blob
+ CFRuntime::AWSS3Client.create_from_svc('test-cfruntime-svc-test-blob')
+end
+
def rabbit_service
CFRuntime::CarrotClient.create_from_svc('test-cfruntime-svc-test-rabbit')
end
View
3 cfruntime-tests/spec/assets/service_bindings_by_type/Gemfile
@@ -6,4 +6,5 @@ gem 'sinatra'
gem 'json'
gem 'carrot'
gem 'pg',"~> 0.11.0"
-gem 'cf-runtime'
+gem 'cf-runtime'
+gem 'aws-s3'
View
25 cfruntime-tests/spec/assets/service_bindings_by_type/app.rb
@@ -7,6 +7,7 @@
require 'uri'
require 'pg'
require 'cfruntime'
+require 'aws/s3'
get '/env' do
ENV['VMC_SERVICES']
@@ -85,6 +86,26 @@
read_from_rabbit(params[:key], client)
end
+post '/service/blob/:bucket' do
+ load_blob
+ AWS::S3::Bucket.create(params[:bucket])
+end
+
+get '/service/blob/:bucket' do
+ load_blob
+ AWS::S3::Bucket.find(params[:bucket]).inspect
+end
+
+post '/service/blob/:bucket/:object' do
+ load_blob
+ AWS::S3::S3Object.store(params[:object], request.body, params[:bucket])
+end
+
+get '/service/blob/:bucket/:object' do
+ load_blob
+ AWS::S3::S3Object.value(params[:object], params[:bucket])
+end
+
def load_redis
CFRuntime::RedisClient.create
end
@@ -107,6 +128,10 @@ def load_postgresql
client
end
+def load_blob
+ CFRuntime::AWSS3Client.create
+end
+
def rabbit_service
CFRuntime::CarrotClient.create
end
View
19 cfruntime-tests/spec/cloud_services_spec.rb
@@ -23,6 +23,10 @@
verify_post_to_app @app_name, "service/mongo/abc", 'mongoabc'
verify_post_to_app @app_name, "service/rabbit/abc", 'rabbitabc'
verify_post_to_app @app_name, "service/postgresql/abc", 'postgresqlabc'
+ contents = post_to_app @app_name, "service/blob/container1", "dummy"
+ contents.response_code.should == 200
+ contents.close
+ verify_post_to_app @app_name, "service/blob/container1/file1", "abc"
delete_app @app_name
end
@@ -36,11 +40,15 @@
provision_mongodb_service("test-#{@app_name}2-mongo",@app_name)
provision_postgresql_service("test-#{@app_name}2-postgres",@app_name)
start_app @app_name
- verify_post_to_app @app_name, "service/mysql/abc", 'mysqlabc'
- verify_post_to_app @app_name, "service/redis/abc", 'redisabc'
- verify_post_to_app @app_name, "service/mongo/abc", 'mongoabc'
- verify_post_to_app @app_name, "service/rabbit/abc", 'rabbitabc'
- verify_post_to_app @app_name, "service/postgresql/abc", 'postgresqlabc'
+ verify_post_to_app @app_name, "service/mysql/abc", "mysqlabc"
+ verify_post_to_app @app_name, "service/redis/abc", "redisabc"
+ verify_post_to_app @app_name, "service/mongo/abc", "mongoabc"
+ verify_post_to_app @app_name, "service/rabbit/abc", "rabbitabc"
+ verify_post_to_app @app_name, "service/postgresql/abc", "postgresqlabc"
+ contents = post_to_app @app_name, "service/blob/container1", "dummy"
+ contents.response_code.should == 200
+ contents.close
+ verify_post_to_app @app_name, "service/blob/container1/file1", "abc"
delete_app @app_name
end
@@ -71,6 +79,7 @@ def deploy_and_provision_svcs app,app_dir
provision_rabbitmq_service("test-#{app}-rabbit",app)
provision_mongodb_service("test-#{app}-mongo",app)
provision_postgresql_service("test-#{app}-postgres",app)
+ provision_blob_service("test-#{app}-blob",app)
start_app app
end
end
View
12 cfruntime-tests/spec/spec_helper.rb
@@ -185,6 +185,18 @@ def provision_postgresql_service name,app
attach_provisioned_service app,service_manifest
end
+ def provision_blob_service name,app
+ @client.create_service(:blob, name)
+ service_manifest = {
+ :type=>"generic",
+ :vendor=>"blob",
+ :tier=>"free",
+ :version=>"0.5.1",
+ :name=>name,
+ }
+ attach_provisioned_service app,service_manifest
+ end
+
def attach_provisioned_service app, service_manifest
app_manifest = get_app_status app
provisioned_service = app_manifest[:services]
View
7 cfruntime/Gemfile.lock
@@ -14,13 +14,18 @@ GEM
amq-client (~> 0.9.4)
amq-protocol (>= 0.9.4)
eventmachine
+ aws-s3 (0.6.3)
+ builder
+ mime-types
+ xml-simple
bson (1.6.4)
builder (3.0.0)
carrot (1.2.0)
ci_reporter (1.6.9)
builder (>= 2.1.2)
diff-lcs (1.1.3)
eventmachine (0.12.10)
+ mime-types (1.19)
mongo (1.2.4)
bson (>= 1.2.4)
multi_json (1.3.6)
@@ -45,12 +50,14 @@ GEM
simplecov-html (0.5.3)
simplecov-rcov (0.2.3)
simplecov (>= 0.4.1)
+ xml-simple (1.1.1)
PLATFORMS
ruby
DEPENDENCIES
amqp (~> 0.8)
+ aws-s3 (~> 0.6.3)
carrot (~> 1.0)
cf-runtime!
ci_reporter (~> 1.6.5)
View
1 cfruntime/cf-runtime.gemspec
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
s.add_development_dependency "mongo", "~> 1.2.0"
s.add_development_dependency "pg", "~> 0.11.0"
s.add_development_dependency "mysql2", "~> 0.2.7"
+ s.add_development_dependency "aws-s3", "~> 0.6.3"
s.add_development_dependency "rake", "~> 0.9.2"
s.add_development_dependency "rack-test", "~> 0.6.1"
s.add_development_dependency "rspec", "~> 2.6.0"
View
4 cfruntime/lib/cfruntime.rb
@@ -22,5 +22,9 @@
require 'cfruntime/redis'
rescue LoadError
end
+begin
+ require 'cfruntime/aws_s3'
+rescue LoadError
+end
require 'cfruntime/properties'
require 'cfruntime/version'
View
43 cfruntime/lib/cfruntime/aws_s3.rb
@@ -0,0 +1,43 @@
+require 'aws/s3'
+require 'cfruntime/properties'
+module CFRuntime
+ class AWSS3Client
+
+ # Creates and returns an AWS-S3 +Client+ instance.
+ # Passes optional Hash of non-connection-related options to +AWS::S3::Base.establish_connection!+.
+ # Raises +ArgumentError+ If zero or multiple blob services are found.
+ def self.create(options={})
+ service_names = CloudApp.service_names_of_type('blob')
+ if service_names.length != 1
+ raise ArgumentError.new("Expected 1 service of blob type, " +
+ "but found #{service_names.length}. " +
+ "Consider using create_from_svc(service_name) instead.")
+ end
+ create_from_svc(service_names[0],options)
+ end
+
+ # Creates and returns a AWS-S3 +Client+ instance connected to a blob service with the
+ # specified name.
+ # Passes optional Hash of non-connection-related options to +AWS::S3::Base.establish_connection!+.
+ # Raises +ArgumentError+ If specified blob service is not found.
+ def self.create_from_svc(service_name, options={})
+ AWS::S3::Base.establish_connection!(options_for_svc(service_name,options))
+ end
+
+ # Merges provided options with connection options for specified blob service.
+ # Returns merged Hash containing (:access_key_id, :secret_access_key, :server, :port).
+ # Raises +ArgumentError+ If specified blob service is not found.
+ def self.options_for_svc(service_name,options={})
+ service_props = CFRuntime::CloudApp.service_props(service_name)
+ if service_props.nil?
+ raise ArgumentError.new("Service with name #{service_name} not found")
+ end
+ cfoptions = options
+ cfoptions[:server] = service_props[:host]
+ cfoptions[:port] = service_props[:port]
+ cfoptions[:access_key_id ] = service_props[:username]
+ cfoptions[:secret_access_key] = service_props[:password]
+ cfoptions
+ end
+ end
+end
View
1 cfruntime/lib/cfruntime/parser.rb
@@ -1,3 +1,4 @@
+require "cfruntime/parser/blob_parser"
require "cfruntime/parser/default_parser"
require "cfruntime/parser/mongodb_parser"
require "cfruntime/parser/mysql_parser"
View
15 cfruntime/lib/cfruntime/parser/blob_parser.rb
@@ -0,0 +1,15 @@
+module CFRuntime
+ class BlobParser
+ def self.parse(svc)
+ serviceopts = {}
+ { :username => :username,
+ :password => :password,
+ :hostname => :host,
+ :port => :port
+ }.each do |from, to|
+ serviceopts[to] = svc["credentials"][from.to_s]
+ end
+ serviceopts
+ end
+ end
+end
View
65 cfruntime/spec/aws_s3_spec.rb
@@ -0,0 +1,65 @@
+require 'spec_helper'
+require 'cfruntime/aws_s3'
+
+describe 'CFRuntime::AWSS3Client' do
+
+ after(:each) do
+ AWS::S3::Base.disconnect!
+ end
+
+ it 'creates a client with a Blob service by type and no additional options' do
+ svcs = {"blob-#{blob_version}"=>[create_blob_service('blob-test')]}
+ with_vcap_services(svcs) do
+ client = CFRuntime::AWSS3Client.create
+ client.options.should=={:access_key_id=>"testuser",:secret_access_key=>"testpw", :server=>SOME_SERVER, :port=>SOME_SERVICE_PORT}
+ end
+ end
+
+ it 'creates a client with a Blob service by type and additional options' do
+ svcs = {"blob-#{blob_version}"=>[create_blob_service('blob-test')]}
+ with_vcap_services(svcs) do
+ client = CFRuntime::AWSS3Client.create({:use_ssl=>"true"})
+ client.options.should=={:access_key_id=>"testuser",:secret_access_key=>"testpw",
+ :server=>SOME_SERVER, :port=>SOME_SERVICE_PORT, :use_ssl=>"true"}
+ end
+ end
+
+ it 'creates a client with a Blob service by name and no additional options' do
+ svcs = {"blob-#{blob_version}"=>[create_blob_service('blob-test')]}
+ with_vcap_services(svcs) do
+ client = CFRuntime::AWSS3Client.create_from_svc('blob-test')
+ client.options.should=={:access_key_id=>"testuser",:secret_access_key=>"testpw",
+ :server=>SOME_SERVER, :port=>SOME_SERVICE_PORT}
+ end
+ end
+
+ it 'creates a client with a Blob service by name and additional options' do
+ svcs = {"blob-#{blob_version}"=>[create_blob_service('blob-test')]}
+ with_vcap_services(svcs) do
+ client = CFRuntime::AWSS3Client.create_from_svc('blob-test', {:use_ssl=>"true"})
+ client.options.should=={:access_key_id=>"testuser",:secret_access_key=>"testpw",
+ :server=>SOME_SERVER, :port=>SOME_SERVICE_PORT, :use_ssl=>"true"}
+ end
+ end
+
+ it 'raises an ArgumentError if no service of Blob type found' do
+ ENV['VCAP_SERVICES'] = nil
+ expect{CFRuntime::AWSS3Client.create}.to raise_error(ArgumentError,
+ 'Expected 1 service of blob type, but found 0. Consider using create_from_svc(service_name) instead.')
+ end
+
+ it 'raises an ArgumentError if multiple services of Blob type found' do
+ svcs = {"blob-#{blob_version}"=>[create_blob_service('blob-test'),
+ create_blob_service('blob-test2')]}
+ with_vcap_services(svcs) do
+ expect{CFRuntime::AWSS3Client.create}.to raise_error(ArgumentError,
+ 'Expected 1 service of blob type, but found 2. Consider using create_from_svc(service_name) instead.')
+ end
+ end
+
+ it 'raises an ArgumentError if Blob service of specified name is not found' do
+ ENV['VCAP_SERVICES'] = nil
+ expect{CFRuntime::AWSS3Client.create_from_svc('non-existent-blob')}.to raise_error(ArgumentError,
+ 'Service with name non-existent-blob not found')
+ end
+end
View
11 cfruntime/spec/support/service_spec_helpers.rb
@@ -61,6 +61,10 @@ def postgres_version
"0.11.0"
end
+ def blob_version
+ "0.5.1"
+ end
+
def create_mongo_service(name, url=false)
svc = "{\"name\":\"#{name}\",\"label\":\"mongodb-#{mongo_version}\",\"plan\":\"free\"," +
"\"tags\":[\"mongodb\",\"mongodb-#{mongo_version}\"],\"credentials\":{\"hostname\":\"#{SOME_SERVER}\"," +
@@ -71,6 +75,13 @@ def create_mongo_service(name, url=false)
svc
end
+ def create_blob_service(name)
+ svc = "{\"name\":\"#{name}\",\"label\":\"blob-#{blob_version}\",\"plan\":\"free\"," +
+ "\"credentials\":{\"hostname\":\"#{SOME_SERVER}\"," +
+ "\"host\":\"#{SOME_SERVER}\",\"port\":#{SOME_SERVICE_PORT},\"username\":\"testuser\",\"password\":\"testpw\"" +
+ "}}"
+ end
+
def create_redis_service(name)
create_service(name, "redis", redis_version, "redisdata")
end

0 comments on commit bacaa1e

Please sign in to comment.