Permalink
Browse files

Multi version support for Mongodb

Change-Id: Ie72cac22dfe8b40269ccfede41ff5b7d532c2fd4
  • Loading branch information...
1 parent 97f3983 commit 2a7833bf95480c273d281ad35cb45dbaaec17c55 Harshawardhan Gadgil committed Jun 30, 2012
View
@@ -23,11 +23,15 @@ class VCAP::Services::MongoDB::NodeBin < VCAP::Services::Base::NodeBin
def additional_config(options, config)
options[:config_template] = File.expand_path("../../resources/mongodb.conf.erb", __FILE__)
options[:port_range] = parse_property(config, "port_range", Range)
- options[:mongod_path] = parse_property(config, "mongod_path", String)
- options[:mongorestore_path] = parse_property(config, "mongorestore_path", String)
+ options[:mongod_path] = parse_property(config, "mongod_path", Hash)
+ options[:mongorestore_path] = parse_property(config, "mongorestore_path", Hash)
options[:mongod_log_dir] = parse_property(config, "mongod_log_dir", String)
options[:max_clients] = parse_property(config, "max_clients", Integer, :optional => true)
options[:quota_files] = parse_property(config, "quota_files", Integer, :optional => true)
+
+ options[:supported_versions] = parse_property(config, "supported_versions", Array)
+ options[:default_version] = parse_property(config, "default_version", String)
+
options
end
@@ -3,7 +3,9 @@ pid: /var/vcap/sys/run/mongodb_backup.pid
backup_base_dir: /mnt/nfs/servicebackup
local_db: sqlite3:/var/vcap/services/mongodb/mongodb_node.db
service_base_dir: /var/vcap/services/mongodb/instances/
-mongodump_path: mongodump
+mongodump_path:
+ "1.8": "mongodump"
+
timeout: 600
logging:
level: debug
@@ -6,6 +6,10 @@ service:
description: 'MongoDB NoSQL store'
plans: ['free']
tags: ['mongodb', 'mongodb-1.8', 'nosql', 'document']
+ supported_versions: ["1.8"]
+ version_aliases:
+ "current": "1.8"
+
ip_route: localhost
#proxy:
# host: proxy
@@ -11,8 +11,15 @@ logging:
mongod_log_dir: /var/vcap/sys/log/mongodb
pid: /var/vcap/sys/run/mongodb_node.pid
node_id: mongodb_node_free_1
-mongod_path: mongod
-mongorestore_path: mongorestore
+
+supported_versions: ["1.8"]
+default_version: "1.8"
+
+mongod_path:
+ "1.8": "mongod"
+mongorestore_path:
+ "1.8": "mongorestore"
+
port_range:
first: 25001
last: 45000
@@ -10,8 +10,12 @@ serialization:
service_base_dir: /var/vcap/services/mongodb/instances/
download_url_template: http://dl.vcap.me/serialized/%{service}/%{name}/snapshots/%{snapshot_id}?token=%{token}
node_id: mongodb_node_free_1
-mongodump_path: mongodump
-mongorestore_path: mongorestore
+
+mongod_path:
+ "1.8": "mongod"
+mongorestore_path:
+ "1.8": "mongorestore"
+
tar_path: tar
logging:
level: debug
@@ -53,6 +53,10 @@ class ProvisionedService
property :adminpass, String, :required => true
property :db, String, :required => true
+ # Making this false for backwards compatibility
+ # Also Refer: get_version
+ property :version, String, :required => false
+
def listening?
begin
TCPSocket.open('localhost', port).close
@@ -108,7 +112,9 @@ def initialize(options)
@free_ports = Set.new
options[:port_range].each {|port| @free_ports << port}
@mutex = Mutex.new
- @supported_versions = ["1.8"]
+
+ @supported_versions = options[:supported_versions]
+ @default_version = options[:default_version]
end
def fetch_port(port=nil)
@@ -205,8 +211,10 @@ def all_bindings_list
end
def provision(plan, credential = nil, version=nil)
- @logger.info("Provision request: plan=#{plan}")
+ @logger.info("Provision request: plan=#{plan}, version=#{version}")
raise ServiceError.new(MongoDBError::MONGODB_INVALID_PLAN, plan) unless plan == @plan
+ raise ServiceError.new(ServiceError::UNSUPPORTED_VERSION, version) if !@supported_versions.include?(version)
+
port = credential && credential['port'] ? fetch_port(credential['port']) : fetch_port
name = credential && credential['name'] ? credential['name'] : UUIDTools::UUID.random_create.to_s
db = credential && credential['db'] ? credential['db'] : 'db'
@@ -218,6 +226,7 @@ def provision(plan, credential = nil, version=nil)
provisioned_service = ProvisionedService.new
provisioned_service.name = name
provisioned_service.port = port
+ provisioned_service.version = version
provisioned_service.plan = 1
provisioned_service.password = UUIDTools::UUID.random_create.to_s
provisioned_service.pid = start_instance(provisioned_service)
@@ -369,7 +378,7 @@ def restore(instance_id, backup_file)
end
# Run mongorestore
- command = "#{@mongorestore_path} -u #{username} -p#{password} --port #{port} #{backup_file}"
+ command = "#{mongorestore_exe_path(get_version(provisioned_service))} -u #{username} -p#{password} --port #{port} #{backup_file}"
output = `#{command}`
res = $?.success?
@logger.debug(output)
@@ -467,6 +476,9 @@ def update_instance(service_credential, binding_credentials)
provisioned_service.db = stored_service.db
provisioned_service.port = port
provisioned_service.pid = start_instance(provisioned_service)
+
+ provisioned_service.version = @default_version # TODO: use the version from dump manifest
+
@logger.debug("Provisioned_service: #{provisioned_service}")
raise "Cannot save provisioned_service" unless provisioned_service.save
@@ -511,6 +523,7 @@ def varz_details
stat['overall'] = overall_stats
stat['db'] = db_stats
stat['name'] = provisioned_service.name
+ stat['version'] = get_version(provisioned_service)
stats << stat
end
@@ -568,8 +581,10 @@ def get_status(instance)
def repair_instance(provisioned_service)
tmpdir = File.join(@base_dir, 'tmp', provisioned_service.name)
FileUtils.mkdir_p(tmpdir)
+ executable = mongod_exe_path(get_version(provisioned_service))
+ @logger.info("Repairing: #{provisioned_service.inspect}, using mongo: #{executable}")
begin
- `#{@mongod_path} --repair --repairpath #{tmpdir} --dbpath #{data_dir(service_dir(provisioned_service.name))}`
+ `#{executable} --repair --repairpath #{tmpdir} --dbpath #{data_dir(service_dir(provisioned_service.name))}`
@logger.warn("Service #{provisioned_service.name} db repair done")
rescue => e
@logger.error("Service #{provisioned_service.name} repair failed: #{e}")
@@ -579,7 +594,7 @@ def repair_instance(provisioned_service)
end
def start_instance(provisioned_service)
- @logger.info("Starting: #{provisioned_service.inspect}")
+ @logger.info("Starting: #{provisioned_service.inspect}, Version: #{get_version(provisioned_service)}")
lockfile = File.join(data_dir(service_dir(provisioned_service.name)), "mongod.lock")
if File.size?(lockfile)
@@ -596,7 +611,6 @@ def start_instance(provisioned_service)
pid
else
$0 = "Starting MongoDB service: #{provisioned_service.name}"
- close_fds
port = provisioned_service.port
password = provisioned_service.password
@@ -611,13 +625,19 @@ def start_instance(provisioned_service)
config = @config_template.result(binding)
config_path = File.join(dir, "mongodb.conf")
+ executable = mongod_exe_path(get_version(provisioned_service))
+
FileUtils.mkdir_p(dir)
FileUtils.mkdir_p(data_dir)
FileUtils.mkdir_p(log_dir)
FileUtils.rm_f(config_path)
File.open(config_path, "w") {|f| f.write(config)}
- cmd = "#{@mongod_path} -f #{config_path}"
+ @logger.debug("Mongo Executable: #{executable}")
+ cmd = "#{executable} -f #{config_path}"
+
+ close_fds
+
exec(cmd) rescue @logger.error("exec(#{cmd}) failed!")
end
end
@@ -756,6 +776,22 @@ def rm_lockfile(service_id)
FileUtils.rm_rf(lockfile)
end
+ def mongod_exe_path(version)
+ @mongod_path[version]
+ end
+
+ def mongorestore_exe_path(version)
+ @mongorestore_path[version]
+ end
+
+ def get_version(provisioned_service)
+ if provisioned_service.version.to_s.empty? # Handle nil / empty case (backwards compatibility)
+ @default_version
+ else
+ provisioned_service.version
+ end
+ end
+
def record_service_log(service_id)
@logger.warn(" *** BEGIN mongodb log - instance: #{service_id}")
@logger.warn("")
@@ -8,8 +8,9 @@
@app_id = "myapp"
@opts = get_node_config()
@logger = @opts[:logger]
+ @default_version = @opts[:default_version]
- @opts[:mongod_path].match "(.+#{File::SEPARATOR}).+"
+ @opts[:mongod_path][@default_version].match "(.+#{File::SEPARATOR}).+"
BINARY_DIR = $1
@config_template = ERB.new(File.read(TEMPLATE_FILE))
@@ -25,7 +26,7 @@
end
before :each do
- @resp = @node.provision("free")
+ @resp = @node.provision("free", nil, @default_version)
sleep 1
@bind_resp = @node.bind(@resp['name'], 'rw')
# Write some data in database
@@ -100,7 +101,7 @@
@node.unprovision(@resp['name'], [])
EM.add_timer(5) { EM.stop }
end
- @node.provision('free', @resp)
+ @node.provision('free', @resp, @default_version)
@node.restore(@resp['name'], dir)
@node.bind(@resp['name'], 'rw', @bind_resp)
@@ -8,9 +8,10 @@
@app_id = "myapp"
@opts = get_node_config()
@logger = @opts[:logger]
+ @default_version = @opts[:default_version]
@node = Node.new(@opts)
- @resp = @node.provision("free")
+ @resp = @node.provision("free", nil, @default_version)
EM.add_timer(1) do
@bind_resp = @node.bind(@resp['name'], 'rw')
@@ -0,0 +1,53 @@
+# Copyright (c) 2009-2011 VMware, Inc.
+$:.unshift(File.dirname(__FILE__))
+require "spec_helper"
+
+describe "provision multiple versions" do
+ MAX_CONNECTIONS = 100
+
+ before :all do
+ EM.run do
+ @opts = get_node_config()
+ @logger = @opts[:logger]
+ @supported_versions = @opts[:supported_versions]
+
+ @node = Node.new(@opts)
+ @node.max_clients = MAX_CONNECTIONS
+
+ EM.add_timer(1) { EM.stop }
+ end
+ end
+
+ it "should allow provisioning all supported versions" do
+ EM.run do
+ @supported_versions.each do |v|
+ resp = @node.provision("free", nil, v)
+
+ conn = Mongo::Connection.new('localhost', resp['port'])
+ version = conn.server_version.to_s
+ conn.close
+
+ version.start_with?(v).should be == true
+
+ @node.unprovision(resp['name'], [])
+ end
+ EM.stop
+ end
+ end
+
+ it "should throw unsupported version exception if provision for an unsupported version is requested" do
+ EM.run do
+ thrown = nil
+ begin
+ @node.provision("free", nil, "non_existent")
+ rescue => e
+ thrown = e.to_s
+ end
+ thrown.should_not be_nil
+ thrown.match(/30004/) == true # ServiceError::UNSUPPORTED_VERSION
+ EM.stop
+ end
+ end
+
+end
+
@@ -8,6 +8,7 @@
@opts = get_node_config
@logger = @opts[:logger]
@node = Node.new(@opts)
+ @default_version = @opts[:default_version]
EM.stop
end
end
@@ -16,7 +17,7 @@
EM.run do
before_instances = @node.all_instances_list
before_bindings = @node.all_bindings_list
- oi = @node.provision("free")
+ oi = @node.provision("free", nil, @default_version)
ob = @node.bind(oi["name"],'rw')
after_instances = @node.all_instances_list
after_bindings = @node.all_bindings_list
@@ -29,7 +30,7 @@
it "should be able to purge the orphan" do
EM.run do
- oi = @node.provision("free")
+ oi = @node.provision("free", nil, @default_version)
ob = @node.bind(oi["name"],'rw')
@node.purge_orphan([oi["name"]],[ob])
@node.all_instances_list.include?(oi["name"]).should be_false
@@ -9,10 +9,12 @@
EM.run do
@opts = get_node_config()
@logger = @opts[:logger]
+ @default_version = @opts[:default_version]
+
@node = Node.new(@opts)
@node.max_clients = MAX_CONNECTION
- EM.add_timer(2) { @resp = @node.provision("free") }
+ EM.add_timer(2) { @resp = @node.provision("free", nil, @default_version) }
EM.add_timer(4) { EM.stop }
end
end
@@ -12,9 +12,10 @@
@app_id = "myapp"
@opts = get_node_config()
@logger = @opts[:logger]
+ @default_version = @opts[:default_version]
@node = Node.new(@opts)
- @resp = @node.provision("free")
+ @resp = @node.provision("free", nil, @default_version)
EM.add_timer(1) do
@bind_resp = @node.bind(@resp['name'], 'rw')
@@ -123,8 +123,8 @@ def get_node_config()
:logger => Logger.new(parse_property(config, "log_file", String, :optional => true) || STDOUT, "daily"),
:plan => parse_property(config, "plan", String),
:capacity => parse_property(config, "capacity", Integer),
- :mongod_path => parse_property(config, "mongod_path", String),
- :mongorestore_path => parse_property(config, "mongorestore_path", String),
+ :mongod_path => parse_property(config, "mongod_path", Hash),
+ :mongorestore_path => parse_property(config, "mongorestore_path", Hash),
:ip_route => parse_property(config, "ip_route", String, :optional => true),
:node_id => parse_property(config, "node_id", String),
:mbus => parse_property(config, "mbus", String),
@@ -133,8 +133,10 @@ def get_node_config()
:max_clients => parse_property(config, "max_clients", Integer, :optional => true),
:base_dir => '/tmp/mongo/instances',
:mongod_log_dir => '/tmp/mongo/mongod_log',
- :local_db => 'sqlite3:/tmp/mongo/mongodb_node.db'
+ :local_db => 'sqlite3:/tmp/mongo/mongodb_node.db',
+ :supported_versions => parse_property(config, "supported_versions", Array),
+ :default_version => parse_property(config, "default_version", String)
}
- options[:logger].level = Logger::FATAL
+ options[:logger].level = Logger::DEBUG
options
end

0 comments on commit 2a7833b

Please sign in to comment.