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,133 +0,0 @@
|
|
|
1
|
-
# 05 - External: SSH, Kubectl, Containerd
|
|
2
|
-
|
|
3
|
-
## Priority: THIRD (parallel with cloud/dns)
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Current State
|
|
8
|
-
|
|
9
|
-
| File | Lines | Purpose |
|
|
10
|
-
| -------------------------- | ----- | ---------------------------- |
|
|
11
|
-
| `remote/ssh_executor.rb` | 73 | SSH command execution |
|
|
12
|
-
| `remote/docker_manager.rb` | 204 | Docker/containerd operations |
|
|
13
|
-
| `remote/volume_manager.rb` | 104 | Volume mount operations |
|
|
14
|
-
| `k8s/renderer.rb` | 45 | kubectl apply wrapper |
|
|
15
|
-
|
|
16
|
-
**Total: ~426 lines across 4 files**
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## Target Structure
|
|
21
|
-
|
|
22
|
-
```
|
|
23
|
-
lib/nvoi/external/
|
|
24
|
-
├── ssh.rb # SSH executor
|
|
25
|
-
├── kubectl.rb # kubectl wrapper (from k8s/renderer.rb)
|
|
26
|
-
└── containerd.rb # containerd/Docker operations (from docker_manager.rb + volume_manager.rb)
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## What Each Does
|
|
32
|
-
|
|
33
|
-
### SSH Executor (`remote/ssh_executor.rb`)
|
|
34
|
-
|
|
35
|
-
- `execute(command, stream: false)` → String
|
|
36
|
-
- `execute_quiet(command)` → nil (ignore errors)
|
|
37
|
-
- `open_shell` → exec into SSH
|
|
38
|
-
|
|
39
|
-
### Docker Manager (`remote/docker_manager.rb`)
|
|
40
|
-
|
|
41
|
-
- `create_network(name)`
|
|
42
|
-
- `build_image(path, tag, cache_from)` → local build, rsync, ctr import
|
|
43
|
-
- `run_container(opts)`
|
|
44
|
-
- `exec(container, command)`
|
|
45
|
-
- `wait_for_health(opts)`
|
|
46
|
-
- `container_status(name)`, `container_logs(name, lines)`
|
|
47
|
-
- `stop_container(name)`, `remove_container(name)`
|
|
48
|
-
- `list_containers(filter)`, `list_images(filter)`
|
|
49
|
-
- `cleanup_old_images(prefix, keep_tags)`
|
|
50
|
-
- `container_running?(name)`
|
|
51
|
-
- `setup_cloudflared(network, token, name)`
|
|
52
|
-
|
|
53
|
-
### Volume Manager (`remote/volume_manager.rb`)
|
|
54
|
-
|
|
55
|
-
- `mount(opts)` → format + mount + fstab
|
|
56
|
-
- `unmount(mount_path)`
|
|
57
|
-
- `mounted?(mount_path)`
|
|
58
|
-
- `remove_from_fstab(mount_path)`
|
|
59
|
-
|
|
60
|
-
### K8s Renderer (`k8s/renderer.rb`)
|
|
61
|
-
|
|
62
|
-
- `render_template(name, data)` → String
|
|
63
|
-
- `apply_manifest(ssh, template_name, data)` → kubectl apply
|
|
64
|
-
- `wait_for_deployment(ssh, name, namespace, timeout)`
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## DRY Opportunities
|
|
69
|
-
|
|
70
|
-
### 1. Rename docker_manager → containerd
|
|
71
|
-
|
|
72
|
-
It's not really Docker anymore. It uses `ctr` (containerd). Name it accurately:
|
|
73
|
-
|
|
74
|
-
```ruby
|
|
75
|
-
# external/containerd.rb
|
|
76
|
-
class Containerd
|
|
77
|
-
def initialize(ssh) ... end
|
|
78
|
-
def build_and_import(path, tag) ... end
|
|
79
|
-
def list_images(filter) ... end
|
|
80
|
-
def cleanup_images(prefix, keep_tags) ... end
|
|
81
|
-
end
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### 2. Extract Volume Logic
|
|
85
|
-
|
|
86
|
-
Volume mounting is separate from container runtime. Could stay in containerd or be separate.
|
|
87
|
-
Decision: Keep in containerd since it's all "server-side stuff via SSH".
|
|
88
|
-
|
|
89
|
-
### 3. Simplify K8s Renderer
|
|
90
|
-
|
|
91
|
-
Move template rendering to `utils/templates.rb`. Keep only kubectl operations:
|
|
92
|
-
|
|
93
|
-
```ruby
|
|
94
|
-
# external/kubectl.rb
|
|
95
|
-
class Kubectl
|
|
96
|
-
def initialize(ssh) ... end
|
|
97
|
-
def apply(manifest) ... end
|
|
98
|
-
def wait_for_deployment(name, namespace, timeout) ... end
|
|
99
|
-
def wait_for_statefulset(name, namespace, timeout) ... end
|
|
100
|
-
def get_pod_name(label) ... end
|
|
101
|
-
def exec(pod, command) ... end
|
|
102
|
-
end
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### 4. SSH as Dependency
|
|
106
|
-
|
|
107
|
-
All external adapters that run on remote servers need SSH:
|
|
108
|
-
|
|
109
|
-
- `Containerd.new(ssh)`
|
|
110
|
-
- `Kubectl.new(ssh)`
|
|
111
|
-
- `VolumeManager.new(ssh)` (if kept separate)
|
|
112
|
-
|
|
113
|
-
This is correct. SSH is the transport, others are domain-specific.
|
|
114
|
-
|
|
115
|
-
---
|
|
116
|
-
|
|
117
|
-
## Migration Steps
|
|
118
|
-
|
|
119
|
-
1. Move `remote/ssh_executor.rb` → `external/ssh.rb`
|
|
120
|
-
2. Rename `remote/docker_manager.rb` → `external/containerd.rb`
|
|
121
|
-
3. Merge `remote/volume_manager.rb` into `external/containerd.rb` (or keep separate)
|
|
122
|
-
4. Extract kubectl operations from `k8s/renderer.rb` → `external/kubectl.rb`
|
|
123
|
-
5. Move template rendering to `utils/templates.rb` (already planned in 02-utils.md)
|
|
124
|
-
6. Update namespace from `Remote::SSHExecutor` to `External::SSH`
|
|
125
|
-
|
|
126
|
-
---
|
|
127
|
-
|
|
128
|
-
## Estimated Effort
|
|
129
|
-
|
|
130
|
-
- **Lines to consolidate:** ~426
|
|
131
|
-
- **Files created:** 3
|
|
132
|
-
- **Files deleted:** 4
|
|
133
|
-
- **DRY savings:** ~30 lines (simplified renderer, naming cleanup)
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
# 06 - CLI Entry Point (Thor Routing)
|
|
2
|
-
|
|
3
|
-
## Priority: FOURTH
|
|
4
|
-
|
|
5
|
-
CLI depends on all command implementations.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Current State
|
|
10
|
-
|
|
11
|
-
| File | Lines | Purpose |
|
|
12
|
-
| -------- | ----- | ----------------------------------------- |
|
|
13
|
-
| `cli.rb` | 191 | Thor commands + CredentialsCLI subcommand |
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## Target Structure
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
lib/nvoi/
|
|
21
|
-
└── cli.rb # Thor routing only (~50 lines)
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## What CLI Currently Does
|
|
27
|
-
|
|
28
|
-
1. **Thor setup** - class options, exit behavior
|
|
29
|
-
2. **Command definitions** - `deploy`, `delete`, `exec`, `credentials`
|
|
30
|
-
3. **Config path resolution** - `resolve_config_path`, `resolve_working_dir`
|
|
31
|
-
4. **Instantiate services** - creates `DeployService`, `DeleteService`, etc.
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
## Target: Thin Router
|
|
36
|
-
|
|
37
|
-
CLI should ONLY:
|
|
38
|
-
|
|
39
|
-
1. Define Thor commands
|
|
40
|
-
2. Parse options
|
|
41
|
-
3. Delegate to `cli/<command>/command.rb`
|
|
42
|
-
|
|
43
|
-
```ruby
|
|
44
|
-
# cli.rb
|
|
45
|
-
module Nvoi
|
|
46
|
-
class CLI < Thor
|
|
47
|
-
class_option :config, aliases: "-c", default: "deploy.enc"
|
|
48
|
-
class_option :dir, aliases: "-d", default: "."
|
|
49
|
-
|
|
50
|
-
desc "deploy", "Deploy application"
|
|
51
|
-
option :dockerfile_path
|
|
52
|
-
def deploy
|
|
53
|
-
require_relative "cli/deploy/command"
|
|
54
|
-
CLI::Deploy::Command.new(options).run
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
desc "delete", "Delete infrastructure"
|
|
58
|
-
def delete
|
|
59
|
-
require_relative "cli/delete/command"
|
|
60
|
-
CLI::Delete::Command.new(options).run
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
desc "exec [COMMAND...]", "Execute command on server"
|
|
64
|
-
option :server, default: "main"
|
|
65
|
-
option :all, type: :boolean
|
|
66
|
-
option :interactive, aliases: "-i", type: :boolean
|
|
67
|
-
def exec(*args)
|
|
68
|
-
require_relative "cli/exec/command"
|
|
69
|
-
CLI::Exec::Command.new(options).run(args)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
desc "credentials SUBCOMMAND", "Manage credentials"
|
|
73
|
-
subcommand "credentials", CLI::Credentials
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
class CLI::Credentials < Thor
|
|
77
|
-
desc "edit", "Edit encrypted credentials"
|
|
78
|
-
def edit
|
|
79
|
-
require_relative "cli/credentials/edit/command"
|
|
80
|
-
CLI::Credentials::Edit::Command.new(options).run
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
desc "show", "Show decrypted credentials"
|
|
84
|
-
def show
|
|
85
|
-
require_relative "cli/credentials/show/command"
|
|
86
|
-
CLI::Credentials::Show::Command.new(options).run
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
---
|
|
93
|
-
|
|
94
|
-
## DRY Opportunities
|
|
95
|
-
|
|
96
|
-
### 1. Move Logic to Commands
|
|
97
|
-
|
|
98
|
-
All the `resolve_config_path`, validation, service instantiation moves INTO each command.
|
|
99
|
-
|
|
100
|
-
### 2. Lazy Requires
|
|
101
|
-
|
|
102
|
-
Use `require_relative` inside methods. Faster CLI startup, only loads what's needed.
|
|
103
|
-
|
|
104
|
-
### 3. CredentialsCLI → Nested Class
|
|
105
|
-
|
|
106
|
-
Currently separate class, can be nested under CLI.
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
## Migration Steps
|
|
111
|
-
|
|
112
|
-
1. Create command files first (07-10)
|
|
113
|
-
2. Strip `cli.rb` down to routing only
|
|
114
|
-
3. Move `CredentialsCLI` inline as `CLI::Credentials`
|
|
115
|
-
4. Add lazy requires for each command
|
|
116
|
-
|
|
117
|
-
---
|
|
118
|
-
|
|
119
|
-
## Estimated Effort
|
|
120
|
-
|
|
121
|
-
- **Lines after refactor:** ~50 (down from 191)
|
|
122
|
-
- **Logic moved to:** `cli/*/command.rb` files
|
|
123
|
-
- **DRY savings:** 141 lines moved (not deleted, redistributed)
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
# 07 - CLI: Deploy Command
|
|
2
|
-
|
|
3
|
-
## Priority: FIFTH
|
|
4
|
-
|
|
5
|
-
Depends on external adapters, utils, objects.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Current State
|
|
10
|
-
|
|
11
|
-
| File | Lines | Purpose |
|
|
12
|
-
| ------------------- | ----- | --------------------------- |
|
|
13
|
-
| `service/deploy.rb` | 81 | DeployService orchestration |
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## Target Structure
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
lib/nvoi/cli/deploy/
|
|
21
|
-
├── command.rb # Entry point + step orchestration
|
|
22
|
-
└── steps/
|
|
23
|
-
├── provision_network.rb
|
|
24
|
-
├── provision_server.rb
|
|
25
|
-
├── provision_volume.rb
|
|
26
|
-
├── setup_k3s.rb
|
|
27
|
-
├── configure_tunnel.rb
|
|
28
|
-
├── build_image.rb
|
|
29
|
-
├── deploy_database.rb
|
|
30
|
-
├── deploy_service.rb
|
|
31
|
-
└── cleanup_images.rb
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
## What Deploy Does (Current Flow)
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
1. Load config
|
|
40
|
-
2. Init provider
|
|
41
|
-
3. Validate provider config
|
|
42
|
-
4. provision_server:
|
|
43
|
-
a. ServerProvisioner.run → provisions network, firewall, servers
|
|
44
|
-
b. VolumeProvisioner.run → creates/attaches/mounts volumes
|
|
45
|
-
c. K3sClusterSetup.run → installs K3s on master + workers
|
|
46
|
-
5. configure_tunnels:
|
|
47
|
-
a. TunnelConfigurator.run → creates Cloudflare tunnels + DNS
|
|
48
|
-
6. deploy_application:
|
|
49
|
-
a. Orchestrator.run:
|
|
50
|
-
- Acquire lock
|
|
51
|
-
- ImageBuilder.build_and_push
|
|
52
|
-
- Push to registry
|
|
53
|
-
- ServiceDeployer.deploy_app_secret
|
|
54
|
-
- ServiceDeployer.deploy_database
|
|
55
|
-
- ServiceDeployer.deploy_service (for each service)
|
|
56
|
-
- ServiceDeployer.deploy_app_service (for each app)
|
|
57
|
-
- ServiceDeployer.deploy_cloudflared
|
|
58
|
-
- ServiceDeployer.verify_traffic_switchover
|
|
59
|
-
- Cleaner.cleanup_old_images
|
|
60
|
-
- Release lock
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
---
|
|
64
|
-
|
|
65
|
-
## Target Flow (Simplified)
|
|
66
|
-
|
|
67
|
-
```ruby
|
|
68
|
-
# cli/deploy/command.rb
|
|
69
|
-
class Command
|
|
70
|
-
def run
|
|
71
|
-
config = Utils::ConfigLoader.new.load(config_path)
|
|
72
|
-
provider = External::Cloud.for(config)
|
|
73
|
-
log = Utils::Logger.new
|
|
74
|
-
|
|
75
|
-
Steps::ProvisionNetwork.new(config, provider, log).run
|
|
76
|
-
Steps::ProvisionServer.new(config, provider, log).run
|
|
77
|
-
Steps::ProvisionVolume.new(config, provider, log).run
|
|
78
|
-
Steps::SetupK3s.new(config, provider, log).run
|
|
79
|
-
tunnels = Steps::ConfigureTunnel.new(config, log).run
|
|
80
|
-
Steps::BuildImage.new(config, log).run
|
|
81
|
-
Steps::DeployService.new(config, tunnels, log).run
|
|
82
|
-
Steps::CleanupImages.new(config, log).run
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
## DRY Opportunities
|
|
90
|
-
|
|
91
|
-
### 1. Flatten the Hierarchy
|
|
92
|
-
|
|
93
|
-
Current: `DeployService` → `Steps::*` → `Deployer::*`
|
|
94
|
-
Target: `Command` → `Steps::*` directly
|
|
95
|
-
|
|
96
|
-
Kill `Deployer::Orchestrator`. Its logic moves into `Steps::DeployService`.
|
|
97
|
-
|
|
98
|
-
### 2. Remove Thin Wrappers
|
|
99
|
-
|
|
100
|
-
`Steps::ApplicationDeployer` is just:
|
|
101
|
-
|
|
102
|
-
```ruby
|
|
103
|
-
def run
|
|
104
|
-
orchestrator = Deployer::Orchestrator.new(@config, @provider, @log)
|
|
105
|
-
orchestrator.run(@server_ip, @tunnels, @working_dir)
|
|
106
|
-
end
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
Delete it. Put that logic in `Steps::DeployService`.
|
|
110
|
-
|
|
111
|
-
### 3. Consolidate ServiceDeployer
|
|
112
|
-
|
|
113
|
-
`Deployer::ServiceDeployer` (312 lines) does too much:
|
|
114
|
-
|
|
115
|
-
- Deploy app secret
|
|
116
|
-
- Deploy app service
|
|
117
|
-
- Deploy database
|
|
118
|
-
- Deploy generic service
|
|
119
|
-
- Deploy cloudflared
|
|
120
|
-
- Verify traffic
|
|
121
|
-
- Run pre-run commands
|
|
122
|
-
|
|
123
|
-
Split into focused steps or keep as internal helper in `Steps::DeployService`.
|
|
124
|
-
|
|
125
|
-
### 4. Lock Management
|
|
126
|
-
|
|
127
|
-
Deployment lock is in `Orchestrator`. Move to `Command`:
|
|
128
|
-
|
|
129
|
-
```ruby
|
|
130
|
-
def run
|
|
131
|
-
acquire_lock(ssh)
|
|
132
|
-
# ... steps ...
|
|
133
|
-
ensure
|
|
134
|
-
release_lock(ssh)
|
|
135
|
-
end
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## Migration Steps
|
|
141
|
-
|
|
142
|
-
1. Create `lib/nvoi/cli/deploy/command.rb`
|
|
143
|
-
2. Create `lib/nvoi/cli/deploy/steps/` directory
|
|
144
|
-
3. Move `steps/server_provisioner.rb` → `cli/deploy/steps/provision_server.rb`
|
|
145
|
-
4. Move `steps/volume_provisioner.rb` → `cli/deploy/steps/provision_volume.rb`
|
|
146
|
-
5. Merge `steps/k3s_cluster_setup.rb` + `steps/k3s_provisioner.rb` → `cli/deploy/steps/setup_k3s.rb`
|
|
147
|
-
6. Move `steps/tunnel_configurator.rb` → `cli/deploy/steps/configure_tunnel.rb`
|
|
148
|
-
7. Move `deployer/image_builder.rb` → `cli/deploy/steps/build_image.rb`
|
|
149
|
-
8. Merge `deployer/orchestrator.rb` + `deployer/service_deployer.rb` → `cli/deploy/steps/deploy_service.rb`
|
|
150
|
-
9. Move `deployer/cleaner.rb` → `cli/deploy/steps/cleanup_images.rb`
|
|
151
|
-
10. Extract network/firewall from ServerProvisioner → `cli/deploy/steps/provision_network.rb`
|
|
152
|
-
11. Delete old files: `service/deploy.rb`, `deployer/orchestrator.rb`, `steps/application_deployer.rb`
|
|
153
|
-
|
|
154
|
-
---
|
|
155
|
-
|
|
156
|
-
## Estimated Effort
|
|
157
|
-
|
|
158
|
-
**Files involved:**
|
|
159
|
-
|
|
160
|
-
- `service/deploy.rb` (81 lines)
|
|
161
|
-
- `steps/server_provisioner.rb` (44 lines)
|
|
162
|
-
- `steps/volume_provisioner.rb` (155 lines)
|
|
163
|
-
- `steps/k3s_provisioner.rb` (352 lines)
|
|
164
|
-
- `steps/k3s_cluster_setup.rb` (106 lines)
|
|
165
|
-
- `steps/tunnel_configurator.rb` (67 lines)
|
|
166
|
-
- `steps/application_deployer.rb` (27 lines)
|
|
167
|
-
- `deployer/orchestrator.rb` (147 lines)
|
|
168
|
-
- `deployer/infrastructure.rb` (127 lines)
|
|
169
|
-
- `deployer/image_builder.rb` (24 lines)
|
|
170
|
-
- `deployer/service_deployer.rb` (312 lines)
|
|
171
|
-
- `deployer/cleaner.rb` (37 lines)
|
|
172
|
-
|
|
173
|
-
**Total: ~1479 lines to reorganize**
|
|
174
|
-
|
|
175
|
-
- **Files created:** 9 (command.rb + 8 steps)
|
|
176
|
-
- **Files deleted:** 12
|
|
177
|
-
- **DRY savings:** ~150 lines (removed wrappers, flattened hierarchy)
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
# 08 - CLI: Deploy Steps (Details)
|
|
2
|
-
|
|
3
|
-
## Priority: FIFTH (part of deploy)
|
|
4
|
-
|
|
5
|
-
Each step is a focused, single-responsibility class.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Step 1: provision_network.rb (~50 lines)
|
|
10
|
-
|
|
11
|
-
**Source:** Extract from `deployer/infrastructure.rb`
|
|
12
|
-
|
|
13
|
-
**Does:**
|
|
14
|
-
|
|
15
|
-
- Find or create network
|
|
16
|
-
- Find or create firewall
|
|
17
|
-
|
|
18
|
-
```ruby
|
|
19
|
-
# cli/deploy/steps/provision_network.rb
|
|
20
|
-
module Nvoi
|
|
21
|
-
module CLI
|
|
22
|
-
module Deploy
|
|
23
|
-
module Steps
|
|
24
|
-
class ProvisionNetwork
|
|
25
|
-
def initialize(config, provider, log)
|
|
26
|
-
@config = config
|
|
27
|
-
@provider = provider
|
|
28
|
-
@log = log
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def run
|
|
32
|
-
@log.info "Provisioning network: %s", @config.network_name
|
|
33
|
-
network = @provider.find_or_create_network(@config.network_name)
|
|
34
|
-
|
|
35
|
-
@log.info "Provisioning firewall: %s", @config.firewall_name
|
|
36
|
-
firewall = @provider.find_or_create_firewall(@config.firewall_name)
|
|
37
|
-
|
|
38
|
-
{ network: network, firewall: firewall }
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
---
|
|
48
|
-
|
|
49
|
-
## Step 2: provision_server.rb (~80 lines)
|
|
50
|
-
|
|
51
|
-
**Source:** `steps/server_provisioner.rb` + `deployer/infrastructure.rb`
|
|
52
|
-
|
|
53
|
-
**Does:**
|
|
54
|
-
|
|
55
|
-
- Create servers for each group
|
|
56
|
-
- Wait for SSH ready
|
|
57
|
-
- Return main server IP
|
|
58
|
-
|
|
59
|
-
---
|
|
60
|
-
|
|
61
|
-
## Step 3: provision_volume.rb (~150 lines)
|
|
62
|
-
|
|
63
|
-
**Source:** `steps/volume_provisioner.rb`
|
|
64
|
-
|
|
65
|
-
**Does:**
|
|
66
|
-
|
|
67
|
-
- Collect all volumes needed (database, services, app)
|
|
68
|
-
- Create volumes via provider
|
|
69
|
-
- Attach to servers
|
|
70
|
-
- Mount via SSH (format if needed, add to fstab)
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## Step 4: setup_k3s.rb (~400 lines)
|
|
75
|
-
|
|
76
|
-
**Source:** Merge `steps/k3s_provisioner.rb` + `steps/k3s_cluster_setup.rb`
|
|
77
|
-
|
|
78
|
-
**Does:**
|
|
79
|
-
|
|
80
|
-
- Wait for cloud-init
|
|
81
|
-
- Install K3s server on master
|
|
82
|
-
- Setup kubeconfig
|
|
83
|
-
- Setup in-cluster registry
|
|
84
|
-
- Setup NGINX ingress
|
|
85
|
-
- Install K3s agent on workers
|
|
86
|
-
- Label nodes
|
|
87
|
-
- Get cluster token
|
|
88
|
-
|
|
89
|
-
**Note:** This is the largest step. Consider keeping as single file with private methods, or split into:
|
|
90
|
-
|
|
91
|
-
- `setup_k3s/master.rb`
|
|
92
|
-
- `setup_k3s/worker.rb`
|
|
93
|
-
- `setup_k3s/registry.rb`
|
|
94
|
-
|
|
95
|
-
---
|
|
96
|
-
|
|
97
|
-
## Step 5: configure_tunnel.rb (~70 lines)
|
|
98
|
-
|
|
99
|
-
**Source:** `steps/tunnel_configurator.rb`
|
|
100
|
-
|
|
101
|
-
**Does:**
|
|
102
|
-
|
|
103
|
-
- For each app service with domain:
|
|
104
|
-
- Create/find Cloudflare tunnel
|
|
105
|
-
- Configure tunnel ingress
|
|
106
|
-
- Create DNS CNAME record
|
|
107
|
-
- Return array of TunnelInfo
|
|
108
|
-
|
|
109
|
-
---
|
|
110
|
-
|
|
111
|
-
## Step 6: build_image.rb (~100 lines)
|
|
112
|
-
|
|
113
|
-
**Source:** `deployer/image_builder.rb` + parts of `remote/docker_manager.rb`
|
|
114
|
-
|
|
115
|
-
**Does:**
|
|
116
|
-
|
|
117
|
-
- Build Docker image locally
|
|
118
|
-
- Save to tar
|
|
119
|
-
- Rsync to remote server
|
|
120
|
-
- Import into containerd
|
|
121
|
-
- Tag image
|
|
122
|
-
- Push to in-cluster registry
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
## Step 7: deploy_database.rb (~80 lines)
|
|
127
|
-
|
|
128
|
-
**Source:** `steps/database_provisioner.rb` + `deployer/service_deployer.rb#deploy_database`
|
|
129
|
-
|
|
130
|
-
**Does:**
|
|
131
|
-
|
|
132
|
-
- Skip if SQLite (handled by app volumes)
|
|
133
|
-
- Deploy database secret (POSTGRES_USER, etc.)
|
|
134
|
-
- Deploy StatefulSet with hostPath volume
|
|
135
|
-
- Wait for database pod to be Running
|
|
136
|
-
|
|
137
|
-
---
|
|
138
|
-
|
|
139
|
-
## Step 8: deploy_service.rb (~300 lines)
|
|
140
|
-
|
|
141
|
-
**Source:** Merge `deployer/orchestrator.rb` + `deployer/service_deployer.rb`
|
|
142
|
-
|
|
143
|
-
**Does:**
|
|
144
|
-
|
|
145
|
-
- Deploy app secret (env vars)
|
|
146
|
-
- Deploy additional services (redis, etc.)
|
|
147
|
-
- Deploy app services (web, worker)
|
|
148
|
-
- Deploy cloudflared sidecars
|
|
149
|
-
- Verify traffic routing
|
|
150
|
-
- Run pre-run commands (migrations)
|
|
151
|
-
|
|
152
|
-
**Note:** Uses `External::Kubectl` for all manifest operations.
|
|
153
|
-
|
|
154
|
-
---
|
|
155
|
-
|
|
156
|
-
## Step 9: cleanup_images.rb (~40 lines)
|
|
157
|
-
|
|
158
|
-
**Source:** `deployer/cleaner.rb`
|
|
159
|
-
|
|
160
|
-
**Does:**
|
|
161
|
-
|
|
162
|
-
- List all images with prefix
|
|
163
|
-
- Keep newest N images
|
|
164
|
-
- Delete the rest
|
|
165
|
-
|
|
166
|
-
---
|
|
167
|
-
|
|
168
|
-
## Step Dependencies
|
|
169
|
-
|
|
170
|
-
```
|
|
171
|
-
provision_network ─┐
|
|
172
|
-
├─► provision_server ─► provision_volume ─► setup_k3s
|
|
173
|
-
│
|
|
174
|
-
configure_tunnel ──┼─► build_image ─► deploy_service ─► cleanup_images
|
|
175
|
-
│
|
|
176
|
-
└── (parallel where possible)
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
---
|
|
180
|
-
|
|
181
|
-
## Common Patterns Across Steps
|
|
182
|
-
|
|
183
|
-
### Constructor Signature
|
|
184
|
-
|
|
185
|
-
All steps follow:
|
|
186
|
-
|
|
187
|
-
```ruby
|
|
188
|
-
def initialize(config, provider_or_deps, log)
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
### Return Values
|
|
192
|
-
|
|
193
|
-
- Most return `nil` (side effects only)
|
|
194
|
-
- Some return data for next step:
|
|
195
|
-
- `provision_server` → main_server_ip
|
|
196
|
-
- `configure_tunnel` → [TunnelInfo]
|
|
197
|
-
- `provision_network` → { network:, firewall: }
|
|
198
|
-
|
|
199
|
-
### Error Handling
|
|
200
|
-
|
|
201
|
-
Steps raise specific errors. Command catches and logs.
|