hetzner-k3s 0.5.0 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,13 +1,14 @@
1
- require "thor"
2
- require "http"
3
- require "sshkey"
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+ require 'http'
5
+ require 'sshkey'
4
6
  require 'ipaddr'
5
7
  require 'open-uri'
6
- require "yaml"
7
-
8
- require_relative "cluster"
9
- require_relative "version"
8
+ require 'yaml'
10
9
 
10
+ require_relative 'cluster'
11
+ require_relative 'version'
11
12
 
12
13
  module Hetzner
13
14
  module K3s
@@ -16,354 +17,426 @@ module Hetzner
16
17
  true
17
18
  end
18
19
 
19
- desc "version", "Print the version"
20
+ def initialize(*args)
21
+ @errors = []
22
+ @used_server_types = []
23
+
24
+ super
25
+ end
26
+
27
+ desc 'version', 'Print the version'
20
28
  def version
21
29
  puts Hetzner::K3s::VERSION
22
30
  end
23
31
 
24
- desc "create-cluster", "Create a k3s cluster in Hetzner Cloud"
32
+ desc 'create-cluster', 'Create a k3s cluster in Hetzner Cloud'
25
33
  option :config_file, required: true
26
-
27
34
  def create_cluster
28
- validate_config_file :create
29
- Cluster.new(hetzner_client: hetzner_client, hetzner_token: find_hetzner_token).create configuration: configuration
35
+ validate_configuration :create
36
+ Cluster.new(hetzner_client:, hetzner_token:).create configuration:
30
37
  end
31
38
 
32
- desc "delete-cluster", "Delete an existing k3s cluster in Hetzner Cloud"
39
+ desc 'delete-cluster', 'Delete an existing k3s cluster in Hetzner Cloud'
33
40
  option :config_file, required: true
34
-
35
41
  def delete_cluster
36
- validate_config_file :delete
37
- Cluster.new(hetzner_client: hetzner_client, hetzner_token: find_hetzner_token).delete configuration: configuration
42
+ validate_configuration :delete
43
+ Cluster.new(hetzner_client:, hetzner_token:).delete configuration:
38
44
  end
39
45
 
40
- desc "upgrade-cluster", "Upgrade an existing k3s cluster in Hetzner Cloud to a new version"
46
+ desc 'upgrade-cluster', 'Upgrade an existing k3s cluster in Hetzner Cloud to a new version'
41
47
  option :config_file, required: true
42
48
  option :new_k3s_version, required: true
43
- option :force, default: "false"
44
-
49
+ option :force, default: 'false'
45
50
  def upgrade_cluster
46
- validate_config_file :upgrade
47
- 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]
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])
48
55
  end
49
56
 
50
- desc "releases", "List available k3s releases"
57
+ desc 'releases', 'List available k3s releases'
51
58
  def releases
52
- find_available_releases.each do |release|
59
+ available_releases.each do |release|
53
60
  puts release
54
61
  end
55
62
  end
56
63
 
57
64
  private
58
65
 
59
- attr_reader :configuration, :hetzner_client, :k3s_version
60
- attr_accessor :errors, :used_server_types
61
-
62
- def validate_config_file(action)
63
- config_file_path = options[:config_file]
64
-
65
- if File.exists?(config_file_path)
66
- begin
67
- @configuration = YAML.load_file(options[:config_file])
68
- unless configuration.is_a? Hash
69
- raise "Configuration is invalid"
70
- exit 1
71
- end
72
- rescue => e
73
- puts "Please ensure that the config file is a correct YAML manifest."
74
- exit 1
75
- end
76
- else
77
- puts "Please specify a correct path for the config file."
78
- exit 1
79
- end
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
80
83
 
