Skip to content

Commit

Permalink
Implement editing model servers
Browse files Browse the repository at this point in the history
Signed-off-by: Mark Winter <[email protected]>
  • Loading branch information
markwinter committed Jun 21, 2022
1 parent da68607 commit 9ea3bd0
Show file tree
Hide file tree
Showing 82 changed files with 29,275 additions and 9,187 deletions.
58 changes: 58 additions & 0 deletions .github/workflows/docker-publish-releasing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Models web app Docker Publisher (Releasing)

on:
push:
# Publish `master` as Docker `latest` image.
branches:
- master

paths:
- releasing/VERSION

# Run tests for any PRs.
pull_request:

env:
IMAGE_NAME: models-web-app

jobs:
# Run tests.
# See also https://docs.docker.com/docker-hub/builds/automated-testing/
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Run tests
run: |
docker build . --file Dockerfile
# Push image to GitHub Packages.
# See also https://docs.docker.com/docker-hub/builds/
push:
# Ensure test job passes before pushing image.
needs: test

runs-on: ubuntu-latest
if: github.event_name == 'push'

steps:
- uses: actions/checkout@v2

- name: Build image
run: |
docker build . --file Dockerfile --tag ${IMAGE_NAME}
- name: Log into registry
run: docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASSWORD }}

- name: Push image
run: |
IMAGE_ID=kserve/$IMAGE_NAME
# Change all uppercase to lowercase
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
VERSION=$(cat ./releasing/VERSION)
echo IMAGE_ID=$IMAGE_ID
echo VERSION=$VERSION
docker tag $IMAGE_NAME $IMAGE_ID:$VERSION
docker push $IMAGE_ID:$VERSION
22 changes: 22 additions & 0 deletions .github/workflows/flake-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Python checks

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
flake8-lint:
runs-on: ubuntu-latest
name: Lint
steps:
- name: Check out source repository
uses: actions/checkout@v2
- name: Set up Python environment
uses: actions/setup-python@v1
with:
python-version: "3.7"
- name: flake8 Lint
uses: py-actions/flake8@v1
with:
path: backend
21 changes: 21 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
**/__pycache__/
**/.vscode/
**/static/*
**/fonts/

# Swap
[._]*.s[a-v][a-z]
!*.svg # comment out if you don't need vector files
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]

# Session
Session.vim
Sessionx.vim

# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ RUN apt-get update && apt-get install git -y
WORKDIR /kf
RUN git clone https://github.com/kubeflow/kubeflow.git && \
cd kubeflow && \
git checkout e6fdf51
git checkout d1da825

# --- Build the backend kubeflow-wheel ---
FROM python:3.7-slim-buster AS backend-kubeflow-wheel
Expand All @@ -18,7 +18,7 @@ COPY --from=fetch-kubeflow-kubeflow $BACKEND_LIB .
RUN python setup.py sdist bdist_wheel

# --- Build the frontend kubeflow library ---
FROM node:10 AS frontend-kubeflow-lib
FROM node:12-buster-slim AS frontend-kubeflow-lib

WORKDIR /src

Expand All @@ -30,7 +30,7 @@ COPY --from=fetch-kubeflow-kubeflow $LIB/ ./
RUN npm run build

# --- Build the frontend ---
FROM node:12 AS frontend
FROM node:12-buster-slim AS frontend

WORKDIR /src
COPY ./frontend/package*.json ./
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
IMG ?= gcr.io/kfserving/models-web-app
IMG ?= kserve/models-web-app

# We want the git tag to be the last commit to this directory so we don't
# bump the image on unrelated changes.
Expand Down
1 change: 1 addition & 0 deletions OWNERS
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
approvers:
- kimwnasptd
- yuzisun
reviewers:
- elikatsis
- StefanoFioravanzo
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ The web app currently works with `v1beta1` versions of `InferenceService` object

## Connect to the app

The web app is installed alongside the other KFServing components, either in the `kfserving-system` or in the `kubeflow` namespace. There is a `VirtualService` that exposes the app via an Istio Ingress Gateway. Depending on the installation environment the following Ingress Gateway will be used.
The web app is installed alongside the other KServe components, either in the `kserve` or in the `kubeflow` namespace. There is a `VirtualService` that exposes the app via an Istio Ingress Gateway. Depending on the installation environment the following Ingress Gateway will be used.

