beaker-google 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.github/release.yml +41 -0
  3. data/.github/workflows/ci.yml +11 -8
  4. data/.github/workflows/release.yml +91 -17
  5. data/.rubocop.yml +2 -0
  6. data/.rubocop_todo.yml +20 -0
  7. data/CHANGELOG.md +32 -0
  8. data/Gemfile +8 -0
  9. data/README.md +2 -2
  10. data/Rakefile +7 -0
  11. data/beaker-google.gemspec +9 -6
  12. data/lib/beaker/hypervisor/google_compute.rb +4 -4
  13. data/lib/beaker/hypervisor/google_compute_helper.rb +15 -5
  14. data/lib/beaker-google/version.rb +1 -1
  15. data/spec/classes/beaker/google_compute_constants_and_defaults_spec.rb +141 -0
  16. data/spec/classes/beaker/google_compute_helper_api_version_spec.rb +37 -0
  17. data/spec/classes/beaker/google_compute_helper_configuration_validation_spec.rb +100 -0
  18. data/spec/classes/beaker/google_compute_helper_constants_and_defaults_spec.rb +66 -0
  19. data/spec/classes/beaker/google_compute_helper_edge_cases_spec.rb +173 -0
  20. data/spec/classes/beaker/google_compute_helper_error_handling_spec.rb +105 -0
  21. data/spec/classes/beaker/google_compute_helper_spec.rb +368 -0
  22. data/spec/classes/beaker/google_compute_helper_utilities_spec.rb +95 -0
  23. data/spec/classes/beaker/google_compute_initialization_spec.rb +52 -0
  24. data/spec/classes/beaker/google_compute_provision_spec.rb +68 -0
  25. data/spec/classes/beaker/google_compute_spec.rb +95 -0
  26. data/spec/classes/beaker/google_compute_ssh_key_management_spec.rb +115 -0
  27. data/spec/classes/beaker/google_spec.rb +68 -0
  28. data/spec/classes/beaker-google/beaker_google_version_spec.rb +19 -0
  29. data/spec/classes/beaker_integration_spec.rb +67 -0
  30. data/spec/spec_helper.rb +45 -0
  31. data/spec/support/google_compute_shared_setup.rb +138 -0
  32. data/spec/support/shared_mocks.rb +89 -0
  33. metadata +59 -15
  34. data/.github/workflows/codeql-analysis.yml +0 -70
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ffc4ea5302c98fad23ea68232a5903995e0cc420be84ce68910846ef387ea66
4
- data.tar.gz: 1d0a898f54cca4d1033bbbea4ebf1ec30ed9b78f304e853edecbc4b299874fde
3
+ metadata.gz: ad1087b2a3feb7c707d5e76d9a7290c1aee3c7b7d70d976f547547332bf759c2
4
+ data.tar.gz: 0545ab3de6ddcf6ff30e5a40185acdeb8c840bd89656af1b8cd257b46e67e0a0
5
5
  SHA512:
6
- metadata.gz: 8fbdf6e207d365a19348ecba021d169a6da1ac9daf8bdf76257c7bb74de293e19e070acf5071954af0b7db8c2ae5e65af35fe8c2719221741206d9a99fde6a36
7
- data.tar.gz: cc44c5be51e602b046338e878b59e4582ae14233c105b2de9fc5115f6d6c935830e845d1dae8038ae4bb4179e879d1481d22f294dc4d701ca66d5074c476fdd3
6
+ metadata.gz: 3b0be0f5e35f3810a556687d6fb4724b0e40890f920b8c410fe9d87a57c13a51371156c694cedde8313f0b5263ec087ac90c1eb8aaed8b5ed67682dd827d4028
7
+ data.tar.gz: 8332beb8864b708e07a613f84b06ae82dcce3c505059d7a374af5434a1bf51a0e35b06178b7eafaaf431aa0eb270847c7ed87994e73644faf7256517104d2386
@@ -0,0 +1,41 @@
1
+ ---
2
+ # https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes
3
+
4
+ changelog:
5
+ exclude:
6
+ labels:
7
+ - duplicate
8
+ - invalid
9
+ - modulesync
10
+ - question
11
+ - skip-changelog
12
+ - wont-fix
13
+ - wontfix
14
+ - github_actions
15
+
16
+ categories:
17
+ - title: Breaking Changes 🛠
18
+ labels:
19
+ - backwards-incompatible
20
+
21
+ - title: New Features 🎉
22
+ labels:
23
+ - enhancement
24
+
25
+ - title: Bug Fixes 🐛
26
+ labels:
27
+ - bug
28
+ - bugfix
29
+
30
+ - title: Documentation Updates 📚
31
+ labels:
32
+ - documentation
33
+ - docs
34
+
35
+ - title: Dependency Updates ⬆️
36
+ labels:
37
+ - dependencies
38
+
39
+ - title: Other Changes
40
+ labels:
41
+ - "*"
@@ -3,6 +3,9 @@ name: Test
3
3
  on:
