hetzner-k3s 0.4.7 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Hetzner
2
4
  module K3s
3
- VERSION = "0.4.7"
5
+ VERSION = '0.5.1'
4
6
  end
5
7
  end
@@ -0,0 +1,103 @@
1
+ module Utils
2
+ CMD_FILE_PATH = '/tmp/cli.cmd'
3
+
4
+ def which(cmd)
5
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
6
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
7
+ exts.each do |ext|
8
+ exe = File.join(path, "#{cmd}#{ext}")
9
+ return exe if File.executable?(exe) && !File.directory?(exe)
10
+ end
11
+ end
12
+ nil
13
+ end
14
+
15
+ def write_file(path, content, append: false)
16
+ File.open(path, append ? 'a' : 'w') { |file| file.write(content) }
17
+ end
18
+
19
+ def run(command, kubeconfig_path:)
20
+ env = ENV.to_hash.merge({ 'KUBECONFIG' => kubeconfig_path })
21
+
22
+ write_file CMD_FILE_PATH, <<-CONTENT
23
+ set -euo pipefail
24
+ #{command}
25
+ CONTENT
26
+
27
+ FileUtils.chmod('+x', CMD_FILE_PATH)
28
+
29
+ begin
30
+ process = nil
31
+
32
+ at_exit do
33
+ process&.send_signal('SIGTERM')
34
+ rescue Errno::ESRCH, Interrupt
35
+ end
36
+
37
+ Subprocess.check_call(['bash', '-c', CMD_FILE_PATH], env:) do |p|
38
+ process = p
39
+ end
40
+ rescue Subprocess::NonZeroExit
41
+ puts 'Command failed: non-zero exit code'
42
+ exit 1
43
+ rescue Interrupt
44
+ puts 'Command interrupted'
45
+ exit 1
46
+ end
47
+ end
48
+
49
+ def wait_for_ssh(server)
50
+ retries = 0
51
+
52
+ Timeout.timeout(5) do
53
+ server_name = server['name']
54
+
55
+ puts "Waiting for server #{server_name} to be up..."
56
+
57
+ loop do
58
+ result = ssh(server, 'echo UP')
59
+ break if result == 'UP'
60
+ end
61
+
62
+ puts "...server #{server_name} is now up."
63
+ end
64
+ rescue Errno::ENETUNREACH, Errno::EHOSTUNREACH, Timeout::Error, IOError
65
+ retries += 1
66
+ retry if retries <= 15
67
+ end
68
+
69
+ def ssh(server, command, print_output: false)
70
+ retries = 0
71
+
72
+ public_ip = server.dig('public_net', 'ipv4', 'ip')
73
+ output = ''
74
+
75
+ params = { verify_host_key: (verify_host_key ? :always : :never) }
76
+
77
+ params[:keys] = private_ssh_key_path && [private_ssh_key_path]
78
+
79
+ Net::SSH.start(public_ip, 'root', params) do |session|
80
+ session.exec!(command) do |_channel, _stream, data|
81
+ output << data
82
+ puts data if print_output
83
+ end
84
+ end
85
+ output.chop
86
+ rescue Net::SSH::Disconnect => e
87
+ retries += 1
88
+ retry unless retries > 15 || e.message =~ /Too many authentication failures/
89
+ rescue Net::SSH::ConnectionTimeout, Errno::ECONNREFUSED, Errno::ENETUNREACH, Errno::EHOSTUNREACH
90
+ retries += 1
91
+ retry if retries <= 15
92
+ rescue Net::SSH::AuthenticationFailed
93
+ puts '\nCannot continue: SSH authentication failed. Please ensure that the private SSH key is correct.'
94
+ exit 1
95
+ rescue Net::SSH::HostKeyMismatch
96
+ puts <<-MESSAGE
97
+ Cannot continue: Unable to SSH into server with IP #{public_ip} because the existing fingerprint in the known_hosts file does not match that of the actual host key.\n
98
+ This is due to a security check but can also happen when creating a new server that gets assigned the same IP address as another server you've owned in the past.\n
99
+ If are sure no security is being violated here and you're just creating new servers, you can eiher remove the relevant lines from your known_hosts (see IPs from the cloud console) or disable host key verification by setting the option 'verify_host_key' to false in the configuration file for the cluster.
100
+ MESSAGE
101
+ exit 1
102
+ end
103
+ end
data/lib/hetzner.rb CHANGED
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Hetzner
2
4
  end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hetzner-k3s
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.7
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vito Botta
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-11-13 00:00:00.000000000 Z
11
+ date: 2022-02-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: thor
14
+ name: bcrypt_pbkdf
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: http
28
+ name: ed25519
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: net-ssh
42
+ name: http
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: k8s-ruby
56
+ name: net-ssh
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -81,7 +81,7 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: ed25519
84
+ name: subprocess
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
@@ -95,7 +95,7 @@ dependencies:
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: bcrypt_pbkdf
98
+ name: thor
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
111
125
  description: A CLI to create a Kubernetes cluster in Hetzner Cloud very quickly using
