Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: sklearnserver integration #11

Merged
merged 15 commits into from
Jun 15, 2023
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
32 changes: 16 additions & 16 deletions sklearnserver/rockcraft.yaml
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
# Based on https://github.com/SeldonIO/seldon-core/tree/master/servers/sklearnserver/sklearnserver
name: sklearnserver
summary: An image for Seldon SKLearn Server
description: |
This image is used as part of the Charmed Kubeflow product. The SKLearn Server serves
models which have been stored as pickles.
version: v1.16.0_1 # version format: <KF-upstream-version>_<Charmed-KF-version>
version: v1.16.0_20.04_1 # <upstream-version>_<base-version>_<Charmed-KF-version>
license: Apache-2.0
base: ubuntu:20.04
run-user: _daemon_
services:
sklearnserver:
override: replace
summary: "sklearnserver service"
startup: enabled
# Yet again, use a subshell to jam conda into a working state. Can't use bashrc, because it immediately
# exits if PS1 isn't set, so no-go from scripts
command: bash -c 'cd /microservice && export PATH=/opt/conda/bin/${PATH} && eval $(/opt/conda/bin/conda shell.bash hook 2> /dev/null) && source /opt/conda/etc/profile.d/conda.sh && conda activate && seldon-core-microservice $MODEL_NAME --service-type $SERVICE_TYPE --persistence $PERSISTENCE'
# exits if PS1 isn't set, so no-go from scripts.
command: bash -c 'cd /microservice && export PATH=/opt/conda/bin/:${PATH} && eval $(/opt/conda/bin/conda shell.bash hook 2> /dev/null) && source /opt/conda/etc/profile.d/conda.sh && conda activate && seldon-core-microservice ${MODEL_NAME} --service-type ${SERVICE_TYPE} &> /tmp/log.txt'
environment:
# the following environment variables are taken from:
# https://github.com/SeldonIO/seldon-core/blob/master/servers/sklearnserver/environment
# NOTE: PERSISTENCE is omitted because it is depricated
MODEL_NAME: "SKLearnServer"
SERVICE_TYPE: "MODEL"
PERSISTENCE: "0"
user: ubuntu

platforms:
amd64:

Expand Down Expand Up @@ -78,15 +80,13 @@ parts:
# but it does need to match pebble's workdir
install -D -m 755 ${CRAFT_STAGE}/microservice/SKLearnServer.py microservice/SKLearnServer.py

security-team-requirement:
plugin: nil
after: [sklearnserver]
override-build: |
# security requirement
# there are no packages installed in `bare` base which is used in this rock
# `--root` option is not available in dpkg-query version which is packaged with 20.04
mkdir -p ${CRAFT_PART_INSTALL}/usr/share/rocks
(echo "# os-release" && cat /etc/os-release && echo "# dpkg-query" && \
dpkg-query -f '${db:Status-Abbrev},${binary:Package},${Version},${source:Package},${Source:Version}\n' -W) \
> ${CRAFT_PART_INSTALL}/usr/share/rocks/dpkg.query

non-root-user:
plugin: nil
overlay-script: |
# Create a user in the $CRAFT_OVERLAY chroot
groupadd -R $CRAFT_OVERLAY -g 1001 ubuntu
useradd -R $CRAFT_OVERLAY -M -r -u 1001 -g ubuntu ubuntu
(echo "# os-release" && cat /etc/os-release && echo "# dpkg-query") \
> ${CRAFT_PART_INSTALL}/usr/share/rocks/dpkg.query
41 changes: 41 additions & 0 deletions sklearnserver/tests/test_rock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright 2022 Canonical Ltd.
# See LICENSE file for licensing details.
#
# Tests for required artifacts to be present in the ROCK image.
#

from charmed_kubeflow_chisme.rock import CheckRock
from pathlib import Path

import os
import logging
import random
import pytest
import string
import subprocess
import yaml
from pytest_operator.plugin import OpsTest

@pytest.fixture()
def rock_test_env():
"""Yields a temporary directory and random docker container name, then cleans them up after."""
container_name = "".join([str(i) for i in random.choices(string.ascii_lowercase, k=8)])
yield container_name

try:
subprocess.run(["docker", "rm", container_name])
except Exception:
pass

