rubocop fixes

This commit is contained in:
Samuel Beaulieu 2021-12-09 18:43:36 -06:00
parent 662f965c0f
commit 5c67073dad
No known key found for this signature in database
GPG key ID: 12030F74136D0F34
5 changed files with 342 additions and 339 deletions

View file

@ -1,4 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'googleauth' require 'googleauth'
require 'google/apis/compute_v1' require 'google/apis/compute_v1'
require 'bigdecimal' require 'bigdecimal'
@ -8,6 +9,7 @@ require 'vmpooler/providers/base'
module Vmpooler module Vmpooler
class PoolManager class PoolManager
class Provider class Provider
# This class represent a GCE provider to CRUD resources in a gce cloud.
class Gce < Vmpooler::PoolManager::Provider::Base class Gce < Vmpooler::PoolManager::Provider::Base
# The connection_pool method is normally used only for testing # The connection_pool method is normally used only for testing
attr_reader :connection_pool attr_reader :connection_pool
@ -75,7 +77,7 @@ module Vmpooler
return provider_config['machine_type'] if provider_config['machine_type'] return provider_config['machine_type'] if provider_config['machine_type']
end end
#Base methods that are implemented: # Base methods that are implemented:
# vms_in_pool lists all the VM names in a pool, which is based on the VMs # vms_in_pool lists all the VM names in a pool, which is based on the VMs
# having a label "pool" that match a pool config name. # having a label "pool" that match a pool config name.
@ -87,10 +89,11 @@ module Vmpooler
# [Hashtable] # [Hashtable]
# [String] name : the name of the VM instance (unique for whole project) # [String] name : the name of the VM instance (unique for whole project)
def vms_in_pool(pool_name) def vms_in_pool(pool_name)
debug_logger("vms_in_pool") debug_logger('vms_in_pool')
vms = [] vms = []
pool = pool_config(pool_name) pool = pool_config(pool_name)
raise("Pool #{pool_name} does not exist for the provider #{name}") if pool.nil? raise("Pool #{pool_name} does not exist for the provider #{name}") if pool.nil?
zone = zone(pool_name) zone = zone(pool_name)
filter = "(labels.pool = #{pool_name})" filter = "(labels.pool = #{pool_name})"
instance_list = connection.list_instances(project, zone, filter: filter) instance_list = connection.list_instances(project, zone, filter: filter)
@ -121,13 +124,14 @@ module Vmpooler
# [String] zone : URL of the zone where the instance resides. # [String] zone : URL of the zone where the instance resides.
# [String] machine_type : Full or partial URL of the machine type resource to use for this instance, in the format: zones/zone/machineTypes/machine-type. # [String] machine_type : Full or partial URL of the machine type resource to use for this instance, in the format: zones/zone/machineTypes/machine-type.
def get_vm(pool_name, vm_name) def get_vm(pool_name, vm_name)
debug_logger("get_vm") debug_logger('get_vm')
vm_hash = nil vm_hash = nil
begin begin
vm_object = connection.get_instance(project, zone(pool_name), vm_name) vm_object = connection.get_instance(project, zone(pool_name), vm_name)
rescue ::Google::Apis::ClientError => e rescue ::Google::Apis::ClientError => e
raise e unless e.status_code == 404 raise e unless e.status_code == 404
#swallow the ClientError error 404 and return nil when the VM was not found
# swallow the ClientError error 404 and return nil when the VM was not found
return nil return nil
end end
@ -150,38 +154,37 @@ module Vmpooler
# returns # returns
# [Hashtable] of the VM as per get_vm(pool_name, vm_name) # [Hashtable] of the VM as per get_vm(pool_name, vm_name)
def create_vm(pool_name, new_vmname) def create_vm(pool_name, new_vmname)
debug_logger("create_vm") debug_logger('create_vm')
pool = pool_config(pool_name) pool = pool_config(pool_name)
raise("Pool #{pool_name} does not exist for the provider #{name}") if pool.nil? raise("Pool #{pool_name} does not exist for the provider #{name}") if pool.nil?
vm_hash = nil
# harcoded network info # harcoded network info
network_interfaces = Google::Apis::ComputeV1::NetworkInterface.new( network_interfaces = Google::Apis::ComputeV1::NetworkInterface.new(
:network => network_name network: network_name
) )
initParams = { init_params = {
:source_image => pool['template'], #The source image to create this disk. source_image: pool['template'], # The source image to create this disk.
:labels => {'vm' => new_vmname, 'pool' => pool_name}, labels: { 'vm' => new_vmname, 'pool' => pool_name },
:disk_name => "#{new_vmname}-disk0" disk_name: "#{new_vmname}-disk0"
} }
disk = Google::Apis::ComputeV1::AttachedDisk.new( disk = Google::Apis::ComputeV1::AttachedDisk.new(
:auto_delete => true, auto_delete: true,
:boot => true, boot: true,
:initialize_params => Google::Apis::ComputeV1::AttachedDiskInitializeParams.new(initParams) initialize_params: Google::Apis::ComputeV1::AttachedDiskInitializeParams.new(init_params)
) )
# Assume all pool config is valid i.e. not missing # Assume all pool config is valid i.e. not missing
client = ::Google::Apis::ComputeV1::Instance.new( client = ::Google::Apis::ComputeV1::Instance.new(
:name => new_vmname, name: new_vmname,
:machine_type => pool['machine_type'], machine_type: pool['machine_type'],
:disks => [disk], disks: [disk],
:network_interfaces => [network_interfaces], network_interfaces: [network_interfaces],
:labels => {'vm' => new_vmname, 'pool' => pool_name} labels: { 'vm' => new_vmname, 'pool' => pool_name }
) )
debug_logger("trigger insert_instance") debug_logger('trigger insert_instance')
result = connection.insert_instance(project, zone(pool_name), client) result = connection.insert_instance(project, zone(pool_name), client)
result = wait_for_operation(project, pool_name, result) wait_for_operation(project, pool_name, result)
vm_hash = get_vm(pool_name, new_vmname) get_vm(pool_name, new_vmname)
vm_hash
end end
# create_disk creates an additional disk for an existing VM. It will name the new # create_disk creates an additional disk for an existing VM. It will name the new
@ -200,7 +203,7 @@ module Vmpooler
# returns # returns
# [boolean] true : once the operations are finished # [boolean] true : once the operations are finished
def create_disk(pool_name, vm_name, disk_size) def create_disk(pool_name, vm_name, disk_size)
debug_logger("create_disk") debug_logger('create_disk')
pool = pool_config(pool_name) pool = pool_config(pool_name)
raise("Pool #{pool_name} does not exist for the provider #{name}") if pool.nil? raise("Pool #{pool_name} does not exist for the provider #{name}") if pool.nil?
@ -208,18 +211,19 @@ module Vmpooler
vm_object = connection.get_instance(project, zone(pool_name), vm_name) vm_object = connection.get_instance(project, zone(pool_name), vm_name)
rescue ::Google::Apis::ClientError => e rescue ::Google::Apis::ClientError => e
raise e unless e.status_code == 404 raise e unless e.status_code == 404
#if it does not exist
# if it does not exist
raise("VM #{vm_name} in pool #{pool_name} does not exist for the provider #{name}") raise("VM #{vm_name} in pool #{pool_name} does not exist for the provider #{name}")
end end
# this number should start at 1 when there is only the boot disk, # this number should start at 1 when there is only the boot disk,
# eg the new disk will be named spicy-proton-disk1 # eg the new disk will be named spicy-proton-disk1
number_disk = vm_object.disks.length() number_disk = vm_object.disks.length
disk_name = "#{vm_name}-disk#{number_disk}" disk_name = "#{vm_name}-disk#{number_disk}"
disk = Google::Apis::ComputeV1::Disk.new( disk = Google::Apis::ComputeV1::Disk.new(
:name => disk_name, name: disk_name,
:size_gb => disk_size, size_gb: disk_size,
:labels => {"pool" => pool_name, "vm" => vm_name} labels: { 'pool' => pool_name, 'vm' => vm_name }
) )
debug_logger("trigger insert_disk #{disk_name} for #{vm_name}") debug_logger("trigger insert_disk #{disk_name} for #{vm_name}")
result = connection.insert_disk(project, zone(pool_name), disk) result = connection.insert_disk(project, zone(pool_name), disk)
@ -228,9 +232,9 @@ module Vmpooler
new_disk = connection.get_disk(project, zone(pool_name), disk_name) new_disk = connection.get_disk(project, zone(pool_name), disk_name)
attached_disk = Google::Apis::ComputeV1::AttachedDisk.new( attached_disk = Google::Apis::ComputeV1::AttachedDisk.new(
:auto_delete => true, auto_delete: true,
:boot => false, boot: false,
:source => new_disk.self_link source: new_disk.self_link
) )
debug_logger("trigger attach_disk #{disk_name} for #{vm_name}") debug_logger("trigger attach_disk #{disk_name} for #{vm_name}")
result = connection.attach_disk(project, zone(pool_name), vm_object.name, attached_disk) result = connection.attach_disk(project, zone(pool_name), vm_object.name, attached_disk)
@ -254,12 +258,13 @@ module Vmpooler
# RuntimeError if the vm_name cannot be found # RuntimeError if the vm_name cannot be found
# RuntimeError if the snapshot_name already exists for this VM # RuntimeError if the snapshot_name already exists for this VM
def create_snapshot(pool_name, vm_name, new_snapshot_name) def create_snapshot(pool_name, vm_name, new_snapshot_name)
debug_logger("create_snapshot") debug_logger('create_snapshot')
begin begin
vm_object = connection.get_instance(project, zone(pool_name), vm_name) vm_object = connection.get_instance(project, zone(pool_name), vm_name)
rescue ::Google::Apis::ClientError => e rescue ::Google::Apis::ClientError => e
raise e unless e.status_code == 404 raise e unless e.status_code == 404
#if it does not exist
# if it does not exist
raise("VM #{vm_name} in pool #{pool_name} does not exist for the provider #{name}") raise("VM #{vm_name} in pool #{pool_name} does not exist for the provider #{name}")
end end
@ -272,11 +277,11 @@ module Vmpooler
snapshot_obj = ::Google::Apis::ComputeV1::Snapshot.new( snapshot_obj = ::Google::Apis::ComputeV1::Snapshot.new(
name: "#{new_snapshot_name}-#{disk_name}", name: "#{new_snapshot_name}-#{disk_name}",
labels: { labels: {
"snapshot_name" => new_snapshot_name, 'snapshot_name' => new_snapshot_name,
"vm" => vm_name, 'vm' => vm_name,
"pool" => pool_name, 'pool' => pool_name,
"diskname" => disk_name, 'diskname' => disk_name,
"boot" => attached_disk.boot.to_s 'boot' => attached_disk.boot.to_s
} }
) )
debug_logger("trigger async create_disk_snapshot #{vm_name}: #{new_snapshot_name}-#{disk_name}") debug_logger("trigger async create_disk_snapshot #{vm_name}: #{new_snapshot_name}-#{disk_name}")
@ -284,7 +289,7 @@ module Vmpooler
# do them all async, keep a list, check later # do them all async, keep a list, check later
result_list << result result_list << result
end end
#now check they are done # now check they are done
result_list.each do |result| result_list.each do |result|
wait_for_operation(project, pool_name, result) wait_for_operation(project, pool_name, result)
end end
@ -310,12 +315,13 @@ module Vmpooler
# RuntimeError if the vm_name cannot be found # RuntimeError if the vm_name cannot be found
# RuntimeError if the snapshot_name already exists for this VM # RuntimeError if the snapshot_name already exists for this VM
def revert_snapshot(pool_name, vm_name, snapshot_name) def revert_snapshot(pool_name, vm_name, snapshot_name)
debug_logger("revert_snapshot") debug_logger('revert_snapshot')
begin begin
vm_object = connection.get_instance(project, zone(pool_name), vm_name) vm_object = connection.get_instance(project, zone(pool_name), vm_name)
rescue ::Google::Apis::ClientError => e rescue ::Google::Apis::ClientError => e
raise e unless e.status_code == 404 raise e unless e.status_code == 404
#if it does not exist
# if it does not exist
raise("VM #{vm_name} in pool #{pool_name} does not exist for the provider #{name}") raise("VM #{vm_name} in pool #{pool_name} does not exist for the provider #{name}")
end end
@ -328,8 +334,7 @@ module Vmpooler
wait_for_operation(project, pool_name, result) wait_for_operation(project, pool_name, result)
# Delete existing disks # Delete existing disks
if vm_object.disks vm_object.disks&.each do |attached_disk|
vm_object.disks.each do |attached_disk|
debug_logger("trigger detach_disk #{vm_name}: #{attached_disk.device_name}") debug_logger("trigger detach_disk #{vm_name}: #{attached_disk.device_name}")
result = connection.detach_disk(project, zone(pool_name), vm_name, attached_disk.device_name) result = connection.detach_disk(project, zone(pool_name), vm_name, attached_disk.device_name)
wait_for_operation(project, pool_name, result) wait_for_operation(project, pool_name, result)
@ -338,16 +343,15 @@ module Vmpooler
result = connection.delete_disk(project, zone(pool_name), current_disk_name) result = connection.delete_disk(project, zone(pool_name), current_disk_name)
wait_for_operation(project, pool_name, result) wait_for_operation(project, pool_name, result)
end end
end
# this block is sensitive to disruptions, for example if vmpooler is stopped while this is running # this block is sensitive to disruptions, for example if vmpooler is stopped while this is running
snapshot_object.each do |snapshot| snapshot_object.each do |snapshot|
current_disk_name = snapshot.labels['diskname'] current_disk_name = snapshot.labels['diskname']
bootable = (snapshot.labels['boot'] == "true") bootable = (snapshot.labels['boot'] == 'true')
disk = Google::Apis::ComputeV1::Disk.new( disk = Google::Apis::ComputeV1::Disk.new(
:name => current_disk_name, name: current_disk_name,
:labels => {"pool" => pool_name, "vm" => vm_name}, labels: { 'pool' => pool_name, 'vm' => vm_name },
:source_snapshot => snapshot.self_link source_snapshot: snapshot.self_link
) )
# create disk in GCE as a separate resource # create disk in GCE as a separate resource
debug_logger("trigger insert_disk #{vm_name}: #{current_disk_name} based on #{snapshot.self_link}") debug_logger("trigger insert_disk #{vm_name}: #{current_disk_name} based on #{snapshot.self_link}")
@ -356,9 +360,9 @@ module Vmpooler
# read the new disk info # read the new disk info
new_disk_info = connection.get_disk(project, zone(pool_name), current_disk_name) new_disk_info = connection.get_disk(project, zone(pool_name), current_disk_name)
new_attached_disk = Google::Apis::ComputeV1::AttachedDisk.new( new_attached_disk = Google::Apis::ComputeV1::AttachedDisk.new(
:auto_delete => true, auto_delete: true,
:boot => bootable, boot: bootable,
:source => new_disk_info.self_link source: new_disk_info.self_link
) )
# attach the new disk to existing instance # attach the new disk to existing instance
debug_logger("trigger attach_disk #{vm_name}: #{current_disk_name}") debug_logger("trigger attach_disk #{vm_name}: #{current_disk_name}")
@ -380,18 +384,19 @@ module Vmpooler
# returns # returns
# [boolean] true : once the operations are finished # [boolean] true : once the operations are finished
def destroy_vm(pool_name, vm_name) def destroy_vm(pool_name, vm_name)
debug_logger("destroy_vm") debug_logger('destroy_vm')
deleted = false deleted = false
begin begin
vm_object = connection.get_instance(project, zone(pool_name), vm_name) connection.get_instance(project, zone(pool_name), vm_name)
rescue ::Google::Apis::ClientError => e rescue ::Google::Apis::ClientError => e
raise e unless e.status_code == 404 raise e unless e.status_code == 404
# If a VM doesn't exist then it is effectively deleted # If a VM doesn't exist then it is effectively deleted
deleted = true deleted = true
debug_logger("instance #{vm_name} already deleted") debug_logger("instance #{vm_name} already deleted")
end end
if(!deleted) unless deleted
debug_logger("trigger delete_instance #{vm_name}") debug_logger("trigger delete_instance #{vm_name}")
result = connection.delete_instance(project, zone(pool_name), vm_name) result = connection.delete_instance(project, zone(pool_name), vm_name)
wait_for_operation(project, pool_name, result, 10) wait_for_operation(project, pool_name, result, 10)
@ -401,41 +406,37 @@ module Vmpooler
filter = "(labels.vm = #{vm_name})" filter = "(labels.vm = #{vm_name})"
disk_list = connection.list_disks(project, zone(pool_name), filter: filter) disk_list = connection.list_disks(project, zone(pool_name), filter: filter)
result_list = [] result_list = []
unless disk_list.items.nil? disk_list.items&.each do |disk|
disk_list.items.each do |disk|
debug_logger("trigger delete_disk #{disk.name}") debug_logger("trigger delete_disk #{disk.name}")
result = connection.delete_disk(project, zone(pool_name), disk.name) result = connection.delete_disk(project, zone(pool_name), disk.name)
# do them all async, keep a list, check later # do them all async, keep a list, check later
result_list << result result_list << result
end end
end # now check they are done
#now check they are done result_list.each do |r|
result_list.each do |result| wait_for_operation(project, pool_name, r)
wait_for_operation(project, pool_name, result)
end end
# list and delete leftover snapshots, this could happen if snapshots were taken, # list and delete leftover snapshots, this could happen if snapshots were taken,
# as they are not removed when the original disk is deleted or the instance is detroyed # as they are not removed when the original disk is deleted or the instance is detroyed
snapshot_list = find_all_snapshots(vm_name) snapshot_list = find_all_snapshots(vm_name)
result_list = [] result_list = []
unless snapshot_list.nil? snapshot_list&.each do |snapshot|
snapshot_list.each do |snapshot|
debug_logger("trigger delete_snapshot #{snapshot.name}") debug_logger("trigger delete_snapshot #{snapshot.name}")
result = connection.delete_snapshot(project, snapshot.name) result = connection.delete_snapshot(project, snapshot.name)
# do them all async, keep a list, check later # do them all async, keep a list, check later
result_list << result result_list << result
end end
end # now check they are done
#now check they are done result_list.each do |r|
result_list.each do |result| wait_for_operation(project, pool_name, r)
wait_for_operation(project, pool_name, result)
end end
true true
end end
def vm_ready?(_pool_name, vm_name) def vm_ready?(_pool_name, vm_name)
begin begin
#TODO: we could use a healthcheck resource attached to instance # TODO: we could use a healthcheck resource attached to instance
open_socket(vm_name, global_config[:config]['domain']) open_socket(vm_name, global_config[:config]['domain'])
rescue StandardError => _e rescue StandardError => _e
return false return false
@ -446,7 +447,7 @@ module Vmpooler
# Scans zones that are configured for list of resources (VM, disks, snapshots) that do not have the label.pool set # Scans zones that are configured for list of resources (VM, disks, snapshots) that do not have the label.pool set
# to one of the configured pools. If it is also not in the allowlist, the resource is destroyed # to one of the configured pools. If it is also not in the allowlist, the resource is destroyed
def purge_unconfigured_resources(allowlist) def purge_unconfigured_resources(allowlist)
debug_logger("purge_unconfigured_resources") debug_logger('purge_unconfigured_resources')
pools_array = provided_pools pools_array = provided_pools
filter = {} filter = {}
# we have to group things by zone, because the API search feature is done against a zone and not global # we have to group things by zone, because the API search feature is done against a zone and not global
@ -455,45 +456,44 @@ module Vmpooler
filter[zone(pool)] = [] if filter[zone(pool)].nil? filter[zone(pool)] = [] if filter[zone(pool)].nil?
filter[zone(pool)] << "(labels.pool != #{pool})" filter[zone(pool)] << "(labels.pool != #{pool})"
end end
filter.keys.each do |zone| filter.each_key do |zone|
# this filter should return any item that have a labels.pool that is not in the config OR # this filter should return any item that have a labels.pool that is not in the config OR
# do not have a pool label at all # do not have a pool label at all
filter_string = filter[zone].join(" AND ") + " OR -labels.pool:*" filter_string = "#{filter[zone].join(' AND ')} OR -labels.pool:*"
#VMs # VMs
instance_list = connection.list_instances(project, zone, filter: filter_string) instance_list = connection.list_instances(project, zone, filter: filter_string)
result_list = [] result_list = []
unless instance_list.items.nil? instance_list.items&.each do |vm|
instance_list.items.each do |vm|
next if should_be_ignored(vm, allowlist) next if should_be_ignored(vm, allowlist)
debug_logger("trigger async delete_instance #{vm.name}") debug_logger("trigger async delete_instance #{vm.name}")
result = connection.delete_instance(project, zone, vm.name) result = connection.delete_instance(project, zone, vm.name)
result_list << result result_list << result
end end
end # now check they are done
#now check they are done
result_list.each do |result| result_list.each do |result|
wait_for_zone_operation(project, zone, result) wait_for_zone_operation(project, zone, result)
end end
#Disks # Disks
disks_list = connection.list_disks(project, zone, filter: filter_string) disks_list = connection.list_disks(project, zone, filter: filter_string)
unless disks_list.items.nil? disks_list.items&.each do |disk|
disks_list.items.each do |disk|
next if should_be_ignored(disk, allowlist) next if should_be_ignored(disk, allowlist)
debug_logger("trigger async no wait delete_disk #{disk.name}") debug_logger("trigger async no wait delete_disk #{disk.name}")
result = connection.delete_disk(project, zone, disk.name) connection.delete_disk(project, zone, disk.name)
end
end end
#Snapshots # Snapshots
snapshot_list = connection.list_snapshots(project, filter: filter_string) snapshot_list = connection.list_snapshots(project, filter: filter_string)
unless snapshot_list.items.nil? next if snapshot_list.items.nil?
snapshot_list.items.each do |sn| snapshot_list.items.each do |sn|
next if should_be_ignored(sn, allowlist) next if should_be_ignored(sn, allowlist)
debug_logger("trigger async no wait delete_snapshot #{sn.name}") debug_logger("trigger async no wait delete_snapshot #{sn.name}")
result = connection.delete_snapshot(project, sn.name) connection.delete_snapshot(project, sn.name)
end
end end
end end
end end
@ -506,45 +506,47 @@ module Vmpooler
# [String] vm_name : Name of the VM to check if ready # [String] vm_name : Name of the VM to check if ready
# returns # returns
# [Boolean] : true if successful, false if an error occurred and it should retry # [Boolean] : true if successful, false if an error occurred and it should retry
def tag_vm_user(pool, vm) def tag_vm_user(pool, vm_name)
user = get_current_user(vm) user = get_current_user(vm_name)
vm_hash = get_vm(pool, vm) vm_hash = get_vm(pool, vm_name)
return false if vm_hash.nil? return false if vm_hash.nil?
new_labels = vm_hash['labels'] new_labels = vm_hash['labels']
# bailing in this case since labels should exist, and continuing would mean losing them # bailing in this case since labels should exist, and continuing would mean losing them
return false if new_labels.nil? return false if new_labels.nil?
# add new label called token-user, with value as user # add new label called token-user, with value as user
new_labels['token-user'] = user new_labels['token-user'] = user
begin begin
instances_set_labels_request_object = Google::Apis::ComputeV1::InstancesSetLabelsRequest.new(label_fingerprint:vm_hash['label_fingerprint'], labels: new_labels) 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) result = connection.set_instance_labels(project, zone(pool), vm_name, instances_set_labels_request_object)
wait_for_zone_operation(project, zone(pool), result) wait_for_zone_operation(project, zone(pool), result)
rescue StandardError => _e rescue StandardError => _e
return false return false
end end
return true true
end end
# END BASE METHODS # END BASE METHODS
def should_be_ignored(item, allowlist) def should_be_ignored(item, allowlist)
return false if allowlist.nil? return false if allowlist.nil?
allowlist.map!(&:downcase) # remove uppercase from configured values because its not valid as resource label allowlist.map!(&:downcase) # remove uppercase from configured values because its not valid as resource label
array_flattened_labels = [] array_flattened_labels = []
unless item.labels.nil? item.labels&.each do |k, v|
item.labels.each do |k,v|
array_flattened_labels << "#{k}=#{v}" array_flattened_labels << "#{k}=#{v}"
end end
end
(!item.labels.nil? && allowlist&.include?(item.labels['pool'])) || # the allow list specifies the value within the pool label (!item.labels.nil? && allowlist&.include?(item.labels['pool'])) || # the allow list specifies the value within the pool label
(allowlist&.include?("") && !item.labels&.keys&.include?('pool')) || # the allow list specifies "" string and the pool label is not set (allowlist&.include?('') && !item.labels&.keys&.include?('pool')) || # the allow list specifies "" string and the pool label is not set
!(allowlist & array_flattened_labels).empty? # the allow list specify a fully qualified label eg user=Bob and the item has it !(allowlist & array_flattened_labels).empty? # the allow list specify a fully qualified label eg user=Bob and the item has it
end end
def get_current_user(vm) def get_current_user(vm_name)
@redis.with_metrics do |redis| @redis.with_metrics do |redis|
user = redis.hget("vmpooler__vm__#{vm}", 'token:user') user = redis.hget("vmpooler__vm__#{vm_name}", 'token:user')
return "" if user.nil? return '' if user.nil?
# cleanup so it's a valid label value # cleanup so it's a valid label value
# can't have upercase # can't have upercase
user = user.downcase user = user.downcase
@ -555,13 +557,13 @@ module Vmpooler
end end
# Compute resource wait for operation to be DONE (synchronous operation) # Compute resource wait for operation to be DONE (synchronous operation)
def wait_for_zone_operation(project, zone, result, retries=5) def wait_for_zone_operation(project, zone, result, retries = 5)
while result.status != 'DONE' while result.status != 'DONE'
result = connection.wait_zone_operation(project, zone, result.name) result = connection.wait_zone_operation(project, zone, result.name)
debug_logger(" -> wait_for_zone_operation status #{result.status} (#{result.name})") debug_logger(" -> wait_for_zone_operation status #{result.status} (#{result.name})")
end end
if result.error # unsure what kind of error can be stored here if result.error # unsure what kind of error can be stored here
error_message = "" error_message = ''
# array of errors, combine them all # array of errors, combine them all
result.error.each do |error| result.error.each do |error|
error_message = "#{error_message} #{error.code}:#{error.message}" error_message = "#{error_message} #{error.code}:#{error.message}"
@ -571,19 +573,20 @@ module Vmpooler
result result
rescue Google::Apis::TransmissionError => e rescue Google::Apis::TransmissionError => e
# Error returned once timeout reached, each retry typically about 1 minute. # Error returned once timeout reached, each retry typically about 1 minute.
if retries > 0 if retries.positive?
retries = retries - 1 retries -= 1
retry retry
end end
raise raise
rescue Google::Apis::ClientError => e rescue Google::Apis::ClientError => e
raise e unless e.status_code == 404 raise e unless e.status_code == 404
# if the operation is not found, and we are 'waiting' on it, it might be because it # if the operation is not found, and we are 'waiting' on it, it might be because it
# is already finished # is already finished
puts "waited on #{result.name} but was not found, so skipping" puts "waited on #{result.name} but was not found, so skipping"
end end
def wait_for_operation(project, pool_name, result, retries=5) def wait_for_operation(project, pool_name, result, retries = 5)
wait_for_zone_operation(project, zone(pool_name), result, retries) wait_for_zone_operation(project, zone(pool_name), result, retries)
end end
@ -596,15 +599,15 @@ module Vmpooler
{ {
'name' => vm_object.name, 'name' => vm_object.name,
'hostname' => vm_object.hostname, '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! 'template' => 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, 'poolname' => vm_object.labels&.key?('pool') ? vm_object.labels['pool'] : nil,
'boottime' => vm_object.creation_timestamp, 'boottime' => vm_object.creation_timestamp,
'status' => vm_object.status, # One of the following values: PROVISIONING, STAGING, RUNNING, STOPPING, SUSPENDING, SUSPENDED, REPAIRING, and TERMINATED 'status' => vm_object.status, # One of the following values: PROVISIONING, STAGING, RUNNING, STOPPING, SUSPENDING, SUSPENDED, REPAIRING, and TERMINATED
'zone' => vm_object.zone, 'zone' => vm_object.zone,
'machine_type' => vm_object.machine_type, 'machine_type' => vm_object.machine_type,
'labels' => vm_object.labels, 'labels' => vm_object.labels,
'label_fingerprint' => vm_object.label_fingerprint 'label_fingerprint' => vm_object.label_fingerprint
#'powerstate' => powerstate # 'powerstate' => powerstate
} }
end end
@ -618,7 +621,7 @@ module Vmpooler
retry_factor = global_config[:config]['retry_factor'] || 10 retry_factor = global_config[:config]['retry_factor'] || 10
try = 1 try = 1
begin begin
scopes = ["https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/cloud-platform"] scopes = ['https://www.googleapis.com/auth/compute', 'https://www.googleapis.com/auth/cloud-platform']
authorization = Google::Auth.get_application_default(scopes) authorization = Google::Auth.get_application_default(scopes)
@ -627,7 +630,7 @@ module Vmpooler
metrics.increment('connect.open') metrics.increment('connect.open')
compute compute
rescue StandardError => e #is that even a thing? rescue StandardError => e # is that even a thing?
metrics.increment('connect.fail') metrics.increment('connect.fail')
raise e if try >= max_tries raise e if try >= max_tries
@ -653,18 +656,18 @@ module Vmpooler
# this is used because for one vm, with the same snapshot name there could be multiple snapshots, # this is used because for one vm, with the same snapshot name there could be multiple snapshots,
# one for each disk # one for each disk
def find_snapshot(vm, snapshotname) def find_snapshot(vm_name, snapshotname)
filter = "(labels.vm = #{vm}) AND (labels.snapshot_name = #{snapshotname})" filter = "(labels.vm = #{vm_name}) AND (labels.snapshot_name = #{snapshotname})"
snapshot_list = connection.list_snapshots(project,filter: filter) snapshot_list = connection.list_snapshots(project, filter: filter)
return snapshot_list.items #array of snapshot objects snapshot_list.items # array of snapshot objects
end end
# find all snapshots ever created for one vm, # find all snapshots ever created for one vm,
# regardless of snapshot name, for example when deleting it all # regardless of snapshot name, for example when deleting it all
def find_all_snapshots(vm) def find_all_snapshots(vm_name)
filter = "(labels.vm = #{vm})" filter = "(labels.vm = #{vm_name})"
snapshot_list = connection.list_snapshots(project,filter: filter) snapshot_list = connection.list_snapshots(project, filter: filter)
return snapshot_list.items #array of snapshot objects snapshot_list.items # array of snapshot objects
end end
def disk_name_from_source(attached_disk) def disk_name_from_source(attached_disk)
@ -673,10 +676,10 @@ module Vmpooler
# used in local dev environment, set DEBUG_FLAG=true # used in local dev environment, set DEBUG_FLAG=true
# this way the upstream vmpooler manager does not get polluted with logs # this way the upstream vmpooler manager does not get polluted with logs
def debug_logger(message, send_to_upstream=false) def debug_logger(message, send_to_upstream: false)
#the default logger is simple and does not enforce debug levels (the first argument) # the default logger is simple and does not enforce debug levels (the first argument)
puts message if ENV['DEBUG_FLAG'] puts message if ENV['DEBUG_FLAG']
logger.log("[g]", message) if send_to_upstream logger.log('[g]', message) if send_to_upstream
end end
end end
end end

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# this file is used to Mock the GCE objects, for example the main ComputeService object # this file is used to Mock the GCE objects, for example the main ComputeService object
MockResult = Struct.new( MockResult = Struct.new(
# https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/ComputeV1/Operation.html # https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/ComputeV1/Operation.html
@ -7,7 +9,7 @@ MockResult = Struct.new(
keyword_init: true keyword_init: true
) )
MockOperationError = Array.new MockOperationError = [].freeze
MockOperationErrorError = Struct.new( MockOperationErrorError = Struct.new(
# https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/ComputeV1/Operation/Error/Error.html # https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/ComputeV1/Operation/Error/Error.html
@ -27,19 +29,19 @@ MockInstance = Struct.new(
) )
MockInstanceList = Struct.new( MockInstanceList = Struct.new(
#https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/ComputeV1/InstanceList.html # https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/ComputeV1/InstanceList.html
:id, :items, :kind, :next_page_token, :self_link, :warning, :id, :items, :kind, :next_page_token, :self_link, :warning,
keyword_init: true keyword_init: true
) )
MockDiskList = Struct.new( MockDiskList = Struct.new(
#https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/ComputeV1/DiskList.html # https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/ComputeV1/DiskList.html
:id, :items, :kind, :next_page_token, :self_link, :warning, :id, :items, :kind, :next_page_token, :self_link, :warning,
keyword_init: true keyword_init: true
) )
MockDisk = Struct.new( MockDisk = Struct.new(
#https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/ComputeV1/Disk.html # https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/ComputeV1/Disk.html
:creation_timestamp, :description, :disk_encryption_key, :guest_os_features, :id, :kind, :label_fingerprint, :labels, :creation_timestamp, :description, :disk_encryption_key, :guest_os_features, :id, :kind, :label_fingerprint, :labels,
:last_attach_timestamp, :last_detach_timestamp, :license_codes, :licenses, :name, :options, :last_attach_timestamp, :last_detach_timestamp, :license_codes, :licenses, :name, :options,
:physical_block_size_bytes, :region, :replica_zones, :resource_policies, :self_link, :size_gb, :source_disk, :physical_block_size_bytes, :region, :replica_zones, :resource_policies, :self_link, :size_gb, :source_disk,
@ -49,13 +51,13 @@ MockDisk = Struct.new(
) )
MockSnapshotList = Struct.new( MockSnapshotList = Struct.new(
#https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/ComputeV1/DiskList.html # https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/ComputeV1/DiskList.html
:id, :items, :kind, :next_page_token, :self_link, :warning, :id, :items, :kind, :next_page_token, :self_link, :warning,
keyword_init: true keyword_init: true
) )
MockSnapshot = Struct.new( MockSnapshot = Struct.new(
#https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/ComputeV1/Snapshot.html # https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/ComputeV1/Snapshot.html
:auto_created, :chain_name, :creation_timestamp, :description, :disk_size_gb, :download_bytes, :id, :kind, :auto_created, :chain_name, :creation_timestamp, :description, :disk_size_gb, :download_bytes, :id, :kind,
:label_fingerprint, :labels, :license_codes, :licenses, :name, :self_link, :snapshot_encryption_key, :source_disk, :label_fingerprint, :labels, :license_codes, :licenses, :name, :self_link, :snapshot_encryption_key, :source_disk,
:source_disk_encryption_key, :source_disk_id, :status, :storage_bytes, :storage_bytes_status, :storage_locations, :source_disk_encryption_key, :source_disk_id, :status, :storage_bytes, :storage_bytes_status, :storage_locations,
@ -63,7 +65,7 @@ MockSnapshot = Struct.new(
) )
MockAttachedDisk = Struct.new( MockAttachedDisk = Struct.new(
#https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/ComputeV1/AttachedDisk.html # https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/ComputeV1/AttachedDisk.html
:auto_delete, :boot, :device_name, :disk_encryption_key, :disk_size_gb, :guest_os_features, :index, :auto_delete, :boot, :device_name, :disk_encryption_key, :disk_size_gb, :guest_os_features, :index,
:initialize_params, :interface, :kind, :licenses, :mode, :shielded_instance_initial_state, :source, :type, :initialize_params, :interface, :kind, :licenses, :mode, :shielded_instance_initial_state, :source, :type,
keyword_init: true keyword_init: true
@ -80,6 +82,7 @@ MockComputeServiceConnection = Struct.new(
def get_instance def get_instance
MockInstance.new MockInstance.new
end end
# Alias to serviceContent.propertyCollector # Alias to serviceContent.propertyCollector
def insert_instance def insert_instance
MockResult.new MockResult.new
@ -90,20 +93,18 @@ end
# Mocking Methods # Mocking Methods
# ------------------------------------------------------------------------------------------------------------- # -------------------------------------------------------------------------------------------------------------
=begin # def mock_RbVmomi_VIM_ClusterComputeResource(options = {})
def mock_RbVmomi_VIM_ClusterComputeResource(options = {}) # options[:name] = 'Cluster' + rand(65536).to_s if options[:name].nil?
options[:name] = 'Cluster' + rand(65536).to_s if options[:name].nil? #
# mock = MockClusterComputeResource.new()
mock = MockClusterComputeResource.new() #
# mock.name = options[:name]
mock.name = options[:name] # # All cluster compute resources have a root Resource Pool
# All cluster compute resources have a root Resource Pool # mock.resourcePool = mock_RbVmomi_VIM_ResourcePool({:name => options[:name]})
mock.resourcePool = mock_RbVmomi_VIM_ResourcePool({:name => options[:name]}) #
# allow(mock).to receive(:is_a?) do |expected_type|
allow(mock).to receive(:is_a?) do |expected_type| # expected_type == RbVmomi::VIM::ClusterComputeResource
expected_type == RbVmomi::VIM::ClusterComputeResource # end
end #
# mock
mock # end
end
=end

View file

@ -1,23 +1,22 @@
# frozen_string_literal: true
require 'mock_redis' require 'mock_redis'
def redis def redis
unless @redis @redis ||= MockRedis.new
@redis = MockRedis.new
end
@redis @redis
end end
# Mock an object which represents a Logger. This stops the proliferation # Mock an object which represents a Logger. This stops the proliferation
# of allow(logger).to .... expectations in tests. # of allow(logger).to .... expectations in tests.
class MockLogger class MockLogger
def log(_level, string) def log(_level, string); end
end
end end
def expect_json(ok = true, http = 200) def expect_json(ok = true, http = 200)
expect(last_response.header['Content-Type']).to eq('application/json') expect(last_response.header['Content-Type']).to eq('application/json')
if (ok == true) then if ok == true
expect(JSON.parse(last_response.body)['ok']).to eq(true) expect(JSON.parse(last_response.body)['ok']).to eq(true)
else else
expect(JSON.parse(last_response.body)['ok']).to eq(false) expect(JSON.parse(last_response.body)['ok']).to eq(false)
@ -35,7 +34,7 @@ def get_token_data(token)
redis.hgetall("vmpooler__token__#{token}") redis.hgetall("vmpooler__token__#{token}")
end end
def token_exists?(token) def token_exists?(_token)
result = get_token_data result = get_token_data
result && !result.empty? result && !result.empty?
end end
@ -43,7 +42,7 @@ end
def create_ready_vm(template, name, redis, token = nil) def create_ready_vm(template, name, redis, token = nil)
create_vm(name, redis, token) create_vm(name, redis, token)
redis.sadd("vmpooler__ready__#{template}", name) redis.sadd("vmpooler__ready__#{template}", name)
redis.hset("vmpooler__vm__#{name}", "template", template) redis.hset("vmpooler__vm__#{name}", 'template', template)
end end
def create_running_vm(template, name, redis, token = nil, user = nil) def create_running_vm(template, name, redis, token = nil, user = nil)
@ -57,7 +56,7 @@ end
def create_pending_vm(template, name, redis, token = nil) def create_pending_vm(template, name, redis, token = nil)
create_vm(name, redis, token) create_vm(name, redis, token)
redis.sadd("vmpooler__pending__#{template}", name) redis.sadd("vmpooler__pending__#{template}", name)
redis.hset("vmpooler__vm__#{name}", "template", template) redis.hset("vmpooler__vm__#{name}", 'template', template)
end end
def create_vm(name, redis, token = nil, user = nil) def create_vm(name, redis, token = nil, user = nil)
@ -100,12 +99,12 @@ end
def snapshot_revert_vm(vm, snapshot = '12345678901234567890123456789012', redis) def snapshot_revert_vm(vm, snapshot = '12345678901234567890123456789012', redis)
redis.sadd('vmpooler__tasks__snapshot-revert', "#{vm}:#{snapshot}") redis.sadd('vmpooler__tasks__snapshot-revert', "#{vm}:#{snapshot}")
redis.hset("vmpooler__vm__#{vm}", "snapshot:#{snapshot}", "1") redis.hset("vmpooler__vm__#{vm}", "snapshot:#{snapshot}", '1')
end end
def snapshot_vm(vm, snapshot = '12345678901234567890123456789012', redis) def snapshot_vm(vm, snapshot = '12345678901234567890123456789012', redis)
redis.sadd('vmpooler__tasks__snapshot', "#{vm}:#{snapshot}") redis.sadd('vmpooler__tasks__snapshot', "#{vm}:#{snapshot}")
redis.hset("vmpooler__vm__#{vm}", "snapshot:#{snapshot}", "1") redis.hset("vmpooler__vm__#{vm}", "snapshot:#{snapshot}", '1')
end end
def disk_task_vm(vm, disk_size = '10', redis) def disk_task_vm(vm, disk_size = '10', redis)
@ -127,7 +126,7 @@ def vm_reverted_to_snapshot?(vm, redis, snapshot = nil)
end end
def pool_has_ready_vm?(pool, vm, redis) def pool_has_ready_vm?(pool, vm, redis)
!!redis.sismember('vmpooler__ready__' + pool, vm) !!redis.sismember("vmpooler__ready__#{pool}", vm)
end end
def create_ondemand_request_for_test(request_id, score, platforms_string, redis, user = nil, token = nil) def create_ondemand_request_for_test(request_id, score, platforms_string, redis, user = nil, token = nil)

View file

@ -1,9 +1,9 @@
=begin # frozen_string_literal: true
require 'simplecov'
SimpleCov.start do # require 'simplecov'
add_filter '/spec/' # SimpleCov.start do
end # add_filter '/spec/'
=end # end
require 'helpers' require 'helpers'
require 'rspec' require 'rspec'
require 'vmpooler' require 'vmpooler'
@ -19,16 +19,16 @@ def fixtures_dir
File.join(project_root_dir, 'spec', 'fixtures') File.join(project_root_dir, 'spec', 'fixtures')
end end
def create_google_client_error(status_code, message, reason="notFound") def create_google_client_error(status_code, message, reason = 'notFound')
Google::Apis::ClientError.new(Google::Apis::ClientError, status_code:status_code, body:'{ Google::Apis::ClientError.new(Google::Apis::ClientError, status_code: status_code, body: '{
"error": { "error": {
"code": '+status_code.to_s+', "code": ' + status_code.to_s + ',
"message": "'+message+'", "message": "' + message + '",
"errors": [ "errors": [
{ {
"message": "'+message+'", "message": "' + message + '",
"domain": "global", "domain": "global",
"reason": "'+reason+'" "reason": "' + reason + '"
} }
] ]
} }

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'spec_helper' require 'spec_helper'
require 'mock_redis' require 'mock_redis'
require 'vmpooler/providers/gce' require 'vmpooler/providers/gce'
@ -12,19 +14,20 @@ describe 'Vmpooler::PoolManager::Provider::Gce' do
let(:poolname) { 'debian-9' } let(:poolname) { 'debian-9' }
let(:provider_options) { { 'param' => 'value' } } let(:provider_options) { { 'param' => 'value' } }
let(:project) { 'dio-samuel-dev' } let(:project) { 'dio-samuel-dev' }
let(:zone){ 'us-west1-b' } let(:zone) { 'us-west1-b' }
let(:config) { YAML.load(<<-EOT let(:config) do
--- YAML.safe_load(<<~EOT
:config: ---
:config:
max_tries: 3 max_tries: 3
retry_factor: 10 retry_factor: 10
:providers: :providers:
:gce: :gce:
connection_pool_timeout: 1 connection_pool_timeout: 1
project: '#{project}' project: '#{project}'
zone: '#{zone}' zone: '#{zone}'
network_name: 'global/networks/default' network_name: 'global/networks/default'
:pools: :pools:
- name: '#{poolname}' - name: '#{poolname}'
alias: [ 'mockpool' ] alias: [ 'mockpool' ]
template: 'projects/debian-cloud/global/images/family/debian-9' template: 'projects/debian-cloud/global/images/family/debian-9'
@ -34,20 +37,21 @@ describe 'Vmpooler::PoolManager::Provider::Gce' do
provider: 'gce' provider: 'gce'
network_name: 'default' network_name: 'default'
machine_type: 'zones/#{zone}/machineTypes/e2-micro' machine_type: 'zones/#{zone}/machineTypes/e2-micro'
EOT EOT
) )
} end
let(:vmname) { 'vm16' } let(:vmname) { 'vm16' }
let(:connection) { MockComputeServiceConnection.new } let(:connection) { MockComputeServiceConnection.new }
let(:redis_connection_pool) { Vmpooler::PoolManager::GenericConnectionPool.new( let(:redis_connection_pool) do
Vmpooler::PoolManager::GenericConnectionPool.new(
metrics: metrics, metrics: metrics,
connpool_type: 'redis_connection_pool', connpool_type: 'redis_connection_pool',
connpool_provider: 'testprovider', connpool_provider: 'testprovider',
size: 1, size: 1,
timeout: 5 timeout: 5
) { MockRedis.new } ) { MockRedis.new }
} end
subject { Vmpooler::PoolManager::Provider::Gce.new(config, logger, metrics, redis_connection_pool, 'gce', provider_options) } subject { Vmpooler::PoolManager::Provider::Gce.new(config, logger, metrics, redis_connection_pool, 'gce', provider_options) }
@ -59,26 +63,26 @@ EOT
describe '#manual tests live' do describe '#manual tests live' do
skip 'runs in gce' do skip 'runs in gce' do
puts "creating" puts 'creating'
result = subject.create_vm(poolname, vmname) result = subject.create_vm(poolname, vmname)
subject.get_vm(poolname, vmname) subject.get_vm(poolname, vmname)
subject.vms_in_pool(poolname) subject.vms_in_pool(poolname)
puts "create snapshot w/ one disk" puts 'create snapshot w/ one disk'
result = subject.create_snapshot(poolname, vmname, "sams") result = subject.create_snapshot(poolname, vmname, 'sams')
puts "create disk" puts 'create disk'
result = subject.create_disk(poolname, vmname, 10) result = subject.create_disk(poolname, vmname, 10)
puts "create snapshot w/ 2 disks" puts 'create snapshot w/ 2 disks'
result = subject.create_snapshot(poolname, vmname, "sams2") result = subject.create_snapshot(poolname, vmname, 'sams2')
puts "revert snapshot" puts 'revert snapshot'
result = subject.revert_snapshot(poolname, vmname, "sams") result = subject.revert_snapshot(poolname, vmname, 'sams')
result = subject.destroy_vm(poolname, vmname) result = subject.destroy_vm(poolname, vmname)
end end
skip 'runs existing' do skip 'runs existing' do
#result = subject.create_snapshot(poolname, vmname, "sams") # result = subject.create_snapshot(poolname, vmname, "sams")
#result = subject.revert_snapshot(poolname, vmname, "sams") # result = subject.revert_snapshot(poolname, vmname, "sams")
#puts subject.get_vm(poolname, vmname) # puts subject.get_vm(poolname, vmname)
result = subject.destroy_vm(poolname, vmname) result = subject.destroy_vm(poolname, vmname)
end end
@ -105,11 +109,13 @@ EOT
end end
context 'Given a pool folder with many VMs' do context 'Given a pool folder with many VMs' do
let(:expected_vm_list) {[ let(:expected_vm_list) do
{ 'name' => 'vm1'}, [
{ 'name' => 'vm2'}, { 'name' => 'vm1' },
{ 'name' => 'vm3'} { 'name' => 'vm2' },
]} { 'name' => 'vm3' }
]
end
before(:each) do before(:each) do
instance_list = MockInstanceList.new(items: []) instance_list = MockInstanceList.new(items: [])
expected_vm_list.each do |vm_hash| expected_vm_list.each do |vm_hash|
@ -126,7 +132,6 @@ EOT
expect(result).to eq(expected_vm_list) expect(result).to eq(expected_vm_list)
end end
end end
end end
describe '#get_vm' do describe '#get_vm' do
@ -136,8 +141,8 @@ EOT
context 'when VM does not exist' do context 'when VM does not exist' do
it 'should return nil' do it 'should return nil' do
allow(connection).to receive(:get_instance).and_raise(create_google_client_error(404,"The resource 'projects/#{project}/zones/#{zone}/instances/#{vmname}' was not found")) allow(connection).to receive(:get_instance).and_raise(create_google_client_error(404, "The resource 'projects/#{project}/zones/#{zone}/instances/#{vmname}' was not found"))
expect(subject.get_vm(poolname,vmname)).to be_nil expect(subject.get_vm(poolname, vmname)).to be_nil
end end
end end
@ -147,18 +152,18 @@ EOT
end end
it 'should return a hash' do it 'should return a hash' do
expect(subject.get_vm(poolname,vmname)).to be_kind_of(Hash) expect(subject.get_vm(poolname, vmname)).to be_kind_of(Hash)
end end
it 'should return the VM name' do it 'should return the VM name' do
result = subject.get_vm(poolname,vmname) result = subject.get_vm(poolname, vmname)
expect(result['name']).to eq(vmname) expect(result['name']).to eq(vmname)
end end
['hostname','boottime','zone','status'].each do |testcase| %w[hostname boottime zone status].each do |testcase|
it "should return nil for #{testcase}" do it "should return nil for #{testcase}" do
result = subject.get_vm(poolname,vmname) result = subject.get_vm(poolname, vmname)
expect(result[testcase]).to be_nil expect(result[testcase]).to be_nil
end end
@ -168,52 +173,53 @@ EOT
context 'when VM exists and contains all information' do context 'when VM exists and contains all information' do
let(:vm_hostname) { "#{vmname}.demo.local" } let(:vm_hostname) { "#{vmname}.demo.local" }
let(:boot_time) { Time.now } let(:boot_time) { Time.now }
let(:vm_object) { MockInstance.new( let(:vm_object) do
MockInstance.new(
name: vmname, name: vmname,
hostname: vm_hostname, hostname: vm_hostname,
labels: {'pool' => poolname}, labels: { 'pool' => poolname },
creation_timestamp: boot_time, creation_timestamp: boot_time,
status: 'RUNNING', status: 'RUNNING',
zone: zone, zone: zone,
machine_type: "zones/#{zone}/machineTypes/e2-micro" machine_type: "zones/#{zone}/machineTypes/e2-micro"
) )
} end
let(:pool_info) { config[:pools][0]} let(:pool_info) { config[:pools][0] }
before(:each) do before(:each) do
allow(connection).to receive(:get_instance).and_return(vm_object) allow(connection).to receive(:get_instance).and_return(vm_object)
end end
it 'should return a hash' do it 'should return a hash' do
expect(subject.get_vm(poolname,vmname)).to be_kind_of(Hash) expect(subject.get_vm(poolname, vmname)).to be_kind_of(Hash)
end end
it 'should return the VM name' do it 'should return the VM name' do
result = subject.get_vm(poolname,vmname) result = subject.get_vm(poolname, vmname)
expect(result['name']).to eq(vmname) expect(result['name']).to eq(vmname)
end end
it 'should return the VM hostname' do it 'should return the VM hostname' do
result = subject.get_vm(poolname,vmname) result = subject.get_vm(poolname, vmname)
expect(result['hostname']).to eq(vm_hostname) expect(result['hostname']).to eq(vm_hostname)
end end
it 'should return the template name' do it 'should return the template name' do
result = subject.get_vm(poolname,vmname) result = subject.get_vm(poolname, vmname)
expect(result['template']).to eq(pool_info['template']) expect(result['template']).to eq(pool_info['template'])
end end
it 'should return the pool name' do it 'should return the pool name' do
result = subject.get_vm(poolname,vmname) result = subject.get_vm(poolname, vmname)
expect(result['poolname']).to eq(pool_info['name']) expect(result['poolname']).to eq(pool_info['name'])
end end
it 'should return the boot time' do it 'should return the boot time' do
result = subject.get_vm(poolname,vmname) result = subject.get_vm(poolname, vmname)
expect(result['boottime']).to eq(boot_time) expect(result['boottime']).to eq(boot_time)
end end
@ -227,33 +233,30 @@ EOT
context 'Given an invalid pool name' do context 'Given an invalid pool name' do
it 'should raise an error' do it 'should raise an error' do
expect{ subject.create_vm('missing_pool', vmname) }.to raise_error(/missing_pool does not exist/) expect { subject.create_vm('missing_pool', vmname) }.to raise_error(/missing_pool does not exist/)
end end
end end
context 'Given a template VM that does not exist' do context 'Given a template VM that does not exist' do
before(:each) do before(:each) do
config[:pools][0]['template'] = 'Templates/missing_template' config[:pools][0]['template'] = 'Templates/missing_template'
=begin # result = MockResult.new
result = MockResult.new # result.status = "PENDING"
result.status = "PENDING" # errors = MockOperationError
errors = MockOperationError # errors << MockOperationErrorError.new(code: "foo", message: "it's missing")
errors << MockOperationErrorError.new(code: "foo", message: "it's missing") # result.error = errors
result.error = errors allow(connection).to receive(:insert_instance).and_raise(create_google_client_error(404, 'The resource \'Templates/missing_template\' was not found'))
=end
allow(connection).to receive(:insert_instance).and_raise(create_google_client_error(404,'The resource \'Templates/missing_template\' was not found'))
end end
it 'should raise an error' do it 'should raise an error' do
expect{ subject.create_vm(poolname, vmname) }.to raise_error(Google::Apis::ClientError) expect { subject.create_vm(poolname, vmname) }.to raise_error(Google::Apis::ClientError)
end end
end end
context 'Given a successful creation' do context 'Given a successful creation' do
before(:each) do before(:each) do
result = MockResult.new result = MockResult.new
result.status = "DONE" result.status = 'DONE'
allow(connection).to receive(:insert_instance).and_return(result) allow(connection).to receive(:insert_instance).and_return(result)
end end
@ -264,7 +267,6 @@ EOT
expect(result.is_a?(Hash)).to be true expect(result.is_a?(Hash)).to be true
end end
it 'should have the new VM name' do it 'should have the new VM name' do
instance = MockInstance.new(name: vmname) instance = MockInstance.new(name: vmname)
allow(connection).to receive(:get_instance).and_return(instance) allow(connection).to receive(:get_instance).and_return(instance)
@ -282,7 +284,7 @@ EOT
context 'Given a missing VM name' do context 'Given a missing VM name' do
before(:each) do before(:each) do
allow(connection).to receive(:get_instance).and_raise(create_google_client_error(404,"The resource 'projects/#{project}/zones/#{zone}/instances/#{vmname}' was not found")) allow(connection).to receive(:get_instance).and_raise(create_google_client_error(404, "The resource 'projects/#{project}/zones/#{zone}/instances/#{vmname}' was not found"))
disk_list = MockDiskList.new(items: nil) disk_list = MockDiskList.new(items: nil)
allow(connection).to receive(:list_disks).and_return(disk_list) allow(connection).to receive(:list_disks).and_return(disk_list)
allow(subject).to receive(:find_all_snapshots).and_return(nil) allow(subject).to receive(:find_all_snapshots).and_return(nil)
@ -299,7 +301,7 @@ EOT
instance = MockInstance.new(name: vmname) instance = MockInstance.new(name: vmname)
allow(connection).to receive(:get_instance).and_return(instance) allow(connection).to receive(:get_instance).and_return(instance)
result = MockResult.new result = MockResult.new
result.status = "DONE" result.status = 'DONE'
allow(subject).to receive(:wait_for_operation).and_return(result) allow(subject).to receive(:wait_for_operation).and_return(result)
allow(connection).to receive(:delete_instance).and_return(result) allow(connection).to receive(:delete_instance).and_return(result)
end end
@ -333,7 +335,6 @@ EOT
subject.destroy_vm(poolname, vmname) subject.destroy_vm(poolname, vmname)
end end
end end
end end
describe '#vm_ready?' do describe '#vm_ready?' do
@ -344,17 +345,17 @@ EOT
end end
it 'should return true' do it 'should return true' do
expect(subject.vm_ready?(poolname,vmname)).to be true expect(subject.vm_ready?(poolname, vmname)).to be true
end end
end end
context 'When an error occurs connecting to the VM' do context 'When an error occurs connecting to the VM' do
before(:each) do before(:each) do
expect(subject).to receive(:open_socket).and_raise(RuntimeError,'MockError') expect(subject).to receive(:open_socket).and_raise(RuntimeError, 'MockError')
end end
it 'should return false' do it 'should return false' do
expect(subject.vm_ready?(poolname,vmname)).to be false expect(subject.vm_ready?(poolname, vmname)).to be false
end end
end end
end end
@ -367,17 +368,17 @@ EOT
context 'Given an invalid pool name' do context 'Given an invalid pool name' do
it 'should raise an error' do it 'should raise an error' do
expect{ subject.create_disk('missing_pool',vmname,disk_size) }.to raise_error(/missing_pool does not exist/) expect { subject.create_disk('missing_pool', vmname, disk_size) }.to raise_error(/missing_pool does not exist/)
end end
end end
context 'when VM does not exist' do context 'when VM does not exist' do
before(:each) do before(:each) do
expect(connection).to receive(:get_instance).and_raise(create_google_client_error(404,"The resource 'projects/#{project}/zones/#{zone}/instances/#{vmname}' was not found")) expect(connection).to receive(:get_instance).and_raise(create_google_client_error(404, "The resource 'projects/#{project}/zones/#{zone}/instances/#{vmname}' was not found"))
end end
it 'should raise an error' do it 'should raise an error' do
expect{ subject.create_disk(poolname,vmname,disk_size) }.to raise_error(/VM #{vmname} .+ does not exist/) expect { subject.create_disk(poolname, vmname, disk_size) }.to raise_error(/VM #{vmname} .+ does not exist/)
end end
end end
@ -386,11 +387,11 @@ EOT
disk = MockDisk.new(name: vmname) disk = MockDisk.new(name: vmname)
instance = MockInstance.new(name: vmname, disks: [disk]) instance = MockInstance.new(name: vmname, disks: [disk])
allow(connection).to receive(:get_instance).and_return(instance) allow(connection).to receive(:get_instance).and_return(instance)
expect(connection).to receive(:insert_disk).and_raise(RuntimeError,'Mock Disk Error') expect(connection).to receive(:insert_disk).and_raise(RuntimeError, 'Mock Disk Error')
end end
it 'should raise an error' do it 'should raise an error' do
expect{ subject.create_disk(poolname,vmname,disk_size) }.to raise_error(/Mock Disk Error/) expect { subject.create_disk(poolname, vmname, disk_size) }.to raise_error(/Mock Disk Error/)
end end
end end
@ -400,7 +401,7 @@ EOT
instance = MockInstance.new(name: vmname, disks: [disk]) instance = MockInstance.new(name: vmname, disks: [disk])
allow(connection).to receive(:get_instance).and_return(instance) allow(connection).to receive(:get_instance).and_return(instance)
result = MockResult.new result = MockResult.new
result.status = "DONE" result.status = 'DONE'
allow(connection).to receive(:insert_disk).and_return(result) allow(connection).to receive(:insert_disk).and_return(result)
allow(subject).to receive(:wait_for_operation).and_return(result) allow(subject).to receive(:wait_for_operation).and_return(result)
new_disk = MockDisk.new(name: "#{vmname}-disk1", self_link: "/foo/bar/baz/#{vmname}-disk1") new_disk = MockDisk.new(name: "#{vmname}-disk1", self_link: "/foo/bar/baz/#{vmname}-disk1")
@ -409,7 +410,7 @@ EOT
end end
it 'should return true' do it 'should return true' do
expect(subject.create_disk(poolname,vmname,disk_size)).to be true expect(subject.create_disk(poolname, vmname, disk_size)).to be true
end end
end end
end end
@ -423,11 +424,11 @@ EOT
context 'when VM does not exist' do context 'when VM does not exist' do
before(:each) do before(:each) do
allow(connection).to receive(:get_instance).and_raise(create_google_client_error(404,"The resource 'projects/#{project}/zones/#{zone}/instances/#{vmname}' was not found")) allow(connection).to receive(:get_instance).and_raise(create_google_client_error(404, "The resource 'projects/#{project}/zones/#{zone}/instances/#{vmname}' was not found"))
end end
it 'should raise an error' do it 'should raise an error' do
expect{ subject.create_snapshot(poolname,vmname,snapshot_name) }.to raise_error(/VM #{vmname} .+ does not exist/) expect { subject.create_snapshot(poolname, vmname, snapshot_name) }.to raise_error(/VM #{vmname} .+ does not exist/)
end end
end end
@ -438,7 +439,7 @@ EOT
allow(connection).to receive(:get_instance).and_return(instance) allow(connection).to receive(:get_instance).and_return(instance)
snapshots = [MockSnapshot.new(name: snapshot_name)] snapshots = [MockSnapshot.new(name: snapshot_name)]
allow(subject).to receive(:find_snapshot).and_return(snapshots) allow(subject).to receive(:find_snapshot).and_return(snapshots)
expect{ subject.create_snapshot(poolname,vmname,snapshot_name) }.to raise_error(/Snapshot #{snapshot_name} .+ already exists /) expect { subject.create_snapshot(poolname, vmname, snapshot_name) }.to raise_error(/Snapshot #{snapshot_name} .+ already exists /)
end end
end end
@ -449,11 +450,11 @@ EOT
allow(connection).to receive(:get_instance).and_return(instance) allow(connection).to receive(:get_instance).and_return(instance)
snapshots = nil snapshots = nil
allow(subject).to receive(:find_snapshot).and_return(snapshots) allow(subject).to receive(:find_snapshot).and_return(snapshots)
allow(connection).to receive(:create_disk_snapshot).and_raise(RuntimeError,'Mock Snapshot Error') allow(connection).to receive(:create_disk_snapshot).and_raise(RuntimeError, 'Mock Snapshot Error')
end end
it 'should raise an error' do it 'should raise an error' do
expect{ subject.create_snapshot(poolname,vmname,snapshot_name) }.to raise_error(/Mock Snapshot Error/) expect { subject.create_snapshot(poolname, vmname, snapshot_name) }.to raise_error(/Mock Snapshot Error/)
end end
end end
@ -465,12 +466,12 @@ EOT
snapshots = nil snapshots = nil
allow(subject).to receive(:find_snapshot).and_return(snapshots) allow(subject).to receive(:find_snapshot).and_return(snapshots)
result = MockResult.new result = MockResult.new
result.status = "DONE" result.status = 'DONE'
allow(connection).to receive(:create_disk_snapshot).and_return(result) allow(connection).to receive(:create_disk_snapshot).and_return(result)
end end
it 'should return true' do it 'should return true' do
expect(subject.create_snapshot(poolname,vmname,snapshot_name)).to be true expect(subject.create_snapshot(poolname, vmname, snapshot_name)).to be true
end end
it 'should snapshot each attached disk' do it 'should snapshot each attached disk' do
@ -480,7 +481,7 @@ EOT
allow(connection).to receive(:get_instance).and_return(instance) allow(connection).to receive(:get_instance).and_return(instance)
expect(connection.should_receive(:create_disk_snapshot).twice) expect(connection.should_receive(:create_disk_snapshot).twice)
subject.create_snapshot(poolname,vmname,snapshot_name) subject.create_snapshot(poolname, vmname, snapshot_name)
end end
end end
end end
@ -494,11 +495,11 @@ EOT
context 'when VM does not exist' do context 'when VM does not exist' do
before(:each) do before(:each) do
allow(connection).to receive(:get_instance).and_raise(create_google_client_error(404,"The resource 'projects/#{project}/zones/#{zone}/instances/#{vmname}' was not found")) allow(connection).to receive(:get_instance).and_raise(create_google_client_error(404, "The resource 'projects/#{project}/zones/#{zone}/instances/#{vmname}' was not found"))
end end
it 'should raise an error' do it 'should raise an error' do
expect{ subject.revert_snapshot(poolname,vmname,snapshot_name) }.to raise_error(/VM #{vmname} .+ does not exist/) expect { subject.revert_snapshot(poolname, vmname, snapshot_name) }.to raise_error(/VM #{vmname} .+ does not exist/)
end end
end end
@ -509,7 +510,7 @@ EOT
allow(connection).to receive(:get_instance).and_return(instance) allow(connection).to receive(:get_instance).and_return(instance)
snapshots = nil snapshots = nil
allow(subject).to receive(:find_snapshot).and_return(snapshots) allow(subject).to receive(:find_snapshot).and_return(snapshots)
expect{ subject.revert_snapshot(poolname,vmname,snapshot_name) }.to raise_error(/Snapshot #{snapshot_name} .+ does not exist /) expect { subject.revert_snapshot(poolname, vmname, snapshot_name) }.to raise_error(/Snapshot #{snapshot_name} .+ does not exist /)
end end
end end
@ -524,7 +525,7 @@ EOT
allow(connection).to receive(:start_instance) allow(connection).to receive(:start_instance)
expect(subject).not_to receive(:detach_disk) expect(subject).not_to receive(:detach_disk)
expect(subject).not_to receive(:delete_disk) expect(subject).not_to receive(:delete_disk)
subject.revert_snapshot(poolname,vmname,snapshot_name) subject.revert_snapshot(poolname, vmname, snapshot_name)
end end
end end
@ -537,11 +538,11 @@ EOT
allow(subject).to receive(:find_snapshot).and_return(snapshots) allow(subject).to receive(:find_snapshot).and_return(snapshots)
allow(connection).to receive(:stop_instance) allow(connection).to receive(:stop_instance)
allow(subject).to receive(:wait_for_operation) allow(subject).to receive(:wait_for_operation)
expect(connection).to receive(:detach_disk).and_raise(RuntimeError,'Mock Snapshot Error') expect(connection).to receive(:detach_disk).and_raise(RuntimeError, 'Mock Snapshot Error')
end end
it 'should raise an error' do it 'should raise an error' do
expect{ subject.revert_snapshot(poolname,vmname,snapshot_name) }.to raise_error(/Mock Snapshot Error/) expect { subject.revert_snapshot(poolname, vmname, snapshot_name) }.to raise_error(/Mock Snapshot Error/)
end end
end end
@ -550,7 +551,7 @@ EOT
attached_disk = MockAttachedDisk.new(device_name: vmname, source: "foo/bar/baz/#{vmname}") attached_disk = MockAttachedDisk.new(device_name: vmname, source: "foo/bar/baz/#{vmname}")
instance = MockInstance.new(name: vmname, disks: [attached_disk]) instance = MockInstance.new(name: vmname, disks: [attached_disk])
allow(connection).to receive(:get_instance).and_return(instance) allow(connection).to receive(:get_instance).and_return(instance)
snapshots = [MockSnapshot.new(name: snapshot_name, self_link: "foo/bar/baz/snapshot/#{snapshot_name}", labels: {"diskname" => vmname})] snapshots = [MockSnapshot.new(name: snapshot_name, self_link: "foo/bar/baz/snapshot/#{snapshot_name}", labels: { 'diskname' => vmname })]
allow(subject).to receive(:find_snapshot).and_return(snapshots) allow(subject).to receive(:find_snapshot).and_return(snapshots)
allow(connection).to receive(:stop_instance) allow(connection).to receive(:stop_instance)
allow(subject).to receive(:wait_for_operation) allow(subject).to receive(:wait_for_operation)
@ -564,7 +565,7 @@ EOT
end end
it 'should return true' do it 'should return true' do
expect(subject.revert_snapshot(poolname,vmname,snapshot_name)).to be true expect(subject.revert_snapshot(poolname, vmname, snapshot_name)).to be true
end end
end end
end end
@ -581,7 +582,7 @@ EOT
allow(subject).to receive(:wait_for_zone_operation) allow(subject).to receive(:wait_for_zone_operation)
end end
it 'should attempt to delete unconfigured instances when they dont have a label' do it 'should attempt to delete unconfigured instances when they dont have a label' do
instance_list = MockInstanceList.new(items: [MockInstance.new(name: "foo")]) instance_list = MockInstanceList.new(items: [MockInstance.new(name: 'foo')])
disk_list = MockDiskList.new(items: nil) disk_list = MockDiskList.new(items: nil)
snapshot_list = MockSnapshotList.new(items: nil) snapshot_list = MockSnapshotList.new(items: nil)
# the instance_list is filtered in the real code, and should only return non-configured VMs based on labels # the instance_list is filtered in the real code, and should only return non-configured VMs based on labels
@ -593,7 +594,7 @@ EOT
subject.purge_unconfigured_resources(nil) subject.purge_unconfigured_resources(nil)
end end
it 'should attempt to delete unconfigured instances when they have a label that is not a configured pool' do it 'should attempt to delete unconfigured instances when they have a label that is not a configured pool' do
instance_list = MockInstanceList.new(items: [MockInstance.new(name: "foo", labels: {"pool" => "foobar"})]) instance_list = MockInstanceList.new(items: [MockInstance.new(name: 'foo', labels: { 'pool' => 'foobar' })])
disk_list = MockDiskList.new(items: nil) disk_list = MockDiskList.new(items: nil)
snapshot_list = MockSnapshotList.new(items: nil) snapshot_list = MockSnapshotList.new(items: nil)
allow(connection).to receive(:list_instances).and_return(instance_list) allow(connection).to receive(:list_instances).and_return(instance_list)
@ -604,8 +605,8 @@ EOT
end end
it 'should attempt to delete unconfigured disks and snapshots when they do not have a label' do it 'should attempt to delete unconfigured disks and snapshots when they do not have a label' do
instance_list = MockInstanceList.new(items: nil) instance_list = MockInstanceList.new(items: nil)
disk_list = MockDiskList.new(items: [MockDisk.new(name: "diskfoo")]) disk_list = MockDiskList.new(items: [MockDisk.new(name: 'diskfoo')])
snapshot_list = MockSnapshotList.new(items: [MockSnapshot.new(name: "snapfoo")]) snapshot_list = MockSnapshotList.new(items: [MockSnapshot.new(name: 'snapfoo')])
allow(connection).to receive(:list_instances).and_return(instance_list) allow(connection).to receive(:list_instances).and_return(instance_list)
allow(connection).to receive(:list_disks).and_return(disk_list) allow(connection).to receive(:list_disks).and_return(disk_list)
allow(connection).to receive(:list_snapshots).and_return(snapshot_list) allow(connection).to receive(:list_snapshots).and_return(snapshot_list)
@ -618,10 +619,10 @@ EOT
context 'with allowlist containing a pool name' do context 'with allowlist containing a pool name' do
before(:each) do before(:each) do
allow(subject).to receive(:wait_for_zone_operation) allow(subject).to receive(:wait_for_zone_operation)
$allowlist = ["allowed"] $allowlist = ['allowed']
end end
it 'should attempt to delete unconfigured instances when they dont have the allowlist label' do it 'should attempt to delete unconfigured instances when they dont have the allowlist label' do
instance_list = MockInstanceList.new(items: [MockInstance.new(name: "foo", labels: {"pool" => "not_this"})]) instance_list = MockInstanceList.new(items: [MockInstance.new(name: 'foo', labels: { 'pool' => 'not_this' })])
disk_list = MockDiskList.new(items: nil) disk_list = MockDiskList.new(items: nil)
snapshot_list = MockSnapshotList.new(items: nil) snapshot_list = MockSnapshotList.new(items: nil)
allow(connection).to receive(:list_instances).and_return(instance_list) allow(connection).to receive(:list_instances).and_return(instance_list)
@ -631,7 +632,7 @@ EOT
subject.purge_unconfigured_resources($allowlist) subject.purge_unconfigured_resources($allowlist)
end end
it 'should ignore unconfigured instances when they have a label that is allowed' do it 'should ignore unconfigured instances when they have a label that is allowed' do
instance_list = MockInstanceList.new(items: [MockInstance.new(name: "foo", labels: {"pool" => "allowed"})]) instance_list = MockInstanceList.new(items: [MockInstance.new(name: 'foo', labels: { 'pool' => 'allowed' })])
disk_list = MockDiskList.new(items: nil) disk_list = MockDiskList.new(items: nil)
snapshot_list = MockSnapshotList.new(items: nil) snapshot_list = MockSnapshotList.new(items: nil)
allow(connection).to receive(:list_instances).and_return(instance_list) allow(connection).to receive(:list_instances).and_return(instance_list)
@ -642,8 +643,8 @@ EOT
end end
it 'should ignore unconfigured disks and snapshots when they have a label that is allowed' do it 'should ignore unconfigured disks and snapshots when they have a label that is allowed' do
instance_list = MockInstanceList.new(items: nil) instance_list = MockInstanceList.new(items: nil)
disk_list = MockDiskList.new(items: [MockDisk.new(name: "diskfoo", labels: {"pool" => "allowed"})]) disk_list = MockDiskList.new(items: [MockDisk.new(name: 'diskfoo', labels: { 'pool' => 'allowed' })])
snapshot_list = MockSnapshotList.new(items: [MockSnapshot.new(name: "snapfoo", labels: {"pool" => "allowed"})]) snapshot_list = MockSnapshotList.new(items: [MockSnapshot.new(name: 'snapfoo', labels: { 'pool' => 'allowed' })])
allow(connection).to receive(:list_instances).and_return(instance_list) allow(connection).to receive(:list_instances).and_return(instance_list)
allow(connection).to receive(:list_disks).and_return(disk_list) allow(connection).to receive(:list_disks).and_return(disk_list)
allow(connection).to receive(:list_snapshots).and_return(snapshot_list) allow(connection).to receive(:list_snapshots).and_return(snapshot_list)
@ -652,10 +653,10 @@ EOT
subject.purge_unconfigured_resources($allowlist) subject.purge_unconfigured_resources($allowlist)
end end
it 'should ignore unconfigured item when they have the empty label that is allowed, which means we allow the pool label to not be set' do it 'should ignore unconfigured item when they have the empty label that is allowed, which means we allow the pool label to not be set' do
$allowlist = ["allowed", ""] $allowlist = ['allowed', '']
instance_list = MockInstanceList.new(items: [MockInstance.new(name: "foo", labels: {"some" => "not_important"})]) instance_list = MockInstanceList.new(items: [MockInstance.new(name: 'foo', labels: { 'some' => 'not_important' })])
disk_list = MockDiskList.new(items: [MockDisk.new(name: "diskfoo", labels: {"other" => "thing"})]) disk_list = MockDiskList.new(items: [MockDisk.new(name: 'diskfoo', labels: { 'other' => 'thing' })])
snapshot_list = MockSnapshotList.new(items: [MockSnapshot.new(name: "snapfoo")]) snapshot_list = MockSnapshotList.new(items: [MockSnapshot.new(name: 'snapfoo')])
allow(connection).to receive(:list_instances).and_return(instance_list) allow(connection).to receive(:list_instances).and_return(instance_list)
allow(connection).to receive(:list_disks).and_return(disk_list) allow(connection).to receive(:list_disks).and_return(disk_list)
allow(connection).to receive(:list_snapshots).and_return(snapshot_list) allow(connection).to receive(:list_snapshots).and_return(snapshot_list)
@ -669,10 +670,10 @@ EOT
context 'with allowlist containing a pool name and the empty string' do context 'with allowlist containing a pool name and the empty string' do
before(:each) do before(:each) do
allow(subject).to receive(:wait_for_zone_operation) allow(subject).to receive(:wait_for_zone_operation)
$allowlist = ["allowed", ""] $allowlist = ['allowed', '']
end end
it 'should attempt to delete unconfigured instances when they dont have the allowlist label' do it 'should attempt to delete unconfigured instances when they dont have the allowlist label' do
instance_list = MockInstanceList.new(items: [MockInstance.new(name: "foo", labels: {"pool" => "not_this"})]) instance_list = MockInstanceList.new(items: [MockInstance.new(name: 'foo', labels: { 'pool' => 'not_this' })])
disk_list = MockDiskList.new(items: nil) disk_list = MockDiskList.new(items: nil)
snapshot_list = MockSnapshotList.new(items: nil) snapshot_list = MockSnapshotList.new(items: nil)
allow(connection).to receive(:list_instances).and_return(instance_list) allow(connection).to receive(:list_instances).and_return(instance_list)
@ -683,8 +684,8 @@ EOT
end end
it 'should ignore unconfigured disks and snapshots when they have a label that is allowed' do it 'should ignore unconfigured disks and snapshots when they have a label that is allowed' do
instance_list = MockInstanceList.new(items: nil) instance_list = MockInstanceList.new(items: nil)
disk_list = MockDiskList.new(items: [MockDisk.new(name: "diskfoo", labels: {"pool" => "allowed"})]) disk_list = MockDiskList.new(items: [MockDisk.new(name: 'diskfoo', labels: { 'pool' => 'allowed' })])
snapshot_list = MockSnapshotList.new(items: [MockSnapshot.new(name: "snapfoo", labels: {"pool" => "allowed"})]) snapshot_list = MockSnapshotList.new(items: [MockSnapshot.new(name: 'snapfoo', labels: { 'pool' => 'allowed' })])
allow(connection).to receive(:list_instances).and_return(instance_list) allow(connection).to receive(:list_instances).and_return(instance_list)
allow(connection).to receive(:list_disks).and_return(disk_list) allow(connection).to receive(:list_disks).and_return(disk_list)
allow(connection).to receive(:list_snapshots).and_return(snapshot_list) allow(connection).to receive(:list_snapshots).and_return(snapshot_list)
@ -693,9 +694,9 @@ EOT
subject.purge_unconfigured_resources($allowlist) subject.purge_unconfigured_resources($allowlist)
end end
it 'should ignore unconfigured item when they have the empty label that is allowed, which means we allow the pool label to not be set' do it 'should ignore unconfigured item when they have the empty label that is allowed, which means we allow the pool label to not be set' do
instance_list = MockInstanceList.new(items: [MockInstance.new(name: "foo", labels: {"some" => "not_important"})]) instance_list = MockInstanceList.new(items: [MockInstance.new(name: 'foo', labels: { 'some' => 'not_important' })])
disk_list = MockDiskList.new(items: [MockDisk.new(name: "diskfoo", labels: {"other" => "thing"})]) disk_list = MockDiskList.new(items: [MockDisk.new(name: 'diskfoo', labels: { 'other' => 'thing' })])
snapshot_list = MockSnapshotList.new(items: [MockSnapshot.new(name: "snapfoo")]) snapshot_list = MockSnapshotList.new(items: [MockSnapshot.new(name: 'snapfoo')])
allow(connection).to receive(:list_instances).and_return(instance_list) allow(connection).to receive(:list_instances).and_return(instance_list)
allow(connection).to receive(:list_disks).and_return(disk_list) allow(connection).to receive(:list_disks).and_return(disk_list)
allow(connection).to receive(:list_snapshots).and_return(snapshot_list) allow(connection).to receive(:list_snapshots).and_return(snapshot_list)
@ -709,10 +710,10 @@ EOT
context 'with allowlist containing a a fully qualified label that is not pool' do context 'with allowlist containing a a fully qualified label that is not pool' do
before(:each) do before(:each) do
allow(subject).to receive(:wait_for_zone_operation) allow(subject).to receive(:wait_for_zone_operation)
$allowlist = ["user=Bob"] $allowlist = ['user=Bob']
end end
it 'should attempt to delete unconfigured instances when they dont have the allowlist label' do it 'should attempt to delete unconfigured instances when they dont have the allowlist label' do
instance_list = MockInstanceList.new(items: [MockInstance.new(name: "foo", labels: {"pool" => "not_this"})]) instance_list = MockInstanceList.new(items: [MockInstance.new(name: 'foo', labels: { 'pool' => 'not_this' })])
disk_list = MockDiskList.new(items: nil) disk_list = MockDiskList.new(items: nil)
snapshot_list = MockSnapshotList.new(items: nil) snapshot_list = MockSnapshotList.new(items: nil)
allow(connection).to receive(:list_instances).and_return(instance_list) allow(connection).to receive(:list_instances).and_return(instance_list)
@ -722,9 +723,9 @@ EOT
subject.purge_unconfigured_resources($allowlist) subject.purge_unconfigured_resources($allowlist)
end end
it 'should ignore unconfigured item when they match the fully qualified label' do it 'should ignore unconfigured item when they match the fully qualified label' do
instance_list = MockInstanceList.new(items: [MockInstance.new(name: "foo", labels: {"some" => "not_important", "user" => "bob"})]) instance_list = MockInstanceList.new(items: [MockInstance.new(name: 'foo', labels: { 'some' => 'not_important', 'user' => 'bob' })])
disk_list = MockDiskList.new(items: [MockDisk.new(name: "diskfoo", labels: {"other" => "thing", "user" => "bob"})]) disk_list = MockDiskList.new(items: [MockDisk.new(name: 'diskfoo', labels: { 'other' => 'thing', 'user' => 'bob' })])
snapshot_list = MockSnapshotList.new(items: [MockSnapshot.new(name: "snapfoo", labels: {"user" => "bob"})]) snapshot_list = MockSnapshotList.new(items: [MockSnapshot.new(name: 'snapfoo', labels: { 'user' => 'bob' })])
allow(connection).to receive(:list_instances).and_return(instance_list) allow(connection).to receive(:list_instances).and_return(instance_list)
allow(connection).to receive(:list_disks).and_return(disk_list) allow(connection).to receive(:list_disks).and_return(disk_list)
allow(connection).to receive(:list_snapshots).and_return(snapshot_list) allow(connection).to receive(:list_snapshots).and_return(snapshot_list)
@ -737,23 +738,22 @@ EOT
it 'should raise any errors' do it 'should raise any errors' do
expect(subject).to receive(:provided_pools).and_throw('mockerror') expect(subject).to receive(:provided_pools).and_throw('mockerror')
expect{ subject.purge_unconfigured_resources(nil) }.to raise_error(/mockerror/) expect { subject.purge_unconfigured_resources(nil) }.to raise_error(/mockerror/)
end end
end end
describe '#get_current_user' do describe '#get_current_user' do
it 'should downcase and replace invalid chars with dashes' do it 'should downcase and replace invalid chars with dashes' do
redis_connection_pool.with_metrics do |redis| redis_connection_pool.with_metrics do |redis|
redis.hset("vmpooler__vm__#{vmname}", 'token:user', "BOBBY.PUPPET") redis.hset("vmpooler__vm__#{vmname}", 'token:user', 'BOBBY.PUPPET')
expect(subject.get_current_user(vmname)).to eq("bobby-puppet") expect(subject.get_current_user(vmname)).to eq('bobby-puppet')
end end
end end
it 'returns "" for nil values' do it 'returns "" for nil values' do
redis_connection_pool.with_metrics do |redis| redis_connection_pool.with_metrics do |_redis|
expect(subject.get_current_user(vmname)).to eq("") expect(subject.get_current_user(vmname)).to eq('')
end end
end end
end end
end end