hetzner-k3s 0.5.8 → 0.5.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c0d855f62ab9e222986d220edcdde203f7eb363ab725c8aa4e1f1389f2b251e2
4
- data.tar.gz: 19d6a1ff6769cbec2207539d615d6a873eaede07aec9b15a43e6ef9d79101731
3
+ metadata.gz: 65fe7fa7e6bdeeb49175cfb92184ab320ef8ac40a3aa03f921f1e5fda1b172d7
4
+ data.tar.gz: 1448919374ef90f7614580251b28000c470b0ed7fba39724e8953f29f176c1bb
5
5
  SHA512:
6
- metadata.gz: d8fdc127e71f790e530d3abf97ca30d22b28d75eff687e436d1351336595ae7ba912c0c947c8b1738ab9d50447e577c4be753db3c20a2bfbcca195fcc6a0d193
7
- data.tar.gz: 5cb4203c6270f0e82b66049fefd7244aaeb42e1dd89e257a0e1ba4b126db04ed8adf27b715b10874248f6661b6fed3126e562913ec2342f086bbff4c717c329b
6
+ metadata.gz: 15f783adb7408a1df5c3e9a114d9dcbc61830ead33acb7bc162ca935b6cdcfe26507e2c404d48c831cae600fe765adc31e686b511c4f675c561d6eb9acabafd2
7
+ data.tar.gz: df10039025e735d6a73c84fb60516d70bc0aa48220db1bab9d99dd30caad87339ba6be83e2d3f1029f40f95719a6fcde5784956c86ef20d524b67d504f93920a
data/.rubocop.yml CHANGED
@@ -1,4 +1,4 @@
1
- Gemspec/DateAssignment: # new in 1.10
1
+ Gemspec/DeprecatedAttributeAssignment: # new in 1.10
2
2
  Enabled: true
3
3
  Gemspec/RequireMFA: # new in 1.23
4
4
  Enabled: true
data/README.md CHANGED
@@ -44,7 +44,7 @@ Alternatively, if you don't want to set up a Ruby runtime but have Docker instal
44
44
  docker run --rm -it \
45
45
  -v ${PWD}:/cluster \
46
46
  -v ${HOME}/.ssh:/tmp/.ssh \
47
- vitobotta/hetzner-k3s:v0.5.8 \
47
+ vitobotta/hetzner-k3s:v0.5.9 \
48
48
  create-cluster \
49
49
  --config-file /cluster/test.yaml
50
50
  ```
@@ -65,6 +65,8 @@ public_ssh_key_path: "~/.ssh/id_rsa.pub"
65
65
  private_ssh_key_path: "~/.ssh/id_rsa"
66
66
  ssh_allowed_networks:
67
67
  - 0.0.0.0/0
68
+ api_allowed_networks:
69
+ - 0.0.0.0/0
68
70
  verify_host_key: false
69
71
  location: nbg1
70
72
  schedule_workloads_on_masters: false
@@ -104,6 +106,7 @@ enable_encryption: true
104
106
  # kube_proxy_args:
105
107
  # - arg1
106
108
  # - ...
109
+ # existing_network: <specify if you want to use an existing network, otherwise one will be created for this cluster>
107
110
 
108
111
  ```
109
112
 
@@ -143,9 +146,11 @@ curl \
143
146
  'https://api.hetzner.cloud/v1/images'