@pytest.mark.abort_on_fail
def test_rock(ops_test: OpsTest, rock_test_env):
"""Test rock."""
check_rock = CheckRock("rockcraft.yaml")
container_name = rock_test_env
LOCAL_ROCK_IMAGE = f"{check_rock.get_image_name()}:{check_rock.get_version()}"

# verify that all artifacts are in correct locations
subprocess.run(["docker", "run", LOCAL_ROCK_IMAGE, "exec", "ls", "-la", "/microservice/SKLearnServer.py"], check=True)

# verify that rockcraft.yaml contains correct image name for PREDICTIVE_UNIT_IMAGE environment variable
#assert CheckRock.get_environment()["PREDICTIVE_UNIT_IMAGE"].contains(LOCAL_ROCK_IMAGE)
16 changes: 12 additions & 4 deletions seldon-core-operator/tox.ini → sklearnserver/tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ commands =
VERSION=$(yq eval .version rockcraft.yaml) && \
ARCH=$(yq eval ".platforms | keys" rockcraft.yaml | awk -F " " '\''{ print $2 }'\'') && \
ROCK="$\{NAME\}_$\{VERSION\}_$\{ARCH\}" && \
sudo skopeo --insecure-policy copy oci-archive:$ROCK.rock docker-daemon:$ROCK:$VERSION && \
docker save $ROCK > $ROCK.tar'
sudo skopeo --insecure-policy copy oci-archive:$ROCK.rock docker-daemon:$ROCK:$VERSION'

# run rock tests
pytest -v --tb native --show-capture=all --log-cli-level=INFO {posargs} {toxinidir}/tests

Expand All @@ -43,6 +43,7 @@ allowlist_externals =
rm
tox
rockcraft
sed
deps =
juju~=2.9.0
pytest
Expand All @@ -54,14 +55,21 @@ commands =
# clone related charm
rm -rf {env:LOCAL_CHARM_DIR}
git clone --branch {env:CHARM_BRANCH} {env:CHARM_REPO} {env:LOCAL_CHARM_DIR}
# replace jinja2 templated value with yq safe placeholder
sed -i "s/namespace: {{ namespace }}/namespace: YQ_SAFE/" {env:LOCAL_CHARM_DIR}/src/templates/configmap.yaml.j2
# upload rock to docker and microk8s cache, replace charm's container with local rock reference
bash -c 'NAME=$(yq eval .name rockcraft.yaml) && \
VERSION=$(yq eval .version rockcraft.yaml) && \
ARCH=$(yq eval ".platforms | keys" rockcraft.yaml | awk -F " " '\''{ print $2 }'\'') && \
ROCK="$\{NAME\}_$\{VERSION\}_$\{ARCH\}" && \
sudo skopeo --insecure-policy copy oci-archive:$ROCK.rock docker-daemon:$ROCK:$VERSION && \
docker save $ROCK > $ROCK.tar && \
microk8s ctr image import $ROCK.tar && \
yq e -i ".resources.oci-image.upstream-source=\"$ROCK:$VERSION\"" {env:LOCAL_CHARM_DIR}/metadata.yaml'
microk8s ctr image import $ROCK.tar --digests=true && \
predictor_servers=$(yq e ".data.predictor_servers" {env:LOCAL_CHARM_DIR}/src/templates/configmap.yaml.j2) && \
predictor_servers=$(jq --arg jq_rock $ROCK -r '\''.SKLEARN_SERVER.protocols.seldon.image=$jq_rock'\'' <<< $predictor_servers) && \
predictor_servers=$(jq --arg jq_version $VERSION -r '\''.SKLEARN_SERVER.protocols.seldon.defaultImageVersion=$jq_version'\'' <<< $predictor_servers) yq e -i ".data.predictor_servers=strenv(predictor_servers)" {env:LOCAL_CHARM_DIR}/src/templates/configmap.yaml.j2'
# replace yq safe placeholder with original value
sed -i "s/namespace: YQ_SAFE/namespace: {{ namespace }}/" {env:LOCAL_CHARM_DIR}/src/templates/configmap.yaml.j2
# run charm integration test with rock
tox -c {env:LOCAL_CHARM_DIR} -e integration