81
- @errors = []
82
- @used_server_types = []
83
-
84
- validate_token
85
- validate_cluster_name
86
- validate_kubeconfig_path
87
-
88
- case action
89
- when :create
90
- validate_public_ssh_key
91
- validate_private_ssh_key
92
- validate_ssh_allowed_networks
93
- validate_location
94
- validate_k3s_version
95
- validate_masters
96
- validate_worker_node_pools
97
- validate_verify_host_key
98
- validate_additional_packages
99
- when :delete
100
- validate_kubeconfig_path_must_exist
101
- when :upgrade
102
- validate_kubeconfig_path_must_exist
103
- validate_new_k3s_version
104
- end
84
+ errors.flatten!
105
85
 
106
- errors.flatten!
86
+ return if errors.empty?
107
87
 
108
- unless errors.empty?
109
- puts "Some information in the configuration file requires your attention:"
110
- errors.each do |error|
111
- puts " - #{error}"
112
- end
88
+ puts 'Some information in the configuration file requires your attention:'
113
89
 
114
- exit 1
115
- end
90
+ errors.each do |error|
91
+ puts " - #{error}"
116
92
  end
117
93
 
118
- def valid_token?
119
- return @valid unless @valid.nil?
94
+ exit 1
95
+ end
120
96
 
121
- begin
122
- token = find_hetzner_token
123
- @hetzner_client = Hetzner::Client.new(token: token)
124
- response = hetzner_client.get("/locations")
125
- error_code = response.dig("error", "code")
126
- @valid = if error_code and error_code.size > 0
127
- false
128
- else
129
- true
130
- end
131
- rescue
132
- @valid = false
133
- end
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
134
108
  end
109
+ end
135
110
 
136
- def validate_token
137
- errors << "Invalid Hetzner Cloud token" unless valid_token?
138
- end
111
+ def validate_token
112
+ errors << 'Invalid Hetzner Cloud token' unless valid_token?
113
+ end
139
114
 
140
- def validate_cluster_name
141
- errors << "Cluster name is an invalid format (only lowercase letters, digits and dashes are allowed)" unless configuration["cluster_name"] =~ /\A[a-z\d-]+\z/
142
- errors << "Ensure that the cluster name starts with a normal letter" unless configuration["cluster_name"] =~ /\A[a-z]+.*\z/
143
- end
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/
144
117
 
145
- def validate_kubeconfig_path
146
- path = File.expand_path(configuration.dig("kubeconfig_path"))
147
- errors << "kubeconfig path cannot be a directory" and return if File.directory? path
118
+ return if configuration['cluster_name'] =~ /\A[a-z]+.*\z/
148
119
 
149
- directory = File.dirname(path)
150
- errors << "Directory #{directory} doesn't exist" unless File.exists? directory
151
- rescue
152
- errors << "Invalid path for the kubeconfig"
153
- end
120
+ errors << 'Ensure that the cluster name starts with a normal letter'
121
+ end
154
122
 
155
- def validate_public_ssh_key
156
- path = File.expand_path(configuration.dig("public_ssh_key_path"))
157
- errors << "Invalid Public SSH key path" and return unless File.exists? path
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
158
126
 
159
- key = File.read(path)
160
- errors << "Public SSH key is invalid" unless ::SSHKey.valid_ssh_public_key?(key)
161
- rescue
162
- errors << "Invalid Public SSH key path"
163
- end
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
164
132
 
165
- def validate_private_ssh_key
166
- return unless (private_ssh_key_path = configuration.dig("private_ssh_key_path"))
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
167
136
 
168
- path = File.expand_path(private_ssh_key_path)
169
- errors << "Invalid Private SSH key path" and return unless File.exists?(path)
170
- rescue
171
- errors << "Invalid Private SSH key path"
172
- end
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
173
142
 
174
- def validate_kubeconfig_path_must_exist
175
- path = File.expand_path configuration.dig("kubeconfig_path")
176
- errors << "kubeconfig path is invalid" and return unless File.exists? path
177
- errors << "kubeconfig path cannot be a directory" if File.directory? path
178
- rescue
179
- errors << "Invalid kubeconfig path"
180
- end
143
+ def validate_private_ssh_key
144
+ private_ssh_key_path = configuration['private_ssh_key_path']
181
145
 
