Skip to content

Commit 374f682

Browse files
authored
Merge branch 'logicalclocks:main' into dedup-basic-hopsworks
2 parents eccf5ec + 85105f5 commit 374f682

20 files changed

+678
-357
lines changed

Diff for: python/hopsworks/connection.py

+3
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,9 @@ def connect(self):
244244
self._host,
245245
self._port,
246246
self._project,
247+
None,
248+
None,
249+
None,
247250
self._hostname_verification,
248251
self._trust_store_path,
249252
self._cert_folder,

Diff for: python/hopsworks/constants.py

+57-122
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,124 +14,59 @@
1414
# limitations under the License.
1515
#
1616

17-
18-
class JOBS:
19-
SUCCESS_STATES = ["FINISHED", "SUCCEEDED"]
20-
ERROR_STATES = [
21-
"FAILED",
22-
"KILLED",
23-
"FRAMEWORK_FAILURE",
24-
"APP_MASTER_START_FAILED",
25-
"INITIALIZATION_FAILED",
26-
]
27-
28-
29-
class GIT:
30-
SUCCESS_STATES = ["SUCCESS"]
31-
ERROR_STATES = ["FAILED", "KILLED", "INITIALIZATION_FAILED", "TIMEDOUT"]
32-
33-
34-
class SERVICES:
35-
LIST = ["JOBS", "KAFKA", "JUPYTER", "HIVE", "SERVING", "FEATURESTORE", "AIRFLOW"]
36-
37-
38-
class OPENSEARCH_CONFIG:
39-
SSL_CONFIG = "es.net.ssl"
40-
NODES_WAN_ONLY = "es.nodes.wan.only"
41-
NODES = "es.nodes"
42-
SSL_KEYSTORE_LOCATION = "es.net.ssl.keystore.location"
43-
SSL_KEYSTORE_PASSWORD = "es.net.ssl.keystore.pass"
44-
SSL_TRUSTSTORE_LOCATION = "es.net.ssl.truststore.location"
45-
SSL_TRUSTSTORE_PASSWORD = "es.net.ssl.truststore.pass"
46-
HTTP_AUTHORIZATION = "es.net.http.header.Authorization"
47-
INDEX = "es.resource"
48-
HOSTS = "hosts"
49-
HTTP_COMPRESS = "http_compress"
50-
HEADERS = "headers"
51-
USE_SSL = "use_ssl"
52-
VERIFY_CERTS = "verify_certs"
53-
SSL_ASSERT_HOSTNAME = "ssl_assert_hostname"
54-
CA_CERTS = "ca_certs"
55-
56-
57-
class KAFKA_SSL_CONFIG:
58-
"""
59-
Kafka SSL constant strings for configuration
60-
"""
61-
62-
SSL = "SSL"
63-
SSL_TRUSTSTORE_LOCATION_CONFIG = "ssl.truststore.location"
64-
SSL_TRUSTSTORE_LOCATION_DOC = "The location of the trust store file. "
65-
SSL_TRUSTSTORE_PASSWORD_CONFIG = "ssl.truststore.password"
66-
SSL_TRUSTSTORE_PASSWORD_DOC = "The password for the trust store file. If a password is not set access to the truststore is still available, but integrity checking is disabled."
67-
SSL_KEYSTORE_LOCATION_CONFIG = "ssl.keystore.location"
68-
SSL_KEYSTORE_PASSWORD_CONFIG = "ssl.keystore.password"
69-
SSL_KEY_PASSWORD_CONFIG = "ssl.key.password"
70-
SECURITY_PROTOCOL_CONFIG = "security.protocol"
71-
SSL_CERTIFICATE_LOCATION_CONFIG = "ssl.certificate.location"
72-
SSL_CA_LOCATION_CONFIG = "ssl.ca.location"
73-
SSL_PRIVATE_KEY_LOCATION_CONFIG = "ssl.key.location"
74-
SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG = (
75-
"ssl.endpoint.identification.algorithm"
76-
)
77-
78-
79-
class KAFKA_PRODUCER_CONFIG:
80-
"""
81-
Constant strings for Kafka producers
82-
"""
83-
84-
BOOTSTRAP_SERVERS_CONFIG = "bootstrap.servers"
85-
KEY_SERIALIZER_CLASS_CONFIG = "key.serializer"
86-
VALUE_SERIALIZER_CLASS_CONFIG = "value.serializer"
87-
88-
89-
class KAFKA_CONSUMER_CONFIG:
90-
"""
91-
Constant strings for Kafka consumers
92-
"""
93-
94-
GROUP_ID_CONFIG = "group.id"
95-
CLIENT_ID_CONFIG = "client.id"
96-
ENABLE_AUTO_COMMIT_CONFIG = "enable.auto.commit"
97-
AUTO_COMMIT_INTERVAL_MS_CONFIG = "auto.commit.interval.ms"
98-
SESSION_TIMEOUT_MS_CONFIG = "session.timeout.ms"
99-
KEY_DESERIALIZER_CLASS_CONFIG = "key.deserializer"
100-
VALUE_DESERIALIZER_CLASS_CONFIG = "value.deserializer"
101-
AUTO_OFFSET_RESET_CONFIG = "auto.offset.reset"
102-
ENABLE_AUTO_COMMIT_CONFIG = "enable.auto.commit"
103-
KEY_DESERIALIZER_CLASS_CONFIG = "key.deserializer"
104-
VALUE_DESERIALIZER_CLASS_CONFIG = "value.deserializer"
105-
106-
107-
class ENV_VARS:
108-
"""
109-
Constant strings for environment variables
110-
"""
111-
112-
KAFKA_BROKERS = "KAFKA_BROKERS"
113-
ELASTIC_ENDPOINT_ENV_VAR = "ELASTIC_ENDPOINT"
114-
115-
116-
class SSL_CONFIG:
117-
"""
118-
General SSL configuration constants for Hops-TLS
119-
"""
120-
121-
KEYSTORE_SUFFIX = "__kstore.jks"
122-
TRUSTSTORE_SUFFIX = "__tstore.jks"
123-
PASSWORD_SUFFIX = "__cert.key"
124-
125-
K_CERTIFICATE_CONFIG = "k_certificate"
126-
T_CERTIFICATE_CONFIG = "t_certificate"
127-
PEM_CLIENT_CERTIFICATE_CONFIG = "client.pem"
128-
PEM_CLIENT_KEY_CONFIG = "client_key.pem"
129-
PEM_CA_CHAIN_CERTIFICATE_CONFIG = "ca_chain.pem"
130-
DOMAIN_CA_TRUSTSTORE = "domain_ca_truststore"
131-
CRYPTO_MATERIAL_PASSWORD = "material_passwd"
132-
PEM_CA_ROOT_CERT = "/srv/hops/kagent/host-certs/hops_root_ca.pem"
133-
SSL_ENABLED = "ipc.server.ssl.enabled"
134-
135-
136-
class HOSTS:
137-
APP_HOST = "c.app.hopsworks.ai"
17+
from hopsworks_common.constants import (
18+
ARTIFACT_VERSION,
19+
DEFAULT,
20+
DEPLOYABLE_COMPONENT,
21+
DEPLOYMENT,
22+
ENV_VARS,
23+
GIT,
24+
HOSTS,
25+
INFERENCE_BATCHER,
26+
INFERENCE_ENDPOINTS,
27+
INFERENCE_LOGGER,
28+
JOBS,
29+
KAFKA_CONSUMER_CONFIG,
30+
KAFKA_PRODUCER_CONFIG,
31+
KAFKA_SSL_CONFIG,
32+
KAFKA_TOPIC,
33+
MODEL,
34+
MODEL_REGISTRY,
35+
MODEL_SERVING,
36+
OPENSEARCH_CONFIG,
37+
PREDICTOR,
38+
PREDICTOR_STATE,
39+
RESOURCES,
40+
SERVICES,
41+
SSL_CONFIG,
42+
Default,
43+
)
44+
45+
46+
__all__ = [
47+
ARTIFACT_VERSION,
48+
DEFAULT,
49+
DEPLOYABLE_COMPONENT,
50+
DEPLOYMENT,
51+
ENV_VARS,
52+
GIT,
53+
HOSTS,
54+
INFERENCE_BATCHER,
55+
INFERENCE_ENDPOINTS,
56+
INFERENCE_LOGGER,
57+
JOBS,
58+
KAFKA_CONSUMER_CONFIG,
59+
KAFKA_PRODUCER_CONFIG,
60+
KAFKA_SSL_CONFIG,
61+
KAFKA_TOPIC,
62+
MODEL,
63+
MODEL_REGISTRY,
64+
MODEL_SERVING,
65+
OPENSEARCH_CONFIG,
66+
PREDICTOR,
67+
PREDICTOR_STATE,
68+
RESOURCES,
69+
SERVICES,
70+
SSL_CONFIG,
71+
Default,
72+
]

