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

request logger metadata lookup #3183

Merged
merged 11 commits into from
May 19, 2021
27 changes: 27 additions & 0 deletions components/seldon-request-logger/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,41 @@ SHELL := /bin/bash
VERSION := $(shell cat ../../version.txt)
REPO := seldonio

.EXPORT_ALL_VARIABLES:
CLIENT_ID=sd-api
[email protected]
OIDC_PASSWORD=xxxxxx
OIDC_SCOPES=openid profile email groups
DEPLOY_API_HOST=http://xx.xx.xx.xx/seldon-deploy/api/v1alpha1
OIDC_PROVIDER=http://xx.xx.xx.xx/auth/realms/deploy-realm
ELASTICSEARCH_HOST=localhost
ELASTICSEARCH_PORT=9200

#to test metadata, set xxs above then run deploy_test_model and create_dummy_metadata
#then run_elastic in another window
#then run_local
#then curl_metadata

deploy_test_model:
./testing/deploy_model.sh

create_dummy_metadata:
python testing/create_dummy_metadata.py

run_container:
docker run -p 2222:8080 seldonio/seldon-request-logger:latest

run_elastic:
docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch-oss:7.6.0

run_local: export FLASK_RUN_PORT = 2222
run_local: export FLASK_APP = default_logger.py
run_local:
cd app && flask run

curl_metadata:
curl -X GET -k -v http://localhost:2222/metadata -H "Content-Type: application/json"

#see test.sh for more test data
test:
curl -v "http://localhost:2222/" \
Expand Down
2 changes: 1 addition & 1 deletion components/seldon-request-logger/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Run the logger:
```
make run_local
```
And in another window run app/test.sh
And in another window run testing/test.sh

The output of the logger will say where docs are indexed. The contents can be checked in postman by querying on the elastic host e.g.
```
Expand Down
25 changes: 25 additions & 0 deletions components/seldon-request-logger/app/default_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
log.setLevel(logging.ERROR)

es = log_helper.connect_elasticsearch()
log_mapping.init_api()


@app.route("/", methods=["GET", "POST"])
Expand Down Expand Up @@ -78,6 +79,30 @@ def index():
return Response("problem logging request", 500)


#below basically proxies to metadata service in deploy for diagnostic purposes
@app.route("/metadata", methods=["GET", "POST"])
def metadata():
try:
print('in metadata endpoint - this is for debugging')
print(request.args)
serving_engine = request.args.get('serving_engine','SeldonDeployment')
if serving_engine is None or not serving_engine:
serving_engine = 'SeldonDeployment'

namespace = request.args.get('namespace','seldon')
name = request.args.get('name','sklearn')
predictor = request.args.get('predictor','default')

print(namespace+'/'+name+'/'+predictor)

metadata = log_mapping.fetch_metadata(namespace=namespace,serving_engine=serving_engine,
inferenceservice_name=name,predictor_name=predictor)
return str(metadata)
except Exception as ex:
print(ex)
sys.stdout.flush()
return Response("problem looking up metadata", 500)

def process_and_update_elastic_doc(
elastic_object, message_type, message_body, request_id, headers, index_name
):
Expand Down
90 changes: 87 additions & 3 deletions components/seldon-request-logger/app/log_mapping.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
import log_helper
import os
import seldon_deploy_sdk
from seldon_deploy_sdk import Configuration, ApiClient,EnvironmentApi,ModelMetadataServiceApi
from seldon_deploy_sdk.auth import OIDCAuthenticator

oidc_server = os.getenv("OIDC_PROVIDER")
oidc_client_id = os.getenv("CLIENT_ID")
oidc_client_secret = os.getenv("CLIENT_SECRET")
oidc_scopes = os.getenv("OIDC_SCOPES")
oidc_username = os.getenv("OIDC_USERNAME")
oidc_password = os.getenv("OIDC_PASSWORD")
oidc_auth_method = os.getenv("OIDC_AUTH_METHOD")
deploy_api_host = os.getenv("DEPLOY_API_HOST")
env_api = None
metadata_api = None

default_mapping = {
"properties": {
Expand Down Expand Up @@ -76,10 +91,11 @@ def get_index_mapping(index_name, upsert_body):
index_mapping = default_mapping.copy()
inferenceservice_name = upsert_body[log_helper.INFERENCESERVICE_HEADER_NAME] if log_helper.INFERENCESERVICE_HEADER_NAME in upsert_body else ""
namespace_name = upsert_body[log_helper.NAMESPACE_HEADER_NAME] if log_helper.NAMESPACE_HEADER_NAME in upsert_body else ""
endpoint_name = upsert_body[log_helper.ENDPOINT_HEADER_NAME] if log_helper.ENDPOINT_HEADER_NAME in upsert_body else ""
serving_engine = upsert_body["ServingEngine"] if "ServingEngine" in upsert_body else "seldon"

metadata = fetch_metadata(
namespace_name, serving_engine, inferenceservice_name)
namespace_name, serving_engine, inferenceservice_name, endpoint_name)
if not metadata:
return index_mapping
else:
Expand All @@ -105,9 +121,77 @@ def get_field_mapping(metadata):
props = update_props_element(props, elm)
return None if not props else {"properties": props}

def init_api():
config = Configuration()
config.host = deploy_api_host
config.oidc_client_id = oidc_client_id
config.oidc_server = oidc_server
config.username = oidc_username
config.password = oidc_password
config.oidc_client_secret = oidc_client_secret
config.auth_method = oidc_auth_method

if not config.auth_method or config.auth_method is None:
config.auth_method = 'password_grant'

if not config.host or config.host is None:
print('No DEPLOY_API_HOST - will not look up metadata from Deploy')
return

if not config.oidc_server or config.oidc_server is None:
print('No OIDC_PROVIDER - auth will not be used in connecting to metadata')
return

auth = None
if config.oidc_server:
auth = OIDCAuthenticator(config)
config.access_token = auth.authenticate()

api_client = ApiClient(configuration=config, authenticator=auth)

env_api = EnvironmentApi(api_client)
print('connected to deploy')
print(env_api.read_user())
global metadata_api
metadata_api = ModelMetadataServiceApi(api_client)


def fetch_metadata(namespace, serving_engine, inferenceservice_name):
# TODO: Fetch real metadata
def fetch_user():
user = env_api.read_user()
return user


def fetch_metadata(namespace, serving_engine, inferenceservice_name, predictor_name):

if metadata_api is None:
print('metadata service not configured')
return None

# TODO: in next iteration will only need one lookup straight to model metadata
# was expcting to set deployment_type=serving_engine but deployment_type does not seem to be a param
runtime_metadata = metadata_api.model_metadata_service_list_runtime_metadata_for_model(
deployment_name=inferenceservice_name,deployment_namespace=namespace,predictor_name=predictor_name)
if runtime_metadata is not None and runtime_metadata.runtime_metadata is not None:
print(runtime_metadata.runtime_metadata)
if len(runtime_metadata.runtime_metadata) == 0:
print('no runtime metadata for '+namespace+'/'+inferenceservice_name)
return None
if len(runtime_metadata.runtime_metadata) > 1:
print('multiple models for '+namespace+'/'+inferenceservice_name+'/'+predictor_name+
' - runtime metadata will not be used')
return None
model_uri = runtime_metadata.runtime_metadata[0].model_uri
SachinVarghese marked this conversation as resolved.
Show resolved Hide resolved
print('model is '+model_uri)
model_metadata = metadata_api.model_metadata_service_list_model_metadata(uri=model_uri)
if model_metadata is None or len(model_metadata.models) == 0:
print('no model corresponding to runtime metadata '+namespace+'/'+inferenceservice_name)
return None

print('prediction schema for '+namespace+'/'+inferenceservice_name)
print(model_metadata.models[0].prediction_schema)
return model_metadata.models[0].prediction_schema
else:
print('no metadata found for '+namespace+' / '+inferenceservice_name+' / '+predictor_name)
return None


Expand Down
7 changes: 4 additions & 3 deletions components/seldon-request-logger/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Flask==1.0.2
numpy==1.14.5
Flask>=1.0.2
numpy>=1.14.5
dict_digger==0.2.1
seldon_core
elasticsearch==7.5.1
elasticsearch==7.12.1
click==8.0.0a1
seldon-deploy-sdk==1.3.0.dev1
61 changes: 61 additions & 0 deletions components/seldon-request-logger/testing/create_dummy_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import time
import seldon_deploy_sdk
import json
from seldon_deploy_sdk import V1Model, ModelMetadataServiceApi, Configuration, ApiClient, EnvironmentApi
from seldon_deploy_sdk.auth import OIDCAuthenticator
from seldon_deploy_sdk.rest import ApiException
import os

config = Configuration()
config.host = os.getenv('DEPLOY_API_HOST')
config.oidc_client_id = os.getenv('CLIENT_ID')
config.oidc_server = os.getenv('OIDC_PROVIDER')
config.username = os.getenv('OIDC_USERNAME')
config.password = os.getenv('OIDC_PASSWORD')
config.auth_method = 'password_grant'
config.scope = os.getenv('OIDC_SCOPES')
#to use client credential set above to client_credentials and uncomment and set config.oidc_client_secret
#config.oidc_client_secret = 'xxxxx'
#note client has to be configured in identity provider for client_credentials

auth = OIDCAuthenticator(config)

config.access_token = auth.authenticate()
print(config.access_token)
api_client = ApiClient(configuration=config,authenticator=auth)
api_instance = ModelMetadataServiceApi(api_client)


models = [
# Same model different versions
{
"uri": "gs://test-model-beta-v2.0.0",
"name": "iris",
"version": "v1.0.0",
"artifact_type": "SKLEARN",
"task_type": "classification",
"tags": {"author": "Jon"},
},
{
"uri": "gs://seldon-models/sklearn/iris",
"name": "iris",
"version": "v2.0.0",
"artifact_type": "SKLEARN",
"task_type": "classification",
"tags": {"author": "Bob"},
},
]

for model in models:
body = V1Model(**model)
try:
env_api = EnvironmentApi(api_client)
print(env_api.read_user())

api_response = api_instance.model_metadata_service_create_model_metadata(body)
print(str(api_response))

metadata_list = api_instance.model_metadata_service_list_model_metadata()
print(str(metadata_list))
except ApiException as e:
print(f"Couldn't create model: {json.loads(e.body)['message']}")
1 change: 1 addition & 0 deletions components/seldon-request-logger/testing/deploy_model.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
kubectl apply -n seldon -f ../../servers/sklearnserver/samples/iris.yaml