(POOLER-150) Synchronize checkout operations for API

This commit adds a shared mutex to vmpooler API so that checkout requests can be synchronized across threads. Without this change it is possible in some scenarios for vmpooler to allocate the same SUT to different API requests for a VM.
This commit is contained in:
kirby@puppetlabs.com 2019-10-21 14:24:34 -07:00
parent 79bd140ab9
commit 30bf2074fe
6 changed files with 27 additions and 14 deletions

View file

@ -14,6 +14,7 @@ git logs & PR history.
### Fixed ### Fixed
- Correctly detect create\_linked\_clone on a pool level (POOLER-147) - Correctly detect create\_linked\_clone on a pool level (POOLER-147)
- Ensure that checkout operations are synchronized
# [0.7.0](https://github.com/puppetlabs/vmpooler/compare/0.6.3...0.7.0) # [0.7.0](https://github.com/puppetlabs/vmpooler/compare/0.6.3...0.7.0)

View file

@ -24,7 +24,8 @@ if torun.include? 'api'
api = Thread.new do api = Thread.new do
thr = Vmpooler::API.new thr = Vmpooler::API.new
redis = Vmpooler.new_redis(redis_host, redis_port, redis_password) redis = Vmpooler.new_redis(redis_host, redis_port, redis_password)
thr.helpers.configure(config, redis, metrics) checkoutlock = Mutex.new
thr.helpers.configure(config, redis, metrics, checkoutlock)
thr.helpers.execute! thr.helpers.execute!
end end
torun_threads << api torun_threads << api

View file

@ -36,10 +36,11 @@ module Vmpooler
use Vmpooler::API::Reroute use Vmpooler::API::Reroute
use Vmpooler::API::V1 use Vmpooler::API::V1
def configure(config, redis, metrics) def configure(config, redis, metrics, checkoutlock)
self.settings.set :config, config self.settings.set :config, config
self.settings.set :redis, redis self.settings.set :redis, redis
self.settings.set :metrics, metrics self.settings.set :metrics, metrics
self.settings.set :checkoutlock, checkoutlock
end end
def execute! def execute!

View file

@ -36,6 +36,10 @@ module Vmpooler
validate_token(backend) validate_token(backend)
end end
def checkoutlock
Vmpooler::API::settings.checkoutlock
end
def fetch_single_vm(template) def fetch_single_vm(template)
template_backends = [template] template_backends = [template]
aliases = Vmpooler::API.settings.config[:alias] aliases = Vmpooler::API.settings.config[:alias]
@ -67,6 +71,7 @@ module Vmpooler
end end
end end
checkoutlock.synchronize do
template_backends.each do |template_backend| template_backends.each do |template_backend|
vms = backend.smembers("vmpooler__ready__#{template_backend}") vms = backend.smembers("vmpooler__ready__#{template_backend}")
next if vms.empty? next if vms.empty?
@ -83,6 +88,7 @@ module Vmpooler
end end
[nil, nil, nil] [nil, nil, nil]
end end
end
def return_vm_to_ready_state(template, vm) def return_vm_to_ready_state(template, vm)
backend.smove("vmpooler__running__#{template}", "vmpooler__ready__#{template}", vm) backend.smove("vmpooler__running__#{template}", "vmpooler__ready__#{template}", vm)

View file

@ -29,12 +29,14 @@ describe Vmpooler::API::V1 do
} }
let(:current_time) { Time.now } let(:current_time) { Time.now }
let(:vmname) { 'abcdefghijkl' } let(:vmname) { 'abcdefghijkl' }
let(:checkoutlock) { Mutex.new }
before(:each) do before(:each) do
app.settings.set :config, config app.settings.set :config, config
app.settings.set :redis, redis app.settings.set :redis, redis
app.settings.set :metrics, metrics app.settings.set :metrics, metrics
app.settings.set :config, auth: false app.settings.set :config, auth: false
app.settings.set :checkoutlock, checkoutlock
create_token('abcdefghijklmnopqrstuvwxyz012345', 'jdoe', current_time) create_token('abcdefghijklmnopqrstuvwxyz012345', 'jdoe', current_time)
end end

View file

@ -29,12 +29,14 @@ describe Vmpooler::API::V1 do
let(:current_time) { Time.now } let(:current_time) { Time.now }
let(:socket) { double('socket') } let(:socket) { double('socket') }
let(:checkoutlock) { Mutex.new }
before(:each) do before(:each) do
app.settings.set :config, config app.settings.set :config, config
app.settings.set :redis, redis app.settings.set :redis, redis
app.settings.set :metrics, metrics app.settings.set :metrics, metrics
app.settings.set :config, auth: false app.settings.set :config, auth: false
app.settings.set :checkoutlock, checkoutlock
create_token('abcdefghijklmnopqrstuvwxyz012345', 'jdoe', current_time) create_token('abcdefghijklmnopqrstuvwxyz012345', 'jdoe', current_time)
end end