Diff for: python/hopsworks_common/client/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ def init(
5656
api_key_file,
5757
api_key_value,
5858
)
59+
elif isinstance(_client, external.Client) and not _client._project_name:
60+
_client._hsfs_post_init(project, engine, region_name)
5961

6062

6163
def get_instance() -> Union[hopsworks.Client, external.Client]:

Diff for: python/hopsworks_common/client/external.py

+14-9
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,6 @@ def __init__(
6565
self._port = port
6666
self._base_url = "https://" + self._host + ":" + str(self._port)
6767
_logger.info("Base URL: %s", self._base_url)
68-
self._project_name = project
69-
if project is not None:
70-
project_info = self._get_project_info(project)
71-
self._project_id = str(project_info["projectId"])
72-
_logger.debug("Setting Project ID: %s", self._project_id)
73-
else:
74-
self._project_id = None
75-
_logger.debug("Project name: %s", self._project_name)
7668
self._region_name = region_name or self.DEFAULT_REGION
7769
_logger.debug("Region name: %s", self._region_name)
7870

@@ -99,6 +91,19 @@ def __init__(
9991
self._cert_folder_base = cert_folder
10092
self._cert_folder = None
10193

94+
self._hsfs_post_init(project, engine, region_name)
95+
96+
def _hsfs_post_init(self, project, engine, region_name):
97+
self._region_name = region_name or self._region_name or self.DEFAULT_REGION
98+
self._project_name = project
99+
if project is not None:
100+
project_info = self._get_project_info(project)
101+
self._project_id = str(project_info["projectId"])
102+
_logger.debug("Setting Project ID: %s", self._project_id)
103+
else:
104+
self._project_id = None
105+
_logger.debug("Project name: %s", self._project_name)
106+
102107
if project is None:
103108
return
104109

@@ -151,7 +156,7 @@ def __init__(
151156
_spark_session._jsc.hadoopConfiguration().set(conf_key, conf_value)
152157

153158
def download_certs(self, project):
154-
res = self._materialize_certs(self, project)
159+
res = self._materialize_certs(project)
155160
self._write_pem_file(res["caChain"], self._get_ca_chain_path())
156161
self._write_pem_file(res["clientCert"], self._get_client_cert_path())
157162
self._write_pem_file(res["clientKey"], self._get_client_key_path())

Diff for: python/hopsworks_common/client/hopsworks.py

+8-61
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@
1414
# limitations under the License.
1515
#
1616

17-
import base64
1817
import os
19-
import textwrap
2018
from pathlib import Path
2119

2220
import requests
@@ -85,7 +83,14 @@ def _get_trust_store_path(self):
8583
"""Convert truststore from jks to pem and return the location"""
8684
ca_chain_path = Path(self.PEM_CA_CHAIN)
8785
if not ca_chain_path.exists():
88-
self._write_ca_chain(ca_chain_path)
86+
keystore_pw = self._cert_key
87+
ks = jks.KeyStore.load(
88+
self._get_jks_key_store_path(), keystore_pw, try_decrypt_keys=True
89+
)
90+
ts = jks.KeyStore.load(
91+
self._get_jks_trust_store_path(), keystore_pw, try_decrypt_keys=True
92+
)
93+
self._write_ca_chain(ks, ts, ca_chain_path)
8994
return str(ca_chain_path)
9095

9196
def _get_ca_chain_path(self, project_name=None) -> str:
@@ -97,64 +102,6 @@ def _get_client_cert_path(self, project_name=None) -> str:
97102
def _get_client_key_path(self, project_name=None) -> str:
98103
return os.path.join("/tmp", "client_key.pem")
99104

100-
def _write_ca_chain(self, ca_chain_path):
101-
"""
102-
Converts JKS trustore file into PEM to be compatible with Python libraries
103-
"""
104-
keystore_pw = self._cert_key
105-
keystore_ca_cert = self._convert_jks_to_pem(
106-
self._get_jks_key_store_path(), keystore_pw
107-
)
108-
truststore_ca_cert = self._convert_jks_to_pem(
109-
self._get_jks_trust_store_path(), keystore_pw
110-
)
111-
112-
with ca_chain_path.open("w") as f:
113-
f.write(keystore_ca_cert + truststore_ca_cert)
114-
115-
def _convert_jks_to_pem(self, jks_path, keystore_pw):
116-
"""
117-
Converts a keystore JKS that contains client private key,
118-
client certificate and CA certificate that was used to
119-
sign the certificate to PEM format and returns the CA certificate.
120-
Args:
121-
:jks_path: path to the JKS file
122-
:pw: password for decrypting the JKS file
123-
Returns:
124-
strings: (ca_cert)
125-
"""
126-
# load the keystore and decrypt it with password
127-
ks = jks.KeyStore.load(jks_path, keystore_pw, try_decrypt_keys=True)
128-
ca_certs = ""
129-
130-
# Convert CA Certificates into PEM format and append to string
131-
for _alias, c in ks.certs.items():
132-
ca_certs = ca_certs + self._bytes_to_pem_str(c.cert, "CERTIFICATE")
133-
return ca_certs
134-
135-
def _bytes_to_pem_str(self, der_bytes, pem_type):
136-
"""
137-
Utility function for creating PEM files
138-
139-
Args:
140-
der_bytes: DER encoded bytes
141-
pem_type: type of PEM, e.g Certificate, Private key, or RSA private key
142-
143-
Returns:
144-
PEM String for a DER-encoded certificate or private key
145-
"""
146-
pem_str = ""
147-
pem_str = pem_str + "-----BEGIN {}-----".format(pem_type) + "\n"
148-
pem_str = (
149-
pem_str
150-
+ "\r\n".join(
151-
textwrap.wrap(base64.b64encode(der_bytes).decode("ascii"), 64)
152-
)
153-
+ "\n"
154-
)
155-
pem_str = pem_str + "-----END {}-----".format(pem_type) + "\n"
156-
return pem_str
157-
158105
def _get_jks_trust_store_path(self):
159106
"""
160107
Get truststore location

0 commit comments

Comments
 (0)