hetzner-k3s 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +11 -0
- data/lib/hetzner/infra/client.rb +17 -3
- data/lib/hetzner/infra/load_balancer.rb +4 -4
- data/lib/hetzner/infra/ssh_key.rb +30 -6
- data/lib/hetzner/k3s/cluster.rb +27 -14
- data/lib/hetzner/k3s/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d251d84c9600608af2e382ae10838104bb8fe81fa87b1058b25fd571187a0a88
|
4
|
+
data.tar.gz: 9530af5905cebe724e6be2eafaf60da07172a7a640b1913e3572ee419b0feaed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3b567d8c78c29bc0785d943c52ab65eab40d934ebdf01045c7efd0c76d74087eca2f81c1a790fb4d700bf39c92a696aca9794e17c252467de7838bb2149e56e
|
7
|
+
data.tar.gz: 010a87dd114c2d1209f8da453ebb8123fbbf4f4684dacf3aa2e85d648715c78013f798e68b413603a25f9869e153afced6104cceca5914060aaedb6de8a358b2
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -208,6 +208,17 @@ The other annotations should be self explanatory. You can find a list of the ava
|
|
208
208
|
|
209
209
|
Once the cluster is ready you can create persistent volumes out of the box with the default storage class `hcloud-volumes`, since the Hetzner CSI driver is installed automatically. This will use Hetzner's block storage (based on Ceph so it's replicated and highly available) for your persistent volumes. Note that the minimum size of a volume is 10Gi. If you specify a smaller size for a volume, the volume will be created with a capacity of 10Gi anyway.
|
210
210
|
|
211
|
+
|
212
|
+
## changelog
|
213
|
+
|
214
|
+
- 0.3.0
|
215
|
+
- Handle case when an SSH key with the given fingerprint already exists in the Hetzner project
|
216
|
+
- Handle a timeout of 5 seconds for requests to the Hetzner API
|
217
|
+
- Retry waiting for server to be up when timeouts/host-unreachable errors occur
|
218
|
+
- Ignore known_hosts entry to prevent errors when recreating servers with IPs that have been used previously
|
219
|
+
|
220
|
+
- 0.2.0
|
221
|
+
- Allow mixing servers of different series Intel/AMD
|
211
222
|
## Contributing and support
|
212
223
|
|
213
224
|
Please create a PR if you want to propose any changes, or open an issue if you are having trouble with the tool - I will do my best to help if I can.
|
data/lib/hetzner/infra/client.rb
CHANGED
@@ -9,15 +9,21 @@ module Hetzner
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def get(path)
|
12
|
-
|
12
|
+
make_request do
|
13
|
+
JSON.parse HTTP.headers(headers).get(BASE_URI + path).body
|
14
|
+
end
|
13
15
|
end
|
14
16
|
|
15
17
|
def post(path, data)
|
16
|
-
|
18
|
+
make_request do
|
19
|
+
HTTP.headers(headers).post(BASE_URI + path, json: data)
|
20
|
+
end
|
17
21
|
end
|
18
22
|
|
19
23
|
def delete(path, id)
|
20
|
-
|
24
|
+
make_request do
|
25
|
+
HTTP.headers(headers).delete(BASE_URI + path + "/" + id.to_s)
|
26
|
+
end
|
21
27
|
end
|
22
28
|
|
23
29
|
private
|
@@ -28,5 +34,13 @@ module Hetzner
|
|
28
34
|
"Content-Type": "application/json"
|
29
35
|
}
|
30
36
|
end
|
37
|
+
|
38
|
+
def make_request &block
|
39
|
+
Timeout::timeout(5) do
|
40
|
+
block.call
|
41
|
+
end
|
42
|
+
rescue Timeout::Error
|
43
|
+
retry
|
44
|
+
end
|
31
45
|
end
|
32
46
|
end
|
@@ -26,12 +26,12 @@ module Hetzner
|
|
26
26
|
JSON.parse(response)["load_balancer"]["id"]
|
27
27
|
end
|
28
28
|
|
29
|
-
def delete
|
29
|
+
def delete(ha:)
|
30
30
|
if load_balancer = find_load_balancer
|
31
|
-
puts "Deleting API load balancer..."
|
31
|
+
puts "Deleting API load balancer..." unless ha
|
32
32
|
hetzner_client.delete("/load_balancers", load_balancer["id"])
|
33
|
-
puts "...API load balancer deleted."
|
34
|
-
|
33
|
+
puts "...API load balancer deleted." unless ha
|
34
|
+
elsif ha
|
35
35
|
puts "API load balancer no longer exists, skipping."
|
36
36
|
end
|
37
37
|
|
@@ -26,11 +26,17 @@ module Hetzner
|
|
26
26
|
JSON.parse(response)["ssh_key"]["id"]
|
27
27
|
end
|
28
28
|
|
29
|
-
def delete
|
29
|
+
def delete(ssh_key_path:)
|
30
|
+
@ssh_key_path = ssh_key_path
|
31
|
+
|
30
32
|
if ssh_key = find_ssh_key
|
31
|
-
|
32
|
-
|
33
|
-
|
33
|
+
if ssh_key["name"] == cluster_name
|
34
|
+
puts "Deleting ssh_key..."
|
35
|
+
hetzner_client.delete("/ssh_keys", ssh_key["id"])
|
36
|
+
puts "...ssh_key deleted."
|
37
|
+
else
|
38
|
+
puts "The SSH key existed before creating the cluster, so I won't delete it."
|
39
|
+
end
|
34
40
|
else
|
35
41
|
puts "SSH key no longer exists, skipping."
|
36
42
|
end
|
@@ -42,15 +48,33 @@ module Hetzner
|
|
42
48
|
|
43
49
|
attr_reader :hetzner_client, :cluster_name, :ssh_key_path
|
44
50
|
|
51
|
+
def public_key
|
52
|
+
@public_key ||= File.read(ssh_key_path).chop
|
53
|
+
end
|
54
|
+
|
45
55
|
def ssh_key_config
|
46
56
|
{
|
47
57
|
name: cluster_name,
|
48
|
-
public_key:
|
58
|
+
public_key: public_key
|
49
59
|
}
|
50
60
|
end
|
51
61
|
|
62
|
+
def fingerprint
|
63
|
+
@fingerprint ||= ::SSHKey.fingerprint(public_key)
|
64
|
+
end
|
65
|
+
|
52
66
|
def find_ssh_key
|
53
|
-
hetzner_client.get("/ssh_keys")["ssh_keys"].detect
|
67
|
+
key = hetzner_client.get("/ssh_keys")["ssh_keys"].detect do |ssh_key|
|
68
|
+
ssh_key["fingerprint"] == fingerprint
|
69
|
+
end
|
70
|
+
|
71
|
+
unless key
|
72
|
+
key = hetzner_client.get("/ssh_keys")["ssh_keys"].detect do |ssh_key|
|
73
|
+
ssh_key["name"] == cluster_name
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
key
|
54
78
|
end
|
55
79
|
|
56
80
|
end
|
data/lib/hetzner/k3s/cluster.rb
CHANGED
@@ -45,6 +45,7 @@ class Cluster
|
|
45
45
|
def delete(configuration:)
|
46
46
|
@cluster_name = configuration.dig("cluster_name")
|
47
47
|
@kubeconfig_path = File.expand_path(configuration.dig("kubeconfig_path"))
|
48
|
+
@ssh_key_path = File.expand_path(configuration.dig("ssh_key_path"))
|
48
49
|
|
49
50
|
delete_resources
|
50
51
|
end
|
@@ -148,6 +149,7 @@ class Cluster
|
|
148
149
|
end
|
149
150
|
|
150
151
|
def delete_resources
|
152
|
+
# Deleting nodes defined according to Kubernetes first
|
151
153
|
begin
|
152
154
|
Timeout::timeout(5) do
|
153
155
|
servers = kubernetes_client.api("v1").resource("nodes").list
|
@@ -164,6 +166,17 @@ class Cluster
|
|
164
166
|
puts "Unable to fetch nodes from Kubernetes API. Is the cluster online?"
|
165
167
|
end
|
166
168
|
|
169
|
+
# Deleting nodes defined in the config file just in case there are leftovers i.e. nodes that
|
170
|
+
# were not part of the cluster for some reason
|
171
|
+
|
172
|
+
threads = all_servers.each do |server|
|
173
|
+
Thread.new do
|
174
|
+
Hetzner::Server.new(hetzner_client: hetzner_client, cluster_name: cluster_name).delete(server_name: server["name"])
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
threads.each(&:join)
|
179
|
+
|
167
180
|
puts
|
168
181
|
|
169
182
|
sleep 5 # give time for the servers to actually be deleted
|
@@ -181,12 +194,12 @@ class Cluster
|
|
181
194
|
Hetzner::SSHKey.new(
|
182
195
|
hetzner_client: hetzner_client,
|
183
196
|
cluster_name: cluster_name
|
184
|
-
).delete
|
197
|
+
).delete(ssh_key_path: ssh_key_path)
|
185
198
|
|
186
199
|
Hetzner::LoadBalancer.new(
|
187
200
|
hetzner_client: hetzner_client,
|
188
201
|
cluster_name: cluster_name
|
189
|
-
).delete
|
202
|
+
).delete(ha: (masters.size > 1))
|
190
203
|
|
191
204
|
end
|
192
205
|
|
@@ -431,17 +444,19 @@ class Cluster
|
|
431
444
|
end
|
432
445
|
|
433
446
|
def wait_for_ssh(server)
|
434
|
-
|
447
|
+
Timeout::timeout(5) do
|
448
|
+
server_name = server["name"]
|
435
449
|
|
436
|
-
|
450
|
+
puts "Waiting for server #{server_name} to be up..."
|
437
451
|
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
452
|
+
loop do
|
453
|
+
result = ssh(server, "echo UP")
|
454
|
+
break if result == "UP"
|
455
|
+
end
|
442
456
|
|
443
|
-
|
444
|
-
|
457
|
+
puts "...server #{server_name} is now up."
|
458
|
+
end
|
459
|
+
rescue Errno::ENETUNREACH, Errno::EHOSTUNREACH, Timeout::Error
|
445
460
|
retry
|
446
461
|
end
|
447
462
|
|
@@ -449,7 +464,7 @@ class Cluster
|
|
449
464
|
public_ip = server.dig("public_net", "ipv4", "ip")
|
450
465
|
output = ""
|
451
466
|
|
452
|
-
Net::SSH.start(public_ip, "root") do |session|
|
467
|
+
Net::SSH.start(public_ip, "root", verify_host_key: :never) do |session|
|
453
468
|
session.exec!(command) do |channel, stream, data|
|
454
469
|
output << data
|
455
470
|
puts data if print_output
|
@@ -457,11 +472,9 @@ class Cluster
|
|
457
472
|
end
|
458
473
|
|
459
474
|
output.chop
|
460
|
-
rescue Net::SSH::ConnectionTimeout
|
461
|
-
retry
|
462
475
|
rescue Net::SSH::Disconnect => e
|
463
476
|
retry unless e.message =~ /Too many authentication failures/
|
464
|
-
rescue Errno::ECONNREFUSED
|
477
|
+
rescue Net::SSH::ConnectionTimeout, Errno::ECONNREFUSED, Errno::ENETUNREACH, Errno::EHOSTUNREACH
|
465
478
|
retry
|
466
479
|
end
|
467
480
|
|
data/lib/hetzner/k3s/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hetzner-k3s
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vito Botta
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-08-
|
11
|
+
date: 2021-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|