Skip to content

Commit e541f7f

Browse files
committed
[FSTORE-1389] get_secrets_api and create_project should be accessible as hopsworks module functions
1 parent 12aa2bb commit e541f7f

File tree

4 files changed

+116
-9
lines changed

4 files changed

+116
-9
lines changed

python/hopsworks/__init__.py

+79-4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
from hopsworks.client.exceptions import RestAPIError, ProjectException
2626
from hopsworks import version, constants, client
2727
from hopsworks.connection import Connection
28+
from hopsworks.core import project_api, secret_api
29+
from hopsworks.decorators import NoHopsworksConnectionError
2830

2931
# Needs to run before import of hsml and hsfs
3032
warnings.filterwarnings(action="ignore", category=UserWarning, module=r".*psycopg2")
@@ -39,7 +41,8 @@
3941
_hw_connection = Connection.connection
4042

4143
_connected_project = None
42-
44+
_secrets_api = None
45+
_project_api = None
4346

4447
def hw_formatwarning(message, category, filename, lineno, line=None):
4548
return "{}: {}\n".format(category.__name__, message)
@@ -110,6 +113,7 @@ def login(
110113
if "REST_ENDPOINT" in os.environ:
111114
_hw_connection = _hw_connection()
112115
_connected_project = _hw_connection.get_project()
116+
_initialize_module_apis()
113117
print("\nLogged in to project, explore it here " + _connected_project.get_url())
114118
return _connected_project
115119

@@ -173,6 +177,7 @@ def login(
173177
"\nLogged in to project, explore it here "
174178
+ _connected_project.get_url()
175179
)
180+
_initialize_module_apis()
176181
return _connected_project
177182
except RestAPIError:
178183
logout()
@@ -200,7 +205,12 @@ def login(
200205
logout()
201206
raise e
202207

203-
print("\nLogged in to project, explore it here " + _connected_project.get_url())
208+
if _connected_project is None:
209+
print("Could not find any project, use hopsworks.create_project('my_project') to create one")
210+
else:
211+
print("\nLogged in to project, explore it here " + _connected_project.get_url())
212+
213+
_initialize_module_apis()
204214
return _connected_project
205215

206216

@@ -246,7 +256,7 @@ def _prompt_project(valid_connection, project):
246256
saas_projects = valid_connection.get_projects()
247257
if project is None:
248258
if len(saas_projects) == 0:
249-
raise ProjectException("Could not find any project")
259+
return None
250260
elif len(saas_projects) == 1:
251261
return saas_projects[0]
252262
else:
@@ -283,7 +293,72 @@ def _prompt_project(valid_connection, project):
283293

284294
def logout():
285295
global _hw_connection
286-
if isinstance(_hw_connection, Connection):
296+
global _project_api
297+
global _secrets_api
298+
299+
if _is_connection_active():
287300
_hw_connection.close()
301+
288302
client.stop()
303+
_project_api = None
304+
_secrets_api = None
289305
_hw_connection = Connection.connection
306+
307+
def _is_connection_active():
308+
global _hw_connection
309+
return isinstance(_hw_connection, Connection)
310+
311+
def _initialize_module_apis():
312+
global _project_api
313+
global _secrets_api
314+
_project_api = project_api.ProjectApi()
315+
_secrets_api = secret_api.SecretsApi()
316+
317+
def create_project(
318+
name: str, description: str = None, feature_store_topic: str = None
319+
):
320+
"""Create a new project.
321+
322+
Example for creating a new project
323+
324+
```python
325+
326+
import hopsworks
327+
328+
hopsworks.login()
329+
330+
hopsworks.create_project("my_hopsworks_project", description="An example Hopsworks project")
331+
332+
```
333+
# Arguments
334+
name: The name of the project.
335+
description: optional description of the project
336+
feature_store_topic: optional feature store topic name
337+
338+
# Returns
339+
`Project`. A project handle object to perform operations on.
340+
"""
341+
global _hw_connection
342+
global _connected_project
343+
344+
if not _is_connection_active():
345+
raise NoHopsworksConnectionError()
346+
347+
new_project = _hw_connection._project_api._create_project(name, description, feature_store_topic)
348+
if _connected_project is None:
349+
_connected_project = new_project
350+
print("Setting {} as the active project".format(_connected_project.name))
351+
return _connected_project
352+
else:
353+
print("You are already using the project {}, to access the new project use hopsworks.login(project='{}')".format(_connected_project.name, new_project.name))
354+
355+
def get_secrets_api():
356+
"""Get the secrets api.
357+
358+
# Returns
359+
`SecretsApi`: The Secrets Api handle
360+
"""
361+
global _secrets_api
362+
if not _is_connection_active():
363+
raise NoHopsworksConnectionError()
364+
return _secrets_api

python/hopsworks/core/secret_api.py

+31-5
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
# limitations under the License.
1515
#
1616

17-
from hopsworks import client, secret
17+
from hopsworks import client, secret, util
1818
from hopsworks.core import project_api
1919
import json
20+
import getpass
21+
from hopsworks.client.exceptions import RestAPIError
2022

2123

2224
class SecretsApi:
@@ -46,7 +48,7 @@ def get_secret(self, name: str, owner: str = None):
4648
"""Get a secret.
4749
4850
# Arguments
49-
name: Name of the project.
51+
name: Name of the secret.
5052
owner: email of the owner for a secret shared with the current project.
5153
# Returns
5254
`Secret`: The Secret object
@@ -69,9 +71,33 @@ def get_secret(self, name: str, owner: str = None):
6971
"shared",
7072
]
7173

72-
return secret.Secret.from_response_json(
73-
_client._send_request("GET", path_params, query_params=query_params)
74-
)[0]
74+
return secret.Secret.from_response_json(_client._send_request("GET", path_params, query_params=query_params))[0]
75+
76+
def get(self, name: str, owner: str = None):
77+
"""Get the secret's value.
78+
If the secret does not exist, it prompts the user to create the secret if the application is running interactively
79+
80+
# Arguments
81+
name: Name of the secret.
82+
owner: email of the owner for a secret shared with the current project.
83+
# Returns
84+
`str`: The secret value
85+
# Raises
86+
`RestAPIError`: If unable to get the secret
87+
"""
88+
try:
89+
return self.get_secret(name=name, owner=owner).value
90+
except RestAPIError as e:
91+
print(e.response.json())
92+
if (
93+
e.response.json().get("errorCode", "") == 160048
94+
and e.response.status_code == 404
95+
and util.is_interactive()
96+
):
97+
secret_input = getpass.getpass(prompt="\nCould not find secret, enter value here to create it: ")
98+
return self.create_secret(name, secret_input).value
99+
else:
100+
raise e
75101

76102
def create_secret(self, name: str, value: str, project: str = None):
77103
"""Create a new secret.

python/hopsworks/project.py

+2
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ def get_feature_store(self, name: str = None):
107107
Defaulting to the project name of default feature store. To get a
108108
shared feature store, the project name of the feature store is required.
109109
110+
# Arguments
111+
name: Project name of the feature store.
110112
# Returns
111113
`hsfs.feature_store.FeatureStore`: The Feature Store API
112114
# Raises

python/hopsworks/util.py

+4
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,7 @@ def get_hostname_replaced_url(sub_path: str):
7979
href = urljoin(client.get_instance()._base_url, sub_path)
8080
url_parsed = client.get_instance().replace_public_host(urlparse(href))
8181
return url_parsed.geturl()
82+
83+
def is_interactive():
84+
import __main__ as main
85+
return not hasattr(main, '__file__')

0 commit comments

Comments
 (0)