Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
f608f0e
Update README.md
OskarLiew Dec 9, 2021
be6877e
Update README.md
OskarLiew Dec 9, 2021
c9306e6
Adding issue templates
OskarLiew Dec 9, 2021
5cbcc0b
Merge branch 'develop' of github.com:vikinganalytics/daeploy into dev…
OskarLiew Dec 9, 2021
7dc5367
Adding cors (#86)
zanpak May 30, 2022
0404b66
Update pypi-upload.yml
zanpak May 30, 2022
76ea9cb
Updating requirements and fixing models.
zanpak Oct 9, 2024
b312354
Update the docker file
kavehtoyser Apr 16, 2026
c1b6cf2
Merge branch 'develop' into updating-requirements
kavehtoyser Apr 16, 2026
f3ffc1a
Upgrade all dependencies to latest versions with compatibility fixes
kavehtoyser Apr 16, 2026
c7cc8fd
Fix black formatting and pylint warnings from CI
kavehtoyser Apr 16, 2026
8b7675e
Disable new pylint checks introduced by unpinning pylint
kavehtoyser Apr 16, 2026
1565fbf
Fix pytest-manager CI job to use Python 3.12
kavehtoyser Apr 16, 2026
42fb1f8
Add httpx to dev requirements for FastAPI TestClient
kavehtoyser Apr 16, 2026
5dc8246
Fix test compatibility with upgraded dependencies
kavehtoyser Apr 16, 2026
392f288
Fix test compatibility with upgraded dependencies
kavehtoyser Apr 16, 2026
9c6a49d
Fix httpx and Jinja2 compatibility issues
kavehtoyser Apr 16, 2026
f754686
Fix TemplateResponse keyword args for Starlette compatibility
kavehtoyser Apr 16, 2026
b8585bd
Fix SDK tests and drop Python 3.9 from CI matrix
kavehtoyser Apr 16, 2026
0850fec
Replace pkg_resources with importlib.metadata
kavehtoyser Apr 16, 2026
e0fdf0b
Add pytest-timeout to prevent silent test hangs in CI
kavehtoyser Apr 24, 2026
7eeaf0a
Fix Pydantic v2 HttpUrl and Optional field issues in service API
kavehtoyser Apr 24, 2026
fe957ae
Fix test_logs_date_format to check output instead of stdout
kavehtoyser Apr 24, 2026
a5ef6c8
Replace setuptools.sandbox with subprocess in e2e wheel build
kavehtoyser Apr 24, 2026
4ab6c9b
Fix remaining e2e failures: scikit-learn rename and notebook kernel
kavehtoyser May 5, 2026
fc7e936
Bump pinned daeploy in pickle template from 0.4.6 to 1.3.1
kavehtoyser May 5, 2026
15ea802
Add diagnostics to pickle_service fixture
kavehtoyser May 5, 2026
51f4e65
Poll for pickle service container instead of fixed sleep
kavehtoyser May 5, 2026
55fb50f
Poll pickle service endpoint instead of container existence
kavehtoyser May 5, 2026
828963b
Bump pickle service test timeout to 900s
kavehtoyser May 5, 2026
ca97a8f
Add manager-log dump if pickle service polling times out
kavehtoyser May 5, 2026
709dded
Also dump pickle container logs on polling timeout
kavehtoyser May 5, 2026
3f38894
Regenerate pickle_e2e_testing.pkl with current scikit-learn
kavehtoyser May 5, 2026
5f793b3
Train pickle model on numpy arrays to avoid feature-name mismatch
kavehtoyser May 5, 2026
6379051
Print container logs and response body if pickle predict fails
kavehtoyser May 5, 2026
f1aad5f
Convert pickle service predictions to native Python types
kavehtoyser May 5, 2026
0a8d542
Make NetworkSettingsResponse Docker-29-compatible
kavehtoyser Jun 21, 2026
df09143
Join background kill thread in test_logs_stream
kavehtoyser Jun 21, 2026
d2a4be8
Add UI redesign design spec (login, dashboard, logs)
kavehtoyser Jun 21, 2026
e3ad3a0
Add UI redesign implementation plan + committed mockup reference
kavehtoyser Jun 21, 2026
7b3f661
Add UI design tokens, bundled fonts, and local logo mark
kavehtoyser Jun 21, 2026
07bb97d
Reskin login page; serve bundled assets via /assets static mount
kavehtoyser Jun 21, 2026
229221e
Reskin dashboard: token-based styles and control-plane layout
kavehtoyser Jun 21, 2026
6ff91ed
Make dashboard notifications panel live (refresh + clear)
kavehtoyser Jun 21, 2026
150059f
Add streaming logs view with Follow/auto-scroll toggle
kavehtoyser Jun 21, 2026
632d7b6
Serve /assets without auth so the login page can load its styling
kavehtoyser Jun 21, 2026
a38d417
UI redesign: final-review cleanups (live label, real version, dead co…
kavehtoyser Jun 21, 2026
9bb0952
Refactor service-row builder to satisfy pylint (too-many-locals)
kavehtoyser Jun 21, 2026
facd942
Update pinned proxy snapshot for the public /assets router
kavehtoyser Jun 21, 2026
28eccf4
Make InspectResponse GraphDriver optional (Docker 29 containerd store)
kavehtoyser Jun 22, 2026
866f3af
Add streaming manager-logs view (reuses the per-service logs UI)
kavehtoyser Jun 22, 2026
8604df7
Add 1.4.0 CHANGES entry and update Python-version docs
kavehtoyser Jun 23, 2026
77f1412
Merge pull request #90 from vikinganalytics/updating-requirements
kavehtoyser Jun 23, 2026
bf1f2b7
Pin Sphinx < 8 so the docs build (sphinx-multiversion) works
kavehtoyser Jun 23, 2026
95ab93e
Fix docs redirect script for newer packaging (skip non-release dirs)
kavehtoyser Jun 23, 2026
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
36 changes: 36 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''

---

**Checklist**

- [ ] I have searched for, and not found, an issue for this bug

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]

**Additional context**
Add any other context about the problem here.
24 changes: 24 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''

---

**Checklist**

- [ ] I have searched for, and not found, an issue for this feature.

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.
17 changes: 9 additions & 8 deletions .github/workflows/ci-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- name: "Set up Python 3"
uses: actions/setup-python@v2
with:
python-version: '3.8'
python-version: '3.12'
- name: "Install dependencies"
run: |
pip install --upgrade pip
Expand All @@ -38,7 +38,7 @@ jobs:
- name: "Set up Python 3"
uses: actions/setup-python@v2
with:
python-version: '3.8'
python-version: '3.12'
- name: "Install dependencies"
run: "pip install flake8"
- name: "Run flake8!"
Expand All @@ -53,10 +53,10 @@ jobs:
username: ${{ secrets.TEST_DOCKER_USERNAME }}
password: ${{ secrets.TEST_DOCKER_PASSWORD }}
- uses: actions/checkout@v2
- name: "Set up Python 3.8"
- name: "Set up Python 3.12"
uses: actions/setup-python@v2
with:
python-version: "3.8"
with:
python-version: "3.12"
- name: "Install dependencies"
run: |
pip install --upgrade pip
Expand All @@ -79,7 +79,7 @@ jobs:
needs: [black, pylint, flake8, pytest-manager]
strategy:
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
python-version: ["3.10", "3.11", "3.12"]
steps:
- name: Login to Docker Hub
uses: docker/login-action@v1
Expand Down Expand Up @@ -118,12 +118,13 @@ jobs:
- name: "Set up Python 3"
uses: actions/setup-python@v2
with:
python-version: '3.8'
python-version: '3.12'
- name: "Install dependencies"
run: |
pip install --upgrade pip
pip install -r requirements_manager.txt
pip install -r requirements_manager.txt
pip install -r requirements_sdk.txt
pip install -r requirements_dev.txt
python -m ipykernel install --user --name python3
- name: "Running E2E tests with pytest"
run: "python -m pytest --verbose tests/e2e_test/"
4 changes: 2 additions & 2 deletions .github/workflows/pypi-upload.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
pip install setuptools wheel twine packaging
- name: Build and publish to pypi.org
env:
TWINE_USERNAME: ${{ secrets.GLOBAL_PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.GLOBAL_PYPI_PASSWORD }}
DAEPLOY_RELEASE_VERSION: ${{ github.event.release.tag_name }}
run: |
python setup.py bdist_wheel
twine upload dist/*
twine upload dist/*
17 changes: 12 additions & 5 deletions .pylintrc
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
[pylint]
disable =
disable =
R0801,
C0330,
C0326,
no-self-argument,
no-name-in-module,
too-few-public-methods,
too-many-arguments,
too-many-positional-arguments,
logging-fstring-interpolation,
fixme,
missing-module-docstring,
missing-function-docstring,
missing-class-docstring,
raise-missing-from,
unsubscriptable-object # TODO: Only required in python 3.9
unsubscriptable-object,
consider-using-with,
use-dict-literal,
missing-timeout,
unspecified-encoding,
useless-option-value,
invalid-name,
import-error

max-line-length = 88
max-line-length = 88
ignored-modules = IPython
18 changes: 18 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@

All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/).

## 1.4.0

### New Features

- Redesigned web UI: a modern dark login, dashboard, and a streaming logs view with a Follow / auto-scroll toggle (for both service and manager logs). Fully self-contained — no external CDNs or hot-linked assets.

### Changed

- Modernized the manager and SDK dependency stack and moved to Python 3.12.

### Bugfixes

- Docker Engine 29 compatibility: `daeploy ls`, the dashboard, and service inspection no longer return HTTP 500 on hosts using the newer Docker storage drivers.

### Breaking

- Dropped Python 3.9. Building services requires the Python 3.12 `daeploy/s2i-python` builder image (release 0.1.3).

## 1.3.0

Daeploy goes Open Source and free to use for any purpose!
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
## Stage 1: Build image
FROM python:3.8 AS build-image
FROM python:3.12 AS build-image

# Install S2i
RUN wget -c https://github.com/openshift/source-to-image/releases/download/v1.3.0/source-to-image-v1.3.0-eed2850f-linux-amd64.tar.gz \
Expand All @@ -20,7 +20,7 @@ COPY ./requirements_manager.txt .
RUN pip install -r requirements_manager.txt

## Stage 2: Production image
FROM python:3.8-slim AS production-image
FROM python:3.12-slim AS production-image

# Install Git
RUN apt-get update && apt-get install -y git
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ docker run -v /var/run/docker.sock:/var/run/docker.sock -p 80:80 -p 443:443 -e D

The application will be available according to the following:

- Docker container listens on ports 80 (http) and 433 (https)
- Proxy listens on ports 5080 (http) (and 5443 (https), NOT fully implemented yet)
- Docker container listens on ports 80 (http) and 443 (https)
- Proxy listens on ports 5080 (http) and 5443 (https)
- Manager is available at ```/``` (or locally on port 8000)
- Any started services are available at ```/services/{service_name}/``` (or locally on ports 8001 and upwards)
- Proxy built-in dashboard is available at ```/proxy/dashboard/```
Expand Down Expand Up @@ -133,4 +133,4 @@ Can be found here: [Scope Statement](https://vikinganalytics.sharepoint.com/site

## Architecture Overview

![Architecture Overview](docs/source/content/img/daeploy_architecture.jpg)
![Architecture Overview](docs/source/content/img/daeploy_architecture.jpg)
32 changes: 18 additions & 14 deletions daeploy/_service/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
from contextlib import contextmanager
import json

from sqlalchemy import create_engine, and_
from sqlalchemy import create_engine, and_, MetaData
from sqlalchemy.ext.automap import automap_base
from sqlalchemy.orm import sessionmaker, mapper, clear_mappers
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy import Column, DateTime, Float, Text

from daeploy.utilities import get_db_table_limit
Expand All @@ -20,7 +20,7 @@
SERVICE_DB_PATH = Path("service_db.db")

ENGINE = create_engine(f"sqlite:///{str(SERVICE_DB_PATH)}")
Base = automap_base()
Base = declarative_base()
Session = sessionmaker(bind=ENGINE)

QUEUE = queue.Queue()
Expand Down Expand Up @@ -69,9 +69,6 @@ def create_new_ts_table(name: str, dtype: Type) -> Type:
# Create the actual table
MapperClass.__table__.create(ENGINE, checkfirst=True)

# Map everything
mapper(MapperClass, MapperClass.__table__)

LOGGER.info(f"Created new table for variable {name}")

return MapperClass
Expand Down Expand Up @@ -221,16 +218,17 @@ def initialize_db():
global QUEUE
QUEUE = queue.Queue()
global TABLES
Base.prepare(ENGINE, reflect=True) # Automap any existing tables
TABLES = dict(Base.classes) # Make sure we keep track of the auto-mapped tables
# Reflect any existing tables using automap
AutoBase = automap_base(metadata=MetaData())
AutoBase.prepare(autoload_with=ENGINE)
TABLES = dict(AutoBase.classes)
WRITER_THREAD.start()
LOGGER.info("DB started!")


def remove_db():
"""Remove db"""
global WRITER_THREAD
global QUEUE
global WRITER_THREAD, Base

# Stop and join writer thread if alive
if WRITER_THREAD.is_alive():
Expand All @@ -240,10 +238,16 @@ def remove_db():
# Reset it
WRITER_THREAD = threading.Thread(target=_writer, daemon=True)

# Reset tables tracking
TABLES.clear()

# Remove db
SERVICE_DB_PATH.unlink()
ENGINE.dispose()
try:
SERVICE_DB_PATH.unlink()
except FileNotFoundError:
pass

# Reset mappers and metadata object
clear_mappers()
Base.metadata.clear()
# Reset base so new tables get fresh mappers
Base = declarative_base()
LOGGER.info("DB has been shut down!")
34 changes: 30 additions & 4 deletions daeploy/_service/service.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
import asyncio
import datetime
import functools
Expand All @@ -13,7 +14,8 @@
from fastapi import Body, Request, FastAPI
from fastapi.encoders import jsonable_encoder
from fastapi.concurrency import run_in_threadpool
from pydantic import create_model, validate_arguments
from fastapi.middleware.cors import CORSMiddleware
from pydantic import create_model, validate_call

from daeploy._service.logger import setup_logging
from daeploy._service.db import clean_database, initialize_db, remove_db, write_to_ts
Expand All @@ -31,7 +33,6 @@
)
from daeploy.communication import notify, Severity


setup_logging()
logger = logging.getLogger(__name__)

Expand All @@ -45,8 +46,29 @@ def _disable_http_logs(path: str):
)


def cors_allowed_origins():
"""assumes allowed origin are passed as a single string separated by ;
Example 'https://origin1.com;https://orogin2.com'

Returns:
list: url of allowed origins
"""
return os.environ.get("DAEPLOY_ALLOW_ORIGIN", "").split(";")


def get_cors_config():
cors_config = {}
cors_config["allow_credentials"] = False
cors_config["allow_origins"] = cors_allowed_origins()
cors_config["allow_methods"] = ["GET", "POST", "PUT", "DELETE"]
cors_config["allow_headers"] = ["Authorization"]
return cors_config


class _Service:
def __init__(self):
cors_config = get_cors_config()

self.app = FastAPI(
root_path=get_service_root_path(),
title=f"{get_service_name()} {get_service_version()}",
Expand All @@ -58,6 +80,10 @@ def __init__(self):
],
)

# Custom Middleware
if bool(os.environ.get("DAEPLOY_ENABLE_CORS")):
self.app.add_middleware(CORSMiddleware, **cors_config)

self.parameters = {}

# daeploy-specific setup
Expand Down Expand Up @@ -192,7 +218,7 @@ async def wrapper(_request: Request, *args, **kwargs):
_disable_http_logs(path)

# Wrap the original func in a pydantic validation wrapper and return that
return validate_arguments(deco_func)
return validate_call(deco_func)

# This ensures that we can use the decorator with or without arguments
if not (callable(func) or func is None):
Expand Down Expand Up @@ -343,7 +369,7 @@ def add_parameter(
if isinstance(value, Number):
value = float(value)

@validate_arguments()
@validate_call()
def update_parameter(value: value.__class__) -> Any:
logger.info(f"Parameter {parameter} changed to {value}")
self.parameters[parameter]["value"] = value
Expand Down
Loading
Loading