mirror of
https://github.com/puppetlabs/vmpooler.git
synced 2026-01-26 01:58:41 -05:00
Removed api/v1 spec tests
This commit is contained in:
parent
eaa1104dd7
commit
fb80d989c8
9 changed files with 22 additions and 2480 deletions
|
|
@ -1,402 +0,0 @@
|
|||
require 'spec_helper'
|
||||
require 'rack/test'
|
||||
|
||||
describe Vmpooler::API::V1 do
|
||||
include Rack::Test::Methods
|
||||
|
||||
def app()
|
||||
Vmpooler::API
|
||||
end
|
||||
|
||||
# Added to ensure no leakage in rack state from previous tests.
|
||||
# Removes all routes, filters, middleware and extension hooks from the current class
|
||||
# https://rubydoc.info/gems/sinatra/Sinatra/Base#reset!-class_method
|
||||
before(:each) do
|
||||
app.reset!
|
||||
end
|
||||
|
||||
let(:config) {
|
||||
{
|
||||
config: {
|
||||
'site_name' => 'test pooler',
|
||||
'vm_lifetime_auth' => 2,
|
||||
'experimental_features' => true
|
||||
},
|
||||
pools: [
|
||||
{'name' => 'pool1', 'size' => 5, 'template' => 'templates/pool1', 'clone_target' => 'default_cluster'},
|
||||
{'name' => 'pool2', 'size' => 10}
|
||||
],
|
||||
pools_at_startup: [
|
||||
{'name' => 'pool1', 'size' => 5, 'template' => 'templates/pool1', 'clone_target' => 'default_cluster'},
|
||||
{'name' => 'pool2', 'size' => 10}
|
||||
],
|
||||
statsd: { 'prefix' => 'stats_prefix'},
|
||||
alias: { 'poolone' => 'pool1' },
|
||||
pool_names: [ 'pool1', 'pool2', 'poolone' ]
|
||||
}
|
||||
}
|
||||
|
||||
describe '/config/pooltemplate' do
|
||||
let(:prefix) { '/api/v1' }
|
||||
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
||||
|
||||
let(:current_time) { Time.now }
|
||||
|
||||
before(:each) do
|
||||
expect(app).to receive(:run!).once
|
||||
app.execute([:api], config, redis, metrics, nil)
|
||||
app.settings.set :config, auth: false
|
||||
create_token('abcdefghijklmnopqrstuvwxyz012345', 'jdoe', current_time)
|
||||
end
|
||||
|
||||
describe 'DELETE /config/pooltemplate/:pool' do
|
||||
it 'resets a pool template' do
|
||||
post "#{prefix}/config/pooltemplate", '{"pool1":"templates/new_template"}'
|
||||
delete "#{prefix}/config/pooltemplate/pool1"
|
||||
expect_json(ok = true, http = 201)
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
template_before_reset: 'templates/new_template',
|
||||
template_before_overrides: 'templates/pool1'
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'succeeds when the pool has not been overridden' do
|
||||
delete "#{prefix}/config/pooltemplate/pool1"
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'fails on nonexistent pools' do
|
||||
delete "#{prefix}/config/pooltemplate/poolpoolpool"
|
||||
expect_json(ok = false, http = 404)
|
||||
end
|
||||
|
||||
context 'with experimental features disabled' do
|
||||
before(:each) do
|
||||
config[:config]['experimental_features'] = false
|
||||
end
|
||||
|
||||
it 'should return 405' do
|
||||
delete "#{prefix}/config/pooltemplate/pool1"
|
||||
expect_json(ok = false, http = 405)
|
||||
|
||||
expected = { ok: false }
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'POST /config/pooltemplate' do
|
||||
it 'updates a pool template' do
|
||||
post "#{prefix}/config/pooltemplate", '{"pool1":"templates/new_template"}'
|
||||
expect_json(ok = true, http = 201)
|
||||
|
||||
expected = { ok: true }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'fails on nonexistent pools' do
|
||||
post "#{prefix}/config/pooltemplate", '{"poolpoolpool":"templates/newtemplate"}'
|
||||
expect_json(ok = false, http = 400)
|
||||
end
|
||||
|
||||
it 'updates multiple pools' do
|
||||
post "#{prefix}/config/pooltemplate", '{"pool1":"templates/new_template","pool2":"templates/new_template2"}'
|
||||
expect_json(ok = true, http = 201)
|
||||
|
||||
expected = { ok: true }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'fails when not all pools exist' do
|
||||
post "#{prefix}/config/pooltemplate", '{"pool1":"templates/new_template","pool3":"templates/new_template2"}'
|
||||
expect_json(ok = false, http = 400)
|
||||
|
||||
expected = {
|
||||
ok: false,
|
||||
bad_templates: ['pool3']
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'returns no changes when the template does not change' do
|
||||
post "#{prefix}/config/pooltemplate", '{"pool1":"templates/pool1"}'
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expected = { ok: true }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'fails when a invalid template parameter is provided' do
|
||||
post "#{prefix}/config/pooltemplate", '{"pool1":"template1"}'
|
||||
expect_json(ok = false, http = 400)
|
||||
|
||||
expected = {
|
||||
ok: false,
|
||||
bad_templates: ['pool1']
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'fails when a template starts with /' do
|
||||
post "#{prefix}/config/pooltemplate", '{"pool1":"/template1"}'
|
||||
expect_json(ok = false, http = 400)
|
||||
|
||||
expected = {
|
||||
ok: false,
|
||||
bad_templates: ['pool1']
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'fails when a template ends with /' do
|
||||
post "#{prefix}/config/pooltemplate", '{"pool1":"template1/"}'
|
||||
expect_json(ok = false, http = 400)
|
||||
|
||||
expected = {
|
||||
ok: false,
|
||||
bad_templates: ['pool1']
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
context 'with experimental features disabled' do
|
||||
before(:each) do
|
||||
config[:config]['experimental_features'] = false
|
||||
end
|
||||
|
||||
it 'should return 405' do
|
||||
post "#{prefix}/config/pooltemplate", '{"pool1":"template/template1"}'
|
||||
expect_json(ok = false, http = 405)
|
||||
|
||||
expected = { ok: false }
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'DELETE /config/poolsize' do
|
||||
it 'resets a pool size' do
|
||||
post "#{prefix}/config/poolsize", '{"pool1":"2"}'
|
||||
delete "#{prefix}/config/poolsize/pool1"
|
||||
expect_json(ok = true, http = 201)
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool_size_before_reset: 2,
|
||||
pool_size_before_overrides: 5
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'fails when a specified pool does not exist' do
|
||||
delete "#{prefix}/config/poolsize/pool10"
|
||||
expect_json(ok = false, http = 404)
|
||||
expected = { ok: false }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'succeeds when a pool has not been overridden' do
|
||||
delete "#{prefix}/config/poolsize/pool1"
|
||||
expect_json(ok = true, http = 200)
|
||||
expected = {
|
||||
ok: true,
|
||||
pool_size_before_reset: 5,
|
||||
pool_size_before_overrides: 5
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
context 'with experimental features disabled' do
|
||||
before(:each) do
|
||||
config[:config]['experimental_features'] = false
|
||||
end
|
||||
|
||||
it 'should return 405' do
|
||||
delete "#{prefix}/config/poolsize/pool1"
|
||||
expect_json(ok = false, http = 405)
|
||||
|
||||
expected = { ok: false }
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /config/poolsize' do
|
||||
it 'changes a pool size' do
|
||||
post "#{prefix}/config/poolsize", '{"pool1":"2"}'
|
||||
expect_json(ok = true, http = 201)
|
||||
|
||||
expected = { ok: true }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'changes a pool size for multiple pools' do
|
||||
post "#{prefix}/config/poolsize", '{"pool1":"2","pool2":"2"}'
|
||||
expect_json(ok = true, http = 201)
|
||||
|
||||
expected = { ok: true }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'fails when a specified pool does not exist' do
|
||||
post "#{prefix}/config/poolsize", '{"pool10":"2"}'
|
||||
expect_json(ok = false, http = 400)
|
||||
expected = {
|
||||
ok: false,
|
||||
not_configured: ['pool10']
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'succeeds with 200 when no change is required' do
|
||||
post "#{prefix}/config/poolsize", '{"pool1":"5"}'
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expected = { ok: true }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'succeeds with 201 when at least one pool changes' do
|
||||
post "#{prefix}/config/poolsize", '{"pool1":"5","pool2":"5"}'
|
||||
expect_json(ok = true, http = 201)
|
||||
|
||||
expected = { ok: true }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'fails when a non-integer value is provided for size' do
|
||||
post "#{prefix}/config/poolsize", '{"pool1":"four"}'
|
||||
expect_json(ok = false, http = 400)
|
||||
|
||||
expected = {
|
||||
ok: false,
|
||||
not_configured: ['pool1']
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'fails when a negative value is provided for size' do
|
||||
post "#{prefix}/config/poolsize", '{"pool1":"-1"}'
|
||||
expect_json(ok = false, http = 400)
|
||||
|
||||
expected = {
|
||||
ok: false,
|
||||
not_configured: ['pool1']
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
context 'with experimental features disabled' do
|
||||
before(:each) do
|
||||
config[:config]['experimental_features'] = false
|
||||
end
|
||||
|
||||
it 'should return 405' do
|
||||
post "#{prefix}/config/poolsize", '{"pool1":"1"}'
|
||||
expect_json(ok = false, http = 405)
|
||||
|
||||
expected = { ok: false }
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /config/clonetarget' do
|
||||
it 'changes the clone target' do
|
||||
post "#{prefix}/config/clonetarget", '{"pool1":"cluster1"}'
|
||||
expect_json(ok = true, http = 201)
|
||||
|
||||
expected = { ok: true }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'changes a pool size for multiple pools' do
|
||||
post "#{prefix}/config/clonetarget", '{"pool1":"cluster1","pool2":"cluster2"}'
|
||||
expect_json(ok = true, http = 201)
|
||||
|
||||
expected = { ok: true }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'fails when a specified pool does not exist' do
|
||||
post "#{prefix}/config/clonetarget", '{"pool10":"cluster1"}'
|
||||
expect_json(ok = false, http = 400)
|
||||
expected = {
|
||||
ok: false,
|
||||
bad_templates: ['pool10']
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'succeeds with 200 when no change is required' do
|
||||
post "#{prefix}/config/clonetarget", '{"pool1":"default_cluster"}'
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expected = { ok: true }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'succeeds with 201 when at least one pool changes' do
|
||||
post "#{prefix}/config/clonetarget", '{"pool1":"default_cluster","pool2":"cluster2"}'
|
||||
expect_json(ok = true, http = 201)
|
||||
|
||||
expected = { ok: true }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
context 'with experimental features disabled' do
|
||||
before(:each) do
|
||||
config[:config]['experimental_features'] = false
|
||||
end
|
||||
|
||||
it 'should return 405' do
|
||||
post "#{prefix}/config/clonetarget", '{"pool1":"cluster1"}'
|
||||
expect_json(ok = false, http = 405)
|
||||
|
||||
expected = { ok: false }
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /config' do
|
||||
let(:prefix) { '/api/v1' }
|
||||
|
||||
it 'returns pool configuration when set' do
|
||||
get "#{prefix}/config"
|
||||
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result['pool_configuration']).to eq(config[:pools])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,387 +0,0 @@
|
|||
require 'spec_helper'
|
||||
require 'rack/test'
|
||||
|
||||
describe Vmpooler::API::V1 do
|
||||
include Rack::Test::Methods
|
||||
|
||||
def app()
|
||||
Vmpooler::API
|
||||
end
|
||||
# Added to ensure no leakage in rack state from previous tests.
|
||||
# Removes all routes, filters, middleware and extension hooks from the current class
|
||||
# https://rubydoc.info/gems/sinatra/Sinatra/Base#reset!-class_method
|
||||
before(:each) do
|
||||
app.reset!
|
||||
end
|
||||
|
||||
describe '/ondemandvm' do
|
||||
let(:prefix) { '/api/v1' }
|
||||
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
||||
let(:config) {
|
||||
{
|
||||
config: {
|
||||
'site_name' => 'test pooler',
|
||||
'vm_lifetime_auth' => 2,
|
||||
'max_ondemand_instances_per_request' => 50,
|
||||
'backend_weight' => {
|
||||
'compute1' => 5,
|
||||
'compute2' => 0
|
||||
}
|
||||
},
|
||||
pools: [
|
||||
{'name' => 'pool1', 'size' => 0, 'clone_target' => 'compute1'},
|
||||
{'name' => 'pool2', 'size' => 0, 'clone_target' => 'compute2'},
|
||||
{'name' => 'pool3', 'size' => 0, 'clone_target' => 'compute1'}
|
||||
],
|
||||
alias: {
|
||||
'poolone' => ['pool1'],
|
||||
'pool2' => ['pool1']
|
||||
},
|
||||
pool_names: [ 'pool1', 'pool2', 'pool3', 'poolone' ]
|
||||
}
|
||||
}
|
||||
let(:current_time) { Time.now }
|
||||
let(:vmname) { 'abcdefghijkl' }
|
||||
let(:checkoutlock) { Mutex.new }
|
||||
let(:uuid) { SecureRandom.uuid }
|
||||
|
||||
before(:each) do
|
||||
expect(app).to receive(:run!).once
|
||||
app.execute([:api], config, redis, metrics, nil)
|
||||
app.settings.set :config, auth: false
|
||||
app.settings.set :checkoutlock, checkoutlock
|
||||
create_token('abcdefghijklmnopqrstuvwxyz012345', 'jdoe', current_time)
|
||||
config[:pools].each do |pool|
|
||||
redis.sadd('vmpooler__pools', pool['name'])
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /ondemandvm' do
|
||||
|
||||
context 'with a configured pool' do
|
||||
|
||||
context 'with no request_id provided in payload' do
|
||||
before(:each) do
|
||||
expect(SecureRandom).to receive(:uuid).and_return(uuid)
|
||||
end
|
||||
|
||||
it 'generates a request_id when none is provided' do
|
||||
post "#{prefix}/ondemandvm", '{"pool1":"1"}'
|
||||
expect_json(true, 201)
|
||||
|
||||
expected = {
|
||||
"ok": true,
|
||||
"request_id": uuid
|
||||
}
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'uses a configured platform to fulfill a ondemand request' 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
|
||||
|
||||
it 'creates a provisioning request in redis' do
|
||||
expect(redis).to receive(:zadd).with('vmpooler__provisioning__request', Integer, uuid).and_return(1)
|
||||
post "#{prefix}/ondemandvm", '{"poolone":"1"}'
|
||||
end
|
||||
|
||||
it 'sets a platform string in redis for the request to indicate selected platforms' do
|
||||
expect(redis).to receive(:hset).with("vmpooler__odrequest__#{uuid}", 'requested', 'poolone:pool1:1')
|
||||
post "#{prefix}/ondemandvm", '{"poolone":"1"}'
|
||||
end
|
||||
|
||||
context 'with a backend of 0 weight' do
|
||||
before(:each) do
|
||||
config[:config]['backend_weight']['compute1'] = 0
|
||||
end
|
||||
|
||||
it 'sets the platform string in redis for the request to indicate the selected platforms' do
|
||||
expect(redis).to receive(:hset).with("vmpooler__odrequest__#{uuid}", 'requested', 'pool1:pool1:1')
|
||||
post "#{prefix}/ondemandvm", '{"pool1":"1"}'
|
||||
end
|
||||
end
|
||||
|
||||
it 'sets the platform string in redis for the request to indicate the selected platforms using weight' do
|
||||
expect(redis).to receive(:hset).with("vmpooler__odrequest__#{uuid}", 'requested', 'pool2:pool1:1')
|
||||
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
|
||||
|
||||
it 'should include domain in the return reply' do
|
||||
post "#{prefix}/ondemandvm", '{"poolone":"1"}'
|
||||
expect_json(true, 201)
|
||||
expected = {
|
||||
"ok": true,
|
||||
"request_id": uuid,
|
||||
"domain": domain
|
||||
}
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a resource request that exceeds the specified limit' do
|
||||
let(:max_instances) { 50 }
|
||||
before(:each) do
|
||||
config[:config]['max_ondemand_instances_per_request'] = max_instances
|
||||
end
|
||||
|
||||
it 'should reject the request with a message' do
|
||||
post "#{prefix}/ondemandvm", '{"pool1":"51"}'
|
||||
expect_json(false, 403)
|
||||
expected = {
|
||||
"ok": false,
|
||||
"message": "requested amount of instances exceeds the maximum #{max_instances}"
|
||||
}
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
end
|
||||
|
||||
context 'with request_id provided in the payload' do
|
||||
it 'uses the given request_id when provided' do
|
||||
post "#{prefix}/ondemandvm", '{"pool1":"1","request_id":"1234"}'
|
||||
expect_json(true, 201)
|
||||
|
||||
expected = {
|
||||
"ok": true,
|
||||
"request_id": "1234"
|
||||
}
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'returns 409 conflict error when the request_id has been used' do
|
||||
post "#{prefix}/ondemandvm", '{"pool1":"1","request_id":"1234"}'
|
||||
post "#{prefix}/ondemandvm", '{"pool1":"1","request_id":"1234"}'
|
||||
expect_json(false, 409)
|
||||
|
||||
expected = {
|
||||
"ok": false,
|
||||
"request_id": "1234",
|
||||
"message": "request_id '1234' has already been created"
|
||||
}
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
end
|
||||
|
||||
context 'with auth configured' do
|
||||
|
||||
it 'sets the token and user' do
|
||||
app.settings.set :config, auth: true
|
||||
expect(SecureRandom).to receive(:uuid).and_return(uuid)
|
||||
allow(redis).to receive(:hset)
|
||||
expect(redis).to receive(:hset).with("vmpooler__odrequest__#{uuid}", 'token:token', 'abcdefghijklmnopqrstuvwxyz012345')
|
||||
expect(redis).to receive(:hset).with("vmpooler__odrequest__#{uuid}", 'token:user', 'jdoe')
|
||||
post "#{prefix}/ondemandvm", '{"pool1":"1"}', {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a pool that is not configured' do
|
||||
let(:badpool) { 'pool4' }
|
||||
it 'returns the bad template' do
|
||||
post "#{prefix}/ondemandvm", '{"pool4":"1"}'
|
||||
expect_json(false, 404)
|
||||
|
||||
expected = {
|
||||
"ok": false,
|
||||
"bad_templates": [ badpool ]
|
||||
}
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns 400 and a message when JSON is invalid' do
|
||||
post "#{prefix}/ondemandvm", '{"pool1":"1}'
|
||||
expect_json(false, 400)
|
||||
expected = {
|
||||
"ok": false,
|
||||
"message": "JSON payload could not be parsed"
|
||||
}
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /ondemandvm' do
|
||||
it 'returns 404 with message when request is not found' do
|
||||
get "#{prefix}/ondemandvm/#{uuid}"
|
||||
expect_json(false, 404)
|
||||
expected = {
|
||||
"ok": false,
|
||||
"message": "no request found for request_id '#{uuid}'"
|
||||
}
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
context 'when the request is found' do
|
||||
let(:score) { current_time }
|
||||
let(:platforms_string) { 'pool1:pool1:1' }
|
||||
before(:each) do
|
||||
create_ondemand_request_for_test(uuid, score, platforms_string, redis)
|
||||
end
|
||||
|
||||
it 'returns 202 while the request is waiting' do
|
||||
get "#{prefix}/ondemandvm/#{uuid}"
|
||||
expect_json(true, 202)
|
||||
expected = {
|
||||
"ok": true,
|
||||
"request_id": uuid,
|
||||
"ready": false,
|
||||
"pool1": {
|
||||
"ready": "0",
|
||||
"pending": "1"
|
||||
}
|
||||
}
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
context 'with ready instances' do
|
||||
before(:each) do
|
||||
create_ondemand_vm(vmname, uuid, 'pool1', 'pool1', redis)
|
||||
set_ondemand_request_status(uuid, 'ready', redis)
|
||||
end
|
||||
|
||||
it 'returns 200 with hostnames when the request is ready' do
|
||||
get "#{prefix}/ondemandvm/#{uuid}"
|
||||
expect_json(true, 200)
|
||||
expected = {
|
||||
"ok": true,
|
||||
"request_id": uuid,
|
||||
"ready": true,
|
||||
"pool1": {
|
||||
"hostname": [
|
||||
vmname
|
||||
]
|
||||
}
|
||||
}
|
||||
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 result' do
|
||||
get "#{prefix}/ondemandvm/#{uuid}"
|
||||
expected = {
|
||||
"ok": true,
|
||||
"request_id": uuid,
|
||||
"ready": true,
|
||||
"pool1": {
|
||||
"hostname": [
|
||||
vmname
|
||||
]
|
||||
},
|
||||
"domain": domain
|
||||
}
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a deleted request' do
|
||||
before(:each) do
|
||||
set_ondemand_request_status(uuid, 'deleted', redis)
|
||||
end
|
||||
|
||||
it 'returns a message that the request has been deleted' do
|
||||
get "#{prefix}/ondemandvm/#{uuid}"
|
||||
expect_json(true, 200)
|
||||
expected = {
|
||||
"ok": true,
|
||||
"request_id": uuid,
|
||||
"ready": false,
|
||||
"message": "The request has been deleted"
|
||||
}
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a failed request' do
|
||||
let(:ondemand_request_ttl) { 5 }
|
||||
before(:each) do
|
||||
config[:config]['ondemand_request_ttl'] = ondemand_request_ttl
|
||||
set_ondemand_request_status(uuid, 'failed', redis)
|
||||
end
|
||||
|
||||
it 'returns a message that the request has failed' do
|
||||
get "#{prefix}/ondemandvm/#{uuid}"
|
||||
expect_json(true, 200)
|
||||
expected = {
|
||||
"ok": true,
|
||||
"request_id": uuid,
|
||||
"ready": false,
|
||||
"message": "The request failed to provision instances within the configured ondemand_request_ttl '#{ondemand_request_ttl}'"
|
||||
}
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /ondemandvm' do
|
||||
let(:expiration) { 129_600_0 }
|
||||
it 'returns 404 with message when request is not found' do
|
||||
delete "#{prefix}/ondemandvm/#{uuid}"
|
||||
expect_json(false, 404)
|
||||
expected = {
|
||||
"ok": false,
|
||||
"message": "no request found for request_id '#{uuid}'"
|
||||
}
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
context 'when the request is found' do
|
||||
let(:platforms_string) { 'pool1:pool1:1' }
|
||||
let(:score) { current_time.to_i }
|
||||
before(:each) do
|
||||
create_ondemand_request_for_test(uuid, score, platforms_string, redis)
|
||||
end
|
||||
|
||||
it 'returns 200 for a deleted request' do
|
||||
delete "#{prefix}/ondemandvm/#{uuid}"
|
||||
expect_json(true, 200)
|
||||
expected = { 'ok': true }
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'marks the request hash for expiration in two weeks' do
|
||||
expect(redis).to receive(:expire).with("vmpooler__odrequest__#{uuid}", expiration)
|
||||
delete "#{prefix}/ondemandvm/#{uuid}"
|
||||
end
|
||||
|
||||
context 'with running instances' do
|
||||
let(:pool) { 'pool1' }
|
||||
let(:pool_alias) { pool }
|
||||
before(:each) do
|
||||
create_ondemand_vm(vmname, uuid, pool, pool_alias, redis)
|
||||
end
|
||||
|
||||
it 'moves allocated instances to the completed queue' do
|
||||
expect(redis).to receive(:smove).with("vmpooler__running__#{pool}", "vmpooler__completed__#{pool}", vmname)
|
||||
delete "#{prefix}/ondemandvm/#{uuid}"
|
||||
end
|
||||
|
||||
it 'deletes the set tracking instances allocated for the request' do
|
||||
expect(redis).to receive(:del).with("vmpooler__#{uuid}__#{pool_alias}__#{pool}")
|
||||
delete "#{prefix}/ondemandvm/#{uuid}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
require 'spec_helper'
|
||||
require 'rack/test'
|
||||
|
||||
describe Vmpooler::API::V1 do
|
||||
include Rack::Test::Methods
|
||||
|
||||
def app()
|
||||
Vmpooler::API
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
Vmpooler::API.reset!
|
||||
end
|
||||
|
||||
let(:config) {
|
||||
{
|
||||
config: {
|
||||
'site_name' => 'test pooler',
|
||||
'vm_lifetime_auth' => 2,
|
||||
'experimental_features' => true
|
||||
},
|
||||
pools: [
|
||||
{'name' => 'pool1', 'size' => 5, 'template' => 'templates/pool1', 'clone_target' => 'default_cluster'},
|
||||
{'name' => 'pool2', 'size' => 10}
|
||||
],
|
||||
statsd: { 'prefix' => 'stats_prefix'},
|
||||
alias: { 'poolone' => 'pool1' },
|
||||
pool_names: [ 'pool1', 'pool2', 'poolone' ]
|
||||
}
|
||||
}
|
||||
|
||||
describe '/poolreset' do
|
||||
let(:prefix) { '/api/v1' }
|
||||
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
||||
|
||||
let(:current_time) { Time.now }
|
||||
|
||||
before(:each) do
|
||||
expect(app).to receive(:run!).once
|
||||
app.execute([:api], config, redis, metrics, nil)
|
||||
app.settings.set :config, auth: false
|
||||
create_token('abcdefghijklmnopqrstuvwxyz012345', 'jdoe', current_time)
|
||||
end
|
||||
|
||||
describe 'POST /poolreset' do
|
||||
it 'refreshes ready and pending instances from a pool' do
|
||||
post "#{prefix}/poolreset", '{"pool1":"1"}'
|
||||
expect_json(ok = true, http = 201)
|
||||
|
||||
expected = { ok: true }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'fails on nonexistent pools' do
|
||||
post "#{prefix}/poolreset", '{"poolpoolpool":"1"}'
|
||||
expect_json(ok = false, http = 400)
|
||||
end
|
||||
|
||||
it 'resets multiple pools' do
|
||||
post "#{prefix}/poolreset", '{"pool1":"1","pool2":"1"}'
|
||||
expect_json(ok = true, http = 201)
|
||||
|
||||
expected = { ok: true }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'fails when not all pools exist' do
|
||||
post "#{prefix}/poolreset", '{"pool1":"1","pool3":"1"}'
|
||||
expect_json(ok = false, http = 400)
|
||||
|
||||
expected = {
|
||||
ok: false,
|
||||
bad_pools: ['pool3']
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
context 'with experimental features disabled' do
|
||||
before(:each) do
|
||||
config[:config]['experimental_features'] = false
|
||||
end
|
||||
|
||||
it 'should return 405' do
|
||||
post "#{prefix}/poolreset", '{"pool1":"1"}'
|
||||
expect_json(ok = false, http = 405)
|
||||
|
||||
expected = { ok: false }
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
end
|
||||
|
||||
it 'should return 400 for invalid json' do
|
||||
post "#{prefix}/poolreset", '{"pool1":"1}'
|
||||
expect_json(ok = false, http = 400)
|
||||
|
||||
expected = { ok: false }
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'should return 400 with a bad pool name' do
|
||||
post "#{prefix}/poolreset", '{"pool11":"1"}'
|
||||
expect_json(ok = false, http = 400)
|
||||
|
||||
expected = { ok: false }
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'should return 404 when there is no payload' do
|
||||
post "#{prefix}/poolreset"
|
||||
expect_json(ok = false, http = 404)
|
||||
|
||||
expected = { ok: false }
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,247 +0,0 @@
|
|||
require 'spec_helper'
|
||||
require 'rack/test'
|
||||
|
||||
def has_set_tag?(vm, tag, value)
|
||||
value == redis.hget("vmpooler__vm__#{vm}", "tag:#{tag}")
|
||||
end
|
||||
|
||||
describe Vmpooler::API::V1 do
|
||||
include Rack::Test::Methods
|
||||
|
||||
def app()
|
||||
Vmpooler::API
|
||||
end
|
||||
|
||||
# Added to ensure no leakage in rack state from previous tests.
|
||||
# Removes all routes, filters, middleware and extension hooks from the current class
|
||||
# https://rubydoc.info/gems/sinatra/Sinatra/Base#reset!-class_method
|
||||
before(:each) do
|
||||
app.reset!
|
||||
end
|
||||
|
||||
describe 'status and metrics endpoints' do
|
||||
let(:prefix) { '/api/v1' }
|
||||
|
||||
let(:config) {
|
||||
{
|
||||
config: {
|
||||
'site_name' => 'test pooler',
|
||||
'vm_lifetime_auth' => 2,
|
||||
},
|
||||
pools: [
|
||||
{'name' => 'pool1', 'size' => 5, 'alias' => ['poolone', 'poolun']},
|
||||
{'name' => 'pool2', 'size' => 10},
|
||||
{'name' => 'pool3', 'size' => 10, 'alias' => 'NotArray'}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
let(:current_time) { Time.now }
|
||||
|
||||
before(:each) do
|
||||
expect(app).to receive(:run!).once
|
||||
app.execute([:api], config, redis, nil, nil)
|
||||
app.settings.set :config, auth: false
|
||||
create_token('abcdefghijklmnopqrstuvwxyz012345', 'jdoe', current_time)
|
||||
end
|
||||
|
||||
describe 'GET /status' do
|
||||
it 'returns the configured maximum size for each pool' do
|
||||
get "#{prefix}/status/"
|
||||
|
||||
# of course /status doesn't conform to the weird standard everything else uses...
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result["pools"]["pool1"]["max"]).to be(5)
|
||||
expect(result["pools"]["pool2"]["max"]).to be(10)
|
||||
end
|
||||
|
||||
it 'returns the number of ready vms for each pool' do
|
||||
3.times {|i| create_ready_vm("pool1", "vm-#{i}", redis) }
|
||||
get "#{prefix}/status/"
|
||||
|
||||
# of course /status doesn't conform to the weird standard everything else uses...
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result["pools"]["pool1"]["ready"]).to be(3)
|
||||
expect(result["pools"]["pool2"]["ready"]).to be(0)
|
||||
end
|
||||
|
||||
it 'returns the number of running vms for each pool' do
|
||||
3.times {|i| create_running_vm("pool1", "vm-#{i}", redis) }
|
||||
4.times {|i| create_running_vm("pool2", "vm-#{i}", redis) }
|
||||
|
||||
get "#{prefix}/status/"
|
||||
|
||||
# of course /status doesn't conform to the weird standard everything else uses...
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result["pools"]["pool1"]["running"]).to be(3)
|
||||
expect(result["pools"]["pool2"]["running"]).to be(4)
|
||||
end
|
||||
|
||||
it 'returns the number of pending vms for each pool' do
|
||||
3.times {|i| create_pending_vm("pool1", "vm-#{i}", redis) }
|
||||
4.times {|i| create_pending_vm("pool2", "vm-#{i}", redis) }
|
||||
|
||||
get "#{prefix}/status/"
|
||||
|
||||
# of course /status doesn't conform to the weird standard everything else uses...
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result["pools"]["pool1"]["pending"]).to be(3)
|
||||
expect(result["pools"]["pool2"]["pending"]).to be(4)
|
||||
end
|
||||
|
||||
it 'returns aliases if configured in the pool' do
|
||||
get "#{prefix}/status/"
|
||||
|
||||
# of course /status doesn't conform to the weird standard everything else uses...
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result["pools"]["pool1"]["alias"]).to eq(['poolone', 'poolun'])
|
||||
expect(result["pools"]["pool2"]["alias"]).to be(nil)
|
||||
expect(result["pools"]["pool3"]["alias"]).to eq('NotArray')
|
||||
end
|
||||
|
||||
it '(for v1 backwards compatibility) lists any empty pools in the status section' do
|
||||
get "#{prefix}/status/"
|
||||
|
||||
# of course /status doesn't conform to the weird standard everything else uses...
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result["status"]["empty"].sort).to eq(["pool1", "pool2", "pool3"])
|
||||
end
|
||||
end
|
||||
describe 'GET /status with view query parameter' do
|
||||
it 'returns capacity when specified' do
|
||||
get "#{prefix}/status?view=capacity"
|
||||
|
||||
# of course /status doesn't conform to the weird standard everything else uses...
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result["capacity"]).to_not be(nil)
|
||||
expect(result["queue"]).to be(nil)
|
||||
expect(result["clone"]).to be(nil)
|
||||
expect(result["boot"]).to be(nil)
|
||||
expect(result["pools"]).to be(nil)
|
||||
expect(result["status"]).to_not be(nil)
|
||||
end
|
||||
it 'returns pools and queue when specified' do
|
||||
get "#{prefix}/status?view=pools,queue"
|
||||
|
||||
# of course /status doesn't conform to the weird standard everything else uses...
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result["capacity"]).to be(nil)
|
||||
expect(result["queue"]).to_not be(nil)
|
||||
expect(result["clone"]).to be(nil)
|
||||
expect(result["boot"]).to be(nil)
|
||||
expect(result["pools"]).to_not be(nil)
|
||||
expect(result["status"]).to_not be(nil)
|
||||
end
|
||||
it 'does nothing with invalid view names' do
|
||||
get "#{prefix}/status?view=clone,boot,invalidThingToView"
|
||||
|
||||
# of course /status doesn't conform to the weird standard everything else uses...
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result["capacity"]).to be(nil)
|
||||
expect(result["queue"]).to be(nil)
|
||||
expect(result["clone"]).to_not be(nil)
|
||||
expect(result["boot"]).to_not be(nil)
|
||||
expect(result["pools"]).to be(nil)
|
||||
expect(result["status"]).to_not be(nil)
|
||||
end
|
||||
it 'returns everything when view is not specified' do
|
||||
get "#{prefix}/status"
|
||||
|
||||
# of course /status doesn't conform to the weird standard everything else uses...
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result["capacity"]).to_not be(nil)
|
||||
expect(result["queue"]).to_not be(nil)
|
||||
expect(result["clone"]).to_not be(nil)
|
||||
expect(result["boot"]).to_not be(nil)
|
||||
expect(result["pools"]).to_not be(nil)
|
||||
expect(result["status"]).to_not be(nil)
|
||||
end
|
||||
it 'returns everything when view is alone' do
|
||||
get "#{prefix}/status?view"
|
||||
|
||||
# of course /status doesn't conform to the weird standard everything else uses...
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result["capacity"]).to_not be(nil)
|
||||
expect(result["queue"]).to_not be(nil)
|
||||
expect(result["clone"]).to_not be(nil)
|
||||
expect(result["boot"]).to_not be(nil)
|
||||
expect(result["pools"]).to_not be(nil)
|
||||
expect(result["status"]).to_not be(nil)
|
||||
end
|
||||
it 'returns status only when view is empty' do
|
||||
get "#{prefix}/status?view="
|
||||
|
||||
# of course /status doesn't conform to the weird standard everything else uses...
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result["capacity"]).to be(nil)
|
||||
expect(result["queue"]).to be(nil)
|
||||
expect(result["clone"]).to be(nil)
|
||||
expect(result["boot"]).to be(nil)
|
||||
expect(result["pools"]).to be(nil)
|
||||
expect(result["status"]).to_not be(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /poolstat' do
|
||||
it 'returns empty list when pool is not set' do
|
||||
get "#{prefix}/poolstat"
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result == {})
|
||||
end
|
||||
it 'returns empty list when pool is not found' do
|
||||
get "#{prefix}/poolstat?pool=unknownpool"
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result == {})
|
||||
end
|
||||
it 'returns one pool when requesting one with alias' do
|
||||
get "#{prefix}/poolstat?pool=pool1"
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result["pools"].size == 1)
|
||||
expect(result["pools"]["pool1"]["ready"]).to eq(0)
|
||||
expect(result["pools"]["pool1"]["max"]).to eq(5)
|
||||
expect(result["pools"]["pool1"]["alias"]).to eq(['poolone', 'poolun'])
|
||||
end
|
||||
it 'returns one pool when requesting one without alias' do
|
||||
get "#{prefix}/poolstat?pool=pool2"
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result["pools"].size == 1)
|
||||
expect(result["pools"]["pool2"]["ready"]).to eq(0)
|
||||
expect(result["pools"]["pool2"]["max"]).to eq(10)
|
||||
expect(result["pools"]["pool2"]["alias"]).to be(nil)
|
||||
end
|
||||
it 'returns multiple pools when requesting csv' do
|
||||
get "#{prefix}/poolstat?pool=pool1,pool2"
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result["pools"].size == 2)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /totalrunning' do
|
||||
it 'returns the number of running VMs' do
|
||||
get "#{prefix}/totalrunning"
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
5.times {|i| create_running_vm("pool1", "vm-#{i}", redis, redis) }
|
||||
5.times {|i| create_running_vm("pool3", "vm-#{i}", redis, redis) }
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result["running"] == 10)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,210 +0,0 @@
|
|||
require 'spec_helper'
|
||||
require 'rack/test'
|
||||
|
||||
describe Vmpooler::API::V1 do
|
||||
include Rack::Test::Methods
|
||||
|
||||
def app()
|
||||
Vmpooler::API
|
||||
end
|
||||
|
||||
# Added to ensure no leakage in rack state from previous tests.
|
||||
# Removes all routes, filters, middleware and extension hooks from the current class
|
||||
# https://rubydoc.info/gems/sinatra/Sinatra/Base#reset!-class_method
|
||||
before(:each) do
|
||||
app.reset!
|
||||
end
|
||||
|
||||
describe '/token' do
|
||||
let(:prefix) { '/api/v1' }
|
||||
let(:current_time) { Time.now }
|
||||
let(:config) { {
|
||||
config: {}
|
||||
} }
|
||||
|
||||
before(:each) do
|
||||
expect(app).to receive(:run!).once
|
||||
app.execute([:api], config, redis, nil, nil)
|
||||
end
|
||||
|
||||
describe 'GET /token' do
|
||||
context '(auth not configured)' do
|
||||
let(:config) { {
|
||||
config: {},
|
||||
auth: false
|
||||
} }
|
||||
|
||||
it 'returns a 404' do
|
||||
get "#{prefix}/token"
|
||||
expect_json(ok = false, http = 404)
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
let(:config) {
|
||||
{
|
||||
config: {},
|
||||
auth: {
|
||||
'provider' => 'dummy'
|
||||
}
|
||||
}
|
||||
}
|
||||
let(:username_str) { 'admin' }
|
||||
let(:password_str) { 's3cr3t' }
|
||||
|
||||
it 'returns a 401 if not authed' do
|
||||
get "#{prefix}/token"
|
||||
expect_json(ok = false, http = 401)
|
||||
end
|
||||
|
||||
it 'returns a list of tokens if authed' do
|
||||
create_token "abc", "admin", current_time
|
||||
|
||||
authorize 'admin', 's3cr3t'
|
||||
get "#{prefix}/token"
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expect(JSON.parse(last_response.body)['abc']['created']).to eq(current_time.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /token' do
|
||||
context '(auth not configured)' do
|
||||
let(:config) { {
|
||||
config: {},
|
||||
auth: false
|
||||
} }
|
||||
|
||||
it 'returns a 404' do
|
||||
post "#{prefix}/token"
|
||||
expect_json(ok = false, http = 404)
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
let(:config) {
|
||||
{
|
||||
config: {},
|
||||
auth: {
|
||||
'provider' => 'dummy'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it 'returns a 401 if not authed' do
|
||||
post "#{prefix}/token"
|
||||
expect_json(ok = false, http = 401)
|
||||
end
|
||||
|
||||
it 'returns a newly created token if authed' do
|
||||
authorize 'admin', 's3cr3t'
|
||||
post "#{prefix}/token"
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
returned_token = JSON.parse(last_response.body)['token']
|
||||
expect(returned_token.length).to be(32)
|
||||
expect(get_token_data(returned_token)['user']).to eq("admin")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '/token/:token' do
|
||||
let(:prefix) { '/api/v1' }
|
||||
let(:current_time) { Time.now }
|
||||
|
||||
before(:each) do
|
||||
expect(app).to receive(:run!).once
|
||||
app.execute([:api], config, redis, nil, nil)
|
||||
app.settings.set :config, config
|
||||
app.settings.set :redis, redis
|
||||
end
|
||||
|
||||
def create_vm_for_token(token, pool, vm)
|
||||
redis.sadd("vmpooler__running__#{pool}", vm)
|
||||
redis.hset("vmpooler__vm__#{vm}", "token:token", token)
|
||||
end
|
||||
|
||||
describe 'GET /token/:token' do
|
||||
context '(auth not configured)' do
|
||||
let(:config) { {
|
||||
config: {},
|
||||
auth: false
|
||||
} }
|
||||
|
||||
it 'returns a 404' do
|
||||
get "#{prefix}/token/this"
|
||||
expect_json(ok = false, http = 404)
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
let(:config) { {
|
||||
config: {},
|
||||
auth: true,
|
||||
pools: [
|
||||
{'name' => 'pool1', 'size' => 5}
|
||||
]
|
||||
} }
|
||||
|
||||
it 'returns a token' do
|
||||
create_token "mytoken", "admin", current_time
|
||||
create_vm_for_token "mytoken", "pool1", "vmhostname"
|
||||
|
||||
get "#{prefix}/token/mytoken"
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expect(JSON.parse(last_response.body)['ok']).to eq(true)
|
||||
expect(JSON.parse(last_response.body)['mytoken']['user']).to eq('admin')
|
||||
expect(JSON.parse(last_response.body)['mytoken']['vms']['running']).to include('vmhostname')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /token/:token' do
|
||||
context '(auth not configured)' do
|
||||
let(:config) { {
|
||||
config: {},
|
||||
auth: false
|
||||
} }
|
||||
|
||||
it 'returns a 404' do
|
||||
delete "#{prefix}/token/this"
|
||||
expect_json(ok = false, http = 404)
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
let(:config) {
|
||||
{
|
||||
config: {},
|
||||
auth: {
|
||||
'provider' => 'dummy'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it 'returns a 401 if not authed' do
|
||||
delete "#{prefix}/token/this"
|
||||
expect_json(ok = false, http = 401)
|
||||
end
|
||||
|
||||
it 'deletes a token if authed' do
|
||||
create_token("mytoken", "admin", current_time)
|
||||
authorize 'admin', 's3cr3t'
|
||||
|
||||
delete "#{prefix}/token/mytoken"
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'fails if token does not exist' do
|
||||
authorize 'admin', 's3cr3t'
|
||||
|
||||
delete "#{prefix}/token/missingtoken"
|
||||
expect_json(ok = false, http = 401) # TODO: should this be 404?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,352 +0,0 @@
|
|||
require 'spec_helper'
|
||||
require 'rack/test'
|
||||
|
||||
def has_set_tag?(vm, tag, value)
|
||||
value == redis.hget("vmpooler__vm__#{vm}", "tag:#{tag}")
|
||||
end
|
||||
|
||||
describe Vmpooler::API::V1 do
|
||||
include Rack::Test::Methods
|
||||
|
||||
def app()
|
||||
Vmpooler::API
|
||||
end
|
||||
|
||||
# Added to ensure no leakage in rack state from previous tests.
|
||||
# Removes all routes, filters, middleware and extension hooks from the current class
|
||||
# https://rubydoc.info/gems/sinatra/Sinatra/Base#reset!-class_method
|
||||
before(:each) do
|
||||
app.reset!
|
||||
end
|
||||
|
||||
describe '/vm/:hostname' do
|
||||
let(:prefix) { '/api/v1' }
|
||||
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
||||
|
||||
let(:config) {
|
||||
{
|
||||
config: {
|
||||
'site_name' => 'test pooler',
|
||||
'vm_lifetime_auth' => 2,
|
||||
|
||||
},
|
||||
pools: [
|
||||
{'name' => 'pool1', 'size' => 5},
|
||||
{'name' => 'pool2', 'size' => 10}
|
||||
],
|
||||
alias: { 'poolone' => 'pool1' },
|
||||
auth: false
|
||||
}
|
||||
}
|
||||
|
||||
let(:current_time) { Time.now }
|
||||
|
||||
before(:each) do
|
||||
expect(app).to receive(:run!).once
|
||||
app.execute([:api], config, redis, metrics, nil)
|
||||
create_token('abcdefghijklmnopqrstuvwxyz012345', 'jdoe', current_time)
|
||||
end
|
||||
|
||||
describe 'PUT /vm/:hostname' do
|
||||
it 'allows tags to be set' do
|
||||
create_vm('testhost', redis)
|
||||
put "#{prefix}/vm/testhost", '{"tags":{"tested_by":"rspec"}}'
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expect has_set_tag?('testhost', 'tested_by', 'rspec')
|
||||
end
|
||||
|
||||
it 'skips empty tags' do
|
||||
create_vm('testhost', redis)
|
||||
put "#{prefix}/vm/testhost", '{"tags":{"tested_by":""}}'
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expect !has_set_tag?('testhost', 'tested_by', '')
|
||||
end
|
||||
|
||||
it 'does not set tags if request body format is invalid' do
|
||||
create_vm('testhost', redis)
|
||||
put "#{prefix}/vm/testhost", '{"tags":{"tested"}}'
|
||||
expect_json(ok = false, http = 400)
|
||||
|
||||
expect !has_set_tag?('testhost', 'tested', '')
|
||||
end
|
||||
|
||||
context '(allowed_tags configured)' do
|
||||
it 'fails if specified tag is not in allowed_tags array' do
|
||||
app.settings.set :config,
|
||||
{ :config => { 'allowed_tags' => ['created_by', 'project', 'url'] } }
|
||||
|
||||
create_vm('testhost', redis)
|
||||
|
||||
put "#{prefix}/vm/testhost", '{"tags":{"created_by":"rspec","tested_by":"rspec"}}'
|
||||
expect_json(ok = false, http = 400)
|
||||
|
||||
expect !has_set_tag?('testhost', 'tested_by', 'rspec')
|
||||
end
|
||||
end
|
||||
|
||||
context '(tagfilter configured)' do
|
||||
before(:each) do
|
||||
app.settings.set :config, tagfilter: { 'url' => '(.*)\/' }
|
||||
end
|
||||
|
||||
it 'correctly filters tags' do
|
||||
create_vm('testhost', redis)
|
||||
|
||||
put "#{prefix}/vm/testhost", '{"tags":{"url":"foo.com/something.html"}}'
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expect has_set_tag?('testhost', 'url', 'foo.com')
|
||||
end
|
||||
|
||||
it "doesn't eat tags not matching filter" do
|
||||
create_vm('testhost', redis)
|
||||
put "#{prefix}/vm/testhost", '{"tags":{"url":"foo.com"}}'
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expect has_set_tag?('testhost', 'url', 'foo.com')
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth not configured)' do
|
||||
before(:each) do
|
||||
app.settings.set :config, auth: false
|
||||
end
|
||||
|
||||
it 'allows VM lifetime to be modified without a token' do
|
||||
create_vm('testhost', redis)
|
||||
|
||||
put "#{prefix}/vm/testhost", '{"lifetime":"1"}'
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
vm = fetch_vm('testhost')
|
||||
expect(vm['lifetime'].to_i).to eq(1)
|
||||
end
|
||||
|
||||
it 'does not allow a lifetime to be 0' do
|
||||
create_vm('testhost', redis)
|
||||
|
||||
put "#{prefix}/vm/testhost", '{"lifetime":"0"}'
|
||||
expect_json(ok = false, http = 400)
|
||||
|
||||
vm = fetch_vm('testhost')
|
||||
expect(vm['lifetime']).to be_nil
|
||||
end
|
||||
|
||||
it 'does not enforce a lifetime' do
|
||||
create_vm('testhost', redis)
|
||||
|
||||
put "#{prefix}/vm/testhost", '{"lifetime":"20000"}'
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
vm = fetch_vm('testhost')
|
||||
expect(vm['lifetime']).to eq("20000")
|
||||
end
|
||||
|
||||
it 'does not allow a lifetime to be initially past config max_lifetime_upper_limit' do
|
||||
app.settings.set :config,
|
||||
{ :config => { 'max_lifetime_upper_limit' => 168 } }
|
||||
create_vm('testhost', redis)
|
||||
|
||||
put "#{prefix}/vm/testhost", '{"lifetime":"200"}'
|
||||
expect_json(ok = false, http = 400)
|
||||
|
||||
vm = fetch_vm('testhost')
|
||||
expect(vm['lifetime']).to be_nil
|
||||
end
|
||||
|
||||
# it 'does not allow a lifetime to be extended past config 168' do
|
||||
# app.settings.set :config,
|
||||
# { :config => { 'max_lifetime_upper_limit' => 168 } }
|
||||
# create_vm('testhost', redis)
|
||||
#
|
||||
# set_vm_data('testhost', "checkout", (Time.now - (69*60*60)), redis)
|
||||
# puts redis.hget("vmpooler__vm__testhost", 'checkout')
|
||||
# put "#{prefix}/vm/testhost", '{"lifetime":"100"}'
|
||||
# expect_json(ok = false, http = 400)
|
||||
#
|
||||
# vm = fetch_vm('testhost')
|
||||
# expect(vm['lifetime']).to be_nil
|
||||
# end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
before(:each) do
|
||||
app.settings.set :config, auth: true
|
||||
end
|
||||
|
||||
it 'allows VM lifetime to be modified with a token' do
|
||||
create_vm('testhost', redis)
|
||||
|
||||
put "#{prefix}/vm/testhost", '{"lifetime":"1"}', {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
vm = fetch_vm('testhost')
|
||||
expect(vm['lifetime'].to_i).to eq(1)
|
||||
end
|
||||
|
||||
it 'does not allows VM lifetime to be modified without a token' do
|
||||
create_vm('testhost', redis)
|
||||
|
||||
put "#{prefix}/vm/testhost", '{"lifetime":"1"}'
|
||||
expect_json(ok = false, http = 401)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /vm/:hostname' do
|
||||
context '(auth not configured)' do
|
||||
it 'does not delete a non-existant VM' do
|
||||
delete "#{prefix}/vm/testhost"
|
||||
expect_json(ok = false, http = 404)
|
||||
end
|
||||
|
||||
it 'deletes an existing VM' do
|
||||
create_running_vm('pool1', 'testhost', redis)
|
||||
expect fetch_vm('testhost')
|
||||
|
||||
delete "#{prefix}/vm/testhost"
|
||||
expect_json(ok = true, http = 200)
|
||||
expect !fetch_vm('testhost')
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
before(:each) do
|
||||
app.settings.set :config, auth: true
|
||||
end
|
||||
|
||||
context '(checked-out without token)' do
|
||||
it 'deletes a VM without supplying a token' do
|
||||
create_running_vm('pool1', 'testhost', redis)
|
||||
expect fetch_vm('testhost')
|
||||
|
||||
delete "#{prefix}/vm/testhost"
|
||||
expect_json(ok = true, http = 200)
|
||||
expect !fetch_vm('testhost')
|
||||
end
|
||||
end
|
||||
|
||||
context '(checked-out with token)' do
|
||||
it 'fails to delete a VM without supplying a token' do
|
||||
create_running_vm('pool1', 'testhost', redis, 'abcdefghijklmnopqrstuvwxyz012345')
|
||||
expect fetch_vm('testhost')
|
||||
|
||||
delete "#{prefix}/vm/testhost"
|
||||
expect_json(ok = false, http = 401)
|
||||
expect fetch_vm('testhost')
|
||||
end
|
||||
|
||||
it 'deletes a VM when token is supplied' do
|
||||
create_running_vm('pool1', 'testhost', redis, 'abcdefghijklmnopqrstuvwxyz012345')
|
||||
expect fetch_vm('testhost')
|
||||
|
||||
delete "#{prefix}/vm/testhost", "", {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expect !fetch_vm('testhost')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /vm/:hostname/snapshot' do
|
||||
context '(auth not configured)' do
|
||||
it 'creates a snapshot' do
|
||||
create_vm('testhost', redis)
|
||||
post "#{prefix}/vm/testhost/snapshot"
|
||||
expect_json(ok = true, http = 202)
|
||||
expect(JSON.parse(last_response.body)['testhost']['snapshot'].length).to be(32)
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
before(:each) do
|
||||
app.settings.set :config, auth: true
|
||||
end
|
||||
|
||||
it 'returns a 401 if not authed' do
|
||||
post "#{prefix}/vm/testhost/snapshot"
|
||||
expect_json(ok = false, http = 401)
|
||||
expect !has_vm_snapshot?('testhost', redis)
|
||||
end
|
||||
|
||||
it 'creates a snapshot if authed' do
|
||||
create_vm('testhost', redis)
|
||||
snapshot_vm('testhost', 'testsnapshot', redis)
|
||||
|
||||
post "#{prefix}/vm/testhost/snapshot", "", {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
expect_json(ok = true, http = 202)
|
||||
expect(JSON.parse(last_response.body)['testhost']['snapshot'].length).to be(32)
|
||||
expect has_vm_snapshot?('testhost', redis)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /vm/:hostname/snapshot/:snapshot' do
|
||||
context '(auth not configured)' do
|
||||
it 'reverts to a snapshot' do
|
||||
create_vm('testhost', redis)
|
||||
snapshot_vm('testhost', 'testsnapshot', redis)
|
||||
|
||||
post "#{prefix}/vm/testhost/snapshot/testsnapshot"
|
||||
expect_json(ok = true, http = 202)
|
||||
expect vm_reverted_to_snapshot?('testhost', redis, 'testsnapshot')
|
||||
end
|
||||
|
||||
it 'fails if the specified snapshot does not exist' do
|
||||
create_vm('testhost', redis)
|
||||
|
||||
post "#{prefix}/vm/testhost/snapshot/testsnapshot", "", {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
expect_json(ok = false, http = 404)
|
||||
expect !vm_reverted_to_snapshot?('testhost', redis, 'testsnapshot')
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
before(:each) do
|
||||
app.settings.set :config, auth: true
|
||||
end
|
||||
|
||||
it 'returns a 401 if not authed' do
|
||||
create_vm('testhost', redis)
|
||||
snapshot_vm('testhost', 'testsnapshot', redis)
|
||||
|
||||
post "#{prefix}/vm/testhost/snapshot/testsnapshot"
|
||||
expect_json(ok = false, http = 401)
|
||||
expect !vm_reverted_to_snapshot?('testhost', redis, 'testsnapshot')
|
||||
end
|
||||
|
||||
it 'fails if authed and the specified snapshot does not exist' do
|
||||
create_vm('testhost', redis)
|
||||
|
||||
post "#{prefix}/vm/testhost/snapshot/testsnapshot", "", {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
expect_json(ok = false, http = 404)
|
||||
expect !vm_reverted_to_snapshot?('testhost', redis, 'testsnapshot')
|
||||
end
|
||||
|
||||
it 'reverts to a snapshot if authed' do
|
||||
create_vm('testhost', redis)
|
||||
snapshot_vm('testhost', 'testsnapshot', redis)
|
||||
|
||||
post "#{prefix}/vm/testhost/snapshot/testsnapshot", "", {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
expect_json(ok = true, http = 202)
|
||||
expect vm_reverted_to_snapshot?('testhost', redis, 'testsnapshot')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,435 +0,0 @@
|
|||
require 'spec_helper'
|
||||
require 'rack/test'
|
||||
|
||||
describe Vmpooler::API::V1 do
|
||||
include Rack::Test::Methods
|
||||
|
||||
def app()
|
||||
Vmpooler::API
|
||||
end
|
||||
|
||||
# Added to ensure no leakage in rack state from previous tests.
|
||||
# Removes all routes, filters, middleware and extension hooks from the current class
|
||||
# https://rubydoc.info/gems/sinatra/Sinatra/Base#reset!-class_method
|
||||
before(:each) do
|
||||
app.reset!
|
||||
end
|
||||
|
||||
describe '/vm' do
|
||||
let(:prefix) { '/api/v1' }
|
||||
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
||||
let(:config) {
|
||||
{
|
||||
config: {
|
||||
'site_name' => 'test pooler',
|
||||
'vm_lifetime_auth' => 2
|
||||
},
|
||||
pools: [
|
||||
{'name' => 'pool1', 'size' => 5},
|
||||
{'name' => 'pool2', 'size' => 10},
|
||||
{'name' => 'pool3', 'size' => 10}
|
||||
],
|
||||
statsd: { 'prefix' => 'stats_prefix'},
|
||||
alias: { 'poolone' => ['pool1'] },
|
||||
pool_names: [ 'pool1', 'pool2', 'pool3', 'poolone', 'genericpool' ]
|
||||
}
|
||||
}
|
||||
let(:current_time) { Time.now }
|
||||
let(:vmname) { 'abcdefghijkl' }
|
||||
let(:checkoutlock) { Mutex.new }
|
||||
|
||||
before(:each) do
|
||||
expect(app).to receive(:run!).once
|
||||
app.execute([:api], config, redis, metrics, nil)
|
||||
app.settings.set :config, auth: false
|
||||
app.settings.set :checkoutlock, checkoutlock
|
||||
create_token('abcdefghijklmnopqrstuvwxyz012345', 'jdoe', current_time)
|
||||
end
|
||||
|
||||
describe 'GET /vm/:hostname' do
|
||||
it 'returns correct information on a running vm' do
|
||||
create_running_vm 'pool1', vmname, redis
|
||||
expect(TCPSocket).to receive(:gethostbyname).and_raise(RuntimeError)
|
||||
get "#{prefix}/vm/#{vmname}"
|
||||
expect_json(ok = true, http = 200)
|
||||
response_body = (JSON.parse(last_response.body)[vmname])
|
||||
|
||||
expect(response_body["template"]).to eq("pool1")
|
||||
expect(response_body["lifetime"]).to eq(0)
|
||||
expect(response_body["running"]).to be >= 0
|
||||
expect(response_body["remaining"]).to be <= 0
|
||||
expect(response_body["start_time"]).to eq(current_time.to_datetime.rfc3339)
|
||||
expect(response_body["end_time"]).to eq(current_time.to_datetime.rfc3339)
|
||||
expect(response_body["state"]).to eq("running")
|
||||
expect(response_body["ip"]).to eq("")
|
||||
expect(response_body["host"]).to eq("host1")
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /vm' do
|
||||
|
||||
let(:socket) { double('socket') }
|
||||
it 'returns a single VM' do
|
||||
create_ready_vm 'pool1', vmname, redis
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm", '{"pool1":"1"}'
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: vmname
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'returns a single VM for an alias' do
|
||||
create_ready_vm 'pool1', vmname, redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm", '{"poolone":"1"}'
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
poolone: {
|
||||
hostname: vmname
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'fails on nonexistant pools' do
|
||||
post "#{prefix}/vm", '{"poolpoolpool":"1"}'
|
||||
expect_json(ok = false, http = 404)
|
||||
end
|
||||
|
||||
it 'returns 503 for empty pool when aliases are not defined' do
|
||||
app.settings.config.delete(:alias)
|
||||
app.settings.config[:pool_names] = ['pool1', 'pool2']
|
||||
|
||||
create_ready_vm 'pool1', vmname, redis
|
||||
post "#{prefix}/vm/pool1"
|
||||
post "#{prefix}/vm/pool1"
|
||||
|
||||
expected = { ok: false }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
expect_json(ok = false, http = 503)
|
||||
end
|
||||
|
||||
it 'returns 503 for empty pool referenced by alias' do
|
||||
create_ready_vm 'pool2', vmname, redis
|
||||
post "#{prefix}/vm/poolone"
|
||||
|
||||
expected = { ok: false }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
expect_json(ok = false, http = 503)
|
||||
end
|
||||
|
||||
it 'returns multiple VMs' do
|
||||
create_ready_vm 'pool1', vmname, redis
|
||||
create_ready_vm 'pool2', 'qrstuvwxyz012345', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm", '{"pool1":"1","pool2":"1"}'
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: vmname
|
||||
},
|
||||
pool2: {
|
||||
hostname: 'qrstuvwxyz012345'
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'returns multiple VMs even when multiple instances from the same pool are requested' do
|
||||
create_ready_vm 'pool1', '1abcdefghijklmnop', redis
|
||||
create_ready_vm 'pool1', '2abcdefghijklmnop', redis
|
||||
create_ready_vm 'pool2', 'qrstuvwxyz012345', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm", '{"pool1":"2","pool2":"1"}'
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: [ '1abcdefghijklmnop', '2abcdefghijklmnop' ]
|
||||
},
|
||||
pool2: {
|
||||
hostname: 'qrstuvwxyz012345'
|
||||
}
|
||||
}
|
||||
|
||||
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 eq('qrstuvwxyz012345')
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'returns multiple VMs even when multiple instances from multiple pools are requested' do
|
||||
create_ready_vm 'pool1', '1abcdefghijklmnop', redis
|
||||
create_ready_vm 'pool1', '2abcdefghijklmnop', redis
|
||||
create_ready_vm 'pool2', '1qrstuvwxyz012345', redis
|
||||
create_ready_vm 'pool2', '2qrstuvwxyz012345', redis
|
||||
create_ready_vm 'pool2', '3qrstuvwxyz012345', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm", '{"pool1":"2","pool2":"3"}'
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: [ '1abcdefghijklmnop', '2abcdefghijklmnop' ]
|
||||
},
|
||||
pool2: {
|
||||
hostname: [ '1qrstuvwxyz012345', '2qrstuvwxyz012345', '3qrstuvwxyz012345' ]
|
||||
}
|
||||
}
|
||||
|
||||
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_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'returns VMs from multiple backend pools requested by an alias' do
|
||||
Vmpooler::API.settings.config[:alias]['genericpool'] = ['pool1', 'pool2', 'pool3']
|
||||
|
||||
create_ready_vm 'pool1', '1abcdefghijklmnop', redis
|
||||
create_ready_vm 'pool2', '2abcdefghijklmnop', redis
|
||||
create_ready_vm 'pool3', '1qrstuvwxyz012345', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm", '{"genericpool":"3"}'
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
genericpool: {
|
||||
hostname: [ '1abcdefghijklmnop', '2abcdefghijklmnop', '1qrstuvwxyz012345' ]
|
||||
}
|
||||
}
|
||||
|
||||
result = JSON.parse(last_response.body)
|
||||
expect(result['ok']).to eq(true)
|
||||
expect(result['genericpool']['hostname']).to include('1abcdefghijklmnop', '2abcdefghijklmnop', '1qrstuvwxyz012345')
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'returns the first VM that was moved to the ready state when checking out a VM' do
|
||||
create_ready_vm 'pool1', '1abcdefghijklmnop', redis
|
||||
create_ready_vm 'pool1', '2abcdefghijklmnop', redis
|
||||
create_ready_vm 'pool1', '3abcdefghijklmnop', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm", '{"pool1":"1"}'
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
"pool1": {
|
||||
"hostname": "1abcdefghijklmnop"
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'fails when not all requested vms can be allocated' do
|
||||
create_ready_vm 'pool1', '1abcdefghijklmnop', redis
|
||||
|
||||
post "#{prefix}/vm", '{"pool1":"1","pool2":"1"}'
|
||||
|
||||
expected = { ok: false }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
expect_json(ok = false, http = 503)
|
||||
end
|
||||
|
||||
it 'returns any checked out vms to their pools when not all requested vms can be allocated' do
|
||||
create_ready_vm 'pool1', '1abcdefghijklmnop', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm", '{"pool1":"1","pool2":"1"}'
|
||||
|
||||
expected = { ok: false }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
expect_json(ok = false, http = 503)
|
||||
|
||||
expect(pool_has_ready_vm?('pool1', '1abcdefghijklmnop', redis)).to eq(true)
|
||||
end
|
||||
|
||||
it 'fails when not all requested vms can be allocated, when requesting multiple instances from a pool' do
|
||||
create_ready_vm 'pool1', '1abcdefghijklmnop', redis
|
||||
|
||||
post "#{prefix}/vm", '{"pool1":"2","pool2":"1"}'
|
||||
|
||||
expected = { ok: false }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
expect_json(ok = false, http = 503)
|
||||
end
|
||||
|
||||
it 'returns any checked out vms to their pools when not all requested vms can be allocated, when requesting multiple instances from a pool' do
|
||||
create_ready_vm 'pool1', '1abcdefghijklmnop', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm", '{"pool1":"2","pool2":"1"}'
|
||||
|
||||
expected = { ok: false }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
expect_json(ok = false, http = 503)
|
||||
|
||||
expect(pool_has_ready_vm?('pool1', '1abcdefghijklmnop', redis)).to eq(true)
|
||||
end
|
||||
|
||||
it 'fails when not all requested vms can be allocated, when requesting multiple instances from multiple pools' do
|
||||
create_ready_vm 'pool1', '1abcdefghijklmnop', redis
|
||||
|
||||
post "#{prefix}/vm", '{"pool1":"2","pool2":"3"}'
|
||||
|
||||
expected = { ok: false }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
expect_json(ok = false, http = 503)
|
||||
end
|
||||
|
||||
it 'returns any checked out vms to their pools when not all requested vms can be allocated, when requesting multiple instances from multiple pools' do
|
||||
create_ready_vm 'pool1', '1abcdefghijklmnop', redis
|
||||
create_ready_vm 'pool1', '2abcdefghijklmnop', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm", '{"pool1":"2","pool2":"3"}'
|
||||
|
||||
expected = { ok: false }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
expect_json(ok = false, http = 503)
|
||||
|
||||
expect(pool_has_ready_vm?('pool1', '1abcdefghijklmnop', redis)).to eq(true)
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: "2#{vmname}"
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
|
||||
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
|
||||
app.settings.set :config, auth: false
|
||||
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm", '{"pool1":"1"}', {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: 'abcdefghijklmnop'
|
||||
}
|
||||
}
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
|
||||
vm = fetch_vm('abcdefghijklmnop')
|
||||
expect(vm['lifetime']).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
it 'extends VM lifetime if auth token is provided' do
|
||||
app.settings.set :config, auth: true
|
||||
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm", '{"pool1":"1"}', {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: 'abcdefghijklmnop'
|
||||
}
|
||||
}
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
|
||||
vm = fetch_vm('abcdefghijklmnop')
|
||||
expect(vm['lifetime'].to_i).to eq(2)
|
||||
end
|
||||
|
||||
it 'does not extend VM lifetime if auth token is not provided' do
|
||||
app.settings.set :config, auth: true
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm", '{"pool1":"1"}'
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: 'abcdefghijklmnop'
|
||||
}
|
||||
}
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
|
||||
vm = fetch_vm('abcdefghijklmnop')
|
||||
expect(vm['lifetime']).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,327 +0,0 @@
|
|||
require 'spec_helper'
|
||||
require 'rack/test'
|
||||
|
||||
describe Vmpooler::API::V1 do
|
||||
include Rack::Test::Methods
|
||||
|
||||
def app()
|
||||
Vmpooler::API
|
||||
end
|
||||
|
||||
# Added to ensure no leakage in rack state from previous tests.
|
||||
# Removes all routes, filters, middleware and extension hooks from the current class
|
||||
# https://rubydoc.info/gems/sinatra/Sinatra/Base#reset!-class_method
|
||||
before(:each) do
|
||||
app.reset!
|
||||
end
|
||||
|
||||
describe '/vm/:template' do
|
||||
let(:prefix) { '/api/v1' }
|
||||
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
||||
let(:config) {
|
||||
{
|
||||
config: {
|
||||
'site_name' => 'test pooler',
|
||||
'vm_lifetime_auth' => 2,
|
||||
},
|
||||
providers: { vsphere: {} },
|
||||
pools: [
|
||||
{'name' => 'pool1', 'size' => 5},
|
||||
{'name' => 'pool2', 'size' => 10},
|
||||
{'name' => 'poolone', 'size' => 0}
|
||||
],
|
||||
statsd: { 'prefix' => 'stats_prefix'},
|
||||
alias: { 'poolone' => 'pool1' },
|
||||
pool_names: [ 'pool1', 'pool2', 'poolone' ]
|
||||
}
|
||||
}
|
||||
|
||||
let(:current_time) { Time.now }
|
||||
let(:socket) { double('socket') }
|
||||
let(:checkoutlock) { Mutex.new }
|
||||
|
||||
before(:each) do
|
||||
expect(app).to receive(:run!).once
|
||||
app.execute([:api], config, redis, metrics, nil)
|
||||
app.settings.set :config, auth: false
|
||||
app.settings.set :checkoutlock, checkoutlock
|
||||
create_token('abcdefghijklmnopqrstuvwxyz012345', 'jdoe', current_time)
|
||||
end
|
||||
|
||||
describe 'POST /vm/:template' do
|
||||
it 'returns a single VM' do
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm/pool1", ''
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: 'abcdefghijklmnop'
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'returns a single VM for an alias' do
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm/poolone", ''
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
poolone: {
|
||||
hostname: 'abcdefghijklmnop'
|
||||
}
|
||||
}
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'fails on nonexistant pools' do
|
||||
post "#{prefix}/vm/poolpoolpool", ''
|
||||
expect_json(ok = false, http = 404)
|
||||
end
|
||||
|
||||
it 'returns 503 for empty pool when aliases are not defined' do
|
||||
app.settings.config.delete(:alias)
|
||||
app.settings.config[:pool_names] = ['pool1', 'pool2']
|
||||
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop', redis
|
||||
post "#{prefix}/vm/pool1"
|
||||
post "#{prefix}/vm/pool1"
|
||||
|
||||
expected = { ok: false }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
expect_json(ok = false, http = 503)
|
||||
end
|
||||
|
||||
it 'returns 503 for empty pool referenced by alias' do
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop', redis
|
||||
post "#{prefix}/vm/poolone"
|
||||
|
||||
expected = { ok: false }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
expect_json(ok = false, http = 503)
|
||||
end
|
||||
|
||||
it 'returns multiple VMs' do
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop', redis
|
||||
create_ready_vm 'pool2', 'qrstuvwxyz012345', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm/pool1+pool2", ''
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: 'abcdefghijklmnop'
|
||||
},
|
||||
pool2: {
|
||||
hostname: 'qrstuvwxyz012345'
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
end
|
||||
|
||||
it 'returns multiple VMs even when multiple instances from multiple pools are requested' do
|
||||
create_ready_vm 'pool1', '1abcdefghijklmnop', redis
|
||||
create_ready_vm 'pool1', '2abcdefghijklmnop', redis
|
||||
|
||||
create_ready_vm 'pool2', '1qrstuvwxyz012345', redis
|
||||
create_ready_vm 'pool2', '2qrstuvwxyz012345', redis
|
||||
create_ready_vm 'pool2', '3qrstuvwxyz012345', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm/pool1+pool1+pool2+pool2+pool2", ''
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: [ '1abcdefghijklmnop', '2abcdefghijklmnop' ]
|
||||
},
|
||||
pool2: {
|
||||
hostname: [ '1qrstuvwxyz012345', '2qrstuvwxyz012345', '3qrstuvwxyz012345' ]
|
||||
}
|
||||
}
|
||||
|
||||
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_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'fails when not all requested vms can be allocated' do
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop', redis
|
||||
|
||||
post "#{prefix}/vm/pool1+pool2", ''
|
||||
|
||||
expected = { ok: false }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
expect_json(ok = false, http = 503)
|
||||
end
|
||||
|
||||
it 'returns any checked out vms to their pools when not all requested vms can be allocated' do
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm/pool1+pool2", ''
|
||||
|
||||
expected = { ok: false }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
expect_json(ok = false, http = 503)
|
||||
|
||||
expect(pool_has_ready_vm?('pool1', 'abcdefghijklmnop', redis)).to eq(true)
|
||||
end
|
||||
|
||||
it 'fails when not all requested vms can be allocated, when requesting multiple instances from a pool' do
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop', redis
|
||||
create_ready_vm 'pool1', '0123456789012345', redis
|
||||
|
||||
post "#{prefix}/vm/pool1+pool1+pool2", ''
|
||||
|
||||
expected = { ok: false }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
expect_json(ok = false, http = 503)
|
||||
end
|
||||
|
||||
it 'returns any checked out vms to their pools when not all requested vms can be allocated, when requesting multiple instances from a pool' do
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop', redis
|
||||
create_ready_vm 'pool1', '0123456789012345', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm/pool1+pool1+pool2", ''
|
||||
|
||||
expected = { ok: false }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
expect_json(ok = false, http = 503)
|
||||
|
||||
expect(pool_has_ready_vm?('pool1', 'abcdefghijklmnop', redis)).to eq(true)
|
||||
expect(pool_has_ready_vm?('pool1', '0123456789012345', redis)).to eq(true)
|
||||
end
|
||||
|
||||
it 'fails when not all requested vms can be allocated, when requesting multiple instances from multiple pools' do
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop', redis
|
||||
create_ready_vm 'pool2', '0123456789012345', redis
|
||||
|
||||
post "#{prefix}/vm/pool1+pool1+pool2+pool2+pool2", ''
|
||||
|
||||
expected = { ok: false }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
expect_json(ok = false, http = 503)
|
||||
end
|
||||
|
||||
it 'returns any checked out vms to their pools when not all requested vms can be allocated, when requesting multiple instances from multiple pools' do
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop', redis
|
||||
create_ready_vm 'pool2', '0123456789012345', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm/pool1+pool1+pool2+pool2+pool2", ''
|
||||
|
||||
expected = { ok: false }
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
expect_json(ok = false, http = 503)
|
||||
|
||||
expect(pool_has_ready_vm?('pool1', 'abcdefghijklmnop', redis)).to eq(true)
|
||||
expect(pool_has_ready_vm?('pool2', '0123456789012345', redis)).to eq(true)
|
||||
end
|
||||
|
||||
context '(auth not configured)' do
|
||||
it 'does not extend VM lifetime if auth token is provided' do
|
||||
app.settings.set :config, auth: false
|
||||
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm/pool1", '', {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: 'abcdefghijklmnop'
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
vm = fetch_vm('abcdefghijklmnop')
|
||||
expect(vm['lifetime']).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
it 'extends VM lifetime if auth token is provided' do
|
||||
app.settings.set :config, auth: true
|
||||
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm/pool1", '', {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: 'abcdefghijklmnop'
|
||||
}
|
||||
}
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
|
||||
vm = fetch_vm('abcdefghijklmnop')
|
||||
expect(vm['lifetime'].to_i).to eq(2)
|
||||
end
|
||||
|
||||
it 'does not extend VM lifetime if auth token is not provided' do
|
||||
app.settings.set :config, auth: true
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop', redis
|
||||
|
||||
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).and_return(socket)
|
||||
|
||||
post "#{prefix}/vm/pool1", ''
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: 'abcdefghijklmnop'
|
||||
}
|
||||
}
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
|
||||
vm = fetch_vm('abcdefghijklmnop')
|
||||
expect(vm['lifetime']).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -341,6 +341,28 @@ 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
|
||||
|
||||
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)
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: "2#{vmname}"
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
|
||||
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
|
||||
app.settings.set :config, auth: false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue