nvoi 0.1.6 → 0.1.8
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/.claude/todo/refactor/12-cli-db-command.md +128 -0
- data/.claude/todo/refactor-execution/00-entrypoint.md +49 -0
- data/.claude/todo/refactor-execution/01-objects.md +42 -0
- data/.claude/todo/refactor-execution/02-utils.md +41 -0
- data/.claude/todo/refactor-execution/03-external-cloud.md +38 -0
- data/.claude/todo/refactor-execution/04-external-dns.md +35 -0
- data/.claude/todo/refactor-execution/05-external-other.md +46 -0
- data/.claude/todo/refactor-execution/06-cli-deploy.md +45 -0
- data/.claude/todo/refactor-execution/07-cli-delete.md +43 -0
- data/.claude/todo/refactor-execution/08-cli-exec.md +30 -0
- data/.claude/todo/refactor-execution/09-cli-credentials.md +34 -0
- data/.claude/todo/refactor-execution/10-cli-db.md +31 -0
- data/.claude/todo/refactor-execution/11-cli-router.md +44 -0
- data/.claude/todo/refactor-execution/12-cleanup.md +120 -0
- data/.claude/todo/refactor-execution/_monitoring-strategy.md +126 -0
- data/.claude/todos/buckets.md +41 -0
- data/.claude/todos.md +550 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +35 -4
- data/Rakefile +1 -1
- data/ingest +0 -0
- data/lib/nvoi/cli/config/command.rb +219 -0
- data/lib/nvoi/cli/delete/steps/teardown_dns.rb +12 -11
- data/lib/nvoi/cli/deploy/command.rb +27 -11
- data/lib/nvoi/cli/deploy/steps/build_image.rb +48 -6
- data/lib/nvoi/cli/deploy/steps/configure_tunnel.rb +15 -13
- data/lib/nvoi/cli/deploy/steps/deploy_service.rb +8 -15
- data/lib/nvoi/cli/deploy/steps/setup_k3s.rb +10 -1
- data/lib/nvoi/cli/logs/command.rb +66 -0
- data/lib/nvoi/cli/onboard/command.rb +761 -0
- data/lib/nvoi/cli/unlock/command.rb +72 -0
- data/lib/nvoi/cli.rb +257 -0
- data/lib/nvoi/config_api/actions/app.rb +30 -30
- data/lib/nvoi/config_api/actions/compute_provider.rb +31 -31
- data/lib/nvoi/config_api/actions/database.rb +42 -42
- data/lib/nvoi/config_api/actions/domain_provider.rb +40 -0
- data/lib/nvoi/config_api/actions/env.rb +12 -12
- data/lib/nvoi/config_api/actions/init.rb +67 -0
- data/lib/nvoi/config_api/actions/secret.rb +12 -12
- data/lib/nvoi/config_api/actions/server.rb +35 -35
- data/lib/nvoi/config_api/actions/service.rb +52 -0
- data/lib/nvoi/config_api/actions/volume.rb +18 -18
- data/lib/nvoi/config_api/base.rb +15 -21
- data/lib/nvoi/config_api/result.rb +5 -5
- data/lib/nvoi/config_api.rb +51 -28
- data/lib/nvoi/external/cloud/aws.rb +26 -1
- data/lib/nvoi/external/cloud/hetzner.rb +27 -1
- data/lib/nvoi/external/cloud/scaleway.rb +32 -6
- data/lib/nvoi/external/containerd.rb +1 -44
- data/lib/nvoi/external/dns/cloudflare.rb +34 -16
- data/lib/nvoi/external/ssh.rb +0 -12
- data/lib/nvoi/external/ssh_tunnel.rb +100 -0
- data/lib/nvoi/objects/configuration.rb +20 -0
- data/lib/nvoi/utils/namer.rb +9 -0
- data/lib/nvoi/utils/retry.rb +1 -1
- data/lib/nvoi/version.rb +1 -1
- data/lib/nvoi.rb +16 -0
- data/templates/app-ingress.yaml.erb +3 -1
- metadata +27 -1
|
@@ -127,7 +127,7 @@ module Nvoi
|
|
|
127
127
|
|
|
128
128
|
def create_server(opts)
|
|
129
129
|
# Validate server type
|
|
130
|
-
server_types =
|
|
130
|
+
server_types = list_server_types_api
|
|
131
131
|
unless server_types.key?(opts.type)
|
|
132
132
|
raise Errors::ValidationError, "invalid server type: #{opts.type}"
|
|
133
133
|
end
|
|
@@ -170,7 +170,7 @@ module Nvoi
|
|
|
170
170
|
end
|
|
171
171
|
|
|
172
172
|
def wait_for_server(server_id, max_attempts)
|
|
173
|
-
server = Utils::Retry.poll(max_attempts
|
|
173
|
+
server = Utils::Retry.poll(max_attempts:, interval: Utils::Constants::SERVER_READY_INTERVAL) do
|
|
174
174
|
s = get_server_api(server_id)
|
|
175
175
|
to_server(s) if s["state"] == "running" && s.dig("public_ip", "address")
|
|
176
176
|
end
|
|
@@ -281,7 +281,7 @@ module Nvoi
|
|
|
281
281
|
# Validation operations
|
|
282
282
|
|
|
283
283
|
def validate_instance_type(instance_type)
|
|
284
|
-
server_types =
|
|
284
|
+
server_types = list_server_types_api
|
|
285
285
|
unless server_types.key?(instance_type)
|
|
286
286
|
raise Errors::ValidationError, "invalid scaleway server type: #{instance_type}"
|
|
287
287
|
end
|
|
@@ -298,7 +298,7 @@ module Nvoi
|
|
|
298
298
|
end
|
|
299
299
|
|
|
300
300
|
def validate_credentials
|
|
301
|
-
|
|
301
|
+
list_server_types_api
|
|
302
302
|
true
|
|
303
303
|
rescue Errors::AuthenticationError => e
|
|
304
304
|
raise Errors::ValidationError, "scaleway credentials invalid: #{e.message}"
|
|
@@ -310,6 +310,32 @@ module Nvoi
|
|
|
310
310
|
server&.public_ipv4
|
|
311
311
|
end
|
|
312
312
|
|
|
313
|
+
# List available server types for onboarding
|
|
314
|
+
def list_server_types
|
|
315
|
+
list_server_types_api.map do |name, info|
|
|
316
|
+
{
|
|
317
|
+
name:,
|
|
318
|
+
cores: info.dig("ncpus"),
|
|
319
|
+
ram: info.dig("ram"),
|
|
320
|
+
hourly_price: info.dig("hourly_price")
|
|
321
|
+
}
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# List available zones for onboarding
|
|
326
|
+
def list_zones
|
|
327
|
+
VALID_ZONES.map do |z|
|
|
328
|
+
parts = z.split("-")
|
|
329
|
+
city = case parts[0..1].join("-")
|
|
330
|
+
when "fr-par" then "Paris"
|
|
331
|
+
when "nl-ams" then "Amsterdam"
|
|
332
|
+
when "pl-waw" then "Warsaw"
|
|
333
|
+
else parts[0..1].join("-")
|
|
334
|
+
end
|
|
335
|
+
{ name: z, city: }
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
|
|
313
339
|
private
|
|
314
340
|
|
|
315
341
|
def zone_to_region(zone)
|
|
@@ -402,7 +428,7 @@ module Nvoi
|
|
|
402
428
|
post(instance_url("/servers/#{id}/action"), { action: })
|
|
403
429
|
end
|
|
404
430
|
|
|
405
|
-
def
|
|
431
|
+
def list_server_types_api
|
|
406
432
|
get(instance_url("/products/servers"))["servers"] || {}
|
|
407
433
|
end
|
|
408
434
|
|
|
@@ -450,7 +476,7 @@ module Nvoi
|
|
|
450
476
|
end
|
|
451
477
|
|
|
452
478
|
def wait_for_server_state(server_id, target_state, max_attempts)
|
|
453
|
-
Utils::Retry.poll(max_attempts
|
|
479
|
+
Utils::Retry.poll(max_attempts:, interval: 2) do
|
|
454
480
|
server = get_server_api(server_id)
|
|
455
481
|
server if server["state"] == target_state
|
|
456
482
|
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
module Nvoi
|
|
4
4
|
module External
|
|
5
5
|
# Containerd manages container operations on remote servers via containerd/ctr
|
|
6
|
+
# Used for image listing and cleanup on the remote server
|
|
6
7
|
class Containerd
|
|
7
8
|
attr_reader :ssh
|
|
8
9
|
|
|
@@ -10,50 +11,6 @@ module Nvoi
|
|
|
10
11
|
@ssh = ssh
|
|
11
12
|
end
|
|
12
13
|
|
|
13
|
-
# Build image locally, save to tar, rsync to remote, load with containerd
|
|
14
|
-
def build_and_deploy_image(path, tag, cache_from: nil)
|
|
15
|
-
cache_args = cache_from ? "--cache-from #{cache_from}" : ""
|
|
16
|
-
local_build_cmd = "cd #{path} && DOCKER_BUILDKIT=1 docker build --platform linux/amd64 #{cache_args} --build-arg BUILDKIT_INLINE_CACHE=1 -t #{tag} ."
|
|
17
|
-
|
|
18
|
-
unless system("bash", "-c", local_build_cmd)
|
|
19
|
-
raise Errors::SshError, "local build failed"
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
tar_file = "/tmp/#{tag.tr(':', '_')}.tar"
|
|
23
|
-
unless system("docker", "save", tag, "-o", tar_file)
|
|
24
|
-
raise Errors::SshError, "docker save failed"
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
begin
|
|
28
|
-
remote_tar_path = "/tmp/#{tag.tr(':', '_')}.tar"
|
|
29
|
-
rsync_cmd = [
|
|
30
|
-
"rsync", "-avz",
|
|
31
|
-
"-e", "ssh -i #{@ssh.ssh_key} -o StrictHostKeyChecking=no",
|
|
32
|
-
tar_file,
|
|
33
|
-
"#{@ssh.user}@#{@ssh.ip}:#{remote_tar_path}"
|
|
34
|
-
]
|
|
35
|
-
|
|
36
|
-
unless system(*rsync_cmd)
|
|
37
|
-
raise Errors::SshError, "rsync failed"
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
@ssh.execute("sudo ctr -n k8s.io images import #{remote_tar_path}")
|
|
41
|
-
|
|
42
|
-
full_image_ref = "docker.io/library/#{tag}"
|
|
43
|
-
|
|
44
|
-
begin
|
|
45
|
-
@ssh.execute("sudo ctr -n k8s.io images tag #{full_image_ref} #{tag}")
|
|
46
|
-
rescue Errors::SshCommandError => e
|
|
47
|
-
list_output = @ssh.execute("sudo ctr -n k8s.io images ls") rescue ""
|
|
48
|
-
raise Errors::SshError, "failed to tag imported image: #{e.message}\nAvailable images:\n#{list_output}"
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
@ssh.execute_ignore_errors("rm #{remote_tar_path}")
|
|
52
|
-
ensure
|
|
53
|
-
File.delete(tar_file) if File.exist?(tar_file)
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
14
|
def list_images(filter)
|
|
58
15
|
output = @ssh.execute("sudo ctr -n k8s.io images ls -q | grep '#{filter}' | sort -r")
|
|
59
16
|
return [] if output.empty?
|
|
@@ -64,24 +64,25 @@ module Nvoi
|
|
|
64
64
|
response["result"]
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
-
def update_tunnel_configuration(tunnel_id,
|
|
67
|
+
def update_tunnel_configuration(tunnel_id, hostnames, service_url)
|
|
68
|
+
hostnames = Array(hostnames)
|
|
68
69
|
url = "accounts/#{@account_id}/cfd_tunnel/#{tunnel_id}/configurations"
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
]
|
|
79
|
-
}
|
|
71
|
+
ingress_rules = hostnames.map do |hostname|
|
|
72
|
+
{
|
|
73
|
+
hostname:,
|
|
74
|
+
service: service_url,
|
|
75
|
+
originRequest: { httpHostHeader: hostname.sub(/^\*\./, "") } # Use apex for wildcard
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
ingress_rules << { service: "http_status:404" }
|
|
80
79
|
|
|
80
|
+
config = { ingress: ingress_rules }
|
|
81
81
|
put(url, { config: })
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
-
def verify_tunnel_configuration(tunnel_id,
|
|
84
|
+
def verify_tunnel_configuration(tunnel_id, expected_hostnames, expected_service, max_attempts)
|
|
85
|
+
expected_hostnames = Array(expected_hostnames)
|
|
85
86
|
url = "accounts/#{@account_id}/cfd_tunnel/#{tunnel_id}/configurations"
|
|
86
87
|
|
|
87
88
|
max_attempts.times do
|
|
@@ -90,10 +91,11 @@ module Nvoi
|
|
|
90
91
|
|
|
91
92
|
if response["success"]
|
|
92
93
|
config = response.dig("result", "config")
|
|
93
|
-
config&.dig("ingress")
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
ingress = config&.dig("ingress") || []
|
|
95
|
+
configured_hostnames = ingress.map { |r| r["hostname"] }.compact
|
|
96
|
+
|
|
97
|
+
if expected_hostnames.all? { |h| configured_hostnames.include?(h) }
|
|
98
|
+
return true
|
|
97
99
|
end
|
|
98
100
|
end
|
|
99
101
|
rescue StandardError
|
|
@@ -122,6 +124,16 @@ module Nvoi
|
|
|
122
124
|
|
|
123
125
|
# DNS operations
|
|
124
126
|
|
|
127
|
+
def list_zones
|
|
128
|
+
url = "zones"
|
|
129
|
+
response = get(url)
|
|
130
|
+
|
|
131
|
+
results = response["result"] || []
|
|
132
|
+
results.map do |z|
|
|
133
|
+
{ id: z["id"], name: z["name"], status: z["status"] }
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
125
137
|
def find_zone(domain)
|
|
126
138
|
url = "zones"
|
|
127
139
|
response = get(url)
|
|
@@ -135,6 +147,12 @@ module Nvoi
|
|
|
135
147
|
Objects::Dns::Zone.new(id: zone_data["id"], name: zone_data["name"])
|
|
136
148
|
end
|
|
137
149
|
|
|
150
|
+
def subdomain_available?(zone_id, subdomain, domain)
|
|
151
|
+
fqdn = subdomain.empty? ? domain : "#{subdomain}.#{domain}"
|
|
152
|
+
# Check for CNAME or A record
|
|
153
|
+
!find_dns_record(zone_id, fqdn, "CNAME") && !find_dns_record(zone_id, fqdn, "A")
|
|
154
|
+
end
|
|
155
|
+
|
|
138
156
|
def find_dns_record(zone_id, name, record_type)
|
|
139
157
|
url = "zones/#{zone_id}/dns_records"
|
|
140
158
|
response = get(url)
|
data/lib/nvoi/external/ssh.rb
CHANGED
|
@@ -62,18 +62,6 @@ module Nvoi
|
|
|
62
62
|
raise Errors::SshCommandError, "SCP download failed: #{output}" unless status.success?
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
def rsync(local_path, remote_path)
|
|
66
|
-
rsync_args = [
|
|
67
|
-
"-avz",
|
|
68
|
-
"-e", "ssh #{build_ssh_args.join(' ')}",
|
|
69
|
-
local_path,
|
|
70
|
-
"#{@user}@#{@ip}:#{remote_path}"
|
|
71
|
-
]
|
|
72
|
-
|
|
73
|
-
output, status = Open3.capture2e("rsync", *rsync_args)
|
|
74
|
-
raise Errors::SshCommandError, "rsync failed: #{output}" unless status.success?
|
|
75
|
-
end
|
|
76
|
-
|
|
77
65
|
private
|
|
78
66
|
|
|
79
67
|
def build_ssh_args
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/ssh"
|
|
4
|
+
|
|
5
|
+
module Nvoi
|
|
6
|
+
module External
|
|
7
|
+
# SshTunnel manages SSH port forwarding using net-ssh
|
|
8
|
+
class SshTunnel
|
|
9
|
+
attr_reader :local_port, :remote_port
|
|
10
|
+
|
|
11
|
+
def initialize(ip:, user:, key_path:, local_port:, remote_port:)
|
|
12
|
+
@ip = ip
|
|
13
|
+
@user = user
|
|
14
|
+
@key_path = key_path
|
|
15
|
+
@local_port = local_port
|
|
16
|
+
@remote_port = remote_port
|
|
17
|
+
@session = nil
|
|
18
|
+
@thread = nil
|
|
19
|
+
@running = false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def start
|
|
23
|
+
Nvoi.logger.info "Starting SSH tunnel: localhost:%d -> %s:%d", @local_port, @ip, @remote_port
|
|
24
|
+
|
|
25
|
+
@session = Net::SSH.start(
|
|
26
|
+
@ip,
|
|
27
|
+
@user,
|
|
28
|
+
keys: [@key_path],
|
|
29
|
+
non_interactive: true,
|
|
30
|
+
verify_host_key: :never
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
@session.forward.local(@local_port, "localhost", @remote_port)
|
|
34
|
+
@running = true
|
|
35
|
+
|
|
36
|
+
@thread = Thread.new do
|
|
37
|
+
Thread.current.report_on_exception = false
|
|
38
|
+
@session.loop { @running }
|
|
39
|
+
rescue IOError, Net::SSH::Disconnect, Errno::EBADF
|
|
40
|
+
# Session closed during shutdown, exit gracefully
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Wait for tunnel to establish
|
|
44
|
+
sleep 0.3
|
|
45
|
+
verify_tunnel!
|
|
46
|
+
|
|
47
|
+
Nvoi.logger.success "SSH tunnel established"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def stop
|
|
51
|
+
return unless @running
|
|
52
|
+
|
|
53
|
+
Nvoi.logger.info "Stopping SSH tunnel"
|
|
54
|
+
@running = false
|
|
55
|
+
|
|
56
|
+
# Give the event loop a moment to see @running = false
|
|
57
|
+
sleep 0.1
|
|
58
|
+
|
|
59
|
+
begin
|
|
60
|
+
@session&.forward&.cancel_local(@local_port)
|
|
61
|
+
rescue StandardError
|
|
62
|
+
# Ignore errors during cleanup
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
begin
|
|
66
|
+
@session&.close
|
|
67
|
+
rescue StandardError
|
|
68
|
+
# Ignore errors during cleanup
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Wait for thread to exit gracefully
|
|
72
|
+
@thread&.join(1)
|
|
73
|
+
|
|
74
|
+
@session = nil
|
|
75
|
+
@thread = nil
|
|
76
|
+
|
|
77
|
+
Nvoi.logger.success "SSH tunnel closed"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def alive?
|
|
81
|
+
@running && @thread&.alive? && @session && !@session.closed?
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def verify_tunnel!
|
|
87
|
+
unless alive?
|
|
88
|
+
raise Errors::SshError, "SSH tunnel failed to start"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Verify the port is actually listening
|
|
92
|
+
require "socket"
|
|
93
|
+
socket = TCPSocket.new("localhost", @local_port)
|
|
94
|
+
socket.close
|
|
95
|
+
rescue Errno::ECONNREFUSED
|
|
96
|
+
raise Errors::SshError, "SSH tunnel started but port #{@local_port} not accessible"
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -28,6 +28,7 @@ module Nvoi
|
|
|
28
28
|
validate_database_secrets(app.database) if app.database
|
|
29
29
|
inject_database_env_vars
|
|
30
30
|
validate_service_server_bindings
|
|
31
|
+
validate_domain_uniqueness
|
|
31
32
|
end
|
|
32
33
|
|
|
33
34
|
def provider_name
|
|
@@ -211,6 +212,25 @@ module Nvoi
|
|
|
211
212
|
)
|
|
212
213
|
end
|
|
213
214
|
end
|
|
215
|
+
|
|
216
|
+
def validate_domain_uniqueness
|
|
217
|
+
app = @deploy.application
|
|
218
|
+
return unless app.app
|
|
219
|
+
|
|
220
|
+
seen = {}
|
|
221
|
+
app.app.each do |name, cfg|
|
|
222
|
+
next unless cfg.domain && !cfg.domain.empty?
|
|
223
|
+
|
|
224
|
+
hostnames = Utils::Namer.build_hostnames(cfg.subdomain, cfg.domain)
|
|
225
|
+
hostnames.each do |hostname|
|
|
226
|
+
if seen[hostname]
|
|
227
|
+
raise Errors::ConfigValidationError,
|
|
228
|
+
"domain '#{hostname}' used by both '#{seen[hostname]}' and '#{name}'"
|
|
229
|
+
end
|
|
230
|
+
seen[hostname] = name
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
214
234
|
end
|
|
215
235
|
|
|
216
236
|
# Deploy represents the root deployment configuration
|
data/lib/nvoi/utils/namer.rb
CHANGED
|
@@ -185,6 +185,15 @@ module Nvoi
|
|
|
185
185
|
end
|
|
186
186
|
end
|
|
187
187
|
|
|
188
|
+
# Returns array of hostnames - apex returns [domain, *.domain], subdomain returns [sub.domain]
|
|
189
|
+
def self.build_hostnames(subdomain, domain)
|
|
190
|
+
if subdomain.nil? || subdomain.empty? || subdomain == "@"
|
|
191
|
+
[domain, "*.#{domain}"]
|
|
192
|
+
else
|
|
193
|
+
["#{subdomain}.#{domain}"]
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
188
197
|
private
|
|
189
198
|
|
|
190
199
|
def hash_string(str)
|
data/lib/nvoi/utils/retry.rb
CHANGED
|
@@ -77,7 +77,7 @@ module Nvoi
|
|
|
77
77
|
|
|
78
78
|
# Poll with error on timeout
|
|
79
79
|
def self.poll!(max_attempts: 30, interval: 2, error_message: "operation timed out")
|
|
80
|
-
result = poll(max_attempts
|
|
80
|
+
result = poll(max_attempts:, interval:) { yield }
|
|
81
81
|
raise Errors::TimeoutError, error_message unless result
|
|
82
82
|
|
|
83
83
|
result
|
data/lib/nvoi/version.rb
CHANGED
data/lib/nvoi.rb
CHANGED
|
@@ -16,8 +16,24 @@ require "faraday"
|
|
|
16
16
|
|
|
17
17
|
loader = Zeitwerk::Loader.for_gem
|
|
18
18
|
loader.ignore("#{__dir__}/nvoi/cli") # CLI commands are lazy-loaded
|
|
19
|
+
loader.ignore("#{__dir__}/nvoi/config_api") # ConfigApi uses non-standard naming
|
|
19
20
|
loader.setup
|
|
20
21
|
|
|
22
|
+
# Load ConfigApi manually (uses non-standard naming convention)
|
|
23
|
+
require_relative "nvoi/config_api/result"
|
|
24
|
+
require_relative "nvoi/config_api/base"
|
|
25
|
+
require_relative "nvoi/config_api/actions/init"
|
|
26
|
+
require_relative "nvoi/config_api/actions/domain_provider"
|
|
27
|
+
require_relative "nvoi/config_api/actions/compute_provider"
|
|
28
|
+
require_relative "nvoi/config_api/actions/server"
|
|
29
|
+
require_relative "nvoi/config_api/actions/volume"
|
|
30
|
+
require_relative "nvoi/config_api/actions/app"
|
|
31
|
+
require_relative "nvoi/config_api/actions/database"
|
|
32
|
+
require_relative "nvoi/config_api/actions/secret"
|
|
33
|
+
require_relative "nvoi/config_api/actions/env"
|
|
34
|
+
require_relative "nvoi/config_api/actions/service"
|
|
35
|
+
require_relative "nvoi/config_api"
|
|
36
|
+
|
|
21
37
|
module Nvoi
|
|
22
38
|
class << self
|
|
23
39
|
attr_accessor :logger
|
|
@@ -8,7 +8,8 @@ metadata:
|
|
|
8
8
|
spec:
|
|
9
9
|
ingressClassName: nginx
|
|
10
10
|
rules:
|
|
11
|
-
|
|
11
|
+
<% domains.each do |domain| -%>
|
|
12
|
+
- host: "<%= domain %>"
|
|
12
13
|
http:
|
|
13
14
|
paths:
|
|
14
15
|
- path: /
|
|
@@ -18,3 +19,4 @@ spec:
|
|
|
18
19
|
name: <%= name %>
|
|
19
20
|
port:
|
|
20
21
|
number: <%= port %>
|
|
22
|
+
<% end -%>
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: nvoi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- NVOI
|
|
@@ -173,6 +173,20 @@ executables:
|
|
|
173
173
|
extensions: []
|
|
174
174
|
extra_rdoc_files: []
|
|
175
175
|
files:
|
|
176
|
+
- ".claude/todo/refactor-execution/00-entrypoint.md"
|
|
177
|
+
- ".claude/todo/refactor-execution/01-objects.md"
|
|
178
|
+
- ".claude/todo/refactor-execution/02-utils.md"
|
|
179
|
+
- ".claude/todo/refactor-execution/03-external-cloud.md"
|
|
180
|
+
- ".claude/todo/refactor-execution/04-external-dns.md"
|
|
181
|
+
- ".claude/todo/refactor-execution/05-external-other.md"
|
|
182
|
+
- ".claude/todo/refactor-execution/06-cli-deploy.md"
|
|
183
|
+
- ".claude/todo/refactor-execution/07-cli-delete.md"
|
|
184
|
+
- ".claude/todo/refactor-execution/08-cli-exec.md"
|
|
185
|
+
- ".claude/todo/refactor-execution/09-cli-credentials.md"
|
|
186
|
+
- ".claude/todo/refactor-execution/10-cli-db.md"
|
|
187
|
+
- ".claude/todo/refactor-execution/11-cli-router.md"
|
|
188
|
+
- ".claude/todo/refactor-execution/12-cleanup.md"
|
|
189
|
+
- ".claude/todo/refactor-execution/_monitoring-strategy.md"
|
|
176
190
|
- ".claude/todo/refactor/00-overview.md"
|
|
177
191
|
- ".claude/todo/refactor/01-objects.md"
|
|
178
192
|
- ".claude/todo/refactor/02-utils.md"
|
|
@@ -185,9 +199,12 @@ files:
|
|
|
185
199
|
- ".claude/todo/refactor/09-cli-delete-command.md"
|
|
186
200
|
- ".claude/todo/refactor/10-cli-exec-command.md"
|
|
187
201
|
- ".claude/todo/refactor/11-cli-credentials-command.md"
|
|
202
|
+
- ".claude/todo/refactor/12-cli-db-command.md"
|
|
188
203
|
- ".claude/todo/refactor/_target.md"
|
|
189
204
|
- ".claude/todo/scaleway.impl.md"
|
|
190
205
|
- ".claude/todo/scaleway.reference.md"
|
|
206
|
+
- ".claude/todos.md"
|
|
207
|
+
- ".claude/todos/buckets.md"
|
|
191
208
|
- ".rubocop.yml"
|
|
192
209
|
- Gemfile
|
|
193
210
|
- Gemfile.lock
|
|
@@ -311,8 +328,10 @@ files:
|
|
|
311
328
|
- examples/rails-single/vendor/.keep
|
|
312
329
|
- examples/rails-single/yarn.lock
|
|
313
330
|
- exe/nvoi
|
|
331
|
+
- ingest
|
|
314
332
|
- lib/nvoi.rb
|
|
315
333
|
- lib/nvoi/cli.rb
|
|
334
|
+
- lib/nvoi/cli/config/command.rb
|
|
316
335
|
- lib/nvoi/cli/credentials/edit/command.rb
|
|
317
336
|
- lib/nvoi/cli/credentials/show/command.rb
|
|
318
337
|
- lib/nvoi/cli/db/command.rb
|
|
@@ -334,13 +353,19 @@ files:
|
|
|
334
353
|
- lib/nvoi/cli/deploy/steps/provision_volume.rb
|
|
335
354
|
- lib/nvoi/cli/deploy/steps/setup_k3s.rb
|
|
336
355
|
- lib/nvoi/cli/exec/command.rb
|
|
356
|
+
- lib/nvoi/cli/logs/command.rb
|
|
357
|
+
- lib/nvoi/cli/onboard/command.rb
|
|
358
|
+
- lib/nvoi/cli/unlock/command.rb
|
|
337
359
|
- lib/nvoi/config_api.rb
|
|
338
360
|
- lib/nvoi/config_api/actions/app.rb
|
|
339
361
|
- lib/nvoi/config_api/actions/compute_provider.rb
|
|
340
362
|
- lib/nvoi/config_api/actions/database.rb
|
|
363
|
+
- lib/nvoi/config_api/actions/domain_provider.rb
|
|
341
364
|
- lib/nvoi/config_api/actions/env.rb
|
|
365
|
+
- lib/nvoi/config_api/actions/init.rb
|
|
342
366
|
- lib/nvoi/config_api/actions/secret.rb
|
|
343
367
|
- lib/nvoi/config_api/actions/server.rb
|
|
368
|
+
- lib/nvoi/config_api/actions/service.rb
|
|
344
369
|
- lib/nvoi/config_api/actions/volume.rb
|
|
345
370
|
- lib/nvoi/config_api/base.rb
|
|
346
371
|
- lib/nvoi/config_api/result.rb
|
|
@@ -360,6 +385,7 @@ files:
|
|
|
360
385
|
- lib/nvoi/external/dns/cloudflare.rb
|
|
361
386
|
- lib/nvoi/external/kubectl.rb
|
|
362
387
|
- lib/nvoi/external/ssh.rb
|
|
388
|
+
- lib/nvoi/external/ssh_tunnel.rb
|
|
363
389
|
- lib/nvoi/objects/config_override.rb
|
|
364
390
|
- lib/nvoi/objects/configuration.rb
|
|
365
391
|
- lib/nvoi/objects/database.rb
|