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
1 change: 1 addition & 0 deletions .github/workflows/hosting-runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
- k8s/local-hosting/**
- observability.py
- main.py
- utils/model_download.py
- .github/workflows/hosting-runtime.yml
workflow_dispatch: {}

Expand Down
16 changes: 16 additions & 0 deletions .github/workflows/model-install-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Model installation regression
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
model-install:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: python -m unittest discover -s tests -p test_model_download.py -v
1 change: 1 addition & 0 deletions k8s/local-hosting/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ RUN python -c 'from backends.onnx.backend import ONNXBackend; from PIL import Im

RUN python -m pip install --no-cache-dir sentry-sdk==2.73.0
COPY observability.py main.py /app/
COPY utils/model_download.py /app/utils/model_download.py
ARG SENTRY_RELEASE
ENV SENTRY_DSN=https://8bd44d815901211012fcae52d51371f9@o4512337837686784.ingest.us.sentry.io/4512337894703104 \
SENTRY_ENVIRONMENT=production \
Expand Down
37 changes: 37 additions & 0 deletions tests/test_model_download.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Exercise installation across actual filesystem boundaries, without a network."""
import io
import tempfile
import unittest
import zipfile
from pathlib import Path
from unittest.mock import patch
from utils.model_download import ensure_tflite_ssd_mobilenet_v1, ModelDownloadError


class ModelInstallTest(unittest.TestCase):
def archive(self):
data = io.BytesIO()
with zipfile.ZipFile(data, 'w') as z:
z.writestr('detect.tflite', b'model-fixture')
return data.getvalue()

def test_cross_filesystem_install(self):
if not Path('/dev/shm').is_dir():
self.skipTest('requires a separate writable tmpfs')
with tempfile.TemporaryDirectory(dir='/dev/shm') as directory:
root = Path(directory)
self.assertNotEqual(root.stat().st_dev, Path(tempfile.gettempdir()).stat().st_dev)
with patch('urllib.request.urlopen', return_value=io.BytesIO(self.archive())):
ensure_tflite_ssd_mobilenet_v1(model_path=str(root/'model'), labels_path=str(root/'labels'))
self.assertEqual((root/'model').read_bytes(), b'model-fixture')
self.assertFalse(list(root.glob('.light-object-detect-*')))

def test_failed_refresh_keeps_existing_model(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
(root/'model').write_bytes(b'known-good')
with patch('urllib.request.urlopen', side_effect=OSError('interrupted')):
with self.assertRaises(ModelDownloadError):
ensure_tflite_ssd_mobilenet_v1(model_path=str(root/'model'), labels_path=str(root/'labels'), force=True)
self.assertEqual((root/'model').read_bytes(), b'known-good')
self.assertFalse(list(root.glob('.light-object-detect-*')))
4 changes: 2 additions & 2 deletions utils/model_download.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def ensure_tflite_ssd_mobilenet_v1(
model_file.parent.mkdir(parents=True, exist_ok=True)

try:
with tempfile.TemporaryDirectory(prefix="light-object-detect-") as tmpdir:
with tempfile.TemporaryDirectory(prefix=".light-object-detect-", dir=model_file.parent) as tmpdir:
tmp_dir = Path(tmpdir)
zip_dest = tmp_dir / "model.zip"

Expand All @@ -169,7 +169,7 @@ def ensure_tflite_ssd_mobilenet_v1(
if not extracted_path.exists():
raise ModelDownloadError("Extraction succeeded but file is missing on disk.")

# Atomic-ish replace
# Staging and destination share a filesystem, so replacement is atomic.
tmp_model = tmp_dir / "detect.tmp.tflite"
tmp_model.write_bytes(extracted_path.read_bytes())
tmp_model.replace(model_file)
Expand Down
Loading