182
- def server_types
183
- return [] unless valid_token?
184
- @server_types ||= hetzner_client.get("/server_types")["server_types"].map{ |server_type| server_type["name"] }
185
- rescue
186
- @errors << "Cannot fetch server types with Hetzner API, please try again later"
187
- false
188
- end
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
189
162
 
190
- def locations
191
- return [] unless valid_token?
192
- @locations ||= hetzner_client.get("/locations")["locations"].map{ |location| location["name"] }
193
- rescue
194
- @errors << "Cannot fetch locations with Hetzner API, please try again later"
195
- []
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'] }
196
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
197
206
 
198
- def validate_location
199
- return if locations.empty? && !valid_token?
200
- errors << "Invalid location - available locations: nbg1 (Nuremberg, Germany), fsn1 (Falkenstein, Germany), hel1 (Helsinki, Finland) or ash (Ashburn, Virginia, USA)" unless locations.include? configuration.dig("location")
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
201
220
  end
202
221
 
203
- def find_available_releases
204
- @available_releases ||= begin
205
- response = HTTP.get("https://api.github.com/repos/k3s-io/k3s/tags?per_page=999").body
206
- JSON.parse(response).map { |hash| hash["name"] }
207
- end
208
- rescue
209
- errors << "Cannot fetch the releases with Hetzner API, please try again later"
222
+ if masters_pool.nil?
223
+ errors << 'Invalid masters configuration'
224
+ return
210
225
  end
211
226
 
212
- def validate_k3s_version
213
- k3s_version = configuration.dig("k3s_version")
214
- available_releases = find_available_releases
215
- errors << "Invalid k3s version" unless available_releases.include? k3s_version
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
216
236
  end
217
237
 
218
- def validate_new_k3s_version
219
- new_k3s_version = options[:new_k3s_version]
220
- available_releases = find_available_releases
221
- errors << "The new k3s version is invalid" unless available_releases.include? new_k3s_version
238
+ return if worker_node_pools.size.zero? && schedule_workloads_on_masters?
239
+
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
222
250
  end
251
+ end
223
252
 
224
- def validate_masters
225
- masters_pool = nil
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
226
257
 
227
- begin
228
- masters_pool = configuration.dig("masters")
229
- rescue
230
- errors << "Invalid masters configuration"
231
- return
232
- end
258
+ def validate_instance_group(instance_group, workers: true)
259
+ instance_group_errors = []
233
260
 
234
- if masters_pool.nil?
235
- errors << "Invalid masters configuration"
236
- return
237
- end
261
+ instance_group_type = workers ? "Worker mode pool '#{instance_group['name']}'" : 'Masters pool'
238
262
 
239
- validate_instance_group masters_pool, workers: false
240
- end
263
+ instance_group_errors << "#{instance_group_type} has an invalid name" unless !workers || instance_group['name'] =~ /\A([A-Za-z0-9\-_]+)\Z/
241
264
 
242
- def validate_worker_node_pools
243
- worker_node_pools = nil
265
+ instance_group_errors << "#{instance_group_type} is in an invalid format" unless instance_group.is_a? Hash
244
266
 
245
- begin
246
- worker_node_pools = configuration.dig("worker_node_pools")
247
- rescue
248
- unless schedule_workloads_on_masters?
249
- errors << "Invalid node pools configuration"
250
- return
251
- end
252
- end
267
+ instance_group_errors << "#{instance_group_type} has an invalid instance type" unless !valid_token? || server_types.include?(instance_group['instance_type'])
253
268
 
254
- if worker_node_pools.nil? && schedule_workloads_on_masters?
255
- return
256
- end
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)
257
272
 
258
- if !worker_node_pools.is_a? Array
259
- errors << "Invalid node pools configuration"
260
- elsif worker_node_pools.size == 0
261
- unless schedule_workloads_on_masters?
262
- errors << "At least one node pool is required in order to schedule workloads"
263
- end
264
- elsif worker_node_pools.map{ |worker_node_pool| worker_node_pool["name"]}.uniq.size != worker_node_pools.size
265
- errors << "Each node pool must have an unique name"
266
- elsif server_types
267
- worker_node_pools.each do |worker_node_pool|
268
- validate_instance_group worker_node_pool
269
- end
270
- end
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
271
275
  end
