Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed
- Updated Dockerfiles (`Dockerfile`, `jyq.Dockerfile`) from `rust:1.85-alpine` to `rust:1.88-alpine` to fix release workflow failure caused by `time@0.3.47` requiring rustc 1.88.0
- Aligned all markdown table columns across documentation files (`FAQ.md`, `README.md`, `docs/security.md`, `docs/seeding.md`, `docs/usage.md`)
- Aligned all markdown table columns across documentation files (`FAQ.md`, `README.md`, `docs/seeding.md`, `docs/templating.md`, `docs/usage.md`, `tests/README.md`)
- Fixed clippy `collapsible_if` lint in seed executor's unique key check
- Removed dead code: unused `src/cmd/seed.rs` module (replaced by `src/seed/`)
- Suppressed unused field warning on `AutoIdConfig.id_type` (reserved for future use)
Expand All @@ -88,7 +88,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Unit tests for seed schema parsing, database operations, executor logic, references, idempotency, reset, and edge cases

### Changed
- Complete rewrite from Go to Rust for ~76% smaller Docker images (7.4MB → 1.8MB)
- Complete rewrite from Go to Rust for smaller Docker images (7.4MB → ~5MB)
- CLI framework changed from cobra to clap
- Template engine changed from Go text/template to minijinja (Jinja2-style); access env vars via `{{ env.VAR }}`
- CI/CD workflows updated for Rust toolchain (cargo test, clippy, rustfmt)
Expand Down
16 changes: 8 additions & 8 deletions FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,14 @@ This is useful when you're shipping logs to a centralized system like Loki, Data

All retry parameters are flags on the `wait-for` subcommand:

| Flag | Default | What it does |
| ------------------ | --------- | ------------------------------------------------------------------------ |
| `--max-attempts` | `60` | Total number of attempts before giving up |
| `--initial-delay` | `1s` | Delay after the first failure |
| `--max-delay` | `30s` | Upper bound on delay between retries |
| `--backoff-factor` | `2.0` | Multiplier applied to the delay after each attempt |
| `--jitter` | `0.1` | Random fraction (0.0–1.0) added to each delay to prevent thundering herd |
| `--timeout` | `5m` | Hard deadline across all targets |
| Flag | Default | What it does |
| ------------------ | ------- | ------------------------------------------------------------------------ |
| `--max-attempts` | `60` | Total number of attempts before giving up |
| `--initial-delay` | `1s` | Delay after the first failure |
| `--max-delay` | `30s` | Upper bound on delay between retries |
| `--backoff-factor` | `2.0` | Multiplier applied to the delay after each attempt |
| `--jitter` | `0.1` | Random fraction (0.0–1.0) added to each delay to prevent thundering herd |
| `--timeout` | `5m` | Hard deadline across all targets |

Example — fast retries with low jitter:

Expand Down
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Initium replaces fragile bash scripts in your initContainers with a single, secu
## Features

- **Single static binary** — zero runtime dependencies, built `FROM scratch`
- **Tiny image** — ~1.8 MB multi-arch container (amd64 + arm64)
- **Tiny image** — ~5 MB multi-arch container (amd64 + arm64)
- **Zero CVEs** — no OS packages, no shell, no attack surface
- **PSA `restricted` compatible** — runs as non-root (UID 65534), read-only filesystem, all capabilities dropped
- **Sidecar mode** — `--sidecar` flag keeps the process alive for use as a Kubernetes sidecar container
Expand Down Expand Up @@ -106,10 +106,10 @@ cargo build --release --no-default-features --features postgres,sqlite
cargo build --release --no-default-features --features sqlite
```

| Feature | Default | Description |
| ---------- | ------- | ------------------ |
| `sqlite` | ✅ | SQLite driver |
| `postgres` | ✅ | PostgreSQL driver |
| Feature | Default | Description |
| ---------- | ------- | -------------------- |
| `sqlite` | ✅ | SQLite driver |
| `postgres` | ✅ | PostgreSQL driver |
| `mysql` | ✅ | MySQL/MariaDB driver |

## Helm Chart
Expand Down Expand Up @@ -330,13 +330,13 @@ helm install my-app charts/initium \

Initium was built to address limitations in existing init container tools:

| Tool | Language | Image size | Multi-tool | Database seeding | Security posture |
| --------------------------------------------------------------------------- | -------- | ----------- | ---------- | ---------------- | ----------------------- |
| **Initium** | Rust | ~1.8 MB | Yes | Yes | PSA `restricted`, no OS |
| [wait-for-it](https://github.com/vishnubob/wait-for-it) | Bash | Needs shell | No | No | Requires shell + netcat |
| [dockerize](https://github.com/jwilder/dockerize) | Go | ~17 MB | Partial | No | Full OS image |
| [k8s-wait-for](https://github.com/groundnuty/k8s-wait-for) | Bash | Needs shell | No | No | Requires shell + kubectl|
| [wait4x](https://github.com/atkrad/wait4x) | Go | ~12 MB | No | No | Minimal OS |
| Tool | Language | Image size | Multi-tool | Database seeding | Security posture |
| ---------------------------------------------------------- | -------- | ----------- | ---------- | ---------------- | ------------------------ |
| **Initium** | Rust | ~5 MB | Yes | Yes | PSA `restricted`, no OS |
| [wait-for-it](https://github.com/vishnubob/wait-for-it) | Bash | Needs shell | No | No | Requires shell + netcat |
| [dockerize](https://github.com/jwilder/dockerize) | Go | ~17 MB | Partial | No | Full OS image |
| [k8s-wait-for](https://github.com/groundnuty/k8s-wait-for) | Bash | Needs shell | No | No | Requires shell + kubectl |
| [wait4x](https://github.com/atkrad/wait4x) | Go | ~12 MB | No | No | Minimal OS |

If you only need TCP/HTTP readiness checks, any of these tools work. Initium is designed for teams that also need migrations, seeding, config rendering, and secret fetching in a single security-hardened binary.

Expand Down
118 changes: 59 additions & 59 deletions docs/seeding.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ Seed spec files are MiniJinja templates: they are rendered with environment vari

## Supported Databases

| Driver | Connection URL format |
| ------------ | ------------------------------------------------ |
| `postgres` | `postgres://user:pass@host:5432/dbname` |
| `mysql` | `mysql://user:pass@host:3306/dbname` |
| `sqlite` | `/path/to/database.db` or `:memory:` for tests |
| Driver | Connection URL format |
| ---------- | ---------------------------------------------- |
| `postgres` | `postgres://user:pass@host:5432/dbname` |
| `mysql` | `mysql://user:pass@host:3306/dbname` |
| `sqlite` | `/path/to/database.db` or `:memory:` for tests |

