Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Allow BOSH deploy compiling to reuse VMs.

Change-Id: I5254e126189e0978f4d3023ddb45b9cd603b1cbe
  • Loading branch information...
commit 0da04ab529fd93c7ffff584b474a4ec8ab212410 1 parent 03aad11
@lisbakke lisbakke authored
View
94 agent/lib/agent/message/compile_package.rb
@@ -20,6 +20,13 @@ def initialize(args)
@base_dir = Bosh::Agent::Config.base_dir
+ # The maximum amount of disk percentage that can be used during
+ # compilation before an error will be thrown. This is to prevent
+ # package compilation throwing arbitrary errors when disk space runs
+ # out.
+ # @attr [Integer] The max percentage of disk that can be used in
+ # compilation.
+ @max_disk_usage_pct = 90
FileUtils.mkdir_p(File.join(@base_dir, 'data', 'tmp'))
@log_file = "#{@base_dir}/data/tmp/#{Bosh::Agent::Config.agent_id}"
@@ -31,17 +38,28 @@ def initialize(args)
def start
# TODO implement sha1 verification
# TODO propagate errors
- begin
- install_dependencies
- get_source_package
- unpack_source_package
- compile
- pack
- result = upload
- return {"result" => result}
- rescue RuntimeError => e
- @logger.warn("%s\n%s" % [e.message, e.backtrace.join("\n")])
- raise Bosh::Agent::MessageHandlerError, e
+ install_dependencies
+ get_source_package
+ unpack_source_package
+ compile
+ pack
+ result = upload
+ return { "result" => result }
+ rescue RuntimeError => e
+ @logger.warn("%s\n%s" % [e.message, e.backtrace.join("\n")])
+ raise Bosh::Agent::MessageHandlerError, e
+ ensure
+ clear_log_file(@log_file)
+ delete_tmp_files
+ end
+
+ # Delete the leftover compilation files after a compilation is done. This
+ # is done so that the reuse_compilation_vms option does not fill up a VM.
+ def delete_tmp_files
+ [@compile_base, @install_base].each do |dir|
+ if Dir.exists?(dir)
+ FileUtils.rm_rf(dir)
+ end
end
end
@@ -94,10 +112,52 @@ def unpack_source_package
end
end
+ def disk_info_as_array(path)
+ # "Filesystem 1024-blocks Used Available Capacity Mounted on\n
+ # /dev/disk0s2 195312496 92743676 102312820 48% /\n"
+ df_out = `df -Pk #{path} 2>&1`
+ unless $?.exitstatus == 0
+ raise Bosh::Agent::MessageHandlerError, "Command 'df -Pk #{path}' " +
+ "on compilation VM failed with:\n#{df_out}\nexit code: " +
+ "#{$?.exitstatus}"
+ end
+
+ # "/dev/disk0s2 195312496 92743568 102312928 48% /\n"
+ df_out = df_out.sub(/[^\/]*/, "")
+ # ["/dev/disk0s2", "195312496", "92743568", "102312928", "48%", "/\n"]
+ df_out.split(/[ ]+/)
+ end
+
+ # Get the amount of total disk (in KBytes).
+ # @param [String] path Path that the disk size is being requested for.
+ # @return [Integer] The total disk size (in KBytes).
+ def disk_total(path)
+ disk_info_as_array(path)[1].to_i
+ end
+
+ # Get the amount of disk being used (in KBytes).
+ # @param [String] path Path that the disk usage is being requested for.
+ # @return [Integer] The amount being used (in KBytes).
+ def disk_used(path)
+ disk_info_as_array(path)[2].to_i
+ end
+
+ # Get the percentage of disk that is used on the compilation VM.
+ # @param [String] path Path that the disk usage is being requested for.
+ # @return [Float] The percentage of disk in use.
+ def pct_disk_used(path)
+ 100 * disk_used(path).to_f / disk_total(path).to_f
+ end
+
def compile
FileUtils.rm_rf install_dir if File.directory?(install_dir)
FileUtils.mkdir_p install_dir
-
+ pct_space_used = pct_disk_used(@compile_base)
+ if pct_space_used >= @max_disk_usage_pct
+ raise Bosh::Agent::MessageHandlerError,
+ "Compile Package Failure. Greater than #{@max_disk_usage_pct}% " +
+ "is used (#{pct_space_used}%."
+ end
Dir.chdir(compile_dir) do
# Prevent these from getting inhereted from the agent
@@ -129,6 +189,16 @@ def pack
end
end
+ # Clears the log file after a compilation runs. This is needed because if
+ # reuse_compilation_vms is being used then without clearing the log then
+ # the log from each subsequent compilation will include the previous
+ # compilation's output.
+ # @param [String] log_file Path to the log file.
+ def clear_log_file(log_file)
+ File.delete(log_file) if File.exists?(log_file)
+ @logger = Logger.new(log_file)
+ end
+
def upload
compiled_blobstore_id = nil
File.open(compiled_package, 'r') do |f|
View
36 agent/spec/unit/message/compile_package_spec.rb
@@ -15,6 +15,8 @@
@handler.compile_base = File.dirname(__FILE__) + '/../../tmp/data/compile'
@handler.install_base = File.dirname(__FILE__) + '/../../tmp/data/packages'
+ @handler.stub(:disk_used).and_return(5)
+ @handler.stub(:disk_total).and_return(10)
end
it 'should have a blobstore client' do
@@ -44,8 +46,8 @@
@handler.get_source_package
@handler.unpack_source_package
- @handler.compile
+ @handler.compile
dummy_file = File.join(@handler.install_base, @handler.package_name, @handler.package_version.to_s, 'dummy.txt')
File.exist?(dummy_file).should be_true
end
@@ -90,6 +92,38 @@
end
end
+ it 'should correctly calculate disk percentage used' do
+ @handler.stub(:disk_used).and_return(6)
+ @handler.stub(:disk_total).and_return(10)
+ @handler.pct_disk_used('./').should == 60
+ end
+
+ it 'should throw error when disk percentage >= 90' do
+ @handler.stub(:pct_disk_used).and_return(90)
+ dummy_compile_data
+
+ @handler.get_source_package
+ @handler.unpack_source_package
+ lambda {
+ @handler.compile
+ }.should raise_error(Bosh::Agent::MessageHandlerError)
+ end
+
+ it 'should clear the log file every time create_logger is called' do
+ dir = Dir.mktmpdir
+ begin
+ logger = @handler.clear_log_file("#{dir}/logger")
+ logger.info("test log data")
+ logger.close
+ File.read("#{dir}/logger")["test log data"].should_not be_nil
+ logger = @handler.clear_log_file("#{dir}/logger")
+ logger.close
+ File.read("#{dir}/logger")["test log data"].should be_nil
+ ensure
+ FileUtils.remove_entry_secure(dir)
+ end
+ end
+
def dummy_compile_data
dummy_compile_setup(dummy_package_data)
end
View
2  director/lib/director.rb
@@ -53,6 +53,8 @@ module Director
require "director/cycle_helper"
require "director/encryption_helper"
require "director/vm_creator"
+require "director/vm_data"
+require "director/vm_reuser"
require "director/deployment_plan"
require "director/deployment_plan_compiler"
require "director/duration"
View
8 director/lib/director/deployment_plan/compilation_config.rb
@@ -22,7 +22,9 @@ class CompilationConfig
# @return [Hash] environment to use when creating VMs. (optional)
attr_accessor :env
- ##
+ # @return [Bool] if VMs should be reused for compilation tasks. (optional)
+ attr_accessor :reuse_compilation_vms
+
# Creates compilation configuration spec from the deployment manifest.
# @param [DeploymentPlan] deployment
# @param [Hash] compilation_config parsed compilation config YAML section
@@ -32,6 +34,10 @@ def initialize(deployment, compilation_config)
:class => Integer, :min => 1)
network_name = safe_property(compilation_config, "network",
:class => String)
+ @reuse_compilation_vms = safe_property(compilation_config,
+ "reuse_compilation_vms",
+ :class => :boolean,
+ :optional => true)
@network = deployment.network(network_name)
if @network.nil?
raise "Compilation workers reference an unknown " +
View
132 director/lib/director/package_compiler.rb
@@ -18,6 +18,7 @@ def initialize(deployment_plan)
@network = @deployment_plan.compilation.network
@compilation_resources = @deployment_plan.compilation.cloud_properties
@compilation_env = @deployment_plan.compilation.env
+ @vm_reuser = VmReuser.new
end
def compile
@@ -28,7 +29,7 @@ def compile
@compile_tasks.each_value do |task|
if task.ready_to_compile?
@logger.info("Marking package ready for compilation: `%s/%s'" % [
- task.package.name, task.stemcell.name])
+ task.package.name, stemcell_name_version(task.stemcell)])
@ready_tasks << task
end
end
@@ -48,20 +49,28 @@ def compile
def compile_packages
@event_log.begin_stage("Compiling packages", compilation_count)
ThreadPool.new(:max_threads => @deployment_plan.compilation.workers).wrap do |pool|
- loop do
- # process as many tasks without waiting
+ begin
loop do
- task = @ready_tasks.pop
-
- break if task.nil?
- break if @job && @job.task_cancelled?
-
- @logger.info("Enqueuing package ready for compilation: `#{task}'")
- pool.process { process_task(task) }
+ # process as many tasks without waiting
+ loop do
+ task = @ready_tasks.pop
+
+ break unless task
+ break if @job && @job.task_cancelled?
+ @logger.info("Enqueuing package ready for compilation: '#{task}'")
+ pool.process { process_task(task) }
+ end
+ break if !pool.working? && @ready_tasks.empty?
+ sleep(0.1)
+ end
+ ensure
+ # Delete all of the VMs if we were reusing complation VMs. This can't
+ # happen until everything was done compiling.
+ if @deployment_plan.compilation.reuse_compilation_vms
+ @vm_reuser.each do |vm_data|
+ tear_down_vm(vm_data)
+ end
end
-
- break if !pool.working? && @ready_tasks.empty?
- sleep(0.1)
end
end
end
@@ -71,7 +80,7 @@ def process_task(task)
stemcell = task.stemcell
package_desc = "#{package.name}/#{package.version}"
- stemcell_desc = "#{stemcell.name}/#{stemcell.version})"
+ stemcell_desc = stemcell_name_version(stemcell)
with_thread_name("compile_package(#{package_desc}, #{stemcell_desc})") do
if @job && @job.task_cancelled?
@@ -88,7 +97,15 @@ def process_task(task)
def reserve_networks
@network_reservations = []
- @deployment_plan.compilation.workers.times do
+ num_workers = @deployment_plan.compilation.workers
+ num_stemcells = @ready_tasks.map { |task| task.stemcell }.uniq.size
+ # If we're reusing VMs, we allow up to num_stemcells * num_workers VMs.
+ # This is the simplest approach to dealing with a deployment that has more
+ # than 1 stemcell.
+ num_networks = @deployment_plan.compilation.reuse_compilation_vms ?
+ num_stemcells * num_workers : num_workers
+
+ num_networks.times do
reservation = NetworkReservation.new(
:type => NetworkReservation::DYNAMIC)
@network.reserve(reservation)
@@ -108,21 +125,31 @@ def release_networks
end
end
+ # A helper function for creating the stemcell string name/version commonly
+ # printed out.
+ # @param [Models::Stemcell] stemcell The stemcells to create a string for.
+ # @return [String] The string name for the stemcell.
+ def stemcell_name_version(stemcell)
+ "#{stemcell.name}/#{stemcell.version}"
+ end
+
def compile_package(task)
package = task.package
stemcell = task.stemcell
build = generate_build_number(package, stemcell)
+ agent_task = nil
+
@logger.info("Compiling package: #{package.name}/#{package.version} on " +
- "stemcell: #{stemcell.name}/#{stemcell.version}")
+ "stemcell: #{stemcell_name_version(stemcell)}")
- agent_task = nil
- prepare_vm(stemcell) do |agent|
- @logger.info("Compiling package on compilation VM")
- agent_task = agent.compile_package(package.blobstore_id, package.sha1, package.name,
- "#{package.version}.#{build}", task.dependency_spec)
+ prepare_vm(stemcell) do |vm_data|
+ agent_task = vm_data.agent.compile_package(package.blobstore_id,
+ package.sha1, package.name, "#{package.version}.#{build}",
+ task.dependency_spec)
end
+
task_result = agent_task["result"]
compiled_package = Models::CompiledPackage.create do |p|
@@ -133,10 +160,41 @@ def compile_package(task)
p.blobstore_id = task_result["blobstore_id"]
p.dependency_key = task.dependency_key
end
+
task.compiled_package = compiled_package
end
+ # This method will create a VM for each stemcell in the stemcells array
+ # passed in. The VMs are yielded and their destruction is ensured.
+ # @param [Models::Stemcell] stemcell The stemcells that need to have
+ # compilation VMs created.
+ # @yield [VmData] Yields a VmData object that contains all the data for the
+ # VM that should be used for compilation. This may be a reused VM or a
+ # freshly created VM.
def prepare_vm(stemcell)
+ # If we're reusing VMs, try to just return an already-created VM.
+ if @deployment_plan.compilation.reuse_compilation_vms
+ vm_data = @vm_reuser.get_vm(stemcell)
+ unless vm_data.nil?
+ @logger.info("Reusing compilation VM cid #{vm_data.vm.cid} for " +
+ "stemcell #{stemcell_name_version(stemcell)}.")
+ begin
+ yield vm_data
+ ensure
+ vm_data.release
+ end
+ return
+ end
+ # This shouldn't happen. If it does there's a bug.
+ if (@vm_reuser.get_num_vms(stemcell) >=
+ @deployment_plan.compilation.workers)
+ raise "There should never be more VMs for a stemcell than the " +
+ "number of workers in reuse_compilation_vms mode."
+ end
+ end
+
+ @logger.info("Creating new compilation VM for stemcell " +
+ "#{stemcell_name_version(stemcell)}.")
reservation = nil
@networks_mutex.synchronize do
reservation = @network_reservations.shift
@@ -149,21 +207,34 @@ def prepare_vm(stemcell)
vm = VmCreator.new.create(@deployment_plan.deployment, stemcell,
@compilation_resources, network_settings,
nil, @compilation_env)
+ vm_data = @vm_reuser.add_vm(reservation, vm, stemcell, network_settings)
@logger.info("Configuring compilation VM: #{vm.cid}")
+
begin
agent = AgentClient.new(vm.agent_id)
agent.wait_until_ready
configure_vm(vm, agent, network_settings)
-
- yield(agent)
+ vm_data.agent = agent
+ yield vm_data
ensure
- @logger.info("Deleting compilation VM: #{vm.cid}")
- @cloud.delete_vm(vm.cid)
- vm.destroy
+ vm_data.release
+ unless @deployment_plan.compilation.reuse_compilation_vms
+ tear_down_vm(vm_data)
+ end
end
+ end
+
+ # Tears down a VM and releases the network reservations.
+ # @param [VmData] vm_data The VmData object for the VM to tear down.
+ def tear_down_vm(vm_data)
+ vm = vm_data.vm
+ reservation = vm_data.reservation
+ @logger.info("Deleting compilation VM: #{vm.cid}")
+ @cloud.delete_vm(vm.cid)
+ vm.destroy
@networks_mutex.synchronize do
@network_reservations << reservation
end
@@ -177,7 +248,8 @@ def enqueue_unblocked_tasks(task)
dependencies.each do |blocked_task|
if blocked_task.ready_to_compile?
@logger.info("Marking unblocked package for compilation: " +
- "#{blocked_task.package.name}/#{blocked_task.stemcell.name}")
+ "#{blocked_task.package.name}/" +
+ "#{stemcell_name_version(blocked_task.stemcell)}")
@ready_tasks << blocked_task
end
end
@@ -203,7 +275,8 @@ def generate_compile_tasks
@deployment_plan.jobs.each do |job|
@logger.info("Processing job: #{job.name}")
stemcell = job.resource_pool.stemcell.stemcell
- @logger.info("Job will be deployed on: #{job.resource_pool.stemcell.name}")
+ @logger.info("Job will be deployed on: " +
+ "#{stemcell_name_version(job.resource_pool.stemcell)}")
template = job.template
template.packages.each do |package|
process_package(package, stemcell, job)
@@ -223,7 +296,8 @@ def process_package(package, stemcell, job)
if compiled_package
@logger.info("Found compiled_package: #{compiled_package.id}")
else
- @logger.info("Package \"#{package.name}\" needs to be compiled on \"#{stemcell.name}\"")
+ @logger.info("Package \"#{package.name}\" needs to be compiled on " +
+ "\"#{stemcell_name_version(stemcell)}\"")
end
task_key = [package.name, stemcell.id]
View
61 director/lib/director/vm_data.rb
@@ -0,0 +1,61 @@
+module Bosh::Director
+ # A class for storing VMs and their associated data so that they can be
+ # accessed and deleted easily.
+ class VmData
+
+ # @attr [NetworkReservation] The network reservation for this VM.
+ attr_reader :reservation
+
+ # @attr [Models::Vm] The VM.
+ attr_reader :vm
+
+ # @attr[Models::Stemcell] The Stemcell this VM is running.
+ attr_reader :stemcell
+
+ # @attr [Hash] A hash containing the network reservation.
+ attr_reader :network_settings
+
+ # @attr [Integer] The agent ID running on this VM.
+ attr_reader :agent_id
+
+ # @attr [AgentClient] The agent running on this VM.
+ attr_accessor :agent
+
+ # Initializes a VmData.
+ # @param [NetworkReservation] reservation The network reservation for this
+ # VM.
+ # @param [Models::Vm] vm The VM to be reused.
+ # @param [Models::Stemcell] stemcell The Stemcell to make the VM on.
+ # @param [Hash] network_settings A hash containing the network reservation.
+ def initialize(reservation, vm, stemcell, network_settings)
+ @reservation = reservation
+ @vm = vm
+ @stemcell = stemcell
+ @network_settings = network_settings
+ @agent_id = vm.agent_id
+ @being_used = false
+ @being_used_mutex = Mutex.new
+ end
+
+ # Marks that this VM is being used.
+ # @return [Boolean] Returns whether it could successfully mark this VM as in
+ # use.
+ def mark_in_use
+ @being_used_mutex.synchronize do
+ if @being_used
+ return false
+ else
+ @being_used = true
+ return true
+ end
+ end
+ end
+
+ # Releases the current VM to be reused.
+ def release
+ @being_used_mutex.synchronize do
+ @being_used = false
+ end
+ end
+ end
+end
View
59 director/lib/director/vm_reuser.rb
@@ -0,0 +1,59 @@
+module Bosh::Director
+ # A class for maintaining VmData objects, making reusing VMs easier.
+ class VmReuser
+ def initialize
+ @stemcells_to_vms = {}
+ end
+
+ # Adds a VM's information to the pool of VMs that can be reused.
+ # @param [NetworkReservation] reservation The network reservation for this
+ # VM.
+ # @param [Models::Vm] vm The VM to be reused.
+ # @param [Models::Stemcell] stemcell The Stemcell to make the VM on.
+ # @param [Hash] network_settings A hash containing the network reservation.
+ # @return [VmData] The VmData instance for the new VM.
+ def add_vm(reservation, vm, stemcell, network_settings)
+ vm_d = VmData.new(reservation, vm, stemcell, network_settings)
+ @stemcells_to_vms[stemcell] ||= []
+ @stemcells_to_vms[stemcell] << vm_d
+ vm_d.mark_in_use
+ vm_d
+ end
+
+ # Returns the VmData instance of a VM that is not in use and can be reused.
+ # @param [Models::Stemcell] stemcell The stemcell that the VM must be
+ # running.
+ # @return [VmData?] The VmData instance for an existing unused VM, if one
+ # exists. Otherwise, nil.
+ def get_vm(stemcell)
+ unless @stemcells_to_vms[stemcell].nil?
+ @stemcells_to_vms[stemcell].each do |vm_data|
+ if vm_data.mark_in_use
+ return vm_data
+ end
+ end
+ end
+ return nil
+ end
+
+ # Gets the total number of compilation VMs created with a given stemcell.
+ # @param [Models::Stemcell] stemcell The stemcell the VMs are running.
+ # @return [Integer] The number of VMs running a given stemcell.
+ def get_num_vms(stemcell)
+ return @stemcells_to_vms[stemcell].nil? ?
+ 0 : @stemcells_to_vms[stemcell].size
+ end
+
+ # An iterator for all compilation VMs on all stemcells.
+ # @yield [VmData] Yields each VM in VmReuser.
+ def each
+ @stemcells_to_vms.each do |stemcell, vms|
+ vms.each do |vm|
+ yield vm
+ end
+ end
+ end
+
+ end
+
+end
View
29 director/spec/unit/deployment_plan/compilation_config_spec.rb
@@ -94,5 +94,32 @@
})
config.env.should == {"password" => "password1"}
end
+
+ it "should allow reuse_compilation_vms to be set" do
+ config = BD::DeploymentPlan::CompilationConfig.new(@deployment, {
+ "workers" => 1,
+ "network" => "foo",
+ "cloud_properties" => {
+ "foo" => "bar"
+ },
+ "reuse_compilation_vms" => true
+ })
+ config.reuse_compilation_vms.should == true
+ end
+
+ it "should throw an error when a boolean property isnt boolean" do
+ lambda {
+ config = BD::DeploymentPlan::CompilationConfig.new(@deployment, {
+ "workers" => 1,
+ "network" => "foo",
+ "cloud_properties" => {
+ "foo" => "bar"
+ },
+ # the non-boolean boolean
+ "reuse_compilation_vms" => 1
+ })
+ }.should raise_error(Bosh::Director::ValidationInvalidType)
+ end
+
end
-end
+end
View
95 director/spec/unit/package_compiler_spec.rb
@@ -3,6 +3,14 @@
require File.expand_path("../../spec_helper", __FILE__)
describe Bosh::Director::PackageCompiler do
+
+ def create_task(ready)
+ task = stub(:CompileTask)
+ task.stub(:compiled_package).and_return(nil)
+ task.stub(:ready_to_compile?).and_return(ready)
+ task
+ end
+
before(:each) do
@task = stub(:CompileTask)
@package = BD::Models::Package.make(
@@ -22,6 +30,7 @@
@compilation_config.stub(:env).and_return({"env" => "baz"})
@compilation_config.stub(:workers).and_return(3)
@compilation_config.stub(:network).and_return(@network)
+ @compilation_config.stub(:reuse_compilation_vms).and_return(false)
@deployment = BD::Models::Deployment.make
@deployment_plan = stub(:DeploymentPlan)
@deployment_plan.stub(:compilation).and_return(@compilation_config)
@@ -56,12 +65,6 @@
end
describe :compile_packages do
- def create_task(ready)
- task = stub(:CompileTask)
- task.stub(:compiled_package).and_return(nil)
- task.stub(:ready_to_compile?).and_return(ready)
- task
- end
it "should schedule ready tasks" do
tasks = {"a" => create_task(true), "b" => create_task(false)}
@@ -79,6 +82,22 @@ def create_task(ready)
@package_compiler.should_receive(:process_task).with(tasks["a"])
@package_compiler.compile_packages
end
+
+ it "should delete VMs after compilation is done when reuse_compilation_vms is specified" do
+ @deployment_plan.compilation.stub(:reuse_compilation_vms).and_return(true)
+ tasks = { "a" => create_task(true), "b" => create_task(false) }
+ vm_data1 = stub(:VmData)
+ vm_data2 = stub(:VmData)
+ @package_compiler.instance_eval do
+ @compile_tasks = tasks
+ @ready_tasks = [tasks["a"]]
+ @vm_reuser = BD::VmReuser.new
+ @vm_reuser.should_receive(:each).and_yield(vm_data1).and_yield(vm_data2)
+ end
+ @package_compiler.should_receive(:tear_down_vm).twice
+ @package_compiler.stub(:process_task)
+ @package_compiler.compile_packages
+ end
end
describe :process_task do
@@ -99,8 +118,10 @@ def create_task(ready)
@task.stub(:dependency_key).and_return("dep key")
@task.stub(:dependency_spec).and_return(dep_spec)
+ vm_data = stub(:VmData)
+ vm_data.should_receive(:agent).and_return(agent)
@package_compiler.should_receive(:prepare_vm).with(@stemcell).
- and_yield(agent)
+ and_yield(vm_data)
agent.should_receive(:compile_package).
with("blob-1", "sha-1", "gcc", "1.2.1", dep_spec).
and_return({"result" => {"sha1" => "sha-2",
@@ -146,13 +167,69 @@ def create_task(ready)
@cloud.should_receive(:delete_vm).with("vm-123")
yielded_agent = nil
- @package_compiler.prepare_vm(@stemcell) do |a|
- yielded_agent = a
+ @package_compiler.prepare_vm(@stemcell) do |vm_data|
+ yielded_agent = vm_data.agent
end
yielded_agent.should == agent
BD::Models::Vm.count.should == 0
end
+
+ it "should return an existing VM if reuse_compilation_vms is specified" do
+ @deployment_plan.compilation.stub(:reuse_compilation_vms).and_return(true)
+ vm_data = stub(:VmData)
+ vm_data.should_receive(:release)
+ vm = BD::Models::Vm.make(:agent_id => "agent-1", :cid => "vm-123")
+ vm_data.should_receive(:vm).and_return(vm)
+ @package_compiler.instance_eval do
+ @vm_reuser = BD::VmReuser.new
+ @vm_reuser.should_receive(:get_vm).and_return(vm_data)
+ end
+
+ yielded_vm_d = nil
+ @package_compiler.prepare_vm(@stemcell) do |vm_d|
+ yielded_vm_d = vm_d
+ end
+ yielded_vm_d.should == vm_data
+ end
+
+ it "should not delete the new VM if reuse_compilation_vms is specified" do
+ @deployment_plan.compilation.stub(:reuse_compilation_vms).and_return(true)
+ vm_data = stub(:VmData)
+ vm_data.should_receive(:release)
+ vm = BD::Models::Vm.make(:agent_id => "agent-1", :cid => "vm-123")
+ vm_data.should_receive(:agent=)
+ @package_compiler.instance_eval do
+ @vm_reuser = BD::VmReuser.new
+ @vm_reuser.should_receive(:get_vm).and_return(nil)
+ @vm_reuser.should_receive(:add_vm).and_return(vm_data)
+ end
+
+ network_reservation = stub(:NetworkReservation)
+ @package_compiler.network_reservations = [network_reservation]
+
+ network_settings = {"net" => "ip"}
+ @network.stub(:network_settings).with(network_reservation).
+ and_return(network_settings)
+
+ vm_creator = stub(:VmCreator)
+ vm_creator.should_receive(:create).
+ with(@deployment, @stemcell, {"foo" => "bar"},
+ {"my-net" => network_settings}, nil, {"env" => "baz"}).
+ and_return(vm)
+ BD::VmCreator.stub(:new).and_return(vm_creator)
+
+ agent = stub(:AgentClient)
+ agent.should_receive(:wait_until_ready)
+ BD::AgentClient.stub(:new).with("agent-1").and_return(agent)
+
+ @package_compiler.should_receive(:configure_vm).
+ with(vm, agent, {"my-net" => network_settings})
+
+ @package_compiler.prepare_vm(@stemcell) do |vm_d|
+ end
+ @package_compiler.should_not_receive(:tear_down_vm)
+ end
end
describe :enqueue_unblocked_tasks
Please sign in to comment.
Something went wrong with that request. Please try again.