hetzner-k3s 0.5.6 → 0.5.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,6 +8,7 @@ require 'open-uri'
8
8
  require 'yaml'
9
9
 
10
10
  require_relative 'cluster'
11
+ require_relative 'configuration'
11
12
  require_relative 'version'
12
13
 
13
14
  module Hetzner
@@ -17,13 +18,6 @@ module Hetzner
17
18
  true
18
19
  end
19
20
 
20
- def initialize(*args)
21
- @errors = []
22
- @used_server_types = []
23
-
24
- super
25
- end
26
-
27
21
  desc 'version', 'Print the version'
28
22
  def version
29
23
  puts Hetzner::K3s::VERSION
@@ -32,15 +26,15 @@ module Hetzner
32
26
  desc 'create-cluster', 'Create a k3s cluster in Hetzner Cloud'
33
27
  option :config_file, required: true
34
28
  def create_cluster
35
- validate_configuration :create
36
- Cluster.new(hetzner_client:, hetzner_token:).create configuration:
29
+ configuration.validate action: :create
30
+ Cluster.new(configuration:).create
37
31
  end
38
32
 
39
33
  desc 'delete-cluster', 'Delete an existing k3s cluster in Hetzner Cloud'
40
34
  option :config_file, required: true
41
35
  def delete_cluster
42
- validate_configuration :delete
43
- Cluster.new(hetzner_client:, hetzner_token:).delete configuration:
36
+ configuration.validate action: :delete
37
+ Cluster.new(configuration:).delete
44
38
  end
45
39
 
46
40
  desc 'upgrade-cluster', 'Upgrade an existing k3s cluster in Hetzner Cloud to a new version'
@@ -48,394 +42,28 @@ module Hetzner
48
42
  option :new_k3s_version, required: true
49
43
  option :force, default: 'false'
50
44
  def upgrade_cluster
51
- validate_configuration :upgrade
52
-
53
- Cluster.new(hetzner_client:, hetzner_token:)
54
- .upgrade(configuration:, new_k3s_version: options[:new_k3s_version], config_file: options[:config_file])
45
+ configuration.validate action: :upgrade
46
+ Cluster.new(configuration:).upgrade(new_k3s_version: options[:new_k3s_version], config_file: options[:config_file])
55
47
  end
56
48
 
57
49
  desc 'releases', 'List available k3s releases'
58
50
  def releases
59
- available_releases.each do |release|
51
+ Hetzner::Configuration.available_releases.each do |release|
60
52
  puts release
61
53
  end
62
54
  end
63
55
 
64
56
  private
65
57
 
