Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
721d0a7
inherit without impl
cospectrum Jul 1, 2025
100918c
implement for gitlab instead
cospectrum Jul 1, 2025
576d50a
del blank file
cospectrum Jul 1, 2025
18a8dcf
refactor
cospectrum Jul 4, 2025
89de678
rename middlewares
cospectrum Jul 4, 2025
31a79c4
update python version
cospectrum Jul 4, 2025
43dc20e
uv lock
cospectrum Jul 4, 2025
72fd1fe
final version of create_bucket
cospectrum Jul 5, 2025
2d35bfe
gitlab client
cospectrum Jul 5, 2025
92829d2
fix delete obj unit test
cospectrum Jul 5, 2025
7a7d5c4
no such bucket
cospectrum Jul 5, 2025
caa9436
idempotent delete on key
cospectrum Jul 5, 2025
6a200fa
list objects using gitlab client
cospectrum Jul 7, 2025
44401cc
add Keysmith
cospectrum Jul 9, 2025
3ab3eba
test keysmith
cospectrum Jul 9, 2025
7cd51ac
test lock finally
cospectrum Jul 9, 2025
44ee4f9
refactor and add delete bucket
cospectrum Jul 12, 2025
a4327e5
rm python setup job
cospectrum Jul 12, 2025
b417801
edit validation error msg
cospectrum Jul 12, 2025
d082066
1 test_store instead of test stores
cospectrum Jul 12, 2025
4771964
lint
cospectrum Jul 12, 2025
8ca15ba
fix some gitlab errors
cospectrum Jul 12, 2025
e2d17f3
fix fetch of raw bytes
cospectrum Jul 12, 2025
0de43cb
rm gitlab create_file
cospectrum Jul 12, 2025
7f8f75d
run e2e tests for gitlab store
cospectrum Jul 12, 2025
9c2d3de
fix inmemory pytest tag for third_part
cospectrum Jul 12, 2025
78c43a1
rm gitpython
cospectrum Jul 12, 2025
5c19093
replace keysmith with rwlock
cospectrum Jul 14, 2025
29491ac
catch some errors
cospectrum Jul 14, 2025
f04d357
edit race test
cospectrum Jul 14, 2025
830d868
configure monitoring
cospectrum Jul 15, 2025
99c3fb2
add telemetry for logs
cospectrum Jul 15, 2025
344631e
httpx instrumentation
cospectrum Jul 15, 2025
e315774
file stuff for swagger
cospectrum Jul 15, 2025
8187892
test version
cospectrum Jul 15, 2025
f651e95
mypy
cospectrum Jul 15, 2025
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
11 changes: 3 additions & 8 deletions .github/actions/common-setup/action.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
name: Common Project Setup
description: Setup Python, install uv, and install dependencies
inputs:
python-version:
description: which python to install
required: false
default: '3.10'
uv-version:
description: which uv to use
required: false
Expand All @@ -16,10 +12,9 @@ runs:
uses: astral-sh/setup-uv@v6
with:
version: ${{ inputs.uv-version }}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
- name: Install the project
shell: bash
run: uv sync --all-packages --locked
- name: Print python version
shell: bash
run: uv run python --version
Empty file removed .github/workflows/.keep
Empty file.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,6 @@ jobs:
- name: tests/third_party/s3-tests
run: |
cd tests/third_party/s3-tests
uv run tox -- s3tests_boto3/functional/test_s3.py -m boxdrive
uv run tox -- s3tests_boto3/functional/test_s3.py -m inmemory
env:
S3TEST_CONF: s3tests.conf
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,4 @@ __marimo__/
pyrightconfig.json
tmp
.tmp
.volumes
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.13
3.12
33 changes: 23 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@
S3-compatible API with **Abstract Object Store** in Python (FastAPI).
Work in progress.

## Installation
## Built-in stores
- `InMemoryStore`
- `GitlabStore` (with some limitations)

## Quick Start

0. install `boxdrive` from [pypi](https://pypi.org/project/boxdrive/) using your favorite package manager:
```bash
uv add boxdrive
```

## Quick Start

### Basic Usage

1. create `main.py`:
```python
from boxdrive import create_app
Expand Down Expand Up @@ -71,7 +72,7 @@ class MyCustomStore(ObjectStore):
async def put_object(
self, bucket_name: BucketName, key: Key, data: bytes, content_type: ContentType | None = None
) -> ObjectInfo: ...
async def delete_object(self, bucket_name: BucketName, key: Key) -> ObjectInfo: ...
async def delete_object(self, bucket_name: BucketName, key: Key) -> None: ...
async def head_object(self, bucket_name: BucketName, key: Key) -> ObjectInfo: ...
async def list_objects(
self,
Expand Down Expand Up @@ -99,22 +100,34 @@ class MyCustomStore(ObjectStore):

## Development

### Tests
### Start API

0. start monitoring pipeline:
```sh
docker compose -f docker-compose-monitoring.yaml up --detach --wait
```

1. start api:
```sh
uv run fastapi dev examples/inmemory.py
```

### Run Tests

#### unit
```bash
uv run pytest tests/unit
```

#### e2e
API should run in the background.
`API` should run in the background.
```bash
export S3_ENDPOINT_URL=http://127.0.0.1:8000
uv run run pytest tests/e2e
```

#### third_party/s3-tests
API should run in the background.
`API` should run in the background.
```bash
cd tests/third_party/s3-tests
export S3TEST_CONF=s3tests.conf
Expand All @@ -123,7 +136,7 @@ uv run tox -- s3tests_boto3/functional/test_s3.py -m boxdrive
See [tests/third_party/s3-tests/boxdrive.md](./tests/third_party/s3-tests/boxdrive.md)
for additional info.

### Code Quality
### Check Code Quality

```bash
uv run ruff format .
Expand Down
38 changes: 38 additions & 0 deletions docker-compose-monitoring.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
networks:
internal:
driver: bridge

services:
clickhouse:
image: clickhouse/clickhouse-server:25.6.3
ports:
- 8123:8123 # HTTP interface
- 9000:9000 # Native interface
environment:
CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1
volumes:
- .volumes/clickhouse_lib:/var/lib/clickhouse
- .volumes/clickhouse_log:/var/log/clickhouse-server
networks:
- internal
healthcheck:
test: wget --no-verbose --tries=1 http://127.0.0.1:8123/ping || exit 1
interval: 10s
timeout: 10s
retries: 3

otel-collector:
image: otel/opentelemetry-collector-contrib:0.129.1
ports:
- 4317:4317 # OTLP gRPC receiver
- 4318:4318 # OTLP HTTP receiver
environment:
CLICKHOUSE_ENDPOINT: tcp://clickhouse:9000?dial_timeout=10s
command: ["--config=/etc/otel-collector-config.yml"]
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yml
networks:
- internal
depends_on:
clickhouse:
condition: service_healthy
2 changes: 1 addition & 1 deletion examples/custom_store.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class MyCustomStore(ObjectStore):
async def put_object(
self, bucket_name: BucketName, key: Key, data: bytes, content_type: ContentType | None = None
) -> ObjectInfo: ...
async def delete_object(self, bucket_name: BucketName, key: Key) -> ObjectInfo: ...
async def delete_object(self, bucket_name: BucketName, key: Key) -> None: ...
async def head_object(self, bucket_name: BucketName, key: Key) -> ObjectInfo: ...
async def list_objects(
self,
Expand Down
16 changes: 16 additions & 0 deletions examples/gitlab.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import os

from boxdrive import create_app
from boxdrive.stores import GitlabStore

repo_id = int(os.environ["REPO_ID"])
branch = os.environ.get("BRANCH", "main")
access_token = os.environ["ACCESS_TOKEN"]

otel_exporter_http_endpoint = os.getenv("OTEL_EXPORTER_HTTP_ENDPOINT")

store = GitlabStore(repo_id, branch, access_token=access_token)
app = create_app(
store,
otel_exporter_http_endpoint=otel_exporter_http_endpoint,
)
9 changes: 8 additions & 1 deletion examples/inmemory.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import os

from boxdrive import create_app
from boxdrive.stores import InMemoryStore

otel_exporter_http_endpoint = os.getenv("OTEL_EXPORTER_HTTP_ENDPOINT")

store = InMemoryStore()
app = create_app(store)
app = create_app(
store,
otel_exporter_http_endpoint=otel_exporter_http_endpoint,
)
2 changes: 2 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
packages = with pkgs; [
git
uv

clickhouse
];
};
};
Expand Down
55 changes: 55 additions & 0 deletions otel-collector-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318

processors:
batch:
timeout: 5s
send_batch_size: 100000

exporters:
clickhouse:
endpoint: ${env:CLICKHOUSE_ENDPOINT}
database: otel
async_insert: true
ttl: 72h
compress: lz4
create_schema: true
logs_table_name: otel_logs
traces_table_name: otel_traces
timeout: 5s
metrics_tables:
gauge:
name: "otel_metrics_gauge"
sum:
name: "otel_metrics_sum"
summary:
name: "otel_metrics_summary"
histogram:
name: "otel_metrics_histogram"
exponential_histogram:
name: "otel_metrics_exp_histogram"
retry_on_failure:
enabled: true
initial_interval: 5s
max_interval: 30s
max_elapsed_time: 300s

service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [clickhouse]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [clickhouse]
logs:
receivers: [otlp]
processors: [batch]
exporters: [clickhouse]
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
[project]
name = "boxdrive"
version = "0.0.2"
version = "0.0.3"
description = "S3-compatible API with Abstract Object Store in Python (FastAPI)."
readme = "README.md"
authors = [{ name = "Alex Severin", email = "severinalexeyv@gmail.com" }]
requires-python = ">=3.10"
requires-python = ">=3.12"
dependencies = [
"fastapi[standard]>=0.115.14",
"pydantic>=2.11.7",
"pydantic-xml>=2.17.2",
"opentelemetry-instrumentation-logging>=0.55b1",
"opentelemetry-instrumentation-fastapi>=0.55b1",
"opentelemetry-sdk>=1.34.1",
"aiorwlock>=1.5.0",
"opentelemetry-exporter-otlp>=1.35.0",
"opentelemetry-instrumentation-httpx>=0.56b0",
]

[dependency-groups]
Expand Down
Loading