Skip to content
Browse files

Merge "agent should honor nats MAX_PAYLOAD_SIZE"

  • Loading branch information...
2 parents 7990976 + bd2aae6 commit 63fa64be6c4688cf3af9e4f44a1e15dded063f14 @pmenglund pmenglund committed with Gerrit Code Review May 4, 2012
View
1 agent/lib/agent.rb
@@ -23,6 +23,7 @@ module Bosh
require "agent/template"
require "agent/errors"
+require "agent/remote_exception"
require "agent/config"
require "agent/util"
View
9 agent/lib/agent/errors.rb
@@ -7,7 +7,14 @@ class Error < StandardError; end
class FatalError < Error; end
class StateError < Error; end
- class MessageHandlerError < Error; end
+ class MessageHandlerError < Error
+ attr_reader :blob
+ def initialize(message, blob=nil)
+ super(message)
+ @blob = blob
+ end
+ end
+
class UnknownMessage < Error; end
class LoadSettingsError < Error; end
View
96 agent/lib/agent/handler.rb
@@ -133,12 +133,7 @@ def setup_sshd_monitor
end
def handle_message(json)
- begin
- msg = Yajl::Parser.new.parse(json)
- rescue Yajl::ParseError => e
- @logger.info("Failed to parse message: #{json}: #{e.inspect}: #{e.backtrace}")
- return
- end
+ msg = Yajl::Parser.new.parse(json)
unless msg["reply_to"]
@logger.info("Missing reply_to in: #{msg}")
@@ -162,42 +157,50 @@ def handle_message(json)
processor = lookup(method)
if processor
- Thread.new {
- if processor.respond_to?(:long_running?)
- if @restarting_agent
- payload = {:exception => "restarting agent"}
- publish(reply_to, payload)
- else
- @lock.synchronize do
- if @long_running_agent_task.empty?
- process_long_running(reply_to, processor, args)
- else
- payload = {:exception => "already running long running task"}
- publish(reply_to, payload)
- end
- end
- end
- else
- payload = process(processor, args)
-
- if Config.configure && method == 'prepare_network_change'
- publish(reply_to, payload) {
- post_prepare_network_change
- }
- else
- publish(reply_to, payload)
- end
-
- end
- }
+ Thread.new { process_in_thread(processor, reply_to, method, args) }
elsif method == "get_task"
handle_get_task(reply_to, args.first)
elsif method == "shutdown"
handle_shutdown(reply_to)
else
- payload = {:exception => "unknown message #{msg.inspect}"}
- publish(reply_to, payload)
+ re = RemoteException.new("unknown message #{msg.inspect}")
+ publish(reply_to, re.to_hash)
end
+ rescue Yajl::ParseError => e
+ @logger.info("Failed to parse message: #{json}: #{e.inspect}: #{e.backtrace}")
+ end
+
+ def process_in_thread(processor, reply_to, method, args)
+ if processor.respond_to?(:long_running?)
+ if @restarting_agent
+ exception = RemoteException.new("restarting agent")
+ publish(reply_to, exception.to_hash)
+ else
+ @lock.synchronize do
+ if @long_running_agent_task.empty?
+ process_long_running(reply_to, processor, args)
+ else
+ exception = RemoteException.new("already running long running task")
+ publish(reply_to, exception.to_hash)
+ end
+ end
+ end
+ else
+ payload = process(processor, args)
+
+ if Config.configure && method == 'prepare_network_change'
+ publish(reply_to, payload) {
+ post_prepare_network_change
+ }
+ else
+ publish(reply_to, payload)
+ end
+
+ end
+ rescue => e
+ # since this is running in a thread we're going to be nice and
+ # log an error as this would otherwise be lost
+ @logger.error("#{processor.to_s}: #{e.message}\n#{e.backtrace.join("\n")}")
end
def handle_get_task(reply_to, agent_task_id)
@@ -214,14 +217,31 @@ def handle_get_task(reply_to, agent_task_id)
end
end
+ # TODO once we upgrade to nats 0.4.22 we can use
+ # NATS.server_info[:max_payload] instead of NATS_MAX_PAYLOAD_SIZE
+ NATS_MAX_PAYLOAD_SIZE = 1024 * 1024
+
def publish(reply_to, payload, &blk)
@logger.info("reply_to: #{reply_to}: payload: #{payload.inspect}")
if @credentials
+ unencrypted = payload
payload = encrypt(reply_to, payload)
end
- @nats.publish(reply_to, Yajl::Encoder.encode(payload), blk)
+ json = Yajl::Encoder.encode(payload)
+
+ # TODO figure out if we want to try to scale down the message instead
+ # of generating an exception
+ if json.bytesize < NATS_MAX_PAYLOAD_SIZE
+ @nats.publish(reply_to, json, blk)
+ else
+ msg = "message > NATS_MAX_PAYLOAD, stored in blobstore"
+ original = @credentials ? payload : unencrypted
+ exception = RemoteException.new(msg, nil, original)
+ @logger.fatal(msg)
+ @nats.publish(reply_to, exception.to_hash, blk)
+ end
end
def process_long_running(reply_to, processor, args)
@@ -251,7 +271,7 @@ def process(processor, args)
return {:value => result}
rescue Bosh::Agent::MessageHandlerError => e
@logger.info("#{e.inspect}: #{e.backtrace}")
- return {:exception => "#{e.inspect}: #{e.backtrace}"}
+ return RemoteException.from(e).to_hash
rescue Exception => e
kill_main_thread_in(KILL_AGENT_THREAD_TIMEOUT)
@logger.error("#{e.inspect}: #{e.backtrace}")
View
2 agent/lib/agent/http_handler.rb
@@ -119,7 +119,7 @@ def handle_message(json)
begin
payload = @handler.handle_message(json)
rescue => e
- payload = {:exception => e.inspect}
+ payload = RemoteException.from(e).to_hash
end
Yajl::Encoder.encode(payload, :terminator => "\n")
View
20 agent/lib/agent/message/compile_package.rb
@@ -107,9 +107,11 @@ def unpack_source_package
Dir.chdir(compile_dir) do
# TODO: error handling
output = `tar -zxf #{@source_file} 2>&1`
+ # stick the output in the blobstore
unless $?.exitstatus == 0
- raise Bosh::Agent::MessageHandlerError,
- "Compile Package Unpack Source Failure (exit code: #{$?.exitstatus}): #{output}"
+ raise Bosh::Agent::MessageHandlerError.new(
+ "Compile Package Unpack Source Failure (exit code: #{$?.exitstatus})",
+ output)
end
end
end
@@ -171,9 +173,10 @@ def compile
if File.exist?('packaging')
@logger.info("Compiling #{@package_name} #{@package_version}")
output = `bash -x packaging 2>&1`
+ # stick the output in the blobstore
unless $?.exitstatus == 0
- raise Bosh::Agent::MessageHandlerError,
- "Compile Package Failure (exit code: #{$?.exitstatus}): #{output}"
+ raise Bosh::Agent::MessageHandlerError.new(
+ "Compile Package Failure (exit code: #{$?.exitstatus})", output)
end
@logger.info(output)
end
@@ -207,10 +210,13 @@ def upload
compiled_blobstore_id = @blobstore_client.create(f)
end
compiled_sha1 = Digest::SHA1.hexdigest(File.read(compiled_package))
- @logger.info("Uploaded #{@package_name} #{@package_version}
- (sha1: #{compiled_sha1}, blobstore_id: #{compiled_blobstore_id})")
+ compile_log_id = @blobstore_client.create(@log_file)
+ @logger.info("Uploaded #{@package_name} #{@package_version} " +
+ "(sha1: #{compiled_sha1}, " +
+ "blobstore_id: #{compiled_blobstore_id})")
@logger = nil
- { "sha1" => compiled_sha1, "blobstore_id" => compiled_blobstore_id, "compile_log" => File.read(@log_file) }
+ { "sha1" => compiled_sha1, "blobstore_id" => compiled_blobstore_id,
+ "compile_log_id" => compile_log_id }
end
end
View
62 agent/lib/agent/remote_exception.rb
@@ -0,0 +1,62 @@
+module Bosh::Agent
+
+ # Helper class to pass around and fromat exceptions in the agent
+ # to the director
+ class RemoteException
+ attr_reader :message, :backtrace, :blob
+
+ def initialize(message, backtrace=nil, blob=nil)
+ @message = message
+ @backtrace = backtrace.nil? ? caller : backtrace
+ @blob = blob
+ end
+
+ # Stores the blob in the configured blobstore
+ #
+ # @return [String] blobstore id of the stored object, or an error
+ # string which can be displayed instead of the blob
+ def store_blob
+ bsc_options = Bosh::Agent::Config.blobstore_options
+ bsc_provider = Bosh::Agent::Config.blobstore_provider
+
+ blobstore = Bosh::Blobstore::Client.create(bsc_provider, bsc_options)
+
+ logger.info("Uploading blob for '#{@message}' to blobstore")
+
+ blobstore_id = nil
+ blobstore_id = blobstore.create(@blob)
+
+ blobstore_id
+ rescue Bosh::Blobstore::BlobstoreError => e
+ logger.warn("unable to upload blob for '#{@message}'")
+ "error: unable to upload blob to blobstore: #{e.message}"
+ end
+
+ # Returns a hash of the [RemoteException] suitable to convert to json
+ #
+ # @return [Hash] [RemoteException] represented as a [Hash]
+ def to_hash
+ hash = {:message => @message}
+ hash[:backtrace] = @backtrace
+ hash[:blobstore_id] = store_blob if @blob
+ {:exception => hash}
+ end
+
+ def logger
+ Bosh::Agent::Config.logger
+ end
+
+ # Helper class method that creates a [Bosh::Agent::RemoteException]
+ # from an [Exception]
+ #
+ # @return [Bosh::Agent::RemoteException]
+ def self.from(exception)
+ blob = nil
+ if exception.instance_of?(Bosh::Agent::MessageHandlerError)
+ blob = exception.blob
+ end
+ self.new(exception.message, exception.backtrace, blob)
+ end
+
+ end
+end
View
4 agent/lib/agent/util.rb
@@ -64,8 +64,8 @@ def unpack_blob(blobstore_id, sha1, install_path)
logger.info("Installing to: #{install_path}")
Dir.chdir(install_path) do
output = `tar --no-same-owner -zxvf #{blob_data_file}`
- raise Bosh::Agent::MessageHandlerError,
- "Failed to unpack blob: #{output}" unless $?.exitstatus == 0
+ raise Bosh::Agent::MessageHandlerError.new(
+ "Failed to unpack blob", output) unless $?.exitstatus == 0
end
rescue Exception => e
logger.info("Failure unpacking blob: #{e.inspect} #{e.backtrace}")
View
6 agent/spec/functional/http_spec.rb
@@ -134,18 +134,18 @@ def http_up?
it "should respond to apply message" do
task = nil
- http('apply', []) do |msg|
+ http('apply', [{'foo' => 'bar'}]) do |msg|
task = msg
task['state'].should == "running"
task['agent_task_id'].should_not be_nil
end
while task['state'] == "running"
- sleep 0.5
+ sleep 0.1
http('get_task', [ task['agent_task_id'] ]) do |msg|
task = msg
unless task['state']
- msg.should have_key('exception')
+ msg.should have_key('foo')
break
end
end
View
59 agent/spec/functional/message_spec.rb
@@ -13,9 +13,8 @@ def nats(method, args=nil)
NATS.start(:uri => @nats_uri) do
sid = NATS.subscribe('rspec') do |json|
msg = Yajl::Parser.new.parse(json)
- msg.should have_key('value')
if block_given?
- yield msg['value']
+ yield msg
end
throw :done
end
@@ -29,6 +28,11 @@ def nats(method, args=nil)
end
end
+ def get_value(msg)
+ msg.should have_key('value')
+ msg['value']
+ end
+
# wait for the first heartbeat to appear or timeout after 5 seconds
def wait_for_nats(timeout=5)
count = 0
@@ -75,24 +79,27 @@ def wait_for_nats(timeout=5)
it "should respond to state message" do
nats('state') do |msg|
- msg.should have_key('deployment')
- msg.should have_key('networks')
- msg.should have_key('resource_pool')
- msg.should have_key('agent_id')
- msg.should have_key('vm')
- msg.should have_key('job_state')
+ value = get_value(msg)
+ value.should have_key('deployment')
+ value.should have_key('networks')
+ value.should have_key('resource_pool')
+ value.should have_key('agent_id')
+ value.should have_key('vm')
+ value.should have_key('job_state')
end
end
it "should respond to ping message" do
nats('ping') do |msg|
- msg.should == 'pong'
+ value = get_value(msg)
+ value.should == 'pong'
end
end
- it "should respond to fetch logs message" do
+ it "should respond to noop message" do
nats('noop') do |msg|
- msg.should == 'nope'
+ value = get_value(msg)
+ value.should == 'nope'
end
end
@@ -108,17 +115,14 @@ def wait_for_nats(timeout=5)
it "should respond to start message" do
nats('start') do |msg|
- msg.should == 'started'
+ value = get_value(msg)
+ value.should == 'started'
end
end
it "should respond to drain message" do
task = nil
- nats("drain", ["shutdown", "bar"]) do |msg|
- task = msg
- task["state"].should == "running"
- task["agent_task_id"].should_not be_nil
- end
+ nats("drain", ["shutdown", "bar"])
while task.is_a?(Hash) && task["state"] == "running"
sleep 0.5
@@ -134,11 +138,7 @@ def wait_for_nats(timeout=5)
it "should respond to stop message" do
task = nil
- nats("stop") do |msg|
- task = msg
- task["state"].should == "running"
- task["agent_task_id"].should_not be_nil
- end
+ nats("stop")
while task.is_a?(Hash) && task["state"] == "running"
sleep 0.5
@@ -152,6 +152,21 @@ def wait_for_nats(timeout=5)
end
end
+ it "should respond to compile message" do
+ pending "need to mock package to compile"
+ nats('compile_package') do |msg|
+ msg.should have_key('value')
+ value = msg['value']
+ value.should have_key('state')
+ end
+ end
+
+ it "should return an exception for unknow message" do
+ nats('foobar') do |msg|
+ msg.should have_key('exception')
+ end
+ end
+
after(:all) do
Process.kill(:TERM, @agent_pid)
Process.kill(:TERM, @nats_pid)
View
20 agent/spec/unit/handler_spec.rb
@@ -49,7 +49,10 @@ def self.process(args)
end
end
payload = handler.process(klazz, nil)
- payload[:exception].should match(/#<Bosh::Agent::MessageHandlerError: boo!/)
+ payload.should have_key :exception
+ exception = payload[:exception]
+ exception.should have_key :message
+ exception[:message].should == "boo!"
end
it "should process long running tasks" do
@@ -149,7 +152,8 @@ def self.process(args)
msg["session_id"].should == @encryption_handler.session_id
decrypted_data = @encryption_handler.decrypt(msg["encrypted_data"])
- decrypted_data["exception"].should match(/bogus_ping/)
+ decrypted_data["exception"].should have_key("message")
+ decrypted_data["exception"]["message"].should match(/bogus_ping/)
}
encrypted_data = @encryption_handler.encrypt(
@@ -293,4 +297,16 @@ def self.process(args)
end
end
+ it "should raise a RemoteException when message > NATS_MAX_PAYLOAD" do
+ payload = "a" * (Bosh::Agent::Handler::NATS_MAX_PAYLOAD_SIZE + 1)
+ @nats.should_receive(:publish).with("reply", "exception", nil)
+
+ mock = double(Bosh::Agent::RemoteException)
+ mock.stub(:to_hash).and_return("exception")
+ Bosh::Agent::RemoteException.should_receive(:new).and_return(mock)
+
+ handler = Bosh::Agent::Handler.new
+ handler.start
+ handler.publish("reply", payload)
+ end
end
View
6 agent/spec/unit/message/compile_package_spec.rb
@@ -86,11 +86,13 @@
sha1 = Digest::SHA1.hexdigest(File.read(@handler.compiled_package))
File.open(@handler.compiled_package) do |f|
stub_blobstore_id = "bfa8e2e1-d386-4df7-ad5e-fd21f49333d6"
+ compile_log_id = "bfa8e2e1-d386-4df7-ad5e-fd21f49333d7"
- @handler.blobstore_client.stub(:create).with(instance_of(File)).and_return(stub_blobstore_id)
+ @handler.blobstore_client.stub(:create).and_return(stub_blobstore_id, compile_log_id)
+ # @handler.blobstore_client.stub(:create).with(instance_of(File)).and_return(stub_blobstore_id)
result = @handler.upload
result.delete('compile_log')
- result.should == { "sha1" => sha1, "blobstore_id" => stub_blobstore_id}
+ result.should == { "sha1" => sha1, "blobstore_id" => stub_blobstore_id, "compile_log_id" => compile_log_id}
end
end
View
73 agent/spec/unit/remote_exception_spec.rb
@@ -0,0 +1,73 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Bosh::Agent::RemoteException do
+
+ before(:each) do
+ @tmp_dir = Dir.mktmpdir
+ @base_dir = File.join(@tmp_dir, "basedir")
+ @blobstore_dir = File.join(@tmp_dir, "blobstore")
+
+ FileUtils.mkdir_p(File.join(@tmp_dir, "blobstore"))
+ FileUtils.mkdir_p(@base_dir)
+
+ Bosh::Agent::Config.base_dir = @base_dir
+ Bosh::Agent::Config.state = Bosh::Agent::State.new(File.join(@base_dir, "state.yml"))
+
+ Bosh::Agent::Config.blobstore_provider = "local"
+ Bosh::Agent::Config.blobstore_options = { "blobstore_path" => @blobstore_dir }
+ end
+
+ after(:each) do
+ FileUtils.rm_rf(@tmp_dir)
+ Bosh::Agent::Config.state = nil
+ end
+
+ it "should return valid json" do
+ message = "message"
+ backtrace = "backtrace"
+ remote_exception = Bosh::Agent::RemoteException.new(message, backtrace)
+ hash = remote_exception.to_hash
+ hash.should have_key :exception
+ exception = hash[:exception]
+ exception.should have_key :message
+ exception[:message].should == message
+ exception.should have_key :backtrace
+ exception[:backtrace].should == backtrace
+ exception.should_not have_key :blobstore_id
+ end
+
+ it "should return valid json with blob" do
+ message = "message"
+ backtrace = "backtrace"
+ blob = "blob"
+ remote_exception = Bosh::Agent::RemoteException.new(message, backtrace, blob)
+ hash = remote_exception.to_hash
+ hash.should have_key :exception
+ exception = hash[:exception]
+ exception.should have_key :blobstore_id
+ exception[:blobstore_id].should_not be_nil
+ end
+
+ it "should set a backtrace if none is provided" do
+ message = "message"
+ remote_exception = Bosh::Agent::RemoteException.new(message)
+ remote_exception.backtrace.should_not be_nil
+ end
+
+ it "should have a helper constructor" do
+ message = "message"
+ blob = "blob"
+ begin
+ raise Bosh::Agent::MessageHandlerError.new(message, blob)
+ rescue Bosh::Agent::MessageHandlerError => e
+ re = Bosh::Agent::RemoteException.from(e)
+ end
+
+ # e = Bosh::Agent::MessageHandlerError.new(message, blob)
+ # re = Bosh::Agent::RemoteException.from(e)
+ re.message.should == message
+ re.backtrace.should_not be_empty
+ re.blob.should == blob
+ end
+
+end
View
2 director/lib/director.rb
@@ -504,7 +504,7 @@ def task_timeout?(task)
# GET /resources/deadbeef
get "/resources/:id" do
- tmp_file = @resource_manager.get_resource(params[:id])
+ tmp_file = @resource_manager.get_resource_path(params[:id])
send_disposable_file(tmp_file, :type => "application/x-gzip")
end
View
46 director/lib/director/api/resource_manager.rb
@@ -3,16 +3,50 @@
module Bosh::Director
module Api
class ResourceManager
+
+ # Retrieves the resource `id` from the blobstore and stores it
+ # locally, and returns the path to the file
+ #
+ # @param [String] blobstore id
+ # @return [String] path to the contents of the blobstore id
+ def get_resource_path(id)
+ blobstore_resource(id) do |blobstore|
+ random_name = "resource-#{UUIDTools::UUID.random_create}"
+ path = File.join(Dir.tmpdir, random_name)
+
+ File.open(path, "w") do |f|
+ blobstore.get(id, f)
+ end
+
+ path
+ end
+ end
+
+ # Retrieves the resource `id` from the blobstore and returns the
+ # contents of it
+ #
+ # @param [String] blobstore id
+ # @return [String] contents of the blobstore id
def get_resource(id)
- blobstore = Bosh::Director::Config.blobstore
- random_name = "resource-#{UUIDTools::UUID.random_create}"
- path = File.join(Dir.tmpdir, random_name)
+ blobstore_resource(id) do |blobstore|
+ blobstore.get(id)
+ end
+ end
- File.open(path, "w") do |f|
- blobstore.get(id, f)
+ # Deletes the resource `id` from the blobstore
+ #
+ # @param [String] blobstore id
+ def delete_resource(id)
+ blobstore_resource(id) do |blobstore|
+ blobstore.delete(id)
end
+ end
+
+ private
- path
+ def blobstore_resource(id)
+ blobstore = Bosh::Director::Config.blobstore
+ yield blobstore
rescue Bosh::Blobstore::NotFound
raise ResourceNotFound, id
rescue Bosh::Blobstore::BlobstoreError => e
View
56 director/lib/director/client.rb
@@ -79,6 +79,7 @@ def handle_method(method_name, args)
end
result.synchronize do
+ inject_compile_log(response)
result.merge!(response)
cond.signal
end
@@ -95,9 +96,62 @@ def handle_method(method_name, args)
end
end
- raise result["exception"] if result.has_key?("exception")
+ if result.has_key?("exception")
+ exception = result["exception"]
+ raise format_exception(exception)
+ end
result["value"]
end
+ # the blob is removed from the blobstore once we have fetched it,
+ # but if there is a crash before it is injected into the response
+ # and then logged, there is a chance that we loose it
+ def inject_compile_log(response)
+ if response["value"] && response["value"].is_a?(Hash) &&
+ response["value"]["result"] &&
+ id = response["value"]["result"]["compile_log_id"]
+ rm = Api::ResourceManager.new
+ compile_log = rm.get_resource(id)
+ rm.delete_resource(id)
+ response["value"]["result"]["compile_log"] = compile_log
+ end
+ end
+
+ # this guards against old agents sending
+ # {:exception => "message"}
+ # instead of the new exception format
+ # {:exception => {
+ # :message => "message",
+ # :backtrace => "backtrace",
+ # :blobstore_id => id
+ # }
+ # }
+ def format_exception(exception)
+ if exception.instance_of?(Hash)
+ msg = exception["message"]
+ append(msg, exception["backtrace"]) do |backtrace|
+ backtrace.join("\n")
+ end
+ append(msg, exception["blobstore_id"]) do |id|
+ rm = Api::ResourceManager.new
+ blob = rm.get_resource(id)
+ rm.delete_resource(id)
+ blob
+ end
+ else
+ msg = exception
+ end
+
+ # make sure raise gets a String
+ msg.to_s
+ end
+
+ def append(msg, what)
+ if what
+ msg += "\n"
+ msg += yield what
+ end
+ end
+
end
end
View
2 director/spec/functional/director_controller_spec.rb
@@ -466,7 +466,7 @@ def expect_redirect_to_queued_task(response)
FileUtils.touch(tmp_file)
manager = mock("manager")
Bosh::Director::Api::ResourceManager.stub!(:new).and_return(manager)
- manager.stub!(:get_resource).with("deadbeef").and_return(tmp_file)
+ manager.stub!(:get_resource_path).with("deadbeef").and_return(tmp_file)
File.exists?(tmp_file).should be_true
get "/resources/deadbeef"
View
18 director/spec/unit/api/resource_manager_spec.rb
@@ -36,9 +36,25 @@
it "saves resource to a local file" do
id = @blobstore.create("some data")
- path = @manager.get_resource(id)
+ path = @manager.get_resource_path(id)
File.exists?(path).should be_true
File.read(path).should == "some data"
end
+
+ it "should return the contents of the blobstore id" do
+ contents = "some data"
+ id = @blobstore.create(contents)
+ @manager.get_resource(id).should == contents
+ end
+
+ it "should delete a resource from the blobstore" do
+ contents = "some data"
+ id = @blobstore.create(contents)
+ @manager.delete_resource(id)
+ lambda {
+ @manager.get_resource(id)
+ }.should_not raise_error BD::ResourceError
+ end
+
end
View
35 director/spec/unit/client_spec.rb
@@ -30,12 +30,19 @@
{:arguments => ["arg 1", 2, {:test => "blah"}], :method => :test_method}
).and_return { |*args|
callback = args[2]
- callback.call({"exception" => "test"})
+ callback.call({"exception" => {"message" => "test",
+ "backtrace" => ["backtrace"], "blobstore_id" => "bsid"}})
"3"
}
+ rm = double(Bosh::Director::Api::ResourceManager)
+ rm.should_receive(:get_resource).and_return("an exception")
+ rm.should_receive(:delete_resource)
+ Bosh::Director::Api::ResourceManager.should_receive(:new).and_return(rm)
@client = Bosh::Director::Client.new("test_service", "test_service_id")
- lambda {@client.test_method("arg 1", 2, {:test =>"blah"})}.should raise_exception(RuntimeError, "test")
+ lambda {
+ @client.test_method("arg 1", 2, {:test =>"blah"})
+ }.should raise_exception(RuntimeError, "test")
end
it "should handle timeouts" do
@@ -152,4 +159,28 @@
@client.test_method("arg 1", 2, {:test => "blah"}).should eql(5)
end
+ it "should inject compile log into response" do
+ nats_rpc = mock("nats_rpc")
+
+ Bosh::Director::Config.stub!(:nats_rpc).and_return(nats_rpc)
+
+ nats_rpc.should_receive(:send).with("test_service.test_service_id",
+ {:arguments => ["arg 1", 2, {:test => "blah"}], :method => :test_method}
+ ).and_return { |*args|
+ callback = args[2]
+ callback.call({"value" => {"result" => {"compile_log_id" => "foo"}}})
+ "3"
+ }
+
+ rm = double(Bosh::Director::Api::ResourceManager)
+ rm.should_receive(:get_resource).and_return("blob")
+ rm.should_receive(:delete_resource)
+ Bosh::Director::Api::ResourceManager.should_receive(:new).and_return(rm)
+
+ @client = Bosh::Director::Client.new("test_service", "test_service_id")
+ value = @client.test_method("arg 1", 2, {:test => "blah"})
+ value["result"].should have_key "compile_log"
+ value["result"]["compile_log"].should == "blob"
+ end
+
end

0 comments on commit 63fa64b

Please sign in to comment.
Something went wrong with that request. Please try again.