66
- attr_reader :configuration, :hetzner_client, :k3s_version
67
- attr_accessor :errors, :used_server_types
68
-
69
- def validate_configuration(action)
70
- validate_configuration_file
71
- validate_token
72
- validate_cluster_name
73
- validate_kubeconfig_path
74
-
75
- case action
76
- when :create
77
- validate_create
78
- when :delete
79
- validate_kubeconfig_path_must_exist
80
- when :upgrade
81
- validate_upgrade
82
- end
83
-
84
- errors.flatten!
85
-
86
- return if errors.empty?
87
-
88
- puts 'Some information in the configuration file requires your attention:'
89
-
90
- errors.each do |error|
91
- puts " - #{error}"
92
- end
93
-
94
- exit 1
95
- end
96
-
97
- def valid_token?
98
- return @valid unless @valid.nil?
99
-
100
- begin
101
- token = hetzner_token
102
- @hetzner_client = Hetzner::Client.new(token:)
103
- response = hetzner_client.get('/locations')
104
- error_code = response.dig('error', 'code')
105
- @valid = error_code&.size != 0
106
- rescue StandardError
107
- @valid = false
108
- end
109
- end
110
-
111
- def validate_token
112
- errors << 'Invalid Hetzner Cloud token' unless valid_token?
113
- end
114
-
115
- def validate_cluster_name
116
- errors << 'Cluster name is an invalid format (only lowercase letters, digits and dashes are allowed)' unless configuration['cluster_name'] =~ /\A[a-z\d-]+\z/
117
-
118
- return if configuration['cluster_name'] =~ /\A[a-z]+.*\z/
119
-
120
- errors << 'Ensure that the cluster name starts with a normal letter'
121
- end
122
-
123
- def validate_kubeconfig_path
124
- path = File.expand_path(configuration['kubeconfig_path'])
125
- errors << 'kubeconfig path cannot be a directory' and return if File.directory? path
126
-
127
- directory = File.dirname(path)
128
- errors << "Directory #{directory} doesn't exist" unless File.exist? directory
129
- rescue StandardError
130
- errors << 'Invalid path for the kubeconfig'
131
- end
132
-
133
- def validate_public_ssh_key
134
- path = File.expand_path(configuration['public_ssh_key_path'])
135
- errors << 'Invalid Public SSH key path' and return unless File.exist? path
136
-
137
- key = File.read(path)
138
- errors << 'Public SSH key is invalid' unless ::SSHKey.valid_ssh_public_key?(key)
139
- rescue StandardError
140
- errors << 'Invalid Public SSH key path'
141
- end
142
-
143
- def validate_private_ssh_key
144
- private_ssh_key_path = configuration['private_ssh_key_path']
145
-
146
- return unless private_ssh_key_path
147
-
148
- path = File.expand_path(private_ssh_key_path)
149
- errors << 'Invalid Private SSH key path' and return unless File.exist?(path)
150
- rescue StandardError
151
- errors << 'Invalid Private SSH key path'
152
- end
153
-
154
- def validate_kubeconfig_path_must_exist
155
- path = File.expand_path configuration['kubeconfig_path']
156
- errors << 'kubeconfig path is invalid' and return unless File.exist? path
157
-
158
- errors << 'kubeconfig path cannot be a directory' if File.directory? path
159
- rescue StandardError
160
- errors << 'Invalid kubeconfig path'
161
- end
162
-
163
- def server_types
164
- return [] unless valid_token?
165
-
166
- @server_types ||= hetzner_client.get('/server_types')['server_types'].map { |server_type| server_type['name'] }
167
- rescue StandardError
168
- @errors << 'Cannot fetch server types with Hetzner API, please try again later'
169
- false
170
- end
171
-
172
- def locations
173
- return [] unless valid_token?
174
-
175
- @locations ||= hetzner_client.get('/locations')['locations'].map { |location| location['name'] }
176
- rescue StandardError
177
- @errors << 'Cannot fetch locations with Hetzner API, please try again later'
178
- []
179
- end
180
-
181
- def valid_location?(location)
182
- return if locations.empty? && !valid_token?
183
-
184
- locations.include? location
185
- end
186
-
187
- def validate_masters_location
188
- return if valid_location?(configuration['location'])
189
-
190
- errors << 'Invalid location for master nodes - valid locations: nbg1 (Nuremberg, Germany), fsn1 (Falkenstein, Germany), hel1 (Helsinki, Finland) or ash (Ashburn, Virginia, USA)'
191
- end
192
-
193
- def available_releases
194
- @available_releases ||= begin
195
- response = HTTP.get('https://api.github.com/repos/k3s-io/k3s/tags?per_page=999').body
196
- JSON.parse(response).map { |hash| hash['name'] }
197
- end
198
- rescue StandardError
199
- errors << 'Cannot fetch the releases with Hetzner API, please try again later'
200
- end
201
-
202
- def validate_k3s_version
203
- k3s_version = configuration['k3s_version']
204
- errors << 'Invalid k3s version' unless available_releases.include? k3s_version
205
- end
206
-
207
- def validate_new_k3s_version
208
- new_k3s_version = options[:new_k3s_version]
209
- errors << 'The new k3s version is invalid' unless available_releases.include? new_k3s_version
210
- end
211
-
212
- def validate_masters
213
- masters_pool = nil
214
-
215
- begin
216
- masters_pool = configuration['masters']
217
- rescue StandardError
218
- errors << 'Invalid masters configuration'
219
- return
220
- end
221
-
222
- if masters_pool.nil?
223
- errors << 'Invalid masters configuration'
224
- return
225
- end
226
-
227
- validate_instance_group masters_pool, workers: false
228
- end
229
-
230
- def validate_worker_node_pools
231
- worker_node_pools = configuration['worker_node_pools'] || []
232
-
233
- unless worker_node_pools.size.positive? || schedule_workloads_on_masters?
234
- errors << 'Invalid node pools configuration'
235
- return
236
- end
237
-
238
- return if worker_node_pools.size.zero? && schedule_workloads_on_masters?
58
+ attr_reader :hetzner_token, :hetzner_client
239
59
 
240
- if !worker_node_pools.is_a? Array
241
- errors << 'Invalid node pools configuration'
242
- elsif worker_node_pools.size.zero?
243
- errors << 'At least one node pool is required in order to schedule workloads' unless schedule_workloads_on_masters?
244
- elsif worker_node_pools.map { |worker_node_pool| worker_node_pool['name'] }.uniq.size != worker_node_pools.size
245
- errors << 'Each node pool must have an unique name'
246
- elsif server_types
247
- worker_node_pools.each do |worker_node_pool|
248
- validate_instance_group worker_node_pool
249
- end
60
+ def configuration
61
+ @configuration ||= begin
62
+ config = ::Hetzner::Configuration.new(options:)
63
+ @hetzner_token = config.hetzner_token
64
+ config
250
65
  end
251
66
  end
252
-
253
- def schedule_workloads_on_masters?
254
- schedule_workloads_on_masters = configuration['schedule_workloads_on_masters']
255
- schedule_workloads_on_masters ? !!schedule_workloads_on_masters : false
256
- end
257
-
258
- def validate_instance_group(instance_group, workers: true)
259
- instance_group_errors = []
260
-
261
- instance_group_type = workers ? "Worker mode pool '#{instance_group['name']}'" : 'Masters pool'
262
-
263
- instance_group_errors << "#{instance_group_type} has an invalid name" unless !workers || instance_group['name'] =~ /\A([A-Za-z0-9\-_]+)\Z/
264
-
265
- instance_group_errors << "#{instance_group_type} is in an invalid format" unless instance_group.is_a? Hash
266
-
267
- instance_group_errors << "#{instance_group_type} has an invalid instance type" unless !valid_token? || server_types.include?(instance_group['instance_type'])
268
-
269
- if workers
270
- location = instance_group.fetch('location', configuration['location'])
271
- instance_group_errors << "#{instance_group_type} has an invalid location - valid locations: nbg1 (Nuremberg, Germany), fsn1 (Falkenstein, Germany), hel1 (Helsinki, Finland) or ash (Ashburn, Virginia, USA)" unless valid_location?(location)
272
-
273
- in_network_zone = configuration['location'] == 'ash' ? location == 'ash' : location != 'ash'
274
- instance_group_errors << "#{instance_group_type} must be in the same network zone as the masters. If the masters are located in Ashburn, all the node pools must be located in Ashburn too, otherwise none of the node pools should be located in Ashburn." unless in_network_zone
275
- end
276
-
277
- if instance_group['instance_count'].is_a? Integer
278
- if instance_group['instance_count'] < 1
279
- instance_group_errors << "#{instance_group_type} must have at least one node"
280
- elsif instance_group['instance_count'] > 10
281
- instance_group_errors << "#{instance_group_type} cannot have more than 10 nodes due to a limitation with the Hetzner placement groups. You can add more node pools if you need more nodes."
282
- elsif !workers
283
- instance_group_errors << 'Masters count must equal to 1 for non-HA clusters or an odd number (recommended 3) for an HA cluster' unless instance_group['instance_count'].odd?
284
- end
285
- else
286
- instance_group_errors << "#{instance_group_type} has an invalid instance count"
287
- end
288
-
289
- used_server_types << instance_group['instance_type']
290
-
291
- errors << instance_group_errors
292
- end
293
-
294
- def validate_verify_host_key
295
- return unless [true, false].include?(configuration.fetch('public_ssh_key_path', false))
296
-
297
- errors << 'Please set the verify_host_key option to either true or false'
298
- end
299
-
300
- def hetzner_token
301
- @token = ENV['HCLOUD_TOKEN']
302
- return @token if @token
303
-
304
- @token = configuration['hetzner_token']
305
- end
306
-
307
- def validate_ssh_allowed_networks
308
- networks ||= configuration['ssh_allowed_networks']
309
-
310
- if networks.nil? || networks.empty?
311
- errors << 'At least one network/IP range must be specified for SSH access'
312
- return
313
- end
314
-
315
- invalid_networks = networks.reject do |network|
316
- IPAddr.new(network)
317
- rescue StandardError
318
- false
319
- end
320
-
321
- unless invalid_networks.empty?
322
- invalid_networks.each do |network|
323
- errors << "The network #{network} is an invalid range"
324
- end
325
- end
326
-
327
- invalid_ranges = networks.reject do |network|
328
- network.include? '/'
329
- end
330
-
331
- unless invalid_ranges.empty?
332
- invalid_ranges.each do |_network|
333
- errors << 'Please use the CIDR notation for the networks to avoid ambiguity'
334
- end
335
- end
336
-
337
- return unless invalid_networks.empty?
338
-
339
- current_ip = URI.open('http://whatismyip.akamai.com').read
340
-
341
- current_ip_networks = networks.detect do |network|
342
- IPAddr.new(network).include?(current_ip)
343
- rescue StandardError
344
- false
345
- end
346
-
347
- 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
348
- end
349
-
350
- def validate_additional_packages
351
- additional_packages = configuration['additional_packages']
352
- errors << 'Invalid additional packages configuration - it should be an array' if additional_packages && !additional_packages.is_a?(Array)
353
- end
354
-
355
- def validate_create
356
- validate_public_ssh_key
357
- validate_private_ssh_key
358
- validate_ssh_allowed_networks
359
- validate_masters_location
360
- validate_k3s_version
361
- validate_masters
362
- validate_worker_node_pools
363
- validate_verify_host_key
364
- validate_additional_packages
365
- validate_kube_api_server_args
366
- validate_kube_scheduler_args
367
- validate_kube_controller_manager_args
368
- validate_kube_cloud_controller_manager_args
369
- validate_kubelet_args
370
- validate_kube_proxy_args
371
- end
372
-
373
- def validate_upgrade
374
- validate_kubeconfig_path_must_exist
375
- validate_new_k3s_version
376
- end
377
-
378
- def validate_configuration_file
379
- config_file_path = options[:config_file]
380
-
381
- if File.exist?(config_file_path)
382
- begin
383
- @configuration = YAML.load_file(options[:config_file])
384
- unless configuration.is_a? Hash
385
- puts 'Configuration is invalid'
386
- exit 1
387
- end
388
- rescue StandardError
389
- puts 'Please ensure that the config file is a correct YAML manifest.'
390
- exit 1
391
- end
392
- else
393
- puts 'Please specify a correct path for the config file.'
394
- exit 1
395
- end
396
- end
397
-
398
- def validate_kube_api_server_args
399
- kube_api_server_args = configuration['kube_api_server_args']
400
- return unless kube_api_server_args
401
-
402
- errors << 'kube_api_server_args must be an array of arguments' unless kube_api_server_args.is_a? Array
403
- end
404
-
405
- def validate_kube_scheduler_args
406
- kube_scheduler_args = configuration['kube_scheduler_args']
407
- return unless kube_scheduler_args
408
-
409
- errors << 'kube_scheduler_args must be an array of arguments' unless kube_scheduler_args.is_a? Array
410
- end
411
-
412
- def validate_kube_controller_manager_args
413
- kube_controller_manager_args = configuration['kube_controller_manager_args']
414
- return unless kube_controller_manager_args
415
-
416
- errors << 'kube_controller_manager_args must be an array of arguments' unless kube_controller_manager_args.is_a? Array
417
- end
418
-
419
- def validate_kube_cloud_controller_manager_args
420
- kube_cloud_controller_manager_args = configuration['kube_cloud_controller_manager_args']
421
- return unless kube_cloud_controller_manager_args
422
-
423
- errors << 'kube_cloud_controller_manager_args must be an array of arguments' unless kube_cloud_controller_manager_args.is_a? Array
424
- end
425
-
426
- def validate_kubelet_args
427
- kubelet_args = configuration['kubelet_args']
428
- return unless kubelet_args
429
-
430
- errors << 'kubelet_args must be an array of arguments' unless kubelet_args.is_a? Array
431
- end
432
-
433
- def validate_kube_proxy_args
434
- kube_proxy_args = configuration['kube_proxy_args']
435
- return unless kube_proxy_args
436
-
437
- errors << 'kube_proxy_args must be an array of arguments' unless kube_proxy_args.is_a? Array
438
- end
439
67
  end