| Installation mode | IngressGateway |
| - | - |
| Standalone KFServing | knative-ingress-gateway.knative-serving |
| Standalone KServe | knative-ingress-gateway.knative-serving |
| Kubeflow | kubeflow-gateway.kubeflow |

To access the app you will need to navigate with your browser to
Expand All @@ -27,11 +27,11 @@ You can apply the mentioned configurations by doing the following commands:
```bash
# edit the configmap
# CONFIG=config/overlays/kubeflow/kustomization.yaml
CONFIG=config/web-app/kustomization.yaml
CONFIG=config/base/kustomization.yaml
vim ${CONFIG}

# Add the following env vars to the configMapGenerator's literals
# for kfserving-models-web-app-config
# for kserve-models-web-app-config
- APP_PREFIX=/
- APP_DISABLE_AUTH="True"
- APP_SECURE_COOKIES="False"
Expand Down Expand Up @@ -83,7 +83,7 @@ cd dist/kubeflow
npm link

# run the app frontend
cd $KFSERVING_REPO/web-app/frontend
cd $KSERVE_MODELS_WEB_APP_REPO/frontend
npm i
npm link kubeflow
npm run build:watch
Expand All @@ -95,7 +95,7 @@ npm run build:watch
```bash
# create a virtual env and install deps
# https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/
cd $KFSERVING_REPO/web-app/backend
cd $KSERVE_MODELS_WEB_APP_REPO/backend
python3.7 -m pip install --user virtualenv
python3.7 -m venv web-apps-dev
source web-apps-dev/bin/activate
Expand Down
4 changes: 4 additions & 0 deletions backend/.flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[flake8]
docstring_convention = google
exclude = assets,__init__.py,__pycache__
ignore = D100,D104,D107,W503
2 changes: 2 additions & 0 deletions backend/apps/common/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Package with the base code between backend versions."""
import kubeflow.kubeflow.crud_backend as base
from kubeflow.kubeflow.crud_backend import config, logging

Expand All @@ -8,6 +9,7 @@

def create_app(name=__name__, static_folder="static",
cfg: config.Config = None):
"""Create the WSGI app."""
cfg = config.Config() if cfg is None else cfg

app = base.create_app(name, static_folder, cfg)
Expand Down
1 change: 1 addition & 0 deletions backend/apps/common/routes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Common routes across backends."""
from flask import Blueprint

bp = Blueprint("base_routes", __name__)
Expand Down
2 changes: 2 additions & 0 deletions backend/apps/common/routes/delete.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Route handlers for DELETE requests."""
from kubeflow.kubeflow.crud_backend import api, logging

from .. import versions
Expand All @@ -11,6 +12,7 @@
methods=["DELETE"],
)
def delete_inference_service(inference_service, namespace):
"""Handle DELETE requests and delete the provided InferenceService."""
log.info("Deleting InferenceService %s/%s'", namespace, inference_service)
gvk = versions.inference_service_gvk()
api.delete_custom_rsrc(**gvk,
Expand Down
10 changes: 9 additions & 1 deletion backend/apps/common/routes/get.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""GET request handlers."""
from flask import request

from kubeflow.kubeflow.crud_backend import api, logging
Expand All @@ -10,6 +11,7 @@

@bp.route("/api/namespaces/<namespace>/inferenceservices")
def get_inference_services(namespace):
"""Return a list of InferenceService CRs as json objects."""
gvk = versions.inference_service_gvk()
inference_services = api.list_custom_rsrc(**gvk, namespace=namespace)

Expand All @@ -19,18 +21,20 @@ def get_inference_services(namespace):

@bp.route("/api/namespaces/<namespace>/inferenceservices/<name>")
def get_inference_service(namespace, name):
"""Return an InferenceService CR as a json object."""
inference_service = api.get_custom_rsrc(**versions.inference_service_gvk(),
namespace=namespace, name=name)
if request.args.get("logs", "false") == "true":
# find the logs
return api.success_response(
"serviceLogs", get_inference_service_logs(inference_service)
"serviceLogs", get_inference_service_logs(inference_service),
)

return api.success_response("inferenceService", inference_service)


def get_inference_service_logs(svc):
"""Return all logs for all isvc component pods."""
namespace = svc["metadata"]["namespace"]
components = request.args.getlist("component")

Expand Down Expand Up @@ -58,6 +62,7 @@ def get_inference_service_logs(svc):

