mirror of
https://github.com/puppetlabs/vmpooler.git
synced 2026-01-26 01:58:41 -05:00
Merge pull request #138 from sschneid/qeng_2807
(QENG-2807) Allow pool 'alias' names
This commit is contained in:
commit
20fa7d20be
4 changed files with 274 additions and 82 deletions
|
|
@ -33,6 +33,20 @@ module Vmpooler
|
|||
parsed_config[:config]['vm_checktime'] ||= 15
|
||||
parsed_config[:config]['vm_lifetime'] ||= 24
|
||||
|
||||
# Create an index of pool aliases
|
||||
parsed_config[:pools].each do |pool|
|
||||
if pool['alias']
|
||||
if pool['alias'].kind_of?(Array)
|
||||
pool['alias'].each do |a|
|
||||
parsed_config[:alias] ||= {}
|
||||
parsed_config[:alias][a] = pool['name']
|
||||
end
|
||||
elsif pool['alias'].kind_of?(String)
|
||||
parsed_config[:alias][pool['alias']] = pool['name']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if parsed_config[:graphite]['server']
|
||||
parsed_config[:graphite]['prefix'] ||= 'vmpooler'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -28,6 +28,60 @@ module Vmpooler
|
|||
validate_token(backend)
|
||||
end
|
||||
|
||||
def alias_deref(hash)
|
||||
newhash = {}
|
||||
|
||||
hash.each do |key, val|
|
||||
if backend.exists('vmpooler__ready__' + key)
|
||||
newhash[key] = val
|
||||
else
|
||||
if Vmpooler::API.settings.config[:alias][key]
|
||||
newkey = Vmpooler::API.settings.config[:alias][key]
|
||||
newhash[newkey] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newhash
|
||||
end
|
||||
|
||||
def checkout_vm(template, result)
|
||||
vm = backend.spop('vmpooler__ready__' + template)
|
||||
|
||||
unless vm.nil?
|
||||
backend.sadd('vmpooler__running__' + template, vm)
|
||||
backend.hset('vmpooler__active__' + template, vm, Time.now)
|
||||
backend.hset('vmpooler__vm__' + vm, 'checkout', Time.now)
|
||||
|
||||
if Vmpooler::API.settings.config[:auth] and has_token?
|
||||
validate_token(backend)
|
||||
|
||||
backend.hset('vmpooler__vm__' + vm, 'token:token', request.env['HTTP_X_AUTH_TOKEN'])
|
||||
backend.hset('vmpooler__vm__' + vm, 'token:user',
|
||||
backend.hget('vmpooler__token__' + request.env['HTTP_X_AUTH_TOKEN'], 'user')
|
||||
)
|
||||
|
||||
if config['vm_lifetime_auth'].to_i > 0
|
||||
backend.hset('vmpooler__vm__' + vm, 'lifetime', config['vm_lifetime_auth'].to_i)
|
||||
end
|
||||
end
|
||||
|
||||
result[template] ||= {}
|
||||
|
||||
if result[template]['hostname']
|
||||
result[template]['hostname'] = [result[template]['hostname']] unless result[template]['hostname'].is_a?(Array)
|
||||
result[template]['hostname'].push(vm)
|
||||
else
|
||||
result[template]['hostname'] = vm
|
||||
end
|
||||
else
|
||||
status 503
|
||||
result['ok'] = false
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
get "#{api_prefix}/status/?" do
|
||||
content_type :json
|
||||
|
||||
|
|
@ -282,11 +336,15 @@ module Vmpooler
|
|||
post "#{api_prefix}/vm/?" do
|
||||
content_type :json
|
||||
|
||||
result = {}
|
||||
result = { 'ok' => false }
|
||||
|
||||
jdata = alias_deref(JSON.parse(request.body.read))
|
||||
|
||||
if not jdata.nil? and not jdata.empty?
|
||||
available = 1
|
||||
|
||||
jdata = JSON.parse(request.body.read)
|
||||
else
|
||||
status 404
|
||||
end
|
||||
|
||||
jdata.each do |key, val|
|
||||
if backend.scard('vmpooler__ready__' + key).to_i < val.to_i
|
||||
|
|
@ -298,46 +356,10 @@ module Vmpooler
|
|||
result['ok'] = true
|
||||
|
||||
jdata.each do |key, val|
|
||||
result[key] ||= {}
|
||||
|
||||
val.to_i.times do |_i|
|
||||
vm = backend.spop('vmpooler__ready__' + key)
|
||||
|
||||
unless vm.nil?
|
||||
backend.sadd('vmpooler__running__' + key, vm)
|
||||
backend.hset('vmpooler__active__' + key, vm, Time.now)
|
||||
backend.hset('vmpooler__vm__' + vm, 'checkout', Time.now)
|
||||
|
||||
if Vmpooler::API.settings.config[:auth] and has_token?
|
||||
validate_token(backend)
|
||||
|
||||
backend.hset('vmpooler__vm__' + vm, 'token:token', request.env['HTTP_X_AUTH_TOKEN'])
|
||||
backend.hset('vmpooler__vm__' + vm, 'token:user',
|
||||
backend.hget('vmpooler__token__' + request.env['HTTP_X_AUTH_TOKEN'], 'user')
|
||||
)
|
||||
|
||||
if config['vm_lifetime_auth'].to_i > 0
|
||||
backend.hset('vmpooler__vm__' + vm, 'lifetime', config['vm_lifetime_auth'].to_i)
|
||||
result = checkout_vm(key, result)
|
||||
end
|
||||
end
|
||||
|
||||
result[key] ||= {}
|
||||
|
||||
if result[key]['hostname']
|
||||
result[key]['hostname'] = [result[key]['hostname']] unless result[key]['hostname'].is_a?(Array)
|
||||
result[key]['hostname'].push(vm)
|
||||
else
|
||||
result[key]['hostname'] = vm
|
||||
end
|
||||
else
|
||||
status 503
|
||||
result['ok'] = false
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
status 503
|
||||
result['ok'] = false
|
||||
end
|
||||
|
||||
if result['ok'] && config['domain']
|
||||
|
|
@ -350,7 +372,7 @@ module Vmpooler
|
|||
post "#{api_prefix}/vm/:template/?" do
|
||||
content_type :json
|
||||
|
||||
result = {}
|
||||
result = { 'ok' => false }
|
||||
payload = {}
|
||||
|
||||
params[:template].split('+').each do |template|
|
||||
|
|
@ -358,10 +380,16 @@ module Vmpooler
|
|||
payload[template] = payload[template] + 1
|
||||
end
|
||||
|
||||
available = 1
|
||||
payload = alias_deref(payload)
|
||||
|
||||
payload.keys.each do |template|
|
||||
if backend.scard('vmpooler__ready__' + template) < payload[template]
|
||||
if not payload.nil? and not payload.empty?
|
||||
available = 1
|
||||
else
|
||||
status 404
|
||||
end
|
||||
|
||||
payload.each do |key, val|
|
||||
if backend.scard('vmpooler__ready__' + key).to_i < val.to_i
|
||||
available = 0
|
||||
end
|
||||
end
|
||||
|
|
@ -369,45 +397,11 @@ module Vmpooler
|
|||
if (available == 1)
|
||||
result['ok'] = true
|
||||
|
||||
params[:template].split('+').each do |template|
|
||||
result[template] ||= {}
|
||||
|
||||
vm = backend.spop('vmpooler__ready__' + template)
|
||||
|
||||
unless vm.nil?
|
||||
backend.sadd('vmpooler__running__' + template, vm)
|
||||
backend.hset('vmpooler__active__' + template, vm, Time.now)
|
||||
backend.hset('vmpooler__vm__' + vm, 'checkout', Time.now)
|
||||
|
||||
if Vmpooler::API.settings.config[:auth] and has_token?
|
||||
validate_token(backend)
|
||||
|
||||
backend.hset('vmpooler__vm__' + vm, 'token:token', request.env['HTTP_X_AUTH_TOKEN'])
|
||||
backend.hset('vmpooler__vm__' + vm, 'token:user',
|
||||
backend.hget('vmpooler__token__' + request.env['HTTP_X_AUTH_TOKEN'], 'user')
|
||||
)
|
||||
|
||||
if config['vm_lifetime_auth'].to_i > 0
|
||||
backend.hset('vmpooler__vm__' + vm, 'lifetime', config['vm_lifetime_auth'].to_i)
|
||||
payload.each do |key, val|
|
||||
val.to_i.times do |_i|
|
||||
result = checkout_vm(key, result)
|
||||
end
|
||||
end
|
||||
|
||||
result[template] ||= {}
|
||||
|
||||
if result[template]['hostname']
|
||||
result[template]['hostname'] = [result[template]['hostname']] unless result[template]['hostname'].is_a?(Array)
|
||||
result[template]['hostname'].push(vm)
|
||||
else
|
||||
result[template]['hostname'] = vm
|
||||
end
|
||||
else
|
||||
status 503
|
||||
result['ok'] = false
|
||||
end
|
||||
end
|
||||
else
|
||||
status 503
|
||||
result['ok'] = false
|
||||
end
|
||||
|
||||
if result['ok'] && config['domain']
|
||||
|
|
|
|||
|
|
@ -189,7 +189,8 @@ describe Vmpooler::API::V1 do
|
|||
pools: [
|
||||
{'name' => 'pool1', 'size' => 5},
|
||||
{'name' => 'pool2', 'size' => 10}
|
||||
]
|
||||
],
|
||||
alias: { 'poolone' => 'pool1' }
|
||||
} }
|
||||
|
||||
before do
|
||||
|
|
@ -221,6 +222,31 @@ describe Vmpooler::API::V1 do
|
|||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'returns a single VM for an alias' do
|
||||
expect(redis).to receive(:exists).with("vmpooler__ready__poolone").and_return(false)
|
||||
|
||||
post "#{prefix}/vm", '{"poolone":"1"}'
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: 'abcdefghijklmnop'
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'fails on nonexistant pools' do
|
||||
expect(redis).to receive(:exists).with("vmpooler__ready__poolpoolpool").and_return(false)
|
||||
|
||||
post "#{prefix}/vm", '{"poolpoolpool":"1"}'
|
||||
|
||||
expect_json(ok = false, http = 404)
|
||||
end
|
||||
|
||||
it 'returns multiple VMs' do
|
||||
post "#{prefix}/vm", '{"pool1":"1","pool2":"1"}'
|
||||
|
||||
|
|
@ -304,6 +330,158 @@ describe Vmpooler::API::V1 do
|
|||
end
|
||||
end
|
||||
|
||||
describe '/vm/:template' do
|
||||
let(:redis) { double('redis') }
|
||||
let(:prefix) { '/api/v1' }
|
||||
let(:config) { {
|
||||
config: {
|
||||
'site_name' => 'test pooler',
|
||||
'vm_lifetime_auth' => 2
|
||||
},
|
||||
pools: [
|
||||
{'name' => 'pool1', 'size' => 5},
|
||||
{'name' => 'pool2', 'size' => 10}
|
||||
],
|
||||
alias: { 'poolone' => 'pool1' }
|
||||
} }
|
||||
|
||||
before do
|
||||
app.settings.set :config, config
|
||||
app.settings.set :redis, redis
|
||||
|
||||
allow(redis).to receive(:exists).and_return '1'
|
||||
allow(redis).to receive(:hget).with('vmpooler__token__abcdefghijklmnopqrstuvwxyz012345', 'user').and_return 'jdoe'
|
||||
allow(redis).to receive(:hset).and_return '1'
|
||||
allow(redis).to receive(:sadd).and_return '1'
|
||||
allow(redis).to receive(:scard).and_return '5'
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool1').and_return 'abcdefghijklmnop'
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool2').and_return 'qrstuvwxyz012345'
|
||||
end
|
||||
|
||||
describe 'POST /vm/:template' do
|
||||
it 'returns a single VM' do
|
||||
post "#{prefix}/vm/pool1", ''
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: 'abcdefghijklmnop'
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'returns a single VM for an alias' do
|
||||
expect(redis).to receive(:exists).with("vmpooler__ready__poolone").and_return(false)
|
||||
|
||||
post "#{prefix}/vm/poolone", ''
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: 'abcdefghijklmnop'
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'fails on nonexistant pools' do
|
||||
expect(redis).to receive(:exists).with("vmpooler__ready__poolpoolpool").and_return(false)
|
||||
|
||||
post "#{prefix}/vm/poolpoolpool", ''
|
||||
|
||||
expect_json(ok = false, http = 404)
|
||||
end
|
||||
|
||||
it 'returns multiple VMs' do
|
||||
post "#{prefix}/vm/pool1+pool2", ''
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: 'abcdefghijklmnop'
|
||||
},
|
||||
pool2: {
|
||||
hostname: 'qrstuvwxyz012345'
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
context '(auth not configured)' do
|
||||
let(:config) { { auth: false } }
|
||||
|
||||
it 'does not extend VM lifetime if auth token is provided' do
|
||||
expect(redis).not_to receive(:hset).with("vmpooler__vm__abcdefghijklmnop", "lifetime", 2)
|
||||
|
||||
post "#{prefix}/vm/pool1", '', {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: 'abcdefghijklmnop'
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
let(:config) { { auth: true } }
|
||||
|
||||
it 'extends VM lifetime if auth token is provided' do
|
||||
expect(redis).to receive(:hset).with("vmpooler__vm__abcdefghijklmnop", "lifetime", 2).once
|
||||
|
||||
post "#{prefix}/vm/pool1", '', {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: 'abcdefghijklmnop'
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'does not extend VM lifetime if auth token is not provided' do
|
||||
expect(redis).not_to receive(:hset).with("vmpooler__vm__abcdefghijklmnop", "lifetime", 2)
|
||||
|
||||
post "#{prefix}/vm/pool1", ''
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: 'abcdefghijklmnop'
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '/vm/:hostname' do
|
||||
let(:redis) { double('redis') }
|
||||
let(:prefix) { '/api/v1' }
|
||||
|
|
|
|||
|
|
@ -188,6 +188,10 @@
|
|||
# The name of the pool.
|
||||
# (required)
|
||||
#
|
||||
# - alias
|
||||
# Other names this pool can be requested as.
|
||||
# (optional)
|
||||
#
|
||||
# - template
|
||||
# The template or virtual machine target to spawn clones from.
|
||||
# (required)
|
||||
|
|
@ -221,6 +225,7 @@
|
|||
|
||||
:pools:
|
||||
- name: 'debian-7-i386'
|
||||
alias: [ 'debian-7-32' ]
|
||||
template: 'Templates/debian-7-i386'
|
||||
folder: 'Pooled VMs/debian-7-i386'
|
||||
datastore: 'vmstorage'
|
||||
|
|
@ -228,6 +233,7 @@
|
|||
timeout: 15
|
||||
ready_ttl: 1440
|
||||
- name: 'debian-7-x86_64'
|
||||
alias: [ 'debian-7-64', 'debian-7-amd64' ]
|
||||
template: 'Templates/debian-7-x86_64'
|
||||
folder: 'Pooled VMs/debian-7-x86_64'
|
||||
datastore: 'vmstorage'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue