From 8594160fbe8e0acb144b365600f39c7201e2f7a2 Mon Sep 17 00:00:00 2001 From: Samuel Beaulieu Date: Thu, 9 Dec 2021 11:35:29 -0600 Subject: [PATCH] Implementing the tag_vm_user method This method gets the user name based on the token:user metadata in redis, cleans it up to be safe for a label name (lowercase, numbers and dash or underscore only), and update the existing instance labels adding a new one called token-user --- lib/vmpooler/providers/gce.rb | 62 ++++++++++++++++++++++++++++----- spec/unit/providers/gce_spec.rb | 20 ++++++++++- 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/lib/vmpooler/providers/gce.rb b/lib/vmpooler/providers/gce.rb index d86681d..5ed9d03 100644 --- a/lib/vmpooler/providers/gce.rb +++ b/lib/vmpooler/providers/gce.rb @@ -488,6 +488,37 @@ module Vmpooler end end + # tag_vm_user This method is called once we know who is using the VM (it is running). This method enables seeing + # who is using what in the provider pools. + # + # inputs + # [String] pool_name : Name of the pool + # [String] vm_name : Name of the VM to check if ready + # returns + # [Boolean] : true if successful, false if an error occurred and it should retry + def tag_vm_user(pool, vm) + @redis.with_metrics do |redis| + user = get_current_user(vm) + vm_hash = get_vm(pool, vm) + return false if vm_hash.nil? + new_labels = vm_hash['labels'] + # bailing in this case since labels should exist, and continuing would mean losing them + return false if new_labels.nil? + # add new label called token-user, with value as user + new_labels['token-user'] = user + begin + instances_set_labels_request_object = Google::Apis::ComputeV1::InstancesSetLabelsRequest.new(label_fingerprint:vm_hash['label_fingerprint'], labels: new_labels) + result = connection.set_instance_labels(project, zone(pool), vm, instances_set_labels_request_object) + wait_for_zone_operation(project, zone(pool), result, connection) + rescue StandardError => _e + return false + end + return true + end + end + + # END BASE METHODS + def should_be_ignored(item, allowlist) allowlist.map!(&:downcase) # remove uppercase from configured values because its not valid as resource label array_flattened_labels = [] @@ -501,7 +532,18 @@ module Vmpooler !(allowlist & array_flattened_labels).empty? # the allow list specify a fully qualified label eg user=Bob and the item has it end - # END BASE METHODS + def get_current_user(vm) + @redis.with_metrics do |redis| + user = redis.hget("vmpooler__vm__#{vm}", 'token:user') + return "" if user.nil? + # cleanup so it's a valid label value + # can't have upercase + user = user.downcase + # replace invalid chars with dash + user = user.gsub(/[^0-9a-z_-]/, '-') + return user + end + end # Compute resource wait for operation to be DONE (synchronous operation) def wait_for_zone_operation(project, zone, result, connection, retries=5) @@ -542,14 +584,16 @@ module Vmpooler return nil if pool_configuration.nil? { - 'name' => vm_object.name, - 'hostname' => vm_object.hostname, - 'template' => pool_configuration && pool_configuration.key?('template') ? pool_configuration['template'] : nil, #TODO: get it from the API, not from config, but this is what vSphere does too! - 'poolname' => vm_object.labels && vm_object.labels.key?('pool') ? vm_object.labels['pool'] : nil, - 'boottime' => vm_object.creation_timestamp, - 'status' => vm_object.status, # One of the following values: PROVISIONING, STAGING, RUNNING, STOPPING, SUSPENDING, SUSPENDED, REPAIRING, and TERMINATED - 'zone' => vm_object.zone, - 'machine_type' => vm_object.machine_type + 'name' => vm_object.name, + 'hostname' => vm_object.hostname, + 'template' => pool_configuration && pool_configuration.key?('template') ? pool_configuration['template'] : nil, #TODO: get it from the API, not from config, but this is what vSphere does too! + 'poolname' => vm_object.labels && vm_object.labels.key?('pool') ? vm_object.labels['pool'] : nil, + 'boottime' => vm_object.creation_timestamp, + 'status' => vm_object.status, # One of the following values: PROVISIONING, STAGING, RUNNING, STOPPING, SUSPENDING, SUSPENDED, REPAIRING, and TERMINATED + 'zone' => vm_object.zone, + 'machine_type' => vm_object.machine_type, + 'labels' => vm_object.labels, + 'label_fingerprint' => vm_object.label_fingerprint #'powerstate' => powerstate } end diff --git a/spec/unit/providers/gce_spec.rb b/spec/unit/providers/gce_spec.rb index 888593d..f6b93af 100644 --- a/spec/unit/providers/gce_spec.rb +++ b/spec/unit/providers/gce_spec.rb @@ -38,7 +38,7 @@ EOT ) } - let(:vmname) { 'vm13' } + let(:vmname) { 'vm15' } let(:connection) { MockComputeServiceConnection.new } let(:redis_connection_pool) { Vmpooler::PoolManager::GenericConnectionPool.new( metrics: metrics, @@ -61,6 +61,8 @@ EOT skip 'runs in gce' do puts "creating" result = subject.create_vm(poolname, vmname) + subject.get_vm(poolname, vmname) +=begin puts "create snapshot w/ one disk" result = subject.create_snapshot(poolname, vmname, "sams") puts "create disk" @@ -69,6 +71,7 @@ EOT result = subject.create_snapshot(poolname, vmname, "sams2") puts "revert snapshot" result = subject.revert_snapshot(poolname, vmname, "sams") +=end #result = subject.destroy_vm(poolname, vmname) end @@ -737,5 +740,20 @@ EOT expect{ subject.purge_unconfigured_resources(nil) }.to raise_error(/mockerror/) end end + + describe '#get_current_user' do + it 'should downcase and replace invalid chars with dashes' do + redis_connection_pool.with_metrics do |redis| + redis.hset("vmpooler__vm__#{vmname}", 'token:user', "BOBBY.PUPPET") + expect(subject.get_current_user(vmname)).to eq("bobby-puppet") + end + end + + it 'returns "" for nil values' do + redis_connection_pool.with_metrics do |redis| + expect(subject.get_current_user(vmname)).to eq("") + end + end + end end