From add88c7bba000232fd8b5969e857ecf0c76f048a Mon Sep 17 00:00:00 2001 From: Scott Schneider Date: Tue, 28 Jul 2015 12:03:14 -0700 Subject: [PATCH] (QENG-1304) vmpooler should require an auth key for VM destruction --- lib/vmpooler/api/v1.rb | 12 +++-- spec/vmpooler/api/v1_spec.rb | 90 ++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/lib/vmpooler/api/v1.rb b/lib/vmpooler/api/v1.rb index 8df2608..4060e90 100644 --- a/lib/vmpooler/api/v1.rb +++ b/lib/vmpooler/api/v1.rb @@ -438,10 +438,14 @@ module Vmpooler params[:hostname] = hostname_shorten(params[:hostname], config['domain']) - pools.each do |pool| - if backend.sismember('vmpooler__running__' + pool['name'], params[:hostname]) - backend.srem('vmpooler__running__' + pool['name'], params[:hostname]) - backend.sadd('vmpooler__completed__' + pool['name'], params[:hostname]) + if backend.exists('vmpooler__vm__' + params[:hostname]) + rdata = backend.hgetall('vmpooler__vm__' + params[:hostname]) + + need_token! if rdata['token:token'] + + if backend.sismember('vmpooler__running__' + rdata['template'], params[:hostname]) + backend.srem('vmpooler__running__' + rdata['template'], params[:hostname]) + backend.sadd('vmpooler__completed__' + rdata['template'], params[:hostname]) status 200 result['ok'] = true diff --git a/spec/vmpooler/api/v1_spec.rb b/spec/vmpooler/api/v1_spec.rb index 0e58d38..bd3c915 100644 --- a/spec/vmpooler/api/v1_spec.rb +++ b/spec/vmpooler/api/v1_spec.rb @@ -433,6 +433,96 @@ describe Vmpooler::API::V1 do 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(:exists).with('vmpooler__vm__testhost').and_return false + expect(redis).not_to receive(:sismember) + expect(redis).not_to receive(:hgetall) + expect(redis).not_to receive(:sadd) + expect(redis).not_to receive(:srem) + + delete "#{prefix}/vm/testhost" + + expect(last_response).not_to be_ok + expect(last_response.header['Content-Type']).to eq('application/json') + expect(last_response.body).to eq(JSON.pretty_generate({'ok' => false})) + expect(last_response.status).to eq(404) + end + + it 'deletes an existing VM' do + expect(redis).to receive(:exists).with('vmpooler__vm__testhost').and_return '1' + expect(redis).to receive(:sismember).with('vmpooler__running__pool1', 'testhost').and_return '1' + expect(redis).to receive(:hgetall).with('vmpooler__vm__testhost').and_return({"template" => "pool1"}) + expect(redis).to receive(:sadd) + expect(redis).to receive(:srem) + + delete "#{prefix}/vm/testhost" + + expect(last_response).to be_ok + expect(last_response.header['Content-Type']).to eq('application/json') + expect(last_response.body).to eq(JSON.pretty_generate({'ok' => true})) + expect(last_response.status).to eq(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(:exists).with('vmpooler__vm__testhost').and_return '1' + expect(redis).to receive(:sismember).with('vmpooler__running__pool1', 'testhost').and_return '1' + expect(redis).to receive(:hgetall).with('vmpooler__vm__testhost').and_return({"template" => "pool1"}) + expect(redis).to receive(:sadd) + expect(redis).to receive(:srem) + + delete "#{prefix}/vm/testhost" + + expect(last_response).to be_ok + expect(last_response.header['Content-Type']).to eq('application/json') + expect(last_response.body).to eq(JSON.pretty_generate({'ok' => true})) + expect(last_response.status).to eq(200) + end + end + + context '(checked-out with token)' do + it 'fails to delete a VM without supplying a token' do + expect(redis).to receive(:exists).with('vmpooler__vm__testhost').and_return '1' + 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(last_response).not_to be_ok + expect(last_response.header['Content-Type']).to eq('application/json') + expect(last_response.body).to eq(JSON.pretty_generate({'ok' => false})) + expect(last_response.status).to eq(401) + end + + it 'deletes a VM when token is supplied' do + expect(redis).to receive(:exists).with('vmpooler__vm__testhost').and_return '1' + expect(redis).to receive(:hgetall).with('vmpooler__vm__testhost').and_return({"template" => "pool1", "token:token" => "abcdefghijklmnopqrstuvwxyz012345"}) + expect(redis).to receive(:sismember).with('vmpooler__running__pool1', 'testhost').and_return '1' + expect(redis).to receive(:sadd) + expect(redis).to receive(:srem) + + delete "#{prefix}/vm/testhost", "", { + 'HTTP_X_AUTH_TOKEN' => 'abcdefghijklmnopqrstuvwxyz012345' + } + + expect(last_response).to be_ok + expect(last_response.header['Content-Type']).to eq('application/json') + expect(last_response.body).to eq(JSON.pretty_generate({'ok' => true})) + expect(last_response.status).to eq(200) + end + end + end + end + describe 'POST /vm/:hostname/snapshot' do context '(auth not configured)' do let(:config) { { auth: false } }