minilake 1.5.0__tar.gz
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.
- minilake-1.5.0/.dockerignore +24 -0
- minilake-1.5.0/.gitignore +71 -0
- minilake-1.5.0/.pre-commit-config.yaml +22 -0
- minilake-1.5.0/CONTRIBUTING.md +212 -0
- minilake-1.5.0/Dockerfile +40 -0
- minilake-1.5.0/Dockerfile.notebook +22 -0
- minilake-1.5.0/Dockerfile.test +41 -0
- minilake-1.5.0/FEATURES.md +1131 -0
- minilake-1.5.0/LICENSE +21 -0
- minilake-1.5.0/PKG-INFO +154 -0
- minilake-1.5.0/README.md +115 -0
- minilake-1.5.0/docker-compose.test.yml +66 -0
- minilake-1.5.0/docker-compose.yml +70 -0
- minilake-1.5.0/docs/configuration.md +137 -0
- minilake-1.5.0/docs/databricks-sdk.md +149 -0
- minilake-1.5.0/docs/getting-started.md +127 -0
- minilake-1.5.0/docs/index.md +57 -0
- minilake-1.5.0/docs/mcp/examples.md +205 -0
- minilake-1.5.0/docs/mcp/index.md +86 -0
- minilake-1.5.0/docs/mcp/resources-and-prompts.md +68 -0
- minilake-1.5.0/docs/mcp/tools.md +93 -0
- minilake-1.5.0/docs/mcp/troubleshooting.md +120 -0
- minilake-1.5.0/docs/releases.md +109 -0
- minilake-1.5.0/docs/spark-and-delta.md +138 -0
- minilake-1.5.0/docs/terraform.md +65 -0
- minilake-1.5.0/docs/testing.md +90 -0
- minilake-1.5.0/examples/terraform/main.tf +106 -0
- minilake-1.5.0/examples/terraform/terraform.tfvars.example +10 -0
- minilake-1.5.0/examples/terraform/variables.tf +12 -0
- minilake-1.5.0/examples/use_as_lib.py +124 -0
- minilake-1.5.0/minilake_logo.png +0 -0
- minilake-1.5.0/notebooks/minilake_delta_quickstart.ipynb +164 -0
- minilake-1.5.0/pyproject.toml +120 -0
- minilake-1.5.0/scripts/generate-certs.sh +35 -0
- minilake-1.5.0/scripts/run-tests-docker.sh +121 -0
- minilake-1.5.0/src/minilake/__init__.py +31 -0
- minilake-1.5.0/src/minilake/__main__.py +6 -0
- minilake-1.5.0/src/minilake/admin.py +111 -0
- minilake-1.5.0/src/minilake/app.py +184 -0
- minilake-1.5.0/src/minilake/cli.py +207 -0
- minilake-1.5.0/src/minilake/config.py +147 -0
- minilake-1.5.0/src/minilake/docker_executor.py +285 -0
- minilake-1.5.0/src/minilake/duckdb_pool.py +155 -0
- minilake-1.5.0/src/minilake/errors.py +87 -0
- minilake-1.5.0/src/minilake/mcp/__init__.py +14 -0
- minilake-1.5.0/src/minilake/mcp/client.py +128 -0
- minilake-1.5.0/src/minilake/mcp/prompts.py +86 -0
- minilake-1.5.0/src/minilake/mcp/resources.py +260 -0
- minilake-1.5.0/src/minilake/mcp/server.py +151 -0
- minilake-1.5.0/src/minilake/mcp/tools/__init__.py +7 -0
- minilake-1.5.0/src/minilake/mcp/tools/admin.py +33 -0
- minilake-1.5.0/src/minilake/mcp/tools/clusters.py +61 -0
- minilake-1.5.0/src/minilake/mcp/tools/composite.py +188 -0
- minilake-1.5.0/src/minilake/mcp/tools/dbfs.py +52 -0
- minilake-1.5.0/src/minilake/mcp/tools/files.py +47 -0
- minilake-1.5.0/src/minilake/mcp/tools/jobs.py +310 -0
- minilake-1.5.0/src/minilake/mcp/tools/secrets.py +45 -0
- minilake-1.5.0/src/minilake/mcp/tools/sql.py +146 -0
- minilake-1.5.0/src/minilake/mcp/tools/unity_catalog.py +163 -0
- minilake-1.5.0/src/minilake/mcp/tools/warehouses.py +48 -0
- minilake-1.5.0/src/minilake/mcp/tools/workspace.py +66 -0
- minilake-1.5.0/src/minilake/models/__init__.py +1 -0
- minilake-1.5.0/src/minilake/models/clusters.py +123 -0
- minilake-1.5.0/src/minilake/models/common.py +21 -0
- minilake-1.5.0/src/minilake/models/dbfs.py +59 -0
- minilake-1.5.0/src/minilake/models/identity.py +23 -0
- minilake-1.5.0/src/minilake/models/jobs.py +275 -0
- minilake-1.5.0/src/minilake/models/permissions.py +47 -0
- minilake-1.5.0/src/minilake/models/secrets.py +44 -0
- minilake-1.5.0/src/minilake/models/sql.py +161 -0
- minilake-1.5.0/src/minilake/models/unity_catalog.py +245 -0
- minilake-1.5.0/src/minilake/models/workspace.py +89 -0
- minilake-1.5.0/src/minilake/persistence.py +93 -0
- minilake-1.5.0/src/minilake/services/__init__.py +96 -0
- minilake-1.5.0/src/minilake/services/catchall.py +32 -0
- minilake-1.5.0/src/minilake/services/clusters.py +269 -0
- minilake-1.5.0/src/minilake/services/dbfs.py +243 -0
- minilake-1.5.0/src/minilake/services/files.py +196 -0
- minilake-1.5.0/src/minilake/services/identity.py +40 -0
- minilake-1.5.0/src/minilake/services/jobs.py +673 -0
- minilake-1.5.0/src/minilake/services/permissions.py +129 -0
- minilake-1.5.0/src/minilake/services/secrets.py +133 -0
- minilake-1.5.0/src/minilake/services/sql_statements.py +888 -0
- minilake-1.5.0/src/minilake/services/sql_warehouses.py +152 -0
- minilake-1.5.0/src/minilake/services/unity_catalog.py +960 -0
- minilake-1.5.0/src/minilake/services/workspace.py +357 -0
- minilake-1.5.0/src/minilake/tls.py +122 -0
- minilake-1.5.0/src/minilake/uc_types.py +293 -0
- minilake-1.5.0/tests/conftest.py +189 -0
- minilake-1.5.0/tests/error_handling/__init__.py +0 -0
- minilake-1.5.0/tests/error_handling/test_400_bad_request.py +101 -0
- minilake-1.5.0/tests/error_handling/test_404_not_found.py +68 -0
- minilake-1.5.0/tests/error_handling/test_501_not_implemented.py +21 -0
- minilake-1.5.0/tests/mcp_server/conftest.py +89 -0
- minilake-1.5.0/tests/mcp_server/test_capabilities.py +99 -0
- minilake-1.5.0/tests/mcp_server/test_errors.py +41 -0
- minilake-1.5.0/tests/mcp_server/test_job_tools.py +189 -0
- minilake-1.5.0/tests/mcp_server/test_sql_tools.py +73 -0
- minilake-1.5.0/tests/mcp_server/test_uc_tools.py +197 -0
- minilake-1.5.0/tests/mcp_server/test_workspace_tools.py +73 -0
- minilake-1.5.0/tests/test_admin.py +63 -0
- minilake-1.5.0/tests/test_app.py +76 -0
- minilake-1.5.0/tests/test_clusters.py +156 -0
- minilake-1.5.0/tests/test_config.py +71 -0
- minilake-1.5.0/tests/test_dbfs.py +94 -0
- minilake-1.5.0/tests/test_docker_executor.py +93 -0
- minilake-1.5.0/tests/test_files.py +85 -0
- minilake-1.5.0/tests/test_golden_path.py +328 -0
- minilake-1.5.0/tests/test_identity.py +16 -0
- minilake-1.5.0/tests/test_jobs.py +408 -0
- minilake-1.5.0/tests/test_models.py +114 -0
- minilake-1.5.0/tests/test_permissions.py +79 -0
- minilake-1.5.0/tests/test_persistence.py +130 -0
- minilake-1.5.0/tests/test_secrets.py +74 -0
- minilake-1.5.0/tests/test_sql_statements.py +279 -0
- minilake-1.5.0/tests/test_tls.py +72 -0
- minilake-1.5.0/tests/test_warehouses.py +78 -0
- minilake-1.5.0/tests/test_workspace.py +105 -0
- minilake-1.5.0/tests/test_workspace_files.py +81 -0
- minilake-1.5.0/tests/unity_catalog/__init__.py +0 -0
- minilake-1.5.0/tests/unity_catalog/conftest.py +78 -0
- minilake-1.5.0/tests/unity_catalog/test_catalogs.py +130 -0
- minilake-1.5.0/tests/unity_catalog/test_delta_tables.py +298 -0
- minilake-1.5.0/tests/unity_catalog/test_schemas.py +130 -0
- minilake-1.5.0/tests/unity_catalog/test_spark_catalog_protocol.py +138 -0
- minilake-1.5.0/tests/unity_catalog/test_tables.py +289 -0
- minilake-1.5.0/tests/unity_catalog/test_types.py +146 -0
- minilake-1.5.0/tests/unity_catalog/test_volumes.py +65 -0
- minilake-1.5.0/uv.lock +2040 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
.git
|
|
2
|
+
.gitignore
|
|
3
|
+
.pytest_cache
|
|
4
|
+
.coverage
|
|
5
|
+
htmlcov
|
|
6
|
+
dist
|
|
7
|
+
build
|
|
8
|
+
*.egg-info
|
|
9
|
+
__pycache__
|
|
10
|
+
*.pyc
|
|
11
|
+
*.pyo
|
|
12
|
+
.venv
|
|
13
|
+
venv
|
|
14
|
+
.env
|
|
15
|
+
.env.*
|
|
16
|
+
!.env.example
|
|
17
|
+
data/
|
|
18
|
+
.certs/
|
|
19
|
+
certs/
|
|
20
|
+
.DS_Store
|
|
21
|
+
Thumbs.db
|
|
22
|
+
docs/
|
|
23
|
+
examples/
|
|
24
|
+
CONTRIBUTING.md
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
|
|
23
|
+
# Virtual environments
|
|
24
|
+
venv/
|
|
25
|
+
env/
|
|
26
|
+
ENV/
|
|
27
|
+
.venv/
|
|
28
|
+
|
|
29
|
+
# Testing
|
|
30
|
+
.pytest_cache/
|
|
31
|
+
.coverage
|
|
32
|
+
htmlcov/
|
|
33
|
+
.tox/
|
|
34
|
+
|
|
35
|
+
# IDE
|
|
36
|
+
.vscode/
|
|
37
|
+
.idea/
|
|
38
|
+
*.swp
|
|
39
|
+
*.swo
|
|
40
|
+
*~
|
|
41
|
+
.DS_Store
|
|
42
|
+
|
|
43
|
+
# minilake specific
|
|
44
|
+
data/
|
|
45
|
+
.certs/
|
|
46
|
+
certs/
|
|
47
|
+
*.duckdb
|
|
48
|
+
*.duckdb.wal
|
|
49
|
+
*.db
|
|
50
|
+
|
|
51
|
+
# Environment
|
|
52
|
+
.env
|
|
53
|
+
.env.local
|
|
54
|
+
.env.*.local
|
|
55
|
+
|
|
56
|
+
# Terraform
|
|
57
|
+
.terraform/
|
|
58
|
+
*.tfstate
|
|
59
|
+
*.tfstate.*
|
|
60
|
+
.terraform.lock.hcl
|
|
61
|
+
|
|
62
|
+
# Docker
|
|
63
|
+
docker-compose.override.yml
|
|
64
|
+
|
|
65
|
+
# OS
|
|
66
|
+
.DS_Store
|
|
67
|
+
Thumbs.db
|
|
68
|
+
|
|
69
|
+
# Claude Code local settings (machine-specific; .claude/CLAUDE.md IS tracked)
|
|
70
|
+
.claude/settings.local.json
|
|
71
|
+
.claude/scheduled_tasks.lock
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v5.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-toml
|
|
9
|
+
- id: check-added-large-files
|
|
10
|
+
args: ["--maxkb=1024"]
|
|
11
|
+
- id: check-merge-conflict
|
|
12
|
+
- id: mixed-line-ending
|
|
13
|
+
args: ["--fix=lf"]
|
|
14
|
+
|
|
15
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
16
|
+
rev: v0.16.0
|
|
17
|
+
hooks:
|
|
18
|
+
- id: ruff
|
|
19
|
+
args: ["--fix"]
|
|
20
|
+
files: ^(src|tests)/
|
|
21
|
+
- id: ruff-format
|
|
22
|
+
files: ^(src|tests)/
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# Contributing to minilake
|
|
2
|
+
|
|
3
|
+
Thanks for wanting to contribute. minilake emulates the Databricks REST API for a single developer running it locally — real SQL via DuckDB, real Delta Lake, real Job execution, no cloud, no fake auth. Each API group is one service module plus one models module; adding a new endpoint or fixing a bug should take minutes, not hours.
|
|
4
|
+
|
|
5
|
+
## Project Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
minilake/
|
|
9
|
+
├── src/minilake/
|
|
10
|
+
│ ├── app.py # FastAPI app factory, lifespan (DuckDB pool, persistence)
|
|
11
|
+
│ ├── admin.py # /_minilake/health, /ready, /reset, /services
|
|
12
|
+
│ ├── cli.py # `minilake` entry point
|
|
13
|
+
│ ├── config.py # Settings (env vars)
|
|
14
|
+
│ ├── errors.py # DatabricksError -> {error_code, message}
|
|
15
|
+
│ ├── duckdb_pool.py # Per-catalog ATTACH, per-warehouse connections
|
|
16
|
+
│ ├── docker_executor.py # Sibling-container (or subprocess) real Job execution
|
|
17
|
+
│ ├── persistence.py # MINILAKE_PERSIST JSON snapshot save/load
|
|
18
|
+
│ ├── models/
|
|
19
|
+
│ │ ├── unity_catalog.py, jobs.py, sql.py, secrets.py, clusters.py, ...
|
|
20
|
+
│ └── services/
|
|
21
|
+
│ ├── __init__.py # SERVICE_REGISTRY (name -> module path)
|
|
22
|
+
│ ├── unity_catalog.py, jobs.py, sql_statements.py, clusters.py, ...
|
|
23
|
+
├── tests/
|
|
24
|
+
│ ├── conftest.py # minilake_server, workspace_client, reset_state fixtures
|
|
25
|
+
│ ├── test_<service>.py # one file per service, real databricks-sdk client
|
|
26
|
+
│ └── unity_catalog/ # sub-package for UC's larger surface area
|
|
27
|
+
├── .github/workflows/ # ci.yml (lint + tests), release.yml (tag -> GHCR + Release)
|
|
28
|
+
├── Dockerfile, docker-compose*.yml
|
|
29
|
+
└── FEATURES.md # endpoint-by-endpoint status, source of truth for scope
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## For infrastructure changes (Dockerfiles, CI/CD workflows, pyproject, dependencies), open an issue first. PRs containing such changes without a prior issue will be rejected.
|
|
35
|
+
|
|
36
|
+
## For New API Groups — Open an Issue First
|
|
37
|
+
|
|
38
|
+
> **This section applies only when you are adding a brand-new Databricks API group** (a new module under `src/minilake/services/`).
|
|
39
|
+
|
|
40
|
+
**Before writing any code for a new API group, open a GitHub issue.** Use the `enhancement` label and describe:
|
|
41
|
+
|
|
42
|
+
1. **Which Databricks API group** (e.g. `Repos`, `Model Registry`, `Query History`) and its base path (e.g. `/api/2.0/repos`).
|
|
43
|
+
2. **Which operations** you actually need — not the full API surface. minilake favors the operations real `databricks-sdk`/Terraform-provider users actually hit over wire-format completeness for every action.
|
|
44
|
+
3. **A real use case** — what SDK call, Terraform resource, or CI workflow drove the need. "I want full parity with Databricks" is not a use case.
|
|
45
|
+
4. **Real vs. state-machine vs. stub** — per this project's "fail loudly, don't fake it" philosophy (see `.claude/CLAUDE.md`), be explicit about what will actually execute for real (like SQL/Delta/Jobs do today) versus what will be metadata-only. It's fine to ship a real state machine with no backing compute (like Clusters); it's not fine to ship 20 endpoints that silently return empty/fake data with no indication.
|
|
46
|
+
5. **Scope boundaries** — what's explicitly out for the first PR.
|
|
47
|
+
|
|
48
|
+
A maintainer will confirm the scope and point you at the right pattern (synchronous DuckDB-backed, real-file-backed, or state-machine) before you write code. This avoids large PRs that get rejected for scope drift or for faking behavior the project deliberately avoids.
|
|
49
|
+
|
|
50
|
+
**PRs that add a new API group without a corresponding scoped issue will be closed and the contributor asked to open one.**
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Adding a New API Group
|
|
55
|
+
|
|
56
|
+
Every service follows the same pattern (see `.claude/CLAUDE.md` for the authoritative version of this checklist):
|
|
57
|
+
|
|
58
|
+
### 1. Create `src/minilake/models/myservice.py`
|
|
59
|
+
|
|
60
|
+
Pydantic request/response models only — no business logic here.
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from pydantic import BaseModel
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class CreateThingRequest(BaseModel):
|
|
67
|
+
name: str
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class ThingInfo(BaseModel):
|
|
71
|
+
name: str
|
|
72
|
+
created_at: int
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 2. Create `src/minilake/services/myservice.py`
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
"""MyService API endpoints."""
|
|
79
|
+
|
|
80
|
+
import time
|
|
81
|
+
from typing import Any, Dict
|
|
82
|
+
|
|
83
|
+
from fastapi import APIRouter
|
|
84
|
+
|
|
85
|
+
from minilake.errors import DatabricksError
|
|
86
|
+
from minilake.models.myservice import CreateThingRequest, ThingInfo
|
|
87
|
+
|
|
88
|
+
router = APIRouter(prefix="/api/2.0/myservice", tags=["myservice"])
|
|
89
|
+
|
|
90
|
+
_state: Dict[str, Any] = {"things": {}}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@router.post("/things", response_model=ThingInfo)
|
|
94
|
+
async def create_thing(req: CreateThingRequest) -> ThingInfo:
|
|
95
|
+
if req.name in _state["things"]:
|
|
96
|
+
raise DatabricksError(error_code="ALREADY_EXISTS", message="...", status_code=400)
|
|
97
|
+
thing = {"name": req.name, "created_at": int(time.time() * 1000)}
|
|
98
|
+
_state["things"][req.name] = thing
|
|
99
|
+
return ThingInfo(**thing)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# ============================================================================
|
|
103
|
+
# State Management
|
|
104
|
+
# ============================================================================
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def get_state() -> Dict[str, Any]:
|
|
108
|
+
return _state.copy()
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def restore_state(data: Dict[str, Any]) -> None:
|
|
112
|
+
global _state
|
|
113
|
+
_state.update(data)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
async def reset() -> None:
|
|
117
|
+
global _state
|
|
118
|
+
_state = {"things": {}}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Every service module **must** export `router`, `get_state()`, `restore_state()`, `reset()`, and **must not** depend on global mutable state outside the module.
|
|
122
|
+
|
|
123
|
+
### 3. Register in `src/minilake/services/__init__.py`
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
SERVICE_REGISTRY = {
|
|
127
|
+
# ... existing ...
|
|
128
|
+
"myservice": "minilake.services.myservice",
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 4. Write tests in `tests/test_myservice.py`
|
|
133
|
+
|
|
134
|
+
Tests **must** use the real `databricks-sdk` `WorkspaceClient`/`AccountClient` pointed at the local server — the SDK is the source of truth, not a mock:
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
import pytest
|
|
138
|
+
from databricks.sdk import WorkspaceClient
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@pytest.mark.crud
|
|
142
|
+
def test_create_thing(workspace_client: WorkspaceClient):
|
|
143
|
+
thing = workspace_client.myservice.create_thing(name="test")
|
|
144
|
+
assert thing.name == "test"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Use `@pytest.mark.serial` for tests that mutate workspace-wide state and can't run in parallel; `@pytest.mark.crud`/`workflow`/`error` otherwise.
|
|
148
|
+
|
|
149
|
+
### 5. Update `FEATURES.md`
|
|
150
|
+
|
|
151
|
+
Add an entry under the right section (Fully Implemented / Not Implemented) with the endpoint list and status — this file is the single source of truth for what's actually built.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Running Tests Locally
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
# Full suite, real server, real databricks-sdk client
|
|
159
|
+
docker compose -f docker-compose.test.yml up --build --abort-on-container-exit
|
|
160
|
+
|
|
161
|
+
# Locally without Docker (starts minilake as a subprocess)
|
|
162
|
+
uv run pytest tests/ -v
|
|
163
|
+
|
|
164
|
+
# A single service
|
|
165
|
+
uv run pytest tests/test_myservice.py -v
|
|
166
|
+
|
|
167
|
+
# With coverage
|
|
168
|
+
uv run pytest tests/ --cov=minilake --cov-report=html
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Code Conventions
|
|
174
|
+
|
|
175
|
+
(See `.claude/CLAUDE.md` for the full, authoritative set of project rules — this is a summary.)
|
|
176
|
+
|
|
177
|
+
- **Services own routes + state; models own shapes** — never define Pydantic models inside a service module.
|
|
178
|
+
- **`get_state()` / `restore_state()` / `reset()`** — every service exposes these three, called by `/_minilake/reset` and (if `MINILAKE_PERSIST=1`) by shutdown/startup snapshotting.
|
|
179
|
+
- **Real execution where feasible** — SQL against real DuckDB, files as real files, Jobs as real subprocesses/containers. Don't add a fake response where a real one is achievable.
|
|
180
|
+
- **Fail loudly on unsupported features** — return `501 {"error_code": "NOT_IMPLEMENTED", ...}` for out-of-scope APIs. Never silently accept and fake a response.
|
|
181
|
+
- **No real auth** — parse auth headers for routing only, never verify signatures. This is intentional (see [Known Gaps](README.md#known-gaps)), not a TODO.
|
|
182
|
+
- **Tests are real, not mocked** — every feature needs a `tests/test_<service>.py` using the real SDK client against a real running server.
|
|
183
|
+
- **Lint/format** — `ruff check` / `ruff format` via pre-commit (`uv run pre-commit install` after cloning; CI runs the same hooks).
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Pull Request Checklist
|
|
188
|
+
|
|
189
|
+
- [ ] Service module in `src/minilake/services/`, models in `src/minilake/models/`
|
|
190
|
+
- [ ] Registered in `SERVICE_REGISTRY` (`src/minilake/services/__init__.py`)
|
|
191
|
+
- [ ] `get_state()` / `restore_state()` / `reset()` implemented
|
|
192
|
+
- [ ] Tests added in `tests/test_<service>.py` using the real `databricks-sdk` client, and passing
|
|
193
|
+
- [ ] `uv run pre-commit run --all-files` passes (lint + format)
|
|
194
|
+
- [ ] `FEATURES.md` updated with the new endpoint list and status
|
|
195
|
+
- [ ] Full suite still green: `docker compose -f docker-compose.test.yml up --build --abort-on-container-exit`
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## What We're Looking For
|
|
200
|
+
|
|
201
|
+
High-value contributions right now (see `FEATURES.md`'s Roadmap for the full picture):
|
|
202
|
+
|
|
203
|
+
- **Test coverage for existing modules** — `jobs.py`, `sql_statements.py`, and `unity_catalog.py` are covered mostly on happy paths; edge cases and error branches need more tests.
|
|
204
|
+
- **Secrets ACLs** (`secrets/acls/*`) — scope/secret CRUD is real, ACL endpoints don't exist yet.
|
|
205
|
+
- **DBT task / pipeline task execution** in Jobs.
|
|
206
|
+
- **Real Unity Catalog REST protocol for native Spark catalog resolution** — see FEATURES.md Roadmap Phase 7 for what this actually involves; it's a materially larger effort, discuss scope in an issue first.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Questions?
|
|
211
|
+
|
|
212
|
+
Open a GitHub Discussion or file an issue with the `question` label.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
FROM python:3.11-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /opt/minilake
|
|
4
|
+
|
|
5
|
+
# Install system dependencies
|
|
6
|
+
RUN apt-get update && apt-get install -y \
|
|
7
|
+
build-essential \
|
|
8
|
+
curl \
|
|
9
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
10
|
+
|
|
11
|
+
# Copy project files
|
|
12
|
+
COPY pyproject.toml pyproject.toml
|
|
13
|
+
COPY src/ src/
|
|
14
|
+
COPY README.md README.md
|
|
15
|
+
|
|
16
|
+
# Install minilake with the MCP extra. Baked into the image so MINILAKE_MCP=1 is all that's
|
|
17
|
+
# needed to turn the MCP server on; the extra stays optional for PyPI installs.
|
|
18
|
+
RUN pip install --no-cache-dir -e ".[mcp]"
|
|
19
|
+
|
|
20
|
+
# Create data directory
|
|
21
|
+
RUN mkdir -p /data
|
|
22
|
+
|
|
23
|
+
# Expose ports (HTTP + HTTPS)
|
|
24
|
+
EXPOSE 8000 8443
|
|
25
|
+
|
|
26
|
+
# Set environment variables
|
|
27
|
+
ENV PYTHONUNBUFFERED=1
|
|
28
|
+
ENV MINILAKE_DATA_DIR=/data
|
|
29
|
+
ENV MINILAKE_HOST=0.0.0.0
|
|
30
|
+
ENV MINILAKE_PORT=8000
|
|
31
|
+
# Native HTTPS is opt-in: set MINILAKE_TLS=1 to also serve TLS on :8443 (an
|
|
32
|
+
# auto-generated self-signed cert lands under /data/certs). To bring your own
|
|
33
|
+
# cert, set MINILAKE_SSL_CERTFILE / MINILAKE_SSL_KEYFILE.
|
|
34
|
+
|
|
35
|
+
# Health check
|
|
36
|
+
HEALTHCHECK --interval=10s --timeout=5s --start-period=10s --retries=3 \
|
|
37
|
+
CMD curl -f http://localhost:8000/_minilake/health || exit 1
|
|
38
|
+
|
|
39
|
+
# Run minilake server
|
|
40
|
+
CMD ["minilake", "--host", "0.0.0.0", "--port", "8000"]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
FROM jupyter/pyspark-notebook:spark-3.5.0
|
|
2
|
+
|
|
3
|
+
USER root
|
|
4
|
+
|
|
5
|
+
# Delta Lake, matching this image's bundled Spark 3.5.0.
|
|
6
|
+
RUN pip install --no-cache-dir delta-spark==3.1.0 databricks-sdk
|
|
7
|
+
|
|
8
|
+
# Pre-resolve and cache the Delta Lake jars at build time so the first cell a
|
|
9
|
+
# user runs doesn't need network access / Maven resolution.
|
|
10
|
+
RUN python3 -c "\
|
|
11
|
+
from delta import configure_spark_with_delta_pip; \
|
|
12
|
+
from pyspark.sql import SparkSession; \
|
|
13
|
+
builder = SparkSession.builder.appName('warmup') \
|
|
14
|
+
.config('spark.sql.extensions', 'io.delta.sql.DeltaSparkSessionExtension') \
|
|
15
|
+
.config('spark.sql.catalog.spark_catalog', 'org.apache.spark.sql.delta.catalog.DeltaCatalog'); \
|
|
16
|
+
spark = configure_spark_with_delta_pip(builder).getOrCreate(); \
|
|
17
|
+
spark.stop()"
|
|
18
|
+
|
|
19
|
+
COPY notebooks/minilake_delta_quickstart.ipynb /home/${NB_USER}/work/minilake_delta_quickstart.ipynb
|
|
20
|
+
RUN chown ${NB_UID}:${NB_GID} /home/${NB_USER}/work/minilake_delta_quickstart.ipynb
|
|
21
|
+
|
|
22
|
+
USER ${NB_UID}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
FROM python:3.11-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /opt/minilake
|
|
4
|
+
|
|
5
|
+
# Install system dependencies + uv
|
|
6
|
+
RUN apt-get update && apt-get install -y \
|
|
7
|
+
build-essential \
|
|
8
|
+
curl \
|
|
9
|
+
&& rm -rf /var/lib/apt/lists/* && \
|
|
10
|
+
pip install --no-cache-dir uv
|
|
11
|
+
|
|
12
|
+
# Copy project files
|
|
13
|
+
COPY pyproject.toml pyproject.toml
|
|
14
|
+
COPY src/ src/
|
|
15
|
+
COPY tests/ tests/
|
|
16
|
+
COPY README.md README.md
|
|
17
|
+
|
|
18
|
+
# Install minilake + all dev dependencies
|
|
19
|
+
RUN uv pip install -e ".[mcp]" --python /usr/local/bin/python && \
|
|
20
|
+
uv pip install \
|
|
21
|
+
pytest>=8.0 \
|
|
22
|
+
pytest-xdist>=3.6 \
|
|
23
|
+
httpx>=0.27 \
|
|
24
|
+
databricks-sdk>=0.40 \
|
|
25
|
+
pytest-cov \
|
|
26
|
+
pytest-json-report \
|
|
27
|
+
pytest-asyncio \
|
|
28
|
+
deltalake>=0.19 \
|
|
29
|
+
pandas>=2.0 \
|
|
30
|
+
pyarrow>=14.0 \
|
|
31
|
+
--python /usr/local/bin/python
|
|
32
|
+
|
|
33
|
+
# Create data directory for tests
|
|
34
|
+
RUN mkdir -p /data
|
|
35
|
+
|
|
36
|
+
# Set test environment
|
|
37
|
+
ENV MINILAKE_DATA_DIR=/data
|
|
38
|
+
ENV PYTHONUNBUFFERED=1
|
|
39
|
+
|
|
40
|
+
# Run tests
|
|
41
|
+
CMD ["pytest", "tests/", "-v", "--cov=minilake", "--cov-report=html", "--cov-report=term-missing", "--tb=short"]
|