## Quick Start

Expand Down Expand Up @@ -65,47 +65,47 @@ phases:

### Field reference

| Field | Type | Required | Description |
| -------------------------------------------------- | ---------- | ---------- | ---------------------------------------------------------------- |
| `database.driver` | string | Yes | Database driver: `postgres`, `mysql`, or `sqlite` |
| `database.url` | string | No | Direct database connection URL |
| `database.url_env` | string | No | Environment variable containing the database URL |
| `database.tracking_table` | string | No | Name of the seed tracking table (default: `initium_seed`) |
| `phases[].name` | string | Yes | Unique phase name |
| `phases[].order` | integer | No | Execution order (lower first, default: 0) |
| `phases[].database` | string | No | Target database name (for create/switch) |
| `phases[].schema` | string | No | Target schema name (for create/switch) |
| `phases[].create_if_missing` | boolean | No | Create the database/schema if it does not exist (default: false) |
| `phases[].timeout` | string | No | Default wait timeout (e.g. `30s`, `1m`, `1m30s`; default: `30s`) |
| `phases[].wait_for[].type` | string | Yes | Object type: `table`, `view`, `schema`, or `database` |
| `phases[].wait_for[].name` | string | Yes | Object name to wait for |
| `phases[].wait_for[].timeout` | string | No | Per-object timeout override (e.g. `60s`, `2m`, `1m30s`) |
| `phases[].seed_sets[].name` | string | Yes | Unique name for the seed set (used in tracking) |
| `phases[].seed_sets[].order` | integer | No | Execution order (lower values first, default: 0) |
| `phases[].seed_sets[].tables[].table` | string | Yes | Target database table name |
| `phases[].seed_sets[].tables[].order` | integer | No | Execution order within the seed set (default: 0) |
| `phases[].seed_sets[].tables[].unique_key` | string[] | No | Columns for duplicate detection |
| `phases[].seed_sets[].tables[].auto_id.column` | string | No | Auto-generated ID column name |
| `phases[].seed_sets[].tables[].auto_id.id_type` | string | No | ID type (default: `integer`) |
| `phases[].seed_sets[].tables[].rows[]._ref` | string | No | Internal reference name for cross-table references |
| Field | Type | Required | Description |
| ----------------------------------------------- | -------- | -------- | ---------------------------------------------------------------- |
| `database.driver` | string | Yes | Database driver: `postgres`, `mysql`, or `sqlite` |
| `database.url` | string | No | Direct database connection URL |
| `database.url_env` | string | No | Environment variable containing the database URL |
| `database.tracking_table` | string | No | Name of the seed tracking table (default: `initium_seed`) |
| `phases[].name` | string | Yes | Unique phase name |
| `phases[].order` | integer | No | Execution order (lower first, default: 0) |
| `phases[].database` | string | No | Target database name (for create/switch) |
| `phases[].schema` | string | No | Target schema name (for create/switch) |
| `phases[].create_if_missing` | boolean | No | Create the database/schema if it does not exist (default: false) |
| `phases[].timeout` | string | No | Default wait timeout (e.g. `30s`, `1m`, `1m30s`; default: `30s`) |
| `phases[].wait_for[].type` | string | Yes | Object type: `table`, `view`, `schema`, or `database` |
| `phases[].wait_for[].name` | string | Yes | Object name to wait for |
| `phases[].wait_for[].timeout` | string | No | Per-object timeout override (e.g. `60s`, `2m`, `1m30s`) |
| `phases[].seed_sets[].name` | string | Yes | Unique name for the seed set (used in tracking) |
| `phases[].seed_sets[].order` | integer | No | Execution order (lower values first, default: 0) |
| `phases[].seed_sets[].tables[].table` | string | Yes | Target database table name |
| `phases[].seed_sets[].tables[].order` | integer | No | Execution order within the seed set (default: 0) |
| `phases[].seed_sets[].tables[].unique_key` | string[] | No | Columns for duplicate detection |
| `phases[].seed_sets[].tables[].auto_id.column` | string | No | Auto-generated ID column name |
| `phases[].seed_sets[].tables[].auto_id.id_type` | string | No | ID type (default: `integer`) |
| `phases[].seed_sets[].tables[].rows[]._ref` | string | No | Internal reference name for cross-table references |