272
276
 
273
- def schedule_workloads_on_masters?
274
- schedule_workloads_on_masters = configuration.dig("schedule_workloads_on_masters")
275
- schedule_workloads_on_masters ? !!schedule_workloads_on_masters : false
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"
276
287
  end
277
288
 
278
- def validate_instance_group(instance_group, workers: true)
279
- instance_group_errors = []
289
+ used_server_types << instance_group['instance_type']
280
290
 
281
- instance_group_type = workers ? "Worker mode pool #{instance_group["name"]}" : "Masters pool"
291
+ errors << instance_group_errors
292
+ end
282
293
 
283
- unless !workers || instance_group["name"] =~ /\A([A-Za-z0-9\-\_]+)\Z/
284
- instance_group_errors << "#{instance_group_type} has an invalid name"
285
- end
294
+ def validate_verify_host_key
295
+ return unless [true, false].include?(configuration.fetch('public_ssh_key_path', false))
286
296
 
287
- unless instance_group.is_a? Hash
288
- instance_group_errors << "#{instance_group_type} is in an invalid format"
289
- end
297
+ errors << 'Please set the verify_host_key option to either true or false'
298
+ end
290
299
 
291
- unless !valid_token? or server_types.include?(instance_group["instance_type"])
292
- instance_group_errors << "#{instance_group_type} has an invalid instance type"
293
- end
300
+ def hetzner_token
301
+ @token = ENV['HCLOUD_TOKEN']
302
+ return @token if @token
294
303
 
295
- if instance_group["instance_count"].is_a? Integer
296
- if instance_group["instance_count"] < 1
297
- instance_group_errors << "#{instance_group_type} must have at least one node"
298
- elsif !workers
299
- 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?
300
- end
301
- else
302
- instance_group_errors << "#{instance_group_type} has an invalid instance count"
303
- end
304
+ @token = configuration['hetzner_token']
305
+ end
304
306
 
305
- used_server_types << instance_group["instance_type"]
307
+ def validate_ssh_allowed_networks
308
+ networks ||= configuration['ssh_allowed_networks']
306
309
 
307
- errors << instance_group_errors
310
+ if networks.nil? || networks.empty?
311
+ errors << 'At least one network/IP range must be specified for SSH access'
312
+ return
308
313
  end
309
314
 
310
- def validate_verify_host_key
311
- return unless [true, false].include?(configuration.fetch("public_ssh_key_path", false))
312
- errors << "Please set the verify_host_key option to either true or false"
315
+ invalid_networks = networks.reject do |network|
316
+ IPAddr.new(network)
317
+ rescue StandardError
318
+ false
313
319
  end
314
320
 
315
- def find_hetzner_token
316
- @token = ENV["HCLOUD_TOKEN"]
317
- return @token if @token
318
- @token = configuration.dig("hetzner_token")
321
+ unless invalid_networks.empty?
322
+ invalid_networks.each do |network|
323
+ errors << "The network #{network} is an invalid range"
324
+ end
319
325
  end
320
326
 
321
- def validate_ssh_allowed_networks
322
- networks ||= configuration.dig("ssh_allowed_networks")
327
+ invalid_ranges = networks.reject do |network|
328
+ network.include? '/'
329
+ end
323
330
 
324
- if networks.nil? or networks.empty?
325
- errors << "At least one network/IP range must be specified for SSH access"
326
- return
331
+ unless invalid_ranges.empty?
332
+ invalid_ranges.each do |_network|
333
+ errors << 'Please use the CIDR notation for the networks to avoid ambiguity'
327
334
  end
335
+ end
328
336
 
329
- invalid_networks = networks.reject do |network|
330
- IPAddr.new(network) rescue false
331
- end
337
+ return unless invalid_networks.empty?
332
338
 
333
- unless invalid_networks.empty?
334
- invalid_networks.each do |network|
335
- errors << "The network #{network} is an invalid range"
336
- end
337
- end
339
+ current_ip = URI.open('http://whatismyip.akamai.com').read
338
340
 
339
- invalid_ranges = networks.reject do |network|
340
- network.include? "/"
341
- end
341
+ current_ip_networks = networks.detect do |network|
342
+ IPAddr.new(network).include?(current_ip)
343
+ rescue StandardError
344
+ false
345
+ end
342
346
 
343
- unless invalid_ranges.empty?
344
- invalid_ranges.each do |network|
345
- errors << "Please use the CIDR notation for the networks to avoid ambiguity"
346
- end
347
- end
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
348
349
 
349
- return unless invalid_networks.empty?
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
350
354
 
351
- current_ip = URI.open('http://whatismyip.akamai.com').read
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
+ validate_network_zone
372
+ end
352
373
 
353
- current_ip_networks = networks.detect do |network|
354
- IPAddr.new(network).include?(current_ip) rescue false
355
- end
374
+ def validate_upgrade
375
+ validate_kubeconfig_path_must_exist
376
+ validate_new_k3s_version
377
+ end
356
378
 
357
- unless current_ip_networks
358
- 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"
379
+ def validate_configuration_file
380
+ config_file_path = options[:config_file]
381
+
382
+ if File.exist?(config_file_path)
383
+ begin
384
+ @configuration = YAML.load_file(options[:config_file])
385
+ unless configuration.is_a? Hash
386
+ puts 'Configuration is invalid'
387
+ exit 1
388
+ end
389
+ rescue StandardError
390
+ puts 'Please ensure that the config file is a correct YAML manifest.'
391
+ exit 1
359
392
  end
393
+ else
394
+ puts 'Please specify a correct path for the config file.'
395
+ exit 1
360
396
  end
397
+ end
361
398
 
362
- def validate_additional_packages
363
- additional_packages = configuration.dig("additional_packages")
364
- errors << "Invalid additional packages configuration - it should be an array" if additional_packages && !additional_packages.is_a?(Array)
365
- end
399
+ def validate_kube_api_server_args
400
+ kube_api_server_args = configuration['kube_api_server_args']
401
+ return unless kube_api_server_args
366
402
 
403
+ errors << 'kube_api_server_args must be an array of arguments' unless kube_api_server_args.is_a? Array
404
+ end
405
+
406
+ def validate_kube_scheduler_args
407
+ kube_scheduler_args = configuration['kube_scheduler_args']
408
+ return unless kube_scheduler_args
409
+
410
+ errors << 'kube_scheduler_args must be an array of arguments' unless kube_scheduler_args.is_a? Array
411
+ end
412
+
413
+ def validate_kube_controller_manager_args
414
+ kube_controller_manager_args = configuration['kube_controller_manager_args']
415
+ return unless kube_controller_manager_args
416
+
417
+ errors << 'kube_controller_manager_args must be an array of arguments' unless kube_controller_manager_args.is_a? Array
418
+ end
419
+
420
+ def validate_kube_cloud_controller_manager_args
421
+ kube_cloud_controller_manager_args = configuration['kube_cloud_controller_manager_args']
422
+ return unless kube_cloud_controller_manager_args
423
+
424
+ errors << 'kube_cloud_controller_manager_args must be an array of arguments' unless kube_cloud_controller_manager_args.is_a? Array
425
+ end
426
+
427
+ def validate_kubelet_args
428
+ kubelet_args = configuration['kubelet_args']
429
+ return unless kubelet_args
430
+
431
+ errors << 'kubelet_args must be an array of arguments' unless kubelet_args.is_a? Array
432
+ end
433
+
434
+ def validate_kube_proxy_args
435
+ kube_proxy_args = configuration['kube_proxy_args']
436
+ return unless kube_proxy_args
437
+
438
+ errors << 'kube_proxy_args must be an array of arguments' unless kube_proxy_args.is_a? Array
439
+ end
367
440
  end
368
441
  end
369
442
  end