lib/beaker/hypervisor/google_compute.rb in beaker-google-1.0.0 vs lib/beaker/hypervisor/google_compute.rb in beaker-google-1.2.0
- old
+ new
@@ -1,14 +1,15 @@
-require 'time'
+# frozen_string_literal: true
+require 'securerandom'
+
module Beaker
# Beaker support for the Google Compute Engine.
class GoogleCompute < Beaker::Hypervisor
SLEEPWAIT = 5
- # Hours before an instance is considered a zombie
- ZOMBIE = 3
+ WINDOWS_IMAGE_PROJECT = %w[windows-cloud windows-sql-cloud].freeze
# Do some reasonable sleuthing on the SSH public key for GCE
##
# Try to find the private ssh key file
@@ -16,13 +17,16 @@
# @return [String] The file path for the private key file
#
# @raise [Error] if the private key can not be found
def find_google_ssh_private_key
private_keyfile = ENV.fetch('BEAKER_gce_ssh_public_key',
- File.join(ENV.fetch('HOME', nil), '.ssh', 'google_compute_engine'))
- private_keyfile = @options[:gce_ssh_private_key] if @options[:gce_ssh_private_key] && !File.exist?(private_keyfile)
+ File.join(Dir.home, '.ssh', 'google_compute_engine'))
+ if @options[:gce_ssh_private_key] && !File.exist?(private_keyfile)
+ private_keyfile = @options[:gce_ssh_private_key]
+ end
raise("Could not find GCE Private SSH key at '#{keyfile}'") unless File.exist?(private_keyfile)
+
@options[:gce_ssh_private_key] = private_keyfile
private_keyfile
end
##
@@ -34,10 +38,11 @@
def find_google_ssh_public_key
private_keyfile = find_google_ssh_private_key
public_keyfile = private_keyfile << '.pub'
public_keyfile = @options[:gce_ssh_public_key] if @options[:gce_ssh_public_key] && !File.exist?(public_keyfile)
raise("Could not find GCE Public SSH key at '#{keyfile}'") unless File.exist?(public_keyfile)
+
@options[:gce_ssh_public_key] = public_keyfile
public_keyfile
end
# IP is the only way we can be sure to connect
@@ -77,36 +82,57 @@
super
@options = options
@logger = options[:logger]
@hosts = google_hosts
- @firewall = ''
+ @external_firewall_name = ''
+ @internal_firewall_name = ''
@gce_helper = GoogleComputeHelper.new(options)
end
# Create and configure virtual machines in the Google Compute Engine,
# including their associated disks and firewall rules
def provision
- start = Time.now
- test_group_identifier = "beaker-#{start.to_i}-"
+ test_group_identifier = "beaker-#{SecureRandom.hex(4)}"
# set firewall to open pe ports
network = @gce_helper.get_network
- @firewall = test_group_identifier + generate_host_name
+ @external_firewall_name = "#{test_group_identifier}-external"
- @gce_helper.create_firewall(@firewall, network)
+ # Always allow ssh from anywhere as it's needed for Beaker to run
+ @gce_helper.create_firewall(
+ @external_firewall_name,
+ network,
+ allow: @options[:gce_ports] + ['22/tcp'],
+ source_ranges: ['0.0.0.0/0'],
+ target_tags: [test_group_identifier],
+ )
- @logger.debug("Created Google Compute firewall #{@firewall}")
+ @logger.debug("Created External Google Compute firewall #{@external_firewall_name}")
- @hosts.each do |host|
+ # Create a firewall that opens everything between all the hosts in this test group
+ @internal_firewall_name = "#{test_group_identifier}-internal"
+ internal_ports = ['1-65535/tcp', '1-65535/udp', '-1/icmp']
+ @gce_helper.create_firewall(
+ @internal_firewall_name,
+ network,
+ allow: internal_ports,
+ source_tags: [test_group_identifier],
+ target_tags: [test_group_identifier],
+ )
+ @logger.debug("Created test group Google Compute firewall #{@internal_firewall_name}")
+ @hosts.each do |host|
machine_type_name = ENV.fetch('BEAKER_gce_machine_type', host['gce_machine_type'])
raise "Must provide a machine type name in 'gce_machine_type'." if machine_type_name.nil?
+
# Get the GCE machine type object for this host
machine_type = @gce_helper.get_machine_type(machine_type_name)
- raise "Unable to find machine type named #{machine_type_name} in region #{@compute.default_zone}" if machine_type.nil?
+ if machine_type.nil?
+ raise "Unable to find machine type named #{machine_type_name} in region #{@compute.default_zone}"
+ end
# Find the image to use to create the new VM.
# Either `image` or `family` must be set in the configuration. Accepted formats
# for the image and family:
# - {project}/{image}
@@ -117,22 +143,22 @@
# If a {project} is not specified, default to the project provided in the
# BEAKER_gce_project environment variable
if host[:image]
image_selector = host[:image]
# Do we have a project name?
- if %r{/}.match?(image_selector)
+ if image_selector.include?('/')
image_project, image_name = image_selector.split('/')[0..1]
else
image_project = @gce_helper.options[:gce_project]
image_name = image_selector
end
img = @gce_helper.get_image(image_project, image_name)
raise "Unable to find image #{image_name} from project #{image_project}" if img.nil?
elsif host[:family]
image_selector = host[:family]
# Do we have a project name?
- if %r{/}.match?(image_selector)
+ if image_selector.include?('/')
image_project, family_name = image_selector.split('/')
else
image_project = @gce_helper.options[:gce_project]
family_name = image_selector
end
@@ -140,11 +166,11 @@
raise "Unable to find image in family #{family_name} from project #{image_project}" if img.nil?
else
raise('You must specify either :image or :family')
end
- unique_host_id = test_group_identifier + generate_host_name
+ unique_host_id = "#{test_group_identifier}-#{generate_host_name}"
boot_size = host['volume_size'] || img.disk_size_gb
# The boot disk is created as part of the instance creation
# TODO: Allow creation of other disks
@@ -153,68 +179,75 @@
# create new host name
host['vmhostname'] = unique_host_id
# add a new instance of the image
- operation = @gce_helper.create_instance(host['vmhostname'], img, machine_type, boot_size)
+ operation = @gce_helper.create_instance(host['vmhostname'], img, machine_type, boot_size, host.name)
unless operation.error.nil?
- raise "Unable to create Google Compute Instance #{host.name}: [#{operation.error.errors[0].code}] #{operation.error.errors[0].message}"
+ raise "Unable to create Google Compute Instance #{
+ host.name
+ }: [#{
+ operation.error.errors[0].code
+ }] #{
+ operation.error.errors[0].message
+ }"
end
+
@logger.debug("Created Google Compute instance for #{host.name}: #{host['vmhostname']}")
instance = @gce_helper.get_instance(host['vmhostname'])
+ @gce_helper.add_instance_tag(host['vmhostname'], test_group_identifier)
+ @logger.debug("Added network tag #{test_group_identifier} to instance")
+
# Make sure we have a non root/Adminsitor user to log in as
- if host['user'] == "root" || host['user'] == "Administrator" || host['user'].empty?
- initial_user = 'google_compute'
- else
- initial_user = host['user']
- end
+ initial_user = if host['user'] == 'root' || host['user'] == 'Administrator' || host['user'].empty?
+ 'google_compute'
+ else
+ host['user']
+ end
# add metadata to instance, if there is any to set
# mdata = format_metadata
# TODO: Set a configuration option for this to allow disabeling oslogin
mdata = [
{
key: 'ssh-keys',
- value: "#{initial_user}:#{File.read(find_google_ssh_public_key).strip}"
+ value: "#{initial_user}:#{File.read(find_google_ssh_public_key).strip}",
},
# For now oslogin needs to be disabled as there's no way to log in as root and it would
# take too much work on beaker to add sudo support to everything
{
key: 'enable-oslogin',
- value: 'FALSE'
+ value: 'FALSE',
},
]
-
+
# Check for google's default windows images and turn on ssh if found
- if image_project == "windows-cloud" || image_project == "windows-sql-cloud"
+ if WINDOWS_IMAGE_PROJECT.include?(image_project)
# Turn on SSH on GCP's default windows images
mdata << {
key: 'enable-windows-ssh',
value: 'TRUE',
}
mdata << {
key: 'sysprep-specialize-script-cmd',
- value: 'start /wait googet -noconfirm=true update && start /wait googet -noconfirm=true install google-compute-engine-ssh',
+ value: 'start /wait googet -noconfirm=true update && start /wait googet -noconfirm=true install google-compute-engine-ssh', # rubocop:disable Layout/LineLength
}
# Some versions of windows don't seem to add the OpenSSH directory to the path which prevents scp from working
mdata << {
key: 'sysprep-specialize-script-ps1',
- value: '[Environment]::SetEnvironmentVariable( "PATH", "$ENV:PATH;C:\Program Files\OpenSSH", [EnvironmentVariableTarget]::Machine )',
+ value: '[Environment]::SetEnvironmentVariable( "PATH", "$ENV:PATH;C:\Program Files\OpenSSH", [EnvironmentVariableTarget]::Machine )', # rubocop:disable Layout/LineLength
}
end
unless mdata.empty?
# Add the metadata to the host
@gce_helper.set_metadata_on_instance(host['vmhostname'], mdata)
@logger.debug("Added tags to Google Compute instance #{host.name}: #{host['vmhostname']}")
end
host['ip'] = instance.network_interfaces[0].access_configs[0].nat_ip
- # Add the new host to the firewall
- @gce_helper.add_firewall_tag(@firewall, host['vmhostname'])
-
if host['disable_root_ssh'] == true
@logger.info('Not enabling root ssh as disable_root_ssh is true')
else
real_user = host['user']
host['user'] = initial_user
@@ -233,10 +266,11 @@
end
# Shutdown and destroy virtual machines in the Google Compute Engine,
# including their associated disks and firewall rules
def cleanup
- @gce_helper.delete_firewall(@firewall)
+ @gce_helper.delete_firewall(@external_firewall_name)
+ @gce_helper.delete_firewall(@internal_firewall_name)
@hosts.each do |host|
# TODO: Delete any other disks attached during the instance creation
@gce_helper.delete_instance(host['vmhostname'])
@logger.debug("Deleted Google Compute instance #{host['vmhostname']} for #{host.name}")