Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
13 changes: 10 additions & 3 deletions .env.default
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,18 @@ COMPOSE_PROFILES=gpu
#GIRDER_ADMIN_USER=admin
#GIRDER_ADMIN_PASS=letmein

# Celery connection information
# Girder Worker / Celery (Girder 5 — replaces CELERY_BROKER_URL and WORKER_API_URL)
#
#RABBITMQ_DEFAULT_USER=guest
#RABBITMQ_DEFAULT_PASS=guest
#RABBITMQ_DEFAULT_VHOST=default
#CELERY_BROKER_URL=amqp://guest:guest@rabbit/default
#GIRDER_WORKER_BROKER=amqp://guest:guest@rabbit/default
#GIRDER_WORKER_BACKEND=rpc://guest:guest@localhost/
#GIRDER_SETTING_WORKER_API_URL=http://girder:8080/api/v1
#GIRDER_NOTIFICATION_REDIS_URL=redis://redis:6379

# Docker image tag for kitware/viame-web and workers (Girder 5 builds)
#TAG=girder-5

# RabbitMQ User Queue configs
# the user requires administrator permissions in rabbitmq management plugin
Expand Down Expand Up @@ -60,6 +66,7 @@ COMPOSE_PROFILES=gpu
#ACME_EMAIL=changeme@domain.com
#ACME_CA_SERVER=https://acme-v02.api.letsencrypt.org/directory
#LOG_LEVEL=ERROR
#WORKER_API_URL=https://viame.kitware.com/api/v1
# Split or standalone worker: remote Girder API (replaces legacy WORKER_API_URL)
#GIRDER_SETTING_WORKER_API_URL=https://viame.kitware.com/api/v1
#SOCK_PATH=/var/run/docker.sock
WATCHTOWER_API_TOKEN="customtokenstring"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,6 @@ venv.bak/

# Notebooks
notebooks

# Synced at runtime in development containers
server/dive_server/dive_client/
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ DIVE is a web interface for performing data management, video annotation, and ru
* [Server Development Docs](server/README.md)
* [Deployment Overview](https://kitware.github.io/dive/Deployment-Overview/)
* [Running with Docker Compose](https://kitware.github.io/dive/Deployment-Docker-Compose/)
* [Upgrading to Girder 5](https://kitware.github.io/dive/Deployment-Girder-5-Upgrade/)

## Technologies Used

DIVE uses [Girder](https://girder.readthedocs.io/en/stable/) for data management and has a typical girder + girder worker + docker architecture. See docker scripts for additional details.
DIVE uses [Girder](https://girder.readthedocs.io/en/stable/) 5 for data management and has a typical Girder + Girder Worker + Docker architecture (MongoDB, RabbitMQ, and Redis). See [Running with Docker Compose](https://kitware.github.io/dive/Deployment-Docker-Compose/) and [Upgrading to Girder 5](https://kitware.github.io/dive/Deployment-Girder-5-Upgrade/) for deployment details.

* The client application is a standard [@vue/cli](https://cli.vuejs.org/) application.
* The job runner is built on celery and [Girder Worker](https://girder-worker.readthedocs.io/en/latest/). Command-line executables for VIAME and FFmpeg are built inside the worker docker image.
Expand Down
2 changes: 1 addition & 1 deletion client/.env.production
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VUE_APP_STATIC_PATH=./static/viame/
VUE_APP_STATIC_PATH=/
VUE_APP_GTAG=UA-6042509-43
VUE_APP_SENTRY_DSN=https://0ec02775cc734df98134cecf5c91f782@o267860.ingest.sentry.io/5436001
VUE_APP_UPLOAD_CHUNK_SIZE=67108864
5 changes: 5 additions & 0 deletions client/electron.vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ export default defineConfig(({ mode }) => {
secure: false,
ws: true,
},
'/notifications': {
target: apiProxyTarget,
secure: false,
ws: true,
},
},
},
optimizeDeps: {
Expand Down
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
},
"dependencies": {
"@flatten-js/interval-tree": "^1.0.11",
"@girder/components": "^3.2.0",
"@girder/components": "girder/girder_web_components#girder-5-websocket-upgrade",
"@mdi/font": "^6.2.95",
"@sentry/browser": "^5.24.2",
"@sentry/integrations": "^5.24.2",
Expand Down
4 changes: 3 additions & 1 deletion client/platform/web-girder/plugins/girder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import Vue from 'vue';
import Girder, { RestClient } from '@girder/components/src';

Vue.use(Girder);
// Attempt to get the token from the cookies or from the localStorage
const localToken = window.localStorage.getItem('girderToken');
const girderRest = new RestClient({ apiRoot: 'api/v1' });

girderRest.token = localToken || null;
export function useGirderRest() {
return girderRest;
}
Expand Down
4 changes: 3 additions & 1 deletion client/platform/web-girder/views/Jobs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,11 @@ export default defineComponent({
--gpus all \
--ipc host \
--volume "/opt/noaa/viame:/tmp/addons/extracted:ro" \
-e "WORKER_CONCURRENCY=2" \
-e "DIVE_USERNAME=username" \
-e "DIVE_PASSWORD=CHANGEME" \
kitware/viame-worker:latest</pre>
-e "DIVE_API_URL=https://viame.kitware.com/api/v1" \
kitware/viame-worker:girder-5</pre>
</v-card-text>
</v-card>
</v-card>
Expand Down
3 changes: 3 additions & 0 deletions client/platform/web-girder/views/Login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ export default defineComponent({
const { brandData } = useBrand();
const girderRest = useGirderRest();
function onLogin() {
if (girderRest.token) {
window.localStorage.setItem('girderToken', girderRest.token);
}
router.push('/');
}
girderRest.$on('login', onLogin);
Expand Down
63 changes: 32 additions & 31 deletions client/src/notificatonBus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,59 +16,52 @@ export interface GirderNotification {
/**
* Based on Girder Web Components NotificationBus, but simpler.
* Register notifications directly on the girderRest instance using
* the EventSource api.
* the WebSocket api.
*
* @param rc Girder RestClient
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default function registerNotifications(_rc: any) {
const rc: AugmentedRestClient = _rc; // TODO remove after types fixed
const ES = window.EventSource;
const withCredentials = true;
const timeoutSeconds = 300;
const retryMsDefault = 8_000;
let since = new Date();
let lastConnectionAttempt = new Date();
let eventSourceInstance: EventSource | null = null;
let webSocketInstance: WebSocket | null = null;

function connected() {
return !!eventSourceInstance;
return !!webSocketInstance && webSocketInstance.readyState === WebSocket.OPEN;
}

function emitNotification(notification: GirderNotification) {
const { type, updated } = notification;
if (updated) {
since = new Date(Math.max(+since, +new Date(updated)));
}
const { type } = notification;
for (let i = type.indexOf('.'); i !== -1; i = type.indexOf('.', i + 1)) {
rc.$emit(`message:${type.substring(0, i)}`, notification);
}
rc.$emit(`message:${type}`, notification);
rc.$emit('message', notification);
}

function onSseMessage(e: MessageEvent) {
function onWebSocketMessage(e: MessageEvent) {
emitNotification(JSON.parse(e.data));
}

function disconnect() {
if (eventSourceInstance) {
eventSourceInstance.close();
if (webSocketInstance) {
webSocketInstance.close();
}
eventSourceInstance = null;
webSocketInstance = null;
}

function onSseError() {
const nowSeconds = Math.ceil(Date.now() / 1000);
const lastSeconds = Math.ceil(+lastConnectionAttempt / 1000);
let retryMs = retryMsDefault;
/** If time since last success is at least half the timeout, it's probably just a timeout */
if ((nowSeconds - lastSeconds) > (timeoutSeconds * 0.5)) {
retryMs = 0;
}
function onWebSocketError() {
disconnect();
// eslint-disable-next-line @typescript-eslint/no-use-before-define
window.setTimeout(connect, retryMs);
window.setTimeout(connect, retryMsDefault);
}

function onWebSocketClose() {
webSocketInstance = null;
// Attempt to reconnect after a delay
if (rc.user) {
window.setTimeout(connect, retryMsDefault);
}
}

function connect() {
Expand All @@ -78,12 +71,20 @@ export default function registerNotifications(_rc: any) {
if (!rc.user) {
return;
}
lastConnectionAttempt = new Date();
const sinceSeconds = Math.ceil(+since / 1000);
const url = `${rc.apiRoot}/notification/stream?since=${sinceSeconds}&timeout=${timeoutSeconds}`;
eventSourceInstance = new ES(url, { withCredentials });
eventSourceInstance.onmessage = onSseMessage;
eventSourceInstance.onerror = onSseError;
// Get the token from RestClient
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const { token } = rc as any;
if (!token) {
return;
}
// Construct WebSocket URL from current location
const { protocol, host } = window.location;
const wsProtocol = protocol === 'https:' ? 'wss:' : 'ws:';
const url = `${wsProtocol}//${host}/notifications/me?token=${token}`;
webSocketInstance = new WebSocket(url);
webSocketInstance.onmessage = onWebSocketMessage;
webSocketInstance.onerror = onWebSocketError;
webSocketInstance.onclose = onWebSocketClose;
}

rc.$on('login', connect);
Expand Down
8 changes: 7 additions & 1 deletion client/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ export default defineConfig(({ mode }) => {
secure: false,
ws: true,
},
// WebSocket for Girder notifications (not under /api; see notificatonBus.ts).
'/notifications': {
target: apiProxyTarget,
secure: false,
ws: true,
},
},
},
optimizeDeps: {
Expand All @@ -80,7 +86,7 @@ export default defineConfig(({ mode }) => {
},
},
},
base: mode === 'production' ? '/static/viame/' : '/',
base: '/',
test: {
globals: true,
},
Expand Down
8 changes: 7 additions & 1 deletion docker-compose.override.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ services:
volumes:
- ./server:/opt/dive/src
- ./docker/server_setup.py:/server_setup.py
command: ["--dev"]
command: ["--mode", "development"]
environment:
- DEVELOPMENT_MODE=True

girder_worker_pipelines:
volumes:
Expand All @@ -18,3 +20,7 @@ services:
girder_worker_default:
volumes:
- ./server:/opt/dive/src

localworker:
volumes:
- ./server:/opt/dive/src
54 changes: 47 additions & 7 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ x-worker: &base-worker
build:
context: .
dockerfile: docker/girder_worker.Dockerfile
image: kitware/viame-worker:cpu
image: dive/viame-worker:girder-5-cpu
volumes:
- addons:/tmp/addons:ro # readonly
labels:
Expand Down Expand Up @@ -34,7 +34,7 @@ services:
- "8010:80"

rabbit:
image: rabbitmq:3.8.14-management
image: rabbitmq:4.2-management
ports:
- "5672:5672"
- "15672:15672"
Expand All @@ -49,6 +49,31 @@ services:
volumes:
# Use env config path, default to docker volume
- ${MONGO_DB_PATH:-mongo_db}:/data/db
redis:
image: redis:latest
ports:
- "6379:6379"
volumes:
- redis_data:/data

localworker:
build:
context: .
dockerfile: docker/girder.Dockerfile
image: kitware/viame-web:${TAG:-girder-5}
profiles:
- gpu
- cpu
depends_on:
- mongo
entrypoint: celery -A girder_worker.app worker -Q local
environment:
- "GIRDER_WORKER_BROKER=${GIRDER_WORKER_BROKER:-amqp://guest:guest@rabbit/default}"
- "GIRDER_WORKER_BACKEND=${GIRDER_WORKER_BACKEND:-rpc://guest:guest@localhost/}"
- "GIRDER_MONGO_URI=mongodb://mongo:27017/girder"
- "GIRDER_NOTIFICATION_REDIS_URL=redis://redis:6379"
- "C_FORCE_ROOT=1"

# Memcache is used for caching girder-large-images, should auto configure with it being present
memcached:
image: memcached
Expand All @@ -58,7 +83,7 @@ services:
build:
context: .
dockerfile: docker/girder.Dockerfile
image: kitware/viame-web:${TAG:-latest}
image: kitware/viame-web:${TAG:-girder-5}
init: true
command: ["--mode", "production"]
depends_on:
Expand All @@ -76,8 +101,12 @@ services:
- "GIRDER_MONGO_URI=mongodb://mongo:27017/girder"
- "GIRDER_ADMIN_USER=${GIRDER_ADMIN_USER:-admin}"
- "GIRDER_ADMIN_PASS=${GIRDER_ADMIN_PASS:-letmein}"
- "CELERY_BROKER_URL=${CELERY_BROKER_URL:-amqp://guest:guest@rabbit/default}"
- "WORKER_API_URL=${WORKER_API_URL:-http://girder:8080/api/v1}"
- "GIRDER_WORKER_BROKER=${GIRDER_WORKER_BROKER:-amqp://guest:guest@rabbit/default}"
- "GIRDER_WORKER_BACKEND=${GIRDER_WORKER_BACKEND:-rpc://guest:guest@localhost/}"
- "GIRDER_SETTING_WORKER_API_URL=${GIRDER_SETTING_WORKER_API_URL:-http://girder:8080/api/v1}"
- "GIRDER_STATIC_ROOT_DIR=/opt/dive/clients/girder"
- "GIRDER_NOTIFICATION_REDIS_URL=redis://redis:6379"

# Rabbitmq management variables
- "RABBITMQ_MANAGEMENT_USERNAME=${RABBITMQ_MANAGEMENT_USERNAME:-guest}"
- "RABBITMQ_MANAGEMENT_PASSWORD=${RABBITMQ_MANAGEMENT_PASSWORD:-guest}"
Expand Down Expand Up @@ -109,7 +138,9 @@ services:
environment:
- "WORKER_WATCHING_QUEUES=celery"
- "WORKER_CONCURRENCY=${DEFAULT_WORKER_CONCURRENCY:-3}"
- "CELERY_BROKER_URL=${CELERY_BROKER_URL:-amqp://guest:guest@rabbit/default}"
- "GIRDER_WORKER_BROKER=${GIRDER_WORKER_BROKER:-amqp://guest:guest@rabbit/default}"
- "GIRDER_WORKER_BACKEND=${GIRDER_WORKER_BACKEND:-rpc://guest:guest@localhost/}"
- "GIRDER_SETTING_WORKER_API_URL=${GIRDER_SETTING_WORKER_API_URL:-http://girder:8080/api/v1}"

girder_worker_pipelines:
# Merge base-worker object with this config
Expand All @@ -121,7 +152,7 @@ services:
build:
context: .
dockerfile: docker/girder_worker_gpu.Dockerfile
image: kitware/viame-worker:gpu
image: dev/viame-worker:girder-5gpu
restart: always
deploy:
resources:
Expand All @@ -135,7 +166,12 @@ services:
- "WORKER_WATCHING_QUEUES=pipelines"
- "WORKER_CONCURRENCY=${PIPELINE_WORKER_CONCURRENCY:-1}"
- "WORKER_GPU_UUID=${PIPELINE_GPU_UUID}"
- "GIRDER_WORKER_BROKER=${GIRDER_WORKER_BROKER:-amqp://guest:guest@rabbit/default}"
- "CELERY_BROKER_URL=${CELERY_BROKER_URL:-amqp://guest:guest@rabbit/default}"
- "GIRDER_WORKER_BACKEND=${GIRDER_WORKER_BACKEND:-rpc://guest:guest@localhost/}"
- "GIRDER_SETTING_WORKER_API_URL=${GIRDER_SETTING_WORKER_API_URL:-http://girder:8080/api/v1}"
- "GIRDER_MONGO_URI=mongodb://mongo:27017/girder"
- "GIRDER_NOTIFICATION_REDIS_URL=redis://redis:6379"
healthcheck:
test: ["CMD", "nvidia-smi"]
interval: 15m
Expand Down Expand Up @@ -166,7 +202,10 @@ services:
- "WORKER_WATCHING_QUEUES=training"
- "WORKER_CONCURRENCY=${TRAINING_WORKER_CONCURRENCY:-1}"
- "WORKER_GPU_UUID=${TRAINING_GPU_UUID}"
- "GIRDER_WORKER_BROKER=${GIRDER_WORKER_BROKER:-amqp://guest:guest@rabbit/default}"
- "CELERY_BROKER_URL=${CELERY_BROKER_URL:-amqp://guest:guest@rabbit/default}"
- "GIRDER_WORKER_BACKEND=${GIRDER_WORKER_BACKEND:-rpc://guest:guest@localhost/}"
- "GIRDER_SETTING_WORKER_API_URL=${GIRDER_SETTING_WORKER_API_URL:-http://girder:8080/api/v1}"
healthcheck:
test: ["CMD", "nvidia-smi"]
interval: 15m
Expand All @@ -193,3 +232,4 @@ volumes:
addons:
mongo_db:
girder_assetstore:
redis_data:
Loading
Loading