Skip to content

Commit 126818f

Browse files
committedJul 19, 2024
[FSTORE-1453] Move client, decorators, variable_api and constants to hopsworks_common (#229)
* Move client * Move decorators, adapt client * Create aliases for client and decorators * Remove _python_version from client * Merge hsfs client * Move online_store_rest_client * Adapt online_store_rest_client * Create aliases for variable_api * Fix test_online_store_rest_client * Fix __all__ in client/__init__ * Merge hsfs/decorators * Move constants to hopsworks_common * Create alias for constants * Fix mistype in decorators * Move constants to core * Make alias for decorators in hsml * Fix hopsworks_common/client/hopsworks There was an error with incorrect number of parameters passed to _write_ca_chain.
1 parent c679078 commit 126818f

33 files changed

+717
-916
lines changed
 

‎python/hopsworks/client/__init__.py

+25-58
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright 2022 Logical Clocks AB
2+
# Copyright 2024 Hopsworks AB
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License");
55
# you may not use this file except in compliance with the License.
@@ -14,60 +14,27 @@
1414
# limitations under the License.
1515
#
1616

17-
from hopsworks.client import external, hopsworks
18-
19-
20-
_client = None
21-
_python_version = None
22-
23-
24-
def init(
25-
client_type,
26-
host=None,
27-
port=None,
28-
project=None,
29-
hostname_verification=None,
30-
trust_store_path=None,
31-
cert_folder=None,
32-
api_key_file=None,
33-
api_key_value=None,
34-
):
35-
global _client
36-
if not _client:
37-
if client_type == "hopsworks":
38-
_client = hopsworks.Client()
39-
elif client_type == "external":
40-
_client = external.Client(
41-
host,
42-
port,
43-
project,
44-
hostname_verification,
45-
trust_store_path,
46-
cert_folder,
47-
api_key_file,
48-
api_key_value,
49-
)
50-
51-
52-
def get_instance():
53-
global _client
54-
if _client:
55-
return _client
56-
raise Exception("Couldn't find client. Try reconnecting to Hopsworks.")
57-
58-
59-
def get_python_version():
60-
global _python_version
61-
return _python_version
62-
63-
64-
def set_python_version(python_version):
65-
global _python_version
66-
_python_version = python_version
67-
68-
69-
def stop():
70-
global _client
71-
if _client:
72-
_client._close()
73-
_client = None
17+
from hopsworks_common.client import (
18+
auth,
19+
base,
20+
exceptions,
21+
external,
22+
get_instance,
23+
hopsworks,
24+
init,
25+
online_store_rest_client,
26+
stop,
27+
)
28+
29+
30+
__all__ = [
31+
auth,
32+
base,
33+
exceptions,
34+
external,
35+
get_instance,
36+
hopsworks,
37+
init,
38+
online_store_rest_client,
39+
stop,
40+
]

‎python/hopsworks/client/auth.py

+11-22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright 2022 Logical Clocks AB
2+
# Copyright 2024 Hopsworks AB
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License");
55
# you may not use this file except in compliance with the License.
@@ -14,26 +14,15 @@
1414
# limitations under the License.
1515
#
1616

17-
import requests
17+
from hopsworks_common.client.auth import (
18+
ApiKeyAuth,
19+
BearerAuth,
20+
OnlineStoreKeyAuth,
21+
)
1822

1923

20-
class BearerAuth(requests.auth.AuthBase):
21-
"""Class to encapsulate a Bearer token."""
22-
23-
def __init__(self, token):
24-
self._token = token
25-
26-
def __call__(self, r):
27-
r.headers["Authorization"] = "Bearer " + self._token.strip()
28-
return r
29-
30-
31-
class ApiKeyAuth(requests.auth.AuthBase):
32-
"""Class to encapsulate an API key."""
33-
34-
def __init__(self, token):
35-
self._token = token
36-
37-
def __call__(self, r):
38-
r.headers["Authorization"] = "ApiKey " + self._token.strip()
39-
return r
24+
__all__ = [
25+
ApiKeyAuth,
26+
BearerAuth,
27+
OnlineStoreKeyAuth,
28+
]

‎python/hopsworks/client/base.py

+7-168
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright 2022 Logical Clocks AB
2+
# Copyright 2024 Hopsworks AB
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License");
55
# you may not use this file except in compliance with the License.
@@ -14,172 +14,11 @@
1414
# limitations under the License.
1515
#
1616

17-
import os
18-
from abc import ABC, abstractmethod
17+
from hopsworks_common.client.base import (
18+
Client,
19+
)
1920

20-
import furl
21-
import requests
22-
import urllib3
23-
from hopsworks.client import auth, exceptions
24-
from hopsworks.decorators import connected
2521

26-
27-
urllib3.disable_warnings(urllib3.exceptions.SecurityWarning)
28-
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
29-
30-
31-
class Client(ABC):
32-
TOKEN_FILE = "token.jwt"
33-
APIKEY_FILE = "api.key"
34-
REST_ENDPOINT = "REST_ENDPOINT"
35-
HOPSWORKS_PUBLIC_HOST = "HOPSWORKS_PUBLIC_HOST"
36-
37-
@abstractmethod
38-
def __init__(self):
39-
"""To be implemented by clients."""
40-
pass
41-
42-
def _get_verify(self, verify, trust_store_path):
43-
"""Get verification method for sending HTTP requests to Hopsworks.
44-
45-
Credit to https://gist.github.com/gdamjan/55a8b9eec6cf7b771f92021d93b87b2c
46-
47-
:param verify: perform hostname verification, 'true' or 'false'
48-
:type verify: str
49-
:param trust_store_path: path of the truststore locally if it was uploaded manually to
50-
the external environment such as AWS Sagemaker
51-
:type trust_store_path: str
52-
:return: if verify is true and the truststore is provided, then return the trust store location
53-
if verify is true but the truststore wasn't provided, then return true
54-
if verify is false, then return false
55-
:rtype: str or boolean
56-
"""
57-
if verify == "true":
58-
if trust_store_path is not None:
59-
return trust_store_path
60-
else:
61-
return True
62-
63-
return False
64-
65-
def _get_host_port_pair(self):
66-
"""
67-
Removes "http or https" from the rest endpoint and returns a list
68-
[endpoint, port], where endpoint is on the format /path.. without http://
69-
70-
:return: a list [endpoint, port]
71-
:rtype: list
72-
"""
73-
endpoint = self._base_url
74-
if "http" in endpoint:
75-
last_index = endpoint.rfind("/")
76-
endpoint = endpoint[last_index + 1 :]
77-
host, port = endpoint.split(":")
78-
return host, port
79-
80-
def _read_jwt(self):
81-
"""Retrieve jwt from local container."""
82-
return self._read_file(self.TOKEN_FILE)
83-
84-
def _read_apikey(self):
85-
"""Retrieve apikey from local container."""
86-
return self._read_file(self.APIKEY_FILE)
87-
88-
def _read_file(self, secret_file):
89-
"""Retrieve secret from local container."""
90-
with open(os.path.join(self._secrets_dir, secret_file), "r") as secret:
91-
return secret.read()
92-
93-
def _get_credentials(self, project_id):
94-
"""Makes a REST call to hopsworks for getting the project user certificates needed to connect to services such as Hive
95-
96-
:param project_id: id of the project
97-
:type project_id: int
98-
:return: JSON response with credentials
99-
:rtype: dict
100-
"""
101-
return self._send_request("GET", ["project", project_id, "credentials"])
102-
103-
def _write_pem_file(self, content: str, path: str) -> None:
104-
with open(path, "w") as f:
105-
f.write(content)
106-
107-
@connected
108-
def _send_request(
109-
self,
110-
method,
111-
path_params,
112-
query_params=None,
113-
headers=None,
114-
data=None,
115-
stream=False,
116-
files=None,
117-
with_base_path_params=True,
118-
):
119-
"""Send REST request to Hopsworks.
120-
121-
Uses the client it is executed from. Path parameters are url encoded automatically.
122-
123-
:param method: 'GET', 'PUT' or 'POST'
124-
:type method: str
125-
:param path_params: a list of path params to build the query url from starting after
126-
the api resource, for example `["project", 119, "featurestores", 67]`.
127-
:type path_params: list
128-
:param query_params: A dictionary of key/value pairs to be added as query parameters,
129-
defaults to None
130-
:type query_params: dict, optional
131-
:param headers: Additional header information, defaults to None
132-
:type headers: dict, optional
133-
:param data: The payload as a python dictionary to be sent as json, defaults to None
134-
:type data: dict, optional
135-
:param stream: Set if response should be a stream, defaults to False
136-
:type stream: boolean, optional
137-
:param files: dictionary for multipart encoding upload
138-
:type files: dict, optional
139-
:raises RestAPIError: Raised when request wasn't correctly received, understood or accepted
140-
:return: Response json
141-
:rtype: dict
142-
"""
143-
f_url = furl.furl(self._base_url)
144-
if with_base_path_params:
145-
base_path_params = ["hopsworks-api", "api"]
146-
f_url.path.segments = base_path_params + path_params
147-
else:
148-
f_url.path.segments = path_params
149-
url = str(f_url)
150-
151-
request = requests.Request(
152-
method,
153-
url=url,
154-
headers=headers,
155-
data=data,
156-
params=query_params,
157-
auth=self._auth,
158-
files=files,
159-
)
160-
161-
prepped = self._session.prepare_request(request)
162-
response = self._session.send(prepped, verify=self._verify, stream=stream)
163-
164-
if response.status_code == 401 and self.REST_ENDPOINT in os.environ:
165-
# refresh token and retry request - only on hopsworks
166-
self._auth = auth.BearerAuth(self._read_jwt())
167-
# Update request with the new token
168-
request.auth = self._auth
169-
prepped = self._session.prepare_request(request)
170-
response = self._session.send(prepped, verify=self._verify, stream=stream)
171-
172-
if response.status_code // 100 != 2:
173-
raise exceptions.RestAPIError(url, response)
174-
175-
if stream:
176-
return response
177-
else:
178-
# handle different success response codes
179-
if len(response.content) == 0:
180-
return None
181-
return response.json()
182-
183-
def _close(self):
184-
"""Closes a client. Can be implemented for clean up purposes, not mandatory."""
185-
self._connected = False
22+
__all__ = [
23+
Client,
24+
]

0 commit comments

Comments
 (0)
Failed to load comments.