beaker-google 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,27 +1,24 @@
1
- source ENV['GEM_SOURCE'] || "https://rubygems.org"
1
+ source ENV.fetch('GEM_SOURCE', 'https://rubygems.org')
2
2
 
3
3
  gemspec
4
4
 
5
-
6
-
7
5
  def location_for(place, fake_version = nil)
8
6
  if place =~ /^git:([^#]*)#(.*)/
9
- [fake_version, { :git => $1, :branch => $2, :require => false }].compact
10
- elsif place =~ /^file:\/\/(.*)/
11
- ['>= 0', { :path => File.expand_path($1), :require => false }]
7
+ [fake_version, { git: Regexp.last_match(1), branch: Regexp.last_match(2), require: false }].compact
8
+ elsif place =~ %r{^file://(.*)}
9
+ ['>= 0', { path: File.expand_path(Regexp.last_match(1)), require: false }]
12
10
  else
13
- [place, { :require => false }]
11
+ [place, { require: false }]
14
12
  end
15
13
  end
16
14
 
17
-
18
15
  # We don't put beaker in as a test dependency because we
19
16
  # don't want to create a transitive dependency
20
17
  group :acceptance_testing do
21
- gem "beaker", *location_for(ENV['BEAKER_VERSION'] || '~> 4.0')
18
+ gem 'beaker', *location_for(ENV.fetch('BEAKER_VERSION', '~> 4.0'))
22
19
  end
23
20
 
24
21
 
25
- if File.exists? "#{__FILE__}.local"
26
- eval(File.read("#{__FILE__}.local"), binding)
27
- end
22
+ # if File.exists? "#{__FILE__}.local"
23
+ # eval(File.read("#{__FILE__}.local"), binding)
24
+ # end
data/README.md CHANGED
@@ -1,40 +1,69 @@
1
1
  # beaker-google
2
2
 
3
+ [![License](https://img.shields.io/github/license/voxpupuli/beaker-google.svg)](https://github.com/voxpupuli/beaker-google/blob/master/LICENSE)
4
+ [![Test](https://github.com/voxpupuli/beaker-google/actions/workflows/ci.yml/badge.svg)](https://github.com/voxpupuli/beaker-google/actions/workflows/ci.yml)
5
+ [![codecov](https://codecov.io/gh/voxpupuli/beaker-google/branch/main/graph/badge.svg)](https://codecov.io/gh/voxpupuli/beaker-google)
6
+ [![Release](https://github.com/voxpupuli/beaker-google/actions/workflows/release.yml/badge.svg)](https://github.com/voxpupuli/beaker-google/actions/workflows/release.yml)
7
+ [![RubyGem Version](https://img.shields.io/gem/v/beaker-google.svg)](https://rubygems.org/gems/beaker-google)
8
+ [![RubyGem Downloads](https://img.shields.io/gem/dt/beaker-google.svg)](https://rubygems.org/gems/beaker-google)
9
+ [![Donated by Puppet Inc](https://img.shields.io/badge/donated%20by-Puppet%20Inc-fb7047.svg)](#transfer-notice)
10
+
3
11
  Beaker library to use the Google hypervisor
4
12
 
5
13
  # How to use this wizardry
6
14
 
7
- This is a gem that allows you to use hosts with [google compute](google_compute_engine.md) hypervisor with [Beaker](https://github.com/puppetlabs/beaker).
8
-
9
- See the [documentation](docs/manual.md) for the full manual.
15
+ This is a gem that allows you to use hosts with [Google Compute](https://cloud.google.com/compute) hypervisor with [Beaker](https://github.com/voxpupuli/beaker).
10
16
 
11
17
  Beaker will automatically load the appropriate hypervisors for any given hosts file, so as long as your project dependencies are satisfied there's nothing else to do. No need to `require` this library in your tests.
12
18
 
13
- ## With Beaker 3.x
14
- This gem is already included as [beaker dependency](https://github.com/puppetlabs/beaker/blob/master/beaker.gemspec)
15
- for you, so you don't need to do anything special to use this gem's
16
- functionality with Beaker.
17
-
18
- This library is included as a dependency of Beaker 3.x versions, so there's nothing to do.
19
-
20
19
  ## With Beaker 4.x
21
20
 
22
21
  As of Beaker 4.0, all hypervisor and DSL extension libraries have been removed and are no longer dependencies. In order to use a specific hypervisor or DSL extension library in your project, you will need to include them alongside Beaker in your Gemfile or project.gemspec. E.g.
23
22
 
24
- ~~~ruby
23
+ ```ruby
25
24
  # Gemfile
26
25
  gem 'beaker', '~>4.0'
27
- gem 'beaker-aws'
26
+ gem 'beaker-google'
28
27
  # project.gemspec
29
28
  s.add_runtime_dependency 'beaker', '~>4.0'
30
- s.add_runtime_dependency 'beaker-aws'
31
- ~~~
29
+ s.add_runtime_dependency 'beaker-google'
30
+ ```
31
+
32
+ ## Authentication
33
+
34
+ You must be authenticated to Google Compute Engine to be able to use `beaker-google`. Authentication is attempted in two different ways, and the first that succeeds is used.
35
+
36
+ - Using the environment variable [`GOOGLE_APPLICATION_CREDENTIALS`](https://cloud.google.com/docs/authentication/production#passing_variable), which points to a file containing the credentials for a GCP service account, created by `gcloud iam service-accounts keys create` (or equivalent).
37
+ - Using [Application Default Credentials](https://cloud.google.com/docs/authentication/production).
38
+
39
+ ## Configuration
40
+
41
+ The behavior of this library can be configured using either the beaker host configuration file, or environment variables.
42
+
43
+ | configuration option | required | default | description |
44
+ | -------------------- | ---------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
45
+ | gce_project | true | | The ID of the Google GCP project to host resources. |
46
+ | gce_zone | true | | The zone to place compute instances in. The region is calculated from the zone name. |
47
+ | gce_network | false | Default | The name of the network to attach to instances. If the project uses the default network, this and `gce_subnetwork` can be left empty. |
48
+ | gce_subnetwork | false | Default | THe name of the subnetwork to attach to the instances network interface. If the Default network is not used, this must be supplied. |
49
+ | gce_ssh_private_key | false | $HOME/.ssh/google_compute_engine | The file path of the private key to use to connect to instances. If using the key created by the gcloud tool, this can be left blank. |
50
+ | gce_ssh_public_key | false | <gce_ssh_private_key>.pub | The file path of the public key to upload to the instance. If left blank, attempt to use the file at `gce_ssh_private_key` with a `.pub` extension. |
51
+ | gce_machine_type | false | e2-standard-4 | The machine type to use for the instance. If the `BEAKER_gce_machine_type` environment variable is set, it will be used for all hosts. |
52
+ | volume_size | false | Source Image disk's size | The size of the boot disk for the image. If unset, the disk will be the same size as the image's boot disk. Provided size must be equal to or larger than the image's disk size. |
53
+ | image | true or `family` | | The image to use for creating this instance. It can be either in the form `{project}/{image}` to use an image in a different project, or `{image}`, which will look for the image in `gce_project`. |
54
+ | family | true or `image` | | The image family to use for creating this instance. It can be either in the form `{project}/{family}` to use an image from a family in a different project, or `{family}`, which will look for the image family in `gce_project`. The latest non-deprecated image in the family will be used. |
55
+
56
+ All the variables in the list can be set in the Beaker host configuration file, or the ones starting with `gce_` can be overridden by environment variables in the form `BEAKER_gce_...`. i.e. To override the `gce_machine_type` setting in the environment, set `BEAKER_gce_machine_type`.
57
+
58
+ # Cleanup
59
+
60
+ In cases where the beaker process is killed before finishing, it may leave resources in GCP. These resources will need to be manually deleted.
32
61
 
33
- In Beaker's next major version, the requirement for `beaker-google` will be
34
- pulled from that repo. When that happens, then the usage pattern will change.
35
- In order to use this then, you'll need to include `beaker-google` as a dependency
36
- right next to beaker itself.
62
+ | Resource Type | Name Pattern | Count |
63
+ | ------------- | ------------------- | ------------------------------------------- |
64
+ | Firewall | `beaker-<number>-*` | 1 |
65
+ | Instance | `beaker-*` | One or more depending on test configuration |
37
66
 
38
67
  # Contributing
39
68
 
40
- Please refer to puppetlabs/beaker's [contributing](https://github.com/puppetlabs/beaker/blob/master/CONTRIBUTING.md) guide.
69
+ Please refer to voxpupuli/beaker's [contributing](https://github.com/voxpupuli/beaker/blob/master/CONTRIBUTING.md) guide.
data/Rakefile CHANGED
@@ -5,65 +5,61 @@ require 'rspec/core/rake_task'
5
5
  # Documentation Tasks
6
6
  #
7
7
  ###########################################################
8
- DOCS_DAEMON = "yard server --reload --daemon --server thin"
8
+ DOCS_DAEMON = 'yard server --reload --daemon --server thin'
9
9
  FOREGROUND_SERVER = 'bundle exec yard server --reload --verbose --server thin lib/beaker'
10
10
 
11
- def running?( cmdline )
11
+ def running?(cmdline)
12
12
  ps = `ps -ef`
13
- found = ps.lines.grep( /#{Regexp.quote( cmdline )}/ )
14
- if found.length > 1
15
- raise StandardError, "Found multiple YARD Servers. Don't know what to do."
16
- end
13
+ found = ps.lines.grep(/#{Regexp.quote(cmdline)}/)
14
+ raise StandardError, "Found multiple YARD Servers. Don't know what to do." if found.length > 1
17
15
 
18
16
  yes = found.empty? ? false : true
19
- return yes, found.first
17
+ [yes, found.first]
20
18
  end
21
19
 
22
- def pid_from( output )
20
+ def pid_from(output)
23
21
  output.squeeze(' ').strip.split(' ')[1]
24
22
  end
25
23
 
26
24
  desc 'Start the documentation server in the foreground'
27
- task :docs => 'docs:clear' do
25
+ task docs: 'docs:clear' do
28
26
  original_dir = Dir.pwd
29
- Dir.chdir( File.expand_path(File.dirname(__FILE__)) )
27
+ Dir.chdir(__dir__)
30
28
  sh FOREGROUND_SERVER
31
- Dir.chdir( original_dir )
29
+ Dir.chdir(original_dir)
32
30
  end
33
31
 
34
32
  namespace :docs do
35
-
36
33
  desc 'Clear the generated documentation cache'
37
34
  task :clear do
38
35
  original_dir = Dir.pwd
39
- Dir.chdir( File.expand_path(File.dirname(__FILE__)) )
36
+ Dir.chdir(__dir__)
40
37
  sh 'rm -rf docs'
41
- Dir.chdir( original_dir )
38
+ Dir.chdir(original_dir)
42
39
  end
43
40
 
44
41
  desc 'Generate static documentation'
45
- task :gen => 'docs:clear' do
42
+ task gen: 'docs:clear' do
46
43
  original_dir = Dir.pwd
47
- Dir.chdir( File.expand_path(File.dirname(__FILE__)) )
44
+ Dir.chdir(__dir__)
48
45
  output = `bundle exec yard doc`
49
46
  puts output
50
- if output =~ /\[warn\]|\[error\]/
51
- fail "Errors/Warnings during yard documentation generation"
52
- end
53
- Dir.chdir( original_dir )
47
+ raise 'Errors/Warnings during yard documentation generation' if output =~ /\[warn\]|\[error\]/
48
+
49
+ Dir.chdir(original_dir)
54
50
  end
55
51
 
56
52
  desc 'Run the documentation server in the background, alias `bg`'
57
- task :background => 'docs:clear' do
58
- yes, output = running?( DOCS_DAEMON )
53
+ task background: 'docs:clear' do
54
+ yes, output = running?(DOCS_DAEMON)
59
55
  if yes
60
- puts "Not starting a new YARD Server..."
61
- puts "Found one running with pid #{pid_from( output )}."
56
+ puts 'Not starting a new YARD Server...'
57
+ puts "Found one running with pid #{pid_from(output)}."
62
58
  else
63
59
  original_dir = Dir.pwd
64
- Dir.chdir( File.expand_path(File.dirname(__FILE__)) )
60
+ Dir.chdir(__dir__)
65
61
  sh "bundle exec #{DOCS_DAEMON}"
66
- Dir.chdir( original_dir )
62
+ Dir.chdir(original_dir)
67
63
  end
68
64
  end
69
65
 
@@ -71,37 +67,37 @@ namespace :docs do
71
67
 
72
68
  desc 'Check the status of the documentation server'
73
69
  task :status do
74
- yes, output = running?( DOCS_DAEMON )
70
+ yes, output = running?(DOCS_DAEMON)
75
71
  if yes
76
- pid = pid_from( output )
72
+ pid = pid_from(output)
77
73
  puts "Found a YARD Server running with pid #{pid}"
78
74
  else
79
- puts "Could not find a running YARD Server."
75
+ puts 'Could not find a running YARD Server.'
80
76
  end
81
77
  end
82
78
 
83
- desc "Stop a running YARD Server"
79
+ desc 'Stop a running YARD Server'
84
80
  task :stop do
85
- yes, output = running?( DOCS_DAEMON )
81
+ yes, output = running?(DOCS_DAEMON)
86
82
  if yes
87
- pid = pid_from( output )
83
+ pid = pid_from(output)
88
84
  puts "Found a YARD Server running with pid #{pid}"
89
85
  `kill #{pid}`
90
- puts "Stopping..."
91
- yes, output = running?( DOCS_DAEMON )
86
+ puts 'Stopping...'
87
+ yes, output = running?(DOCS_DAEMON)
92
88
  if yes
93
89
  `kill -9 #{pid}`
94
- yes, output = running?( DOCS_DAEMON )
90
+ yes, output = running?(DOCS_DAEMON)
95
91
  if yes
96
- puts "Could not Stop Server!"
92
+ puts 'Could not Stop Server!'
97
93
  else
98
- puts "Server stopped."
94
+ puts 'Server stopped.'
99
95
  end
100
96
  else
101
- puts "Server stopped."
97
+ puts 'Server stopped.'
102
98
  end
103
99
  else
104
- puts "Could not find a running YARD Server"
100
+ puts 'Could not find a running YARD Server'
105
101
  end
106
102
  end
107
103
  end
@@ -5,11 +5,11 @@ require 'beaker-google/version'
5
5
  Gem::Specification.new do |s|
6
6
  s.name = "beaker-google"
7
7
  s.version = BeakerGoogle::VERSION
8
- s.authors = ["Rishi Javia, Kevin Imber, Tony Vu"]
9
- s.email = ["rishi.javia@puppet.com, kevin.imber@puppet.com, tony.vu@puppet.com"]
10
- s.homepage = "https://github.com/puppetlabs/beaker-google"
8
+ s.authors = ["Puppet", "Voxpupuli"]
9
+ s.email = ["voxpupuli@groups.io"]
10
+ s.homepage = "https://github.com/voxpupuli/beaker-google"
11
11
  s.summary = %q{Beaker DSL Extension Helpers!}
12
- s.description = %q{For use for the Beaker acceptance testing tool}
12
+ s.description = %q{Google Compute Engine support for the Beaker acceptance testing tool.}
13
13
  s.license = 'Apache2'
14
14
 
15
15
  s.files = `git ls-files`.split("\n")
@@ -17,16 +17,13 @@ Gem::Specification.new do |s|
17
17
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
18
  s.require_paths = ["lib"]
19
19
 
20
+ s.required_ruby_version = Gem::Requirement.new('>= 2.4')
21
+
20
22
  # Testing dependencies
21
23
  s.add_development_dependency 'rspec', '~> 3.0'
22
24
  s.add_development_dependency 'rspec-its'
23
- # pin fakefs for Ruby < 2.3
24
- if RUBY_VERSION < "2.3"
25
- s.add_development_dependency 'fakefs', '~> 0.6', '< 0.14'
26
- else
27
- s.add_development_dependency 'fakefs', '~> 0.6'
28
- end
29
- s.add_development_dependency 'rake', '~> 10.1'
25
+ s.add_development_dependency 'fakefs', '~> 1.8'
26
+ s.add_development_dependency 'rake', '~> 13.0'
30
27
  s.add_development_dependency 'simplecov'
31
28
  s.add_development_dependency 'pry', '~> 0.10'
32
29
 
@@ -36,7 +33,8 @@ Gem::Specification.new do |s|
36
33
 
37
34
  # Run time dependencies
38
35
  s.add_runtime_dependency 'stringify-hash', '~> 0.0.0'
39
- s.add_runtime_dependency 'google-api-client', '~> 0.8'
40
36
 
37
+ s.add_runtime_dependency 'google-apis-compute_v1', '~> 0.1'
38
+ s.add_runtime_dependency 'google-apis-oslogin_v1', '~> 0.1'
39
+ s.add_runtime_dependency 'googleauth', '~> 1.2'
41
40
  end
42
-
data/bin/beaker-google CHANGED
@@ -4,7 +4,7 @@ require 'rubygems' unless defined?(Gem)
4
4
  require 'beaker-google'
5
5
 
6
6
  VERSION_STRING =
7
- "
7
+ "
8
8
  _ .--.
9
9
  ( ` )
10
10
  beaker-google .-' `--,
@@ -25,8 +25,6 @@ VERSION_STRING =
25
25
  '=='
26
26
  "
27
27
 
28
-
29
-
30
28
  puts BeakerGoogle::VERSION
31
29
 
32
30
  exit 0
@@ -1,37 +1,52 @@
1
1
  require 'time'
2
2
 
3
3
  module Beaker
4
-
5
4
  # Beaker support for the Google Compute Engine.
6
5
  class GoogleCompute < Beaker::Hypervisor
7
-
8
6
  SLEEPWAIT = 5
9
7
 
10
8
  # Hours before an instance is considered a zombie
11
9
  ZOMBIE = 3
12
10
 
13
11
  # Do some reasonable sleuthing on the SSH public key for GCE
14
- def find_google_ssh_public_key
15
- keyfile = ENV.fetch('BEAKER_gce_ssh_public_key', File.join(ENV['HOME'], '.ssh', 'google_compute_engine.pub'))
16
12
 
17
- if @options[:gce_ssh_public_key] && !File.exist?(keyfile)
18
- keyfile = @options[:gce_ssh_public_key]
19
- end
20
-
21
- raise("Could not find GCE Public SSH Key at '#{keyfile}'") unless File.exist?(keyfile)
13
+ ##
14
+ # Try to find the private ssh key file
15
+ #
16
+ # @return [String] The file path for the private key file
17
+ #
18
+ # @raise [Error] if the private key can not be found
19
+ def find_google_ssh_private_key
20
+ private_keyfile = ENV.fetch('BEAKER_gce_ssh_public_key',
21
+ File.join(ENV.fetch('HOME', nil), '.ssh', 'google_compute_engine'))
22
+ private_keyfile = @options[:gce_ssh_private_key] if @options[:gce_ssh_private_key] && !File.exist?(private_keyfile)
23
+ raise("Could not find GCE Private SSH key at '#{keyfile}'") unless File.exist?(private_keyfile)
24
+ @options[:gce_ssh_private_key] = private_keyfile
25
+ private_keyfile
26
+ end
22
27
 
23
- return keyfile
28
+ ##
29
+ # Try to find the public key file based on the location of the private key or provided data
30
+ #
31
+ # @return [String] The file path for the public key file
32
+ #
33
+ # @raise [Error] if the public key can not be found
34
+ def find_google_ssh_public_key
35
+ private_keyfile = find_google_ssh_private_key
36
+ public_keyfile = private_keyfile << '.pub'
37
+ public_keyfile = @options[:gce_ssh_public_key] if @options[:gce_ssh_public_key] && !File.exist?(public_keyfile)
38
+ raise("Could not find GCE Public SSH key at '#{keyfile}'") unless File.exist?(public_keyfile)
39
+ @options[:gce_ssh_public_key] = public_keyfile
40
+ public_keyfile
24
41
  end
25
42
 
26
- # Create the array of metaData, each member being a hash with a :key and a
27
- # :value. Sets :department, :project and :jenkins_build_url.
28
- def format_metadata
29
- [ {:key => :department, :value => @options[:department]},
30
- {:key => :project, :value => @options[:project]},
31
- {:key => :jenkins_build_url, :value => @options[:jenkins_build_url]},
32
- {:key => :sshKeys, :value => "google_compute:#{File.read(find_google_ssh_public_key).strip}" }
33
- ].delete_if { |member| member[:value].nil? or member[:value].empty?}
43
+ # IP is the only way we can be sure to connect
44
+ # TODO: This isn't being called
45
+ # rubocop:disable Lint/UnusedMethodArgument
46
+ def connection_preference(host)
47
+ [:ip]
34
48
  end
49
+ # rubocop:enable Lint/UnusedMethodArgument
35
50
 
36
51
  # Create a new instance of the Google Compute Engine hypervisor object
37
52
  #
@@ -60,6 +75,7 @@ module Beaker
60
75
  def initialize(google_hosts, options)
61
76
  require 'beaker/hypervisor/google_compute_helper'
62
77
 
78
+ super
63
79
  @options = options
64
80
  @logger = options[:logger]
65
81
  @hosts = google_hosts
@@ -70,67 +86,127 @@ module Beaker
70
86
  # Create and configure virtual machines in the Google Compute Engine,
71
87
  # including their associated disks and firewall rules
72
88
  def provision
73
- attempts = @options[:timeout].to_i / SLEEPWAIT
74
89
  start = Time.now
75
-
76
90
  test_group_identifier = "beaker-#{start.to_i}-"
77
91
 
78
- # get machineType resource, used by all instances
79
- machineType = @gce_helper.get_machineType(start, attempts)
80
-
81
92
  # set firewall to open pe ports
82
- network = @gce_helper.get_network(start, attempts)
93
+ network = @gce_helper.get_network
94
+
83
95
  @firewall = test_group_identifier + generate_host_name
84
- @gce_helper.create_firewall(@firewall, network, start, attempts)
85
96
 
86
- @logger.debug("Created Google Compute firewall #{@firewall}")
97
+ @gce_helper.create_firewall(@firewall, network)
87
98
 
99
+ @logger.debug("Created Google Compute firewall #{@firewall}")
88
100
 
89
101
  @hosts.each do |host|
102
+
103
+ machine_type_name = ENV.fetch('BEAKER_gce_machine_type', host['gce_machine_type'])
104
+ raise "Must provide a machine type name in 'gce_machine_type'." if machine_type_name.nil?
105
+ # Get the GCE machine type object for this host
106
+ machine_type = @gce_helper.get_machine_type(machine_type_name)
107
+ raise "Unable to find machine type named #{machine_type_name} in region #{@compute.default_zone}" if machine_type.nil?
108
+
109
+ # Find the image to use to create the new VM.
110
+ # Either `image` or `family` must be set in the configuration. Accepted formats
111
+ # for the image and family:
112
+ # - {project}/{image}
113
+ # - {project}/{family}
114
+ # - {image}
115
+ # - {family}
116
+ #
117
+ # If a {project} is not specified, default to the project provided in the
118
+ # BEAKER_gce_project environment variable
90
119
  if host[:image]
91
- gplatform = host[:image]
92
- elsif host[:platform]
93
- gplatform = Platform.new(host[:platform])
120
+ image_selector = host[:image]
121
+ # Do we have a project name?
122
+ if %r{/}.match?(image_selector)
123
+ image_project, image_name = image_selector.split('/')[0..1]
124
+ else
125
+ image_project = @gce_helper.options[:gce_project]
126
+ image_name = image_selector
127
+ end
128
+ img = @gce_helper.get_image(image_project, image_name)
129
+ raise "Unable to find image #{image_name} from project #{image_project}" if img.nil?
130
+ elsif host[:family]
131
+ image_selector = host[:family]
132
+ # Do we have a project name?
133
+ if %r{/}.match?(image_selector)
134
+ image_project, family_name = image_selector.split('/')
135
+ else
136
+ image_project = @gce_helper.options[:gce_project]
137
+ family_name = image_selector
138
+ end
139
+ img = @gce_helper.get_latest_image_from_family(image_project, family_name)
140
+ raise "Unable to find image in family #{family_name} from project #{image_project}" if img.nil?
94
141
  else
95
- raise('You must specify either :image or :platform, or both as necessary')
142
+ raise('You must specify either :image or :family')
96
143
  end
97
144
 
98
- img = @gce_helper.get_latest_image(gplatform, start, attempts)
99
-
100
145
  unique_host_id = test_group_identifier + generate_host_name
101
146
 
102
- host['diskname'] = unique_host_id
103
- disk = @gce_helper.create_disk(host['diskname'], img, start, attempts)
104
- @logger.debug("Created Google Compute disk for #{host.name}: #{host['diskname']}")
147
+ boot_size = host['volume_size'] || img.disk_size_gb
148
+
149
+ # The boot disk is created as part of the instance creation
150
+ # TODO: Allow creation of other disks
151
+ # disk = @gce_helper.create_disk(host["diskname"], img, size)
152
+ # @logger.debug("Created Google Compute disk for #{host.name}: #{host["diskname"]}")
105
153
 
106
154
  # create new host name
107
155
  host['vmhostname'] = unique_host_id
108
- #add a new instance of the image
109
- instance = @gce_helper.create_instance(host['vmhostname'], img, machineType, disk, start, attempts)
156
+
157
+ # add a new instance of the image
158
+ operation = @gce_helper.create_instance(host['vmhostname'], img, machine_type, boot_size)
159
+ unless operation.error.nil?
160
+ raise "Unable to create Google Compute Instance #{host.name}: [#{operation.error.errors[0].code}] #{operation.error.errors[0].message}"
161
+ end
110
162
  @logger.debug("Created Google Compute instance for #{host.name}: #{host['vmhostname']}")
163
+ instance = @gce_helper.get_instance(host['vmhostname'])
111
164
 
112
165
  # add metadata to instance, if there is any to set
113
- mdata = format_metadata
114
- unless mdata.empty?
115
- @gce_helper.setMetadata_on_instance(host['vmhostname'], instance['metadata']['fingerprint'],
116
- mdata,
117
- start, attempts)
118
- @logger.debug("Added tags to Google Compute instance #{host.name}: #{host['vmhostname']}")
119
- end
166
+ # mdata = format_metadata
167
+ # TODO: Set a configuration option for this to allow disabeling oslogin
168
+ mdata = [
169
+ {
170
+ key: 'ssh-keys',
171
+ value: "google_compute:#{File.read(find_google_ssh_public_key).strip}"
172
+ },
173
+ # For now oslogin needs to be disabled as there's no way to log in as root and it would
174
+ # take too much work on beaker to add sudo support to everything
175
+ {
176
+ key: 'enable-oslogin',
177
+ value: 'FALSE'
178
+ },
179
+ ]
180
+ next if mdata.empty?
181
+ # Add the metadata to the host
182
+ @gce_helper.set_metadata_on_instance(host['vmhostname'], mdata)
183
+ @logger.debug("Added tags to Google Compute instance #{host.name}: #{host['vmhostname']}")
184
+
185
+ host['ip'] = instance.network_interfaces[0].access_configs[0].nat_ip
186
+
187
+ # Add the new host to the firewall
188
+ @gce_helper.add_firewall_tag(@firewall, host['vmhostname'])
189
+
190
+ if host['disable_root_ssh'] == true
191
+ @logger.info('Not enabling root ssh as disable_root_ssh is true')
192
+ else
120
193
 
121
- # get ip for this host
122
- host['ip'] = instance['networkInterfaces'][0]['accessConfigs'][0]['natIP']
194
+ # # configure ssh
195
+ default_user = host['user']
123
196
 
124
- # configure ssh
125
- default_user = host['user']
126
- host['user'] = 'google_compute'
197
+ # TODO: Pull this out into a configuration option or something
198
+ host['user'] = 'google_compute'
127
199
 
128
- copy_ssh_to_root(host, @options)
129
- enable_root_login(host, @options)
130
- host['user'] = default_user
200
+ # Set the ssh private key we need to use
201
+ host.options['ssh']['keys'] = [find_google_ssh_private_key]
131
202
 
132
- # shut down connection, will reconnect on next exec
133
- host.close
203
+ copy_ssh_to_root(host, @options)
204
+ enable_root_login(host, @options)
205
+ host['user'] = default_user
206
+
207
+ # shut down connection, will reconnect on next exec
208
+ host.close
209
+ end
134
210
 
135
211
  @logger.debug("Instance ready: #{host['vmhostname']} for #{host.name}}")
136
212
  end
@@ -138,70 +214,13 @@ module Beaker
138
214
 
139
215
  # Shutdown and destroy virtual machines in the Google Compute Engine,
140
216
  # including their associated disks and firewall rules
141
- def cleanup()
142
- attempts = @options[:timeout].to_i / SLEEPWAIT
143
- start = Time.now
144
-
145
- @gce_helper.delete_firewall(@firewall, start, attempts)
217
+ def cleanup
218
+ @gce_helper.delete_firewall(@firewall)
146
219
 
147
220
  @hosts.each do |host|
148
- @gce_helper.delete_instance(host['vmhostname'], start, attempts)
221
+ # TODO: Delete any other disks attached during the instance creation
222
+ @gce_helper.delete_instance(host['vmhostname'])
149
223
  @logger.debug("Deleted Google Compute instance #{host['vmhostname']} for #{host.name}")
150
- @gce_helper.delete_disk(host['diskname'], start, attempts)
151
- @logger.debug("Deleted Google Compute disk #{host['diskname']} for #{host.name}")
152
- end
153
-
154
- end
155
-
156
- # Shutdown and destroy Google Compute instances (including their associated
157
- # disks and firewall rules) that have been alive longer than ZOMBIE hours.
158
- def kill_zombies(max_age = ZOMBIE)
159
- now = start = Time.now
160
- attempts = @options[:timeout].to_i / SLEEPWAIT
161
-
162
- # get rid of old instances
163
- instances = @gce_helper.list_instances(start, attempts)
164
- if instances
165
- instances.each do |instance|
166
- created = Time.parse(instance['creationTimestamp'])
167
- alive = (now - created )/60/60
168
- if alive >= max_age
169
- #kill it with fire!
170
- @logger.debug("Deleting zombie instance #{instance['name']}")
171
- @gce_helper.delete_instance( instance['name'], start, attempts )
172
- end
173
- end
174
- else
175
- @logger.debug("No zombie instances found")
176
- end
177
-
178
- # get rid of old disks
179
- disks = @gce_helper.list_disks(start, attempts)
180
- if disks
181
- disks.each do |disk|
182
- created = Time.parse(disk['creationTimestamp'])
183
- alive = (now - created )/60/60
184
- if alive >= max_age
185
-
186
- # kill it with fire!
187
- @logger.debug("Deleting zombie disk #{disk['name']}")
188
- @gce_helper.delete_disk( disk['name'], start, attempts )
189
- end
190
- end
191
- else
192
- @logger.debug("No zombie disks found")
193
- end
194
-
195
- # get rid of non-default firewalls
196
- firewalls = @gce_helper.list_firewalls( start, attempts)
197
-
198
- if firewalls && !firewalls.empty?
199
- firewalls.each do |firewall|
200
- @logger.debug("Deleting non-default firewall #{firewall['name']}")
201
- @gce_helper.delete_firewall( firewall['name'], start, attempts )
202
- end
203
- else
204
- @logger.debug("No zombie firewalls found")
205
224
  end
206
225
  end
207
226
  end