@bp.route("/api/namespaces/<namespace>/knativeServices/<name>")
def get_knative_service(namespace, name):
"""Return a Knative Services object as json."""
svc = api.get_custom_rsrc(**versions.KNATIVE_SERVICE, namespace=namespace,
name=name)

Expand All @@ -66,6 +71,7 @@ def get_knative_service(namespace, name):

@bp.route("/api/namespaces/<namespace>/configurations/<name>")
def get_knative_configuration(namespace, name):
"""Return a Knative Configurations object as json."""
svc = api.get_custom_rsrc(**versions.KNATIVE_CONF, namespace=namespace,
name=name)

Expand All @@ -74,6 +80,7 @@ def get_knative_configuration(namespace, name):

@bp.route("/api/namespaces/<namespace>/revisions/<name>")
def get_knative_revision(namespace, name):
"""Return a Knative Revision object as json."""
svc = api.get_custom_rsrc(**versions.KNATIVE_REVISION, namespace=namespace,
name=name)

Expand All @@ -82,6 +89,7 @@ def get_knative_revision(namespace, name):

@bp.route("/api/namespaces/<namespace>/routes/<name>")
def get_knative_route(namespace, name):
"""Return a Knative Route object as json."""
svc = api.get_custom_rsrc(**versions.KNATIVE_ROUTE, namespace=namespace,
name=name)

Expand Down
11 changes: 7 additions & 4 deletions backend/apps/common/utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Common utils for parsing and handling InferenceServices."""
import os

from kubeflow.kubeflow.crud_backend import api, helpers, logging
Expand All @@ -13,17 +14,21 @@

def load_inference_service_template(**kwargs):
"""
kwargs: the parameters to be replaced in the yaml
Return an InferenceService dict, with defaults from the local yaml.
Reads the yaml for the web app's custom resource, replaces the variables
and returns it as a python dict.
kwargs: the parameters to be replaced in the yaml
"""
return helpers.load_param_yaml(INFERENCESERVICE_TEMPLATE_YAML, **kwargs)


# helper functions for accessing the logs of an InferenceService
def get_inference_service_pods(svc, components=[]):
"""
Return the Pod names for the different isvc components.
Return a dictionary with (endpoint, component) keys,
i.e. ("default", "predictor") and a list of pod names as values
"""
Expand Down Expand Up @@ -60,9 +65,7 @@ def get_inference_service_pods(svc, components=[]):
# FIXME(elikatsis,kimwnasptd): Change the logic of this function according to
# https://github.com/arrikto/dev/issues/867
def get_components_revisions_dict(components, svc):
"""
Return a dictionary{revisionId: component}
"""
"""Return a dictionary{revisionId: component}."""
status = svc["status"]
revisions_dict = {}

Expand Down
6 changes: 6 additions & 0 deletions backend/apps/common/versions.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Helpers with GVK needed, stored in one place."""
from flask import current_app

KNATIVE_ROUTE = {"group": "serving.knative.dev",
Expand All @@ -15,6 +16,11 @@


def inference_service_gvk():
"""
Return the GVK needed for an InferenceService.
This also checks the APP_VERSION env var to detect the version.
"""
try:
version = current_app.config["APP_VERSION"]
if version not in ['v1alpha2', 'v1beta1']:
Expand Down
3 changes: 3 additions & 0 deletions backend/apps/v1beta1/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Package for the v1beta1 backend of the web app."""

import os

from kubeflow.kubeflow.crud_backend import config, logging
Expand All @@ -9,6 +11,7 @@


def create_app(name=__name__, cfg: config.Config = None):
"""Create a WSGI app."""
cfg = config.Config() if cfg is None else cfg

# Properly set the static serving directory
Expand Down
3 changes: 2 additions & 1 deletion backend/apps/v1beta1/routes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Include routes of the app."""
from flask import Blueprint

bp = Blueprint("default_routes", __name__)

from . import post # noqa: F401, E402
from . import post, put # noqa: F401, E402
2 changes: 2 additions & 0 deletions backend/apps/v1beta1/routes/post.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""POST routes of the backend."""
from flask import request

from kubeflow.kubeflow.crud_backend import api, decorators, logging
Expand All @@ -12,6 +13,7 @@
@decorators.request_is_json_type
@decorators.required_body_params("apiVersion", "kind", "metadata", "spec")
def post_inference_service(namespace):
"""Handle creation of an InferenceService."""
cr = request.get_json()

gvk = versions.inference_service_gvk()
Expand Down
Loading

0 comments on commit 9ea3bd0

Please sign in to comment.