From a8ca47adfc1ecb37170c417d7aa6db08675422c6 Mon Sep 17 00:00:00 2001 From: Samuel Beaulieu Date: Mon, 2 Dec 2019 13:38:22 -0600 Subject: [PATCH] (POOLER-123) Implement a max TTL Before this change, we could checkout a vm and set the lifetime to a very high number which would esssentially keep the vm running forever. Now implementing a config setting max_lifetime_upper_limit which enforces a maximum lifetime in hours both for initial checkout and extending a running vm --- lib/vmpooler/api/v1.rb | 16 +++++++++ spec/helpers.rb | 4 +++ spec/integration/api/v1/vm_hostname_spec.rb | 38 ++++++++++++++++++++- vmpooler.yaml.example | 6 ++++ 4 files changed, 63 insertions(+), 1 deletion(-) diff --git a/lib/vmpooler/api/v1.rb b/lib/vmpooler/api/v1.rb index 1f63fc786..5691c8020 100644 --- a/lib/vmpooler/api/v1.rb +++ b/lib/vmpooler/api/v1.rb @@ -892,6 +892,22 @@ def invalid_pool(payload) when 'lifetime' need_token! if Vmpooler::API.settings.config[:auth] + # in hours, defaults to one week + max_lifetime_upper_limit = config[:max_lifetime_upper_limit] + if max_lifetime_upper_limit + max_lifetime_upper_limit = max_lifetime_upper_limit.to_i + unless arg.to_i < max_lifetime_upper_limit + failure = true + end + # also make sure we do not extend past max_lifetime_upper_limit + rdata = backend.hgetall('vmpooler__vm__' + params[:hostname]) + running = ((Time.now - Time.parse(rdata['checkout'])) / 60 / 60).round(2) + unless running + arg.to_i < max_lifetime_upper_limit + failure = true + end + end + + # validate lifetime is within boundaries unless arg.to_i > 0 failure = true end diff --git a/spec/helpers.rb b/spec/helpers.rb index bd3c7485d..589e00f9e 100644 --- a/spec/helpers.rb +++ b/spec/helpers.rb @@ -99,6 +99,10 @@ def fetch_vm(vm) redis.hgetall("vmpooler__vm__#{vm}") end +def set_vm_data(vm, key, value) + redis.hset("vmpooler__vm__#{vm}", key, value) +end + def snapshot_revert_vm(vm, snapshot = '12345678901234567890123456789012') redis.sadd('vmpooler__tasks__snapshot-revert', "#{vm}:#{snapshot}") redis.hset("vmpooler__vm__#{vm}", "snapshot:#{snapshot}", "1") diff --git a/spec/integration/api/v1/vm_hostname_spec.rb b/spec/integration/api/v1/vm_hostname_spec.rb index 1e6ac1974..c53422476 100644 --- a/spec/integration/api/v1/vm_hostname_spec.rb +++ b/spec/integration/api/v1/vm_hostname_spec.rb @@ -20,12 +20,14 @@ def app() config: { 'site_name' => 'test pooler', 'vm_lifetime_auth' => 2, + }, pools: [ {'name' => 'pool1', 'size' => 5}, {'name' => 'pool2', 'size' => 10} ], alias: { 'poolone' => 'pool1' }, + auth: false } } @@ -34,7 +36,6 @@ def app() before(:each) do app.settings.set :config, config app.settings.set :redis, redis - app.settings.set :config, auth: false create_token('abcdefghijklmnopqrstuvwxyz012345', 'jdoe', current_time) end @@ -122,6 +123,41 @@ def app() vm = fetch_vm('testhost') expect(vm['lifetime']).to be_nil end + + it 'does not enforce a lifetime' do + create_vm('testhost') + + 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 168' do + app.settings.set :config, + { :config => { :max_lifetime_upper_limit => 168 } } + create_vm('testhost') + + 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') + + set_vm_data('testhost', "checkout", (Time.now - (69*60*60))) + 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 diff --git a/vmpooler.yaml.example b/vmpooler.yaml.example index f6781b6fa..e29a8cef5 100644 --- a/vmpooler.yaml.example +++ b/vmpooler.yaml.example @@ -498,6 +498,12 @@ # Expects a boolean value # (optional; default: false) # +# - max_lifetime_upper_limit +# Sets a lifetime upper limit (in hours) for how long the vm lifetime can be set via the API. Lifetime can be set and extended +# so this configuration is used to enforce an upper limit to both the initial lifetime request and/or the extended +# lifetime (by checking how long it has already been running). +# (optional; default: unlimited) +# # Example: :config: