nvoi 0.1.5 → 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.
Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/todo/refactor/00-overview.md +171 -0
  3. data/.claude/todo/refactor/01-objects.md +96 -0
  4. data/.claude/todo/refactor/02-utils.md +143 -0
  5. data/.claude/todo/refactor/03-external-cloud.md +164 -0
  6. data/.claude/todo/refactor/04-external-dns.md +104 -0
  7. data/.claude/todo/refactor/05-external.md +133 -0
  8. data/.claude/todo/refactor/06-cli.md +123 -0
  9. data/.claude/todo/refactor/07-cli-deploy-command.md +177 -0
  10. data/.claude/todo/refactor/08-cli-deploy-steps.md +201 -0
  11. data/.claude/todo/refactor/09-cli-delete-command.md +169 -0
  12. data/.claude/todo/refactor/10-cli-exec-command.md +157 -0
  13. data/.claude/todo/refactor/11-cli-credentials-command.md +190 -0
  14. data/.claude/todo/refactor/12-cli-db-command.md +128 -0
  15. data/.claude/todo/refactor/_target.md +79 -0
  16. data/.claude/todo/refactor-execution/00-entrypoint.md +49 -0
  17. data/.claude/todo/refactor-execution/01-objects.md +42 -0
  18. data/.claude/todo/refactor-execution/02-utils.md +41 -0
  19. data/.claude/todo/refactor-execution/03-external-cloud.md +38 -0
  20. data/.claude/todo/refactor-execution/04-external-dns.md +35 -0
  21. data/.claude/todo/refactor-execution/05-external-other.md +46 -0
  22. data/.claude/todo/refactor-execution/06-cli-deploy.md +45 -0
  23. data/.claude/todo/refactor-execution/07-cli-delete.md +43 -0
  24. data/.claude/todo/refactor-execution/08-cli-exec.md +30 -0
  25. data/.claude/todo/refactor-execution/09-cli-credentials.md +34 -0
  26. data/.claude/todo/refactor-execution/10-cli-db.md +31 -0
  27. data/.claude/todo/refactor-execution/11-cli-router.md +44 -0
  28. data/.claude/todo/refactor-execution/12-cleanup.md +120 -0
  29. data/.claude/todo/refactor-execution/_monitoring-strategy.md +126 -0
  30. data/.claude/todo/scaleway.impl.md +644 -0
  31. data/.claude/todo/scaleway.reference.md +520 -0
  32. data/.claude/todos.md +550 -0
  33. data/Gemfile +6 -0
  34. data/Gemfile.lock +46 -5
  35. data/Rakefile +1 -1
  36. data/doc/config-schema.yaml +44 -11
  37. data/examples/golang/deploy.enc +0 -0
  38. data/examples/golang/main.go +18 -0
  39. data/exe/nvoi +3 -1
  40. data/ingest +0 -0
  41. data/lib/nvoi/cli/config/command.rb +219 -0
  42. data/lib/nvoi/cli/credentials/edit/command.rb +384 -0
  43. data/lib/nvoi/cli/credentials/show/command.rb +35 -0
  44. data/lib/nvoi/cli/db/command.rb +308 -0
  45. data/lib/nvoi/cli/delete/command.rb +75 -0
  46. data/lib/nvoi/cli/delete/steps/detach_volumes.rb +98 -0
  47. data/lib/nvoi/cli/delete/steps/teardown_dns.rb +50 -0
  48. data/lib/nvoi/cli/delete/steps/teardown_firewall.rb +46 -0
  49. data/lib/nvoi/cli/delete/steps/teardown_network.rb +30 -0
  50. data/lib/nvoi/cli/delete/steps/teardown_server.rb +50 -0
  51. data/lib/nvoi/cli/delete/steps/teardown_tunnel.rb +44 -0
  52. data/lib/nvoi/cli/delete/steps/teardown_volume.rb +61 -0
  53. data/lib/nvoi/cli/deploy/command.rb +184 -0
  54. data/lib/nvoi/cli/deploy/steps/build_image.rb +27 -0
  55. data/lib/nvoi/cli/deploy/steps/cleanup_images.rb +42 -0
  56. data/lib/nvoi/cli/deploy/steps/configure_tunnel.rb +102 -0
  57. data/lib/nvoi/cli/deploy/steps/deploy_service.rb +399 -0
  58. data/lib/nvoi/cli/deploy/steps/provision_network.rb +44 -0
  59. data/lib/nvoi/cli/deploy/steps/provision_server.rb +143 -0
  60. data/lib/nvoi/cli/deploy/steps/provision_volume.rb +171 -0
  61. data/lib/nvoi/cli/deploy/steps/setup_k3s.rb +490 -0
  62. data/lib/nvoi/cli/exec/command.rb +173 -0
  63. data/lib/nvoi/cli/logs/command.rb +66 -0
  64. data/lib/nvoi/cli/onboard/command.rb +761 -0
  65. data/lib/nvoi/cli/unlock/command.rb +72 -0
  66. data/lib/nvoi/cli.rb +339 -141
  67. data/lib/nvoi/config_api/actions/app.rb +53 -0
  68. data/lib/nvoi/config_api/actions/compute_provider.rb +55 -0
  69. data/lib/nvoi/config_api/actions/database.rb +70 -0
  70. data/lib/nvoi/config_api/actions/domain_provider.rb +40 -0
  71. data/lib/nvoi/config_api/actions/env.rb +32 -0
  72. data/lib/nvoi/config_api/actions/init.rb +67 -0
  73. data/lib/nvoi/config_api/actions/secret.rb +32 -0
  74. data/lib/nvoi/config_api/actions/server.rb +66 -0
  75. data/lib/nvoi/config_api/actions/service.rb +52 -0
  76. data/lib/nvoi/config_api/actions/volume.rb +40 -0
  77. data/lib/nvoi/config_api/base.rb +38 -0
  78. data/lib/nvoi/config_api/result.rb +26 -0
  79. data/lib/nvoi/config_api.rb +93 -0
  80. data/lib/nvoi/errors.rb +68 -50
  81. data/lib/nvoi/external/cloud/aws.rb +450 -0
  82. data/lib/nvoi/external/cloud/base.rb +99 -0
  83. data/lib/nvoi/external/cloud/factory.rb +48 -0
  84. data/lib/nvoi/external/cloud/hetzner.rb +402 -0
  85. data/lib/nvoi/external/cloud/scaleway.rb +559 -0
  86. data/lib/nvoi/external/cloud.rb +15 -0
  87. data/lib/nvoi/external/containerd.rb +86 -0
  88. data/lib/nvoi/external/database/mysql.rb +84 -0
  89. data/lib/nvoi/external/database/postgres.rb +82 -0
  90. data/lib/nvoi/external/database/provider.rb +65 -0
  91. data/lib/nvoi/external/database/sqlite.rb +72 -0
  92. data/lib/nvoi/external/database.rb +22 -0
  93. data/lib/nvoi/external/dns/cloudflare.rb +310 -0
  94. data/lib/nvoi/external/kubectl.rb +65 -0
  95. data/lib/nvoi/external/ssh.rb +106 -0
  96. data/lib/nvoi/objects/config_override.rb +60 -0
  97. data/lib/nvoi/objects/configuration.rb +483 -0
  98. data/lib/nvoi/objects/database.rb +56 -0
  99. data/lib/nvoi/objects/dns.rb +14 -0
  100. data/lib/nvoi/objects/firewall.rb +11 -0
  101. data/lib/nvoi/objects/network.rb +11 -0
  102. data/lib/nvoi/objects/server.rb +14 -0
  103. data/lib/nvoi/objects/service_spec.rb +26 -0
  104. data/lib/nvoi/objects/tunnel.rb +14 -0
  105. data/lib/nvoi/objects/volume.rb +17 -0
  106. data/lib/nvoi/utils/config_loader.rb +172 -0
  107. data/lib/nvoi/utils/constants.rb +61 -0
  108. data/lib/nvoi/{credentials/manager.rb → utils/credential_store.rb} +16 -16
  109. data/lib/nvoi/{credentials → utils}/crypto.rb +8 -5
  110. data/lib/nvoi/{config → utils}/env_resolver.rb +10 -2
  111. data/lib/nvoi/utils/logger.rb +84 -0
  112. data/lib/nvoi/{config/naming.rb → utils/namer.rb} +37 -25
  113. data/lib/nvoi/{deployer → utils}/retry.rb +23 -3
  114. data/lib/nvoi/utils/templates.rb +62 -0
  115. data/lib/nvoi/version.rb +1 -1
  116. data/lib/nvoi.rb +27 -55
  117. data/templates/app-ingress.yaml.erb +3 -1
  118. data/templates/error-backend.yaml.erb +134 -0
  119. metadata +121 -44
  120. data/examples/golang/deploy.yml +0 -54
  121. data/lib/nvoi/cloudflare/client.rb +0 -287
  122. data/lib/nvoi/config/config.rb +0 -248
  123. data/lib/nvoi/config/loader.rb +0 -102
  124. data/lib/nvoi/config/ssh_keys.rb +0 -82
  125. data/lib/nvoi/config/types.rb +0 -274
  126. data/lib/nvoi/constants.rb +0 -59
  127. data/lib/nvoi/credentials/editor.rb +0 -272
  128. data/lib/nvoi/deployer/cleaner.rb +0 -36
  129. data/lib/nvoi/deployer/image_builder.rb +0 -23
  130. data/lib/nvoi/deployer/infrastructure.rb +0 -126
  131. data/lib/nvoi/deployer/orchestrator.rb +0 -146
  132. data/lib/nvoi/deployer/service_deployer.rb +0 -311
  133. data/lib/nvoi/deployer/tunnel_manager.rb +0 -57
  134. data/lib/nvoi/deployer/types.rb +0 -8
  135. data/lib/nvoi/k8s/renderer.rb +0 -44
  136. data/lib/nvoi/k8s/templates.rb +0 -29
  137. data/lib/nvoi/logger.rb +0 -72
  138. data/lib/nvoi/providers/aws.rb +0 -403
  139. data/lib/nvoi/providers/base.rb +0 -111
  140. data/lib/nvoi/providers/hetzner.rb +0 -288
  141. data/lib/nvoi/providers/hetzner_client.rb +0 -170
  142. data/lib/nvoi/remote/docker_manager.rb +0 -203
  143. data/lib/nvoi/remote/ssh_executor.rb +0 -72
  144. data/lib/nvoi/remote/volume_manager.rb +0 -103
  145. data/lib/nvoi/service/delete.rb +0 -234
  146. data/lib/nvoi/service/deploy.rb +0 -80
  147. data/lib/nvoi/service/exec.rb +0 -144
  148. data/lib/nvoi/service/provider.rb +0 -36
  149. data/lib/nvoi/steps/application_deployer.rb +0 -26
  150. data/lib/nvoi/steps/database_provisioner.rb +0 -60
  151. data/lib/nvoi/steps/k3s_cluster_setup.rb +0 -105
  152. data/lib/nvoi/steps/k3s_provisioner.rb +0 -351
  153. data/lib/nvoi/steps/server_provisioner.rb +0 -43
  154. data/lib/nvoi/steps/services_provisioner.rb +0 -29
  155. data/lib/nvoi/steps/tunnel_configurator.rb +0 -66
  156. data/lib/nvoi/steps/volume_provisioner.rb +0 -154
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb54d0e596f64c761d8c3b6f38e6ae0ea7f71186e89744078e6b09e9906bfc1a
4
- data.tar.gz: 22eca4a7d6bc75e7d5f9c0372a261d3c82b88b07f3d82db38828c486249495c7
3
+ metadata.gz: 91c0d6ae24cfc163dd6c00008e5199cfdca4b9864ed6c2b56644b58b90d04ca6
4
+ data.tar.gz: 319109891843c14ae0dfe4c66553bdf06a5bf5a1d009855f97f0c6ffc17bccc6
5
5
  SHA512:
