nvoi 0.1.6 → 0.1.7
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.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/steps/configure_tunnel.rb +15 -13
- data/lib/nvoi/cli/deploy/steps/deploy_service.rb +5 -2
- 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 +4 -0
- data/lib/nvoi/external/dns/cloudflare.rb +34 -16
- 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 +25 -1
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nvoi
|
|
4
|
+
class Cli
|
|
5
|
+
module Config
|
|
6
|
+
# Command helper for all config operations
|
|
7
|
+
# Uses CredentialStore for crypto, ConfigApi for transformations
|
|
8
|
+
class Command
|
|
9
|
+
def initialize(options)
|
|
10
|
+
@options = options
|
|
11
|
+
@working_dir = options[:dir] || "."
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Initialize new config
|
|
15
|
+
def init(name, environment)
|
|
16
|
+
result = ConfigApi.init(name:, environment:)
|
|
17
|
+
|
|
18
|
+
if result.failure?
|
|
19
|
+
error("Failed to initialize: #{result.error_message}")
|
|
20
|
+
return
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Write encrypted config
|
|
24
|
+
config_path = File.join(@working_dir, Utils::DEFAULT_ENCRYPTED_FILE)
|
|
25
|
+
key_path = File.join(@working_dir, Utils::DEFAULT_KEY_FILE)
|
|
26
|
+
|
|
27
|
+
File.binwrite(config_path, result.config)
|
|
28
|
+
File.write(key_path, "#{result.master_key}\n", perm: 0o600)
|
|
29
|
+
|
|
30
|
+
update_gitignore
|
|
31
|
+
|
|
32
|
+
success("Created #{Utils::DEFAULT_ENCRYPTED_FILE}")
|
|
33
|
+
success("Created #{Utils::DEFAULT_KEY_FILE} (keep safe, never commit)")
|
|
34
|
+
puts
|
|
35
|
+
puts "Next steps:"
|
|
36
|
+
puts " nvoi config domain set cloudflare --api-token=TOKEN --account-id=ID"
|
|
37
|
+
puts " nvoi config provider set hetzner --api-token=TOKEN --server-type=cx22 --location=fsn1"
|
|
38
|
+
puts " nvoi config server set web --master"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Domain provider
|
|
42
|
+
def domain_set(provider, api_token:, account_id:)
|
|
43
|
+
with_config do |data|
|
|
44
|
+
ConfigApi.set_domain_provider(data, provider:, api_token:, account_id:)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def domain_rm
|
|
49
|
+
with_config do |data|
|
|
50
|
+
ConfigApi.delete_domain_provider(data)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Compute provider
|
|
55
|
+
def provider_set(provider, **opts)
|
|
56
|
+
with_config do |data|
|
|
57
|
+
ConfigApi.set_compute_provider(data, provider:, **opts)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def provider_rm
|
|
62
|
+
with_config do |data|
|
|
63
|
+
ConfigApi.delete_compute_provider(data)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Server
|
|
68
|
+
def server_set(name, master: false, type: nil, location: nil, count: 1)
|
|
69
|
+
with_config do |data|
|
|
70
|
+
ConfigApi.set_server(data, name:, master:, type:, location:, count:)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def server_rm(name)
|
|
75
|
+
with_config do |data|
|
|
76
|
+
ConfigApi.delete_server(data, name:)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Volume
|
|
81
|
+
def volume_set(server, name, size: 10)
|
|
82
|
+
with_config do |data|
|
|
83
|
+
ConfigApi.set_volume(data, server:, name:, size:)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def volume_rm(server, name)
|
|
88
|
+
with_config do |data|
|
|
89
|
+
ConfigApi.delete_volume(data, server:, name:)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# App
|
|
94
|
+
def app_set(name, servers:, **opts)
|
|
95
|
+
with_config do |data|
|
|
96
|
+
ConfigApi.set_app(data, name:, servers:, **opts)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def app_rm(name)
|
|
101
|
+
with_config do |data|
|
|
102
|
+
ConfigApi.delete_app(data, name:)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Database
|
|
107
|
+
def database_set(servers:, adapter:, **opts)
|
|
108
|
+
with_config do |data|
|
|
109
|
+
ConfigApi.set_database(data, servers:, adapter:, **opts)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def database_rm
|
|
114
|
+
with_config do |data|
|
|
115
|
+
ConfigApi.delete_database(data)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Service
|
|
120
|
+
def service_set(name, servers:, image:, **opts)
|
|
121
|
+
with_config do |data|
|
|
122
|
+
ConfigApi.set_service(data, name:, servers:, image:, **opts)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def service_rm(name)
|
|
127
|
+
with_config do |data|
|
|
128
|
+
ConfigApi.delete_service(data, name:)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Secret
|
|
133
|
+
def secret_set(key_name, value)
|
|
134
|
+
with_config do |data|
|
|
135
|
+
ConfigApi.set_secret(data, key: key_name, value:)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def secret_rm(key_name)
|
|
140
|
+
with_config do |data|
|
|
141
|
+
ConfigApi.delete_secret(data, key: key_name)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Env
|
|
146
|
+
def env_set(key_name, value)
|
|
147
|
+
with_config do |data|
|
|
148
|
+
ConfigApi.set_env(data, key: key_name, value:)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def env_rm(key_name)
|
|
153
|
+
with_config do |data|
|
|
154
|
+
ConfigApi.delete_env(data, key: key_name)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
private
|
|
159
|
+
|
|
160
|
+
def with_config
|
|
161
|
+
store = Utils::CredentialStore.new(
|
|
162
|
+
@working_dir,
|
|
163
|
+
@options[:credentials],
|
|
164
|
+
@options[:master_key]
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
unless store.exists?
|
|
168
|
+
error("Config not found: #{store.encrypted_path}")
|
|
169
|
+
error("Run 'nvoi config init --name=myapp' first")
|
|
170
|
+
return
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Read and parse
|
|
174
|
+
yaml = store.read
|
|
175
|
+
data = YAML.safe_load(yaml, permitted_classes: [Symbol])
|
|
176
|
+
|
|
177
|
+
# Transform
|
|
178
|
+
result = yield(data)
|
|
179
|
+
|
|
180
|
+
if result.failure?
|
|
181
|
+
error("#{result.error_type}: #{result.error_message}")
|
|
182
|
+
else
|
|
183
|
+
# Serialize and write
|
|
184
|
+
store.write(YAML.dump(result.data))
|
|
185
|
+
success("Config updated")
|
|
186
|
+
end
|
|
187
|
+
rescue Errors::CredentialError => e
|
|
188
|
+
error(e.message)
|
|
189
|
+
rescue Errors::DecryptionError => e
|
|
190
|
+
error("Decryption failed: #{e.message}")
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def update_gitignore
|
|
194
|
+
gitignore_path = File.join(@working_dir, ".gitignore")
|
|
195
|
+
entries = ["deploy.key", ".env", ".env.*", "!.env.example", "!.env.*.example"]
|
|
196
|
+
|
|
197
|
+
existing = File.exist?(gitignore_path) ? File.read(gitignore_path) : ""
|
|
198
|
+
additions = entries.reject { |e| existing.include?(e) }
|
|
199
|
+
|
|
200
|
+
return if additions.empty?
|
|
201
|
+
|
|
202
|
+
File.open(gitignore_path, "a") do |f|
|
|
203
|
+
f.puts "" unless existing.end_with?("\n") || existing.empty?
|
|
204
|
+
f.puts "# NVOI - sensitive files"
|
|
205
|
+
additions.each { |e| f.puts e }
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def success(msg)
|
|
210
|
+
puts "\e[32m✓\e[0m #{msg}"
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def error(msg)
|
|
214
|
+
warn "\e[31m✗\e[0m #{msg}"
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
@@ -15,18 +15,15 @@ module Nvoi
|
|
|
15
15
|
def run
|
|
16
16
|
@config.deploy.application.app.each do |_service_name, service|
|
|
17
17
|
next unless service&.domain && !service.domain.empty?
|
|
18
|
-
next if service.subdomain.nil?
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
delete_dns_records(service.domain, service.subdomain)
|
|
21
20
|
end
|
|
22
21
|
end
|
|
23
22
|
|
|
24
23
|
private
|
|
25
24
|
|
|
26
|
-
def
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@log.info "Deleting DNS record: %s", hostname
|
|
25
|
+
def delete_dns_records(domain, subdomain)
|
|
26
|
+
hostnames = Utils::Namer.build_hostnames(subdomain, domain)
|
|
30
27
|
|
|
31
28
|
zone = @cf_client.find_zone(domain)
|
|
32
29
|
unless zone
|
|
@@ -34,13 +31,17 @@ module Nvoi
|
|
|
34
31
|
return
|
|
35
32
|
end
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
hostnames.each do |hostname|
|
|
35
|
+
@log.info "Deleting DNS record: %s", hostname
|
|
36
|
+
|
|
37
|
+
record = @cf_client.find_dns_record(zone.id, hostname, "CNAME")
|
|
38
|
+
if record
|
|
39
|
+
@cf_client.delete_dns_record(zone.id, record.id)
|
|
40
|
+
@log.success "DNS record deleted: %s", hostname
|
|
41
|
+
end
|
|
41
42
|
end
|
|
42
43
|
rescue StandardError => e
|
|
43
|
-
@log.warning "Failed to delete DNS
|
|
44
|
+
@log.warning "Failed to delete DNS records: %s", e.message
|
|
44
45
|
end
|
|
45
46
|
end
|
|
46
47
|
end
|
|
@@ -22,7 +22,6 @@ module Nvoi
|
|
|
22
22
|
@config.deploy.application.app.each do |service_name, service_config|
|
|
23
23
|
next unless service_config.domain && !service_config.domain.empty?
|
|
24
24
|
next unless service_config.port && service_config.port.positive?
|
|
25
|
-
next if service_config.subdomain.nil?
|
|
26
25
|
|
|
27
26
|
tunnel_info = configure_service_tunnel(service_name, service_config)
|
|
28
27
|
tunnels << tunnel_info
|
|
@@ -36,23 +35,24 @@ module Nvoi
|
|
|
36
35
|
|
|
37
36
|
def configure_service_tunnel(service_name, service_config)
|
|
38
37
|
tunnel_name = @config.namer.tunnel_name(service_name)
|
|
39
|
-
|
|
38
|
+
hostnames = Utils::Namer.build_hostnames(service_config.subdomain, service_config.domain)
|
|
39
|
+
primary_hostname = hostnames.first
|
|
40
40
|
|
|
41
41
|
# Service URL points to the NGINX Ingress Controller
|
|
42
42
|
service_url = "http://ingress-nginx-controller.ingress-nginx.svc.cluster.local:80"
|
|
43
43
|
|
|
44
|
-
tunnel = setup_tunnel(tunnel_name,
|
|
44
|
+
tunnel = setup_tunnel(tunnel_name, hostnames, service_url, service_config.domain)
|
|
45
45
|
|
|
46
46
|
Objects::Tunnel::Info.new(
|
|
47
47
|
service_name:,
|
|
48
|
-
hostname
|
|
48
|
+
hostname: primary_hostname,
|
|
49
49
|
tunnel_id: tunnel.tunnel_id,
|
|
50
50
|
tunnel_token: tunnel.tunnel_token
|
|
51
51
|
)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
def setup_tunnel(tunnel_name,
|
|
55
|
-
@log.info "Setting up tunnel: %s -> %s", tunnel_name,
|
|
54
|
+
def setup_tunnel(tunnel_name, hostnames, service_url, domain)
|
|
55
|
+
@log.info "Setting up tunnel: %s -> %s", tunnel_name, hostnames.join(", ")
|
|
56
56
|
|
|
57
57
|
# Find or create tunnel
|
|
58
58
|
tunnel = @cf_client.find_tunnel(tunnel_name)
|
|
@@ -70,21 +70,23 @@ module Nvoi
|
|
|
70
70
|
token = @cf_client.get_tunnel_token(tunnel.id)
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
# Configure tunnel ingress
|
|
74
|
-
@log.info "Configuring tunnel ingress: %s -> %s",
|
|
75
|
-
@cf_client.update_tunnel_configuration(tunnel.id,
|
|
73
|
+
# Configure tunnel ingress for all hostnames
|
|
74
|
+
@log.info "Configuring tunnel ingress: %s -> %s", hostnames.join(", "), service_url
|
|
75
|
+
@cf_client.update_tunnel_configuration(tunnel.id, hostnames, service_url)
|
|
76
76
|
|
|
77
77
|
# Verify configuration propagated
|
|
78
78
|
@log.info "Verifying tunnel configuration..."
|
|
79
|
-
@cf_client.verify_tunnel_configuration(tunnel.id,
|
|
79
|
+
@cf_client.verify_tunnel_configuration(tunnel.id, hostnames, service_url, Utils::Constants::TUNNEL_CONFIG_VERIFY_ATTEMPTS)
|
|
80
80
|
|
|
81
|
-
# Create DNS
|
|
82
|
-
@log.info "Creating DNS CNAME record: %s", hostname
|
|
81
|
+
# Create DNS records for all hostnames
|
|
83
82
|
zone = @cf_client.find_zone(domain)
|
|
84
83
|
raise Errors::CloudflareError, "zone not found: #{domain}" unless zone
|
|
85
84
|
|
|
86
85
|
tunnel_cname = "#{tunnel.id}.cfargotunnel.com"
|
|
87
|
-
|
|
86
|
+
hostnames.each do |hostname|
|
|
87
|
+
@log.info "Creating DNS CNAME record: %s", hostname
|
|
88
|
+
@cf_client.create_or_update_dns_record(zone.id, hostname, "CNAME", tunnel_cname, proxied: true)
|
|
89
|
+
end
|
|
88
90
|
|
|
89
91
|
@log.success "Tunnel configured: %s", tunnel_name
|
|
90
92
|
|
|
@@ -247,11 +247,11 @@ module Nvoi
|
|
|
247
247
|
|
|
248
248
|
# Deploy ingress if domain is specified
|
|
249
249
|
if service_config.domain && !service_config.domain.empty?
|
|
250
|
-
|
|
250
|
+
hostnames = Utils::Namer.build_hostnames(service_config.subdomain, service_config.domain)
|
|
251
251
|
|
|
252
252
|
Utils::Templates.apply_manifest(@ssh, "app-ingress.yaml", {
|
|
253
253
|
name: deployment_name,
|
|
254
|
-
|
|
254
|
+
domains: hostnames,
|
|
255
255
|
port: service_config.port
|
|
256
256
|
})
|
|
257
257
|
end
|
|
@@ -323,6 +323,9 @@ module Nvoi
|
|
|
323
323
|
result = check_public_url(public_url)
|
|
324
324
|
|
|
325
325
|
if result[:success]
|
|
326
|
+
if consecutive_success == 0
|
|
327
|
+
@log.info "[%d/%d] App responding, verifying stability...", attempt + 1, max_attempts
|
|
328
|
+
end
|
|
326
329
|
consecutive_success += 1
|
|
327
330
|
@log.success "[%d/%d] Public URL responding: %s", consecutive_success, required_consecutive, result[:http_code]
|
|
328
331
|
|
|
@@ -467,7 +467,16 @@ module Nvoi
|
|
|
467
467
|
ssh.execute(patch_deployment)
|
|
468
468
|
|
|
469
469
|
@log.info "Waiting for ingress controller to restart..."
|
|
470
|
-
|
|
470
|
+
ready = Utils::Retry.poll(max_attempts: 60, interval: 2) do
|
|
471
|
+
begin
|
|
472
|
+
ready_replicas = ssh.execute("kubectl get deployment ingress-nginx-controller -n ingress-nginx -o jsonpath='{.status.readyReplicas}'").strip
|
|
473
|
+
desired_replicas = ssh.execute("kubectl get deployment ingress-nginx-controller -n ingress-nginx -o jsonpath='{.spec.replicas}'").strip
|
|
474
|
+
!ready_replicas.empty? && !desired_replicas.empty? && ready_replicas == desired_replicas
|
|
475
|
+
rescue Errors::SshCommandError
|
|
476
|
+
false
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
raise Errors::K8sError, "Ingress controller failed to restart" unless ready
|
|
471
480
|
else
|
|
472
481
|
@log.info "Custom error backend already configured"
|
|
473
482
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nvoi
|
|
4
|
+
class Cli
|
|
5
|
+
module Logs
|
|
6
|
+
# Command streams logs from app pods
|
|
7
|
+
class Command
|
|
8
|
+
def initialize(options)
|
|
9
|
+
@options = options
|
|
10
|
+
@log = Nvoi.logger
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def run(app_name)
|
|
14
|
+
config_path = resolve_config_path
|
|
15
|
+
@config = Utils::ConfigLoader.load(config_path)
|
|
16
|
+
|
|
17
|
+
# Apply branch override if specified
|
|
18
|
+
apply_branch_override if @options[:branch]
|
|
19
|
+
|
|
20
|
+
# Initialize cloud provider
|
|
21
|
+
@provider = External::Cloud.for(@config)
|
|
22
|
+
|
|
23
|
+
# Find main server
|
|
24
|
+
server = @provider.find_server(@config.server_name)
|
|
25
|
+
raise Errors::ServiceError, "server not found: #{@config.server_name}" unless server
|
|
26
|
+
|
|
27
|
+
# Build deployment name from app name
|
|
28
|
+
deployment_name = @config.namer.app_deployment_name(app_name)
|
|
29
|
+
|
|
30
|
+
# Build kubectl logs command
|
|
31
|
+
# --prefix shows pod name, --all-containers handles multi-container pods
|
|
32
|
+
follow_flag = @options[:follow] ? "-f" : ""
|
|
33
|
+
tail_flag = "--tail=#{@options[:tail]}"
|
|
34
|
+
|
|
35
|
+
kubectl_cmd = "kubectl logs -l app=#{deployment_name} --prefix --all-containers #{follow_flag} #{tail_flag}".strip.squeeze(" ")
|
|
36
|
+
|
|
37
|
+
@log.info "Streaming logs for %s", deployment_name
|
|
38
|
+
|
|
39
|
+
ssh = External::Ssh.new(server.public_ipv4, @config.ssh_key_path)
|
|
40
|
+
ssh.execute(kubectl_cmd, stream: true)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def resolve_config_path
|
|
46
|
+
config_path = @options[:config] || "deploy.enc"
|
|
47
|
+
working_dir = @options[:dir]
|
|
48
|
+
|
|
49
|
+
if config_path == "deploy.enc" && working_dir && working_dir != "."
|
|
50
|
+
File.join(working_dir, "deploy.enc")
|
|
51
|
+
else
|
|
52
|
+
config_path
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def apply_branch_override
|
|
57
|
+
branch = @options[:branch]
|
|
58
|
+
return if branch.nil? || branch.empty?
|
|
59
|
+
|
|
60
|
+
override = Objects::ConfigOverride.new(branch:)
|
|
61
|
+
override.apply(@config)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|