kitsune-kit 0.5.0 → 0.6.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/CHANGELOG.md +27 -1
- data/README.md +8 -3
- data/docs/architecture/decisions/0007-compose-customization.md +30 -0
- data/docs/architecture.md +5 -1
- data/docs/commands.md +14 -0
- data/docs/configuration.md +16 -0
- data/docs/services/compose.md +164 -0
- data/lib/kitsune/kit/cli.rb +83 -6
- data/lib/kitsune/kit/configuration.rb +96 -12
- data/lib/kitsune/kit/operations/ensure_service.rb +33 -16
- data/lib/kitsune/kit/operations/service_files.rb +14 -10
- data/lib/kitsune/kit/scripts/docker.sh +11 -6
- data/lib/kitsune/kit/scripts/firewall.sh +8 -3
- data/lib/kitsune/kit/scripts/metrics.sh +6 -1
- data/lib/kitsune/kit/scripts/unattended.sh +8 -3
- data/lib/kitsune/kit/service_compose.rb +205 -5
- data/lib/kitsune/kit/version.rb +1 -1
- data/lib/kitsune/kit/workflows/eject_compose.rb +82 -0
- data/lib/kitsune/kit/workflows/initialize_project.rb +8 -0
- data/lib/kitsune/kit.rb +1 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '08f2d244f1d6869964e8722fe07a91e1fd84c8786d56faf3a6d92e5ffedeca03'
|
|
4
|
+
data.tar.gz: f8f5090aa7cb0f7bf3782c35564339dbf5891b675a96e538d99ab57a295334b9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8a09c75539d4ec1cd52bd18aaac184a26491ea770044ef4e0a39ee9dd8057851c3a60944fb51b64173f23c24f36f4f79450d804b89e1ab211861089ef2b853a1
|
|
7
|
+
data.tar.gz: fceed8d60ab1d391ac09e9346ea383722e2407870a913a548eb73e782c29d704b19590859309be147a9f5ff794edd5e45a4a0bba007fdea5b7ae699eb09586a3
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,31 @@ and [Semantic Versioning](https://semver.org/); minor releases may change public
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.6.0] - 2026-08-14
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Generated, overlay and fully custom Docker Compose modes for managed PostgreSQL and Redis.
|
|
13
|
+
- Local `compose show`, `validate`, `diff` and safe `eject` service commands.
|
|
14
|
+
- Project-bound file validation, inline-secret detection and explicit review metadata for unsafe Compose options.
|
|
15
|
+
- Multi-file upload, fingerprinting, state tracking, recovery and rollback for Compose customizations.
|
|
16
|
+
- Unit coverage for each customization mode, security boundary, ejection and remote multi-file execution.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Initialized projects now declare the generated Compose mode explicitly.
|
|
21
|
+
- Service state records the Compose mode, ordered file set and content fingerprint.
|
|
22
|
+
- Kitsune Kit 0.6.0 replaces the fixed service blueprints directly; no legacy blueprint compatibility layer remains.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Keep local Compose inspection, validation, diff and ejection independent from provider and service credentials.
|
|
27
|
+
|
|
28
|
+
### Security
|
|
29
|
+
|
|
30
|
+
- Customizations reject host namespace/device access, elevated capabilities, unmanaged ports, bind mounts, remote
|
|
31
|
+
builds, unmanaged env files and inline secrets unless the documented explicit unsafe escape applies.
|
|
32
|
+
|
|
8
33
|
## [0.5.0] - 2026-08-14
|
|
9
34
|
|
|
10
35
|
### Added
|
|
@@ -111,7 +136,8 @@ and [Semantic Versioning](https://semver.org/); minor releases may change public
|
|
|
111
136
|
- First public preview.
|
|
112
137
|
- Bootstrap commands for DigitalOcean, Docker and PostgreSQL.
|
|
113
138
|
|
|
114
|
-
[Unreleased]: https://github.com/omarhrra/kitsune-kit/compare/v0.
|
|
139
|
+
[Unreleased]: https://github.com/omarhrra/kitsune-kit/compare/v0.6.0...HEAD
|
|
140
|
+
[0.6.0]: https://github.com/omarhrra/kitsune-kit/compare/v0.5.0...v0.6.0
|
|
115
141
|
[0.5.0]: https://github.com/omarhrra/kitsune-kit/compare/v0.4.1...v0.5.0
|
|
116
142
|
[0.4.1]: https://github.com/omarhrra/kitsune-kit/compare/v0.4.0...v0.4.1
|
|
117
143
|
[0.4.0]: https://github.com/omarhrra/kitsune-kit/compare/v0.3.0...v0.4.0
|
data/README.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Kitsune Kit prepares Ubuntu servers for Docker and Kamal deployments through a predictable, inspectable CLI.
|
|
4
4
|
|
|
5
|
-
> Status: `0.
|
|
5
|
+
> Status: `0.6.0` (pre-1.0). DigitalOcean and Ubuntu 22.04/24.04 LTS are supported. The command, configuration and state schemas may still change before 1.0.
|
|
6
6
|
|
|
7
7
|
## What it does
|
|
8
8
|
|
|
9
9
|
- Provisions an explicitly tagged DigitalOcean Droplet.
|
|
10
10
|
- Configures a deploy user, verified SSH policy, UFW, swap and unattended security updates.
|
|
11
11
|
- Installs Docker Engine and Docker Compose from Docker's official Ubuntu repository.
|
|
12
|
-
- Optionally installs private PostgreSQL/Redis services or
|
|
12
|
+
- Optionally installs private PostgreSQL/Redis services, with generated, overlay or fully custom Docker Compose.
|
|
13
13
|
- Creates exact DNS records without adopting unrelated resources.
|
|
14
14
|
- Shows a plan before changing infrastructure and records managed state for resume and rollback.
|
|
15
15
|
- Offers both a conventional CLI and an optional full-screen TUI over the same workflows.
|
|
@@ -32,7 +32,7 @@ gem install kitsune-kit
|
|
|
32
32
|
Or add it to a project:
|
|
33
33
|
|
|
34
34
|
```ruby
|
|
35
|
-
gem "kitsune-kit", "~> 0.
|
|
35
|
+
gem "kitsune-kit", "~> 0.6.0"
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
## Safe quick start
|
|
@@ -72,6 +72,7 @@ See [Getting started](docs/getting-started.md) for the complete first-run proced
|
|
|
72
72
|
| `kit rollback` | Restore managed configuration; preserve server and service data | Yes |
|
|
73
73
|
| `kit server ACTION` | Show, create, configure, connect to or destroy the server | Depends |
|
|
74
74
|
| `kit service TYPE ACTION` | Manage PostgreSQL or Redis | Depends |
|
|
75
|
+
| `kit service TYPE compose ACTION` | Show, validate, diff or eject Compose | Local |
|
|
75
76
|
| `kit dns ACTION` | List, plan, apply or remove configured records | Depends |
|
|
76
77
|
| `kit docker ACTION` | Inspect, install or uninstall Docker | Depends |
|
|
77
78
|
| `kit env ACTION [NAME]` | List, read or select environments | Local |
|
|
@@ -118,6 +119,9 @@ kit service postgres install
|
|
|
118
119
|
|
|
119
120
|
See [PostgreSQL](docs/services/postgres.md) and [Redis](docs/services/redis.md).
|
|
120
121
|
|
|
122
|
+
Compose remains generated and secure by default. Use an overlay for ordinary Docker options or eject a complete,
|
|
123
|
+
editable file when the generated model is no longer sufficient. See [Compose customization](docs/services/compose.md).
|
|
124
|
+
|
|
121
125
|
## CLI and optional TUI
|
|
122
126
|
|
|
123
127
|
Kitsune Kit is fully usable without the TUI. Every infrastructure action available in the full-screen interface invokes the same domain workflow and has a conventional command equivalent. Scripts and CI should use subcommands, `--no-input`, and optionally `--format json`.
|
|
@@ -151,6 +155,7 @@ Docker-backed integration tests and credential-gated DigitalOcean E2E tests are
|
|
|
151
155
|
- [Getting started](docs/getting-started.md)
|
|
152
156
|
- [Configuration](docs/configuration.md)
|
|
153
157
|
- [Command reference](docs/commands.md)
|
|
158
|
+
- [Compose customization](docs/services/compose.md)
|
|
154
159
|
- [Security](docs/security.md)
|
|
155
160
|
- [Manual security audit](docs/security-audit.md)
|
|
156
161
|
- [Troubleshooting](docs/troubleshooting.md)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# ADR 0007: Managed Compose customization boundary
|
|
2
|
+
|
|
3
|
+
- Status: accepted
|
|
4
|
+
- Date: 2026-08-14
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
The fixed PostgreSQL and Redis generators are safe and convenient, but cannot express every operational Docker
|
|
9
|
+
setting. Making arbitrary YAML fragments part of the core configuration would duplicate the Compose schema and
|
|
10
|
+
produce a weaker, less familiar interface.
|
|
11
|
+
|
|
12
|
+
## Decision
|
|
13
|
+
|
|
14
|
+
Managed services support three explicit modes: deterministic generated output, a generated base plus one project
|
|
15
|
+
overlay, and one complete project-owned custom document. All modes pass through a single renderer, fingerprint,
|
|
16
|
+
remote validation, state and recovery path.
|
|
17
|
+
|
|
18
|
+
Customization files remain within the project and are subject to size, type, secret and host-boundary validation.
|
|
19
|
+
Security-sensitive Compose settings fail closed unless `allow_unsafe` records an explicit operator decision.
|
|
20
|
+
Inline secrets always fail. The `eject` command creates a complete custom starting point and a configuration
|
|
21
|
+
backup instead of preserving compatibility with the earlier fixed blueprint internals.
|
|
22
|
+
|
|
23
|
+
## Consequences
|
|
24
|
+
|
|
25
|
+
- Users can express ordinary Compose settings without Kitsune Kit reimplementing the Compose schema.
|
|
26
|
+
- Generated mode remains the default and safest supported path.
|
|
27
|
+
- Custom mode transfers responsibility for image, health, network and volume semantics to the project.
|
|
28
|
+
- Kitsune Kit tracks and restores the ordered document set, but does not claim ownership of arbitrary host
|
|
29
|
+
resources introduced through the unsafe escape hatch.
|
|
30
|
+
- Docker's server-side `compose config` remains authoritative for Compose-version compatibility.
|
data/docs/architecture.md
CHANGED
|
@@ -68,7 +68,10 @@ Safety-critical SSH/firewall operations execute inside one preserved authenticat
|
|
|
68
68
|
|
|
69
69
|
## Services
|
|
70
70
|
|
|
71
|
-
`EnsureService` composes smaller collaborators for generated Compose YAML, file
|
|
71
|
+
`EnsureService` composes smaller collaborators for generated, overlay or custom Compose YAML, file
|
|
72
|
+
transactions/backups, firewall reconciliation, data backup and state transitions. The ordered Compose file set is
|
|
73
|
+
fingerprinted and persisted so recovery can restart the prior definition. Data lifecycle is deliberately separate
|
|
74
|
+
from container/config lifecycle.
|
|
72
75
|
|
|
73
76
|
An enabled service in `external` mode is configuration metadata, not an operation. It is visible through the
|
|
74
77
|
CLI but excluded from VPS plans, Docker, firewall, doctor port checks and destructive lifecycle methods.
|
|
@@ -87,3 +90,4 @@ Detailed accepted decisions are in [architecture/decisions](architecture/decisio
|
|
|
87
90
|
- supported platforms;
|
|
88
91
|
- operation semantics;
|
|
89
92
|
- optional TUI backend.
|
|
93
|
+
- managed Compose customization boundary.
|
data/docs/commands.md
CHANGED
|
@@ -101,6 +101,10 @@ kit service postgres install
|
|
|
101
101
|
kit service postgres backup
|
|
102
102
|
kit service postgres remove --yes
|
|
103
103
|
kit service postgres destroy-data --backup-before-destroy --confirm-destroy postgres@ENV
|
|
104
|
+
kit service postgres compose show
|
|
105
|
+
kit service postgres compose validate
|
|
106
|
+
kit service postgres compose diff
|
|
107
|
+
kit service postgres compose eject
|
|
104
108
|
```
|
|
105
109
|
|
|
106
110
|
Replace `postgres` with `redis` for Redis.
|
|
@@ -112,11 +116,21 @@ Replace `postgres` with `redis` for Redis.
|
|
|
112
116
|
configuration backups/files and state. In an interactive terminal it offers the backup and requires typing
|
|
113
117
|
`TYPE@ENV`; automation can request it with `--backup-before-destroy` and must supply `--confirm-destroy`.
|
|
114
118
|
The data archive is retained outside the service directory, but must be copied to independent storage.
|
|
119
|
+
- `compose show`: print the exact generated/custom documents that would be uploaded, without resolving secrets.
|
|
120
|
+
- `compose validate`: parse the configured YAML and run Kitsune Kit's local structure and security checks.
|
|
121
|
+
- `compose diff`: compare the desired Compose fingerprint with the last applied state. An older state without a
|
|
122
|
+
Compose fingerprint is reported as not installed/unknown rather than guessed.
|
|
123
|
+
- `compose eject`: write `.kitsune/compose/TYPE.yml`, back up `config.yml` as `config.yml.backup`, and switch the
|
|
124
|
+
service to `custom`. It refuses existing targets unless `--force` is explicit.
|
|
115
125
|
|
|
116
126
|
When an enabled service has `mode: external`, `status` returns only its configured host/port and
|
|
117
127
|
`managed: false`. All local lifecycle actions fail safely; Kitsune Kit never treats an external provider's data as
|
|
118
128
|
a VPS Docker volume.
|
|
119
129
|
|
|
130
|
+
`install` and `apply` additionally run `docker compose config --quiet` on the server before starting containers.
|
|
131
|
+
For overlay mode every `--file` is passed in deterministic base-then-overlay order. See
|
|
132
|
+
[Compose customization](services/compose.md).
|
|
133
|
+
|
|
120
134
|
## DNS
|
|
121
135
|
|
|
122
136
|
```text
|
data/docs/configuration.md
CHANGED
|
@@ -66,6 +66,10 @@ services:
|
|
|
66
66
|
allowed_cidrs: []
|
|
67
67
|
port: 5432
|
|
68
68
|
password_env: POSTGRES_PASSWORD
|
|
69
|
+
compose:
|
|
70
|
+
mode: generated
|
|
71
|
+
file:
|
|
72
|
+
allow_unsafe: false
|
|
69
73
|
redis:
|
|
70
74
|
enabled: false
|
|
71
75
|
mode: managed
|
|
@@ -76,6 +80,10 @@ services:
|
|
|
76
80
|
allowed_cidrs: []
|
|
77
81
|
port: 6379
|
|
78
82
|
password_env: REDIS_PASSWORD
|
|
83
|
+
compose:
|
|
84
|
+
mode: generated
|
|
85
|
+
file:
|
|
86
|
+
allow_unsafe: false
|
|
79
87
|
|
|
80
88
|
system:
|
|
81
89
|
swap_size_gb: 2
|
|
@@ -135,6 +143,9 @@ SSH policy is ordered to keep a verified path open: create/verify the deploy use
|
|
|
135
143
|
| `allowed_cidrs` | Required and non-empty when `publish` is true. |
|
|
136
144
|
| `port` | Host port, 1–65535. Container ports remain 5432/6379. |
|
|
137
145
|
| `password_env` | Environment-variable name containing the required secret. |
|
|
146
|
+
| `compose.mode` | `generated`, `overlay`, or `custom`; only valid for managed services. |
|
|
147
|
+
| `compose.file` | Project-local YAML file required by `overlay` and `custom`; empty for `generated`. |
|
|
148
|
+
| `compose.allow_unsafe` | Explicit opt-in for security-sensitive Compose options. Defaults to false. |
|
|
138
149
|
|
|
139
150
|
Images, binds, ports and secret names reject newline/shell/YAML injection patterns before any SSH connection is opened.
|
|
140
151
|
|
|
@@ -155,6 +166,11 @@ status` reports the endpoint without its secret. Install, backup, remove and des
|
|
|
155
166
|
availability, TLS, backups and destruction with the external provider. External services never add Docker,
|
|
156
167
|
firewall or private-port checks to the VPS plan.
|
|
157
168
|
|
|
169
|
+
Compose files must be regular, non-symlink files inside the project root and no larger than 256 KiB. `overlay`
|
|
170
|
+
combines the generated base with the configured file; `custom` replaces the generated document and must define
|
|
171
|
+
`services.postgres` or `services.redis` respectively. Inline secrets are rejected. See
|
|
172
|
+
[Compose customization](services/compose.md) for examples, validation rules and commands.
|
|
173
|
+
|
|
158
174
|
### `system`
|
|
159
175
|
|
|
160
176
|
| Field | Validation |
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Compose customization
|
|
2
|
+
|
|
3
|
+
Kitsune Kit can manage PostgreSQL and Redis with a generated Compose document, a user overlay, or a complete
|
|
4
|
+
custom document. All three modes use the same `plan`, `apply`, state, recovery, rollback and data-safety workflow.
|
|
5
|
+
The TUI is optional; these features are fully available through ordinary CLI commands.
|
|
6
|
+
|
|
7
|
+
## Choosing a mode
|
|
8
|
+
|
|
9
|
+
| Mode | Kitsune Kit generates the base | Your file | Best use |
|
|
10
|
+
| --- | --- | --- | --- |
|
|
11
|
+
| `generated` | Yes | None | Secure defaults with image, port and password settings controlled by `config.yml`. |
|
|
12
|
+
| `overlay` | Yes | Compose override | Add labels, resource limits, logging, environment options or other supported overrides. |
|
|
13
|
+
| `custom` | No | Complete Compose document | Take direct ownership of the service definition while retaining Kitsune Kit lifecycle management. |
|
|
14
|
+
|
|
15
|
+
Start with `generated`. Prefer `overlay` while the base service, volume, network and health check still fit. Use
|
|
16
|
+
`custom` when you need to replace those assumptions. There is no legacy-blueprint compatibility layer.
|
|
17
|
+
|
|
18
|
+
## Generated mode
|
|
19
|
+
|
|
20
|
+
```yaml
|
|
21
|
+
services:
|
|
22
|
+
postgres:
|
|
23
|
+
compose:
|
|
24
|
+
mode: generated
|
|
25
|
+
file:
|
|
26
|
+
allow_unsafe: false
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The generated document is deterministic. Passwords appear only as `${POSTGRES_PASSWORD}` or
|
|
30
|
+
`${REDIS_PASSWORD}` references; their values are uploaded separately in a mode-`0600` `.env` file.
|
|
31
|
+
|
|
32
|
+
## Overlay mode
|
|
33
|
+
|
|
34
|
+
Create a project file such as `.kitsune/compose/postgres.override.yml`:
|
|
35
|
+
|
|
36
|
+
```yaml
|
|
37
|
+
services:
|
|
38
|
+
postgres:
|
|
39
|
+
environment:
|
|
40
|
+
LOG_STATEMENT: ddl
|
|
41
|
+
logging:
|
|
42
|
+
options:
|
|
43
|
+
max-size: 20m
|
|
44
|
+
max-file: "5"
|
|
45
|
+
deploy:
|
|
46
|
+
resources:
|
|
47
|
+
limits:
|
|
48
|
+
memory: 1G
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Reference it from configuration:
|
|
52
|
+
|
|
53
|
+
```yaml
|
|
54
|
+
services:
|
|
55
|
+
postgres:
|
|
56
|
+
compose:
|
|
57
|
+
mode: overlay
|
|
58
|
+
file: .kitsune/compose/postgres.override.yml
|
|
59
|
+
allow_unsafe: false
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Kitsune Kit uploads `compose.yml` followed by `compose.override.yml` and supplies both to every Docker Compose
|
|
63
|
+
command in that order. Changes to either document change the plan fingerprint.
|
|
64
|
+
|
|
65
|
+
## Custom mode and eject
|
|
66
|
+
|
|
67
|
+
Create a complete starting document automatically:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
kit service postgres compose eject
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
This operation:
|
|
74
|
+
|
|
75
|
+
1. writes `.kitsune/compose/postgres.yml` from the current generated definition;
|
|
76
|
+
2. copies `.kitsune/config.yml` to `.kitsune/config.yml.backup`;
|
|
77
|
+
3. changes the service to `compose.mode: custom` and references the new file.
|
|
78
|
+
|
|
79
|
+
Existing output or backup files are not overwritten. Review both and use `--force` only when replacement is
|
|
80
|
+
intentional. YAML comments in `config.yml` may be reformatted by ejection; the byte-for-byte backup is retained.
|
|
81
|
+
|
|
82
|
+
A custom PostgreSQL file must contain `services.postgres`; Redis must contain `services.redis`. Configuration
|
|
83
|
+
fields such as `image` no longer alter a custom document, but `password_env`, publication/firewall policy and
|
|
84
|
+
lifecycle state remain part of Kitsune Kit's safety model.
|
|
85
|
+
|
|
86
|
+
## Inspecting and validating
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
kit service postgres compose show
|
|
90
|
+
kit service postgres compose validate
|
|
91
|
+
kit service postgres compose diff
|
|
92
|
+
kit service postgres compose show --format json
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- `show` displays the exact ordered documents and their sources.
|
|
96
|
+
- `validate` parses YAML, validates the required service and applies local security policy.
|
|
97
|
+
- `diff` compares desired content with the last successfully applied Compose fingerprint.
|
|
98
|
+
- `plan` includes mode, ordered files, findings and the overall service fingerprint.
|
|
99
|
+
- `apply` uploads restricted files and runs `docker compose config --quiet` remotely before `up`.
|
|
100
|
+
|
|
101
|
+
Local validation does not replace Docker's own validation. A customization can pass local checks but fail because
|
|
102
|
+
an option is unsupported by the Docker Compose version on the VPS; `apply` then recovers the previous managed
|
|
103
|
+
files and attempts to restart the previous file set.
|
|
104
|
+
|
|
105
|
+
## File boundary
|
|
106
|
+
|
|
107
|
+
Customization files must:
|
|
108
|
+
|
|
109
|
+
- be inside `--root` after path expansion and resolution;
|
|
110
|
+
- be regular files, not symlinks;
|
|
111
|
+
- be at most 256 KiB;
|
|
112
|
+
- contain a top-level YAML mapping;
|
|
113
|
+
- avoid YAML aliases.
|
|
114
|
+
|
|
115
|
+
These constraints keep the deployment input reviewable and prevent a project configuration from reading an
|
|
116
|
+
arbitrary local file.
|
|
117
|
+
|
|
118
|
+
## Secrets
|
|
119
|
+
|
|
120
|
+
Never put a secret value in Compose YAML. Sensitive mapping keys and `KEY=value` environment-list entries are
|
|
121
|
+
rejected unless the value is a `${VARIABLE_NAME}` reference.
|
|
122
|
+
|
|
123
|
+
```yaml
|
|
124
|
+
services:
|
|
125
|
+
postgres:
|
|
126
|
+
environment:
|
|
127
|
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Export the variable before `doctor`, `plan`, `apply` or `install`. Compose display/JSON output never resolves its
|
|
131
|
+
value. The remote `.env`, Compose documents and backups use restricted permissions.
|
|
132
|
+
|
|
133
|
+
## Unsafe options
|
|
134
|
+
|
|
135
|
+
The local policy flags options that cross Kitsune Kit's normal isolation boundary, including:
|
|
136
|
+
|
|
137
|
+
- privileged containers and host PID/IPC/network namespaces;
|
|
138
|
+
- `ALL`, `SYS_ADMIN`, `SYS_PTRACE` or `NET_ADMIN` capabilities;
|
|
139
|
+
- host devices, absolute bind mounts and the Docker socket;
|
|
140
|
+
- ports outside the exact configured bind/port/firewall model;
|
|
141
|
+
- remote `build` contexts and unmanaged `env_file` references.
|
|
142
|
+
|
|
143
|
+
By default, any finding stops `show`, `validate`, `plan` and `apply`. If the configuration is intentionally outside
|
|
144
|
+
the standard boundary, review every finding and set:
|
|
145
|
+
|
|
146
|
+
```yaml
|
|
147
|
+
compose:
|
|
148
|
+
mode: custom
|
|
149
|
+
file: .kitsune/compose/postgres.yml
|
|
150
|
+
allow_unsafe: true
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
This is an escape hatch, not a promise that Kitsune Kit manages or reverses the extra host resources. Findings
|
|
154
|
+
remain visible in metadata and plan details. Inline secrets are always rejected and cannot be enabled by
|
|
155
|
+
`allow_unsafe`.
|
|
156
|
+
|
|
157
|
+
## Rollback behavior
|
|
158
|
+
|
|
159
|
+
State records the mode, ordered filenames and content fingerprint after a verified apply. Before an update,
|
|
160
|
+
Kitsune Kit backs up all currently tracked and desired Compose files plus `.env`. A failed apply removes the new
|
|
161
|
+
file set, restores the backup and starts the prior ordered file set. `kit rollback` uses the same recorded set.
|
|
162
|
+
|
|
163
|
+
Docker volumes remain governed by the existing service contract: `remove` preserves data and `destroy-data`
|
|
164
|
+
requires exact confirmation. Compose customization does not broaden deletion ownership.
|
data/lib/kitsune/kit/cli.rb
CHANGED
|
@@ -205,17 +205,24 @@ module Kitsune
|
|
|
205
205
|
end
|
|
206
206
|
end
|
|
207
207
|
|
|
208
|
-
desc "service TYPE ACTION
|
|
208
|
+
desc "service TYPE ACTION [SUBACTION]",
|
|
209
|
+
"Manage postgres or redis, including Compose show, validate, diff and eject"
|
|
209
210
|
option :confirm_destroy, type: :string, desc: "Exact TYPE@ENV value required for data destruction"
|
|
210
211
|
option :backup_before_destroy, type: :boolean, default: false,
|
|
211
212
|
desc: "Create a restricted remote data backup before destroy-data"
|
|
212
|
-
|
|
213
|
+
option :force, type: :boolean, default: false, desc: "Replace an existing ejected Compose file and backup"
|
|
214
|
+
def service(type, action, subaction = nil)
|
|
213
215
|
execute do
|
|
214
|
-
app, reporter = build_application
|
|
215
|
-
|
|
216
|
-
|
|
216
|
+
app, reporter = action == "compose" ? build_local_application : build_application
|
|
217
|
+
result = if action == "compose"
|
|
218
|
+
dispatch_service_compose(app, type, subaction)
|
|
219
|
+
else
|
|
220
|
+
operation = find_service_operation(app, type, action)
|
|
221
|
+
dispatch_service_action(app, operation, type, action)
|
|
222
|
+
end
|
|
217
223
|
if json? && result.is_a?(Result)
|
|
218
|
-
|
|
224
|
+
command = ["service", type, action, subaction].compact.join(".")
|
|
225
|
+
flush_json(reporter, result, command, app.config.environment)
|
|
219
226
|
end
|
|
220
227
|
0
|
|
221
228
|
end
|
|
@@ -345,6 +352,21 @@ module Kitsune
|
|
|
345
352
|
[app, reporter]
|
|
346
353
|
end
|
|
347
354
|
|
|
355
|
+
def build_local_application
|
|
356
|
+
filter = @secret_filter = build_secret_filter
|
|
357
|
+
bus = Events::Bus.new
|
|
358
|
+
reporter = build_reporter(filter)
|
|
359
|
+
bus.subscribe(reporter)
|
|
360
|
+
subscribe_logger(bus, filter)
|
|
361
|
+
config = Configuration::Loader.new(
|
|
362
|
+
root: options[:root], env: ENV, config_path: options[:config], validate_secrets: false
|
|
363
|
+
).load(environment: options[:env])
|
|
364
|
+
app = Application.new(config: config, provider: nil, state_store: StateStore.new(root: options[:root]),
|
|
365
|
+
event_bus: bus)
|
|
366
|
+
register_configured_secrets(filter, config)
|
|
367
|
+
[app, reporter]
|
|
368
|
+
end
|
|
369
|
+
|
|
348
370
|
def build_tui
|
|
349
371
|
unless $stdin.tty? && $stdout.tty?
|
|
350
372
|
raise Errors::ConfigurationError.new(
|
|
@@ -475,6 +497,61 @@ module Kitsune
|
|
|
475
497
|
end
|
|
476
498
|
end
|
|
477
499
|
|
|
500
|
+
def dispatch_service_compose(app, type, action)
|
|
501
|
+
raise Errors::ConfigurationError, "unknown service type: #{type}" unless %w[postgres redis].include?(type)
|
|
502
|
+
|
|
503
|
+
service = app.config.services.public_send(type)
|
|
504
|
+
if service.mode == "external"
|
|
505
|
+
raise Errors::UnsafeOperationError, "Compose is not managed for an external #{type} service"
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
compose = ServiceCompose.new(config: app.config, type: type, service: service)
|
|
509
|
+
case action
|
|
510
|
+
when "show" then compose_show(compose)
|
|
511
|
+
when "validate" then compose_validate(type, compose)
|
|
512
|
+
when "diff" then compose_diff(app, type, compose)
|
|
513
|
+
when "eject" then compose_eject(app, type)
|
|
514
|
+
else
|
|
515
|
+
raise Errors::ConfigurationError,
|
|
516
|
+
"unknown Compose action: #{action || '(missing)'}; expected show, validate, diff or eject"
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
def compose_show(compose)
|
|
521
|
+
puts compose.display unless json?
|
|
522
|
+
Result.success(compose.metadata.merge(content: compose.display, fingerprint: compose.fingerprint))
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
def compose_validate(type, compose)
|
|
526
|
+
puts "#{type} Compose customization is valid (#{compose.mode})." unless json?
|
|
527
|
+
Result.success(compose.metadata.merge(valid: true, fingerprint: compose.fingerprint))
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
def compose_eject(app, type)
|
|
531
|
+
value = Workflows::EjectCompose.new(
|
|
532
|
+
root: options[:root], config: app.config, type: type, config_path: options[:config]
|
|
533
|
+
).call(force: options[:force])
|
|
534
|
+
puts "Created #{value[:compose_file]} and updated #{value[:config_file]}." unless json?
|
|
535
|
+
Result.success(value)
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
def compose_diff(app, type, compose)
|
|
539
|
+
state = app.state_store.read(app.config.environment).dig("resources", "service.#{type}")
|
|
540
|
+
installed = state&.fetch("compose_fingerprint", nil)
|
|
541
|
+
changed = installed != compose.fingerprint
|
|
542
|
+
value = { changed: changed, desired_fingerprint: compose.fingerprint,
|
|
543
|
+
installed_fingerprint: installed, mode: compose.mode }
|
|
544
|
+
unless json?
|
|
545
|
+
label = if installed
|
|
546
|
+
changed ? "changed" : "unchanged"
|
|
547
|
+
else
|
|
548
|
+
"not installed or from an older state"
|
|
549
|
+
end
|
|
550
|
+
puts "#{type} Compose: #{label}"
|
|
551
|
+
end
|
|
552
|
+
Result.success(value)
|
|
553
|
+
end
|
|
554
|
+
|
|
478
555
|
def destroy_service_data(app, operation, type)
|
|
479
556
|
expected = "#{type}@#{app.config.environment}"
|
|
480
557
|
state = app.state_store.read(app.config.environment).dig("resources", "service.#{type}")
|