440
68
  end
441
69
  end
@@ -19,13 +19,11 @@ require_relative '../utils'
19
19
  class Cluster
20
20
  include Utils
21
21
 
22
- def initialize(hetzner_client:, hetzner_token:)
23
- @hetzner_client = hetzner_client
24
- @hetzner_token = hetzner_token
22
+ def initialize(configuration:)
23
+ @configuration = configuration
25
24
  end
26
25
 
27
- def create(configuration:)
28
- @configuration = configuration
26
+ def create
29
27
  @cluster_name = configuration['cluster_name']
30
28
  @kubeconfig_path = File.expand_path(configuration['kubeconfig_path'])
31
29
  @public_ssh_key_path = File.expand_path(configuration['public_ssh_key_path'])
@@ -37,7 +35,8 @@ class Cluster
37
35
  @masters_location = configuration['location']
38
36
  @verify_host_key = configuration.fetch('verify_host_key', false)
39
37
  @servers = []
40
- @networks = configuration['ssh_allowed_networks']
38
+ @ssh_networks = configuration['ssh_allowed_networks']
39
+ @api_networks = configuration['api_allowed_networks']
41
40
  @enable_encryption = configuration.fetch('enable_encryption', false)
42
41
  @kube_api_server_args = configuration.fetch('kube_api_server_args', [])
