Skip to content

Commit

Permalink
[FSTORE-1389] get_secrets_api and create_project should be accessible…
Browse files Browse the repository at this point in the history
… as hopsworks module functions
  • Loading branch information
robzor92 committed May 7, 2024
1 parent 12aa2bb commit e541f7f
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 9 deletions.
83 changes: 79 additions & 4 deletions python/hopsworks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
from hopsworks.client.exceptions import RestAPIError, ProjectException
from hopsworks import version, constants, client
from hopsworks.connection import Connection
from hopsworks.core import project_api, secret_api
from hopsworks.decorators import NoHopsworksConnectionError

# Needs to run before import of hsml and hsfs

Check failure on line 31 in python/hopsworks/__init__.py

View workflow job for this annotation

GitHub Actions / Lint and Stylecheck

Ruff (I001)

python/hopsworks/__init__.py:17:1: I001 Import block is un-sorted or un-formatted
warnings.filterwarnings(action="ignore", category=UserWarning, module=r".*psycopg2")
Expand All @@ -39,7 +41,8 @@
_hw_connection = Connection.connection

_connected_project = None

_secrets_api = None
_project_api = None

def hw_formatwarning(message, category, filename, lineno, line=None):
return "{}: {}\n".format(category.__name__, message)
Expand Down Expand Up @@ -110,6 +113,7 @@ def login(
if "REST_ENDPOINT" in os.environ:
_hw_connection = _hw_connection()
_connected_project = _hw_connection.get_project()
_initialize_module_apis()
print("\nLogged in to project, explore it here " + _connected_project.get_url())
return _connected_project

Expand Down Expand Up @@ -173,6 +177,7 @@ def login(
"\nLogged in to project, explore it here "
+ _connected_project.get_url()
)
_initialize_module_apis()
return _connected_project
except RestAPIError:
logout()
Expand Down Expand Up @@ -200,7 +205,12 @@ def login(
logout()
raise e

print("\nLogged in to project, explore it here " + _connected_project.get_url())
if _connected_project is None:
print("Could not find any project, use hopsworks.create_project('my_project') to create one")
else:
print("\nLogged in to project, explore it here " + _connected_project.get_url())

_initialize_module_apis()
return _connected_project


Expand Down Expand Up @@ -246,7 +256,7 @@ def _prompt_project(valid_connection, project):
saas_projects = valid_connection.get_projects()
if project is None:
if len(saas_projects) == 0:
raise ProjectException("Could not find any project")
return None
elif len(saas_projects) == 1:
return saas_projects[0]
else:
Expand Down Expand Up @@ -283,7 +293,72 @@ def _prompt_project(valid_connection, project):

def logout():
global _hw_connection
if isinstance(_hw_connection, Connection):
global _project_api
global _secrets_api

if _is_connection_active():
_hw_connection.close()

client.stop()
_project_api = None
_secrets_api = None
_hw_connection = Connection.connection

def _is_connection_active():
global _hw_connection
return isinstance(_hw_connection, Connection)

def _initialize_module_apis():
global _project_api
global _secrets_api
_project_api = project_api.ProjectApi()
_secrets_api = secret_api.SecretsApi()

def create_project(
name: str, description: str = None, feature_store_topic: str = None
):
"""Create a new project.
Example for creating a new project
```python
import hopsworks
hopsworks.login()
hopsworks.create_project("my_hopsworks_project", description="An example Hopsworks project")
```
# Arguments
name: The name of the project.
description: optional description of the project
feature_store_topic: optional feature store topic name
# Returns
`Project`. A project handle object to perform operations on.
"""
global _hw_connection
global _connected_project

if not _is_connection_active():
raise NoHopsworksConnectionError()

new_project = _hw_connection._project_api._create_project(name, description, feature_store_topic)
if _connected_project is None:
_connected_project = new_project
print("Setting {} as the active project".format(_connected_project.name))
return _connected_project
else:
print("You are already using the project {}, to access the new project use hopsworks.login(project='{}')".format(_connected_project.name, new_project.name))

def get_secrets_api():
"""Get the secrets api.
# Returns
`SecretsApi`: The Secrets Api handle
"""
global _secrets_api
if not _is_connection_active():
raise NoHopsworksConnectionError()
return _secrets_api

Check failure on line 364 in python/hopsworks/__init__.py

View workflow job for this annotation

GitHub Actions / Lint and Stylecheck

Ruff (W292)

python/hopsworks/__init__.py:364:24: W292 No newline at end of file
36 changes: 31 additions & 5 deletions python/hopsworks/core/secret_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
# limitations under the License.
#

from hopsworks import client, secret
from hopsworks import client, secret, util
from hopsworks.core import project_api
import json
import getpass
from hopsworks.client.exceptions import RestAPIError


class SecretsApi:

Check failure on line 24 in python/hopsworks/core/secret_api.py

View workflow job for this annotation

GitHub Actions / Lint and Stylecheck

Ruff (I001)

python/hopsworks/core/secret_api.py:17:1: I001 Import block is un-sorted or un-formatted
Expand Down Expand Up @@ -46,7 +48,7 @@ def get_secret(self, name: str, owner: str = None):
"""Get a secret.
# Arguments
name: Name of the project.
name: Name of the secret.
owner: email of the owner for a secret shared with the current project.
# Returns
`Secret`: The Secret object
Expand All @@ -69,9 +71,33 @@ def get_secret(self, name: str, owner: str = None):
"shared",
]

return secret.Secret.from_response_json(
_client._send_request("GET", path_params, query_params=query_params)
)[0]
return secret.Secret.from_response_json(_client._send_request("GET", path_params, query_params=query_params))[0]

def get(self, name: str, owner: str = None):
"""Get the secret's value.
If the secret does not exist, it prompts the user to create the secret if the application is running interactively
# Arguments
name: Name of the secret.
owner: email of the owner for a secret shared with the current project.
# Returns
`str`: The secret value
# Raises
`RestAPIError`: If unable to get the secret
"""
try:
return self.get_secret(name=name, owner=owner).value
except RestAPIError as e:
print(e.response.json())
if (
e.response.json().get("errorCode", "") == 160048
and e.response.status_code == 404
and util.is_interactive()
):
secret_input = getpass.getpass(prompt="\nCould not find secret, enter value here to create it: ")
return self.create_secret(name, secret_input).value
else:
raise e

def create_secret(self, name: str, value: str, project: str = None):
"""Create a new secret.
Expand Down
2 changes: 2 additions & 0 deletions python/hopsworks/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ def get_feature_store(self, name: str = None):
Defaulting to the project name of default feature store. To get a
shared feature store, the project name of the feature store is required.
# Arguments
name: Project name of the feature store.
# Returns
`hsfs.feature_store.FeatureStore`: The Feature Store API
# Raises
Expand Down
4 changes: 4 additions & 0 deletions python/hopsworks/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,7 @@ def get_hostname_replaced_url(sub_path: str):
href = urljoin(client.get_instance()._base_url, sub_path)
url_parsed = client.get_instance().replace_public_host(urlparse(href))
return url_parsed.geturl()

def is_interactive():
import __main__ as main
return not hasattr(main, '__file__')

Check failure on line 85 in python/hopsworks/util.py

View workflow job for this annotation

GitHub Actions / Lint and Stylecheck

Ruff (W292)

python/hopsworks/util.py:85:41: W292 No newline at end of file

0 comments on commit e541f7f

Please sign in to comment.