|
| 1 | +# Vertical Slice B: Managed PostgreSQL Service (Compose + Release) |
| 2 | + |
| 3 | +**Issue**: TBD (`469-02` planned child subissue) |
| 4 | +**Parent Epic**: #469 - Add PostgreSQL Support To Tracker Deployer |
| 5 | +**Related**: |
| 6 | + |
| 7 | +- `templates/docker-compose/.env.tera` |
| 8 | +- `templates/docker-compose/docker-compose.yml.tera` |
| 9 | +- `templates/ansible/variables.yml.tera` |
| 10 | +- `templates/ansible/create-mysql-storage.yml` |
| 11 | +- `src/application/services/rendering/docker_compose.rs` |
| 12 | +- `src/application/command_handlers/release/workflow.rs` |
| 13 | +- `src/application/command_handlers/release/steps/mysql.rs` |
| 14 | +- `src/domain/topology/service.rs` |
| 15 | +- `src/domain/environment/state/release_failed.rs` |
| 16 | +- `src/infrastructure/templating/docker_compose/template/wrappers/env/context.rs` |
| 17 | + |
| 18 | +## Overview |
| 19 | + |
| 20 | +Extend the external PostgreSQL support from Slice A to a fully managed local PostgreSQL deployment mode, including compose service definition, topology wiring, and release-time remote storage preparation. |
| 21 | + |
| 22 | +Generated artifacts and workflow should be sufficient to deploy PostgreSQL alongside tracker on the target host. |
| 23 | + |
| 24 | +## Deployable Outcome |
| 25 | + |
| 26 | +After this slice, users can run the full workflow using PostgreSQL with local service provisioning: |
| 27 | + |
| 28 | +- compose file includes `postgresql` service |
| 29 | +- tracker depends on healthy `postgresql` |
| 30 | +- release prepares PostgreSQL storage directory on remote host |
| 31 | +- `show`/status-level service metadata includes PostgreSQL image when applicable |
| 32 | + |
| 33 | +## Goals |
| 34 | + |
| 35 | +- [ ] Render `.env` with PostgreSQL DSN overrides. |
| 36 | +- [ ] Render `docker-compose.yml` with PostgreSQL service and healthcheck. |
| 37 | +- [ ] Add release workflow steps to prepare PostgreSQL storage on remote host. |
| 38 | +- [ ] Add topology/service model support for PostgreSQL. |
| 39 | +- [ ] Preserve current SQLite/MySQL behavior. |
| 40 | + |
| 41 | +## Inside-Out Execution Order |
| 42 | + |
| 43 | +Implement this slice from runtime internals toward user-facing contract: |
| 44 | + |
| 45 | +1. Templating/runtime internals: implement managed PostgreSQL compose/env rendering and validate by running generated artifacts manually. |
| 46 | +2. Automation and workflow: add ansible storage prep and release step wiring for managed PostgreSQL. |
| 47 | +3. Command wiring: ensure command handlers route managed PostgreSQL mode through new workflow branches. |
| 48 | +4. Presentation and errors: expose service/image/status and actionable failure messages. |
| 49 | +5. Schema/contract refinement: confirm user-facing config contract remains aligned with implemented managed mode. |
| 50 | +6. Slice gate: tests plus one managed PostgreSQL end-to-end run before closing. |
| 51 | + |
| 52 | +## Code-Level Implementation Details |
| 53 | + |
| 54 | +### 0. Existing Code Paths To Extend |
| 55 | + |
| 56 | +- Compose env context: `src/infrastructure/templating/docker_compose/template/wrappers/env/context.rs` |
| 57 | +- Compose builder/database context: `src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/context/database.rs`, `.../builder.rs`, `.../mod.rs` |
| 58 | +- Compose rendering service: `src/application/services/rendering/docker_compose.rs` |
| 59 | +- Topology service enum: `src/domain/topology/service.rs` |
| 60 | +- Release workflow + step modules: `src/application/command_handlers/release/workflow.rs`, `src/application/command_handlers/release/steps/mysql.rs` |
| 61 | +- Release state steps: `src/domain/environment/state/release_failed.rs` |
| 62 | +- Ansible templates + static copy list: `templates/ansible/variables.yml.tera`, `templates/ansible/create-mysql-storage.yml`, `src/infrastructure/templating/ansible/template/renderer/project_generator.rs` |
| 63 | +- Show command docker image logic: `src/application/command_handlers/show/handler.rs` |
| 64 | + |
| 65 | +- Correct tracker database driver override |
| 66 | +- Correct PostgreSQL DSN override |
| 67 | +- Optional local PostgreSQL service definition |
| 68 | +- Correct service dependency and healthcheck behavior |
| 69 | + |
| 70 | +## Architecture Requirements |
| 71 | + |
| 72 | +**DDD Layer**: Application + Infrastructure |
| 73 | +**Module Path**: `src/application/services/rendering/`, `src/infrastructure/templating/docker_compose/`, `templates/` |
| 74 | +**Pattern**: Context builder + Tera templating |
| 75 | + |
| 76 | +### Architectural Constraints |
| 77 | + |
| 78 | +- [ ] No secrets hardcoded into template files. |
| 79 | +- [ ] DSN generation escapes reserved URL characters in user/password. |
| 80 | +- [ ] Generated output remains deterministic in field ordering and optional blocks. |
| 81 | + |
| 82 | +## Specifications |
| 83 | + |
| 84 | +### 1. EnvContext + DSN |
| 85 | + |
| 86 | +Extend `EnvContext` with PostgreSQL mode: |
| 87 | + |
| 88 | +- Add optional `postgresql` service config block analogous to `mysql`. |
| 89 | +- Add constructor similar to `new_with_mysql`, e.g. `new_with_postgresql(...)`. |
| 90 | +- Ensure DSN format uses `postgresql://user:pass@host:port/database`. |
| 91 | +- Keep percent-encoding behavior for user/password. |
| 92 | + |
| 93 | +### 2. Docker Compose Context + Templates |
| 94 | + |
| 95 | +Extend builder/context to support PostgreSQL service setup: |
| 96 | + |
| 97 | +- Add PostgreSQL setup config type. |
| 98 | +- Add `with_postgresql(...)` on builder. |
| 99 | +- Add optional `postgresql` service context in final compose context. |
| 100 | + |
| 101 | +### 3. Template Updates |
| 102 | + |
| 103 | +#### `.env.tera` |
| 104 | + |
| 105 | +- Render `TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER='postgresql'` when PostgreSQL selected. |
| 106 | +- Render `TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__PATH` with PostgreSQL DSN. |
| 107 | +- Add PostgreSQL service env vars (container-specific) when local PostgreSQL service is enabled. |
| 108 | + |
| 109 | +#### `docker-compose.yml.tera` |
| 110 | + |
| 111 | +- Add `postgresql` service block with: |
| 112 | + - image |
| 113 | + - environment |
| 114 | + - volume mount path |
| 115 | + - healthcheck |
| 116 | + - database network |
| 117 | +- Make tracker `depends_on` target `postgresql` in PostgreSQL mode. |
| 118 | +- Ensure MySQL and PostgreSQL blocks are mutually exclusive per selected driver. |
| 119 | + |
| 120 | +### 3. Topology + Show Metadata |
| 121 | + |
| 122 | +- Add `Service::PostgreSQL` in `src/domain/topology/service.rs` and include serialization/name/all() updates. |
| 123 | +- Update topology derivations that currently check `Service::MySQL` when behavior should apply to any managed SQL service. |
| 124 | +- Update show command (`show/handler.rs`) to include PostgreSQL image when tracker uses managed PostgreSQL. |
| 125 | + |
| 126 | +### 4. Release + Ansible Storage Preparation |
| 127 | + |
| 128 | +- Add `postgresql_enabled` in `templates/ansible/variables.yml.tera`. |
| 129 | +- Add `templates/ansible/create-postgresql-storage.yml` (parallel to mysql playbook). |
| 130 | +- Register new playbook in static template copy list (`project_generator.rs`). |
| 131 | +- Add release step module for PostgreSQL storage prep (parallel to `steps/mysql.rs`). |
| 132 | +- Add state step and error mapping for PostgreSQL storage failures. |
| 133 | + |
| 134 | +### 5. Rendering Service Wiring |
| 135 | + |
| 136 | +Update `DockerComposeTemplateRenderingService` decision tree to handle 3-way DB selection: |
| 137 | + |
| 138 | +- sqlite -> existing path |
| 139 | +- mysql -> existing path |
| 140 | +- postgresql -> new path (managed mode) |
| 141 | + |
| 142 | +## Implementation Plan |
| 143 | + |
| 144 | +### Phase 1: Compose Context + Templates (1 day) |
| 145 | + |
| 146 | +- [ ] Task 1.1: Extend env context and compose context builders for managed PostgreSQL. |
| 147 | +- [ ] Task 1.2: Update `.env.tera` and `docker-compose.yml.tera` blocks. |
| 148 | +- [ ] Task 1.3: Add rendering tests for managed PostgreSQL snapshots. |
| 149 | + |
| 150 | +### Phase 2: Release + Ansible (1 day) |
| 151 | + |
| 152 | +- [ ] Task 2.1: Add `create-postgresql-storage.yml` + ansible variable wiring. |
| 153 | +- [ ] Task 2.2: Add PostgreSQL release step + workflow integration + error mapping. |
| 154 | +- [ ] Task 2.3: Add release step unit tests (skip/execute/failure mapping). |
| 155 | + |
| 156 | +### Phase 3: Command + Topology + Show (0.5 day) |
| 157 | + |
| 158 | +- [ ] Task 3.1: Add `Service::PostgreSQL` and update topology-related tests. |
| 159 | +- [ ] Task 3.2: Update show command docker image reporting. |
| 160 | +- [ ] Task 3.3: Verify command-handler routing for managed PostgreSQL path. |
| 161 | + |
| 162 | +### Phase 4: Presentation + Contract Check + E2E Gate (0.5 day) |
| 163 | + |
| 164 | +- [ ] Task 4.1: Validate PostgreSQL-specific release failure messages are actionable. |
| 165 | +- [ ] Task 4.2: Confirm schema/docs contract is still aligned with managed mode behavior. |
| 166 | +- [ ] Task 4.3: Run one managed PostgreSQL end-to-end scenario. |
| 167 | + |
| 168 | +## Acceptance Criteria |
| 169 | + |
| 170 | +- [ ] Managed PostgreSQL deployments render valid `.env` and compose files with healthy dependency checks. |
| 171 | +- [ ] Release workflow creates required PostgreSQL storage directories on remote host. |
| 172 | +- [ ] Topology and show output include PostgreSQL where appropriate. |
| 173 | +- [ ] SQLite/MySQL snapshots and release behavior remain unchanged unless intentionally updated. |
| 174 | +- [ ] New/updated tests for templates, topology, and release step are passing. |
0 commit comments