43
42
  @kube_scheduler_args = configuration.fetch('kube_scheduler_args', [])
@@ -57,8 +56,7 @@ class Cluster
57
56
  deploy_system_upgrade_controller
58
57
  end
59
58
 
60
- def delete(configuration:)
61
- @configuration = configuration
59
+ def delete
62
60
  @cluster_name = configuration['cluster_name']
63
61
  @kubeconfig_path = File.expand_path(configuration['kubeconfig_path'])
64
62
  @public_ssh_key_path = File.expand_path(configuration['public_ssh_key_path'])
@@ -68,8 +66,7 @@ class Cluster
68
66
  delete_resources
69
67
  end
70
68
 
71
- def upgrade(configuration:, new_k3s_version:, config_file:)
72
- @configuration = configuration
69
+ def upgrade(new_k3s_version:, config_file:)
73
70
  @cluster_name = configuration['cluster_name']
74
71
  @kubeconfig_path = File.expand_path(configuration['kubeconfig_path'])
75
72
  @new_k3s_version = new_k3s_version
@@ -82,14 +79,14 @@ class Cluster
82
79
 
83
80
  attr_accessor :servers
84
81
 
85
- attr_reader :hetzner_client, :cluster_name, :kubeconfig_path, :k3s_version,
82
+ attr_reader :configuration, :cluster_name, :kubeconfig_path, :k3s_version,
86
83
  :masters_config, :worker_node_pools,
87
84
  :masters_location, :public_ssh_key_path,
88
- :hetzner_token, :new_k3s_version, :configuration,
89
- :config_file, :verify_host_key, :networks, :private_ssh_key_path,
85
+ :hetzner_token, :new_k3s_version,
86
+ :config_file, :verify_host_key, :ssh_networks, :private_ssh_key_path,
90
87
  :enable_encryption, :kube_api_server_args, :kube_scheduler_args,
91
88
  :kube_controller_manager_args, :kube_cloud_controller_manager_args,
92
- :kubelet_args, :kube_proxy_args
89
+ :kubelet_args, :kube_proxy_args, :api_networks
93
90
 
94
91
  def find_worker_node_pools(configuration)
95
92
  configuration.fetch('worker_node_pools', [])
@@ -119,7 +116,7 @@ class Cluster
119
116
 
120
117
  Hetzner::Firewall.new(hetzner_client:, cluster_name:).delete(all_servers)
121
118
 
122
- Hetzner::Network.new(hetzner_client:, cluster_name:).delete
119
+ Hetzner::Network.new(hetzner_client:, cluster_name:, existing_network:).delete
123
120
 
124
121
  Hetzner::SSHKey.new(hetzner_client:, cluster_name:).delete(public_ssh_key_path:)
125
122
 
@@ -190,15 +187,30 @@ class Cluster
190
187
  puts 'Upgrade will now start. Run `watch kubectl get nodes` to see the nodes being upgraded. This should take a few minutes for a small cluster.'
191
188
  puts 'The API server may be briefly unavailable during the upgrade of the controlplane.'
