nvoi 0.1.8 → 0.2.0
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/Gemfile +1 -5
- data/Gemfile.lock +17 -8
- data/Rakefile +1 -1
- data/lib/nvoi/cli/config/command.rb +46 -41
- data/lib/nvoi/cli/credentials/edit/command.rb +20 -20
- data/lib/nvoi/cli/credentials/show/command.rb +1 -1
- data/lib/nvoi/cli/db/command.rb +10 -10
- data/lib/nvoi/cli/delete/command.rb +2 -2
- data/lib/nvoi/cli/deploy/command.rb +2 -2
- data/lib/nvoi/cli/deploy/steps/configure_tunnel.rb +2 -2
- data/lib/nvoi/cli/deploy/steps/provision_server.rb +1 -1
- data/lib/nvoi/cli/deploy/steps/provision_volume.rb +1 -1
- data/lib/nvoi/cli/exec/command.rb +3 -3
- data/lib/nvoi/cli/logs/command.rb +2 -2
- data/lib/nvoi/cli/onboard/command.rb +176 -622
- data/lib/nvoi/cli/onboard/steps/app.rb +108 -0
- data/lib/nvoi/cli/onboard/steps/app_name.rb +26 -0
- data/lib/nvoi/cli/onboard/steps/compute.rb +139 -0
- data/lib/nvoi/cli/onboard/steps/database.rb +97 -0
- data/lib/nvoi/cli/onboard/steps/domain.rb +48 -0
- data/lib/nvoi/cli/onboard/steps/env.rb +67 -0
- data/lib/nvoi/cli/onboard/ui.rb +84 -0
- data/lib/nvoi/cli/unlock/command.rb +2 -2
- data/lib/nvoi/cli.rb +0 -32
- data/lib/nvoi/configuration/app_service.rb +54 -0
- data/lib/nvoi/configuration/application.rb +44 -0
- data/lib/nvoi/configuration/builder.rb +417 -0
- data/lib/nvoi/configuration/database.rb +56 -0
- data/lib/nvoi/configuration/deploy.rb +15 -0
- data/lib/nvoi/{objects/service_spec.rb → configuration/deployment.rb} +4 -3
- data/lib/nvoi/{objects/config_override.rb → configuration/override.rb} +4 -4
- data/lib/nvoi/configuration/providers.rb +78 -0
- data/lib/nvoi/configuration/result.rb +43 -0
- data/lib/nvoi/configuration/root.rb +234 -0
- data/lib/nvoi/configuration/server.rb +39 -0
- data/lib/nvoi/configuration/service.rb +62 -0
- data/lib/nvoi/external/cloud/aws.rb +12 -12
- data/lib/nvoi/external/cloud/hetzner.rb +7 -7
- data/lib/nvoi/external/cloud/scaleway.rb +7 -7
- data/lib/nvoi/external/cloud/types.rb +42 -0
- data/lib/nvoi/external/database/mysql.rb +1 -1
- data/lib/nvoi/external/database/postgres.rb +1 -1
- data/lib/nvoi/external/database/provider.rb +1 -1
- data/lib/nvoi/external/database/sqlite.rb +1 -1
- data/lib/nvoi/external/database/types.rb +55 -0
- data/lib/nvoi/external/dns/cloudflare.rb +6 -6
- data/lib/nvoi/external/dns/types.rb +24 -0
- data/lib/nvoi/utils/config_loader.rb +12 -12
- data/lib/nvoi/utils/credential_store.rb +4 -4
- data/lib/nvoi/utils/env_resolver.rb +3 -3
- data/lib/nvoi/utils/namer.rb +2 -2
- data/lib/nvoi/utils/presence.rb +23 -0
- data/lib/nvoi/version.rb +1 -1
- data/lib/nvoi.rb +2 -17
- metadata +95 -58
- data/.claude/todo/refactor/00-overview.md +0 -171
- data/.claude/todo/refactor/01-objects.md +0 -96
- data/.claude/todo/refactor/02-utils.md +0 -143
- data/.claude/todo/refactor/03-external-cloud.md +0 -164
- data/.claude/todo/refactor/04-external-dns.md +0 -104
- data/.claude/todo/refactor/05-external.md +0 -133
- data/.claude/todo/refactor/06-cli.md +0 -123
- data/.claude/todo/refactor/07-cli-deploy-command.md +0 -177
- data/.claude/todo/refactor/08-cli-deploy-steps.md +0 -201
- data/.claude/todo/refactor/09-cli-delete-command.md +0 -169
- data/.claude/todo/refactor/10-cli-exec-command.md +0 -157
- data/.claude/todo/refactor/11-cli-credentials-command.md +0 -190
- data/.claude/todo/refactor/12-cli-db-command.md +0 -128
- data/.claude/todo/refactor/_target.md +0 -79
- data/.claude/todo/refactor-execution/00-entrypoint.md +0 -49
- data/.claude/todo/refactor-execution/01-objects.md +0 -42
- data/.claude/todo/refactor-execution/02-utils.md +0 -41
- data/.claude/todo/refactor-execution/03-external-cloud.md +0 -38
- data/.claude/todo/refactor-execution/04-external-dns.md +0 -35
- data/.claude/todo/refactor-execution/05-external-other.md +0 -46
- data/.claude/todo/refactor-execution/06-cli-deploy.md +0 -45
- data/.claude/todo/refactor-execution/07-cli-delete.md +0 -43
- data/.claude/todo/refactor-execution/08-cli-exec.md +0 -30
- data/.claude/todo/refactor-execution/09-cli-credentials.md +0 -34
- data/.claude/todo/refactor-execution/10-cli-db.md +0 -31
- data/.claude/todo/refactor-execution/11-cli-router.md +0 -44
- data/.claude/todo/refactor-execution/12-cleanup.md +0 -120
- data/.claude/todo/refactor-execution/_monitoring-strategy.md +0 -126
- data/.claude/todo/scaleway.impl.md +0 -644
- data/.claude/todo/scaleway.reference.md +0 -520
- data/.claude/todos/buckets.md +0 -41
- data/.claude/todos.md +0 -550
- data/ingest +0 -0
- data/lib/nvoi/config_api/actions/app.rb +0 -53
- data/lib/nvoi/config_api/actions/compute_provider.rb +0 -55
- data/lib/nvoi/config_api/actions/database.rb +0 -70
- data/lib/nvoi/config_api/actions/domain_provider.rb +0 -40
- data/lib/nvoi/config_api/actions/env.rb +0 -32
- data/lib/nvoi/config_api/actions/init.rb +0 -67
- data/lib/nvoi/config_api/actions/secret.rb +0 -32
- data/lib/nvoi/config_api/actions/server.rb +0 -66
- data/lib/nvoi/config_api/actions/service.rb +0 -52
- data/lib/nvoi/config_api/actions/volume.rb +0 -40
- data/lib/nvoi/config_api/base.rb +0 -38
- data/lib/nvoi/config_api/result.rb +0 -26
- data/lib/nvoi/config_api.rb +0 -93
- data/lib/nvoi/objects/configuration.rb +0 -483
- data/lib/nvoi/objects/database.rb +0 -56
- data/lib/nvoi/objects/dns.rb +0 -14
- data/lib/nvoi/objects/firewall.rb +0 -11
- data/lib/nvoi/objects/network.rb +0 -11
- data/lib/nvoi/objects/server.rb +0 -14
- data/lib/nvoi/objects/tunnel.rb +0 -14
- data/lib/nvoi/objects/volume.rb +0 -17
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
# 09 - CLI: Delete Command
|
|
2
|
-
|
|
3
|
-
## Priority: FIFTH (parallel with deploy)
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Current State
|
|
8
|
-
|
|
9
|
-
| File | Lines | Purpose |
|
|
10
|
-
| ------------------- | ----- | -------------------------------------- |
|
|
11
|
-
| `service/delete.rb` | 235 | DeleteService - teardown all resources |
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Target Structure
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
lib/nvoi/cli/delete/
|
|
19
|
-
├── command.rb
|
|
20
|
-
└── steps/
|
|
21
|
-
├── teardown_tunnel.rb
|
|
22
|
-
├── teardown_dns.rb
|
|
23
|
-
├── detach_volumes.rb
|
|
24
|
-
├── teardown_server.rb
|
|
25
|
-
├── teardown_volume.rb
|
|
26
|
-
├── teardown_firewall.rb
|
|
27
|
-
└── teardown_network.rb
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
## What Delete Does (Current)
|
|
33
|
-
|
|
34
|
-
```ruby
|
|
35
|
-
def run
|
|
36
|
-
detach_volumes # Must happen before server deletion
|
|
37
|
-
delete_all_servers # Delete servers from all groups
|
|
38
|
-
delete_volumes # Now safe to delete
|
|
39
|
-
delete_firewall # With retry (might still be attached)
|
|
40
|
-
delete_network
|
|
41
|
-
delete_cloudflare_resources # Tunnels + DNS records
|
|
42
|
-
end
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## Target Flow
|
|
48
|
-
|
|
49
|
-
```ruby
|
|
50
|
-
# cli/delete/command.rb
|
|
51
|
-
class Command
|
|
52
|
-
def run
|
|
53
|
-
config = Utils::ConfigLoader.new.load(config_path)
|
|
54
|
-
provider = External::Cloud.for(config)
|
|
55
|
-
cloudflare = External::DNS::Cloudflare.new(config.cloudflare)
|
|
56
|
-
log = Utils::Logger.new
|
|
57
|
-
|
|
58
|
-
# Order matters!
|
|
59
|
-
Steps::DetachVolumes.new(config, provider, log).run
|
|
60
|
-
Steps::TeardownServer.new(config, provider, log).run
|
|
61
|
-
Steps::TeardownVolume.new(config, provider, log).run
|
|
62
|
-
Steps::TeardownFirewall.new(config, provider, log).run
|
|
63
|
-
Steps::TeardownNetwork.new(config, provider, log).run
|
|
64
|
-
Steps::TeardownTunnel.new(config, cloudflare, log).run
|
|
65
|
-
Steps::TeardownDNS.new(config, cloudflare, log).run
|
|
66
|
-
|
|
67
|
-
log.success "Cleanup complete"
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## DRY Opportunities
|
|
75
|
-
|
|
76
|
-
### 1. Shared Volume Name Collection
|
|
77
|
-
|
|
78
|
-
Both `detach_volumes` and `delete_volumes` call `collect_volume_names`.
|
|
79
|
-
Extract to utils or pass as step output:
|
|
80
|
-
|
|
81
|
-
```ruby
|
|
82
|
-
volumes = Steps::DetachVolumes.new(...).run # returns volume list
|
|
83
|
-
Steps::TeardownVolume.new(...).run(volumes)
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### 2. Shared Hostname Builder
|
|
87
|
-
|
|
88
|
-
`build_hostname` is duplicated. Already planned for `utils/namer.rb`.
|
|
89
|
-
|
|
90
|
-
### 3. Merge Tunnel + DNS Teardown
|
|
91
|
-
|
|
92
|
-
Both iterate over app services with domains. Could be single step:
|
|
93
|
-
|
|
94
|
-
```ruby
|
|
95
|
-
Steps::TeardownCloudflare.new(config, cloudflare, log).run
|
|
96
|
-
# Deletes both tunnels and DNS records
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### 4. Error Tolerance
|
|
100
|
-
|
|
101
|
-
Delete operations should be idempotent. Current code does:
|
|
102
|
-
|
|
103
|
-
```ruby
|
|
104
|
-
rescue StandardError => e
|
|
105
|
-
@log.warning "Failed to delete: %s", e.message
|
|
106
|
-
end
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
Keep this pattern - deletion should continue even if some resources are already gone.
|
|
110
|
-
|
|
111
|
-
---
|
|
112
|
-
|
|
113
|
-
## Step Details
|
|
114
|
-
|
|
115
|
-
### detach_volumes.rb (~40 lines)
|
|
116
|
-
|
|
117
|
-
- Collect all volume names
|
|
118
|
-
- For each: find volume, detach if attached
|
|
119
|
-
|
|
120
|
-
### teardown_server.rb (~50 lines)
|
|
121
|
-
|
|
122
|
-
- For each server group:
|
|
123
|
-
- For each server in group:
|
|
124
|
-
- Find and delete server
|
|
125
|
-
|
|
126
|
-
### teardown_volume.rb (~40 lines)
|
|
127
|
-
|
|
128
|
-
- For each volume name:
|
|
129
|
-
- Find and delete volume
|
|
130
|
-
|
|
131
|
-
### teardown_firewall.rb (~30 lines)
|
|
132
|
-
|
|
133
|
-
- Find firewall by name
|
|
134
|
-
- Delete with retry (may still be detaching)
|
|
135
|
-
|
|
136
|
-
### teardown_network.rb (~20 lines)
|
|
137
|
-
|
|
138
|
-
- Find network by name
|
|
139
|
-
- Delete
|
|
140
|
-
|
|
141
|
-
### teardown_tunnel.rb (~40 lines)
|
|
142
|
-
|
|
143
|
-
- For each app service with domain:
|
|
144
|
-
- Find and delete tunnel
|
|
145
|
-
|
|
146
|
-
### teardown_dns.rb (~40 lines)
|
|
147
|
-
|
|
148
|
-
- For each app service with domain:
|
|
149
|
-
- Find zone
|
|
150
|
-
- Find and delete CNAME record
|
|
151
|
-
|
|
152
|
-
---
|
|
153
|
-
|
|
154
|
-
## Migration Steps
|
|
155
|
-
|
|
156
|
-
1. Create `lib/nvoi/cli/delete/command.rb`
|
|
157
|
-
2. Create `lib/nvoi/cli/delete/steps/` directory
|
|
158
|
-
3. Extract each operation from `service/delete.rb` into separate step
|
|
159
|
-
4. Move `collect_volume_names` to command (shared state)
|
|
160
|
-
5. Delete `service/delete.rb`
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
## Estimated Effort
|
|
165
|
-
|
|
166
|
-
- **Lines to reorganize:** 235
|
|
167
|
-
- **Files created:** 8 (command + 7 steps)
|
|
168
|
-
- **Files deleted:** 1
|
|
169
|
-
- **DRY savings:** ~30 lines (shared hostname, merged tunnel+dns option)
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
# 10 - CLI: Exec Command
|
|
2
|
-
|
|
3
|
-
## Priority: FIFTH (parallel with deploy/delete)
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Current State
|
|
8
|
-
|
|
9
|
-
| File | Lines | Purpose |
|
|
10
|
-
|------|-------|---------|
|
|
11
|
-
| `service/exec.rb` | 145 | ExecService - remote command execution |
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Target Structure
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
lib/nvoi/cli/exec/
|
|
19
|
-
└── command.rb # Single file, no steps needed
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## What Exec Does
|
|
25
|
-
|
|
26
|
-
Three modes:
|
|
27
|
-
1. **Single server:** `nvoi exec "ls -la"` → run on main server
|
|
28
|
-
2. **All servers:** `nvoi exec --all "ls -la"` → run on all servers in parallel
|
|
29
|
-
3. **Interactive:** `nvoi exec -i` → open SSH shell
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## Target Implementation
|
|
34
|
-
|
|
35
|
-
```ruby
|
|
36
|
-
# cli/exec/command.rb
|
|
37
|
-
module Nvoi
|
|
38
|
-
module CLI
|
|
39
|
-
module Exec
|
|
40
|
-
class Command
|
|
41
|
-
def initialize(options)
|
|
42
|
-
@options = options
|
|
43
|
-
@log = Utils::Logger.new
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def run(args)
|
|
47
|
-
config = Utils::ConfigLoader.new.load(config_path)
|
|
48
|
-
provider = External::Cloud.for(config)
|
|
49
|
-
|
|
50
|
-
if @options[:interactive]
|
|
51
|
-
open_shell(config, provider)
|
|
52
|
-
elsif @options[:all]
|
|
53
|
-
run_all(config, provider, args.join(" "))
|
|
54
|
-
else
|
|
55
|
-
run_single(config, provider, args.join(" "), @options[:server])
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
private
|
|
60
|
-
|
|
61
|
-
def run_single(config, provider, command, server_name)
|
|
62
|
-
server = find_server(config, provider, server_name)
|
|
63
|
-
ssh = External::SSH.new(server.public_ipv4, config.ssh_key_path)
|
|
64
|
-
|
|
65
|
-
@log.info "Executing on %s: %s", server.name, command
|
|
66
|
-
ssh.execute(command, stream: true)
|
|
67
|
-
@log.success "Command completed"
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def run_all(config, provider, command)
|
|
71
|
-
servers = all_servers(config, provider)
|
|
72
|
-
@log.info "Executing on %d servers", servers.size
|
|
73
|
-
|
|
74
|
-
threads = servers.map do |server|
|
|
75
|
-
Thread.new do
|
|
76
|
-
ssh = External::SSH.new(server.public_ipv4, config.ssh_key_path)
|
|
77
|
-
output = ssh.execute(command)
|
|
78
|
-
[server.name, output]
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
threads.each do |t|
|
|
83
|
-
name, output = t.value
|
|
84
|
-
output.lines.each { |line| puts "[#{name}] #{line}" }
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def open_shell(config, provider)
|
|
89
|
-
server = find_server(config, provider, @options[:server])
|
|
90
|
-
ssh = External::SSH.new(server.public_ipv4, config.ssh_key_path)
|
|
91
|
-
ssh.open_shell
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def find_server(config, provider, name)
|
|
95
|
-
actual_name = resolve_server_name(config, name)
|
|
96
|
-
provider.find_server(actual_name) or raise ServiceError, "server not found: #{actual_name}"
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def resolve_server_name(config, name)
|
|
100
|
-
return config.server_name if name == "main" || name.nil?
|
|
101
|
-
# Parse "worker-1" → server_name("worker", 1)
|
|
102
|
-
# ...
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def all_servers(config, provider)
|
|
106
|
-
config.deploy.application.servers.flat_map do |group, cfg|
|
|
107
|
-
(1..cfg.count).map { |i| provider.find_server(config.namer.server_name(group, i)) }
|
|
108
|
-
end.compact
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
## DRY Opportunities
|
|
119
|
-
|
|
120
|
-
### 1. Server Name Resolution
|
|
121
|
-
`resolve_server_name` logic is useful. Could go to `utils/namer.rb`:
|
|
122
|
-
```ruby
|
|
123
|
-
# utils/namer.rb
|
|
124
|
-
def resolve_server_reference(ref)
|
|
125
|
-
# "main" → server_name for master
|
|
126
|
-
# "worker-1" → server_name("worker", 1)
|
|
127
|
-
end
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### 2. Server Collection
|
|
131
|
-
`all_servers` pattern could be a config method:
|
|
132
|
-
```ruby
|
|
133
|
-
# objects/config.rb
|
|
134
|
-
def each_server
|
|
135
|
-
deploy.application.servers.each do |group, cfg|
|
|
136
|
-
(1..cfg.count).each { |i| yield(group, i) }
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
---
|
|
142
|
-
|
|
143
|
-
## Migration Steps
|
|
144
|
-
|
|
145
|
-
1. Create `lib/nvoi/cli/exec/command.rb`
|
|
146
|
-
2. Move logic from `service/exec.rb`
|
|
147
|
-
3. Extract `resolve_server_name` to `utils/namer.rb`
|
|
148
|
-
4. Delete `service/exec.rb`
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## Estimated Effort
|
|
153
|
-
|
|
154
|
-
- **Lines to reorganize:** 145
|
|
155
|
-
- **Files created:** 1
|
|
156
|
-
- **Files deleted:** 1
|
|
157
|
-
- **Net change:** ~0 (simple move + namespace change)
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
# 11 - CLI: Credentials Commands
|
|
2
|
-
|
|
3
|
-
## Priority: FIFTH (parallel with others)
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Current State
|
|
8
|
-
|
|
9
|
-
| File | Lines | Purpose |
|
|
10
|
-
| ------------------------- | ----- | ------------------------------- |
|
|
11
|
-
| `cli.rb` (CredentialsCLI) | ~55 | Thor subcommand for credentials |
|
|
12
|
-
| `credentials/editor.rb` | ~80 | Edit/show encrypted credentials |
|
|
13
|
-
| `credentials/manager.rb` | ~150 | Load/save encrypted files |
|
|
14
|
-
| `credentials/crypto.rb` | ~100 | AES encryption |
|
|
15
|
-
|
|
16
|
-
**Total: ~385 lines**
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## Target Structure
|
|
21
|
-
|
|
22
|
-
```
|
|
23
|
-
lib/nvoi/cli/credentials/
|
|
24
|
-
├── edit/
|
|
25
|
-
│ └── command.rb # nvoi credentials edit
|
|
26
|
-
└── show/
|
|
27
|
-
└── command.rb # nvoi credentials show
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
Note: `crypto.rb` and `manager.rb` move to `utils/` (see 02-utils.md)
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## What Each Command Does
|
|
35
|
-
|
|
36
|
-
### credentials edit
|
|
37
|
-
|
|
38
|
-
1. Load or create credentials file
|
|
39
|
-
2. Decrypt to temp file
|
|
40
|
-
3. Open in $EDITOR
|
|
41
|
-
4. Validate YAML on save
|
|
42
|
-
5. Re-encrypt
|
|
43
|
-
6. Update .gitignore if first run
|
|
44
|
-
|
|
45
|
-
### credentials show
|
|
46
|
-
|
|
47
|
-
1. Load credentials file
|
|
48
|
-
2. Decrypt
|
|
49
|
-
3. Print to stdout
|
|
50
|
-
|
|
51
|
-
---
|
|
52
|
-
|
|
53
|
-
## Target Implementation
|
|
54
|
-
|
|
55
|
-
```ruby
|
|
56
|
-
# cli/credentials/edit/command.rb
|
|
57
|
-
module Nvoi
|
|
58
|
-
module CLI
|
|
59
|
-
module Credentials
|
|
60
|
-
module Edit
|
|
61
|
-
class Command
|
|
62
|
-
def initialize(options)
|
|
63
|
-
@options = options
|
|
64
|
-
@log = Utils::Logger.new
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def run
|
|
68
|
-
working_dir = resolve_working_dir
|
|
69
|
-
store = Utils::CredentialStore.new(working_dir, @options[:credentials], @options[:master_key])
|
|
70
|
-
|
|
71
|
-
if store.exists?
|
|
72
|
-
edit_existing(store)
|
|
73
|
-
else
|
|
74
|
-
create_new(store)
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
private
|
|
79
|
-
|
|
80
|
-
def edit_existing(store)
|
|
81
|
-
content = store.read
|
|
82
|
-
new_content = open_in_editor(content)
|
|
83
|
-
validate_yaml!(new_content)
|
|
84
|
-
store.write(new_content)
|
|
85
|
-
@log.success "Credentials updated"
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def create_new(store)
|
|
89
|
-
@log.info "Creating new credentials file"
|
|
90
|
-
template = default_template
|
|
91
|
-
new_content = open_in_editor(template)
|
|
92
|
-
validate_yaml!(new_content)
|
|
93
|
-
store.write(new_content)
|
|
94
|
-
store.update_gitignore
|
|
95
|
-
@log.success "Credentials created"
|
|
96
|
-
@log.warning "Keep your master key safe!"
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def open_in_editor(content)
|
|
100
|
-
# Write to temp file, exec $EDITOR, read back
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def validate_yaml!(content)
|
|
104
|
-
YAML.safe_load(content)
|
|
105
|
-
rescue Psych::SyntaxError => e
|
|
106
|
-
raise ConfigError, "Invalid YAML: #{e.message}"
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
```ruby
|
|
116
|
-
# cli/credentials/show/command.rb
|
|
117
|
-
module Nvoi
|
|
118
|
-
module CLI
|
|
119
|
-
module Credentials
|
|
120
|
-
module Show
|
|
121
|
-
class Command
|
|
122
|
-
def initialize(options)
|
|
123
|
-
@options = options
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def run
|
|
127
|
-
working_dir = resolve_working_dir
|
|
128
|
-
store = Utils::CredentialStore.new(working_dir, @options[:credentials], @options[:master_key])
|
|
129
|
-
puts store.read
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## DRY Opportunities
|
|
141
|
-
|
|
142
|
-
### 1. Merge Crypto + Manager → CredentialStore
|
|
143
|
-
|
|
144
|
-
Already planned in 02-utils.md. Single class:
|
|
145
|
-
|
|
146
|
-
```ruby
|
|
147
|
-
# utils/crypto.rb
|
|
148
|
-
class CredentialStore
|
|
149
|
-
def initialize(working_dir, enc_path = nil, key_path = nil)
|
|
150
|
-
def exists? → bool
|
|
151
|
-
def read → String
|
|
152
|
-
def write(content)
|
|
153
|
-
def update_gitignore
|
|
154
|
-
end
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### 2. Remove Editor Class
|
|
158
|
-
|
|
159
|
-
Current `credentials/editor.rb` is thin wrapper. Logic goes directly into command.
|
|
160
|
-
|
|
161
|
-
### 3. YAML Validation
|
|
162
|
-
|
|
163
|
-
Move to utils for reuse:
|
|
164
|
-
|
|
165
|
-
```ruby
|
|
166
|
-
# utils/config_loader.rb
|
|
167
|
-
def self.validate_yaml!(content)
|
|
168
|
-
YAML.safe_load(content, permitted_classes: [Symbol])
|
|
169
|
-
end
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
## Migration Steps
|
|
175
|
-
|
|
176
|
-
1. Move `credentials/crypto.rb` + `credentials/manager.rb` → `utils/crypto.rb` (02-utils.md)
|
|
177
|
-
2. Create `lib/nvoi/cli/credentials/edit/command.rb`
|
|
178
|
-
3. Create `lib/nvoi/cli/credentials/show/command.rb`
|
|
179
|
-
4. Move editor logic into `edit/command.rb`
|
|
180
|
-
5. Delete `credentials/editor.rb`
|
|
181
|
-
6. Update `cli.rb` to route to new commands
|
|
182
|
-
|
|
183
|
-
---
|
|
184
|
-
|
|
185
|
-
## Estimated Effort
|
|
186
|
-
|
|
187
|
-
- **Lines to reorganize:** ~385
|
|
188
|
-
- **Files created:** 2
|
|
189
|
-
- **Files deleted:** 3 (`editor.rb`, `manager.rb`, `crypto.rb` → merged to utils)
|
|
190
|
-
- **Net reduction:** ~50 lines (removed wrapper, merged classes)
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
# 12 - CLI: Database Commands
|
|
2
|
-
|
|
3
|
-
## Priority: FIFTH (parallel with others)
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Current State (NEW from main)
|
|
8
|
-
|
|
9
|
-
| File | Lines | Purpose |
|
|
10
|
-
|------|-------|---------|
|
|
11
|
-
| `service/db.rb` | 285 | DbService - database branch operations |
|
|
12
|
-
| `database/provider.rb` | 160 | Base provider + factory + structs |
|
|
13
|
-
| `database/postgres.rb` | ~100 | Postgres dump/restore |
|
|
14
|
-
| `database/mysql.rb` | ~100 | MySQL dump/restore |
|
|
15
|
-
| `database/sqlite.rb` | ~80 | SQLite dump/restore |
|
|
16
|
-
|
|
17
|
-
**Total: ~725 lines**
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## What It Does
|
|
22
|
-
|
|
23
|
-
Database "branching" = snapshots for backup/restore:
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
nvoi db branch create [name] # Dump current DB to file
|
|
27
|
-
nvoi db branch list # List saved branches
|
|
28
|
-
nvoi db branch restore <id> # Restore branch to new DB
|
|
29
|
-
nvoi db branch download <id> # Download dump locally
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## Target Structure
|
|
35
|
-
|
|
36
|
-
```
|
|
37
|
-
lib/nvoi/cli/db/
|
|
38
|
-
├── command.rb # Router for subcommands
|
|
39
|
-
├── branch/
|
|
40
|
-
│ ├── create/
|
|
41
|
-
│ │ └── command.rb
|
|
42
|
-
│ ├── list/
|
|
43
|
-
│ │ └── command.rb
|
|
44
|
-
│ ├── restore/
|
|
45
|
-
│ │ └── command.rb
|
|
46
|
-
│ └── download/
|
|
47
|
-
│ └── command.rb
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
OR simpler (single file since operations are small):
|
|
51
|
-
|
|
52
|
-
```
|
|
53
|
-
lib/nvoi/cli/db/
|
|
54
|
-
└── command.rb # All branch operations
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
## Database Module → External
|
|
60
|
-
|
|
61
|
-
The `database/` providers are external adapters (talk to DBs via kubectl exec):
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
lib/nvoi/external/database/
|
|
65
|
-
├── provider.rb # Base + factory
|
|
66
|
-
├── postgres.rb
|
|
67
|
-
├── mysql.rb
|
|
68
|
-
└── sqlite.rb
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
**Note:** These use `kubectl exec` to run dump/restore commands in pods. They're external adapters like cloud providers.
|
|
72
|
-
|
|
73
|
-
---
|
|
74
|
-
|
|
75
|
-
## Objects to Extract
|
|
76
|
-
|
|
77
|
-
From `database/provider.rb`:
|
|
78
|
-
|
|
79
|
-
```ruby
|
|
80
|
-
# objects/database.rb
|
|
81
|
-
Credentials = Struct.new(:user, :password, :host, :port, :database, :path, keyword_init: true)
|
|
82
|
-
DumpOptions = Struct.new(:pod_name, :database, :user, :password, :host_path, keyword_init: true)
|
|
83
|
-
RestoreOptions = Struct.new(:pod_name, :database, :user, :password, :source_db, :host_path, keyword_init: true)
|
|
84
|
-
CreateOptions = Struct.new(:pod_name, :database, :user, :password, keyword_init: true)
|
|
85
|
-
Branch = Struct.new(:id, :created_at, :size, :adapter, :database, keyword_init: true)
|
|
86
|
-
BranchMetadata # class with branches array
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
## DRY Opportunities
|
|
92
|
-
|
|
93
|
-
### 1. SSH Helper Pattern
|
|
94
|
-
`DbService#with_ssh` creates SSH executor. Same pattern in other services.
|
|
95
|
-
Extract to base or mixin:
|
|
96
|
-
|
|
97
|
-
```ruby
|
|
98
|
-
# In command base or utils
|
|
99
|
-
def with_master_ssh(config, provider)
|
|
100
|
-
server_ip = get_master_server_ip(config, provider)
|
|
101
|
-
ssh = External::SSH.new(server_ip, config.ssh_key_path)
|
|
102
|
-
yield ssh
|
|
103
|
-
end
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### 2. Database Credentials Resolution
|
|
107
|
-
`Config::DatabaseHelper.get_credentials` is called from multiple places.
|
|
108
|
-
Keep in config or move to database provider factory.
|
|
109
|
-
|
|
110
|
-
---
|
|
111
|
-
|
|
112
|
-
## Migration Steps
|
|
113
|
-
|
|
114
|
-
1. Create `lib/nvoi/external/database/` directory
|
|
115
|
-
2. Move `database/*.rb` → `external/database/`
|
|
116
|
-
3. Extract Structs to `objects/database.rb`
|
|
117
|
-
4. Create `lib/nvoi/cli/db/command.rb`
|
|
118
|
-
5. Move logic from `service/db.rb` → `cli/db/command.rb`
|
|
119
|
-
6. Delete `service/db.rb`
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## Estimated Effort
|
|
124
|
-
|
|
125
|
-
- **Lines to reorganize:** ~725
|
|
126
|
-
- **Files created:** 5 (1 command + 4 external/database)
|
|
127
|
-
- **Files deleted/moved:** 5
|
|
128
|
-
- **Net change:** minimal (reorganization)
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
lib/nvoi/
|
|
2
|
-
│
|
|
3
|
-
├── cli.rb # Thor routing only
|
|
4
|
-
│
|
|
5
|
-
├── cli/
|
|
6
|
-
│ ├── deploy/
|
|
7
|
-
│ │ ├── command.rb
|
|
8
|
-
│ │ └── steps/
|
|
9
|
-
│ │ ├── provision_network.rb
|
|
10
|
-
│ │ ├── provision_server.rb
|
|
11
|
-
│ │ ├── provision_volume.rb
|
|
12
|
-
│ │ ├── setup_k3s.rb
|
|
13
|
-
│ │ ├── configure_tunnel.rb
|
|
14
|
-
│ │ ├── build_image.rb
|
|
15
|
-
│ │ ├── deploy_service.rb
|
|
16
|
-
│ │ └── cleanup_images.rb
|
|
17
|
-
│ │
|
|
18
|
-
│ ├── delete/
|
|
19
|
-
│ │ ├── command.rb
|
|
20
|
-
│ │ └── steps/
|
|
21
|
-
│ │ ├── teardown_tunnel.rb
|
|
22
|
-
│ │ ├── teardown_dns.rb
|
|
23
|
-
│ │ ├── detach_volumes.rb
|
|
24
|
-
│ │ ├── teardown_server.rb
|
|
25
|
-
│ │ ├── teardown_volume.rb
|
|
26
|
-
│ │ ├── teardown_firewall.rb
|
|
27
|
-
│ │ └── teardown_network.rb
|
|
28
|
-
│ │
|
|
29
|
-
│ ├── exec/
|
|
30
|
-
│ │ └── command.rb
|
|
31
|
-
│ │
|
|
32
|
-
│ └── credentials/
|
|
33
|
-
│ ├── edit/
|
|
34
|
-
│ │ └── command.rb
|
|
35
|
-
│ └── show/
|
|
36
|
-
│ └── command.rb
|
|
37
|
-
│
|
|
38
|
-
├── external/
|
|
39
|
-
│ ├── cloud/
|
|
40
|
-
│ │ ├── base.rb
|
|
41
|
-
│ │ ├── hetzner.rb
|
|
42
|
-
│ │ ├── aws.rb
|
|
43
|
-
│ │ └── scaleway.rb
|
|
44
|
-
│ ├── dns/
|
|
45
|
-
│ │ └── cloudflare.rb
|
|
46
|
-
│ ├── ssh.rb
|
|
47
|
-
│ ├── kubectl.rb
|
|
48
|
-
│ └── containerd.rb
|
|
49
|
-
│
|
|
50
|
-
├── objects/
|
|
51
|
-
│ ├── server.rb
|
|
52
|
-
│ ├── network.rb
|
|
53
|
-
│ ├── firewall.rb
|
|
54
|
-
│ ├── volume.rb
|
|
55
|
-
│ ├── tunnel.rb
|
|
56
|
-
│ ├── dns_record.rb
|
|
57
|
-
│ ├── zone.rb
|
|
58
|
-
│ ├── service_spec.rb
|
|
59
|
-
│ └── config.rb
|
|
60
|
-
│
|
|
61
|
-
└── utils/
|
|
62
|
-
├── namer.rb
|
|
63
|
-
├── env_resolver.rb
|
|
64
|
-
├── crypto.rb
|
|
65
|
-
├── templates.rb
|
|
66
|
-
├── logger.rb
|
|
67
|
-
└── constants.rb
|
|
68
|
-
|
|
69
|
-
---
|
|
70
|
-
|
|
71
|
-
Summary:
|
|
72
|
-
|
|
73
|
-
| Folder | Purpose | Rule |
|
|
74
|
-
| ------------- | ---------------------- | ------------------------------------- |
|
|
75
|
-
| cli/ | Command entrypoints | 1 command = 1 command.rb |
|
|
76
|
-
| cli/\*/steps/ | Multi-step pipelines | Only for commands with ordered phases |
|
|
77
|
-
| external/ | Outside world adapters | APIs, SSH, CLIs |
|
|
78
|
-
| objects/ | Data structures | No behavior, just shape |
|
|
79
|
-
| utils/ | Stateless helpers | Used everywhere |
|