### Wait-for object support by driver

| Object type | SQLite | PostgreSQL | MySQL |
| ------------- | -------- | ------------ | ------- |
| `table` | ✅ | ✅ | ✅ |
| `view` | ✅ | ✅ | ✅ |
| `schema` | ❌ | ✅ | ✅* |
| `database` | ❌ | ✅ | ✅* |
| Object type | SQLite | PostgreSQL | MySQL |
| ----------- | ------ | ---------- | ----- |
| `table` | ✅ | ✅ | ✅ |
| `view` | ✅ | ✅ | ✅ |
| `schema` | ❌ | ✅ | ✅* |
| `database` | ❌ | ✅ | ✅* |

\* In MySQL, `schema` and `database` are synonymous.

### Create-if-missing support by driver

| Operation | SQLite | PostgreSQL | MySQL |
| ------------------- | -------- | ------------ | ------- |
| `CREATE DATABASE` | ❌ | ✅ | ✅ |
| `CREATE SCHEMA` | ❌ | ✅ | ✅* |
| Operation | SQLite | PostgreSQL | MySQL |
| ----------------- | ------ | ---------- | ----- |
| `CREATE DATABASE` | ❌ | ✅ | ✅ |
| `CREATE SCHEMA` | ❌ | ✅ | ✅* |

\* In MySQL, `CREATE SCHEMA` maps to `CREATE DATABASE`.

Expand Down Expand Up @@ -276,30 +276,30 @@ spec:

## CLI Reference

| Flag | Default | Description |
| ----------- | ------------ | --------------------------------------- |
| `--spec` | (required) | Path to seed spec file (YAML or JSON) |
| `--reset` | `false` | Delete existing data and re-apply seeds |
| `--json` | `false` | Enable JSON log output |
| Flag | Default | Description |
| --------- | ---------- | --------------------------------------- |
| `--spec` | (required) | Path to seed spec file (YAML or JSON) |
| `--reset` | `false` | Delete existing data and re-apply seeds |
| `--json` | `false` | Enable JSON log output |

## Failure Modes

| Scenario | Behavior |
| --------------------------------------- | -------------------------------------------------------- |
| Invalid spec file | Fails with parse error before connecting to database |
| Invalid MiniJinja template | Fails with template syntax error before parsing YAML |
| Database unreachable | Fails with connection error |
| Unsupported driver | Fails with descriptive error listing supported drivers |
| Missing env var for URL | Fails with error naming the missing variable |
| Missing env var in `$env:` | Fails with error naming the missing variable |
| Unresolved `@ref:` | Fails with error naming the missing reference |
| Row insertion failure | Entire seed set rolled back via transaction |
| Duplicate row (with unique_key) | Row silently skipped |
| Already-applied seed set | Seed set silently skipped |
| Wait-for object timeout | Fails with structured timeout error naming the object |
| Unsupported object type for driver | Fails immediately with driver-specific error |
| CREATE DATABASE on SQLite | Fails with "not supported" error |
| CREATE SCHEMA on SQLite | Fails with "not supported" error |
| Scenario | Behavior |
| ---------------------------------- | ------------------------------------------------------ |
| Invalid spec file | Fails with parse error before connecting to database |
| Invalid MiniJinja template | Fails with template syntax error before parsing YAML |
| Database unreachable | Fails with connection error |
| Unsupported driver | Fails with descriptive error listing supported drivers |
| Missing env var for URL | Fails with error naming the missing variable |
| Missing env var in `$env:` | Fails with error naming the missing variable |
| Unresolved `@ref:` | Fails with error naming the missing reference |
| Row insertion failure | Entire seed set rolled back via transaction |
| Duplicate row (with unique_key) | Row silently skipped |
| Already-applied seed set | Seed set silently skipped |
| Wait-for object timeout | Fails with structured timeout error naming the object |
| Unsupported object type for driver | Fails immediately with driver-specific error |
| CREATE DATABASE on SQLite | Fails with "not supported" error |
| CREATE SCHEMA on SQLite | Fails with "not supported" error |

## Examples

Expand Down
Loading