beaker-hcloud 1.0.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.
- checksums.yaml +7 -0
- data/.github/dependabot.yml +18 -0
- data/.github/workflows/ci.yml +57 -0
- data/.github/workflows/codeql-analysis.yml +70 -0
- data/.github/workflows/release.yml +32 -0
- data/.gitignore +7 -0
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +20 -0
- data/CHANGELOG.md +37 -0
- data/Gemfile +15 -0
- data/LICENSE +661 -0
- data/README.md +53 -0
- data/Rakefile +26 -0
- data/beaker-hcloud.gemspec +29 -0
- data/lib/beaker/hypervisor/hcloud.rb +95 -0
- data/lib/beaker-hcloud/ssh_data_patches.rb +66 -0
- data/lib/beaker-hcloud/version.rb +5 -0
- data/spec/beaker/hypervisor/hcloud_spec.rb +168 -0
- data/spec/spec_helper.rb +29 -0
- metadata +185 -0
data/README.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# beaker-hcloud
|
2
|
+
|
3
|
+
[](https://github.com/voxpupuli/beaker-hcloud/blob/master/LICENSE)
|
4
|
+
[](https://github.com/voxpupuli/beaker-hcloud/actions/workflows/test.yml)
|
5
|
+
[](https://github.com/voxpupuli/beaker-hcloud/actions/workflows/release.yml)
|
6
|
+
[](https://rubygems.org/gems/beaker-hcloud)
|
7
|
+
[](https://rubygems.org/gems/beaker-hcloud)
|
8
|
+
|
9
|
+
A [beaker](https://github.com/voxpupuli/beaker) extension for provision Hetzner Cloud instances.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Include this gem alongside Beaker in your Gemfile or project.gemspec. E.g.
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
# Gemfile
|
17
|
+
gem 'beaker', '~> 5.0'
|
18
|
+
gem 'beaker-hcloud'
|
19
|
+
|
20
|
+
# project.gemspec
|
21
|
+
s.add_runtime_dependency 'beaker', '~> 5.0'
|
22
|
+
s.add_runtime_dependency 'beaker-hcloud'
|
23
|
+
```
|
24
|
+
|
25
|
+
## Authentication
|
26
|
+
|
27
|
+
You need to create an API token using Hetzner's cloud console. Make
|
28
|
+
sure to create the token in the correct project.
|
29
|
+
|
30
|
+
`beaker-hcloud` expects the token to be in the `BEAKER_HCLOUD_TOKEN`
|
31
|
+
environment variable.
|
32
|
+
|
33
|
+
## Configuration
|
34
|
+
|
35
|
+
Some options can be set to influence how and where server instances
|
36
|
+
are being created:
|
37
|
+
|
38
|
+
|
39
|
+
| configuration option | required | default | description |
|
40
|
+
| -------------------- | -------- | ------- | ----------- |
|
41
|
+
| `image` | true | | The name of one of Hetzner's provided images, e.g. `ubuntu-20.04`, or a custom one, i.e. a snapshot in your account. |
|
42
|
+
| `server_type` | false | `cx11` | Hetzner cloud server type |
|
43
|
+
| `location` | false | `nbg1` | One of Hetzner's datacenter locations |
|
44
|
+
|
45
|
+
# Cleanup
|
46
|
+
|
47
|
+
In cases where the beaker process is killed before finishing, it may leave resources in Hetzner cloud. These will need to be manually deleted.
|
48
|
+
|
49
|
+
Look for servers in your project named exactly as the ones in your beaker host configuration and SSH keys with names beginning with `Beaker-`.
|
50
|
+
|
51
|
+
# Contributing
|
52
|
+
|
53
|
+
Please refer to voxpupuli/beaker's [contributing](https://github.com/voxpupuli/beaker/blob/master/CONTRIBUTING.md) guide.
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'voxpupuli/rubocop/rake'
|
7
|
+
rescue LoadError
|
8
|
+
# the voxpupuli-rubocop gem is optional
|
9
|
+
end
|
10
|
+
|
11
|
+
begin
|
12
|
+
require 'rubygems'
|
13
|
+
require 'github_changelog_generator/task'
|
14
|
+
|
15
|
+
GitHubChangelogGenerator::RakeTask.new :changelog do |config|
|
16
|
+
config.header = "# Changelog\n\nAll notable changes to this project will be documented in this file."
|
17
|
+
config.exclude_labels = %w[duplicate question invalid wontfix wont-fix skip-changelog modulesync github_actions]
|
18
|
+
config.user = 'voxpupuli'
|
19
|
+
config.project = 'beaker-hcloud'
|
20
|
+
config.future_release = Gem::Specification.load("#{config.project}.gemspec").version
|
21
|
+
end
|
22
|
+
rescue LoadError # rubocop:disable Lint/SuppressedException
|
23
|
+
end
|
24
|
+
|
25
|
+
RSpec::Core::RakeTask.new(:spec)
|
26
|
+
task default: %i[spec]
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path('lib', __dir__)
|
4
|
+
require 'beaker-hcloud/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'beaker-hcloud'
|
8
|
+
s.version = BeakerHcloud::VERSION
|
9
|
+
s.summary = 'Hetzner Library for beaker acceptance testing framework'
|
10
|
+
s.description = 'Another gem that extends beaker'
|
11
|
+
s.authors = ['Tim Meusel', 'Vox Pupuli']
|
12
|
+
s.email = 'voxpupuli@groups.io'
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.homepage = 'https://github.com/voxpupuli/beaker-hcloud'
|
15
|
+
s.license = 'AGPL-3.0'
|
16
|
+
|
17
|
+
s.required_ruby_version = '>= 2.7'
|
18
|
+
|
19
|
+
# Testing dependencies
|
20
|
+
s.add_development_dependency 'rake', '~> 13.0', '>= 13.0.6'
|
21
|
+
s.add_development_dependency 'rspec', '~> 3.12'
|
22
|
+
s.add_development_dependency 'voxpupuli-rubocop', '~> 2.0.0'
|
23
|
+
|
24
|
+
s.add_runtime_dependency 'bcrypt_pbkdf', '~> 1.0'
|
25
|
+
s.add_runtime_dependency 'beaker', '~> 5.4'
|
26
|
+
s.add_runtime_dependency 'ed25519', '~> 1.2'
|
27
|
+
s.add_runtime_dependency 'hcloud', '>= 1.0.3', '< 2.0.0'
|
28
|
+
s.add_runtime_dependency 'ssh_data', '~> 1.3'
|
29
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'hcloud'
|
4
|
+
require 'ed25519'
|
5
|
+
require 'bcrypt_pbkdf'
|
6
|
+
|
7
|
+
require_relative '../../beaker-hcloud/ssh_data_patches'
|
8
|
+
|
9
|
+
module Beaker
|
10
|
+
# beaker extension to manage cloud instances from https://www.hetzner.com/cloud
|
11
|
+
class Hcloud < Beaker::Hypervisor
|
12
|
+
# @param [Host, Array<Host>, String, Symbol] hosts One or more hosts to act upon, or a role (String or Symbol) that identifies one or more hosts.
|
13
|
+
# @param [Hash{Symbol=>String}] options Options to pass on to the hypervisor
|
14
|
+
def initialize(hosts, options) # rubocop:disable Lint/MissingSuper
|
15
|
+
@options = options
|
16
|
+
@logger = options[:logger] || Beaker::Logger.new
|
17
|
+
@hosts = hosts
|
18
|
+
|
19
|
+
raise 'You need to pass a token as BEAKER_HCLOUD_TOKEN environment variable' unless ENV['BEAKER_HCLOUD_TOKEN']
|
20
|
+
|
21
|
+
@client = ::Hcloud::Client.new(token: ENV.fetch('BEAKER_HCLOUD_TOKEN'))
|
22
|
+
end
|
23
|
+
|
24
|
+
def provision
|
25
|
+
@logger.notify 'Provisioning hcloud'
|
26
|
+
create_ssh_key
|
27
|
+
@hosts.each do |host|
|
28
|
+
create_server(host)
|
29
|
+
end
|
30
|
+
@logger.notify 'Done provisioning hcloud'
|
31
|
+
end
|
32
|
+
|
33
|
+
def cleanup
|
34
|
+
@logger.notify 'Cleaning up hcloud'
|
35
|
+
@hosts.each do |host|
|
36
|
+
@logger.debug("Deleting hcloud server #{host.name}")
|
37
|
+
@client.servers.find(host[:hcloud_id]).destroy
|
38
|
+
end
|
39
|
+
@logger.notify 'Deleting hcloud SSH key'
|
40
|
+
@client.ssh_keys.find(@options[:ssh][:hcloud_id]).destroy
|
41
|
+
File.unlink(@key_file.path)
|
42
|
+
@logger.notify 'Done cleaning up hcloud'
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def ssh_key_name
|
48
|
+
safe_hostname = Socket.gethostname.tr('.', '-')
|
49
|
+
[
|
50
|
+
'Beaker',
|
51
|
+
ENV.fetch('USER', nil),
|
52
|
+
safe_hostname,
|
53
|
+
@options[:aws_keyname_modifier],
|
54
|
+
@options[:timestamp].strftime('%F_%H_%M_%S_%N'),
|
55
|
+
].join('-')
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_ssh_key
|
59
|
+
@logger.notify 'Generating SSH keypair'
|
60
|
+
ssh_key = SSHData::PrivateKey::ED25519.generate
|
61
|
+
@key_file = Tempfile.create(ssh_key_name)
|
62
|
+
File.write(@key_file.path, ssh_key.openssh(comment: ssh_key_name))
|
63
|
+
@logger.notify 'Creating hcloud SSH key'
|
64
|
+
hcloud_ssh_key = @client.ssh_keys.create(
|
65
|
+
name: ssh_key_name,
|
66
|
+
public_key: ssh_key.public_key.openssh(comment: ssh_key_name),
|
67
|
+
)
|
68
|
+
@options[:ssh][:hcloud_id] = hcloud_ssh_key.id
|
69
|
+
hcloud_ssh_key
|
70
|
+
end
|
71
|
+
|
72
|
+
def create_server(host)
|
73
|
+
@logger.notify "provisioning #{host.name}"
|
74
|
+
location = host[:location] || 'nbg1'
|
75
|
+
server_type = host[:server_type] || 'cx11'
|
76
|
+
action, server = @client.servers.create(
|
77
|
+
name: host.hostname,
|
78
|
+
location: location,
|
79
|
+
server_type: server_type,
|
80
|
+
image: host[:image],
|
81
|
+
ssh_keys: [ssh_key_name],
|
82
|
+
)
|
83
|
+
while action.status == 'running'
|
84
|
+
sleep 5
|
85
|
+
action = @client.actions.find(action.id)
|
86
|
+
server = @client.servers.find(server.id)
|
87
|
+
end
|
88
|
+
host[:ip] = server.public_net['ipv4']['ip']
|
89
|
+
host[:vmhostname] = server.public_net['ipv4']['dns_ptr']
|
90
|
+
host[:hcloud_id] = server.id
|
91
|
+
host.options[:ssh][:keys] = [@key_file.path]
|
92
|
+
server
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ed25519'
|
4
|
+
require 'ssh_data'
|
5
|
+
|
6
|
+
# Patches for the 'ssh_data' gem to allow serialization of
|
7
|
+
# ed25519 private keys in OpenSSH format.
|
8
|
+
module BeakerHcloud
|
9
|
+
module SSHDataPatches
|
10
|
+
# Add encoding methods for OpenSSH's PEM-like format to
|
11
|
+
# store private keys.
|
12
|
+
module EncodingPatch
|
13
|
+
def encode_pem(data, type)
|
14
|
+
encoded_data = Base64.strict_encode64(data)
|
15
|
+
.scan(/.{1,70}/m)
|
16
|
+
.join("\n")
|
17
|
+
.chomp
|
18
|
+
<<~PEM
|
19
|
+
-----BEGIN #{type}-----
|
20
|
+
#{encoded_data}
|
21
|
+
-----END #{type}-----
|
22
|
+
PEM
|
23
|
+
end
|
24
|
+
|
25
|
+
def encode_openssh_private_key(private_key, comment = '')
|
26
|
+
public_key = private_key.public_key
|
27
|
+
private_key_data = [
|
28
|
+
(SecureRandom.random_bytes(4) * 2),
|
29
|
+
public_key.rfc4253,
|
30
|
+
encode_string(private_key.ed25519_key.seed + public_key.ed25519_key.to_str),
|
31
|
+
encode_string(comment),
|
32
|
+
].join
|
33
|
+
unpadded = private_key_data.bytesize % 8
|
34
|
+
private_key_data << Array(1..(8 - unpadded)).pack('c*') unless unpadded.zero?
|
35
|
+
[
|
36
|
+
::SSHData::Encoding::OPENSSH_PRIVATE_KEY_MAGIC,
|
37
|
+
encode_string('none'),
|
38
|
+
encode_string('none'),
|
39
|
+
encode_string(''),
|
40
|
+
encode_uint32(1),
|
41
|
+
encode_string(public_key.rfc4253),
|
42
|
+
encode_string(private_key_data),
|
43
|
+
].join
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Add method to emit OpenSSH-encoded string
|
48
|
+
module Ed25519PrivateKeyPatch
|
49
|
+
def openssh(comment: '')
|
50
|
+
encoded_key = ::SSHData::Encoding.encode_openssh_private_key(
|
51
|
+
self,
|
52
|
+
comment,
|
53
|
+
)
|
54
|
+
::SSHData::Encoding.encode_pem(
|
55
|
+
encoded_key,
|
56
|
+
'OPENSSH PRIVATE KEY',
|
57
|
+
)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if defined?(SSHData)
|
64
|
+
SSHData::Encoding.extend BeakerHcloud::SSHDataPatches::EncodingPatch
|
65
|
+
SSHData::PrivateKey::ED25519.prepend BeakerHcloud::SSHDataPatches::Ed25519PrivateKeyPatch
|
66
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
# rubocop:disable RSpec/MultipleMemoizedHelpers, RSpec/VerifiedDoubles, RSpec/FilePath
|
6
|
+
describe Beaker::Hcloud do
|
7
|
+
let(:logger_double) do
|
8
|
+
double(:logger).as_null_object
|
9
|
+
end
|
10
|
+
let(:options) do
|
11
|
+
opts = Beaker::Options::Presets.new
|
12
|
+
opts.presets
|
13
|
+
.merge(opts.env_vars)
|
14
|
+
.merge({
|
15
|
+
logger: logger_double,
|
16
|
+
timestamp: Time.now,
|
17
|
+
})
|
18
|
+
end
|
19
|
+
let(:host1_hash) do
|
20
|
+
{
|
21
|
+
hypervisor: 'hcloud',
|
22
|
+
image: 'ubuntu-20.04',
|
23
|
+
}
|
24
|
+
end
|
25
|
+
let(:host2_hash) do
|
26
|
+
{
|
27
|
+
hypervisor: 'hcloud',
|
28
|
+
image: 'custom image',
|
29
|
+
location: 'custom location',
|
30
|
+
server_type: 'custom type',
|
31
|
+
}
|
32
|
+
end
|
33
|
+
let(:hosts) do
|
34
|
+
[[1, host1_hash], [2, host2_hash]].map do |number, host_hash|
|
35
|
+
host_hash = Beaker::Options::OptionsHash.new.merge(host_hash)
|
36
|
+
Beaker::Host.create("Server #{number}", host_hash, options)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
let(:server1) do
|
40
|
+
double(:server1,
|
41
|
+
id: 1,
|
42
|
+
public_net: {
|
43
|
+
'ipv4' => {
|
44
|
+
'ip' => '192.168.0.1',
|
45
|
+
'dns_ptr' => 'server1.example.com',
|
46
|
+
},
|
47
|
+
},
|
48
|
+
destroy: true)
|
49
|
+
end
|
50
|
+
let(:server2) do
|
51
|
+
double(:server2,
|
52
|
+
id: 2,
|
53
|
+
public_net: {
|
54
|
+
'ipv4' => {
|
55
|
+
'ip' => '192.168.0.2',
|
56
|
+
'dns_ptr' => 'server2.example.com',
|
57
|
+
},
|
58
|
+
},
|
59
|
+
destroy: true)
|
60
|
+
end
|
61
|
+
let(:action_double) do
|
62
|
+
double(:action, status: 'success')
|
63
|
+
end
|
64
|
+
let(:actions_double) do
|
65
|
+
double(:actions, find: action_double)
|
66
|
+
end
|
67
|
+
let(:servers_double) do
|
68
|
+
servers_double = double(:servers)
|
69
|
+
allow(servers_double).to receive(:create)
|
70
|
+
.and_return([action_double, server1], [action_double, server2])
|
71
|
+
allow(servers_double).to receive(:find)
|
72
|
+
.and_return(server1, server2)
|
73
|
+
servers_double
|
74
|
+
end
|
75
|
+
let(:key_double) do
|
76
|
+
double(:key, id: 23, destroy: true)
|
77
|
+
end
|
78
|
+
let(:ssh_keys_double) do
|
79
|
+
double(:ssh_keys, create: key_double, find: key_double)
|
80
|
+
end
|
81
|
+
let(:hcloud_client) do
|
82
|
+
double(:hcloud_client,
|
83
|
+
actions: actions_double,
|
84
|
+
servers: servers_double,
|
85
|
+
ssh_keys: ssh_keys_double)
|
86
|
+
end
|
87
|
+
let(:hcloud) do
|
88
|
+
described_class.new(hosts, options)
|
89
|
+
end
|
90
|
+
|
91
|
+
before do
|
92
|
+
ENV['BEAKER_HCLOUD_TOKEN'] = 'abc'
|
93
|
+
allow(Hcloud::Client).to receive(:new).and_return(hcloud_client)
|
94
|
+
end
|
95
|
+
|
96
|
+
describe '#provision', :aggregate_failures do
|
97
|
+
subject(:provision) { hcloud.provision }
|
98
|
+
|
99
|
+
before { provision }
|
100
|
+
|
101
|
+
after { hcloud.cleanup }
|
102
|
+
|
103
|
+
it 'uploads an ssh key using the hcloud client' do
|
104
|
+
expect(ssh_keys_double).to have_received(:create)
|
105
|
+
end
|
106
|
+
|
107
|
+
# rubocop:disable RSpec/ExampleLength
|
108
|
+
it 'creates one server for each host via the hcloud client' do
|
109
|
+
expect(servers_double).to have_received(:create)
|
110
|
+
.with(hash_including({
|
111
|
+
name: 'Server 1',
|
112
|
+
location: 'nbg1',
|
113
|
+
server_type: 'cx11',
|
114
|
+
image: 'ubuntu-20.04',
|
115
|
+
}))
|
116
|
+
expect(servers_double).to have_received(:create)
|
117
|
+
.with(hash_including({
|
118
|
+
name: 'Server 2',
|
119
|
+
location: 'custom location',
|
120
|
+
server_type: 'custom type',
|
121
|
+
image: 'custom image',
|
122
|
+
}))
|
123
|
+
end
|
124
|
+
# rubocop:enable RSpec/ExampleLength
|
125
|
+
|
126
|
+
it "saves ip and dns name to the host's settings" do
|
127
|
+
host1, host2 = hosts
|
128
|
+
expect(host1[:ip]).to eq '192.168.0.1'
|
129
|
+
expect(host1[:vmhostname]).to eq 'server1.example.com'
|
130
|
+
expect(host2[:ip]).to eq '192.168.0.2'
|
131
|
+
expect(host2[:vmhostname]).to eq 'server2.example.com'
|
132
|
+
end
|
133
|
+
|
134
|
+
it "saves hcloud's server id with the host's settings" do
|
135
|
+
host1, host2 = hosts
|
136
|
+
expect(host1[:hcloud_id]).to eq 1
|
137
|
+
expect(host2[:hcloud_id]).to eq 2
|
138
|
+
end
|
139
|
+
|
140
|
+
it "saves the path to the temporary ssh key with the host's options" do
|
141
|
+
host1, host2 = hosts
|
142
|
+
expect(host1[:ssh][:keys].size).to eq 1
|
143
|
+
expect(host2[:ssh][:keys].size).to eq 1
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe '#cleanup' do
|
148
|
+
subject(:cleanup) { hcloud.cleanup }
|
149
|
+
|
150
|
+
before do
|
151
|
+
hcloud.provision
|
152
|
+
cleanup
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'destroys first server' do
|
156
|
+
expect(server1).to have_received(:destroy)
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'destroys last server' do
|
160
|
+
expect(server2).to have_received(:destroy)
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'destroys the temporary ssh key' do
|
164
|
+
expect(key_double).to have_received(:destroy)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
# rubocop:enable RSpec/MultipleMemoizedHelpers, RSpec/VerifiedDoubles, RSpec/FilePath
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if ENV['COVERAGE'] == 'yes'
|
4
|
+
require 'simplecov'
|
5
|
+
require 'simplecov-console'
|
6
|
+
require 'codecov'
|
7
|
+
|
8
|
+
SimpleCov.start do
|
9
|
+
track_files 'lib/**/*.rb'
|
10
|
+
|
11
|
+
add_filter '/spec'
|
12
|
+
|
13
|
+
enable_coverage :branch
|
14
|
+
|
15
|
+
# do not track vendored files
|
16
|
+
add_filter '/vendor'
|
17
|
+
add_filter '/.vendor'
|
18
|
+
end
|
19
|
+
|
20
|
+
SimpleCov.formatters = [
|
21
|
+
SimpleCov::Formatter::Console,
|
22
|
+
SimpleCov::Formatter::Codecov,
|
23
|
+
]
|
24
|
+
end
|
25
|
+
$LOAD_PATH.unshift File.expand_path('lib', __dir__)
|
26
|
+
|
27
|
+
require 'beaker'
|
28
|
+
require 'beaker-hcloud/version'
|
29
|
+
require 'beaker/hypervisor/hcloud'
|
metadata
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: beaker-hcloud
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tim Meusel
|
8
|
+
- Vox Pupuli
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2023-09-22 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '13.0'
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 13.0.6
|
24
|
+
type: :development
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - "~>"
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '13.0'
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 13.0.6
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rspec
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.12'
|
41
|
+
type: :development
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.12'
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: voxpupuli-rubocop
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.0.0
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.0.0
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: bcrypt_pbkdf
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.0'
|
69
|
+
type: :runtime
|
70
|
+
prerelease: false
|
71
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.0'
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: beaker
|
78
|
+
requirement: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '5.4'
|
83
|
+
type: :runtime
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '5.4'
|
90
|
+
- !ruby/object:Gem::Dependency
|
91
|
+
name: ed25519
|
92
|
+
requirement: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.2'
|
97
|
+
type: :runtime
|
98
|
+
prerelease: false
|
99
|
+
version_requirements: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.2'
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: hcloud
|
106
|
+
requirement: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.0.3
|
111
|
+
- - "<"
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: 2.0.0
|
114
|
+
type: :runtime
|
115
|
+
prerelease: false
|
116
|
+
version_requirements: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: 1.0.3
|
121
|
+
- - "<"
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: 2.0.0
|
124
|
+
- !ruby/object:Gem::Dependency
|
125
|
+
name: ssh_data
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - "~>"
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '1.3'
|
131
|
+
type: :runtime
|
132
|
+
prerelease: false
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - "~>"
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '1.3'
|
138
|
+
description: Another gem that extends beaker
|
139
|
+
email: voxpupuli@groups.io
|
140
|
+
executables: []
|
141
|
+
extensions: []
|
142
|
+
extra_rdoc_files: []
|
143
|
+
files:
|
144
|
+
- ".github/dependabot.yml"
|
145
|
+
- ".github/workflows/ci.yml"
|
146
|
+
- ".github/workflows/codeql-analysis.yml"
|
147
|
+
- ".github/workflows/release.yml"
|
148
|
+
- ".gitignore"
|
149
|
+
- ".rubocop.yml"
|
150
|
+
- ".rubocop_todo.yml"
|
151
|
+
- CHANGELOG.md
|
152
|
+
- Gemfile
|
153
|
+
- LICENSE
|
154
|
+
- README.md
|
155
|
+
- Rakefile
|
156
|
+
- beaker-hcloud.gemspec
|
157
|
+
- lib/beaker-hcloud/ssh_data_patches.rb
|
158
|
+
- lib/beaker-hcloud/version.rb
|
159
|
+
- lib/beaker/hypervisor/hcloud.rb
|
160
|
+
- spec/beaker/hypervisor/hcloud_spec.rb
|
161
|
+
- spec/spec_helper.rb
|
162
|
+
homepage: https://github.com/voxpupuli/beaker-hcloud
|
163
|
+
licenses:
|
164
|
+
- AGPL-3.0
|
165
|
+
metadata: {}
|
166
|
+
post_install_message:
|
167
|
+
rdoc_options: []
|
168
|
+
require_paths:
|
169
|
+
- lib
|
170
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - ">="
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '2.7'
|
175
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - ">="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0'
|
180
|
+
requirements: []
|
181
|
+
rubygems_version: 3.2.33
|
182
|
+
signing_key:
|
183
|
+
specification_version: 4
|
184
|
+
summary: Hetzner Library for beaker acceptance testing framework
|
185
|
+
test_files: []
|