-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
boa-remote-computing: impl, interfaces, tests
- Loading branch information
Showing
7 changed files
with
172 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
"""addon_imps.remote_computing: imps that implement a "remote_computing"-like interface | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import logging | ||
|
||
from boaapi.boa_client import ( | ||
BOA_API_ENDPOINT, | ||
BoaClient, | ||
BoaException, | ||
) | ||
from django.core.exceptions import ValidationError | ||
|
||
from addon_toolkit.interfaces import remote_computing | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class BoaRemoteComputingImp(remote_computing.RemoteComputingAddonClientRequestorImp): | ||
"""sending compute jobs to Iowa State's Boa cluster.""" | ||
|
||
@classmethod | ||
def confirm_credentials(cls, credentials): | ||
try: | ||
boa_client = cls.create_client(credentials) | ||
boa_client.close() | ||
except BoaException: | ||
raise ValidationError( | ||
"Fail to validate username and password for " | ||
"endpoint:({BOA_API_ENDPOINT})" | ||
) | ||
|
||
@staticmethod | ||
def create_client(credentials): | ||
boa_client = BoaClient(endpoint=BOA_API_ENDPOINT) | ||
boa_client.login(credentials.username, credentials.password) | ||
return boa_client |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import logging | ||
import unittest | ||
from unittest.mock import ( | ||
MagicMock, | ||
patch, | ||
) | ||
|
||
from boaapi.boa_client import ( | ||
BOA_API_ENDPOINT, | ||
BoaException, | ||
) | ||
from django.core.exceptions import ValidationError | ||
|
||
from addon_imps.remote_computing.boa import BoaRemoteComputingImp | ||
from addon_toolkit.credentials import UsernamePasswordCredentials | ||
from addon_toolkit.interfaces.remote_computing import RemoteComputingConfig | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class TestBoaRemoteComputingImp(unittest.IsolatedAsyncioTestCase): | ||
|
||
@patch.object(BoaRemoteComputingImp, "create_client") | ||
def setUp(self, create_client_mock): | ||
self.base_url = BOA_API_ENDPOINT | ||
self.config = RemoteComputingConfig(external_api_url=self.base_url) | ||
self.client = MagicMock() | ||
self.credentials = UsernamePasswordCredentials(username="dog", password="woof") | ||
self.imp = BoaRemoteComputingImp( | ||
config=self.config, credentials=self.credentials | ||
) | ||
self.imp.client = self.client | ||
|
||
@patch.object(BoaRemoteComputingImp, "create_client") | ||
def test_confirm_credentials_success(self, create_client_mock): | ||
creds = UsernamePasswordCredentials(username="dog", password="woof") | ||
self.imp.confirm_credentials(creds) | ||
|
||
create_client_mock.assert_called_once_with(creds) | ||
create_client_mock.return_value.close.assert_called_once_with() | ||
|
||
@patch.object( | ||
BoaRemoteComputingImp, "create_client", side_effect=BoaException("nope") | ||
) | ||
def test_confirm_credentials_fail(self, create_client_mock): | ||
creds = UsernamePasswordCredentials(username="dog", password="woof") | ||
create_client_mock.return_value.side_effect = BoaException("could not login") | ||
with self.assertRaises(ValidationError): | ||
self.imp.confirm_credentials(creds) | ||
|
||
create_client_mock.assert_called_once_with(creds) | ||
|
||
@patch(f"{BoaRemoteComputingImp.__module__}.BoaClient") | ||
def test_create_client(self, create_mock): | ||
mock_login = MagicMock() | ||
create_mock.login.return_value = mock_login | ||
|
||
create_mock.assert_called_once_with(endpoint=BOA_API_ENDPOINT) | ||
mock_login.assert_called_once_with(username="dog", password="woof") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
"""a static (and still in progress) definition of what composes a remote computing addon""" | ||
|
||
import dataclasses | ||
import typing | ||
|
||
from addon_toolkit.constrained_network.http import HttpRequestor | ||
from addon_toolkit.credentials import Credentials | ||
from addon_toolkit.imp import AddonImp | ||
|
||
from ._base import BaseAddonInterface | ||
|
||
|
||
__all__ = ( | ||
"RemoteComputingAddonInterface", | ||
"RemoteComputingAddonImp", | ||
"RemoteComputingConfig", | ||
) | ||
|
||
|
||
### | ||
# dataclasses used for operation args and return values | ||
|
||
|
||
@dataclasses.dataclass(frozen=True) | ||
class RemoteComputingConfig: | ||
external_api_url: str | ||
external_account_id: str | None = None | ||
|
||
|
||
### | ||
# declaration of all remote computing addon operations | ||
|
||
|
||
class RemoteComputingAddonInterface(BaseAddonInterface, typing.Protocol): | ||
|
||
pass | ||
|
||
|
||
@dataclasses.dataclass | ||
class RemoteComputingAddonImp(AddonImp): | ||
"""base class for remote computing addon implementations""" | ||
|
||
ADDON_INTERFACE = RemoteComputingAddonInterface | ||
|
||
config: RemoteComputingConfig | ||
|
||
|
||
@dataclasses.dataclass | ||
class RemoteComputingAddonHttpRequestorImp(RemoteComputingAddonImp): | ||
"""base class for remote computing addon implementations using GV network""" | ||
|
||
network: HttpRequestor | ||
|
||
|
||
@dataclasses.dataclass | ||
class RemoteComputingAddonClientRequestorImp[T](RemoteComputingAddonImp): | ||
"""base class for remote computing addon with custom clients""" | ||
|
||
client: T = dataclasses.field(init=False) | ||
credentials: dataclasses.InitVar[Credentials] | ||
|
||
def __post_init__(self, credentials): | ||
self.client = self.create_client(credentials) | ||
|
||
@staticmethod | ||
def create_client(credentials) -> T: | ||
raise NotImplementedError |