4
4
  - pull_request
5
5
 
6
+ permissions:
7
+ contents: read
8
+
6
9
  env:
7
10
  BUNDLE_WITHOUT: release
8
11
 
@@ -10,11 +13,11 @@ jobs:
10
13
  rubocop:
11
14
  runs-on: ubuntu-latest
12
15
  steps:
13
- - uses: actions/checkout@v4
14
- - name: Install Ruby 3.3
16
+ - uses: actions/checkout@v7
17
+ - name: Install Ruby 3.4
15
18
  uses: ruby/setup-ruby@v1
16
19
  with:
17
- ruby-version: "3.3"
20
+ ruby-version: "3.4"
18
21
  bundler-cache: true
19
22
  - name: Run rubocop
20
23
  run: bundle exec rake rubocop
@@ -27,14 +30,15 @@ jobs:
27
30
  - ruby: "2.7"
28
31
  - ruby: "3.0"
29
32
  - ruby: "3.1"
30
- coverage: "yes"
31
33
  - ruby: "3.2"
32
34
  - ruby: "3.3"
35
+ - ruby: "3.4"
36
+ coverage: "yes"
33
37
  env:
34
38
  COVERAGE: ${{ matrix.coverage }}
35
39
  name: Ruby ${{ matrix.ruby }}
36
40
  steps:
37
- - uses: actions/checkout@v4
41
+ - uses: actions/checkout@v7
38
42
  - name: Install Ruby ${{ matrix.ruby }}
39
43
  uses: ruby/setup-ruby@v1
40
44
  with:
@@ -42,9 +46,8 @@ jobs:
42
46
  bundler-cache: true
43
47
  - name: Verify gem builds
44
48
  run: gem build --strict --verbose *.gemspec
45
- # Currently no tests are defined in this gem
46
- # - name: Run spec tests
47
- # run: bundle exec rake
49
+ - name: Run spec tests
50
+ run: bundle exec rake
48
51
 
49
52
  tests:
50
53
  needs:
@@ -1,32 +1,106 @@
1
- name: Release
1
+ ---
2
+ name: Gem Release
2
3
 
3
4
  on:
4
5
  push:
5
6
  tags:
6
7
  - '*'
7
8
 
9
+ permissions: {}
10
+
8
11
  jobs:
9
- release:
10
- runs-on: ubuntu-latest
12
+ build-release:
13
+ # Prevent releases from forked repositories
11
14
  if: github.repository_owner == 'voxpupuli'
15
+ name: Build the gem
16
+ runs-on: ubuntu-24.04
12
17
  steps:
13
- - uses: actions/checkout@v4
14
- - name: Install Ruby 3.1
18
+ - uses: actions/checkout@v7
19
+ - name: Install Ruby
15
20
  uses: ruby/setup-ruby@v1
16
21
  with:
17
- ruby-version: '3.1'
18
- env:
19
- BUNDLE_WITHOUT: release
22
+ ruby-version: 'ruby'
20
23
  - name: Build gem
21
- run: gem build --strict --verbose *.gemspec
24
+ shell: bash
25
+ run: gem build --verbose *.gemspec
26
+ - name: Upload gem to GitHub cache
27
+ uses: actions/upload-artifact@v7
28
+ with:
29
+ name: gem-artifact
30
+ path: '*.gem'
31
+ retention-days: 1
32
+ compression-level: 0
33
+
34
+ create-github-release:
35
+ needs: build-release
36
+ name: Create GitHub release
37
+ runs-on: ubuntu-24.04
38
+ permissions:
39
+ contents: write # clone repo and create release
40
+ steps:
41
+ - name: Download gem from GitHub cache
42
+ uses: actions/download-artifact@v8
43
+ with:
44
+ name: gem-artifact
45
+ - name: Create Release
46
+ shell: bash
47
+ env:
48
+ GH_TOKEN: ${{ github.token }}
49
+ run: gh release create --repo ${{ github.repository }} ${{ github.ref_name }} --generate-notes *.gem
50
+
51
+ release-to-github:
52
+ needs: build-release
53
+ name: Release to GitHub
54
+ runs-on: ubuntu-24.04
55
+ permissions:
56
+ packages: write # publish to rubygems.pkg.github.com
57
+ steps:
58
+ - name: Download gem from GitHub cache
59
+ uses: actions/download-artifact@v8
60
+ with:
61
+ name: gem-artifact
62
+ - name: Publish gem to GitHub packages
63
+ run: gem push --host https://rubygems.pkg.github.com/${{ github.repository_owner }} *.gem
64
+ env:
65
+ GEM_HOST_API_KEY: ${{ secrets.GITHUB_TOKEN }}
66
+
67
+ release-to-rubygems:
68
+ needs: build-release
69
+ name: Release gem to rubygems.org
70
+ runs-on: ubuntu-24.04
71
+ environment: release # recommended by rubygems.org
72
+ permissions:
73
+ id-token: write # rubygems.org authentication
74
+ steps:
75
+ - name: Download gem from GitHub cache
76
+ uses: actions/download-artifact@v8
77
+ with:
78
+ name: gem-artifact
79
+ - uses: rubygems/configure-rubygems-credentials@v2.1.0
22
80
  - name: Publish gem to rubygems.org
