Python control-panel package driving the full provisioning flow from a site's site.yaml (docs/05): - provision: ZFS web dataset + ownership, per-site DB + least-priv user, generated .env encrypted to secrets.enc.yaml (SOPS/age), render the profile templates + persist site.yaml, per-customer chrooted SFTP account, docker compose up. - deprovision (gated: data destroyed only with --purge, after a final backup), backup (ZFS snapshot + mariadb-dump), restore (rollback + import), render (preview), list. Design: one command/file runner with a real --dry-run (prints every action, redacts secrets); idempotent steps; Config + Site validation mirroring docs/03; passwords never logged. Modules: cli, config, naming, context, render, runner, zfs, database, secrets, sftp, compose, provision, backup. Plus pyproject (heleosctl entry point), config.example.yaml, an example site, and a README. Tests: 22 pure-logic unit tests (naming, config validation, template render across all profiles + db on/off) — all passing. Full provision and deprovision verified end-to-end in --dry-run. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
87 lines
3.9 KiB
Markdown
87 lines
3.9 KiB
Markdown
# control-panel — heleosctl
|
|
|
|
Provisioning CLI for the platform. Reads a site's `site.yaml`, renders the
|
|
[site-templates](../site-templates/README.md) into
|
|
[deployments/](../deployments/README.md), creates the ZFS dataset + database +
|
|
SFTP account, and runs `docker compose up`. Implements the flow in
|
|
[docs/05-provisioning-workflow.md](../docs/05-provisioning-workflow.md).
|
|
|
|
> Runs **on the host** (Linux), typically as root, since it drives `zfs`,
|
|
> `docker`, `useradd`, and `systemctl`. Use `--dry-run` to preview every action
|
|
> first — nothing touches the system until you drop the flag.
|
|
|
|
## Install
|
|
```bash
|
|
cd control-panel
|
|
python -m venv .venv && . .venv/bin/activate
|
|
pip install -e ".[dev]" # installs the `heleosctl` command + pytest
|
|
cp config.example.yaml config.yaml && $EDITOR config.yaml
|
|
```
|
|
Config is found via `--config`, `$HELEOS_CONFIG`, `./config.yaml`, or
|
|
`/etc/heleos/config.yaml`.
|
|
|
|
## Usage
|
|
```bash
|
|
# Preview everything (no changes)
|
|
heleosctl --dry-run provision -f examples/acme-shop.site.yaml
|
|
|
|
# Provision for real
|
|
heleosctl provision -f examples/acme-shop.site.yaml
|
|
|
|
# Inspect / operate on an existing site (by customer+site or by file)
|
|
heleosctl list
|
|
heleosctl render -c acme -s shop # print rendered config
|
|
heleosctl backup -c acme -s shop # snapshot + DB dump
|
|
heleosctl restore -c acme -s shop --snapshot auto-20260101-0300 --db /path/dump.sql
|
|
heleosctl deprovision -c acme -s shop # stop + drop DB, KEEP data
|
|
heleosctl deprovision -c acme -s shop --purge # also destroy the dataset (after a final backup)
|
|
```
|
|
|
|
## What each command does
|
|
| Command | Actions |
|
|
|---------|---------|
|
|
| `provision` | ZFS web dataset + ownership → DB + user (if any) → `.env` + SOPS encrypt → render compose/nginx/site.yaml → per-customer SFTP account → `compose up -d`. |
|
|
| `deprovision` | `compose down` → final backup → drop DB/user → (with `--purge`) destroy dataset + remove config. |
|
|
| `render` | Print the rendered files without writing (debugging). |
|
|
| `backup` | ZFS snapshot + `mariadb-dump` into `db-backups/<customer>/<site>`. |
|
|
| `restore` | ZFS rollback + import a DB dump. |
|
|
| `list` | Table of provisioned sites found under the deployments dir. |
|
|
|
|
## Design notes
|
|
- **Idempotent & declarative:** `site.yaml` is the source of truth; steps use
|
|
`zfs create -p`, `CREATE ... IF NOT EXISTS`, and existence checks so re-running
|
|
converges. A customer's SFTP password is set only when the account is first
|
|
created.
|
|
- **Secrets:** the DB password is generated per site, written to a git-ignored
|
|
`.env`, and encrypted to `secrets.enc.yaml` via SOPS/age (set
|
|
`sops_age_recipient`). Passwords never appear in command logs (`--redacted`).
|
|
- **Safety:** all host mutations go through one runner supporting `--dry-run`;
|
|
data is destroyed only with `--purge`, always after a final backup.
|
|
|
|
## Layout
|
|
```
|
|
src/heleos/
|
|
cli.py # click commands
|
|
config.py # Config + Site loading/validation
|
|
naming.py # slug / db / sftp identifier rules (docs/03)
|
|
context.py # Site -> template variables
|
|
render.py # Jinja2 render of a profile
|
|
runner.py # command + file runner with --dry-run
|
|
zfs.py database.py secrets.py sftp.py compose.py # host ops
|
|
provision.py # provision / deprovision orchestration
|
|
backup.py # snapshot/dump + restore
|
|
tests/ # pure-logic unit tests (naming, config, render)
|
|
```
|
|
|
|
## Test
|
|
```bash
|
|
pip install -e ".[dev]" && pytest # or: PYTHONPATH=src pytest
|
|
```
|
|
|
|
## Known follow-ups
|
|
- **Web-root umask for php-fpm:** SFTP writes with umask 0002 (group-writable);
|
|
php-fpm-created files may need the same for two-way SFTP editing. See the note
|
|
in [site-templates/README.md](../site-templates/README.md).
|
|
- **git commit of `deployments/`** on provision (GitOps audit trail) — currently
|
|
left to the operator; wire into the flow when the deployments repo is set up.
|
|
- **`reconfigure` / `rotate-secret`** commands (documented in docs/05) — to add.
|