|
| 1 | +# Tier 5 Config Screen Implementation Plan |
| 2 | + |
| 3 | +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. |
| 4 | +
|
| 5 | +**Goal:** Add an external database toggle with conditional fields and a generated embedded DB password to the KOTS/EC3 config screen, satisfying Tier 5 rubric items 5.0 and 5.2. |
| 6 | + |
| 7 | +**Architecture:** Two files change. `kots-config.yaml` gains a `database` group with a `db_type` dropdown (embedded/external), conditional external connection fields, and a hidden `db_password_generated` item that uses `RandomString 32` to produce a stable password on first install. `helmchart.yaml` wires all new config values to the chart — `postgresql.enabled`, `postgresql.auth.password`, and the full `externalDatabase.*` block. |
| 8 | + |
| 9 | +**Tech Stack:** KOTS Config custom resource (kots.io/v1beta1), KOTS HelmChart custom resource (kots.io/v1beta2), Helm chart `_helpers.tpl` (already handles embedded/external switch — no changes needed) |
| 10 | + |
| 11 | +--- |
| 12 | + |
| 13 | +## File Map |
| 14 | + |
| 15 | +| File | Change | |
| 16 | +|------|--------| |
| 17 | +| `kots-config.yaml` | Add `database` group with 7 new items | |
| 18 | +| `helmchart.yaml` | Add `postgresql.enabled`, `postgresql.auth.password`, and `externalDatabase.*` overrides | |
| 19 | + |
| 20 | +--- |
| 21 | + |
| 22 | +## Task 1: Add the database config group to kots-config.yaml |
| 23 | + |
| 24 | +**Files:** |
| 25 | +- Modify: `kots-config.yaml` |
| 26 | + |
| 27 | +- [ ] **Step 1: Verify current file contents** |
| 28 | + |
| 29 | +```bash |
| 30 | +cat kots-config.yaml |
| 31 | +``` |
| 32 | + |
| 33 | +Expected: 33 lines, two groups (`gameshelf` and `branding`), no `database` group. |
| 34 | + |
| 35 | +- [ ] **Step 2: Add the database group** |
| 36 | + |
| 37 | +In `kots-config.yaml`, add a new group after the closing of the `gameshelf` group (after line 19, before the `branding` group). The full file should look like this: |
| 38 | + |
| 39 | +```yaml |
| 40 | +apiVersion: kots.io/v1beta1 |
| 41 | +kind: Config |
| 42 | +metadata: |
| 43 | + name: gameshelf |
| 44 | +spec: |
| 45 | + groups: |
| 46 | + - name: gameshelf |
| 47 | + title: GameShelf |
| 48 | + items: |
| 49 | + - name: admin_secret |
| 50 | + title: Admin Password |
| 51 | + type: password |
| 52 | + required: true |
| 53 | + help_text: "Password for the GameShelf admin panel." |
| 54 | + - name: site_name |
| 55 | + title: Site Name |
| 56 | + type: text |
| 57 | + default: "GameShelf" |
| 58 | + help_text: "The name displayed in the browser title and header." |
| 59 | + - name: database |
| 60 | + title: Database |
| 61 | + items: |
| 62 | + - name: db_type |
| 63 | + title: Database |
| 64 | + type: select_one |
| 65 | + default: embedded |
| 66 | + help_text: "Choose Embedded to use the built-in PostgreSQL instance, or External to connect to your own PostgreSQL server." |
| 67 | + items: |
| 68 | + - name: embedded |
| 69 | + title: Embedded (recommended) |
| 70 | + - name: external |
| 71 | + title: External PostgreSQL |
| 72 | + - name: db_password_generated |
| 73 | + title: Embedded Database Password |
| 74 | + type: password |
| 75 | + hidden: true |
| 76 | + default: '{{repl RandomString 32}}' |
| 77 | + help_text: "Auto-generated password for the embedded PostgreSQL instance. Generated once at install and preserved across upgrades." |
| 78 | + - name: db_host |
| 79 | + title: Database Host |
| 80 | + type: text |
| 81 | + when: '{{repl ConfigOption "db_type" | eq "external"}}' |
| 82 | + required: true |
| 83 | + help_text: "Hostname or IP address of your PostgreSQL server (e.g. db.example.com)." |
| 84 | + - name: db_port |
| 85 | + title: Database Port |
| 86 | + type: text |
| 87 | + default: "5432" |
| 88 | + when: '{{repl ConfigOption "db_type" | eq "external"}}' |
| 89 | + help_text: "Port your PostgreSQL server listens on. Default is 5432." |
| 90 | + validation: |
| 91 | + regex: |
| 92 | + pattern: '^\d+$' |
| 93 | + message: "Must be a numeric port number." |
| 94 | + - name: db_name |
| 95 | + title: Database Name |
| 96 | + type: text |
| 97 | + default: "gameshelf" |
| 98 | + when: '{{repl ConfigOption "db_type" | eq "external"}}' |
| 99 | + help_text: "Name of the PostgreSQL database to connect to." |
| 100 | + - name: db_user |
| 101 | + title: Database Username |
| 102 | + type: text |
| 103 | + default: "gameshelf" |
| 104 | + when: '{{repl ConfigOption "db_type" | eq "external"}}' |
| 105 | + help_text: "Username for authenticating with the PostgreSQL database." |
| 106 | + - name: db_password |
| 107 | + title: Database Password |
| 108 | + type: password |
| 109 | + when: '{{repl ConfigOption "db_type" | eq "external"}}' |
| 110 | + required: true |
| 111 | + help_text: "Password for authenticating with the PostgreSQL database." |
| 112 | + - name: branding |
| 113 | + title: Branding |
| 114 | + when: '{{repl LicenseFieldValue "custom_branding_enabled" | eq "true"}}' |
| 115 | + items: |
| 116 | + - name: site_color |
| 117 | + title: Primary Color |
| 118 | + type: text |
| 119 | + default: "#3B82F6" |
| 120 | + help_text: "Primary color for the GameShelf UI (hex format, e.g. #3B82F6). Requires the Custom Branding license entitlement." |
| 121 | + validation: |
| 122 | + regex: |
| 123 | + pattern: '^#[0-9A-Fa-f]{6}$' |
| 124 | + message: "Must be a valid hex color code (e.g. #3B82F6)" |
| 125 | +``` |
| 126 | +
|
| 127 | +- [ ] **Step 3: Verify file looks correct** |
| 128 | +
|
| 129 | +```bash |
| 130 | +cat kots-config.yaml |
| 131 | +``` |
| 132 | + |
| 133 | +Expected: ~90 lines, three groups (`gameshelf`, `database`, `branding`). |
| 134 | + |
| 135 | +- [ ] **Step 4: Commit** |
| 136 | + |
| 137 | +```bash |
| 138 | +git add kots-config.yaml |
| 139 | +git commit -m "feat: add external DB toggle and generated password to config screen" |
| 140 | +``` |
| 141 | + |
| 142 | +--- |
| 143 | + |
| 144 | +## Task 2: Wire database config values into helmchart.yaml |
| 145 | + |
| 146 | +**Files:** |
| 147 | +- Modify: `helmchart.yaml` |
| 148 | + |
| 149 | +- [ ] **Step 1: Verify current helmchart.yaml values section** |
| 150 | + |
| 151 | +```bash |
| 152 | +cat helmchart.yaml |
| 153 | +``` |
| 154 | + |
| 155 | +Expected: `values` section has `adminSecret`, `siteName`, `siteColor`, `customBrandingEnabled`, `imageProxy`, `service`, `image`, `replicated`, `postgresql.image`, `redis.image`. No `postgresql.enabled`, no `postgresql.auth`, no `externalDatabase` block. |
| 156 | + |
| 157 | +- [ ] **Step 2: Add database wiring to the values section** |
| 158 | + |
| 159 | +In `helmchart.yaml`, replace the existing `postgresql:` block: |
| 160 | + |
| 161 | +```yaml |
| 162 | + postgresql: |
| 163 | + image: |
| 164 | + registry: 'repl{{ ReplicatedImageRegistry "index.docker.io" }}' |
| 165 | +``` |
| 166 | +
|
| 167 | +With: |
| 168 | +
|
| 169 | +```yaml |
| 170 | + postgresql: |
| 171 | + enabled: 'repl{{ ConfigOption "db_type" | eq "embedded" }}' |
| 172 | + auth: |
| 173 | + password: 'repl{{ ConfigOption "db_password_generated" }}' |
| 174 | + image: |
| 175 | + registry: 'repl{{ ReplicatedImageRegistry "index.docker.io" }}' |
| 176 | + externalDatabase: |
| 177 | + host: 'repl{{ ConfigOption "db_host" }}' |
| 178 | + port: 'repl{{ ConfigOption "db_port" }}' |
| 179 | + database: 'repl{{ ConfigOption "db_name" }}' |
| 180 | + username: 'repl{{ ConfigOption "db_user" }}' |
| 181 | + password: 'repl{{ ConfigOption "db_password" }}' |
| 182 | +``` |
| 183 | +
|
| 184 | +- [ ] **Step 3: Verify the full helmchart.yaml looks correct** |
| 185 | +
|
| 186 | +```bash |
| 187 | +cat helmchart.yaml |
| 188 | +``` |
| 189 | + |
| 190 | +Expected full file: |
| 191 | + |
| 192 | +```yaml |
| 193 | +apiVersion: kots.io/v1beta2 |
| 194 | +kind: HelmChart |
| 195 | +metadata: |
| 196 | + name: gameshelf |
| 197 | +spec: |
| 198 | + chart: |
| 199 | + name: gameshelf |
| 200 | + chartVersion: "0.0.0" |
| 201 | + values: |
| 202 | + adminSecret: repl{{ ConfigOption `admin_secret`}} |
| 203 | + siteName: repl{{ ConfigOption `site_name`}} |
| 204 | + siteColor: repl{{ ConfigOption `site_color`}} |
| 205 | + customBrandingEnabled: repl{{ LicenseFieldValue `custom_branding_enabled` }} |
| 206 | + imageProxy: |
| 207 | + host: "" |
| 208 | + service: |
| 209 | + type: NodePort |
| 210 | + nodePort: 30081 |
| 211 | + image: |
| 212 | + registry: 'repl{{ ReplicatedImageRegistry "ghcr.io" }}' |
| 213 | + repository: 'aa-replicated/gameshelf' |
| 214 | + pullPolicy: IfNotPresent |
| 215 | + replicated: |
| 216 | + image: |
| 217 | + registry: 'repl{{ ReplicatedImageRegistry "proxy.replicated.com" true }}' |
| 218 | + postgresql: |
| 219 | + enabled: 'repl{{ ConfigOption "db_type" | eq "embedded" }}' |
| 220 | + auth: |
| 221 | + password: 'repl{{ ConfigOption "db_password_generated" }}' |
| 222 | + image: |
| 223 | + registry: 'repl{{ ReplicatedImageRegistry "index.docker.io" }}' |
| 224 | + externalDatabase: |
| 225 | + host: 'repl{{ ConfigOption "db_host" }}' |
| 226 | + port: 'repl{{ ConfigOption "db_port" }}' |
| 227 | + database: 'repl{{ ConfigOption "db_name" }}' |
| 228 | + username: 'repl{{ ConfigOption "db_user" }}' |
| 229 | + password: 'repl{{ ConfigOption "db_password" }}' |
| 230 | + redis: |
| 231 | + image: |
| 232 | + registry: 'repl{{ ReplicatedImageRegistry "index.docker.io" }}' |
| 233 | + builder: |
| 234 | + image: |
| 235 | + registry: "ghcr.io" |
| 236 | + repository: "aa-replicated/gameshelf" |
| 237 | + replicated: |
| 238 | + image: |
| 239 | + registry: "proxy.replicated.com" |
| 240 | + repository: "library/replicated-sdk-image" |
| 241 | + postgresql: |
| 242 | + image: |
| 243 | + registry: "docker.io" |
| 244 | + redis: |
| 245 | + image: |
| 246 | + registry: "docker.io" |
| 247 | +``` |
| 248 | +
|
| 249 | +- [ ] **Step 4: Run helm lint to confirm no structural issues** |
| 250 | +
|
| 251 | +```bash |
| 252 | +helm lint chart/gameshelf/ |
| 253 | +``` |
| 254 | + |
| 255 | +Expected: `1 chart(s) linted, 0 chart(s) failed` |
| 256 | + |
| 257 | +- [ ] **Step 5: Commit and push** |
| 258 | + |
| 259 | +```bash |
| 260 | +git add helmchart.yaml |
| 261 | +git commit -m "feat: wire external DB toggle and generated password into helmchart.yaml" |
| 262 | +git push |
| 263 | +``` |
| 264 | + |
| 265 | +--- |
| 266 | + |
| 267 | +## Task 3: Verify and deploy |
| 268 | + |
| 269 | +- [ ] **Step 1: Promote a new release in the Vendor Portal** |
| 270 | + |
| 271 | +Push triggers CI which creates a new release. Promote it to the Unstable channel. |
| 272 | + |
| 273 | +- [ ] **Step 2: Install with embedded DB (default)** |
| 274 | + |
| 275 | +During the EC3 install config screen: |
| 276 | +- `Database` dropdown: leave as `Embedded (recommended)` |
| 277 | +- Complete install |
| 278 | + |
| 279 | +Expected: |
| 280 | +- `gameshelf-postgresql-0` pod is Running |
| 281 | +- App connects successfully |
| 282 | + |
| 283 | +Verify: |
| 284 | +```bash |
| 285 | +sudo k0s kubectl get pods -A | grep postgresql |
| 286 | +``` |
| 287 | + |
| 288 | +Expected: `gameshelf-postgresql-0 1/1 Running` |
| 289 | + |
| 290 | +- [ ] **Step 3: Verify generated password is stable across upgrade** |
| 291 | + |
| 292 | +After install, trigger an upgrade to a newer release (or re-deploy the same release). App should still connect to the DB without any reconfiguration. No `CrashLoopBackOff` or DB connection errors in the gameshelf pod logs: |
| 293 | + |
| 294 | +```bash |
| 295 | +sudo k0s kubectl logs -l app.kubernetes.io/name=gameshelf -n <namespace> | grep -i "database\|connect\|error" | tail -20 |
| 296 | +``` |
| 297 | + |
| 298 | +Expected: No connection errors. |
| 299 | + |
| 300 | +- [ ] **Step 4: Install with external DB** |
| 301 | + |
| 302 | +During the EC3 install config screen: |
| 303 | +- `Database` dropdown: select `External PostgreSQL` |
| 304 | +- Fill in host, port, name, user, password for a real external PostgreSQL instance |
| 305 | + |
| 306 | +Expected: |
| 307 | +- No `gameshelf-postgresql-0` pod |
| 308 | +- App connects to the external instance |
| 309 | + |
| 310 | +Verify: |
| 311 | +```bash |
| 312 | +sudo k0s kubectl get pods -A | grep postgresql |
| 313 | +``` |
| 314 | + |
| 315 | +Expected: no postgresql pod in the output. |
0 commit comments