81
+ shell: bash
23
82
  run: gem push *.gem
24
- env:
25
- GEM_HOST_API_KEY: '${{ secrets.RUBYGEMS_AUTH_TOKEN }}'
26
- - name: Setup GitHub packages access
83
+
84
+ release-verification:
85
+ name: Check that all releases are done
86
+ runs-on: ubuntu-24.04
87
+ permissions:
88
+ contents: read # minimal permissions that we have to grant
89
+ needs:
90
+ - create-github-release
91
+ - release-to-github
92
+ - release-to-rubygems
93
+ steps:
94
+ - name: Download gem from GitHub cache
95
+ uses: actions/download-artifact@v8
96
+ with:
97
+ name: gem-artifact
98
+ - name: Install Ruby
99
+ uses: ruby/setup-ruby@v1
100
+ with:
101
+ ruby-version: 'ruby'
102
+ - name: Wait for release to propagate
103
+ shell: bash
27
104
  run: |
28
- mkdir -p ~/.gem
29
- echo ":github: Bearer ${{ secrets.GITHUB_TOKEN }}" >> ~/.gem/credentials
30
- chmod 0600 ~/.gem/credentials
31
- - name: Publish gem to GitHub packages
32
- run: gem push --key github --host https://rubygems.pkg.github.com/voxpupuli *.gem
105
+ gem install rubygems-await
106
+ gem await *.gem
data/.rubocop.yml CHANGED
@@ -1,3 +1,5 @@
1
1
  ---
2
+ inherit_from: .rubocop_todo.yml
3
+
2
4
  inherit_gem:
3
5
  voxpupuli-rubocop: rubocop.yml
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,20 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config --no-auto-gen-timestamp`
3
+ # using RuboCop version 1.75.8.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ # This cop supports safe autocorrection (--autocorrect).
11
+ # Configuration parameters: AutoCorrect, CheckForMethodsWithNoSideEffects.
12
+ Lint/Void:
13
+ Exclude:
14
+ - 'lib/beaker/hypervisor/google_compute_helper.rb'
15
+
16
+ # Offense count: 2
17
+ # Configuration parameters: MinSize.
18
+ Performance/CollectionLiteralInLoop:
19
+ Exclude:
20
+ - 'lib/beaker/hypervisor/google_compute_helper.rb'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.2.0](https://github.com/voxpupuli/beaker-google/tree/1.2.0) (2026-11-03)
4
+
5
+ [Full Changelog](https://github.com/voxpupuli/beaker-google/compare/1.2.0...1.2.0)
6
+
7
+ **Fixed bugs:**
8
+
9
+ - Let a non-empty BEAKER\_gce\_ports replace configured gce\_ports [\#74](https://github.com/voxpupuli/beaker-google/pull/74) ([jaevans](https://github.com/jaevans))
10
+
11
+ **Closed issues:**
12
+
13
+ - Compute calls fail with "Invalid API version '2026-11-01'" since google-apis-compute\_v1 0.157.0 [\#76](https://github.com/voxpupuli/beaker-google/issues/76)
14
+
15
+ **Merged pull requests:**
16
+
17
+ - Pin the Compute API version sent to Compute Engine [\#77](https://github.com/voxpupuli/beaker-google/pull/77) ([michael-riddle](https://github.com/michael-riddle))
18
+ - Remove CodeQL workflow in favor of default setup [\#75](https://github.com/voxpupuli/beaker-google/pull/75) ([jaevans](https://github.com/jaevans))
19
+ - Bump actions/checkout from 6 to 7 [\#71](https://github.com/voxpupuli/beaker-google/pull/71) ([dependabot[bot]](https://github.com/apps/dependabot))
20
+ - Bump rubygems/configure-rubygems-credentials from 1.0.0 to 2.1.0 [\#70](https://github.com/voxpupuli/beaker-google/pull/70) ([dependabot[bot]](https://github.com/apps/dependabot))
21
+ - Bump actions/download-artifact from 7 to 8 [\#68](https://github.com/voxpupuli/beaker-google/pull/68) ([dependabot[bot]](https://github.com/apps/dependabot))
22
+ - Bump actions/upload-artifact from 6 to 7 [\#67](https://github.com/voxpupuli/beaker-google/pull/67) ([dependabot[bot]](https://github.com/apps/dependabot))
23
+ - Bump actions/upload-artifact from 5 to 6 [\#66](https://github.com/voxpupuli/beaker-google/pull/66) ([dependabot[bot]](https://github.com/apps/dependabot))
24
+ - Bump actions/download-artifact from 6 to 7 [\#65](https://github.com/voxpupuli/beaker-google/pull/65) ([dependabot[bot]](https://github.com/apps/dependabot))
25
+ - Bump actions/checkout from 5 to 6 [\#64](https://github.com/voxpupuli/beaker-google/pull/64) ([dependabot[bot]](https://github.com/apps/dependabot))
26
+ - Bump actions/upload-artifact from 4 to 5 [\#63](https://github.com/voxpupuli/beaker-google/pull/63) ([dependabot[bot]](https://github.com/apps/dependabot))
27
+ - Bump actions/download-artifact from 5 to 6 [\#62](https://github.com/voxpupuli/beaker-google/pull/62) ([dependabot[bot]](https://github.com/apps/dependabot))
28
+ - Bump github/codeql-action from 3 to 4 [\#61](https://github.com/voxpupuli/beaker-google/pull/61) ([dependabot[bot]](https://github.com/apps/dependabot))
29
+ - Bump actions/checkout from 4 to 5 [\#60](https://github.com/voxpupuli/beaker-google/pull/60) ([dependabot[bot]](https://github.com/apps/dependabot))
30
+ - Update thin requirement from ~\> 1.8, \>= 1.8.2 to \>= 1.8.2, \< 3 [\#59](https://github.com/voxpupuli/beaker-google/pull/59) ([dependabot[bot]](https://github.com/apps/dependabot))
31
+ - Add rspec tests [\#57](https://github.com/voxpupuli/beaker-google/pull/57) ([silug](https://github.com/silug))
32
+ - Update voxpupuli-rubocop requirement from ~\> 2.8.0 to ~\> 3.1.0 [\#56](https://github.com/voxpupuli/beaker-google/pull/56) ([dependabot[bot]](https://github.com/apps/dependabot))
33
+ - Update voxpupuli-rubocop requirement from ~\> 2.7.0 to ~\> 2.8.0 [\#55](https://github.com/voxpupuli/beaker-google/pull/55) ([dependabot[bot]](https://github.com/apps/dependabot))
34
+
3
35
  ## [1.2.0](https://github.com/voxpupuli/beaker-google/tree/1.2.0) (2024-06-07)
4
36
 
5
37
  [Full Changelog](https://github.com/voxpupuli/beaker-google/compare/1.1.0...1.2.0)
data/Gemfile CHANGED
@@ -20,6 +20,14 @@ group :acceptance_testing do
20
20
  gem 'beaker', *location_for(ENV.fetch('BEAKER_VERSION', '~> 4.0'))
21
21
  end
22
22
 
23
+ # For Ruby 3.4 compatibility
24
+ group :development, :test do
25
+ gem 'benchmark', '~> 0.1'
26
+ gem 'irb', '~> 1.2'
27
+ gem 'mutex_m', '~> 0.1'
28
+ gem 'pstore', '~> 0.1'
29
+ end
30
+
23
31
  group :release, optional: true do
24
32
  gem 'faraday-retry', '~> 2.1', require: false
25
33
  gem 'github_changelog_generator', '~> 1.16.4', require: false
data/README.md CHANGED
@@ -46,7 +46,7 @@ The behavior of this library can be configured using either the beaker host conf
46
46
  | gce_zone | true | | The zone to place compute instances in. The region is calculated from the zone name. |
47
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
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_ports | false | `[ ]` | A comma separated list of ports to add to the external firewall. Each port is specified in the format `number/protocol` where protocol is one of `tcp`, `udp`, `icmp`, `esp`, `ah`, `ipip`, or `sctp`. **NOTE:** Port `22/tcp` is required for beaker to function and is automatically added to the firewall. |
49
+ | gce_ports | false | `[ ]` | A list, or a comma separated string, of ports to add to the external firewall. A non-empty `BEAKER_gce_ports` replaces it. Each port is specified in the format `number/protocol` where protocol is one of `tcp`, `udp`, `icmp`, `esp`, `ah`, `ipip`, or `sctp`. **NOTE:** Port `22/tcp` is required for beaker to function and is automatically added to the firewall. |
50
50
  | 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. |
51
51
  | 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. |
52
52
  | 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. |
@@ -62,7 +62,7 @@ Each run of beaker creates a pair of firewalls to protect the hosts, and interna
62
62
 
63
63
  The internal firewall allows all communication between hosts in the node set, while keeping them isolated from any other Beaker jobs that may be running in the same environment. This firewall is attached to all hosts in the test set, and allows all `tcp`, `udp`, and `icmp` traffic.
64
64
 
65
- The external firewall allows outside communication from the internet into the hosts in the test. Due to constrains of the Beaker system, the firewall accepts any source IP (`0.0.0.0/0`) and the SSH port (`22/tcp`) is always allowed. Other ports may be added by using the `gce_ports` configuration option or the `BEAKER_gce_ports` environment variable as a comma separated list of `port/proto` values where `port` is the port number or range, and proto is one of `tcp`, `udp`, `icmp`, `esp`, `ah`, `ipip`, or `sctp`. The port number is only used for `tcp`, `udp`, and `sctp` protocols. Any value, such as `-1` can be used for the other protocols.
65
+ The external firewall allows outside communication from the internet into the hosts in the test. Due to constrains of the Beaker system, the firewall accepts any source IP (`0.0.0.0/0`) and the SSH port (`22/tcp`) is always allowed. Other ports may be opened with the `gce_ports` configuration option, or with the `BEAKER_gce_ports` environment variable, which takes the place of `gce_ports` when both are set. Either takes a list, or a comma separated string, of `port/proto` values where `port` is the port number or range, and proto is one of `tcp`, `udp`, `icmp`, `esp`, `ah`, `ipip`, or `sctp`. The port number is only used for `tcp`, `udp`, and `sctp` protocols. Any value, such as `-1` can be used for the other protocols. A non-empty `BEAKER_gce_ports` replaces the configured list rather than adding to it, so it must name every port the run needs besides `22/tcp`.
66
66
 
67
67
  VM Instances created during this process will by default use the automatically-generated instance name as the hostname in the VM OS. Set the `BEAKER_set_gce_hostname` environment variable to `1` to override this behavior and configure the VM OS with the name defined in the nodeset as the hostname.
68
68
 
data/Rakefile CHANGED
@@ -8,6 +8,13 @@ rescue LoadError
8
8
  # the voxpupuli-rubocop gem is optional
9
9
  end
10
10
 
11
+ RSpec::Core::RakeTask.new(:spec) do |spec|
12
+ spec.pattern = FileList['spec/**/*_spec.rb']
13
+ end
14
+
15
+ # Also make spec part of the default task
16
+ task default: [:spec]
17
+
11
18
  ###########################################################
12
19
  #
13
20
  # Documentation Tasks
@@ -26,16 +26,19 @@ Gem::Specification.new do |s|
26
26
  s.add_development_dependency 'rspec', '~> 3.0'
27
27
  s.add_development_dependency 'rspec-its', '~> 1.3'
28
28
  s.add_development_dependency 'simplecov', '~> 0.22.0'
29
- s.add_development_dependency 'voxpupuli-rubocop', '~> 2.7.0'
29
+ s.add_development_dependency 'voxpupuli-rubocop', '~> 3.1.0'
30
30
 
31
31
  # Documentation dependencies
32
- s.add_development_dependency 'thin', '~> 1.8', '>= 1.8.2'
32
+ s.add_development_dependency 'thin', '>= 1.8.2', '< 3'
33
33
  s.add_development_dependency 'yard', '~> 0.9.36'
34
34
 
35
35
  # Run time dependencies
36
- s.add_runtime_dependency 'stringify-hash', '~> 0.0.0'
36
+ s.add_dependency 'stringify-hash', '~> 0.0.0'
37
37
 
38
- s.add_runtime_dependency 'google-apis-compute_v1', '~> 0.1'
39
- s.add_runtime_dependency 'google-apis-oslogin_v1', '~> 0.1'
40
- s.add_runtime_dependency 'googleauth', '~> 1.2'
38
+ s.add_dependency 'google-apis-compute_v1', '~> 0.1'
39
+ s.add_dependency 'google-apis-oslogin_v1', '~> 0.1'
40
+ s.add_dependency 'googleauth', '~> 1.2'
41
+
42
+ s.add_dependency 'logger', '~> 1.4'
43
+ s.add_dependency 'ostruct', '~> 0.2'
41
44
  end
@@ -18,12 +18,12 @@ module Beaker
18
18
  #
19
19
  # @raise [Error] if the private key can not be found
20
20
  def find_google_ssh_private_key
21
- private_keyfile = ENV.fetch('BEAKER_gce_ssh_public_key',
21
+ private_keyfile = ENV.fetch('BEAKER_gce_ssh_private_key',
22
22
  File.join(Dir.home, '.ssh', 'google_compute_engine'))
23
23
  if @options[:gce_ssh_private_key] && !File.exist?(private_keyfile)
24
24
  private_keyfile = @options[:gce_ssh_private_key]
25
25
  end
26
- raise("Could not find GCE Private SSH key at '#{keyfile}'") unless File.exist?(private_keyfile)
26
+ raise("Could not find GCE Private SSH key at '#{private_keyfile}'") unless File.exist?(private_keyfile)
27
27
 
28
28
  @options[:gce_ssh_private_key] = private_keyfile
29
29
  private_keyfile
@@ -37,9 +37,9 @@ module Beaker
37
37
  # @raise [Error] if the public key can not be found
38
38
  def find_google_ssh_public_key
39
39
  private_keyfile = find_google_ssh_private_key
40
- public_keyfile = private_keyfile << '.pub'
40
+ public_keyfile = "#{private_keyfile}.pub"
41
41
  public_keyfile = @options[:gce_ssh_public_key] if @options[:gce_ssh_public_key] && !File.exist?(public_keyfile)
42
- raise("Could not find GCE Public SSH key at '#{keyfile}'") unless File.exist?(public_keyfile)
42
+ raise("Could not find GCE Public SSH key at '#{public_keyfile}'") unless File.exist?(public_keyfile)
43
43
 
44
44
  @options[:gce_ssh_public_key] = public_keyfile
45
45
  public_keyfile
@@ -16,6 +16,8 @@ module Beaker
16
16
  class GoogleComputeError < StandardError
17
17
  end
18
18
 
19
+ attr_reader :options
20
+
19
21
  SLEEPWAIT = 5
20
22
 
21
23
  AUTH_URL = 'https://www.googleapis.com/auth/compute'
@@ -51,9 +53,12 @@ module Beaker
51
53
  @options[:gce_network] = ENV.fetch('BEAKER_gce_network', DEFAULT_NETWORK_NAME)
52
54
  @options[:gce_subnetwork] = ENV.fetch('BEAKER_gce_subnetwork', nil)
53
55
 
54
- @configure_ports = ENV.fetch('BEAKER_gce_ports', '').strip
55
- # Split the ports based on commas, removing any empty values
56
- @options[:gce_ports] = @configure_ports.split(/\s*?,\s*/).reject(&:empty?)
56
+ # A non-empty BEAKER_gce_ports replaces gce_ports from the hosts file, as
57
+ # BEAKER_gce_project and BEAKER_gce_machine_type replace theirs. Either
58
+ # source may be a comma-separated string or a list.
59
+ env_ports = ENV.fetch('BEAKER_gce_ports', '').strip
60
+ ports = env_ports.empty? ? @options[:gce_ports] : env_ports
61
+ @options[:gce_ports] = Array(ports).flat_map { |entry| entry.to_s.split(',') }.map(&:strip).reject(&:empty?)
57
62
 
58
63
  raise 'You must specify a gce_project for Google Compute Engine instances!' unless @options[:gce_project]
59
64
 
@@ -69,6 +74,11 @@ module Beaker
69
74
  authorizer = authenticate
70
75
  @compute = ::Google::Apis::ComputeV1::ComputeService.new
71
76
  @compute.authorization = authorizer
77
+ # google-apis-compute_v1 sends a dated X-Goog-Api-Version header on every
78
+ # call, and a release can name a version Compute Engine doesn't accept yet
79
+ # (0.157.0 sends 2026-11-01, which is rejected). Request options are
80
+ # applied after that header, so pin the API version this helper targets.
81
+ @compute.request_options.header = { 'X-Goog-Api-Version' => API_VERSION }
72
82
 
73
83
  # Find the appropriate username to log into created instances
74
84
  @cloudoslogin = ::Google::Apis::OsloginV1::CloudOSLoginService.new
@@ -332,7 +342,7 @@ module Beaker
332
342
  allowed = []
333
343
  allow.each do |port|
334
344
  parts = port.split('/', 2)
335
- allowed << if parts[1] == 'tcp' || parts[1] == 'udp' || parts[1] == 'sctp'
345
+ allowed << if %w[tcp udp sctp].include?(parts[1])
336
346
  ::Google::Apis::ComputeV1::Firewall::Allowed.new(ip_protocol: parts[1], ports: [parts[0]])
337
347
  else
338
348
  ::Google::Apis::ComputeV1::Firewall::Allowed.new(ip_protocol: parts[1])
@@ -341,7 +351,7 @@ module Beaker
341
351
  denied = []
342
352
  deny.each do |port|
343
353
  parts = port.split('/', 2)
344
- denied << if parts[1] == 'tcp' || parts[1] == 'udp' || parts[1] == 'sctp'
354
+ denied << if %w[tcp udp sctp].include?(parts[1])
345
355
  ::Google::Apis::ComputeV1::Firewall::Denied.new(ip_protocol: parts[1], ports: [parts[0]])
346
356
  else
347
357
  ::Google::Apis::ComputeV1::Firewall::Denied.new(ip_protocol: parts[1])
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BeakerGoogle
4
- VERSION = '1.2.0'
4
+ VERSION = '1.3.0'
5
5
  end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Beaker::GoogleCompute do
6
+ context 'when testing constants and defaults' do
7
+ let(:base_options) do
8
+ {
9
+ gce_project: 'test-project',
10
+ gce_zone: 'us-central1-a',
11
+ gce_network: 'default',
12
+ gce_ports: ['80/tcp', '443/tcp'],
13
+ logger: instance_double(Beaker::Logger),
14
+ }
15
+ end
16
+
17
+ before do
18
+ # Mock the authentication and service initialization
19
+ allow(Google::Auth).to receive(:get_application_default).and_return(double)
20
+
21
+ compute_service = double
22
+ allow(compute_service).to receive(:authorization=)
23
+ allow(compute_service).to receive(:request_options).and_return(Google::Apis::RequestOptions.new)
24
+ allow(Google::Apis::ComputeV1::ComputeService).to receive(:new).and_return(compute_service)
25
+
26
+ oslogin_service = double
27
+ allow(oslogin_service).to receive(:authorization=)
28
+ allow(Google::Apis::OsloginV1::CloudOSLoginService).to receive(:new).and_return(oslogin_service)
29
+
30
+ client_options = double
31
+ allow(client_options).to receive(:application_name=)
32
+ allow(client_options).to receive(:application_version=)
33
+ allow(Google::Apis::ClientOptions).to receive(:default).and_return(client_options)
34
+ end
35
+
36
+ describe 'environment variable handling' do
37
+ it 'uses default zone constant when environment variable is not set' do
38
+ # Test that the gem uses the default zone constant when no environment variable is set
39
+ expect(Beaker::GoogleComputeHelper::DEFAULT_ZONE_NAME).to eq('us-central1-a')
40
+ end
41
+
42
+ it 'uses default zone from options when initialized' do
43
+ # Test that the helper uses the default zone from options when initialized
44
+ helper = Beaker::GoogleComputeHelper.new(base_options)
45
+ expect(helper.default_zone).to eq('us-central1-a')
46
+ end
47
+
48
+ it 'uses default network constant when environment variable is not set' do
49
+ # Test that the gem uses the default network constant when no environment variable is set
50
+ expect(Beaker::GoogleComputeHelper::DEFAULT_NETWORK_NAME).to eq('default')
51
+ end
52
+
53
+ it 'uses default network from options when initialized' do
54
+ # Test that the helper uses the default network from options when initialized
55
+ helper = Beaker::GoogleComputeHelper.new(base_options)
56
+ expect(helper.default_network).to eq('default')
57
+ end
58
+
59
+ it 'processes port configuration through actual gem logic' do
60
+ # Test that the gem processes port configuration correctly
61
+ helper_with_ports = Beaker::GoogleComputeHelper.new(base_options.merge(gce_ports: ['80/tcp', '443/tcp',
62
+ '22/tcp',]))
63
+ expect(helper_with_ports.options[:gce_ports]).to eq(['80/tcp', '443/tcp', '22/tcp'])
64
+ end
65
+ end
66
+
67
+ describe 'zone and region utilities' do
68
+ let(:zone_test_cases) do
69
+ {
70
+ 'us-central1-a' => 'us-central1',
71
+ 'us-west1-b' => 'us-west1',
72
+ 'europe-west1-c' => 'europe-west1',
73
+ 'asia-east1-a' => 'asia-east1',
74
+ }
75
+ end
76
+
77
+ before do
78
+ allow(ENV).to receive(:fetch).with('BEAKER_gce_network', anything).and_return('default')
79
+ allow(ENV).to receive(:fetch).with('BEAKER_gce_subnetwork', anything).and_return(nil)
80
+ allow(ENV).to receive(:fetch).with('BEAKER_gce_ports', anything).and_return('')
81
+ allow(ENV).to receive(:[]).with('BEAKER_gce_project').and_return(nil)
82
+ allow(ENV).to receive(:[]).with('GOOGLE_APPLICATION_CREDENTIALS').and_return(nil)
83
+ end
84
+
85
+ it 'extracts region from zone using default_region method' do
86
+ allow(ENV).to receive(:fetch).with('BEAKER_gce_zone', anything).and_return('us-central1-a')
87
+
88
+ # Test the actual default_region method from the gem
89
+ helper = Beaker::GoogleComputeHelper.new(base_options.merge(gce_zone: 'us-central1-a'))
90
+ expect(helper.default_region).to eq('us-central1')
91
+ end
92
+
93
+ it 'handles different zone formats' do
94
+ zone_test_cases.each do |zone, expected_region|
95
+ allow(ENV).to receive(:fetch).with('BEAKER_gce_zone', anything).and_return(zone)
96
+ helper = Beaker::GoogleComputeHelper.new(base_options.merge(gce_zone: zone))
97
+ expect(helper.default_region).to eq(expected_region)
98
+ end
99
+ end
100
+ end
101
+
102
+ describe 'connection preferences' do
103
+ it 'prefers IP connections' do
104
+ # Test the actual connection_preference method from the gem
105
+ google_compute = described_class.new([], base_options)
106
+ expect(google_compute.connection_preference(nil)).to eq([:ip])
107
+ end
108
+ end
109
+
110
+ describe 'Windows image detection' do
111
+ it 'uses Windows image project constants' do
112
+ # Test the actual Windows image project constants from the gem
113
+ expect(Beaker::GoogleCompute::WINDOWS_IMAGE_PROJECT).to include('windows-cloud')
114
+ .and include('windows-sql-cloud')
115
+ end
116
+ end
117
+
118
+ private
119
+
120
+ def mock_google_services
121
+ compute = instance_double(Google::Apis::ComputeV1::ComputeService)
122
+ oslogin = instance_double(Google::Apis::OsloginV1::CloudOSLoginService)
123
+ auth = instance_double(Google::Auth::ServiceAccountCredentials)
124
+ client_options = instance_double(Google::Apis::ClientOptions)
125
+
126
+ allow(Google::Apis::ComputeV1::ComputeService).to receive(:new).and_return(compute)
127
+ allow(Google::Apis::OsloginV1::CloudOSLoginService).to receive(:new).and_return(oslogin)
128
+ allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds).and_return(auth)
129
+ allow(Google::Auth::UserRefreshCredentials).to receive(:make_creds).and_return(auth)
130
+ allow(Google::Auth).to receive(:get_application_default).and_return(auth)
131
+
132
+ allow(compute).to receive(:authorization=)
133
+ allow(compute).to receive(:request_options).and_return(Google::Apis::RequestOptions.new)
134
+ allow(oslogin).to receive(:authorization=)
135
+
136
+ allow(client_options).to receive(:application_name=)
137
+ allow(client_options).to receive(:application_version=)
138
+ allow(Google::Apis::ClientOptions).to receive(:default).and_return(client_options)
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ # Uses the real ComputeService, so the request is built by the installed
6
+ # google-apis-compute_v1, including whatever X-Goog-Api-Version it sets.
7
+ RSpec.describe Beaker::GoogleComputeHelper do
8
+ subject(:helper) { described_class.new(options) }
9
+
10
+ let(:options) do
11
+ {
12
+ gce_project: 'test-project',
13
+ gce_zone: 'us-central1-a',
14
+ gce_ports: [],
15
+ logger: instance_double(Beaker::Logger, debug: nil, info: nil, warn: nil, error: nil),
16
+ }
17
+ end
18
+
19
+ before do
20
+ allow(Google::Auth::ServiceAccountCredentials).to receive(:from_env).and_return(nil)
21
+ allow(Google::Auth).to receive(:get_application_default).and_return(nil)
22
+ end
23
+
24
+ describe 'Compute API version' do
25
+ # The request as it is sent: prepare! runs just before execution.
26
+ let(:prepared_command) do
27
+ command = nil
28
+ allow(helper.instance_variable_get(:@compute)).to receive(:execute_or_queue_command) { |cmd| command = cmd }
29
+ helper.get_machine_type
30
+ command.tap(&:prepare!)
31
+ end
32
+
33
+ it 'sends the API version the helper targets, not the client library default' do
34
+ expect(prepared_command.header['X-Goog-Api-Version']).to eq(described_class::API_VERSION)
35
+ end
36
+ end
37
+ end