hetzner-k3s 0.3.9 → 0.4.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 +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +10 -0
- data/lib/hetzner/infra/firewall.rb +79 -57
- data/lib/hetzner/infra/load_balancer.rb +14 -2
- data/lib/hetzner/k3s/cli.rb +78 -12
- data/lib/hetzner/k3s/cluster.rb +20 -43
- 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: 6ee4a4ac2c31ebff805ee20edc3658ffe64be32e50b524ee4af3646e3ffc3a3c
|
|
4
|
+
data.tar.gz: 8cbc33a2a696b19c8e614932d1daa7fa9beddaf9d69dd8377d909cb382e40f87
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ff2ca466abbd198b3bc76c8854113d90033fb606e9f11152ecf6d079564ee4dcbdab359a5b17229770a7dc531a9674b211d079a2204596efe3ec5b67157bf82e
|
|
7
|
+
data.tar.gz: a6a16c64b0ada5c4d1a740894df09a9f41ed0110b06cfd5629b1971c64836a5fd38bceebb7898432b275cb677779606ecd780b917d4095eb161987d26c0eecc0
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -54,6 +54,8 @@ cluster_name: test
|
|
|
54
54
|
kubeconfig_path: "./kubeconfig"
|
|
55
55
|
k3s_version: v1.21.3+k3s1
|
|
56
56
|
ssh_key_path: "~/.ssh/id_rsa.pub"
|
|
57
|
+
ssh_allowed_networks:
|
|
58
|
+
- 0.0.0.0/0
|
|
57
59
|
verify_host_key: false
|
|
58
60
|
location: nbg1
|
|
59
61
|
masters:
|
|
@@ -72,6 +74,8 @@ It should hopefully be self explanatory; you can run `hetzner-k3s releases` to s
|
|
|
72
74
|
|
|
73
75
|
If you are using Docker, then set `kubeconfig_path` to `/cluster/kubeconfig` so that the kubeconfig is created in the same directory where your config file is.
|
|
74
76
|
|
|
77
|
+
If you don't want to specify the Hetzner token in the config file (for example if you want to use the tool with CI), then you can use the `HCLOUD_TOKEN` environment variable instead, which has predecence.
|
|
78
|
+
|
|
75
79
|
**Important**: The tool assignes the label `cluster` to each server it creates, with the clsuter name you specify in the config file, as the value. So please ensure you don't create unrelated servers in the same project having
|
|
76
80
|
the label `cluster=<cluster name>`, because otherwise they will be deleted if you delete the cluster. I recommend you create a separate Hetzner project for each cluster, see note at the end of this README for more details.
|
|
77
81
|
|
|
@@ -235,6 +239,12 @@ I recommend that you create a separate Hetzner project for each cluster, because
|
|
|
235
239
|
|
|
236
240
|
## changelog
|
|
237
241
|
|
|
242
|
+
- 0.4.0
|
|
243
|
+
- Ensure the masters are removed from the API load balancer before deleting the load balancer
|
|
244
|
+
- Ensure the servers are removed from the firewall before deleting it
|
|
245
|
+
- Allow using an environment variable to specify the Hetzner token
|
|
246
|
+
- Allow restricting SSH access to the nodes to specific networks
|
|
247
|
+
|
|
238
248
|
- 0.3.9
|
|
239
249
|
- Add command "version" to print the version of the tool in use
|
|
240
250
|
|
|
@@ -5,7 +5,9 @@ module Hetzner
|
|
|
5
5
|
@cluster_name = cluster_name
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
def create
|
|
8
|
+
def create(ha:, networks:)
|
|
9
|
+
@ha = ha
|
|
10
|
+
@networks = networks
|
|
9
11
|
puts
|
|
10
12
|
|
|
11
13
|
if firewall = find_firewall
|
|
@@ -16,16 +18,21 @@ module Hetzner
|
|
|
16
18
|
|
|
17
19
|
puts "Creating firewall..."
|
|
18
20
|
|
|
19
|
-
response = hetzner_client.post("/firewalls",
|
|
21
|
+
response = hetzner_client.post("/firewalls", create_firewall_config).body
|
|
20
22
|
puts "...firewall created."
|
|
21
23
|
puts
|
|
22
24
|
|
|
23
25
|
JSON.parse(response)["firewall"]["id"]
|
|
24
26
|
end
|
|
25
27
|
|
|
26
|
-
def delete
|
|
28
|
+
def delete(servers)
|
|
27
29
|
if firewall = find_firewall
|
|
28
30
|
puts "Deleting firewall..."
|
|
31
|
+
|
|
32
|
+
servers.each do |server|
|
|
33
|
+
hetzner_client.post("/firewalls/#{firewall["id"]}/actions/remove_from_resources", remove_targets_config(server["id"]))
|
|
34
|
+
end
|
|
35
|
+
|
|
29
36
|
hetzner_client.delete("/firewalls", firewall["id"])
|
|
30
37
|
puts "...firewall deleted."
|
|
31
38
|
else
|
|
@@ -37,64 +44,79 @@ module Hetzner
|
|
|
37
44
|
|
|
38
45
|
private
|
|
39
46
|
|
|
40
|
-
attr_reader :hetzner_client, :cluster_name, :firewall
|
|
47
|
+
attr_reader :hetzner_client, :cluster_name, :firewall, :ha, :networks
|
|
48
|
+
|
|
49
|
+
def create_firewall_config
|
|
50
|
+
rules = [
|
|
51
|
+
{
|
|
52
|
+
"description": "Allow port 22 (SSH)",
|
|
53
|
+
"direction": "in",
|
|
54
|
+
"protocol": "tcp",
|
|
55
|
+
"port": "22",
|
|
56
|
+
"source_ips": networks,
|
|
57
|
+
"destination_ips": []
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"description": "Allow ICMP (ping)",
|
|
61
|
+
"direction": "in",
|
|
62
|
+
"protocol": "icmp",
|
|
63
|
+
"port": nil,
|
|
64
|
+
"source_ips": [
|
|
65
|
+
"0.0.0.0/0",
|
|
66
|
+
"::/0"
|
|
67
|
+
],
|
|
68
|
+
"destination_ips": []
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"description": "Allow all TCP traffic between nodes on the private network",
|
|
72
|
+
"direction": "in",
|
|
73
|
+
"protocol": "tcp",
|
|
74
|
+
"port": "any",
|
|
75
|
+
"source_ips": [
|
|
76
|
+
"10.0.0.0/16"
|
|
77
|
+
],
|
|
78
|
+
"destination_ips": []
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"description": "Allow all UDP traffic between nodes on the private network",
|
|
82
|
+
"direction": "in",
|
|
83
|
+
"protocol": "udp",
|
|
84
|
+
"port": "any",
|
|
85
|
+
"source_ips": [
|
|
86
|
+
"10.0.0.0/16"
|
|
87
|
+
],
|
|
88
|
+
"destination_ips": []
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
unless ha
|
|
93
|
+
rules << {
|
|
94
|
+
"description": "Allow port 6443 (Kubernetes API server)",
|
|
95
|
+
"direction": "in",
|
|
96
|
+
"protocol": "tcp",
|
|
97
|
+
"port": "6443",
|
|
98
|
+
"source_ips": [
|
|
99
|
+
"0.0.0.0/0",
|
|
100
|
+
"::/0"
|
|
101
|
+
],
|
|
102
|
+
"destination_ips": []
|
|
103
|
+
}
|
|
104
|
+
end
|
|
41
105
|
|
|
42
|
-
def firewall_config
|
|
43
106
|
{
|
|
44
107
|
name: cluster_name,
|
|
45
|
-
rules:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"0.0.0.0/0",
|
|
53
|
-
"::/0"
|
|
54
|
-
],
|
|
55
|
-
"destination_ips": []
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
"description": "Allow ICMP (ping)",
|
|
59
|
-
"direction": "in",
|
|
60
|
-
"protocol": "icmp",
|
|
61
|
-
"port": nil,
|
|
62
|
-
"source_ips": [
|
|
63
|
-
"0.0.0.0/0",
|
|
64
|
-
"::/0"
|
|
65
|
-
],
|
|
66
|
-
"destination_ips": []
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
"description": "Allow port 6443 (Kubernetes API server)",
|
|
70
|
-
"direction": "in",
|
|
71
|
-
"protocol": "tcp",
|
|
72
|
-
"port": "6443",
|
|
73
|
-
"source_ips": [
|
|
74
|
-
"0.0.0.0/0",
|
|
75
|
-
"::/0"
|
|
76
|
-
],
|
|
77
|
-
"destination_ips": []
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
"description": "Allow all TCP traffic between nodes on the private network",
|
|
81
|
-
"direction": "in",
|
|
82
|
-
"protocol": "tcp",
|
|
83
|
-
"port": "any",
|
|
84
|
-
"source_ips": [
|
|
85
|
-
"10.0.0.0/16"
|
|
86
|
-
],
|
|
87
|
-
"destination_ips": []
|
|
88
|
-
},
|
|
108
|
+
rules: rules
|
|
109
|
+
}
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def remove_targets_config(server_id)
|
|
113
|
+
{
|
|
114
|
+
"remove_from": [
|
|
89
115
|
{
|
|
90
|
-
"
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
"
|
|
94
|
-
"source_ips": [
|
|
95
|
-
"10.0.0.0/16"
|
|
96
|
-
],
|
|
97
|
-
"destination_ips": []
|
|
116
|
+
"server": {
|
|
117
|
+
"id": server_id
|
|
118
|
+
},
|
|
119
|
+
"type": "server"
|
|
98
120
|
}
|
|
99
121
|
]
|
|
100
122
|
}
|
|
@@ -19,7 +19,7 @@ module Hetzner
|
|
|
19
19
|
|
|
20
20
|
puts "Creating API load_balancer..."
|
|
21
21
|
|
|
22
|
-
response = hetzner_client.post("/load_balancers",
|
|
22
|
+
response = hetzner_client.post("/load_balancers", create_load_balancer_config).body
|
|
23
23
|
puts "...API load balancer created."
|
|
24
24
|
puts
|
|
25
25
|
|
|
@@ -29,6 +29,9 @@ module Hetzner
|
|
|
29
29
|
def delete(ha:)
|
|
30
30
|
if load_balancer = find_load_balancer
|
|
31
31
|
puts "Deleting API load balancer..." unless ha
|
|
32
|
+
|
|
33
|
+
hetzner_client.post("/load_balancers/#{load_balancer["id"]}/actions/remove_target", remove_targets_config)
|
|
34
|
+
|
|
32
35
|
hetzner_client.delete("/load_balancers", load_balancer["id"])
|
|
33
36
|
puts "...API load balancer deleted." unless ha
|
|
34
37
|
elsif ha
|
|
@@ -46,7 +49,7 @@ module Hetzner
|
|
|
46
49
|
"#{cluster_name}-api"
|
|
47
50
|
end
|
|
48
51
|
|
|
49
|
-
def
|
|
52
|
+
def create_load_balancer_config
|
|
50
53
|
{
|
|
51
54
|
"algorithm": {
|
|
52
55
|
"type": "round_robin"
|
|
@@ -76,6 +79,15 @@ module Hetzner
|
|
|
76
79
|
}
|
|
77
80
|
end
|
|
78
81
|
|
|
82
|
+
def remove_targets_config
|
|
83
|
+
{
|
|
84
|
+
"label_selector": {
|
|
85
|
+
"selector": "cluster=#{cluster_name},role=master"
|
|
86
|
+
},
|
|
87
|
+
"type": "label_selector"
|
|
88
|
+
}
|
|
89
|
+
end
|
|
90
|
+
|
|
79
91
|
def find_load_balancer
|
|
80
92
|
hetzner_client.get("/load_balancers")["load_balancers"].detect{ |load_balancer| load_balancer["name"] == load_balancer_name }
|
|
81
93
|
end
|
data/lib/hetzner/k3s/cli.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
require "thor"
|
|
2
2
|
require "http"
|
|
3
3
|
require "sshkey"
|
|
4
|
+
require 'ipaddr'
|
|
5
|
+
require 'open-uri'
|
|
4
6
|
|
|
5
7
|
require_relative "cluster"
|
|
6
8
|
require_relative "version"
|
|
@@ -23,7 +25,7 @@ module Hetzner
|
|
|
23
25
|
def create_cluster
|
|
24
26
|
validate_config_file :create
|
|
25
27
|
|
|
26
|
-
Cluster.new(hetzner_client: hetzner_client).create configuration: configuration
|
|
28
|
+
Cluster.new(hetzner_client: hetzner_client, hetzner_token: find_hetzner_token).create configuration: configuration
|
|
27
29
|
end
|
|
28
30
|
|
|
29
31
|
desc "delete-cluster", "Delete an existing k3s cluster in Hetzner Cloud"
|
|
@@ -31,7 +33,7 @@ module Hetzner
|
|
|
31
33
|
|
|
32
34
|
def delete_cluster
|
|
33
35
|
validate_config_file :delete
|
|
34
|
-
Cluster.new(hetzner_client: hetzner_client).delete configuration: configuration
|
|
36
|
+
Cluster.new(hetzner_client: hetzner_client, hetzner_token: find_hetzner_token).delete configuration: configuration
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
desc "upgrade-cluster", "Upgrade an existing k3s cluster in Hetzner Cloud to a new version"
|
|
@@ -41,7 +43,7 @@ module Hetzner
|
|
|
41
43
|
|
|
42
44
|
def upgrade_cluster
|
|
43
45
|
validate_config_file :upgrade
|
|
44
|
-
Cluster.new(hetzner_client: hetzner_client).upgrade configuration: configuration, new_k3s_version: options[:new_k3s_version], config_file: options[:config_file]
|
|
46
|
+
Cluster.new(hetzner_client: hetzner_client, hetzner_token: find_hetzner_token).upgrade configuration: configuration, new_k3s_version: options[:new_k3s_version], config_file: options[:config_file]
|
|
45
47
|
end
|
|
46
48
|
|
|
47
49
|
desc "releases", "List available k3s releases"
|
|
@@ -82,6 +84,7 @@ module Hetzner
|
|
|
82
84
|
case action
|
|
83
85
|
when :create
|
|
84
86
|
validate_ssh_key
|
|
87
|
+
validate_ssh_allowed_networks
|
|
85
88
|
validate_location
|
|
86
89
|
validate_k3s_version
|
|
87
90
|
validate_masters
|
|
@@ -107,12 +110,26 @@ module Hetzner
|
|
|
107
110
|
end
|
|
108
111
|
end
|
|
109
112
|
|
|
113
|
+
def valid_token?
|
|
114
|
+
return @valid unless @valid.nil?
|
|
115
|
+
|
|
116
|
+
begin
|
|
117
|
+
token = find_hetzner_token
|
|
118
|
+
@hetzner_client = Hetzner::Client.new(token: token)
|
|
119
|
+
response = hetzner_client.get("/locations")
|
|
120
|
+
error_code = response.dig("error", "code")
|
|
121
|
+
@valid = if error_code and error_code.size > 0
|
|
122
|
+
false
|
|
123
|
+
else
|
|
124
|
+
true
|
|
125
|
+
end
|
|
126
|
+
rescue
|
|
127
|
+
@valid = false
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
110
131
|
def validate_token
|
|
111
|
-
|
|
112
|
-
@hetzner_client = Hetzner::Client.new(token: token)
|
|
113
|
-
hetzner_client.get("/locations")
|
|
114
|
-
rescue
|
|
115
|
-
errors << "Invalid Hetzner Cloid token"
|
|
132
|
+
errors << "Invalid Hetzner Cloud token" unless valid_token?
|
|
116
133
|
end
|
|
117
134
|
|
|
118
135
|
def validate_cluster_name
|
|
@@ -149,6 +166,7 @@ module Hetzner
|
|
|
149
166
|
end
|
|
150
167
|
|
|
151
168
|
def server_types
|
|
169
|
+
return [] unless valid_token?
|
|
152
170
|
@server_types ||= hetzner_client.get("/server_types")["server_types"].map{ |server_type| server_type["name"] }
|
|
153
171
|
rescue
|
|
154
172
|
@errors << "Cannot fetch server types with Hetzner API, please try again later"
|
|
@@ -156,13 +174,15 @@ module Hetzner
|
|
|
156
174
|
end
|
|
157
175
|
|
|
158
176
|
def locations
|
|
177
|
+
return [] unless valid_token?
|
|
159
178
|
@locations ||= hetzner_client.get("/locations")["locations"].map{ |location| location["name"] }
|
|
160
179
|
rescue
|
|
161
180
|
@errors << "Cannot fetch locations with Hetzner API, please try again later"
|
|
162
|
-
|
|
181
|
+
[]
|
|
163
182
|
end
|
|
164
183
|
|
|
165
184
|
def validate_location
|
|
185
|
+
return if locations.empty? && !valid_token?
|
|
166
186
|
errors << "Invalid location - available locations: nbg1 (Nuremberg, Germany), fsn1 (Falkenstein, Germany), hel1 (Helsinki, Finland)" unless locations.include? configuration.dig("location")
|
|
167
187
|
end
|
|
168
188
|
|
|
@@ -271,7 +291,7 @@ module Hetzner
|
|
|
271
291
|
instance_group_errors << "#{instance_group_type} is in an invalid format"
|
|
272
292
|
end
|
|
273
293
|
|
|
274
|
-
unless server_types.include?(instance_group["instance_type"])
|
|
294
|
+
unless !valid_token? or server_types.include?(instance_group["instance_type"])
|
|
275
295
|
instance_group_errors << "#{instance_group_type} has an invalid instance type"
|
|
276
296
|
end
|
|
277
297
|
|
|
@@ -296,16 +316,62 @@ module Hetzner
|
|
|
296
316
|
config_hash = YAML.load_file(File.expand_path(configuration["kubeconfig_path"]))
|
|
297
317
|
config_hash['current-context'] = configuration["cluster_name"]
|
|
298
318
|
@kubernetes_client = K8s::Client.config(K8s::Config.new(config_hash))
|
|
299
|
-
rescue
|
|
300
319
|
errors << "Cannot connect to the Kubernetes cluster"
|
|
301
320
|
false
|
|
302
321
|
end
|
|
303
322
|
|
|
304
|
-
|
|
305
323
|
def validate_verify_host_key
|
|
306
324
|
return unless [true, false].include?(configuration.fetch("ssh_key_path", false))
|
|
307
325
|
errors << "Please set the verify_host_key option to either true or false"
|
|
308
326
|
end
|
|
327
|
+
|
|
328
|
+
def find_hetzner_token
|
|
329
|
+
@token = ENV["HCLOUD_TOKEN"]
|
|
330
|
+
return @token if @token
|
|
331
|
+
@token = configuration.dig("hetzner_token")
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def validate_ssh_allowed_networks
|
|
335
|
+
networks ||= configuration.dig("ssh_allowed_networks")
|
|
336
|
+
|
|
337
|
+
if networks.nil? or networks.empty?
|
|
338
|
+
errors << "At least one network/IP range must be specified for SSH access"
|
|
339
|
+
return
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
invalid_networks = networks.reject do |network|
|
|
343
|
+
IPAddr.new(network) rescue false
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
unless invalid_networks.empty?
|
|
347
|
+
invalid_networks.each do |network|
|
|
348
|
+
errors << "The network #{network} is an invalid range"
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
invalid_ranges = networks.reject do |network|
|
|
353
|
+
network.include? "/"
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
unless invalid_ranges.empty?
|
|
357
|
+
invalid_ranges.each do |network|
|
|
358
|
+
errors << "Please use the CIDR notation for the networks to avoid ambiguity"
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
return unless invalid_networks.empty?
|
|
363
|
+
|
|
364
|
+
current_ip = URI.open('http://whatismyip.akamai.com').read
|
|
365
|
+
|
|
366
|
+
current_ip_networks = networks.detect do |network|
|
|
367
|
+
IPAddr.new(network).include?(current_ip) rescue false
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
unless current_ip_networks
|
|
371
|
+
errors << "Your current IP #{current_ip} is not included into any of the networks you've specified, so we won't be able to SSH into the nodes"
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
309
375
|
end
|
|
310
376
|
end
|
|
311
377
|
end
|
data/lib/hetzner/k3s/cluster.rb
CHANGED
|
@@ -16,12 +16,12 @@ require_relative "../k3s/client_patch"
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class Cluster
|
|
19
|
-
def initialize(hetzner_client:)
|
|
19
|
+
def initialize(hetzner_client:, hetzner_token:)
|
|
20
20
|
@hetzner_client = hetzner_client
|
|
21
|
+
@hetzner_token = hetzner_token
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
def create(configuration:)
|
|
24
|
-
@hetzner_token = configuration.dig("hetzner_token")
|
|
25
25
|
@cluster_name = configuration.dig("cluster_name")
|
|
26
26
|
@kubeconfig_path = File.expand_path(configuration.dig("kubeconfig_path"))
|
|
27
27
|
@ssh_key_path = File.expand_path(configuration.dig("ssh_key_path"))
|
|
@@ -31,6 +31,7 @@ class Cluster
|
|
|
31
31
|
@location = configuration.dig("location")
|
|
32
32
|
@verify_host_key = configuration.fetch("verify_host_key", false)
|
|
33
33
|
@servers = []
|
|
34
|
+
@networks = configuration.dig("ssh_allowed_networks")
|
|
34
35
|
|
|
35
36
|
create_resources
|
|
36
37
|
|
|
@@ -69,7 +70,7 @@ class Cluster
|
|
|
69
70
|
:masters_config, :worker_node_pools,
|
|
70
71
|
:location, :ssh_key_path, :kubernetes_client,
|
|
71
72
|
:hetzner_token, :tls_sans, :new_k3s_version, :configuration,
|
|
72
|
-
:config_file, :verify_host_key
|
|
73
|
+
:config_file, :verify_host_key, :networks
|
|
73
74
|
|
|
74
75
|
|
|
75
76
|
def latest_k3s_version
|
|
@@ -78,10 +79,13 @@ class Cluster
|
|
|
78
79
|
end
|
|
79
80
|
|
|
80
81
|
def create_resources
|
|
82
|
+
master_instance_type = masters_config["instance_type"]
|
|
83
|
+
masters_count = masters_config["instance_count"]
|
|
84
|
+
|
|
81
85
|
firewall_id = Hetzner::Firewall.new(
|
|
82
86
|
hetzner_client: hetzner_client,
|
|
83
87
|
cluster_name: cluster_name
|
|
84
|
-
).create
|
|
88
|
+
).create(ha: (masters_count > 1), networks: networks)
|
|
85
89
|
|
|
86
90
|
network_id = Hetzner::Network.new(
|
|
87
91
|
hetzner_client: hetzner_client,
|
|
@@ -95,9 +99,6 @@ class Cluster
|
|
|
95
99
|
|
|
96
100
|
server_configs = []
|
|
97
101
|
|
|
98
|
-
master_instance_type = masters_config["instance_type"]
|
|
99
|
-
masters_count = masters_config["instance_count"]
|
|
100
|
-
|
|
101
102
|
masters_count.times do |i|
|
|
102
103
|
server_configs << {
|
|
103
104
|
location: location,
|
|
@@ -150,42 +151,15 @@ class Cluster
|
|
|
150
151
|
end
|
|
151
152
|
|
|
152
153
|
def delete_resources
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
threads = servers.map do |node|
|
|
159
|
-
Thread.new do
|
|
160
|
-
Hetzner::Server.new(hetzner_client: hetzner_client, cluster_name: cluster_name).delete(server_name: node.metadata[:name])
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
threads.each(&:join) unless threads.empty?
|
|
165
|
-
end
|
|
166
|
-
rescue Timeout::Error, Excon::Error::Socket
|
|
167
|
-
puts "Unable to fetch nodes from Kubernetes API. Is the cluster online?"
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
# Deleting nodes defined in the config file just in case there are leftovers i.e. nodes that
|
|
171
|
-
# were not part of the cluster for some reason
|
|
172
|
-
|
|
173
|
-
threads = all_servers.map do |server|
|
|
174
|
-
Thread.new do
|
|
175
|
-
Hetzner::Server.new(hetzner_client: hetzner_client, cluster_name: cluster_name).delete(server_name: server["name"])
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
threads.each(&:join) unless threads.empty?
|
|
180
|
-
|
|
181
|
-
puts
|
|
182
|
-
|
|
183
|
-
sleep 5 # give time for the servers to actually be deleted
|
|
154
|
+
Hetzner::LoadBalancer.new(
|
|
155
|
+
hetzner_client: hetzner_client,
|
|
156
|
+
cluster_name: cluster_name
|
|
157
|
+
).delete(ha: (masters.size > 1))
|
|
184
158
|
|
|
185
159
|
Hetzner::Firewall.new(
|
|
186
160
|
hetzner_client: hetzner_client,
|
|
187
161
|
cluster_name: cluster_name
|
|
188
|
-
).delete
|
|
162
|
+
).delete(all_servers)
|
|
189
163
|
|
|
190
164
|
Hetzner::Network.new(
|
|
191
165
|
hetzner_client: hetzner_client,
|
|
@@ -197,11 +171,13 @@ class Cluster
|
|
|
197
171
|
cluster_name: cluster_name
|
|
198
172
|
).delete(ssh_key_path: ssh_key_path)
|
|
199
173
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
174
|
+
threads = all_servers.map do |server|
|
|
175
|
+
Thread.new do
|
|
176
|
+
Hetzner::Server.new(hetzner_client: hetzner_client, cluster_name: cluster_name).delete(server_name: server["name"])
|
|
177
|
+
end
|
|
178
|
+
end
|
|
204
179
|
|
|
180
|
+
threads.each(&:join) unless threads.empty?
|
|
205
181
|
end
|
|
206
182
|
|
|
207
183
|
def upgrade_cluster
|
|
@@ -249,6 +225,7 @@ class Cluster
|
|
|
249
225
|
--kube-scheduler-arg="bind-address=0.0.0.0" \
|
|
250
226
|
--node-taint CriticalAddonsOnly=true:NoExecute \
|
|
251
227
|
--kubelet-arg="cloud-provider=external" \
|
|
228
|
+
--advertise-address=$(hostname -I | awk '{print $2}') \
|
|
252
229
|
--node-ip=$(hostname -I | awk '{print $2}') \
|
|
253
230
|
--node-external-ip=$(hostname -I | awk '{print $1}') \
|
|
254
231
|
--flannel-iface=#{flannel_interface} \
|
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.4.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-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: thor
|