112
126
  k3s.
113
127
  email:
@@ -119,6 +133,8 @@ extra_rdoc_files: []
119
133
  files:
120
134
  - ".gitignore"
121
135
  - ".rspec"
136
+ - ".rubocop.yml"
137
+ - ".ruby-version"
122
138
  - ".travis.yml"
123
139
  - CODE_OF_CONDUCT.md
124
140
  - Dockerfile
@@ -128,8 +144,8 @@ files:
128
144
  - README.md
129
145
  - Rakefile
130
146
  - bin/build.sh
131
- - bin/console
132
- - bin/setup
147
+ - bin/console.sh
148
+ - bin/setup.sh
133
149
  - cluster_config.yaml.example
134
150
  - entrypoint.sh
135
151
  - exe/hetzner-k3s
@@ -144,9 +160,9 @@ files:
144
160
  - lib/hetzner/infra/server.rb
145
161
  - lib/hetzner/infra/ssh_key.rb
146
162
  - lib/hetzner/k3s/cli.rb
147
- - lib/hetzner/k3s/client_patch.rb
148
163
  - lib/hetzner/k3s/cluster.rb
149
164
  - lib/hetzner/k3s/version.rb
165
+ - lib/hetzner/utils.rb
150
166
  homepage: https://github.com/vitobotta/hetzner-k3s
151
167
  licenses:
152
168
  - MIT
@@ -154,7 +170,8 @@ metadata:
154
170
  homepage_uri: https://github.com/vitobotta/hetzner-k3s
155
171
  source_code_uri: https://github.com/vitobotta/hetzner-k3s
156
172
  changelog_uri: https://github.com/vitobotta/hetzner-k3s
157
- post_install_message:
173
+ rubygems_mfa_required: 'true'
174
+ post_install_message:
158
175
  rdoc_options: []
159
176
  require_paths:
160
177
  - lib
@@ -162,15 +179,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
162
179
  requirements:
163
180
  - - ">="
164
181
  - !ruby/object:Gem::Version
165
- version: 2.3.0
182
+ version: 3.1.0
166
183
  required_rubygems_version: !ruby/object:Gem::Requirement
167
184
  requirements:
168
185
  - - ">="
169
186
  - !ruby/object:Gem::Version
170
187
  version: '0'
171
188
  requirements: []
172
- rubygems_version: 3.1.4
173
- signing_key:
189
+ rubygems_version: 3.3.3
190
+ signing_key:
174
191
  specification_version: 4
175
192
  summary: A CLI to create a Kubernetes cluster in Hetzner Cloud very quickly using
176
193
  k3s.
@@ -1,38 +0,0 @@
1
- module K8s
2
- class ResourceClient
3
- def initialize(transport, api_client, api_resource, namespace: nil, resource_class: K8s::Resource)
4
- @transport = transport
5
- @api_client = api_client
6
- @api_resource = api_resource
7
- @namespace = namespace
8
- @resource_class = resource_class
9
-
10
- if @api_resource.name.include? '/'
11
- @resource, @subresource = @api_resource.name.split('/', 2)
12
- else
13
- @resource = @api_resource.name
14
- @subresource = nil
15
- end
16
-
17
- # fail "Resource #{api_resource.name} is not namespaced" unless api_resource.namespaced || !namespace
18
- end
19
-
20
- def path(name = nil, subresource: @subresource, namespace: @namespace)
21
- namespace_part = namespace ? ['namespaces', namespace] : []
22
-
23
- if namespaced?
24
- if name && subresource
25
- @api_client.path(*namespace_part, @resource, name, subresource)
26
- elsif name
27
- @api_client.path(*namespace_part, @resource, name)
28
- else namespaced?
29
- @api_client.path(*namespace_part, @resource)
30
- end
31
- elsif name
32
- @api_client.path(@resource, name)
33
- else
34
- @api_client.path(@resource)
35
- end
36
- end
37
- end
38
- end