Skip to content

Commit 0ce600f

Browse files
committed
Merge hsml client
1 parent 34d91f2 commit 0ce600f

36 files changed

+683
-790
lines changed

python/hopsworks/client/__init__.py

+96
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,25 @@
1717
from typing import Literal, Optional, Union
1818

1919
from hopsworks.client import external, hopsworks
20+
from hopsworks.client.istio import base as istio_base
21+
from hopsworks.client.istio import external as istio_external
22+
from hopsworks.client.istio import internal as istio_internal
23+
from hopsworks.constants import HOSTS
2024

2125

2226
_client = None
2327
_python_version = None
2428

29+
_client_type = None
30+
_saas_connection = None
31+
32+
_istio_client = None
33+
34+
_kserve_installed = None
35+
_serving_resource_limits = None
36+
_serving_num_instances_limits = None
37+
_knative_domain = None
38+
2539

2640
def init(
2741
client_type: Union[Literal["hopsworks"], Literal["external"]],
@@ -37,6 +51,12 @@ def init(
3751
api_key_file: Optional[str] = None,
3852
api_key_value: Optional[str] = None,
3953
) -> None:
54+
global _client_type
55+
_client_type = client_type
56+
57+
global _saas_connection
58+
_saas_connection = host == HOSTS.APP_HOST
59+
4060
global _client
4161
if not _client:
4262
if client_type == "hopsworks":
@@ -64,6 +84,77 @@ def get_instance() -> Union[hopsworks.Client, external.Client]:
6484
raise Exception("Couldn't find client. Try reconnecting to Hopsworks.")
6585

6686

87+
def set_istio_client(host, port, project=None, api_key_value=None):
88+
global _client_type, _istio_client
89+
90+
if not _istio_client:
91+
if _client_type == "internal":
92+
_istio_client = istio_internal.Client(host, port)
93+
elif _client_type == "external":
94+
_istio_client = istio_external.Client(host, port, project, api_key_value)
95+
96+
97+
def get_istio_instance() -> istio_base.Client:
98+
global _istio_client
99+
return _istio_client
100+
101+
102+
def get_client_type() -> str:
103+
global _client_type
104+
return _client_type
105+
106+
107+
def is_saas_connection() -> bool:
108+
global _saas_connection
109+
return _saas_connection
110+
111+
112+
def set_kserve_installed(kserve_installed):
113+
global _kserve_installed
114+
_kserve_installed = kserve_installed
115+
116+
117+
def is_kserve_installed() -> bool:
118+
global _kserve_installed
119+
return _kserve_installed
120+
121+
122+
def set_serving_resource_limits(max_resources):
123+
global _serving_resource_limits
124+
_serving_resource_limits = max_resources
125+
126+
127+
def get_serving_resource_limits():
128+
global _serving_resource_limits
129+
return _serving_resource_limits
130+
131+
132+
def set_serving_num_instances_limits(num_instances_range):
133+
global _serving_num_instances_limits
134+
_serving_num_instances_limits = num_instances_range
135+
136+
137+
def get_serving_num_instances_limits():
138+
global _serving_num_instances_limits
139+
return _serving_num_instances_limits
140+
141+
142+
def is_scale_to_zero_required():
143+
# scale-to-zero is required for KServe deployments if the Hopsworks variable `kube_serving_min_num_instances`
144+
# is set to 0. Other possible values are -1 (unlimited num instances) or >1 num instances.
145+
return get_serving_num_instances_limits()[0] == 0
146+
147+
148+
def get_knative_domain():
149+
global _knative_domain
150+
return _knative_domain
151+
152+
153+
def set_knative_domain(knative_domain):
154+
global _knative_domain
155+
_knative_domain = knative_domain
156+
157+
67158
def get_python_version():
68159
global _python_version
69160
return _python_version
@@ -79,3 +170,8 @@ def stop():
79170
if _client:
80171
_client._close()
81172
_client = None
173+
174+
global _istio_client
175+
if _istio_client:
176+
_istio_client._close()
177+
_istio_client = None

python/hopsworks/client/auth.py

+25
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616

1717
from __future__ import annotations
1818

19+
import os
20+
1921
import requests
22+
from hopsworks.client import exceptions
2023

2124

2225
class BearerAuth(requests.auth.AuthBase):
@@ -50,3 +53,25 @@ def __init__(self, token: str) -> None:
5053
def __call__(self, r: requests.Request) -> requests.Request:
5154
r.headers["X-API-KEY"] = self._token
5255
return r
56+
57+
58+
def get_api_key(api_key_value, api_key_file):
59+
if api_key_value is not None:
60+
return api_key_value
61+
elif api_key_file is not None:
62+
file = None
63+
if os.path.exists(api_key_file):
64+
try:
65+
file = open(api_key_file, mode="r")
66+
return file.read()
67+
finally:
68+
file.close()
69+
else:
70+
raise IOError(
71+
"Could not find api key file on path: {}".format(api_key_file)
72+
)
73+
else:
74+
raise exceptions.ExternalClientError(
75+
"Either api_key_file or api_key_value must be set when connecting to"
76+
" hopsworks from an external environment."
77+
)

python/hopsworks/client/exceptions.py

+21
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,27 @@ class FeatureStoreException(Exception):
9999
"""Generic feature store exception"""
100100

101101

102+
class ModelRegistryException(Exception):
103+
"""Generic model registry exception"""
104+
105+
106+
class ModelServingException(Exception):
107+
"""Generic model serving exception"""
108+
109+
ERROR_CODE_SERVING_NOT_FOUND = 240000
110+
ERROR_CODE_ILLEGAL_ARGUMENT = 240001
111+
ERROR_CODE_DUPLICATED_ENTRY = 240011
112+
113+
ERROR_CODE_DEPLOYMENT_NOT_RUNNING = 250001
114+
115+
116+
class InternalClientError(TypeError):
117+
"""Raised when internal client cannot be initialized due to missing arguments."""
118+
119+
def __init__(self, message):
120+
super().__init__(message)
121+
122+
102123
class ExternalClientError(TypeError):
103124
"""Raised when external client cannot be initialized due to missing arguments."""
104125

python/hopsworks/client/external.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ def __init__(
110110
self._cert_key = None
111111
self._cert_folder_base = os.path.join(cert_folder, host)
112112

113-
if engine == "python":
113+
if not project:
114+
return
115+
elif engine == "python":
114116
credentials = self._materialize_certs(cert_folder, host, project)
115117

116118
self._write_pem_file(credentials["caChain"], self._get_ca_chain_path())
@@ -258,6 +260,10 @@ def _validate_spark_configuration(self, _spark_session):
258260
def _close(self):
259261
"""Closes a client and deletes certificates."""
260262
# TODO: check if the certificate cleanup may break users using hsfs python ingestion
263+
if not self._project_name:
264+
self._connected = False
265+
return
266+
261267
_logger.info("Closing external client and cleaning up certificates.")
262268
if self._cert_folder_base is None:
263269
_logger.debug("No certificates to clean up.")

python/hopsworks/client/istio/base.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
import os
1818
from abc import abstractmethod
1919

20-
from hsml.client import base
21-
from hsml.client.istio.grpc.inference_client import GRPCInferenceServerClient
20+
from hopsworks.client import base
21+
from hopsworks.client.istio.grpc.inference_client import GRPCInferenceServerClient
2222

2323

2424
class Client(base.Client):

python/hopsworks/client/istio/external.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
#
1616

1717
import requests
18-
from hsml.client import auth
19-
from hsml.client.istio import base as istio
18+
from hopsworks.client import auth
19+
from hopsworks.client.istio import base as istio
2020

2121

2222
class Client(istio.Client):

python/hopsworks/client/istio/grpc/inference_client.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
# limitations under the License.
1515

1616
import grpc
17-
from hsml.client.istio.grpc.proto.grpc_predict_v2_pb2_grpc import (
17+
from hopsworks.client.istio.grpc.proto.grpc_predict_v2_pb2_grpc import (
1818
GRPCInferenceServiceStub,
1919
)
20-
from hsml.client.istio.utils.infer_type import InferRequest, InferResponse
20+
from hopsworks.client.istio.utils.infer_type import InferRequest, InferResponse
2121

2222

2323
class GRPCInferenceServerClient:

python/hopsworks/client/istio/grpc/proto/grpc_predict_v2_pb2_grpc.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
1616
"""Client and server classes corresponding to protobuf-defined services."""
1717

18-
import hsml.client.istio.grpc.inference_client as inference_client
19-
import hsml.client.istio.grpc.proto.grpc_predict_v2_pb2 as grpc__predict__v2__pb2
18+
import hopsworks.client.istio.grpc.inference_client as inference_client
19+
import hopsworks.client.istio.grpc.proto.grpc_predict_v2_pb2 as grpc__predict__v2__pb2
2020

2121

2222
class GRPCInferenceServiceStub(object):

python/hopsworks/client/istio/internal.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
from pathlib import Path
2121

2222
import requests
23-
from hsml.client import auth, exceptions
24-
from hsml.client.istio import base as istio
23+
from hopsworks.client import auth, exceptions
24+
from hopsworks.client.istio import base as istio
2525

2626

2727
try:

python/hopsworks/client/istio/utils/infer_type.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@
2121
import numpy
2222
import numpy as np
2323
import pandas as pd
24-
from hsml.client.istio.grpc.errors import InvalidInput
25-
from hsml.client.istio.grpc.proto.grpc_predict_v2_pb2 import (
24+
from hopsworks.client.istio.grpc.errors import InvalidInput
25+
from hopsworks.client.istio.grpc.proto.grpc_predict_v2_pb2 import (
2626
InferTensorContents,
2727
ModelInferRequest,
2828
ModelInferResponse,
2929
)
30-
from hsml.client.istio.utils.numpy_codec import from_np_dtype, to_np_dtype
30+
from hopsworks.client.istio.utils.numpy_codec import from_np_dtype, to_np_dtype
3131

3232

3333
GRPC_CONTENT_DATATYPE_MAPPINGS = {

python/hopsworks/connection.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,8 @@ def connect(self):
400400
self._host, variable_api.VariableApi().get_version("hopsworks")
401401
)
402402
self._model_api = model_api.ModelApi()
403-
self._model_serving_api.load_default_configuration() # istio client, default resources,...
403+
if self._project:
404+
self._model_serving_api.load_default_configuration() # istio client, default resources,...
404405
except (TypeError, ConnectionError):
405406
self._connected = False
406407
raise
@@ -431,7 +432,8 @@ def close(self):
431432
from hsfs import engine as hsfs_engine
432433
from hsml import client as hsml_client
433434

434-
OpenSearchClientSingleton().close()
435+
if self._project:
436+
OpenSearchClientSingleton().close()
435437

436438
try:
437439
hsfs_client.stop()

python/hsfs/client/__init__.py

+26
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,25 @@
1919
base,
2020
exceptions,
2121
external,
22+
get_client_type,
2223
get_instance,
24+
get_istio_instance,
25+
get_knative_domain,
2326
get_python_version,
27+
get_serving_num_instances_limits,
28+
get_serving_resource_limits,
2429
hopsworks,
2530
init,
31+
is_kserve_installed,
32+
is_saas_connection,
33+
is_scale_to_zero_required,
2634
online_store_rest_client,
35+
set_istio_client,
36+
set_knative_domain,
37+
set_kserve_installed,
2738
set_python_version,
39+
set_serving_num_instances_limits,
40+
set_serving_resource_limits,
2841
stop,
2942
)
3043

@@ -34,11 +47,24 @@
3447
base,
3548
exceptions,
3649
external,
50+
get_client_type,
3751
get_instance,
52+
get_istio_instance,
53+
get_knative_domain,
3854
get_python_version,
55+
get_serving_num_instances_limits,
56+
get_serving_resource_limits,
3957
hopsworks,
4058
init,
59+
is_kserve_installed,
60+
is_saas_connection,
61+
is_scale_to_zero_required,
4162
online_store_rest_client,
63+
set_istio_client,
64+
set_knative_domain,
65+
set_kserve_installed,
4266
set_python_version,
67+
set_serving_num_instances_limits,
68+
set_serving_resource_limits,
4369
stop,
4470
]

python/hsfs/client/auth.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,17 @@
1414
# limitations under the License.
1515
#
1616

17-
from hopsworks.client.auth import ApiKeyAuth, BearerAuth, OnlineStoreKeyAuth
17+
from hopsworks.client.auth import (
18+
ApiKeyAuth,
19+
BearerAuth,
20+
OnlineStoreKeyAuth,
21+
get_api_key,
22+
)
1823

1924

20-
__all__ = [ApiKeyAuth, BearerAuth, OnlineStoreKeyAuth]
25+
__all__ = [
26+
ApiKeyAuth,
27+
BearerAuth,
28+
OnlineStoreKeyAuth,
29+
get_api_key,
30+
]

0 commit comments

Comments
 (0)