144
147
  ```
145
148
 
146
- Note that if you use a custom image, the creation of the servers may take longer than when using the default image.
149
+ Notes:
147
150
 
148
- Also note: the option `verify_host_key` is by default set to `false` to disable host key verification. This is because sometimes when creating new servers, Hetzner may assign IP addresses that were previously used by other servers you owned in the past. Therefore the host key verification would fail. If you set this option to `true` and this happens, the tool won't be able to continue creating the cluster until you resolve the issue with one of the suggestions it will give you.
151
+ - if you use a custom image, the creation of the servers may take longer than when using the default image
152
+ - the option `verify_host_key` is by default set to `false` to disable host key verification. This is because sometimes when creating new servers, Hetzner may assign IP addresses that were previously used by other servers you owned in the past. Therefore the host key verification would fail. If you set this option to `true` and this happens, the tool won't be able to continue creating the cluster until you resolve the issue with one of the suggestions it will give you
153
+ - the setting `api_allowed_networks` allows specifying which networks can access the Kubernetes API, but this only works with single master clusters currently. Multi-master HA clusters require a load balancer for the API, but load balancers are not yet covered by Hetzner's firewalls.
149
154
 
150
155
  Finally, to create the cluster run:
151
156
 
data/bin/build.sh CHANGED
@@ -4,9 +4,9 @@ set -e
4
4
 
5
5
  IMAGE="vitobotta/hetzner-k3s"
6
6
 
7
- docker build -t ${IMAGE}:v0.5.8 \
7
+ docker build -t ${IMAGE}:v0.5.9 \
8
8
  --platform=linux/amd64 \
9
- --cache-from ${IMAGE}:v0.5.7 \
9
+ --cache-from ${IMAGE}:v0.5.8 \
10
10
  --build-arg BUILDKIT_INLINE_CACHE=1 .
11
11
 
12
- docker push vitobotta/hetzner-k3s:v0.5.8
12
+ docker push vitobotta/hetzner-k3s:v0.5.9
@@ -7,9 +7,10 @@ module Hetzner
7
7
  @cluster_name = cluster_name
8
8
  end
9
9
 
10
- def create(high_availability:, networks:)
10
+ def create(high_availability:, ssh_networks:, api_networks:)
11
11
  @high_availability = high_availability
12
- @networks = networks
12
+ @ssh_networks = ssh_networks
13
+ @api_networks = api_networks
13
14
  puts
14
15
 
15
16
  if (firewall = find_firewall)
@@ -47,7 +48,7 @@ module Hetzner
47
48
 
48
49
  private
49
50
 
50
- attr_reader :hetzner_client, :cluster_name, :firewall, :high_availability, :networks
51
+ attr_reader :hetzner_client, :cluster_name, :firewall, :high_availability, :ssh_networks, :api_networks
51
52
 
52
53
  def create_firewall_config
53
54
  rules = [
@@ -56,7 +57,7 @@ module Hetzner
56
57
  direction: 'in',
57
58
  protocol: 'tcp',
58
59
  port: '22',
59
- source_ips: networks,
60
+ source_ips: ssh_networks,
60
61
  destination_ips: []
61
62
  },
62
63
  {
@@ -98,10 +99,7 @@ module Hetzner
98
99
  direction: 'in',
99
100
  protocol: 'tcp',
100
101
  port: '6443',
101
- source_ips: [
102
- '0.0.0.0/0',
103
- '::/0'
104
- ],
102
+ source_ips: api_networks,
105
103
  destination_ips: []
106
104
  }
107
105
  end
@@ -2,9 +2,10 @@
2
2
 
3
3
  module Hetzner
4
4
  class Network
5
- def initialize(hetzner_client:, cluster_name:)
5
+ def initialize(hetzner_client:, cluster_name:, existing_network:)
6
6
  @hetzner_client = hetzner_client
7
7
  @cluster_name = cluster_name
8
+ @existing_network = existing_network
8
9
  end
9
10
 
10
11
  def create(location:)
@@ -29,9 +30,13 @@ module Hetzner
29
30
 
30
31
  def delete
31
32
  if (network = find_network)
32
- puts 'Deleting network...'
33
- hetzner_client.delete('/networks', network['id'])
34
- puts '...network deleted.'
33
+ if network["name"] == existing_network
34
+ puts "Network existed before cluster, skipping."
35
+ else
36
+ puts 'Deleting network...'
37
+ hetzner_client.delete('/networks', network['id'])
38
+ puts '...network deleted.'
39
+ end
35
40
  else
36
41
  puts 'Network no longer exists, skipping.'
37
42
  end
@@ -39,9 +44,18 @@ module Hetzner
39
44
  puts
40
45
  end
41
46
 
47
+ def find_network
48
+ network_name = existing_network || cluster_name
49
+ hetzner_client.get('/networks')['networks'].detect { |network| network['name'] == network_name }
50
+ end
51
+
52
+ def get
53
+ find_network
54
+ end
55
+
42
56
  private
43
57
 
44
- attr_reader :hetzner_client, :cluster_name, :location
58
+ attr_reader :hetzner_client, :cluster_name, :location, :existing_network
45
59
 
46
60
  def network_config
47
61
  {
@@ -56,9 +70,5 @@ module Hetzner
56
70
  ]
57
71
  }
58
72
  end
59
-
60
- def find_network
61
- hetzner_client.get('/networks')['networks'].detect { |network| network['name'] == cluster_name }
62
- end
63
73
  end
64
74
  end
@@ -35,7 +35,8 @@ class Cluster
35
35
  @masters_location = configuration['location']
36
36
  @verify_host_key = configuration.fetch('verify_host_key', false)
37
37
  @servers = []
38
- @networks = configuration['ssh_allowed_networks']
38
+ @ssh_networks = configuration['ssh_allowed_networks']
39
+ @api_networks = configuration['api_allowed_networks']
39
40
  @enable_encryption = configuration.fetch('enable_encryption', false)
40
41
  @kube_api_server_args = configuration.fetch('kube_api_server_args', [])
41
42
  @kube_scheduler_args = configuration.fetch('kube_scheduler_args', [])
@@ -82,10 +83,10 @@ class Cluster
82
83
  :masters_config, :worker_node_pools,
83
84
  :masters_location, :public_ssh_key_path,
84
85
  :hetzner_token, :new_k3s_version,
85
- :config_file, :verify_host_key, :networks, :private_ssh_key_path,
86
+ :config_file, :verify_host_key, :ssh_networks, :private_ssh_key_path,
86
87
  :enable_encryption, :kube_api_server_args, :kube_scheduler_args,
87
88
  :kube_controller_manager_args, :kube_cloud_controller_manager_args,
88
- :kubelet_args, :kube_proxy_args
89
+ :kubelet_args, :kube_proxy_args, :api_networks
89
90
 
90
91
  def find_worker_node_pools(configuration)
91
92
  configuration.fetch('worker_node_pools', [])
@@ -115,7 +116,7 @@ class Cluster
115
116
 
116
117
  Hetzner::Firewall.new(hetzner_client:, cluster_name:).delete(all_servers)
117
118
 
118
- Hetzner::Network.new(hetzner_client:, cluster_name:).delete
119
+ Hetzner::Network.new(hetzner_client:, cluster_name:, existing_network:).delete
119
120
 
120
121
  Hetzner::SSHKey.new(hetzner_client:, cluster_name:).delete(public_ssh_key_path:)
121
122
 
@@ -195,7 +196,21 @@ class Cluster
195
196
  def master_script(master)
196
197
  server = master == first_master ? ' --cluster-init ' : " --server https://#{api_server_ip}:6443 "
197
198
  flannel_interface = find_flannel_interface(master)
198
- flannel_wireguard = enable_encryption ? ' --flannel-backend=wireguard ' : ' '
199
+
200
+ available_k3s_releases = Hetzner::Configuration.available_releases
201
+ wireguard_native_min_version_index = available_k3s_releases.find_index('v1.23.6+k3s1')
202
+ selected_version_index = available_k3s_releases.find_index(k3s_version)
203
+
204
+ flannel_wireguard = if enable_encryption
205
+ if selected_version_index >= wireguard_native_min_version_index
206
+ ' --flannel-backend=wireguard-native '
207
+ else
208
+ ' --flannel-backend=wireguard '
209
+ end
210
+ else
211
+ ' '
212
+ end
213
+
199
214
  extra_args = "#{kube_api_server_args_list} #{kube_scheduler_args_list} #{kube_controller_manager_args_list} #{kube_cloud_controller_manager_args_list} #{kubelet_args_list} #{kube_proxy_args_list}"
200
215
  taint = schedule_workloads_on_masters? ? ' ' : ' --node-taint CriticalAddonsOnly=true:NoExecute '
201
216
 
@@ -293,7 +308,7 @@ class Cluster
293
308
  namespace: 'kube-system'
294
309
  name: 'hcloud'
295
310
  stringData:
296
- network: "#{cluster_name}"
311
+ network: "#{existing_network || cluster_name}"
297
312
  token: "#{configuration.hetzner_token}"
298
313
  EOF
299
314
  BASH
@@ -313,7 +328,7 @@ class Cluster
313
328
  puts
314
329
  puts 'Deploying k3s System Upgrade Controller...'
315
330
 
316
- cmd = 'kubectl apply -f https://github.com/rancher/system-upgrade-controller/releases/download/v0.8.1/system-upgrade-controller.yaml'
331
+ cmd = 'kubectl apply -f https://github.com/rancher/system-upgrade-controller/releases/download/v0.9.1/system-upgrade-controller.yaml'
317
332
 
318
333
  run cmd, kubeconfig_path: kubeconfig_path
319
334
 
@@ -340,7 +355,7 @@ class Cluster
340
355
 
341
356
  run cmd, kubeconfig_path: kubeconfig_path
342
357
 
343
- cmd = 'kubectl apply -f https://raw.githubusercontent.com/hetznercloud/csi-driver/v1.6.0/deploy/kubernetes/hcloud-csi.yml'
358
+ cmd = 'kubectl apply -f https://raw.githubusercontent.com/hetznercloud/csi-driver/master/deploy/kubernetes/hcloud-csi.yml'
344
359
 
345
360
  run cmd, kubeconfig_path: kubeconfig_path
346
361
 
@@ -470,11 +485,11 @@ class Cluster
470
485
  end
471
486
 
472
487
  def firewall_id
473
- @firewall_id ||= Hetzner::Firewall.new(hetzner_client:, cluster_name:).create(high_availability: (masters_count > 1), networks:)
488
+ @firewall_id ||= Hetzner::Firewall.new(hetzner_client:, cluster_name:).create(high_availability: (masters_count > 1), ssh_networks:, api_networks:)
474
489
  end
475
490
 
476
491
  def network_id
477
- @network_id ||= Hetzner::Network.new(hetzner_client:, cluster_name:).create(location: masters_location)
492
+ @network_id ||= Hetzner::Network.new(hetzner_client:, cluster_name:, existing_network:).create(location: masters_location)
478
493
  end
479
494
 
480
495
  def ssh_key_id
@@ -642,4 +657,8 @@ class Cluster
642
657
  def hetzner_client
643
658
  configuration.hetzner_client
644
659
  end
660
+
661
+ def existing_network
662
+ configuration["existing_network"]
663
+ end
645
664
  end
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Hetzner
4
4
  class Configuration
5
- GITHUB_DELIM_LINKS = ','.freeze
6
- GITHUB_LINK_REGEX = /<([^>]+)>; rel=\"([^\"]+)\"/
5
+ GITHUB_DELIM_LINKS = ','
6
+ GITHUB_LINK_REGEX = /<([^>]+)>; rel="([^"]+)"/
7
7
 
8
8
  attr_reader :hetzner_client
9
9
 
@@ -52,7 +52,7 @@ module Hetzner
52
52
  releases = page_releases
53
53
  link_header = response.headers['link']
54
54
 
55
- while !link_header.nil?
55
+ until link_header.nil?
56
56
  next_page_url = extract_next_github_page_url(link_header)
57
57
 
58
58
  break if next_page_url.nil?
@@ -67,10 +67,10 @@ module Hetzner
67
67
  releases.sort
68
68
  end
69
69
  rescue StandardError
70
- if defined?errors
71
- errors << 'Cannot fetch the releases with Hetzner API, please try again later'
70
+ if defined? errors
71
+ errors << 'Cannot fetch the releases with Github API, please try again later. This may be due to API rate limits.'
72
72
  else
73
- puts 'Cannot fetch the releases with Hetzner API, please try again later'
73
+ puts 'Cannot fetch the releases with Github API, please try again later. This may be due to API rate limits.'
74
74
  end
75
75
  end
76
76
 
@@ -104,9 +104,10 @@ module Hetzner
104
104
  def self.extract_next_github_page_url(link_header)
105
105
  link_header.split(GITHUB_DELIM_LINKS).each do |link|
106
106
  GITHUB_LINK_REGEX.match(link.strip) do |match|
107
- url_part, meta_part = match[1], match[2]
107
+ url_part = match[1]
108
+ meta_part = match[2]
108
109
  next if !url_part || !meta_part
109
- return url_part if meta_part == "next"
110
+ return url_part if meta_part == 'next'
110
111
  end
111
112
  end
112
113
 
@@ -115,7 +116,7 @@ module Hetzner
115
116
 
116
117
  def self.assign_url_part(meta_part, url_part)
117
118
  case meta_part
118
- when "next"
119
+ when 'next'
119
120
  url_part
120
121
  end
121
122
  end
@@ -124,6 +125,7 @@ module Hetzner
124
125
  validate_public_ssh_key
125
126
  validate_private_ssh_key
126
127
  validate_ssh_allowed_networks
128
+ validate_api_allowed_networks
127
129
  validate_masters_location
128
130
  validate_k3s_version
129
131
  validate_masters
@@ -137,6 +139,7 @@ module Hetzner
137
139
  validate_kube_cloud_controller_manager_args
138
140
  validate_kubelet_args
139
141
  validate_kube_proxy_args
142
+ validate_existing_network
140
143
  end
141
144
 
142
145
  def validate_upgrade
@@ -165,11 +168,11 @@ module Hetzner
165
168
  errors << 'Invalid Private SSH key path'
166
169
  end
167
170
 
168
- def validate_ssh_allowed_networks
169
- networks ||= configuration['ssh_allowed_networks']
171
+ def validate_networks(configuration_option, access_type)
172
+ networks ||= configuration[configuration_option]
170
173
 
171
174
  if networks.nil? || networks.empty?
172
- errors << 'At least one network/IP range must be specified for SSH access'
175
+ errors << "At least one network/IP range must be specified for #{access_type} access"
173
176
  return
174
177
  end
175
178
 
@@ -181,7 +184,7 @@ module Hetzner
181
184
 
182
185
  unless invalid_networks.empty?
183
186
  invalid_networks.each do |network|
184
- errors << "The network #{network} is an invalid range"
187
+ errors << "The #{access_type} network #{network} is an invalid range"
185
188
  end
186
189
  end
187
190
 
@@ -191,7 +194,7 @@ module Hetzner
191
194
 
192
195
  unless invalid_ranges.empty?
193
196
  invalid_ranges.each do |_network|
194
- errors << 'Please use the CIDR notation for the networks to avoid ambiguity'
197
+ errors << 'Please use the CIDR notation for the #{access_type} networks to avoid ambiguity'
195
198
  end
196
199
  end
197
200
 
@@ -199,13 +202,30 @@ module Hetzner
199
202
 
200
203
  current_ip = URI.open('http://whatismyip.akamai.com').read
201
204
 
202
- current_ip_networks = networks.detect do |network|
205
+ current_ip_network = networks.detect do |network|
203
206
  IPAddr.new(network).include?(current_ip)
204
207
  rescue StandardError
205
208
  false
206
209
  end
207
210
 
208
- 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" unless current_ip_networks
211
+ unless current_ip_network
212
+ case access_type
213
+ when "SSH"
214
+ errors << "Your current IP #{current_ip} is not included into any of the #{access_type} networks you've specified, so we won't be able to SSH into the nodes "
215
+ when "API"
216
+ errors << "Your current IP #{current_ip} is not included into any of the #{access_type} networks you've specified, so we won't be able to connect to the Kubernetes API"
217
+ end
218
+ end
219
+ end
220
+
221
+
222
+ def validate_ssh_allowed_networks
223
+ return
224
+ validate_networks('ssh_allowed_networks', 'SSH')
225
+ end
226
+
227
+ def validate_api_allowed_networks
228
+ validate_networks('api_allowed_networks', 'API')
209
229
  end
210
230
 
211
231
  def validate_masters_location
@@ -450,5 +470,15 @@ module Hetzner
450
470
  @errors << 'Cannot fetch server types with Hetzner API, please try again later'
451
471
  false
452
472
  end
473
+
474
+ def validate_existing_network
475
+ if configuration["existing_network"]
476
+ existing_network = Hetzner::Network.new(hetzner_client:, cluster_name: configuration["cluster_name"], existing_network: configuration["existing_network"]).get
477
+
478
+ unless existing_network
479
+ @errors << "You have specified that you want to use the existing network named '#{configuration["existing_network"]} but this network doesn't exist"
480
+ end
481
+ end
482
+ end
453
483
  end
454
484
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Hetzner
4
4
  module K3s
5
- VERSION = '0.5.8'
5
+ VERSION = '0.5.9'
6
6
  end
7
7
  end
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.5.8
4
+ version: 0.5.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vito Botta
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-08-11 00:00:00.000000000 Z
11
+ date: 2022-08-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bcrypt_pbkdf