Skip to content

Commit

Permalink
ADCM-6156: Implement missing methods for BundleNode (#26)
Browse files Browse the repository at this point in the history
Co-authored-by: astarovo <[email protected]>
  • Loading branch information
Starovoitov and Starovoitov authored Dec 3, 2024
1 parent f42d8e9 commit 563c94c
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 9 deletions.
11 changes: 6 additions & 5 deletions adcm_aio_client/core/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
from typing import Self

from adcm_aio_client.core.objects.cm import ADCM, BundlesNode, ClustersNode, HostProvidersNode, HostsAccessor
from adcm_aio_client.core.requesters import DefaultRequester
from adcm_aio_client.core.types import AuthToken, Cert, Credentials, Requester, Verify
from adcm_aio_client.core.requesters import BundleRetriever, BundleRetrieverInterface, DefaultRequester, Requester
from adcm_aio_client.core.types import AuthToken, Cert, Credentials, Verify


class ADCMClient:
def __init__(self: Self, requester: Requester) -> None:
def __init__(self: Self, requester: Requester, bundle_retriever: BundleRetrieverInterface) -> None:
self._requester = requester
self.bundle_retriever = bundle_retriever

@cached_property
def clusters(self: Self) -> ClustersNode:
Expand All @@ -40,7 +41,7 @@ def adcm(self: Self) -> ADCM:

@cached_property
def bundles(self: Self) -> BundlesNode:
return BundlesNode(path=("bundles",), requester=self._requester)
return BundlesNode(path=("bundles",), requester=self._requester, retriever=self.bundle_retriever)


async def build_client(
Expand All @@ -55,4 +56,4 @@ async def build_client(
) -> ADCMClient:
requester = DefaultRequester(base_url=url, retries=retries, retry_interval=retry_interval, timeout=timeout)
await requester.login(credentials=Credentials(username="admin", password="admin")) # noqa: S106
return ADCMClient(requester=requester)
return ADCMClient(requester=requester, bundle_retriever=BundleRetriever())
30 changes: 28 additions & 2 deletions adcm_aio_client/core/objects/cm.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from functools import cached_property
from pathlib import Path
from typing import Iterable, Literal, Self
import asyncio

Expand All @@ -21,7 +22,8 @@
)
from adcm_aio_client.core.objects._imports import ClusterImports
from adcm_aio_client.core.objects._mapping import ClusterMapping
from adcm_aio_client.core.types import ADCMEntityStatus, Endpoint
from adcm_aio_client.core.requesters import BundleRetrieverInterface
from adcm_aio_client.core.types import ADCMEntityStatus, Endpoint, Requester, UrlPath

type Filter = object # TODO: implement

Expand All @@ -41,7 +43,10 @@ def get_own_path(self: Self) -> Endpoint:
return ("adcm",)


class License(InteractiveObject): ...
class License(InteractiveObject):
state: str

def accept(self: Self) -> None: ...


class Bundle(Deletable, RootInteractiveObject):
Expand Down Expand Up @@ -71,6 +76,7 @@ def signature_status(self: Self) -> Literal["invalid", "valid", "absent"]:
def _type(self: Self) -> Literal["cluster", "provider"]:
return self._data["mainPrototype"]["type"]

@property
def license(self: Self) -> License:
return self._construct(what=License, from_data=self._data["mainPrototype"]["license"])

Expand All @@ -85,6 +91,26 @@ def _main_prototype_id(self: Self) -> int:
class BundlesNode(PaginatedAccessor[Bundle, None]):
class_type = Bundle

def __init__(self: Self, path: Endpoint, requester: Requester, retriever: BundleRetrieverInterface) -> None:
super().__init__(path, requester)
self.retriever = retriever

async def create(self: Self, source: Path | UrlPath, accept_license: bool = False) -> Bundle: # noqa: FBT001, FBT002
if isinstance(source, UrlPath):
file_content = await self.retriever.download_external_bundle(source)
files = {"file": file_content}
else:
files = {"file": Path(source).read_bytes()}

response = await self._requester.post("bundles", data=files)

bundle = Bundle(requester=self._requester, data=response.as_dict())

if accept_license and bundle.license.state == "unaccepted":
bundle.license.accept()

return bundle

def get_own_path(self: Self) -> Endpoint:
return ("bundles",)

Expand Down
24 changes: 22 additions & 2 deletions adcm_aio_client/core/requesters.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from abc import ABC, abstractmethod
from asyncio import sleep
from contextlib import suppress
from dataclasses import dataclass
Expand All @@ -27,14 +27,15 @@
LoginError,
NoCredentialsError,
NotFoundError,
OperationError,
ResponseDataConversionError,
ResponseError,
RetryRequestError,
ServerError,
UnauthorizedError,
WrongCredentialsError,
)
from adcm_aio_client.core.types import Credentials, PathPart, QueryParameters, Requester
from adcm_aio_client.core.types import Credentials, PathPart, QueryParameters, Requester, UrlPath

Json: TypeAlias = Any
Params = ParamSpec("Params")
Expand Down Expand Up @@ -183,3 +184,22 @@ def _ensure_credentials(self: Self) -> Credentials:
raise NoCredentialsError

return self._credentials


class BundleRetrieverInterface(ABC):
@abstractmethod
async def download_external_bundle(self: Self, url: UrlPath) -> bytes:
pass


class BundleRetriever(BundleRetrieverInterface):
async def download_external_bundle(self: Self, url: UrlPath) -> bytes:
try:
async with httpx.AsyncClient() as client:
response = await client.get(url)
response.raise_for_status()
return response.content
except ValueError as err:
raise OperationError(f"Failed to download the bundle {url}") from err
except httpx.HTTPStatusError as err:
raise OperationError(f"HTTP error occurred: {err}") from err
4 changes: 4 additions & 0 deletions adcm_aio_client/core/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,7 @@ class ADCMEntityStatus(str, Enum):
class MappingOperation(str, Enum):
ADD = "add"
REMOVE = "remove"


class UrlPath(str):
pass

0 comments on commit 563c94c

Please sign in to comment.