From 1df7ab6d34be9991f2ccaa448d36d032aedd9ccd Mon Sep 17 00:00:00 2001 From: Jake Spain Date: Fri, 24 Feb 2023 09:55:49 -0500 Subject: [PATCH] Fix tests based on new dns config --- spec/fixtures/extra_config2.yaml | 1 + spec/fixtures/vmpooler.yaml | 8 +- spec/fixtures/vmpooler2.yaml | 8 +- spec/helpers.rb | 10 + spec/integration/api/v2/ondemandvm_spec.rb | 93 ++---- spec/integration/api/v2/vm_spec.rb | 60 ++-- spec/integration/api/v2/vm_template_spec.rb | 34 ++- spec/unit/env_config.rb | 1 - spec/unit/pool_manager_spec.rb | 306 +++++++++++++++++--- spec/unit/vmpooler_spec.rb | 6 +- 10 files changed, 386 insertions(+), 141 deletions(-) diff --git a/spec/fixtures/extra_config2.yaml b/spec/fixtures/extra_config2.yaml index e9f30d8..af122c2 100644 --- a/spec/fixtures/extra_config2.yaml +++ b/spec/fixtures/extra_config2.yaml @@ -8,4 +8,5 @@ - name: 'pool05' size: 5 provider: dummy + dns_plugin: 'example' ready_ttl: 5 \ No newline at end of file diff --git a/spec/fixtures/vmpooler.yaml b/spec/fixtures/vmpooler.yaml index c5c0522..c7dad1b 100644 --- a/spec/fixtures/vmpooler.yaml +++ b/spec/fixtures/vmpooler.yaml @@ -23,9 +23,13 @@ allowed_tags: - 'created_by' - 'project' - domain: 'example.com' prefix: 'poolvm-' +:dns_configs: + :example: + dns_class: dynamic-dns + domain: 'example.com' + # Uncomment the lines below to suppress metrics to STDOUT # :statsd: # server: 'localhost' @@ -36,8 +40,10 @@ - name: 'pool01' size: 5 provider: dummy + dns_plugin: 'example' ready_ttl: 5 - name: 'pool02' size: 5 provider: dummy + dns_plugin: 'example' ready_ttl: 5 diff --git a/spec/fixtures/vmpooler2.yaml b/spec/fixtures/vmpooler2.yaml index a016d30..92731a5 100644 --- a/spec/fixtures/vmpooler2.yaml +++ b/spec/fixtures/vmpooler2.yaml @@ -23,9 +23,13 @@ allowed_tags: - 'created_by' - 'project' - domain: 'example.com' prefix: 'poolvm-' +:dns_configs: + :example: + dns_class: dynamic-dns + domain: 'example.com' + # Uncomment the lines below to suppress metrics to STDOUT # :statsd: # server: 'localhost' @@ -36,8 +40,10 @@ - name: 'pool03' size: 5 provider: dummy + dns_plugin: 'example' ready_ttl: 5 - name: 'pool04' size: 5 provider: dummy + dns_plugin: 'example' ready_ttl: 5 diff --git a/spec/helpers.rb b/spec/helpers.rb index 87245db..53af74c 100644 --- a/spec/helpers.rb +++ b/spec/helpers.rb @@ -14,6 +14,16 @@ class MockLogger end end +class MockPoolManagerDnsBase + def delete_record(hostname) + + end + + def create_or_replace_record(hostname) + + end +end + def expect_json(ok = true, http = 200) expect(last_response.header['Content-Type']).to eq('application/json') diff --git a/spec/integration/api/v2/ondemandvm_spec.rb b/spec/integration/api/v2/ondemandvm_spec.rb index 5d6d887..446b071 100644 --- a/spec/integration/api/v2/ondemandvm_spec.rb +++ b/spec/integration/api/v2/ondemandvm_spec.rb @@ -28,10 +28,16 @@ describe Vmpooler::API::V2 do 'compute2' => 0 } }, + dns_configs: { + :mock => { + 'dns_class' => 'mock', + 'domain' => 'example.com' + } + }, pools: [ - {'name' => 'pool1', 'size' => 0, 'clone_target' => 'compute1'}, - {'name' => 'pool2', 'size' => 0, 'clone_target' => 'compute2'}, - {'name' => 'pool3', 'size' => 0, 'clone_target' => 'compute1'} + {'name' => 'pool1', 'size' => 0, 'clone_target' => 'compute1', 'dns_plugin' => 'mock'}, + {'name' => 'pool2', 'size' => 0, 'clone_target' => 'compute2', 'dns_plugin' => 'mock'}, + {'name' => 'pool3', 'size' => 0, 'clone_target' => 'compute1', 'dns_plugin' => 'mock'} ], alias: { 'poolone' => ['pool1'], @@ -39,9 +45,7 @@ describe Vmpooler::API::V2 do }, pool_names: [ 'pool1', 'pool2', 'pool3', 'poolone' ], providers: { - :dummy => { - 'domain' => 'dummy.com' - } + :dummy => {}, } } } @@ -117,22 +121,23 @@ describe Vmpooler::API::V2 do post "#{prefix}/ondemandvm", '{"pool2":"1"}' end - context 'with domain set in the config' do - let(:domain) { 'example.com' } - before(:each) do - config[:config]['domain'] = domain - end + # Domain is always included in reply now + # context 'with domain set in the config' do + # let(:domain) { 'example.com' } + # before(:each) do + # config[:config]['domain'] = domain + # end - it 'should include domain in the return reply' do - post "#{prefix}/ondemandvm", '{"poolone":"1"}' - expect_json(true, 201) - expected = { - "ok": true, - "request_id": uuid, - } - expect(last_response.body).to eq(JSON.pretty_generate(expected)) - end - end + # it 'should include domain in the return reply' do + # post "#{prefix}/ondemandvm", '{"poolone":"1"}' + # expect_json(true, 201) + # expected = { + # "ok": true, + # "request_id": uuid, + # } + # expect(last_response.body).to eq(JSON.pretty_generate(expected)) + # end + # end end context 'with a resource request that exceeds the specified limit' do @@ -266,56 +271,12 @@ describe Vmpooler::API::V2 do "ready": true, "pool1": { "hostname": [ - vmname + "#{vmname}.example.com" ] } } expect(last_response.body).to eq(JSON.pretty_generate(expected)) end - - context 'with domain set' do - let(:domain) { 'example.com' } - before(:each) do - config[:config]['domain'] = domain - end - - it 'should include the domain in the hostname as fqdn, not a separate key unlike in v1' do - get "#{prefix}/ondemandvm/#{uuid}" - expected = { - "ok": true, - "request_id": uuid, - "ready": true, - "pool1": { - "hostname": [ - "#{vmname}.#{domain}" - ] - } - } - expect(last_response.body).to eq(JSON.pretty_generate(expected)) - end - end - - context 'with domain set in the provider' do - let(:domain) { 'dummy.com' } - before(:each) do - config[:pools][0]['provider'] = 'dummy' - end - - it 'should include the domain in the hostname as fqdn, not a separate key unlike in v1' do - get "#{prefix}/ondemandvm/#{uuid}" - expected = { - "ok": true, - "request_id": uuid, - "ready": true, - "pool1": { - "hostname": [ - "#{vmname}.#{domain}" - ] - } - } - expect(last_response.body).to eq(JSON.pretty_generate(expected)) - end - end end context 'with a deleted request' do diff --git a/spec/integration/api/v2/vm_spec.rb b/spec/integration/api/v2/vm_spec.rb index 411cbb9..13f80d2 100644 --- a/spec/integration/api/v2/vm_spec.rb +++ b/spec/integration/api/v2/vm_spec.rb @@ -25,14 +25,28 @@ describe Vmpooler::API::V2 do 'vm_lifetime_auth' => 2 }, providers: { - vsphere: {'domain' => 'one.example.com'}, - gce: {'domain' => 'two.example.com'}, - foo: {'domain' => 'three.example.com'} + vsphere: {}, + gce: {}, + foo: {} + }, + dns_configs: { + :one => { + 'dns_class' => 'mock', + 'domain' => 'one.example.com' + }, + :two => { + 'dns_class' => 'mock', + 'domain' => 'two.example.com' + }, + :three => { + 'dns_class' => 'mock', + 'domain' => 'three.example.com' + } }, pools: [ - {'name' => 'pool1', 'size' => 5, 'provider' => 'vsphere'}, - {'name' => 'pool2', 'size' => 10, 'provider' => 'gce'}, - {'name' => 'pool3', 'size' => 10, 'provider' => 'foo'} + {'name' => 'pool1', 'size' => 5, 'provider' => 'vsphere', 'dns_plugin' => 'one'}, + {'name' => 'pool2', 'size' => 10, 'provider' => 'gce', 'dns_plugin' => 'two'}, + {'name' => 'pool3', 'size' => 10, 'provider' => 'foo', 'dns_plugin' => 'three'} ], statsd: { 'prefix' => 'stats_prefix'}, alias: { 'poolone' => ['pool1'] }, @@ -341,27 +355,29 @@ describe Vmpooler::API::V2 do expect(pool_has_ready_vm?('pool1', '2abcdefghijklmnop', redis)).to eq(true) end - it 'returns the second VM when the first fails to respond' do - create_ready_vm 'pool1', vmname, redis - create_ready_vm 'pool1', "2#{vmname}", redis + # The helper create_ready_vm inherently means that the vm has already reached a + # ready state and that open_socket already returned sucessfully before being moved to ready. + # it 'returns the second VM when the first fails to respond' do + # create_running_vm 'pool1', vmname, redis + # create_ready_vm 'pool1', "2#{vmname}", redis - allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).with(vmname, nil).and_raise('mockerror') - allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).with("2#{vmname}", nil).and_return(socket) + # allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).with(vmname, nil).and_raise('mockerror') + # allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).with("2#{vmname}", nil).and_return(socket) - post "#{prefix}/vm", '{"pool1":"1"}' - expect_json(ok = true, http = 200) + # post "#{prefix}/vm", '{"pool1":"1"}' + # expect_json(ok = true, http = 200) - expected = { - ok: true, - pool1: { - hostname: "2#{vmname}" - } - } + # expected = { + # ok: true, + # pool1: { + # hostname: "2#{vmname}.one.example.com" + # } + # } - expect(last_response.body).to eq(JSON.pretty_generate(expected)) + # expect(last_response.body).to eq(JSON.pretty_generate(expected)) - expect(pool_has_ready_vm?('pool1', vmname, redis)).to be false - end + # expect(pool_has_ready_vm?('pool1', vmname, redis)).to be false + # end context '(auth not configured)' do it 'does not extend VM lifetime if auth token is provided' do diff --git a/spec/integration/api/v2/vm_template_spec.rb b/spec/integration/api/v2/vm_template_spec.rb index 862df25..3c6d037 100644 --- a/spec/integration/api/v2/vm_template_spec.rb +++ b/spec/integration/api/v2/vm_template_spec.rb @@ -24,11 +24,17 @@ describe Vmpooler::API::V2 do 'site_name' => 'test pooler', 'vm_lifetime_auth' => 2, }, + dns_configs: { + :example => { + 'dns_class' => 'mock', + 'domain' => 'example.com' + } + }, providers: { vsphere: {} }, pools: [ - {'name' => 'pool1', 'size' => 5}, - {'name' => 'pool2', 'size' => 10}, - {'name' => 'poolone', 'size' => 1} + {'name' => 'pool1', 'size' => 5, 'provider' => 'vsphere', 'dns_plugin' => 'example'}, + {'name' => 'pool2', 'size' => 10, 'provider' => 'vsphere', 'dns_plugin' => 'example'}, + {'name' => 'poolone', 'size' => 1, 'provider' => 'vsphere', 'dns_plugin' => 'example'} ], statsd: { 'prefix' => 'stats_prefix'}, alias: { 'poolone' => 'pool1' }, @@ -60,7 +66,7 @@ describe Vmpooler::API::V2 do expected = { ok: true, pool1: { - hostname: 'abcdefghijklmnop' + hostname: 'abcdefghijklmnop.example.com' } } @@ -77,7 +83,7 @@ describe Vmpooler::API::V2 do expected = { ok: true, poolone: { - hostname: 'abcdefghijklmnop' + hostname: 'abcdefghijklmnop.example.com' } } expect_json(ok = true, http = 200) @@ -126,10 +132,10 @@ describe Vmpooler::API::V2 do expected = { ok: true, pool1: { - hostname: 'abcdefghijklmnop' + hostname: 'abcdefghijklmnop.example.com' }, pool2: { - hostname: 'qrstuvwxyz012345' + hostname: 'qrstuvwxyz012345.example.com' } } @@ -151,17 +157,17 @@ describe Vmpooler::API::V2 do expected = { ok: true, pool1: { - hostname: [ '1abcdefghijklmnop', '2abcdefghijklmnop' ] + hostname: [ '1abcdefghijklmnop.example.com', '2abcdefghijklmnop.example.com' ] }, pool2: { - hostname: [ '1qrstuvwxyz012345', '2qrstuvwxyz012345', '3qrstuvwxyz012345' ] + hostname: [ '1qrstuvwxyz012345.example.com', '2qrstuvwxyz012345.example.com', '3qrstuvwxyz012345.example.com' ] } } result = JSON.parse(last_response.body) expect(result['ok']).to eq(true) - expect(result['pool1']['hostname']).to include('1abcdefghijklmnop', '2abcdefghijklmnop') - expect(result['pool2']['hostname']).to include('1qrstuvwxyz012345', '2qrstuvwxyz012345', '3qrstuvwxyz012345') + expect(result['pool1']['hostname']).to include('1abcdefghijklmnop.example.com', '2abcdefghijklmnop.example.com') + expect(result['pool2']['hostname']).to include('1qrstuvwxyz012345.example.com', '2qrstuvwxyz012345.example.com', '3qrstuvwxyz012345.example.com') expect_json(ok = true, http = 200) end @@ -265,7 +271,7 @@ describe Vmpooler::API::V2 do expected = { ok: true, pool1: { - hostname: 'abcdefghijklmnop' + hostname: 'abcdefghijklmnop.example.com' } } @@ -291,7 +297,7 @@ describe Vmpooler::API::V2 do expected = { ok: true, pool1: { - hostname: 'abcdefghijklmnop' + hostname: 'abcdefghijklmnop.example.com' } } expect(last_response.body).to eq(JSON.pretty_generate(expected)) @@ -311,7 +317,7 @@ describe Vmpooler::API::V2 do expected = { ok: true, pool1: { - hostname: 'abcdefghijklmnop' + hostname: 'abcdefghijklmnop.example.com' } } expect_json(ok = true, http = 200) diff --git a/spec/unit/env_config.rb b/spec/unit/env_config.rb index c64c78f..1529cf7 100644 --- a/spec/unit/env_config.rb +++ b/spec/unit/env_config.rb @@ -22,7 +22,6 @@ describe 'Vmpooler' do ['prefix', test_string, ""], ['logfile', test_string, nil], ['site_name', test_string, nil], - ['domain', test_string, nil], ['clone_target', test_string, nil], ['create_folders', test_bool, nil], ['create_template_delta_disks', test_bool, nil], diff --git a/spec/unit/pool_manager_spec.rb b/spec/unit/pool_manager_spec.rb index bfcead3..0e9c5e6 100644 --- a/spec/unit/pool_manager_spec.rb +++ b/spec/unit/pool_manager_spec.rb @@ -20,6 +20,7 @@ describe 'Pool Manager' do let(:current_time) { Time.now } let(:provider_options) { {} } + let(:dns_options) { {} } let(:redis_connection_pool) { Vmpooler::PoolManager::GenericConnectionPool.new( metrics: metrics, connpool_type: 'redis_connection_pool', @@ -30,16 +31,19 @@ describe 'Pool Manager' do } let(:provider) { Vmpooler::PoolManager::Provider::Base.new(config, logger, metrics, redis_connection_pool, 'mock_provider', provider_options) } - + let(:dns_plugin) { MockPoolManagerDnsBase.new } let(:config) { YAML.load(<<-EOT --- :config: {} +:dns_configs: {} :providers: :mock: + dns_class: base :redis: {} :pools: - name: '#{pool}' size: 1 + dns_plugin: 'mock' EOT ) } @@ -233,6 +237,8 @@ EOT describe '#remove_nonexistent_vm' do before do + expect(subject).to receive(:get_dns_plugin_class_for_pool).and_return(dns_plugin) + expect(dns_plugin).to receive(:delete_record) expect(subject).not_to be_nil end @@ -876,17 +882,17 @@ EOT end it 'calls _clone_vm' do - expect(subject).to receive(:_clone_vm).with(pool_object,provider,nil,nil) + expect(subject).to receive(:_clone_vm).with(pool_object,provider,dns_plugin,nil,nil) - subject.clone_vm(pool_object,provider) + subject.clone_vm(pool_object,provider,dns_plugin) end it 'logs a message if an error is raised' do allow(logger).to receive(:log) expect(logger).to receive(:log).with('s',"[!] [#{pool}] failed while cloning VM with an error: MockError") - expect(subject).to receive(:_clone_vm).with(pool,provider,nil,nil).and_raise('MockError') + expect(subject).to receive(:_clone_vm).with(pool,provider,dns_plugin,nil,nil).and_raise('MockError') - expect{subject.clone_vm(pool,provider)}.to raise_error(/MockError/) + expect{subject.clone_vm(pool,provider,dns_plugin)}.to raise_error(/MockError/) end end @@ -901,18 +907,23 @@ EOT let(:config) { YAML.load(<<-EOT --- +:dns_configs: + :mock: + dns_class: base :providers: :mock_provider: skip_dns_check_before_creating_vm: true :pools: - name: '#{pool}' size: 1 + dns_config: 'mock' EOT ) } it 'should skip the dns check' do #method is skipped expect(subject).not_to receive(:check_dns_available) + allow(subject).to receive(:get_domain_for_pool).and_return('example.com') expect(subject.find_unique_hostname(pool)).to eq("spicy-proton") end end @@ -920,11 +931,15 @@ EOT let(:config) { YAML.load(<<-EOT --- +:dns_configs: + :mock: + dns_class: base :providers: :mock_provider: :pools: - name: '#{pool}' size: 1 + dns_config: 'mock' EOT ) } @@ -953,10 +968,17 @@ EOT let(:config) { YAML.load(<<-EOT --- +:dns_configs: + :mock: + dns_class: base :config: prefix: "prefix" :redis: ttl: #{redis_ttl} +:pools: + - name: 'pool1' + size: 1 + dns_config: 'mock' EOT ) } @@ -966,6 +988,9 @@ EOT allow(metrics).to receive(:timing) expect(metrics).to receive(:timing).with(/clone\./,/0/) expect(provider).to receive(:create_vm).with(pool, String) + allow(provider).to receive(:get_vm_ip_address) + allow(subject).to receive(:get_domain_for_pool).and_return('example.com') + allow(subject).to receive(:get_dns_plugin_class_name_for_pool).and_return(dns_plugin) allow(logger).to receive(:log) redis_connection_pool.with do |redis| expect(subject).to receive(:find_unique_hostname).with(pool).and_return(vm) @@ -976,7 +1001,7 @@ EOT redis_connection_pool.with do |redis| expect(redis.scard("vmpooler__pending__#{pool}")).to eq(0) - subject._clone_vm(pool,provider) + subject._clone_vm(pool,provider,dns_plugin) expect(redis.scard("vmpooler__pending__#{pool}")).to eq(1) expect(redis.hget("vmpooler__vm__#{vm}", 'clone')).to_not be_nil @@ -991,7 +1016,7 @@ EOT redis.incr('vmpooler__tasks__clone') redis.incr('vmpooler__tasks__clone') expect(redis.get('vmpooler__tasks__clone')).to eq('2') - subject._clone_vm(pool,provider) + subject._clone_vm(pool,provider,dns_plugin) expect(redis.get('vmpooler__tasks__clone')).to eq('1') end end @@ -999,19 +1024,20 @@ EOT it 'should log a message that is being cloned from a template' do expect(logger).to receive(:log).with('d',/\[ \] \[#{pool}\] Starting to clone '(.+)'/) - subject._clone_vm(pool,provider) + subject._clone_vm(pool,provider,dns_plugin) end it 'should log a message that it completed being cloned' do expect(logger).to receive(:log).with('s',/\[\+\] \[#{pool}\] '(.+)' cloned in [0-9.]+ seconds/) - subject._clone_vm(pool,provider) + subject._clone_vm(pool,provider,dns_plugin) end end context 'with an error during cloning' do before(:each) do expect(provider).to receive(:create_vm).with(pool, String).and_raise('MockError') + allow(subject).to receive(:get_domain_for_pool) allow(logger).to receive(:log) redis_connection_pool.with do |redis| expect(subject).to receive(:find_unique_hostname).with(pool).and_return(vm) @@ -1022,7 +1048,7 @@ EOT redis_connection_pool.with do |redis| expect(redis.scard("vmpooler__pending__#{pool}")).to eq(0) - expect{subject._clone_vm(pool,provider)}.to raise_error(/MockError/) + expect{subject._clone_vm(pool,provider,dns_plugin)}.to raise_error(/MockError/) expect(redis.scard("vmpooler__pending__#{pool}")).to eq(0) # Get the new VM Name from the pending pool queue as it should be the only entry @@ -1036,7 +1062,7 @@ EOT redis.incr('vmpooler__tasks__clone') redis.incr('vmpooler__tasks__clone') expect(redis.get('vmpooler__tasks__clone')).to eq('2') - expect{subject._clone_vm(pool,provider)}.to raise_error(/MockError/) + expect{subject._clone_vm(pool,provider,dns_plugin)}.to raise_error(/MockError/) expect(redis.get('vmpooler__tasks__clone')).to eq('1') end end @@ -1046,12 +1072,12 @@ EOT redis.pipelined do |pipe| expect(pipe).to receive(:expire) end - expect{subject._clone_vm(pool,provider)}.to raise_error(/MockError/) + expect{subject._clone_vm(pool,provider,dns_plugin)}.to raise_error(/MockError/) end end it 'should raise the error' do - expect{subject._clone_vm(pool,provider)}.to raise_error(/MockError/) + expect{subject._clone_vm(pool,provider,dns_plugin)}.to raise_error(/MockError/) end end @@ -1061,6 +1087,9 @@ EOT allow(metrics).to receive(:timing) expect(metrics).to receive(:timing).with(/clone\./,/0/) expect(provider).to receive(:create_vm).with(pool, String) + allow(provider).to receive(:get_vm_ip_address).with(vm,pool) + allow(subject).to receive(:get_dns_plugin_class_name_for_pool).and_return(dns_plugin) + expect(dns_plugin).to receive(:create_or_replace_record) allow(logger).to receive(:log) redis_connection_pool.with do |redis| expect(subject).to receive(:find_unique_hostname).with(pool).and_return(vm) @@ -1069,7 +1098,7 @@ EOT it 'should set request_id and pool_alias on the vm data' do redis_connection_pool.with do |redis| - subject._clone_vm(pool,provider,request_id,pool) + subject._clone_vm(pool,provider,dns_plugin,request_id,pool) expect(redis.hget("vmpooler__vm__#{vm}", 'pool_alias')).to eq(pool) expect(redis.hget("vmpooler__vm__#{vm}", 'request_id')).to eq(request_id) end @@ -1078,7 +1107,7 @@ EOT it 'should reduce the clone count' do redis_connection_pool.with do |redis| expect(redis).to receive(:decr).with('vmpooler__tasks__ondemandclone') - subject._clone_vm(pool,provider,request_id,pool) + subject._clone_vm(pool,provider,dns_plugin,request_id,pool) end end end @@ -1086,6 +1115,8 @@ EOT context 'with #check_dns_available' do before(:each) do allow(logger).to receive(:log) + allow(provider).to receive(:get_vm_ip_address).and_return(true) + allow(subject).to receive(:get_dns_plugin_class_name_for_pool).and_return(dns_plugin) end it 'should error out if DNS already exists' do vm_name = "foo" @@ -1093,15 +1124,16 @@ EOT expect(subject).to receive(:generate_and_check_hostname).exactly(3).times.and_return([vm_name, true]) #skip this, make it available all times expect(resolv).to receive(:getaddress).exactly(3).times.and_return("1.2.3.4") expect(metrics).to receive(:increment).with("errors.staledns.#{pool}").exactly(3).times - expect{subject._clone_vm(pool,provider)}.to raise_error(/Unable to generate a unique hostname after/) + expect{subject._clone_vm(pool,provider,dns_plugin)}.to raise_error(/Unable to generate a unique hostname after/) end it 'should be successful if DNS does not exist' do vm_name = "foo" resolv = class_double("Resolv").as_stubbed_const(:transfer_nested_constants => true) expect(subject).to receive(:generate_and_check_hostname).and_return([vm_name, true]) expect(resolv).to receive(:getaddress).exactly(1).times.and_raise(Resolv::ResolvError) + expect(provider).to receive(:get_vm_ip_address) expect(provider).to receive(:create_vm).with(pool, String) - subject._clone_vm(pool,provider) + subject._clone_vm(pool,provider,dns_plugin) end end end @@ -1113,17 +1145,17 @@ EOT end it 'calls _destroy_vm' do - expect(subject).to receive(:_destroy_vm).with(vm,pool,provider) + expect(subject).to receive(:_destroy_vm).with(vm,pool,provider,dns_plugin) - subject.destroy_vm(vm,pool,provider) + subject.destroy_vm(vm,pool,provider,dns_plugin) end it 'logs a message if an error is raised' do allow(logger).to receive(:log) expect(logger).to receive(:log).with('d',"[!] [#{pool}] '#{vm}' failed while destroying the VM with an error: MockError") - expect(subject).to receive(:_destroy_vm).with(vm,pool,provider).and_raise('MockError') + expect(subject).to receive(:_destroy_vm).with(vm,pool,provider,dns_plugin).and_raise('MockError') - expect{subject.destroy_vm(vm,pool,provider)}.to raise_error(/MockError/) + expect{subject.destroy_vm(vm,pool,provider,dns_plugin)}.to raise_error(/MockError/) end end @@ -1152,7 +1184,7 @@ EOT redis.pipelined do |pipe| expect(pipe).to receive(:expire).with("vmpooler__vm__#{vm}", 0) end - subject._destroy_vm(vm,pool,provider) + subject._destroy_vm(vm,pool,provider,dns_plugin) end end end @@ -1163,7 +1195,7 @@ EOT end it 'should raise an error' do - expect{ subject._destroy_vm(vm,pool,provider) }.to raise_error(NoMethodError) + expect{ subject._destroy_vm(vm,pool,provider,dns_plugin) }.to raise_error(NoMethodError) end end @@ -1174,7 +1206,7 @@ EOT end it 'should not raise an error' do - subject._destroy_vm(vm,pool,provider) + subject._destroy_vm(vm,pool,provider,dns_plugin) end end @@ -1183,7 +1215,7 @@ EOT expect(logger).to receive(:log).with('s', /\[-\] \[#{pool}\] '#{vm}' destroyed in [0-9.]+ seconds/) allow(logger).to receive(:log) - subject._destroy_vm(vm,pool,provider) + subject._destroy_vm(vm,pool,provider,dns_plugin) end it 'should emit a timing metric' do @@ -1191,13 +1223,13 @@ EOT allow(metrics).to receive(:timing) expect(metrics).to receive(:timing).with("destroy.#{pool}", String) - subject._destroy_vm(vm,pool,provider) + subject._destroy_vm(vm,pool,provider,dns_plugin) end it 'should dereference the mutex' do expect(subject).to receive(:dereference_mutex) - subject._destroy_vm(vm,pool,provider) + subject._destroy_vm(vm,pool,provider,dns_plugin) end end @@ -1211,13 +1243,13 @@ EOT expect(logger).to receive(:log).with('s', /\[-\] \[#{pool}\] '#{vm}' destroyed in [0-9.]+ seconds/).exactly(0).times allow(logger).to receive(:log) - expect{ subject._destroy_vm(vm,pool,provider) }.to raise_error(/MockError/) + expect{ subject._destroy_vm(vm,pool,provider,dns_plugin) }.to raise_error(/MockError/) end it 'should not emit a timing metric' do expect(metrics).to receive(:timing).with("destroy.#{pool}", String).exactly(0).times - expect{ subject._destroy_vm(vm,pool,provider) }.to raise_error(/MockError/) + expect{ subject._destroy_vm(vm,pool,provider,dns_plugin) }.to raise_error(/MockError/) end end @@ -1230,7 +1262,7 @@ EOT it 'should return' do expect(subject).to receive(:vm_mutex).with(vm).and_return(mutex) - expect(subject._destroy_vm(vm,pool,provider)).to eq(nil) + expect(subject._destroy_vm(vm,pool,provider,dns_plugin)).to eq(nil) end end end @@ -1942,6 +1974,7 @@ EOT before(:each) do expect(Thread).to receive(:new).and_yield + allow(Vmpooler::Dns).to receive(:load_used_dns_plugins).and_return('vmpooler/dns/mock') allow(subject).to receive(:_check_snapshot_queue).with(no_args) end @@ -1987,6 +2020,10 @@ EOT let(:maxloop) { 5 } # Note a maxloop of zero can not be tested as it never terminates + before(:each) do + allow(subject).to receive(:load_used_dns_plugins).and_return('vmpooler/dns/mock') + end + after(:each) do # Reset the global variable - Note this is a code smell $threads = nil @@ -2837,6 +2874,23 @@ EOT end context 'on startup' do + let(:config) { + YAML.load(<<-EOT +--- +:config: +:dns_configs: + :mock: + dns_class: base +:pools: + - name: #{pool} + dns_plugin: 'mock' +EOT + )} + + before(:each) do + allow(Vmpooler::Dns).to receive(:get_domain_for_pool) + end + it 'should log a message that VMPooler has started' do expect(logger).to receive(:log).with('d', 'starting vmpooler') @@ -2930,14 +2984,23 @@ EOT --- :providers: :vsphere: {} +:dns_configs: + :mock: + dns_class: base :pools: - name: #{pool} provider: 'vsphere' + dns_plugin: 'mock' - name: 'dummy' provider: 'vsphere' + dns_plugin: 'mock' EOT )} + before(:each) do + allow(Vmpooler::Dns).to receive(:get_domain_for_pool) + end + it 'should call create_provider_object idempotently' do # Even though there are two pools using the vsphere provider, it should only # create the provider object once. @@ -2965,14 +3028,21 @@ EOT context 'move vSphere configuration to providers location' do let(:config) { YAML.load(<<-EOT +:dns_configs: + :mock: + dns_class: base :vsphere: server: 'vsphere.example.com' username: 'vmpooler' password: 'password' :pools: - name: #{pool} + dns_plugin: 'mock' EOT )} + before(:each) do + allow(Vmpooler::Dns).to receive(:get_domain_for_pool) + end it 'should set providers with no provider to vsphere' do expect(subject.config[:providers]).to be nil @@ -2995,13 +3065,22 @@ EOT let(:config) { YAML.load(<<-EOT --- +:dns_configs: + :mock: + dns_class: base :pools: - name: #{pool} + dns_plugin: 'mock' - name: 'dummy' provider: 'dummy' + dns_plugin: 'mock' EOT )} + before(:each) do + allow(Vmpooler::Dns).to receive(:get_domain_for_pool) + end + it 'should set providers with no provider to dummy' do expect(subject.config[:pools][0]['provider']).to be_nil expect(subject.config[:pools][1]['provider']).to eq('dummy') @@ -3022,11 +3101,31 @@ EOT context 'with dead disk_manager thread' do let(:disk_manager_thread) { double('thread', :alive? => false) } + let(:check_loop_delay_min) { 7 } + let(:check_loop_delay_max) { 20 } + let(:check_loop_delay_decay) { 2.1 } + let(:config) { + YAML.load(<<-EOT +--- +:config: + check_loop_delay_min: #{check_loop_delay_min} + check_loop_delay_max: #{check_loop_delay_max} + check_loop_delay_decay: #{check_loop_delay_decay} +:dns_configs: + :mock: + dns_class: base +:pools: + - name: #{pool} + dns_plugin: 'mock' +EOT + ) + } before(:each) do # Reset the global variable - Note this is a code smell $threads = {} $threads['disk_manager'] = disk_manager_thread + allow(Vmpooler::Dns).to receive(:get_domain_for_pool) end it 'should run the check_disk_queue method and log a message' do @@ -3039,10 +3138,30 @@ EOT context 'with dead snapshot_manager thread' do let(:snapshot_manager_thread) { double('thread', :alive? => false) } + let(:check_loop_delay_min) { 7 } + let(:check_loop_delay_max) { 20 } + let(:check_loop_delay_decay) { 2.1 } + let(:config) { + YAML.load(<<-EOT +--- +:config: + check_loop_delay_min: #{check_loop_delay_min} + check_loop_delay_max: #{check_loop_delay_max} + check_loop_delay_decay: #{check_loop_delay_decay} +:dns_configs: + :mock: + dns_class: base +:pools: + - name: #{pool} + dns_plugin: 'mock' +EOT + ) + } before(:each) do # Reset the global variable - Note this is a code smell $threads = {} $threads['snapshot_manager'] = snapshot_manager_thread + allow(Vmpooler::Dns).to receive(:get_domain_for_pool) end it 'should run the check_snapshot_queue method and log a message' do @@ -3060,10 +3179,27 @@ EOT let(:default_check_loop_delay_min) { 5 } let(:default_check_loop_delay_max) { 60 } let(:default_check_loop_delay_decay) { 2.0 } + let(:check_loop_delay_min) { 7 } + let(:check_loop_delay_max) { 20 } + let(:check_loop_delay_decay) { 2.1 } + let(:config) { + YAML.load(<<-EOT +--- +:config: +:dns_configs: + :mock: + dns_class: base +:pools: + - name: #{pool} + dns_plugin: 'mock' +EOT + ) + } before(:each) do # Reset the global variable - Note this is a code smell $threads = {} $threads[pool] = pool_thread + allow(Vmpooler::Dns).to receive(:get_domain_for_pool) end it 'should run the check_pool method and log a message' do @@ -3082,6 +3218,19 @@ EOT let(:default_check_loop_delay_min) { 5 } let(:default_check_loop_delay_max) { 60 } let(:default_check_loop_delay_decay) { 2.0 } + let(:config) { + YAML.load(<<-EOT +--- +:config: +:dns_configs: + :mock: + dns_class: base +:pools: + - name: #{pool} + dns_plugin: 'mock' +EOT + ) + } before(:each) do # Reset the global variable - Note this is a code smell $threads = {} @@ -3108,8 +3257,12 @@ EOT check_loop_delay_min: #{check_loop_delay_min} check_loop_delay_max: #{check_loop_delay_max} check_loop_delay_decay: #{check_loop_delay_decay} +:dns_configs: + :mock: + dns_class: base :pools: - name: #{pool} + dns_plugin: 'mock' EOT ) } @@ -3117,6 +3270,8 @@ EOT # Reset the global variable - Note this is a code smell $threads = {} $threads[pool] = pool_thread + allow(Vmpooler::Dns).to receive(:get_domain_for_pool) + # allow(subject).to receive(:load_used_dns_plugins).and_return('vmpooler/dns/mock') end it 'should run the check_pool method and log a message' do @@ -3134,6 +3289,26 @@ EOT context 'delays between loops' do let(:maxloop) { 2 } let(:loop_delay) { 1 } + let(:check_loop_delay_min) { 7 } + let(:check_loop_delay_max) { 20 } + let(:check_loop_delay_decay) { 2.1 } + let(:maxloop) { 5 } + let(:config) { + YAML.load(<<-EOT +--- +:config: + check_loop_delay_min: #{check_loop_delay_min} + check_loop_delay_max: #{check_loop_delay_max} + check_loop_delay_decay: #{check_loop_delay_decay} +:dns_configs: + :mock: + dns_class: base +:pools: + - name: #{pool} + dns_plugin: 'mock' +EOT + ) + } # Note a maxloop of zero can not be tested as it never terminates before(:each) do @@ -3141,6 +3316,7 @@ EOT allow(subject).to receive(:check_snapshot_queue) allow(subject).to receive(:check_pool) allow(subject).to receive(:check_ondemand_requests) + allow(Vmpooler::Dns).to receive(:get_domain_for_pool) end it 'when a non-default loop delay is specified' do @@ -3152,10 +3328,31 @@ EOT context 'loops specified number of times (5)' do let(:alive_thread) { double('thread', :alive? => true) } + let(:check_loop_delay_min) { 7 } + let(:check_loop_delay_max) { 20 } + let(:check_loop_delay_decay) { 2.1 } let(:maxloop) { 5 } + let(:config) { + YAML.load(<<-EOT +--- +:config: + check_loop_delay_min: #{check_loop_delay_min} + check_loop_delay_max: #{check_loop_delay_max} + check_loop_delay_decay: #{check_loop_delay_decay} +:dns_configs: + :mock: + dns_class: base +:pools: + - name: #{pool} + dns_plugin: 'mock' +EOT + ) + } # Note a maxloop of zero can not be tested as it never terminates before(:each) do - end + allow(subject).to receive(:get_domain_for_pool) + allow(subject).to receive(:get_dns_plugin_config_classes) + end it 'should run startup tasks only once' do redis_connection_pool.with do |redis| @@ -4226,6 +4423,10 @@ EOT } } + before(:each) do + allow(subject).to receive(:get_dns_plugin_class_for_pool).and_return(dns_plugin) + end + it 'should not call clone_vm when number of VMs is equal to the pool size' do expect(subject).to receive(:clone_vm).exactly(0).times @@ -4280,6 +4481,9 @@ EOT end context 'when pool is marked as empty' do + before(:each) do + allow(subject).to receive(:get_dns_plugin_class_for_pool).and_return(dns_plugin) + end before(:each) do redis_connection_pool.with do |redis| @@ -4305,6 +4509,9 @@ EOT end context 'when number of VMs is less than the pool size' do + before(:each) do + allow(subject).to receive(:get_dns_plugin_class_for_pool).and_return(dns_plugin) + end it 'should return the number of cloned VMs' do pool_size = 5 @@ -4361,6 +4568,10 @@ EOT end context 'export metrics' do + before(:each) do + allow(subject).to receive(:get_dns_plugin_class_for_pool).and_return(dns_plugin) + end + it 'increments metrics for ready queue' do redis_connection_pool.with do |redis| create_ready_vm(pool,'vm1',redis) @@ -4451,6 +4662,7 @@ EOT allow(subject).to receive(:destroy_vm) allow(subject).to receive(:clone_vm) allow(provider).to receive(:vms_in_pool).with(pool).and_return(new_vm_response) + allow(subject).to receive(:get_dns_plugin_class_for_pool).and_return(dns_plugin) end it 'calls inventory correctly' do @@ -4555,6 +4767,10 @@ EOT # RUNNING context 'when checking running VMs' do + before(:each) do + allow(subject).to receive(:get_dns_plugin_class_for_pool).and_return(dns_plugin) + end + let(:pool_check_response) { { discovered_vms: 0, @@ -4577,6 +4793,10 @@ EOT # READY context 'when checking ready VMs' do + before(:each) do + allow(subject).to receive(:get_dns_plugin_class_for_pool).and_return(dns_plugin) + end + let(:pool_check_response) { { discovered_vms: 0, @@ -4599,6 +4819,10 @@ EOT # PENDING context 'when checking ready VMs' do + before(:each) do + allow(subject).to receive(:get_dns_plugin_class_for_pool).and_return(dns_plugin) + end + let(:pool_check_response) { { discovered_vms: 0, @@ -4622,6 +4846,10 @@ EOT # COMPLETED context 'when checking completed VMs' do + before(:each) do + allow(subject).to receive(:get_dns_plugin_class_for_pool).and_return(dns_plugin) + end + let(:pool_check_response) { { discovered_vms: 0, @@ -4644,6 +4872,10 @@ EOT # DISCOVERED context 'when checking discovered VMs' do + before(:each) do + allow(subject).to receive(:get_dns_plugin_class_for_pool).and_return(dns_plugin) + end + let(:pool_check_response) { { discovered_vms: 0, @@ -4666,6 +4898,10 @@ EOT # MIGRATIONS context 'when checking migrating VMs' do + before(:each) do + allow(subject).to receive(:get_dns_plugin_class_for_pool).and_return(dns_plugin) + end + let(:pool_check_response) { { discovered_vms: 0, @@ -4695,6 +4931,7 @@ EOT redis_connection_pool.with do |redis| redis.hset('vmpooler__config__poolsize', pool, newpoolsize) end + allow(subject).to receive(:get_dns_plugin_class_for_pool).and_return(dns_plugin) end it 'should change the pool size configuration' do @@ -4736,6 +4973,7 @@ EOT expect(redis).to receive(:scard).with("vmpooler__ready__#{pool}").and_return(1) expect(redis).to receive(:scard).with("vmpooler__pending__#{pool}").and_return(1) end + expect(subject).to receive(:get_dns_plugin_class_for_pool).and_return(dns_plugin) end it 'should call remove_excess_vms' do @@ -4845,6 +5083,7 @@ EOT before(:each) do config[:config]['ondemand_clone_limit'] = 10 expect(subject).to receive(:get_provider_for_pool).and_return(provider) + expect(subject).to receive(:get_dns_plugin_class_for_pool).and_return(dns_plugin) redis_connection_pool.with do |redis| create_ondemand_creationtask("#{request_string}:#{request_id}", current_time.to_i, redis) end @@ -4852,7 +5091,7 @@ EOT it 'creates the vm' do redis_connection_pool.with do |redis| - expect(subject).to receive(:clone_vm).with(pool, provider, request_id, pool_alias) + expect(subject).to receive(:clone_vm).with(pool, provider, dns_plugin, request_id, pool_alias) subject.process_ondemand_vms(redis) end end @@ -4865,6 +5104,7 @@ EOT before(:each) do config[:config]['ondemand_clone_limit'] = 3 expect(subject).to receive(:get_provider_for_pool).and_return(provider) + expect(subject).to receive(:get_dns_plugin_class_for_pool).and_return(dns_plugin) redis_connection_pool.with do |redis| create_ondemand_creationtask("#{request_string}:#{request_id}", current_time.to_i, redis) end @@ -4872,7 +5112,7 @@ EOT it 'should create the maximum number of vms' do redis_connection_pool.with do |redis| - expect(subject).to receive(:clone_vm).with(pool, provider, request_id, pool_alias).exactly(3).times + expect(subject).to receive(:clone_vm).with(pool, provider, dns_plugin, request_id, pool_alias).exactly(3).times subject.process_ondemand_vms(redis) end end diff --git a/spec/unit/vmpooler_spec.rb b/spec/unit/vmpooler_spec.rb index 84d43ed..2e9831f 100644 --- a/spec/unit/vmpooler_spec.rb +++ b/spec/unit/vmpooler_spec.rb @@ -63,9 +63,9 @@ describe 'Vmpooler' do expect(Vmpooler.config[:providers].keys).to include(:dummy) expect(Vmpooler.config[:providers].keys).to include(:alice) expect(Vmpooler.config[:providers].keys).to include(:bob) - merged_pools = [{"name"=>"pool03", "provider"=>"dummy", "ready_ttl"=>5, "size"=>5}, - {"name"=>"pool04", "provider"=>"dummy", "ready_ttl"=>5, "size"=>5}, - {"name"=>"pool05", "provider"=>"dummy", "ready_ttl"=>5, "size"=>5}] + merged_pools = [{"name"=>"pool03", "provider"=>"dummy", "dns_plugin"=>"example", "ready_ttl"=>5, "size"=>5}, + {"name"=>"pool04", "provider"=>"dummy", "dns_plugin"=>"example", "ready_ttl"=>5, "size"=>5}, + {"name"=>"pool05", "provider"=>"dummy", "dns_plugin"=>"example", "ready_ttl"=>5, "size"=>5}] expect(Vmpooler.config[:pools]).to eq(merged_pools) expect(Vmpooler.config[:config]).not_to be_nil #merge does not deleted existing keys end