Metadata-Version: 2.5
Name: dockerdb-cli
Version: 0.2.4
Summary: CLI tool for managing database servers via Docker
License-Expression: GPL-3.0-or-later
Requires-Python: >=3.10
Requires-Dist: jinja2<4,>=3.1.0
Requires-Dist: prompt-toolkit<3.1,>=3.0.41
Requires-Dist: pydantic<3,>=2.6.0
Requires-Dist: pyyaml<7,>=6.0.1
Requires-Dist: questionary<3,>=2.0.1
Requires-Dist: rich<14,>=13.7.0
Requires-Dist: typer>=0.12.0
Description-Content-Type: text/markdown

# dockerdb-cli

CLI tool for managing database servers via Docker. Create, manage, and tear down database instances with a single command.

## Table of Contents

- [Prerequisites](#prerequisites)
- [Installation](#installation)
  - [From PyPI (recommended)](#from-pypi-recommended)
  - [From source (for development)](#from-source-for-development)
  - [Upgrade](#upgrade)
- [Quick Start](#quick-start)
- [Supported Engines](#supported-engines)
  - [MySQL](#mysql)
  - [MariaDB](#mariadb)
  - [PostgreSQL](#postgresql)
  - [MongoDB](#mongodb)
  - [Oracle](#oracle)
  - [SQL Server](#sql-server)
  - [Redis](#redis)
- [Commands Reference](#commands-reference)
  - [`db-cli create <engine>`](#db-cli-create-engine)
  - [`db-cli list`](#db-cli-list)
  - [`db-cli status [name]`](#db-cli-status-name)
  - [`db-cli start|stop|restart [name]`](#db-cli-startstoprestart-name)
  - [`db-cli start-all | stop-all | rm-all`](#db-cli-start-all--stop-all--rm-all)
  - [`db-cli rm [name]`](#db-cli-rm-name)
  - [`db-cli logs [name]`](#db-cli-logs-name)
  - [`db-cli seed [name]`](#db-cli-seed-name)
  - [`db-cli backup [name]`](#db-cli-backup-name)
  - [`db-cli restore [name]`](#db-cli-restore-name)
  - [`db-cli config show|path`](#db-cli-config-showpath)
- [Configuration](#configuration)
- [Sample Data](#sample-data)
- [SSL/TLS](#ssltls)
- [Adding a New Engine](#adding-a-new-engine)
- [Project Structure](#project-structure)
- [Development](#development)
- [License](#license)

## Prerequisites

- Python 3.10+
- [Docker](https://www.docker.com/) (with `docker compose` v2)

## Installation

### From PyPI (recommended)

```bash
pip install dockerdb-cli
```

Or with [pipx](https://pipx.pypa.io/) for isolated install:

```bash
pipx install dockerdb-cli
```

Verify:

```bash
db-cli --version
# or use the full name
dockerdb-cli --version
```

> **Note:** This package installs **two CLI commands** that are identical: `dockerdb-cli` (full name) and `db-cli` (short alias). The rest of this README uses `db-cli` for brevity.

### From source (for development)

```bash
git clone <repo-url>
cd dockerdb-cli
uv sync
uv run dockerdb-cli --version
```

### Upgrade

```bash
pip install --upgrade dockerdb-cli
```

## Quick Start

```bash
# Create a standalone MongoDB server (interactive prompts for version/topology)
db-cli create mongodb

# Create with all options specified (no prompts)
db-cli create mongodb --name my-mongo --version 7.0 --topology standalone

# Create a MySQL master-slave cluster
db-cli create mysql --name my-mysql --topology master-slave --replicas 2

# List all servers
db-cli list

# Check server status
db-cli status my-mongo

# Stop / Start / Restart
db-cli stop my-mongo
db-cli start my-mongo
db-cli restart my-mongo

# View logs
db-cli logs my-mongo
db-cli logs my-mongo --follow

# Seed sample data
db-cli seed my-mongo

# Backup & Restore
db-cli backup my-mongo --output ./mongo-backup.gz
db-cli restore my-mongo --input ./mongo-backup.gz

# Remove server (containers + generated files)
db-cli rm my-mongo

# Remove server AND data volumes
db-cli rm my-mongo --volumes
```

## Supported Engines

Six engines are supported. Default versions are shown in **bold**; default port is the container port.

| Engine | Topologies | Versions | Port |
|--------|------------|----------|:----:|
| **MySQL** | `standalone`, `master-slave` | 5.7, **8.0**, 8.4, 9.0, 9.1 | 3306 |
| **MariaDB** | `standalone`, `master-slave` | 10.6, 10.11, 11.0, **11.4**, 11.7 | 3306 |
| **PostgreSQL** | `standalone`, `master-slave` | 13, 14, 15, 16, **17** | 5432 |
| **MongoDB** | `standalone`, `replica-set` | 4.4, 5.0, 6.0, **7.0**, 8.0 | 27017 |
| **Oracle** | `standalone` | 18 (XE), 21 (XE), **23** (Free) | 1521 |
| **SQL Server** | `standalone` | 2017, 2019, **2022** | 1433 |
| **Redis** | `standalone`, `sentinel`, `cluster` | 6.2, 7.0, 7.2, **7.4**, 8.0 | 6379 |

---

### MySQL

| Topology | Description |
|----------|-------------|
| `standalone` | Single instance |
| `master-slave` | Master + N read-only slaves with GTID replication |

```bash
db-cli create mysql --name dev-mysql --version 8.0
db-cli create mysql --name prod-mysql --topology master-slave --replicas 2
```

Default connection: `mysql -h 127.0.0.1 -P 3306 -uadmin -poneforall test`

---

### MariaDB

| Topology | Description |
|----------|-------------|
| `standalone` | Single instance |
| `master-slave` | Master + N read-only slaves with GTID replication |

```bash
db-cli create mariadb --name dev-maria --version 11.4
db-cli create mariadb --name prod-maria --topology master-slave --replicas 2
```

Default connection: `mysql -h 127.0.0.1 -P 3306 -uadmin -poneforall test`

---

### PostgreSQL

| Topology | Description |
|----------|-------------|
| `standalone` | Single instance |
| `master-slave` | Primary + N streaming-replication standbys |

```bash
db-cli create postgres --name dev-pg --version 17
db-cli create postgres --name prod-pg --topology master-slave --replicas 2
```

Default connection: `PGPASSWORD=oneforall psql -h 127.0.0.1 -p 5432 -U admin test`

---

### MongoDB

| Topology | Description |
|----------|-------------|
| `standalone` | Single instance |
| `replica-set` | 1 primary + N replicas with keyfile auth |

```bash
db-cli create mongodb --name dev-mongo --version 7.0
db-cli create mongodb --name prod-mongo --topology replica-set --replicas 2
```

Default connection: `mongosh "mongodb://admin:oneforall@localhost:27017/test?authSource=admin"`

---

### Oracle

| Topology | Description |
|----------|-------------|
| `standalone` | Single instance (`gvenzl/oracle-free` 23c, `gvenzl/oracle-xe` 18/21) |

```bash
db-cli create oracle --name dev-oracle --version 23
```

Default connection (service name `FREEPDB1` on 23c, `XEPDB1` on 18/21):

```
sqlplus admin/oneforall@//localhost:1521/FREEPDB1
```

---

### SQL Server

| Topology | Description |
|----------|-------------|
| `standalone` | Single instance (`mcr.microsoft.com/mssql/server`, Developer edition) |

The only login is `sa`. The **SA password must be strong** — at least 8 characters and 3 of 4 classes (uppercase, lowercase, digits, symbols) — so the default is `OneForAll!23`, not `oneforall`. The SQL Server image has no init entrypoint, so the target database is created the first time you seed:

```bash
db-cli create mssql --name dev-mssql --version 2022 --password 'OneForAll!23'
db-cli seed dev-mssql   # creates the `test` database and sample tables
```

Default connection: `sqlcmd -S 127.0.0.1,1433 -U sa -P 'OneForAll!23' -d test -C`

---

### Redis

| Topology | Description |
|----------|-------------|
| `standalone` | Single instance |
| `sentinel` | Master + N replicas + 3 sentinels |
| `cluster` | 6 nodes (3 masters + 3 replicas) |

```bash
db-cli create redis --name dev-redis --version 7.4 --password secret
db-cli create redis --name prod-redis-ha --topology sentinel --replicas 2 --password secret
db-cli create redis --name prod-redis-cl --topology cluster --password secret
```

Default connection: `redis-cli -h 127.0.0.1 -p 6379 -a oneforall`

## Commands Reference

### `db-cli create <engine>`

Create and start a new database server.

```
Arguments:
  engine                Database engine: mysql, mariadb, postgres, mongodb, oracle, mssql, redis

Options:
  -n, --name TEXT       Server instance name (prompted if omitted)
  -v, --version TEXT    Database version (prompted if omitted)
  -t, --topology TEXT   standalone, master-slave, replica-set, sentinel, cluster (prompted if omitted)
  -p, --port INT        Host port (auto-detected if omitted)
  -u, --username TEXT   Database username          [default: admin]
  --password TEXT       Database password          [default: oneforall]
  -d, --database TEXT   Default database name      [default: test]
  --ssl                 Enable SSL/TLS with auto-generated certs
  --replicas INT        Number of replicas         [default: 2]
  --no-seed             Skip seeding sample data
  --no-start            Generate files only, don't start containers
```

**Hybrid mode:** If you provide all flags, the command runs non-interactively. If you omit flags, it prompts you interactively.

### `db-cli list`

List all managed servers in a table.

```
Options:
  --json    Output as JSON (machine-readable, for scripting)
```

With `--json`, the full config of every server is printed as a JSON array (`[]` when there are none), so you can pipe it into tools like `jq`:

```bash
db-cli list --json | jq -r '.[].name'
```

### `db-cli status [name]`

Show detailed status for a server, including Docker container state and connection info.

```
Options:
  --json    Output as JSON (machine-readable, for scripting)
```

The JSON payload is the server's full config plus a `status` field (`running`/`stopped`) and a `connection` object. A server name is required in `--json` mode (no interactive picker). JSON goes to stdout; any errors go to stderr, so stdout stays clean for parsing:

```bash
db-cli status my-mongo --json | jq -r '.connection.connection_string'
```

### `db-cli start|stop|restart [name]`

Manage server lifecycle. If `name` is omitted, an interactive prompt lets you choose.

### `db-cli start-all | stop-all | rm-all`

Act on every managed server at once.

- `start-all` — start all servers (uses `up -d` so `--no-start` instances come up too). Non-destructive, so no confirmation is asked; exits non-zero if any server fails to start.
- `stop-all` — stop all running servers.
- `rm-all` — remove all servers.

```
Options (stop-all, rm-all):
  -y, --yes        Skip confirmation prompt
  -v, --volumes    Also remove data volumes (rm-all only)
```

### `db-cli rm [name]`

Remove a server. Stops containers and deletes generated files.

```
Options:
  -v, --volumes    Also remove Docker data volumes
  -y, --yes        Skip confirmation prompt
```

### `db-cli logs [name]`

View Docker container logs.

```
Options:
  -f, --follow     Stream logs in real-time
  -t, --tail INT   Number of lines to show [default: 100]
```

### `db-cli seed [name]`

Seed sample data into a running server. Uses the bundled seed scripts by default.

```
Options:
  -f, --file PATH  Use a custom seed data file instead
```

### `db-cli backup [name]`

Dump database contents to a file.

```
Options:
  -o, --output PATH  Output file [default: backup.sql]
```

### `db-cli restore [name]`

Restore data from a backup file.

```
Options:
  -i, --input PATH   Input backup file [required]
```

### `db-cli config show|path`

Show global configuration or print the config file path.

## Configuration

All data is stored under `~/.dockerdb/`:

```
~/.dockerdb/
├── config.yaml          # Global defaults (credentials, data dir)
├── registry.yaml        # Server instance registry
└── instances/
    └── my-mongo/
        ├── docker-compose.yml   # Generated
        └── init/
            └── mongodb_seed.js  # Seed data
```

Default credentials (can be overridden per-server via `--username`, `--password`):

| Setting | Default |
|---------|---------|
| Username | `admin` |
| Password | `oneforall` |
| Database | `test` |

## Sample Data

When creating a server without `--no-seed`, sample data is automatically seeded on first startup:

**Tables/Collections:** `users`, `products`, `orders`
**View:** `order_summary` (join of all three)

This provides a ready-to-use dataset for development and testing.

## SSL/TLS

Add the `--ssl` flag to enable TLS with auto-generated self-signed certificates:

```bash
db-cli create postgres --name secure-pg --version 17 --ssl
db-cli create mysql    --name secure-mysql --ssl
db-cli create mariadb  --name secure-maria --ssl
db-cli create mongodb  --name secure-mongo --ssl
db-cli create redis    --name secure-redis --ssl
```

Certificates are generated at `~/.dockerdb/instances/<name>/ssl/`:

```
ssl/
├── ca-cert.pem         # CA certificate (use this on the client)
├── ca-key.pem          # CA private key
├── server-cert.pem     # Server certificate
├── server-key.pem      # Server private key
└── server-combined.pem # Cert + key concatenated (used by MongoDB)
```

The CLI prints SSL-aware connection strings after `create`. Example for PostgreSQL:

```
connection_string: postgresql://admin:oneforall@localhost:5432/test?sslmode=require
cli_command:       PGPASSWORD=oneforall psql "host=127.0.0.1 port=5432 ... sslmode=require"
ssl_ca_path:       /Users/.../ssl/ca-cert.pem
```

### Engine support matrix

| Engine | TLS Version | Notes |
|--------|:-----------:|-------|
| PostgreSQL | TLS 1.3 | `sslmode=require` |
| MySQL | TLS 1.3 | `--require-secure-transport=ON` (plain rejected) |
| MariaDB | TLS 1.3 | `--require-secure-transport=ON` |
| MongoDB | requireTLS | uses combined cert+key file |
| Redis | TLS-only | plain port disabled, use `rediss://` scheme |
| Oracle | ✗ | not implemented (requires Oracle Wallet) |

### Caveats

- **Standalone topology only.** Using `--ssl` with `master-slave`, `replica-set`, `sentinel`, or `cluster` will print a warning and disable SSL.
- **Self-signed certs** are not trusted by clients by default. The generated `cli_command` includes flags to skip verification (`--insecure`, `tlsAllowInvalidCertificates`, etc.). For production-grade certs, replace files in `ssl/` and restart the container.

## Adding a New Engine

The tool uses the **Strategy Pattern** to make adding new database engines straightforward. No changes to the CLI commands or core logic are needed.

### Step 1: Create the engine class

Create `db_cli/engines/postgresql.py`:

```python
from db_cli.engines.base import DatabaseEngine
from db_cli.models.compose import ComposeContext
from db_cli.models.engine import EngineDefaults, TopologySpec
from db_cli.models.server import ServerConfig, Topology


class PostgreSQLEngine(DatabaseEngine):
    def get_defaults(self) -> EngineDefaults:
        return EngineDefaults(
            default_version="16",
            supported_versions=["13", "14", "15", "16", "17"],
            default_port=5432,
            container_data_path="/var/lib/postgresql/data",
            docker_image="postgres",
            supported_topologies={
                Topology.STANDALONE: TopologySpec(
                    template_name="standalone.yml.j2",
                    description="Single PostgreSQL instance",
                ),
            },
        )

    def get_supported_topologies(self) -> list[Topology]:
        return [Topology.STANDALONE]

    # ... implement remaining abstract methods
```

### Step 2: Register the engine

In `db_cli/engines/__init__.py`, add:

```python
from db_cli.engines.postgresql import PostgreSQLEngine
register_engine("postgresql", PostgreSQLEngine)
```

### Step 3: Add templates

Create `db_cli/templates/postgresql/standalone.yml.j2`:

```yaml
services:
  {{ server_name }}:
    image: {{ image }}
    container_name: {{ server_name }}
    ports:
{% for port in ports %}
      - "{{ port.host }}:{{ port.container }}"
{% endfor %}
    volumes:
      - {{ server_name }}_data:{{ data_path }}
    environment:
      - POSTGRES_USER={{ credentials.username }}
      - POSTGRES_PASSWORD={{ credentials.password }}
      - POSTGRES_DB={{ credentials.database }}

volumes:
  {{ server_name }}_data:
```

That's it. `db-cli create postgresql` now works with all existing commands (list, start, stop, rm, etc.).

## Project Structure

```
db_cli/
├── main.py                  # Typer app entry point
├── commands/                # CLI commands (thin layer, delegates to core)
│   ├── create.py
│   ├── lifecycle.py         # start, stop, restart, rm
│   ├── status.py            # list, status
│   ├── logs.py
│   ├── data.py              # seed, backup, restore
│   └── config.py
├── core/                    # Business logic
│   ├── config.py            # Global config (~/.dockerdb/config.yaml)
│   ├── registry.py          # Server instance CRUD
│   ├── docker.py            # Docker Compose subprocess wrapper
│   ├── compose_generator.py # Jinja2 template rendering
│   ├── ssl.py               # Self-signed certificate generation
│   └── data.py              # Seed/backup/restore orchestration
├── engines/                 # Strategy pattern: one class per database
│   ├── base.py              # Abstract base class (DatabaseEngine)
│   ├── mongodb.py
│   └── mysql.py
├── models/                  # Pydantic v2 data models
│   ├── server.py            # ServerConfig, Topology, Credentials, ...
│   ├── engine.py            # EngineDefaults, TopologySpec
│   ├── config.py            # GlobalConfig
│   └── compose.py           # ComposeContext (template rendering)
├── templates/               # Jinja2 templates for Docker Compose
│   ├── mongodb/
│   ├── mysql/
│   └── seed_data/
├── ui/                      # Rich console + Questionary prompts
│   ├── console.py
│   ├── prompts.py
│   └── tables.py
└── utils/                   # Validators, port checking, formatters
    ├── validators.py
    ├── ports.py
    ├── formatters.py
    └── completers.py
```

## Development

```bash
# Install dependencies
make install

# Run linter
make lint

# Run tests
make test

# Format code
make format

# Run all checks
make check
```

## License

GPL-3.0-or-later