192
189
 
193
- configuration['k3s_version'] = new_k3s_version
190
+ updated_configuration = configuration.raw
191
+ updated_configuration['k3s_version'] = new_k3s_version
194
192
 
195
- File.write(config_file, configuration.to_yaml)
193
+ File.write(config_file, updated_configuration.to_yaml)
196
194
  end
197
195
 
198
196
  def master_script(master)
199
197
  server = master == first_master ? ' --cluster-init ' : " --server https://#{api_server_ip}:6443 "
200
198
  flannel_interface = find_flannel_interface(master)
201
- 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
+
202
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}"
203
215
  taint = schedule_workloads_on_masters? ? ' ' : ' --node-taint CriticalAddonsOnly=true:NoExecute '
204
216
 
@@ -214,10 +226,8 @@ class Cluster
214
226
  --cluster-cidr=10.244.0.0/16 \
215
227
  --etcd-expose-metrics=true \
216
228
  #{flannel_wireguard} \
217
- --kube-controller-manager-arg="address=0.0.0.0" \
218
229
  --kube-controller-manager-arg="bind-address=0.0.0.0" \
219
230
  --kube-proxy-arg="metrics-bind-address=0.0.0.0" \
220
- --kube-scheduler-arg="address=0.0.0.0" \
221
231
  --kube-scheduler-arg="bind-address=0.0.0.0" \
222
232
  #{taint} #{extra_args} \
223
233
  --kubelet-arg="cloud-provider=external" \
@@ -298,8 +308,8 @@ class Cluster
298
308
  namespace: 'kube-system'
299
309
  name: 'hcloud'
300
310
  stringData:
301
- network: "#{cluster_name}"
302
- token: "#{hetzner_token}"
311
+ network: "#{existing_network || cluster_name}"
312
+ token: "#{configuration.hetzner_token}"
303
313
  EOF
304
314
  BASH
305
315
 
@@ -318,7 +328,7 @@ class Cluster
318
328
  puts
319
329
  puts 'Deploying k3s System Upgrade Controller...'
320
330
 
321
- 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'
322
332
 
323
333
  run cmd, kubeconfig_path: kubeconfig_path
324
334
 
@@ -339,13 +349,13 @@ class Cluster
339
349
  namespace: 'kube-system'
340
350
  name: 'hcloud-csi'
341
351
  stringData:
342
- token: "#{hetzner_token}"
352
+ token: "#{configuration.hetzner_token}"
343
353
  EOF
344
354
  BASH
345
355
 
346
356
  run cmd, kubeconfig_path: kubeconfig_path
347
357
 
348
- 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'
349
359
 
350
360
  run cmd, kubeconfig_path: kubeconfig_path
351
361
 
@@ -450,6 +460,10 @@ class Cluster
450
460
  configuration['additional_packages'] || []
451
461
  end
452
462
 
463
+ def additional_post_create_commands
464
+ configuration['post_create_commands'] || []
465
+ end
466
+
453
467
  def check_kubectl
454
468
  return if which('kubectl')
455
469
 
@@ -471,11 +485,11 @@ class Cluster
471
485
  end
472
486
 
473
487
  def firewall_id
474
- @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:)
475
489
  end
476
490
 
477
491
  def network_id
478
- @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)
479
493
  end
480
494
 
481
495
  def ssh_key_id
@@ -495,7 +509,8 @@ class Cluster
495
509
  network_id:,
496
510
  ssh_key_id:,
497
511
  image:,
498
- additional_packages:
512
+ additional_packages:,
513
+ additional_post_create_commands:
499
514
  }
500
515
  end
501
516
 
@@ -533,7 +548,8 @@ class Cluster
533
548
  network_id:,
534
549
  ssh_key_id:,
535
550
  image:,
536
- additional_packages:
551
+ additional_packages:,
552
+ additional_post_create_commands:
537
553
  }
538
554
  end
539
555
 
@@ -637,4 +653,12 @@ class Cluster
637
653
  " --kube-proxy-arg=\"#{arg}\" "
638
654
  end.join
639
655
  end
656
+
657
+ def hetzner_client
658
+ configuration.hetzner_client
659
+ end
660
+
661
+ def existing_network
662
+ configuration["existing_network"]
663
+ end
640
664
  end