6
- metadata.gz: 2f18b886aa28994c1f6f8caace570f1ad714815f4161ce2bbb48b00be06f7f3f60e7fa14e04d8c1423aa0b55039ca43165bf360c73fc11e8ccb73ea7a7b0a67f
7
- data.tar.gz: 53d83331ca4ecd654fb1d563149f04ffae292474b2cf930cbeefafa00f6bbb746ade07fc1c400409445282718bb50eaf5db26dd0df187ac011313a242b3ca6a0
6
+ metadata.gz: 34b0c1e73b479bb2e10356b27f397a7c34dc9476bd7244490450163736fc7106bd28143ec91bfba1aa27a16c5b1f15d722cfd09d98d7a4155ce7dd752ff30949
7
+ data.tar.gz: 9706c433e608299ac10f71ff257a9d73a0e4f97bab126d3b99d0050474c426dde79fb793204dc55d9369b0654bfba162621106c34d3791fb4e9fb7fed651d8e2
@@ -0,0 +1,171 @@
1
+ # Refactor Overview
2
+
3
+ ## Target Structure
4
+
5
+ ```
6
+ lib/nvoi/
7
+ ├── cli.rb # Thor routing only (~50 lines)
8
+
9
+ ├── cli/
10
+ │ ├── deploy/
11
+ │ │ ├── command.rb
12
+ │ │ └── steps/
13
+ │ │ ├── provision_network.rb
14
+ │ │ ├── provision_server.rb
15
+ │ │ ├── provision_volume.rb
16
+ │ │ ├── setup_k3s.rb
17
+ │ │ ├── configure_tunnel.rb
18
+ │ │ ├── build_image.rb
19
+ │ │ ├── deploy_service.rb
20
+ │ │ └── cleanup_images.rb
21
+ │ │
22
+ │ ├── delete/
23
+ │ │ ├── command.rb
24
+ │ │ └── steps/
25
+ │ │ ├── teardown_tunnel.rb
26
+ │ │ ├── teardown_dns.rb
27
+ │ │ ├── detach_volumes.rb
28
+ │ │ ├── teardown_server.rb
29
+ │ │ ├── teardown_volume.rb
30
+ │ │ ├── teardown_firewall.rb
31
+ │ │ └── teardown_network.rb
32
+ │ │
33
+ │ ├── exec/
34
+ │ │ └── command.rb
35
+ │ │
36
+ │ └── credentials/
37
+ │ ├── edit/
38
+ │ │ └── command.rb
39
+ │ └── show/
40
+ │ └── command.rb
41
+
42
+ ├── external/
43
+ │ ├── cloud/
44
+ │ │ ├── base.rb
45
+ │ │ ├── hetzner.rb
46
+ │ │ ├── aws.rb
47
+ │ │ ├── scaleway.rb
48
+ │ │ └── factory.rb
49
+ │ ├── dns/
50
+ │ │ └── cloudflare.rb
51
+ │ ├── ssh.rb
52
+ │ ├── kubectl.rb
53
+ │ └── containerd.rb
54
+
55
+ ├── objects/
56
+ │ ├── server.rb
57
+ │ ├── network.rb
58
+ │ ├── firewall.rb
59
+ │ ├── volume.rb
60
+ │ ├── tunnel.rb
61
+ │ ├── dns.rb
62
+ │ ├── service_spec.rb
63
+ │ └── config.rb
64
+
65
+ └── utils/
66
+ ├── namer.rb
67
+ ├── env_resolver.rb
68
+ ├── config_loader.rb
69
+ ├── crypto.rb
70
+ ├── templates.rb
71
+ ├── logger.rb
72
+ ├── constants.rb
73
+ ├── errors.rb
74
+ └── retry.rb
75
+ ```
76
+
77
+ ---
78
+
79
+ ## Refactor Order
80
+
81
+ | # | File | Description | Depends On |
82
+ | --- | -------------------------- | ------------------------- | --------------- |
83
+ | 01 | objects.md | Domain entities (Structs) | Nothing |
84
+ | 02 | utils.md | Shared helpers | Objects |
85
+ | 03 | external-cloud.md | Cloud provider adapters | Objects, Utils |
86
+ | 04 | external-dns.md | Cloudflare adapter | Objects, Utils |
87
+ | 05 | external.md | SSH, kubectl, containerd | Objects, Utils |
88
+ | 06 | cli.md | Thor router | Commands |
89
+ | 07 | cli-deploy-command.md | Deploy orchestration | External, Utils |
90
+ | 08 | cli-deploy-steps.md | Deploy step details | External, Utils |
91
+ | 09 | cli-delete-command.md | Delete orchestration | External, Utils |
92
+ | 10 | cli-exec-command.md | Exec command | External, Utils |
93
+ | 11 | cli-credentials-command.md | Credentials commands | Utils |
94
+
95
+ ---
96
+
97
+ ## Line Count Summary
98
+
99
+ ### Current State (~4200 lines)
100
+
101
+ | Category | Lines |
102
+ | --------------------------------- | ----- |
103
+ | Config types + loading | ~550 |
104
+ | Credentials | ~330 |
105
+ | Providers | ~1380 |
106
+ | Cloudflare | ~350 |
107
+ | Remote (SSH, Docker, Volume) | ~430 |
108
+ | K8s (templates, renderer) | ~75 |
109
+ | Steps | ~750 |
110
+ | Deployer | ~580 |
111
+ | Service (CLI entry) | ~460 |
112
+ | Utils (logger, constants, errors) | ~200 |
113
+ | CLI | ~190 |
114
+
115
+ ### Target State (~3800 lines)
116
+
117
+ | Category | Lines | Change |
118
+ | ---------------- | ----- | ---------------- |
119
+ | objects/ | ~400 | consolidated |
120
+ | utils/ | ~600 | merged |
121
+ | external/ | ~1000 | merged providers |
122
+ | cli/deploy/ | ~1200 | flattened |
123
+ | cli/delete/ | ~300 | separated |
124
+ | cli/exec/ | ~150 | simplified |
125
+ | cli/credentials/ | ~100 | simplified |
126
+ | cli.rb | ~50 | routing only |
127
+
128
+ **Net reduction: ~400 lines (10%)**
129
+
130
+ ---
131
+
132
+ ## DRY Wins
133
+
134
+ 1. **Hostname builder** - 3 duplications → 1 in `utils/namer.rb`
135
+ 2. **Provider + Client merge** - 4 files → 3 files
136
+ 3. **TunnelManager absorption** - eliminated wrapper
137
+ 4. **Config loader + SSH key merge** - 2 files → 1 file
138
+ 5. **Crypto + Manager merge** - 2 files → 1 file
139
+ 6. **Templates + Binding merge** - 2 files → 1 file
140
+ 7. **Thin wrapper removal** - `ApplicationDeployer`, `Editor` → gone
141
+ 8. **Struct consolidation** - scattered → `objects/`
142
+
143
+ ---
144
+
145
+ ## Migration Strategy
146
+
147
+ ### Phase 1: Foundation (01-02)
148
+
149
+ Create `objects/` and `utils/`. No breaking changes yet.
150
+ Other code keeps working, just uses new locations.
151
+
152
+ ### Phase 2: External Adapters (03-05)
153
+
154
+ Move and merge providers. Update imports.
155
+ Still backward compatible with old namespaces via aliases if needed.
156
+
157
+ ### Phase 3: Commands (07-11)
158
+
159
+ Create new command structure.
160
+ Old `service/*.rb` files still work during transition.
161
+
162
+ ### Phase 4: Router (06)
163
+
164
+ Switch `cli.rb` to new commands.
165
+ Delete old files.
166
+
167
+ ### Phase 5: Cleanup
168
+
169
+ - Remove empty directories
170
+ - Remove old requires from `lib/nvoi.rb`
171
+ - Update tests
@@ -0,0 +1,96 @@
1
+ # 01 - Objects (Domain Entities)
2
+
3
+ ## Priority: FIRST
4
+
5
+ Objects have no dependencies. Everything else depends on them.
6
+
7
+ ---
8
+
9
+ ## Current State
10
+
11
+ Scattered across multiple files:
12
+
13
+ | Object | Current Location | Lines |
14
+ | ------------------------------------------------------------- | ---------------------- | ----- |
15
+ | `Server`, `Network`, `Firewall`, `Volume` | `providers/base.rb` | ~20 |
16
+ | `ServerCreateOptions`, `VolumeCreateOptions` | `providers/base.rb` | ~5 |
17
+ | `Tunnel`, `Zone`, `DNSRecord` | `cloudflare/client.rb` | ~10 |
18
+ | `TunnelInfo` | `deployer/types.rb` | ~3 |
19
+ | `ServiceSpec` | `config/types.rb` | ~20 |
20
+ | `MountOptions`, `ContainerRunOptions`, `WaitForHealthOptions` | `remote/*.rb` | ~10 |
21
+ | Config types (15+ classes) | `config/types.rb` | ~290 |
22
+
23
+ **Total: ~360 lines scattered across 5 files**
24
+
25
+ ---
26
+
27
+ ## Target Structure
28
+
29
+ ```
30
+ lib/nvoi/objects/
31
+ ├── server.rb # Server, ServerCreateOptions
32
+ ├── network.rb # Network
33
+ ├── firewall.rb # Firewall
34
+ ├── volume.rb # Volume, VolumeCreateOptions, MountOptions
35
+ ├── tunnel.rb # Tunnel, TunnelInfo
36
+ ├── dns.rb # Zone, DNSRecord
37
+ ├── service_spec.rb # ServiceSpec (K8s deployment spec)
38
+ └── config.rb # All config types consolidated
39
+ ```
40
+
41
+ ---
42
+
43
+ ## DRY Opportunities
44
+
45
+ ### 1. Config Types Consolidation
46
+
47
+ Current `config/types.rb` has 15+ classes. Keep them but in `objects/config.rb`.
48
+
49
+ ### 2. Struct vs Class
50
+
51
+ Many objects are simple Structs. Keep them as Structs:
52
+
53
+ ```ruby
54
+ # objects/server.rb
55
+ module Nvoi
56
+ module Objects
57
+ Server = Struct.new(:id, :name, :status, :public_ipv4, keyword_init: true)
58
+ ServerCreateOptions = Struct.new(:name, :type, :image, :location, :user_data, :network_id, :firewall_id, keyword_init: true)
59
+ end
60
+ end
61
+ ```
62
+
63
+ ### 3. TunnelInfo Duplication
64
+
65
+ `TunnelInfo` in `deployer/types.rb` is basically `Tunnel` + extra fields. Merge:
66
+
67
+ ```ruby
68
+ # objects/tunnel.rb
69
+ module Nvoi
70
+ module Objects
71
+ Tunnel = Struct.new(:id, :name, :token, keyword_init: true)
72
+ TunnelInfo = Struct.new(:service_name, :hostname, :tunnel_id, :tunnel_token, :port, keyword_init: true)
73
+ end
74
+ end
75
+ ```
76
+
77
+ ---
78
+
79
+ ## Migration Steps
80
+
81
+ 1. Create `lib/nvoi/objects/` directory
82
+ 2. Extract Structs from `providers/base.rb` → `objects/server.rb`, `objects/network.rb`, etc.
83
+ 3. Extract Structs from `cloudflare/client.rb` → `objects/tunnel.rb`, `objects/dns.rb`
84
+ 4. Move `deployer/types.rb` → `objects/tunnel.rb` (merge TunnelInfo)
85
+ 5. Move `config/types.rb` → `objects/config.rb`
86
+ 6. Extract `MountOptions` from `remote/volume_manager.rb` → `objects/volume.rb`
87
+ 7. Update all requires in `lib/nvoi.rb`
88
+
89
+ ---
90
+
91
+ ## Estimated Effort
92
+
93
+ - **Lines to move:** ~360
94
+ - **Files created:** 8
95
+ - **Files deleted:** 1 (`deployer/types.rb`)
96
+ - **Files modified:** 4 (remove struct definitions)
@@ -0,0 +1,143 @@
1
+ # 02 - Utils (Shared Helpers)
2
+
3
+ ## Priority: SECOND
4
+
5
+ Utils depend only on Objects. Everything else uses them.
6
+
7
+ ---
8
+
9
+ ## Current State
10
+
11
+ | Utility | Current Location | Lines | Purpose |
12
+ | ----------------- | ------------------------ | ----- | ---------------------------------- |
13
+ | `ResourceNamer` | `config/naming.rb` | 197 | Generate consistent resource names |
14
+ | `EnvResolver` | `config/env_resolver.rb` | 64 | Merge env vars from config |
15
+ | `SSHKeyLoader` | `config/ssh_keys.rb` | ~80 | Load SSH keys from config |
16
+ | `ConfigLoader` | `config/loader.rb` | 103 | Load and parse config file |
17
+ | `Crypto` | `credentials/crypto.rb` | ~100 | AES encryption/decryption |
18
+ | `Manager` | `credentials/manager.rb` | ~150 | Credential file management |
19
+ | `Templates` | `k8s/templates.rb` | ~30 | ERB template loading |
20
+ | `TemplateBinding` | `k8s/renderer.rb` | ~20 | ERB binding helper |
21
+ | `Logger` | `logger.rb` | 73 | Colored console output |
22
+ | `Constants` | `constants.rb` | 60 | Magic numbers |
23
+ | `Errors` | `errors.rb` | 70 | Error class hierarchy |
24
+ | `Retry` | `deployer/retry.rb` | ~30 | Retry logic |
25
+
26
+ **Total: ~980 lines across 12 files**
27
+
28
+ ---
29
+
30
+ ## Target Structure
31
+
32
+ ```
33
+ lib/nvoi/utils/
34
+ ├── namer.rb # ResourceNamer
35
+ ├── env_resolver.rb # EnvResolver
36
+ ├── config_loader.rb # ConfigLoader + SSHKeyLoader (merge)
37
+ ├── crypto.rb # Crypto + Manager (merge)
38
+ ├── templates.rb # Templates + TemplateBinding
39
+ ├── logger.rb # Logger
40
+ ├── constants.rb # Constants
41
+ ├── errors.rb # All error classes
42
+ └── retry.rb # Retry logic
43
+ ```
44
+
45
+ ---
46
+
47
+ ## DRY Opportunities
48
+
49
+ ### 1. Merge ConfigLoader + SSHKeyLoader
50
+
51
+ Both deal with config initialization. Single file:
52
+
53
+ ```ruby
54
+ # utils/config_loader.rb
55
+ module Nvoi
56
+ module Utils
57
+ class ConfigLoader
58
+ def load(path)
59
+ # decrypt, parse YAML, load SSH keys, validate
60
+ end
61
+ end
62
+ end
63
+ end
64
+ ```
65
+
66
+ ### 2. Merge Crypto + Manager
67
+
68
+ `Manager` is just `Crypto` + file I/O. Combine:
69
+
70
+ ```ruby
71
+ # utils/crypto.rb
72
+ module Nvoi
73
+ module Utils
74
+ class Crypto
75
+ def encrypt(plaintext) ... end
76
+ def decrypt(ciphertext) ... end
77
+ end
78
+
79
+ class CredentialStore
80
+ def initialize(crypto) ... end
81
+ def read ... end
82
+ def write(content) ... end
83
+ end
84
+ end
85
+ end
86
+ ```
87
+
88
+ ### 3. Merge Templates + TemplateBinding
89
+
90
+ Both in k8s/ but are generic ERB utilities:
91
+
92
+ ```ruby
93
+ # utils/templates.rb
94
+ module Nvoi
95
+ module Utils
96
+ class TemplateRenderer
97
+ def render(name, data) ... end
98
+ end
99
+ end
100
+ end
101
+ ```
102
+
103
+ ### 4. Hostname Builder Duplication
104
+
105
+ `build_hostname(subdomain, domain)` appears in:
106
+
107
+ - `steps/tunnel_configurator.rb`
108
+ - `service/delete.rb`
109
+ - `deployer/service_deployer.rb`
110
+
111
+ Extract to namer:
112
+
113
+ ```ruby
114
+ # utils/namer.rb
115
+ def hostname(subdomain, domain)
116
+ subdomain.nil? || subdomain.empty? || subdomain == "@" ? domain : "#{subdomain}.#{domain}"
117
+ end
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Migration Steps
123
+
124
+ 1. Create `lib/nvoi/utils/` directory
125
+ 2. Move `logger.rb` → `utils/logger.rb` (no changes)
126
+ 3. Move `constants.rb` → `utils/constants.rb` (no changes)
127
+ 4. Move `errors.rb` → `utils/errors.rb` (no changes)
128
+ 5. Merge `config/naming.rb` → `utils/namer.rb` + add `hostname` method
129
+ 6. Merge `config/env_resolver.rb` → `utils/env_resolver.rb`
130
+ 7. Merge `config/loader.rb` + `config/ssh_keys.rb` → `utils/config_loader.rb`
131
+ 8. Merge `credentials/crypto.rb` + `credentials/manager.rb` → `utils/crypto.rb`
132
+ 9. Merge `k8s/templates.rb` + `k8s/renderer.rb` → `utils/templates.rb`
133
+ 10. Move `deployer/retry.rb` → `utils/retry.rb`
134
+ 11. Update all requires
135
+
136
+ ---
137
+
138
+ ## Estimated Effort
139
+
140
+ - **Lines to consolidate:** ~980
141
+ - **Files created:** 9
142
+ - **Files deleted:** 12 (all current locations)
143
+ - **DRY savings:** ~50 lines (hostname duplication, merged classes)
@@ -0,0 +1,164 @@
1
+ # 03 - External: Cloud Providers
2
+
3
+ ## Priority: THIRD
4
+
5
+ External adapters depend on Objects and Utils.
6
+
7
+ ---
8
+
9
+ ## Current State
10
+
11
+ | File | Lines | Purpose |
12
+ | ------------------------------ | ----- | ---------------------------- |
13
+ | `providers/base.rb` | 112 | Abstract interface + Structs |
14
+ | `providers/hetzner.rb` | 289 | Hetzner Cloud implementation |
15
+ | `providers/hetzner_client.rb` | ~200 | Hetzner HTTP client |
16
+ | `providers/aws.rb` | ~250 | AWS implementation |
17
+ | `providers/scaleway.rb` | ~280 | Scaleway implementation |
18
+ | `providers/scaleway_client.rb` | ~200 | Scaleway HTTP client |
19
+ | `service/provider.rb` | ~50 | Provider factory helper |
20
+
21
+ **Total: ~1380 lines across 7 files**
22
+
23
+ ---
24
+
25
+ ## Target Structure
26
+
27
+ ```
28
+ lib/nvoi/external/cloud/
29
+ ├── base.rb # Abstract interface (methods only, no Structs)
30
+ ├── hetzner.rb # Hetzner (merge provider + client)
31
+ ├── aws.rb # AWS
32
+ ├── scaleway.rb # Scaleway (merge provider + client)
33
+ └── factory.rb # Provider initialization helper
34
+ ```
35
+
36
+ ---
37
+
38
+ ## What Each Provider Does
39
+
40
+ All providers implement:
41
+
42
+ ```ruby
43
+ # Network operations
44
+ find_or_create_network(name)
45
+ get_network_by_name(name)
46
+ delete_network(id)
47
+
48
+ # Firewall operations
49
+ find_or_create_firewall(name)
50
+ get_firewall_by_name(name)
51
+ delete_firewall(id)
52
+
53
+ # Server operations
54
+ find_server(name)
55
+ list_servers
56
+ create_server(opts)
57
+ wait_for_server(server_id, max_attempts)
58
+ delete_server(id)
59
+
60
+ # Volume operations
61
+ create_volume(opts)
62
+ get_volume(id)
63
+ get_volume_by_name(name)
64
+ delete_volume(id)
65
+ attach_volume(volume_id, server_id)
66
+ detach_volume(volume_id)
67
+
68
+ # Validation
69
+ validate_instance_type(instance_type)
70
+ validate_region(region)
71
+ validate_credentials
72
+ ```
73
+
74
+ ---
75
+
76
+ ## DRY Opportunities
77
+
78
+ ### 1. Merge Provider + Client
79
+
80
+ Current pattern:
81
+
82
+ - `hetzner.rb` = high-level operations
83
+ - `hetzner_client.rb` = HTTP calls
84
+
85
+ Merge into single file. The "client" is just private methods:
86
+
87
+ ```ruby
88
+ # external/cloud/hetzner.rb
89
+ module Nvoi
90
+ module External
91
+ module Cloud
92
+ class Hetzner < Base
93
+ def find_server(name) ... end # public
94
+
95
+ private
96
+
97
+ def get(path) ... end # HTTP helpers
98
+ def post(path, body) ... end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ ```
104
+
105
+ ### 2. Extract Common HTTP Client
106
+
107
+ All providers do similar HTTP work. Extract base:
108
+
109
+ ```ruby
110
+ # external/cloud/base.rb
111
+ class Base
112
+ private
113
+
114
+ def http_get(url, headers = {}) ... end
115
+ def http_post(url, body, headers = {}) ... end
116
+ def handle_response(response) ... end
117
+ end
118
+ ```
119
+
120
+ ### 3. Provider Factory
121
+
122
+ Current `service/provider.rb` has `ProviderHelper` module. Move to:
123
+
124
+ ```ruby
125
+ # external/cloud/factory.rb
126
+ module Nvoi
127
+ module External
128
+ module Cloud
129
+ def self.for(config)
130
+ case config.provider_name
131
+ when "hetzner" then Hetzner.new(config.hetzner.api_token)
132
+ when "aws" then AWS.new(config.aws)
133
+ when "scaleway" then Scaleway.new(config.scaleway)
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ ```
140
+
141
+ ### 4. Struct Extraction (already in 01-objects.md)
142
+
143
+ Remove `Server`, `Network`, etc. from `base.rb` → use from `objects/`
144
+
145
+ ---
146
+
147
+ ## Migration Steps
148
+
149
+ 1. Create `lib/nvoi/external/cloud/` directory
150
+ 2. Create `base.rb` with interface only (no Structs)
151
+ 3. Merge `hetzner.rb` + `hetzner_client.rb` → `external/cloud/hetzner.rb`
152
+ 4. Move `aws.rb` → `external/cloud/aws.rb`
153
+ 5. Merge `scaleway.rb` + `scaleway_client.rb` → `external/cloud/scaleway.rb`
154
+ 6. Move `service/provider.rb` → `external/cloud/factory.rb`
155
+ 7. Update requires to use `Objects::Server`, etc.
156
+
157
+ ---
158
+
159
+ ## Estimated Effort
160
+
161
+ - **Lines to consolidate:** ~1380
162
+ - **Files created:** 5
163
+ - **Files deleted:** 7
164
+ - **DRY savings:** ~100 lines (merged clients, shared HTTP base)
@@ -0,0 +1,104 @@
1
+ # 04 - External: DNS Provider (Cloudflare)
2
+
3
+ ## Priority: THIRD (parallel with cloud)
4
+
5
+ ---
6
+
7
+ ## Current State
8
+
9
+ | File | Lines | Purpose |
10
+ | ---------------------------- | ----- | -------------------------- |
11
+ | `cloudflare/client.rb` | 288 | Cloudflare API client |
12
+ | `deployer/tunnel_manager.rb` | 58 | Tunnel setup orchestration |
13
+
14
+ **Total: ~346 lines across 2 files**
15
+
16
+ ---
17
+
18
+ ## Target Structure
19
+
20
+ ```
21
+ lib/nvoi/external/dns/
22
+ └── cloudflare.rb # Full Cloudflare client (tunnels + DNS)
23
+ ```
24
+
25
+ ---
26
+
27
+ ## What Cloudflare Client Does
28
+
29
+ ### Tunnel Operations
30
+
31
+ - `create_tunnel(name)` → Tunnel
32
+ - `find_tunnel(name)` → Tunnel | nil
33
+ - `get_tunnel_token(tunnel_id)` → String
34
+ - `update_tunnel_configuration(tunnel_id, hostname, service_url)`
35
+ - `verify_tunnel_configuration(tunnel_id, hostname, service_url, attempts)`
36
+ - `delete_tunnel(tunnel_id)`
37
+
38
+ ### DNS Operations
39
+
40
+ - `find_zone(domain)` → Zone | nil
41
+ - `find_dns_record(zone_id, name, type)` → DNSRecord | nil
42
+ - `create_dns_record(zone_id, name, type, content, proxied:)`
43
+ - `update_dns_record(zone_id, record_id, name, type, content, proxied:)`
44
+ - `create_or_update_dns_record(...)` → convenience method
45
+ - `delete_dns_record(zone_id, record_id)`
46
+
47
+ ---
48
+
49
+ ## DRY Opportunities
50
+
51
+ ### 1. Absorb TunnelManager into Cloudflare Client
52
+
53
+ `TunnelManager.setup_tunnel` does:
54
+
55
+ 1. Find or create tunnel
56
+ 2. Get token
57
+ 3. Configure ingress
58
+ 4. Verify configuration
59
+ 5. Create DNS record
60
+
61
+ This is just a convenience method. Move into client:
62
+
63
+ ```ruby
64
+ # external/dns/cloudflare.rb
65
+ class Cloudflare
66
+ # Existing low-level methods...
67
+
68
+ # High-level convenience
69
+ def setup_tunnel(name, hostname, service_url, domain)
70
+ tunnel = find_tunnel(name) || create_tunnel(name)
71
+ token = tunnel.token || get_tunnel_token(tunnel.id)
72
+ update_tunnel_configuration(tunnel.id, hostname, service_url)
73
+ verify_tunnel_configuration(tunnel.id, hostname, service_url, Constants::TUNNEL_CONFIG_VERIFY_ATTEMPTS)
74
+
75
+ zone = find_zone(domain)
76
+ create_or_update_dns_record(zone.id, hostname, "CNAME", "#{tunnel.id}.cfargotunnel.com")
77
+
78
+ Objects::TunnelInfo.new(tunnel_id: tunnel.id, tunnel_token: token)
79
+ end
80
+ end
81
+ ```
82
+
83
+ ### 2. Remove Struct Definitions
84
+
85
+ Move `Tunnel`, `Zone`, `DNSRecord` to `objects/` (done in 01-objects.md)
86
+
87
+ ---
88
+
89
+ ## Migration Steps
90
+
91
+ 1. Create `lib/nvoi/external/dns/` directory
92
+ 2. Move `cloudflare/client.rb` → `external/dns/cloudflare.rb`
93
+ 3. Merge `deployer/tunnel_manager.rb` into Cloudflare as `setup_tunnel` method
94
+ 4. Remove Struct definitions (use from `objects/`)
95
+ 5. Update namespace from `Cloudflare::Client` to `External::DNS::Cloudflare`
96
+
97
+ ---
98
+
99
+ ## Estimated Effort
100
+
101
+ - **Lines to consolidate:** ~346
102
+ - **Files created:** 1
103
+ - **Files deleted:** 2
104
+ - **DRY savings:** ~20 lines (TunnelManager overhead)