Skip to content

Commit

Permalink
Raise dedicated NotSupportedError for unsupported REST API calls
Browse files Browse the repository at this point in the history
  • Loading branch information
asvetlov committed Nov 1, 2021
1 parent 666679f commit eaeb266
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 16 deletions.
4 changes: 4 additions & 0 deletions neuro-cli/src/neuro_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,10 @@ def main(args: Optional[List[str]] = None) -> None:
log.exception(f"Docker API error: {error.message}")
sys.exit(EX_PROTOCOL)

except neuro_sdk.NotSupportedError as error:
log.exception(f"{_err_to_str(error)}")
sys.exit(EX_SOFTWARE)

except NotImplementedError as error:
log.exception(f"{_err_to_str(error)}")
sys.exit(EX_SOFTWARE)
Expand Down
2 changes: 2 additions & 0 deletions neuro-sdk/src/neuro_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
ConfigError,
IllegalArgumentError,
NDJSONError,
NotSupportedError,
ResourceNotFound,
ServerNotAvailable,
)
Expand Down Expand Up @@ -138,6 +139,7 @@
"Jobs",
"LocalImage",
"NDJSONError",
"NotSupportedError",
"PASS_CONFIG_ENV_NAME",
"Parser",
"Permission",
Expand Down
31 changes: 18 additions & 13 deletions neuro-sdk/src/neuro_sdk/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
from typing import Any, Dict, List, Mapping, Optional

from dateutil.parser import isoparse
from yarl import URL

from .config import Config
from .core import _Core
from .errors import NotSupportedError
from .server_cfg import Preset
from .utils import NoPublicConstructor

Expand Down Expand Up @@ -102,6 +104,14 @@ def __init__(self, core: _Core, config: Config) -> None:
self._core = core
self._config = config

@property
def _admin_url(self) -> URL:
url = self._config.admin_url
if not url:
raise NotSupportedError("admin API is not supported by server")
else:
return url

async def list_cloud_providers(self) -> Dict[str, Dict[str, Any]]:
url = self._config.api_url / "cloud_providers"
auth = await self._config._api_auth()
Expand All @@ -122,7 +132,7 @@ async def list_clusters(self) -> Dict[str, _Cluster]:
return ret

async def add_cluster(self, name: str, config: Dict[str, Any]) -> None:
url = self._config.admin_url / "clusters"
url = self._admin_url / "clusters"
auth = await self._config._api_auth()
payload = {"name": name}
async with self._core.request("POST", url, auth=auth, json=payload) as resp:
Expand All @@ -147,7 +157,7 @@ async def list_cluster_users(
self, cluster_name: Optional[str] = None
) -> List[_ClusterUser]:
cluster_name = cluster_name or self._config.cluster_name
url = self._config.admin_url / "clusters" / cluster_name / "users"
url = self._admin_url / "clusters" / cluster_name / "users"
auth = await self._config._api_auth()
async with self._core.request(
"GET", url, auth=auth, params={"with_user_info": "true"}
Expand All @@ -162,7 +172,7 @@ async def get_cluster_user(
) -> _ClusterUser:
cluster_name = cluster_name or self._config.cluster_name
user_name = user_name or self._config.username
url = self._config.admin_url / "clusters" / cluster_name / "users" / user_name
url = self._admin_url / "clusters" / cluster_name / "users" / user_name
auth = await self._config._api_auth()
async with self._core.request(
"GET", url, auth=auth, params={"with_user_info": "true"}
Expand All @@ -173,7 +183,7 @@ async def get_cluster_user(
async def add_cluster_user(
self, cluster_name: str, user_name: str, role: str
) -> _ClusterUser:
url = self._config.admin_url / "clusters" / cluster_name / "users"
url = self._admin_url / "clusters" / cluster_name / "users"
payload = {"user_name": user_name, "role": role}
auth = await self._config._api_auth()

Expand All @@ -184,7 +194,7 @@ async def add_cluster_user(
return _cluster_user_from_api(payload)

async def remove_cluster_user(self, cluster_name: str, user_name: str) -> None:
url = self._config.admin_url / "clusters" / cluster_name / "users" / user_name
url = self._admin_url / "clusters" / cluster_name / "users" / user_name
auth = await self._config._api_auth()

async with self._core.request("DELETE", url, auth=auth):
Expand All @@ -198,12 +208,7 @@ async def set_user_quota(
total_running_jobs: Optional[int],
) -> _ClusterUser:
url = (
self._config.admin_url
/ "clusters"
/ cluster_name
/ "users"
/ user_name
/ "quota"
self._admin_url / "clusters" / cluster_name / "users" / user_name / "quota"
)
payload = {
"quota": {
Expand All @@ -227,7 +232,7 @@ async def set_user_credits(
credits: Optional[Decimal],
) -> _ClusterUser:
url = (
self._config.admin_url
self._admin_url
/ "clusters"
/ cluster_name
/ "users"
Expand All @@ -253,7 +258,7 @@ async def add_user_credits(
additional_credits: Decimal,
) -> _ClusterUser:
url = (
self._config.admin_url
self._admin_url
/ "clusters"
/ cluster_name
/ "users"
Expand Down
4 changes: 4 additions & 0 deletions neuro-sdk/src/neuro_sdk/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ class ConfigLoadException(Exception):

class NDJSONError(ValueError):
pass


class NotSupportedError(NotImplementedError):
pass
7 changes: 5 additions & 2 deletions neuro-sdk/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ def go(
registry_url: str = "https://registry-dev.neu.ro",
trace_id: str = "bd7a977555f6b982",
clusters: Optional[Dict[str, Cluster]] = None,
token_url: Optional[URL] = None
token_url: Optional[URL] = None,
admin_url: Optional[URL] = None,
) -> Client:
url = URL(url_str)
if clusters is None:
Expand Down Expand Up @@ -162,11 +163,13 @@ def go(
real_auth_config = replace(auth_config, token_url=token_url)
else:
real_auth_config = auth_config
if admin_url is None:
admin_url = URL(url) / ".." / ".." / "apis" / "admin" / "v1"
config = _ConfigData(
auth_config=real_auth_config,
auth_token=_AuthToken.create_non_expiring(token),
url=URL(url),
admin_url=URL(url) / ".." / ".." / "apis" / "admin" / "v1",
admin_url=admin_url,
version=__version__,
cluster_name=next(iter(clusters)),
clusters=clusters,
Expand Down
18 changes: 17 additions & 1 deletion neuro-sdk/tests/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
from decimal import Decimal
from typing import Callable

import pytest
from aiohttp import web
from aiohttp.web import HTTPCreated, HTTPNoContent
from aiohttp.web_exceptions import HTTPOk
from yarl import URL

from neuro_sdk import Client
from neuro_sdk import Client, NotSupportedError
from neuro_sdk.admin import (
_Balance,
_CloudProvider,
Expand Down Expand Up @@ -536,6 +538,20 @@ async def handle_get_cluster_user(request: web.Request) -> web.StreamResponse:
assert requested_users == ["test"]


async def test_not_supported_admin_api(
aiohttp_server: _TestServerFactory, make_client: _MakeClient
) -> None:
app = web.Application()

srv = await aiohttp_server(app)

async with make_client(srv.make_url("/api/v1"), admin_url=URL()) as client:
with pytest.raises(
NotSupportedError, match="admin API is not supported by server"
):
await client._admin.get_cluster_user("default", "test")


async def test_add_cluster_user(
aiohttp_server: _TestServerFactory, make_client: _MakeClient
) -> None:
Expand Down

0 comments on commit eaeb266

Please sign in to comment.