mirror of
https://github.com/puppetlabs/vmpooler.git
synced 2026-01-27 02:18:41 -05:00
Merge remote-tracking branch 'origin/master' into merge-master-into-ci.next
* origin/master: Added IP lookup functionality for /vm/hostname (#154) Add info about vmfloaty Improved tests for vmpooler (#152)
This commit is contained in:
commit
28e84e542d
12 changed files with 952 additions and 1000 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.ruby-version
|
||||
Gemfile.lock
|
||||
vendor
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
cache: bundler
|
||||
sudo: false
|
||||
language: ruby
|
||||
services:
|
||||
- redis-server
|
||||
rvm:
|
||||
- 1.9.3
|
||||
- 2.1.1
|
||||
|
|
|
|||
|
|
@ -80,7 +80,8 @@ A dashboard is provided to offer real-time statistics and historical graphs. It
|
|||
|
||||
## Command-line Utility
|
||||
|
||||
The [vmpooler_client.py](https://github.com/puppetlabs/vmpooler-client) CLI utility provides easy access to the vmpooler service. The tool is cross-platform and written in Python.
|
||||
- The [vmpooler_client.py](https://github.com/puppetlabs/vmpooler-client) CLI utility provides easy access to the vmpooler service. The tool is cross-platform and written in Python.
|
||||
- [vmfloaty](https://github.com/briancain/vmfloaty) is a ruby based CLI tool and scripting library written in ruby.
|
||||
|
||||
## Build status
|
||||
|
||||
|
|
|
|||
|
|
@ -457,6 +457,15 @@ module Vmpooler
|
|||
result[params[:hostname]]['disk'] = rdata['disk'].split(':')
|
||||
end
|
||||
|
||||
# Look up IP address of the hostname
|
||||
begin
|
||||
ipAddress = TCPSocket.gethostbyname(params[:hostname])[3]
|
||||
rescue
|
||||
ipAddress = ""
|
||||
end
|
||||
|
||||
result[params[:hostname]]['ip'] = ipAddress
|
||||
|
||||
if config['domain']
|
||||
result[params[:hostname]]['domain'] = config['domain']
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
def expect_json(
|
||||
ok = true,
|
||||
http = 200
|
||||
)
|
||||
def redis
|
||||
unless @redis
|
||||
@redis = Redis.new
|
||||
@redis.select(15) # let's use the highest numbered database available in a default install
|
||||
end
|
||||
@redis
|
||||
end
|
||||
|
||||
def expect_json(ok = true, http = 200)
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
|
||||
if (ok == true) then
|
||||
|
|
@ -12,3 +17,59 @@ def expect_json(
|
|||
|
||||
expect(last_response.status).to eq(http)
|
||||
end
|
||||
|
||||
def create_token(token, user, timestamp)
|
||||
redis.hset("vmpooler__token__#{token}", 'user', user)
|
||||
redis.hset("vmpooler__token__#{token}", 'created', timestamp)
|
||||
end
|
||||
|
||||
def get_token_data(token)
|
||||
redis.hgetall("vmpooler__token__#{token}")
|
||||
end
|
||||
|
||||
def token_exists?(token)
|
||||
result = get_token_data
|
||||
result && !result.empty?
|
||||
end
|
||||
|
||||
def create_ready_vm(template, name, token = nil)
|
||||
create_vm(name, token)
|
||||
redis.sadd("vmpooler__ready__#{template}", name)
|
||||
redis.hset("vmpooler_vm_#{name}", "template", template)
|
||||
end
|
||||
|
||||
def create_running_vm(template, name, token = nil)
|
||||
create_vm(name, token)
|
||||
redis.sadd("vmpooler__running__#{template}", name)
|
||||
redis.hset("vmpooler__vm__#{name}", "template", template)
|
||||
end
|
||||
|
||||
def create_vm(name, token = nil)
|
||||
redis.hset("vmpooler__vm__#{name}", 'checkout', Time.now)
|
||||
if token
|
||||
redis.hset("vmpooler__vm__#{name}", 'token:token', token)
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_vm(vm)
|
||||
redis.hgetall("vmpooler__vm__#{vm}")
|
||||
end
|
||||
|
||||
def snapshot_vm(vm, snapshot = '12345678901234567890123456789012')
|
||||
redis.sadd('vmpooler__tasks__snapshot', "#{vm}:#{snapshot}")
|
||||
redis.hset("vmpooler__vm__#{vm}", "snapshot:#{snapshot}", "1")
|
||||
end
|
||||
|
||||
def has_vm_snapshot?(vm)
|
||||
redis.smembers('vmpooler__tasks__snapshot').any? do |snapshot|
|
||||
instance, sha = snapshot.split(':')
|
||||
vm == instance
|
||||
end
|
||||
end
|
||||
|
||||
def vm_reverted_to_snapshot?(vm, snapshot = nil)
|
||||
redis.smembers('vmpooler__tasks__snapshot-revert').any? do |action|
|
||||
instance, sha = action.split(':')
|
||||
instance == vm and (snapshot ? (sha == snapshot) : true)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@ require 'helpers'
|
|||
require 'rbvmomi'
|
||||
require 'rspec'
|
||||
require 'vmpooler'
|
||||
require 'redis'
|
||||
|
|
|
|||
173
spec/vmpooler/api/v1/token_spec.rb
Normal file
173
spec/vmpooler/api/v1/token_spec.rb
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
require 'spec_helper'
|
||||
require 'rack/test'
|
||||
|
||||
module Vmpooler
|
||||
class API
|
||||
module Helpers
|
||||
def authenticate(auth, username_str, password_str)
|
||||
username_str == 'admin' and password_str == 's3cr3t'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe Vmpooler::API::V1 do
|
||||
include Rack::Test::Methods
|
||||
|
||||
def app()
|
||||
Vmpooler::API
|
||||
end
|
||||
|
||||
describe '/token' do
|
||||
let(:prefix) { '/api/v1' }
|
||||
let(:current_time) { Time.now }
|
||||
let(:config) { { } }
|
||||
|
||||
before do
|
||||
app.settings.set :config, config
|
||||
app.settings.set :redis, redis
|
||||
end
|
||||
|
||||
describe 'GET /token' do
|
||||
context '(auth not configured)' do
|
||||
let(: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) { { auth: true } }
|
||||
|
||||
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) { { auth: false } }
|
||||
|
||||
it 'returns a 404' do
|
||||
post "#{prefix}/token"
|
||||
expect_json(ok = false, http = 404)
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
let(:config) { { auth: true } }
|
||||
|
||||
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 do
|
||||
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) { { 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) { {
|
||||
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) { { 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) { { auth: true } }
|
||||
|
||||
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
|
||||
317
spec/vmpooler/api/v1/vm_hostname_spec.rb
Normal file
317
spec/vmpooler/api/v1/vm_hostname_spec.rb
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
require 'spec_helper'
|
||||
require 'rack/test'
|
||||
|
||||
module Vmpooler
|
||||
class API
|
||||
module Helpers
|
||||
def authenticate(auth, username_str, password_str)
|
||||
username_str == 'admin' and password_str == 's3cr3t'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
describe '/vm/:hostname' do
|
||||
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' },
|
||||
}
|
||||
}
|
||||
|
||||
let(:current_time) { Time.now }
|
||||
|
||||
before(:each) do
|
||||
redis.flushdb
|
||||
|
||||
app.settings.set :config, config
|
||||
app.settings.set :redis, redis
|
||||
app.settings.set :config, auth: false
|
||||
create_token('abcdefghijklmnopqrstuvwxyz012345', 'jdoe', current_time)
|
||||
end
|
||||
|
||||
describe 'PUT /vm/:hostname' do
|
||||
it 'allows tags to be set' do
|
||||
create_vm('testhost')
|
||||
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')
|
||||
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')
|
||||
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')
|
||||
|
||||
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
|
||||
let(:config) { {
|
||||
tagfilter: { 'url' => '(.*)\/' },
|
||||
} }
|
||||
|
||||
it 'correctly filters tags' do
|
||||
create_vm('testhost')
|
||||
|
||||
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')
|
||||
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
|
||||
let(:config) { { auth: false } }
|
||||
|
||||
it 'allows VM lifetime to be modified without a token' do
|
||||
create_vm('testhost')
|
||||
|
||||
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')
|
||||
|
||||
put "#{prefix}/vm/testhost", '{"lifetime":"0"}'
|
||||
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')
|
||||
|
||||
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')
|
||||
|
||||
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')
|
||||
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')
|
||||
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', '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', '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')
|
||||
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')
|
||||
end
|
||||
|
||||
it 'creates a snapshot if authed' do
|
||||
create_vm('testhost')
|
||||
snapshot_vm('testhost', 'testsnapshot')
|
||||
|
||||
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')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /vm/:hostname/snapshot/:snapshot' do
|
||||
context '(auth not configured)' do
|
||||
it 'reverts to a snapshot' do
|
||||
create_vm('testhost')
|
||||
snapshot_vm('testhost', 'testsnapshot')
|
||||
|
||||
post "#{prefix}/vm/testhost/snapshot/testsnapshot"
|
||||
expect_json(ok = true, http = 202)
|
||||
expect vm_reverted_to_snapshot?('testhost', 'testsnapshot')
|
||||
end
|
||||
|
||||
it 'fails if the specified snapshot does not exist' do
|
||||
create_vm('testhost')
|
||||
|
||||
post "#{prefix}/vm/testhost/snapshot/testsnapshot", "", {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
expect_json(ok = false, http = 404)
|
||||
expect !vm_reverted_to_snapshot?('testhost', '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')
|
||||
snapshot_vm('testhost', 'testsnapshot')
|
||||
|
||||
post "#{prefix}/vm/testhost/snapshot/testsnapshot"
|
||||
expect_json(ok = false, http = 401)
|
||||
expect !vm_reverted_to_snapshot?('testhost', 'testsnapshot')
|
||||
end
|
||||
|
||||
it 'fails if authed and the specified snapshot does not exist' do
|
||||
create_vm('testhost')
|
||||
|
||||
post "#{prefix}/vm/testhost/snapshot/testsnapshot", "", {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
expect_json(ok = false, http = 404)
|
||||
expect !vm_reverted_to_snapshot?('testhost', 'testsnapshot')
|
||||
end
|
||||
|
||||
it 'reverts to a snapshot if authed' do
|
||||
create_vm('testhost')
|
||||
snapshot_vm('testhost', 'testsnapshot')
|
||||
|
||||
post "#{prefix}/vm/testhost/snapshot/testsnapshot", "", {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
expect_json(ok = true, http = 202)
|
||||
expect vm_reverted_to_snapshot?('testhost', 'testsnapshot')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
175
spec/vmpooler/api/v1/vm_spec.rb
Normal file
175
spec/vmpooler/api/v1/vm_spec.rb
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
require 'spec_helper'
|
||||
require 'rack/test'
|
||||
|
||||
module Vmpooler
|
||||
class API
|
||||
module Helpers
|
||||
def authenticate(auth, username_str, password_str)
|
||||
username_str == 'admin' and password_str == 's3cr3t'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe Vmpooler::API::V1 do
|
||||
include Rack::Test::Methods
|
||||
|
||||
def app()
|
||||
Vmpooler::API
|
||||
end
|
||||
|
||||
describe '/vm' do
|
||||
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' },
|
||||
}
|
||||
}
|
||||
|
||||
let(:current_time) { Time.now }
|
||||
|
||||
before(:each) do
|
||||
redis.flushdb
|
||||
|
||||
app.settings.set :config, config
|
||||
app.settings.set :redis, redis
|
||||
app.settings.set :config, auth: false
|
||||
create_token('abcdefghijklmnopqrstuvwxyz012345', 'jdoe', current_time)
|
||||
end
|
||||
|
||||
describe 'POST /vm' do
|
||||
it 'returns a single VM' do
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop'
|
||||
|
||||
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))
|
||||
end
|
||||
|
||||
it 'returns a single VM for an alias' do
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop'
|
||||
|
||||
post "#{prefix}/vm", '{"poolone":"1"}'
|
||||
expect_json(ok = true, http = 200)
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: 'abcdefghijklmnop'
|
||||
}
|
||||
}
|
||||
|
||||
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 multiple VMs' do
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop'
|
||||
create_ready_vm 'pool2', 'qrstuvwxyz012345'
|
||||
|
||||
post "#{prefix}/vm", '{"pool1":"1","pool2":"1"}'
|
||||
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
|
||||
|
||||
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'
|
||||
|
||||
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'
|
||||
|
||||
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'
|
||||
|
||||
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
|
||||
176
spec/vmpooler/api/v1/vm_template_spec.rb
Normal file
176
spec/vmpooler/api/v1/vm_template_spec.rb
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
require 'spec_helper'
|
||||
require 'rack/test'
|
||||
|
||||
module Vmpooler
|
||||
class API
|
||||
module Helpers
|
||||
def authenticate(auth, username_str, password_str)
|
||||
username_str == 'admin' and password_str == 's3cr3t'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe Vmpooler::API::V1 do
|
||||
include Rack::Test::Methods
|
||||
|
||||
def app()
|
||||
Vmpooler::API
|
||||
end
|
||||
|
||||
describe '/vm/:template' do
|
||||
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' },
|
||||
}
|
||||
}
|
||||
|
||||
let(:current_time) { Time.now }
|
||||
|
||||
before(:each) do
|
||||
redis.flushdb
|
||||
|
||||
app.settings.set :config, config
|
||||
app.settings.set :redis, redis
|
||||
app.settings.set :config, auth: false
|
||||
create_token('abcdefghijklmnopqrstuvwxyz012345', 'jdoe', current_time)
|
||||
end
|
||||
|
||||
describe 'POST /vm/:template' do
|
||||
it 'returns a single VM' do
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop'
|
||||
|
||||
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'
|
||||
|
||||
post "#{prefix}/vm/poolone", ''
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
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 multiple VMs' do
|
||||
create_ready_vm 'pool1', 'abcdefghijklmnop'
|
||||
create_ready_vm 'pool2', 'qrstuvwxyz012345'
|
||||
|
||||
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
|
||||
|
||||
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'
|
||||
|
||||
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'
|
||||
|
||||
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'
|
||||
|
||||
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
|
||||
|
|
@ -1,963 +0,0 @@
|
|||
require 'spec_helper'
|
||||
require 'rack/test'
|
||||
|
||||
module Vmpooler
|
||||
class API
|
||||
module Helpers
|
||||
def authenticate(auth, username_str, password_str)
|
||||
username_str == 'admin' and password_str == 's3cr3t'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe Vmpooler::API::V1 do
|
||||
include Rack::Test::Methods
|
||||
|
||||
def app()
|
||||
Vmpooler::API
|
||||
end
|
||||
|
||||
describe '/token' do
|
||||
let(:redis) { double('redis') }
|
||||
let(:prefix) { '/api/v1' }
|
||||
|
||||
before do
|
||||
app.settings.set :config, config
|
||||
app.settings.set :redis, redis
|
||||
end
|
||||
|
||||
describe 'GET /token' do
|
||||
context '(auth not configured)' do
|
||||
let(: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) { { auth: true } }
|
||||
|
||||
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
|
||||
expect(redis).to receive(:keys).with('vmpooler__token__*').and_return(["vmpooler__token__abc"])
|
||||
expect(redis).to receive(:hgetall).with('vmpooler__token__abc').and_return({"user" => "admin", "created" => "now"})
|
||||
|
||||
authorize 'admin', 's3cr3t'
|
||||
|
||||
get "#{prefix}/token"
|
||||
|
||||
expect(JSON.parse(last_response.body)['abc']['created']).to eq('now')
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /token' do
|
||||
context '(auth not configured)' do
|
||||
let(:config) { { auth: false } }
|
||||
|
||||
it 'returns a 404' do
|
||||
post "#{prefix}/token"
|
||||
|
||||
expect_json(ok = false, http = 404)
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
before do
|
||||
allow(redis).to receive(:hset).and_return '1'
|
||||
end
|
||||
|
||||
let(:config) { { auth: true } }
|
||||
|
||||
it 'returns a 401 if not authed' do
|
||||
post "#{prefix}/token"
|
||||
|
||||
expect_json(ok = false, http = 401)
|
||||
end
|
||||
|
||||
it 'returns a token if authed' do
|
||||
authorize 'admin', 's3cr3t'
|
||||
|
||||
post "#{prefix}/token"
|
||||
|
||||
expect(JSON.parse(last_response.body)['token'].length).to be(32)
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '/token/:token' do
|
||||
let(:redis) { double('redis') }
|
||||
let(:prefix) { '/api/v1' }
|
||||
|
||||
before do
|
||||
app.settings.set :config, config
|
||||
app.settings.set :redis, redis
|
||||
end
|
||||
|
||||
describe 'GET /token/:token' do
|
||||
context '(auth not configured)' do
|
||||
let(: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) { {
|
||||
auth: true,
|
||||
pools: [
|
||||
{'name' => 'pool1', 'size' => 5}
|
||||
]
|
||||
} }
|
||||
|
||||
it 'returns a token' do
|
||||
expect(redis).to receive(:hgetall).with('vmpooler__token__this').and_return({'user' => 'admin'})
|
||||
expect(redis).to receive(:smembers).with('vmpooler__running__pool1').and_return(['vmhostname'])
|
||||
expect(redis).to receive(:hget).with('vmpooler__vm__vmhostname', 'token:token').and_return('this')
|
||||
|
||||
get "#{prefix}/token/this"
|
||||
|
||||
expect(JSON.parse(last_response.body)['ok']).to eq(true)
|
||||
expect(JSON.parse(last_response.body)['this']['user']).to eq('admin')
|
||||
expect(JSON.parse(last_response.body)['this']['vms']['running']).to include('vmhostname')
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /token/:token' do
|
||||
context '(auth not configured)' do
|
||||
let(:config) { { auth: false } }
|
||||
|
||||
it 'returns a 404' do
|
||||
delete "#{prefix}/token/this"
|
||||
|
||||
expect_json(ok = false, http = 404)
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
before do
|
||||
allow(redis).to receive(:del).and_return '1'
|
||||
end
|
||||
|
||||
let(:config) { { auth: true } }
|
||||
|
||||
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
|
||||
authorize 'admin', 's3cr3t'
|
||||
|
||||
delete "#{prefix}/token/this"
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '/vm' 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' do
|
||||
it 'returns a single VM' do
|
||||
post "#{prefix}/vm", '{"pool1":"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 '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 nonexistent 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"}'
|
||||
|
||||
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
|
||||
|
||||
it 'returns multiple VMs even when multiple instances from the same pool are requested' do
|
||||
post "#{prefix}/vm", '{"pool1":"2","pool2":"1"}'
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: [ 'abcdefghijklmnop', 'abcdefghijklmnop' ]
|
||||
},
|
||||
pool2: {
|
||||
hostname: 'qrstuvwxyz012345'
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'returns multiple VMs even when multiple instances from multiple pools are requested' do
|
||||
post "#{prefix}/vm", '{"pool1":"2","pool2":"3"}'
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: [ 'abcdefghijklmnop', 'abcdefghijklmnop' ]
|
||||
},
|
||||
pool2: {
|
||||
hostname: [ 'qrstuvwxyz012345', 'qrstuvwxyz012345', 'qrstuvwxyz012345' ]
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool1').and_return 'abcdefghijklmnop'
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool2').and_return nil
|
||||
allow(redis).to receive(:sadd).with("vmpooler__ready__pool1", "abcdefghijklmnop")
|
||||
|
||||
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
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool1').and_return 'abcdefghijklmnop'
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool2').and_return nil
|
||||
expect(redis).to receive(:sadd).with("vmpooler__ready__pool1", "abcdefghijklmnop")
|
||||
|
||||
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 'fails when not all requested vms can be allocated, when requesting multiple instances from a pool' do
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool1').and_return 'abcdefghijklmnop'
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool2').and_return nil
|
||||
allow(redis).to receive(:sadd).with("vmpooler__ready__pool1", "abcdefghijklmnop")
|
||||
|
||||
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
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool1').and_return 'abcdefghijklmnop'
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool2').and_return nil
|
||||
expect(redis).to receive(:sadd).with("vmpooler__ready__pool1", "abcdefghijklmnop").exactly(2).times
|
||||
|
||||
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 'fails when not all requested vms can be allocated, when requesting multiple instances from multiple pools' do
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool1').and_return 'abcdefghijklmnop'
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool2').and_return nil
|
||||
allow(redis).to receive(:sadd).with("vmpooler__ready__pool1", "abcdefghijklmnop")
|
||||
|
||||
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
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool1').and_return 'abcdefghijklmnop'
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool2').and_return nil
|
||||
expect(redis).to receive(:sadd).with("vmpooler__ready__pool1", "abcdefghijklmnop").exactly(2).times
|
||||
|
||||
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
|
||||
|
||||
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":"1"}', {
|
||||
'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":"1"}', {
|
||||
'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":"1"}'
|
||||
|
||||
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/: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 nonexistent 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
|
||||
|
||||
it 'returns multiple VMs even when multiple instances from the same pool are requested' do
|
||||
post "#{prefix}/vm/pool1+pool1+pool2", ''
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: [ 'abcdefghijklmnop', 'abcdefghijklmnop' ]
|
||||
},
|
||||
pool2: {
|
||||
hostname: 'qrstuvwxyz012345'
|
||||
}
|
||||
}
|
||||
|
||||
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'returns multiple VMs even when multiple instances from multiple pools are requested' do
|
||||
post "#{prefix}/vm/pool1+pool1+pool2+pool2+pool2", ''
|
||||
|
||||
expected = {
|
||||
ok: true,
|
||||
pool1: {
|
||||
hostname: [ 'abcdefghijklmnop', 'abcdefghijklmnop' ]
|
||||
},
|
||||
pool2: {
|
||||
hostname: [ 'qrstuvwxyz012345', 'qrstuvwxyz012345', 'qrstuvwxyz012345' ]
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool1').and_return 'abcdefghijklmnop'
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool2').and_return nil
|
||||
allow(redis).to receive(:sadd).with("vmpooler__ready__pool1", "abcdefghijklmnop")
|
||||
|
||||
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
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool1').and_return 'abcdefghijklmnop'
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool2').and_return nil
|
||||
expect(redis).to receive(:sadd).with("vmpooler__ready__pool1", "abcdefghijklmnop")
|
||||
|
||||
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 'fails when not all requested vms can be allocated, when requesting multiple instances from a pool' do
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool1').and_return 'abcdefghijklmnop'
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool2').and_return nil
|
||||
allow(redis).to receive(:sadd).with("vmpooler__ready__pool1", "abcdefghijklmnop")
|
||||
|
||||
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
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool1').and_return 'abcdefghijklmnop'
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool2').and_return nil
|
||||
expect(redis).to receive(:sadd).with("vmpooler__ready__pool1", "abcdefghijklmnop").exactly(2).times
|
||||
|
||||
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 'fails when not all requested vms can be allocated, when requesting multiple instances from multiple pools' do
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool1').and_return 'abcdefghijklmnop'
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool2').and_return nil
|
||||
allow(redis).to receive(:sadd).with("vmpooler__ready__pool1", "abcdefghijklmnop")
|
||||
|
||||
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
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool1').and_return 'abcdefghijklmnop'
|
||||
allow(redis).to receive(:spop).with('vmpooler__ready__pool2').and_return nil
|
||||
expect(redis).to receive(:sadd).with("vmpooler__ready__pool1", "abcdefghijklmnop").exactly(2).times
|
||||
|
||||
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
|
||||
|
||||
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' }
|
||||
let(:config) { {
|
||||
pools: [
|
||||
{'name' => 'pool1', 'size' => 5},
|
||||
{'name' => 'pool2', 'size' => 10}
|
||||
]
|
||||
} }
|
||||
|
||||
before do
|
||||
app.settings.set :config, config
|
||||
app.settings.set :redis, redis
|
||||
|
||||
allow(redis).to receive(:exists).and_return '1'
|
||||
allow(redis).to receive(:hset).and_return '1'
|
||||
end
|
||||
|
||||
describe 'PUT /vm/:hostname' do
|
||||
it 'allows tags to be set' do
|
||||
put "#{prefix}/vm/testhost", '{"tags":{"tested_by":"rspec"}}'
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'skips empty tags' do
|
||||
put "#{prefix}/vm/testhost", '{"tags":{"tested_by":""}}'
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'does not set tags if request body format is invalid' do
|
||||
put "#{prefix}/vm/testhost", '{"tags":{"tested"}}'
|
||||
|
||||
expect_json(ok = false, http = 400)
|
||||
end
|
||||
|
||||
context '(allowed_tags configured)' do
|
||||
let(:config) { {
|
||||
config: {
|
||||
'allowed_tags' => ['created_by', 'project', 'url']
|
||||
}
|
||||
} }
|
||||
|
||||
it 'fails if specified tag is not in allowed_tags array' do
|
||||
put "#{prefix}/vm/testhost", '{"tags":{"created_by":"rspec","tested_by":"rspec"}}'
|
||||
|
||||
expect_json(ok = false, http = 400)
|
||||
end
|
||||
end
|
||||
|
||||
context '(tagfilter configured)' do
|
||||
let(:config) { {
|
||||
tagfilter: { 'url' => '(.*)\/' },
|
||||
} }
|
||||
|
||||
it 'correctly filters tags' do
|
||||
expect(redis).to receive(:hset).with("vmpooler__vm__testhost", "tag:url", "foo.com")
|
||||
|
||||
put "#{prefix}/vm/testhost", '{"tags":{"url":"foo.com/something.html"}}'
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'doesn\'t eat tags not matching filter' do
|
||||
expect(redis).to receive(:hset).with("vmpooler__vm__testhost", "tag:url", "foo.com")
|
||||
|
||||
put "#{prefix}/vm/testhost", '{"tags":{"url":"foo.com"}}'
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth not configured)' do
|
||||
let(:config) { { auth: false } }
|
||||
|
||||
it 'allows VM lifetime to be modified without a token' do
|
||||
put "#{prefix}/vm/testhost", '{"lifetime":"1"}'
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'does not allow a lifetime to be 0' do
|
||||
put "#{prefix}/vm/testhost", '{"lifetime":"0"}'
|
||||
|
||||
expect_json(ok = false, http = 400)
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
let(:config) { { auth: true } }
|
||||
|
||||
it 'allows VM lifetime to be modified with a token' do
|
||||
put "#{prefix}/vm/testhost", '{"lifetime":"1"}', {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
|
||||
it 'does not allows VM lifetime to be modified without a token' do
|
||||
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
|
||||
let(:config) { { auth: false } }
|
||||
|
||||
it 'does not delete a non-existant VM' do
|
||||
expect(redis).to receive(:hgetall).and_return({})
|
||||
expect(redis).not_to receive(:sadd)
|
||||
expect(redis).not_to receive(:srem)
|
||||
|
||||
delete "#{prefix}/vm/testhost"
|
||||
|
||||
expect_json(ok = false, http = 404)
|
||||
end
|
||||
|
||||
it 'deletes an existing VM' do
|
||||
expect(redis).to receive(:hgetall).with('vmpooler__vm__testhost').and_return({"template" => "pool1"})
|
||||
expect(redis).to receive(:srem).and_return(true)
|
||||
expect(redis).to receive(:sadd)
|
||||
|
||||
delete "#{prefix}/vm/testhost"
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
let(:config) { { auth: true } }
|
||||
|
||||
context '(checked-out without token)' do
|
||||
it 'deletes a VM without supplying a token' do
|
||||
expect(redis).to receive(:hgetall).with('vmpooler__vm__testhost').and_return({"template" => "pool1"})
|
||||
expect(redis).to receive(:srem).and_return(true)
|
||||
expect(redis).to receive(:sadd)
|
||||
|
||||
delete "#{prefix}/vm/testhost"
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
end
|
||||
|
||||
context '(checked-out with token)' do
|
||||
it 'fails to delete a VM without supplying a token' do
|
||||
expect(redis).to receive(:hgetall).with('vmpooler__vm__testhost').and_return({"template" => "pool1", "token:token" => "abcdefghijklmnopqrstuvwxyz012345"})
|
||||
expect(redis).not_to receive(:sadd)
|
||||
expect(redis).not_to receive(:srem)
|
||||
|
||||
delete "#{prefix}/vm/testhost"
|
||||
|
||||
expect_json(ok = false, http = 401)
|
||||
end
|
||||
|
||||
it 'deletes a VM when token is supplied' do
|
||||
expect(redis).to receive(:hgetall).with('vmpooler__vm__testhost').and_return({"template" => "pool1", "token:token" => "abcdefghijklmnopqrstuvwxyz012345"})
|
||||
expect(redis).to receive(:srem).and_return(true)
|
||||
expect(redis).to receive(:sadd)
|
||||
|
||||
delete "#{prefix}/vm/testhost", "", {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
|
||||
expect_json(ok = true, http = 200)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /vm/:hostname/snapshot' do
|
||||
context '(auth not configured)' do
|
||||
let(:config) { { auth: false } }
|
||||
|
||||
it 'creates a snapshot' do
|
||||
expect(redis).to receive(:sadd)
|
||||
|
||||
post "#{prefix}/vm/testhost/snapshot"
|
||||
|
||||
expect(JSON.parse(last_response.body)['testhost']['snapshot'].length).to be(32)
|
||||
|
||||
expect_json(ok = true, http = 202)
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
let(:config) { { auth: true } }
|
||||
|
||||
it 'returns a 401 if not authed' do
|
||||
post "#{prefix}/vm/testhost/snapshot"
|
||||
|
||||
expect_json(ok = false, http = 401)
|
||||
end
|
||||
|
||||
it 'creates a snapshot if authed' do
|
||||
expect(redis).to receive(:sadd)
|
||||
|
||||
post "#{prefix}/vm/testhost/snapshot", "", {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
|
||||
expect(JSON.parse(last_response.body)['testhost']['snapshot'].length).to be(32)
|
||||
|
||||
expect_json(ok = true, http = 202)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /vm/:hostname/snapshot/:snapshot' do
|
||||
context '(auth not configured)' do
|
||||
let(:config) { { auth: false } }
|
||||
|
||||
it 'reverts to a snapshot' do
|
||||
expect(redis).to receive(:hget).with('vmpooler__vm__testhost', 'snapshot:testsnapshot').and_return(1)
|
||||
expect(redis).to receive(:sadd)
|
||||
|
||||
post "#{prefix}/vm/testhost/snapshot/testsnapshot"
|
||||
|
||||
expect_json(ok = true, http = 202)
|
||||
end
|
||||
end
|
||||
|
||||
context '(auth configured)' do
|
||||
let(:config) { { auth: true } }
|
||||
|
||||
it 'returns a 401 if not authed' do
|
||||
post "#{prefix}/vm/testhost/snapshot"
|
||||
|
||||
expect_json(ok = false, http = 401)
|
||||
end
|
||||
|
||||
it 'reverts to a snapshot if authed' do
|
||||
expect(redis).to receive(:hget).with('vmpooler__vm__testhost', 'snapshot:testsnapshot').and_return(1)
|
||||
expect(redis).to receive(:sadd)
|
||||
|
||||
post "#{prefix}/vm/testhost/snapshot/testsnapshot", "", {
|
||||
'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345'
|
||||
}
|
||||
|
||||
expect_json(ok = true, http = 202)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -10,6 +10,10 @@ describe Vmpooler::API do
|
|||
|
||||
describe 'Dashboard' do
|
||||
|
||||
before(:each) do
|
||||
redis.flushdb
|
||||
end
|
||||
|
||||
context '/' do
|
||||
before { get '/' }
|
||||
|
||||
|
|
@ -38,7 +42,6 @@ describe Vmpooler::API do
|
|||
it { expect(last_response.status).to eq(404) }
|
||||
it { expect(last_response.header['Content-Type']).to eq('application/json') }
|
||||
it { expect(last_response.body).to eq(JSON.pretty_generate({ok: false})) }
|
||||
|
||||
end
|
||||
|
||||
describe '/dashboard/stats/vmpooler/pool' do
|
||||
|
|
@ -49,7 +52,6 @@ describe Vmpooler::API do
|
|||
],
|
||||
graphite: {}
|
||||
} }
|
||||
let(:redis) { double('redis') }
|
||||
|
||||
before do
|
||||
$config = config
|
||||
|
|
@ -59,13 +61,12 @@ describe Vmpooler::API do
|
|||
end
|
||||
|
||||
context 'without history param' do
|
||||
|
||||
it 'returns basic JSON' do
|
||||
allow(redis).to receive(:scard)
|
||||
allow(redis).to receive(:scard).with('vmpooler__ready__pool1').and_return(3)
|
||||
allow(redis).to receive(:scard).with('vmpooler__ready__pool2').and_return(2)
|
||||
|
||||
expect(redis).to receive(:scard).twice
|
||||
create_ready_vm('pool1', 'vm1')
|
||||
create_ready_vm('pool1', 'vm2')
|
||||
create_ready_vm('pool1', 'vm3')
|
||||
create_ready_vm('pool2', 'vm4')
|
||||
create_ready_vm('pool2', 'vm5')
|
||||
|
||||
get '/dashboard/stats/vmpooler/pool'
|
||||
|
||||
|
|
@ -78,19 +79,15 @@ describe Vmpooler::API do
|
|||
expect(last_response.body).to eq(JSON.pretty_generate(json_hash))
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'with history param' do
|
||||
it 'returns JSON with null history when redis does not has values' do
|
||||
allow(redis).to receive(:scard)
|
||||
expect(redis).to receive(:scard).exactly(4).times
|
||||
|
||||
it 'returns JSON with zeroed history when redis does not have values' do
|
||||
get '/dashboard/stats/vmpooler/pool', :history => true
|
||||
|
||||
json_hash = {
|
||||
pool1: {size: 5, ready: nil, history: [nil]},
|
||||
pool2: {size: 1, ready: nil, history: [nil]}
|
||||
pool1: {size: 5, ready: 0, history: [0]},
|
||||
pool2: {size: 1, ready: 0, history: [0]}
|
||||
}
|
||||
|
||||
expect(last_response).to be_ok
|
||||
|
|
@ -99,10 +96,11 @@ describe Vmpooler::API do
|
|||
end
|
||||
|
||||
it 'returns JSON with history when redis has values' do
|
||||
allow(redis).to receive(:scard).with('vmpooler__ready__pool1').and_return(3)
|
||||
allow(redis).to receive(:scard).with('vmpooler__ready__pool2').and_return(2)
|
||||
|
||||
expect(redis).to receive(:scard).exactly(4).times
|
||||
create_ready_vm('pool1', 'vm1')
|
||||
create_ready_vm('pool1', 'vm2')
|
||||
create_ready_vm('pool1', 'vm3')
|
||||
create_ready_vm('pool2', 'vm4')
|
||||
create_ready_vm('pool2', 'vm5')
|
||||
|
||||
get '/dashboard/stats/vmpooler/pool', :history => true
|
||||
|
||||
|
|
@ -115,9 +113,7 @@ describe Vmpooler::API do
|
|||
expect(last_response.body).to eq(JSON.pretty_generate(json_hash))
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe '/dashboard/stats/vmpooler/running' do
|
||||
|
|
@ -129,7 +125,6 @@ describe Vmpooler::API do
|
|||
],
|
||||
graphite: {}
|
||||
} }
|
||||
let(:redis) { double('redis') }
|
||||
|
||||
before do
|
||||
$config = config
|
||||
|
|
@ -141,10 +136,6 @@ describe Vmpooler::API do
|
|||
context 'without history param' do
|
||||
|
||||
it 'returns basic JSON' do
|
||||
allow(redis).to receive(:scard)
|
||||
|
||||
expect(redis).to receive(:scard).exactly(3).times
|
||||
|
||||
get '/dashboard/stats/vmpooler/running'
|
||||
|
||||
json_hash = {pool: {running: 0}, diffpool: {running: 0}}
|
||||
|
|
@ -155,9 +146,18 @@ describe Vmpooler::API do
|
|||
end
|
||||
|
||||
it 'adds major correctly' do
|
||||
allow(redis).to receive(:scard).with('vmpooler__running__pool-1').and_return(3)
|
||||
allow(redis).to receive(:scard).with('vmpooler__running__pool-2').and_return(5)
|
||||
allow(redis).to receive(:scard).with('vmpooler__running__diffpool-1').and_return(2)
|
||||
create_running_vm('pool-1', 'vm1')
|
||||
create_running_vm('pool-1', 'vm2')
|
||||
create_running_vm('pool-1', 'vm3')
|
||||
|
||||
create_running_vm('pool-2', 'vm4')
|
||||
create_running_vm('pool-2', 'vm5')
|
||||
create_running_vm('pool-2', 'vm6')
|
||||
create_running_vm('pool-2', 'vm7')
|
||||
create_running_vm('pool-2', 'vm8')
|
||||
|
||||
create_running_vm('diffpool-1', 'vm9')
|
||||
create_running_vm('diffpool-1', 'vm10')
|
||||
|
||||
get '/dashboard/stats/vmpooler/running'
|
||||
|
||||
|
|
@ -167,10 +167,7 @@ describe Vmpooler::API do
|
|||
expect(last_response.body).to eq(JSON.pretty_generate(json_hash))
|
||||
expect(last_response.header['Content-Type']).to eq('application/json')
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue