Skip to content

Commit

Permalink
object detection recipe and MS tests (#278)
Browse files Browse the repository at this point in the history
* object detection recipe and MS tests

Signed-off-by: greg pereira <[email protected]>
Signed-off-by: Gregory-Pereira <[email protected]>
Signed-off-by: greg pereira <[email protected]>

* adding a object_detection_client workflow file

Signed-off-by: greg pereira <[email protected]>

* addressing michael's comments

Signed-off-by: greg pereira <[email protected]>

* properly name object detection client job + moving pip install to tests

Signed-off-by: greg pereira <[email protected]>

---------

Signed-off-by: greg pereira <[email protected]>
Signed-off-by: Gregory-Pereira <[email protected]>
  • Loading branch information
Gregory-Pereira authored May 14, 2024
1 parent 9107f0c commit bae8b15
Show file tree
Hide file tree
Showing 25 changed files with 354 additions and 21 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/model_servers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ jobs:
directory: whispercpp
platforms: linux/amd64,linux/arm64
no_gpu: 1
- image_name: object_detection_python
model: facebook-detr-resnet-101
flavor: base
directory: object_detection_python
platforms: linux/amd64,linux/arm64
no_gpu: 1
runs-on: ubuntu-22.04
permissions:
contents: read
Expand Down
89 changes: 89 additions & 0 deletions .github/workflows/object_detection.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
name: Object Detection

on:
pull_request:
branches:
- main
paths:
- ./recipes/computer_vision/object_detection/**
- .github/workflows/object_detection.yaml
push:
branches:
- main
paths:
- ./recipes/computer_vision/object_detection/**
- .github/workflows/object_detection.yaml

workflow_dispatch:

env:
REGISTRY: ghcr.io
REGISTRY_ORG: containers
RECIPE_NAME: object_detection
RECIPE_TYPE: computer_vision
IMAGE_NAME: object_detection_client

jobs:
object-detection-client-build-and-push:
if: "!contains(github.event.pull_request.labels.*.name, 'hold-tests')"
runs-on: ubuntu-22.04
permissions:
contents: read
packages: write
services:
registry:
image: registry:2.8.3
ports:
- 5000:5000
steps:
- uses: actions/[email protected]

- name: Install qemu dependency
run: |
sudo apt-get update
sudo apt-get install -y qemu-user-static
- name: Build Image
id: build_image
uses: redhat-actions/[email protected]
with:
image: ${{ env.REGISTRY }}/${{ env.REGISTRY_ORG }}/${{ env.IMAGE_NAME }}
tags: latest
platforms: linux/amd64,linux/arm64
containerfiles: ./recipes/${{ env.RECIPE_TYPE }}/${{ env.RECIPE_NAME }}/app/Containerfile
context: recipes/${{ env.RECIPE_TYPE }}/${{ env.RECIPE_NAME }}/app

- name: Set up Python
uses: actions/[email protected]
with:
python-version: '3.11'

- name: Install Dependencies
working-directory: ./recipes/${{ env.RECIPE_TYPE }}/${{ env.RECIPE_NAME }}
run: make install

- name: Download model
working-directory: ./models
run: make download-model-facebook-detr-resnet-101

- name: Run Functional Tests
shell: bash
run: make functional-tests
working-directory: ./recipes/${{ env.RECIPE_TYPE }}/${{ env.RECIPE_NAME }}

- name: Login to Registry
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: redhat-actions/[email protected]
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Push Image
id: push_image
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: redhat-actions/[email protected]
with:
image: ${{ steps.build_image.outputs.image }}
tags: ${{ steps.build_image.outputs.tags }}
registry: ${{ env.REGISTRY }}
2 changes: 1 addition & 1 deletion model_servers/common/Makefile.common
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ endif

.PHONY: build
build:
podman build --squash-all --build-arg $(PORT) -t $(IMAGE) . -f base/Containerfile
podman build --squash-all --build-arg PORT=$(PORT) -t $(IMAGE) . -f base/Containerfile

.PHONY: install
install:
Expand Down
26 changes: 13 additions & 13 deletions model_servers/object_detection_python/Makefile
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
APP := object_detection_python
PORT ?= 8000

include ../common/Makefile.common
REGISTRY ?= ghcr.io
REGISTRY_ORG ?= containers

IMAGE_NAME ?= $(REGISTRY_ORG)/$(COMPONENT)/$(APP):latest
IMAGE := $(REGISTRY)/$(IMAGE_NAME)
CUDA_IMAGE := $(REGISTRY)/$(REGISTRY_ORG)/$(COMPONENT)/$(APP)_cuda:latest
VULKAN_IMAGE := $(REGISTRY)/$(REGISTRY_ORG)/$(COMPONENT)/$(APP)_vulkan:latest
MODEL_NAME ?= facebook/detr-resnet-101
MODELS_DIR := /app/models

include ../common/Makefile.common

MODEL_NAME ?= facebook/detr-resnet-101
MODELS_DIR := /models
IMAGE_NAME ?= $(REGISTRY_ORG)/$(APP):latest
IMAGE := $(REGISTRY)/$(IMAGE_NAME)

# Run override required because of the multi-directory models and model_path vs models_dir
.PHONY: run
run:
cd ../../models && \
podman run -it -d -p $(PORT):$(PORT) -v ./$(MODEL_NAME):$(MODELS_DIR)/$(MODEL_NAME):$(BIND_MOUNT_OPTIONS) -e MODEL_PATH=$(MODELS_DIR)/$(MODEL_NAME) -e HOST=0.0.0.0 -e PORT=$(PORT) $(IMAGE)


.PHONY: all
all: build download-model-facebook-detr-resnet-101 run

.PHONY: download-model-facebook-detr-resnet-101
download-model-facebook-detr-resnet-101:
cd ../../models/ && \
python download_hf_models.py -m facebook/detr-resnet-101
cd ../../models && \
make download-model-facebook-detr-resnet-101

.PHONY: test
test:
$(MAKE) download-model-facebook-detr-resnet-101
ln -s ../../models/detr-resnet-101 ./
PORT=$(PORT) MODEL_NAME=$(MODEL_NAME) MODELS_PATH=$(MODELS_PATH) IMAGE=$(IMAGE) PULL_ALWAYS=0 pytest -s -vvv
pip install -r ../../convert_models/requirements.txt
cp -r ../../models/facebook ./
REGISTRY=$(REGISTRY) MODEL_NAME=$(MODEL_NAME) MODELS_DIR=$(MODELS_DIR) IMAGE_NAME=$(IMAGE_NAME) PORT=$(PORT) pytest -s -vvv
5 changes: 2 additions & 3 deletions model_servers/object_detection_python/base/Containerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
FROM registry.access.redhat.com/ubi9/python-311:1-62.1714671026
ARG PORT=8000
WORKDIR /app
COPY src/requirements.txt .
COPY src .
RUN pip install --upgrade pip && \
pip install --no-cache-dir --upgrade -r requirements.txt
COPY src/object_detection_server.py .
EXPOSE $PORT
ENTRYPOINT [ "uvicorn", "object_detection_server:app", "--host", "0.0.0.0" ]
ENTRYPOINT [ "sh", "./run.sh" ]
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@


app = FastAPI()
model = os.getenv("MODEL_PATH", default="facebook/detr-resnet-101")
model = os.getenv("MODEL_PATH", default="/app/models/facebook/detr-resnet-101")
revision = os.getenv("MODEL_REVISION", default="no_timm")

if os.path.isfile(model):
Expand All @@ -30,6 +30,10 @@
class Item(BaseModel):
image: bytes

@app.get("/health")
def tests_alive():
return {"alive": True}

@app.post("/detection")
def detection(item: Item):
b64_image = item.image
Expand Down
9 changes: 9 additions & 0 deletions model_servers/object_detection_python/src/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash

if [ ${MODEL_PATH} ]; then
PORT=${PORT} MODEL_PATH=${MODEL_PATH} uvicorn object_detection_server:app --port ${PORT:=8000} --host ${HOST:=0.0.0.0}
exit 0
fi

echo "Please set either a MODEL_PATH"
exit 1
Empty file.
46 changes: 46 additions & 0 deletions model_servers/object_detection_python/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import pytest_container
import os

REGISTRY = os.getenv("REGISTRY", "ghcr.io")
IMAGE_NAME = os.getenv("IMAGE_NAME", "containers/object_detection_python:latest")
MODEL_NAME = os.getenv("MODEL_NAME", "facebook/detr-resnet-101")
MODELS_DIR = os.getenv("MODELS_DIR", "/app/models")

MODEL_PATH = f"{MODELS_DIR}/{MODEL_NAME}"

PORT = os.getenv("PORT", 8000)
if type(PORT) == str:
try:
PORT = int(PORT)
except:
PORT = 8000

MS = pytest_container.Container(
url=f"containers-storage:{REGISTRY}/{IMAGE_NAME}",
volume_mounts=[
pytest_container.container.BindMount(
container_path=f"{MODEL_PATH}",
host_path=f"./{MODEL_NAME}",
flags=["ro"]
)
],
extra_environment_variables={
"MODEL_PATH": f"{MODEL_PATH}",
"HOST": "0.0.0.0",
"PORT": f"{PORT}",
"IMAGE_NAME": f"{IMAGE_NAME}",
"REGISTRY": f"{REGISTRY}"
},
forwarded_ports=[
pytest_container.PortForwarding(
container_port=PORT,
host_port=PORT
)
],
)

def pytest_generate_tests(metafunc):
pytest_container.auto_container_parametrize(metafunc)

def pytest_addoption(parser):
pytest_container.add_logging_level_options(parser)
8 changes: 8 additions & 0 deletions model_servers/object_detection_python/tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
pip==24.0
pytest-container==0.4.0
pytest-selenium==4.1.0
pytest-testinfra==10.1.0
pytest==8.1.1
requests==2.31.0
selenium==4.19.0
tenacity==8.2.3
12 changes: 12 additions & 0 deletions model_servers/object_detection_python/tests/test_alive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import pytest_container
from .conftest import MS
import tenacity

CONTAINER_IMAGES = [MS]

def test_etc_os_release_present(auto_container: pytest_container.container.ContainerData):
assert auto_container.connection.file("/etc/os-release").exists

@tenacity.retry(stop=tenacity.stop_after_attempt(5), wait=tenacity.wait_exponential())
def test_alive(auto_container: pytest_container.container.ContainerData, host):
host.run_expect([0],f"curl http://localhost:{auto_container.forwarded_ports[0].host_port}",).stdout.strip()
7 changes: 7 additions & 0 deletions models/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ download-model-mistral:
download-model-mistral-code:
$(MAKE) MODEL_NAME=mistral-7b-code-16k-qlora.Q4_K_M.gguf MODEL_URL=https://huggingface.co/TheBloke/Mistral-7B-Code-16K-qlora-GGUF/resolve/main/mistral-7b-code-16k-qlora.Q4_K_M.gguf download-model

.PHONY: download-model-facebook-detr-resnet-101
download-model-facebook-detr-resnet-101:
pip install -r ../convert_models/requirements.txt
cd ../convert_models/ && \
python3 download_huggingface.py -m facebook/detr-resnet-101
cp -r ../convert_models/converted_models/facebook ./

.PHONY: clean
clean:
-rm -f *tmp
Expand Down
15 changes: 15 additions & 0 deletions recipes/computer_vision/object_detection/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
SHELL := /bin/bash
APP ?= object_detection_client
PORT ?= 8501
MODEL_NAME ?= facebook/detr-resnet-101

include ../../common/Makefile.common

.PHONY: functional-tests
functional-tests:
IMAGE_NAME=${IMAGE_NAME} REGISTRY=${REGISTRY} MODEL_NAME=${MODEL_NAME} pytest -vvv --driver=Chrome --driver-path=$(RECIPE_BINARIES_PATH)/chromedriver ${RELATIVE_TESTS_PATH}/functional

RECIPE_BINARIES_PATH := $(shell realpath ../../common/bin)
RELATIVE_MODELS_PATH := ../../../models
RELATIVE_TESTS_PATH := ../tests

40 changes: 40 additions & 0 deletions recipes/computer_vision/object_detection/app/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
altair==5.3.0
attrs==23.2.0
blinker==1.7.0
cachetools==5.3.3
certifi==2024.2.2
charset-normalizer==3.3.2
click==8.1.7
gitdb==4.0.11
GitPython==3.1.43
idna==3.7
Jinja2==3.1.3
jsonschema==4.21.1
jsonschema-specifications==2023.12.1
markdown-it-py==3.0.0
MarkupSafe==2.1.5
mdurl==0.1.2
numpy==1.26.4
packaging==24.0
pandas==2.2.2
pillow==10.3.0
protobuf==4.25.3
pyarrow==15.0.2
pydeck==0.8.1b0
Pygments==2.17.2
python-dateutil==2.9.0.post0
pytz==2024.1
referencing==0.34.0
requests==2.31.0
rich==13.7.1
rpds-py==0.18.0
six==1.16.0
smmap==5.0.1
streamlit==1.33.0
tenacity==8.2.3
toml==0.10.2
toolz==0.12.1
tornado==6.4
typing_extensions==4.11.0
tzdata==2024.1
urllib3==2.2.1

This file was deleted.

8 changes: 8 additions & 0 deletions recipes/computer_vision/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import pytest
import os


@pytest.fixture
def chrome_options(chrome_options):
chrome_options.add_argument("--headless")
return chrome_options
Empty file.
Loading

0 comments on commit bae8b15

Please sign in to comment.