From 4c1d0735dfe498e05dfd6da80fa28b7f958ff1e6 Mon Sep 17 00:00:00 2001 From: Skrynnik Daniil Date: Mon, 20 Jan 2025 17:07:44 +0300 Subject: [PATCH 1/4] ADCM-6280: Prepare an example of interaction with a service --- .../bundles/complex_cluster/config.yaml | 5 ++ tests/integration/examples/conftest.py | 40 +++++++++- .../examples/test_interaction_with_service.py | 80 +++++++++++++++++++ 3 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 tests/integration/examples/test_interaction_with_service.py diff --git a/tests/integration/bundles/complex_cluster/config.yaml b/tests/integration/bundles/complex_cluster/config.yaml index 8f0c176..ff11063 100644 --- a/tests/integration/bundles/complex_cluster/config.yaml +++ b/tests/integration/bundles/complex_cluster/config.yaml @@ -178,6 +178,11 @@ c2: actions: *actions +- type: service + name: with_license + version: 1 + license: ./license.txt + - type: service name: complex_config version: 0.3 diff --git a/tests/integration/examples/conftest.py b/tests/integration/examples/conftest.py index dd72abb..a8d22ff 100644 --- a/tests/integration/examples/conftest.py +++ b/tests/integration/examples/conftest.py @@ -1,8 +1,10 @@ -from collections.abc import Generator +from collections.abc import AsyncGenerator, Generator import pytest_asyncio -from adcm_aio_client.objects import Bundle +from adcm_aio_client import ADCMSession, Credentials +from adcm_aio_client.client import ADCMClient +from adcm_aio_client.objects import Bundle, Cluster, Host from tests.integration.setup_environment import ADCMContainer TIMEOUT = 10 @@ -20,3 +22,37 @@ def adcm( ) -> Generator[ADCMContainer, None, None]: _ = simple_cluster_bundle, complex_cluster_bundle, previous_complex_cluster_bundle, simple_hostprovider_bundle yield adcm + + +@pytest_asyncio.fixture() +async def admin_client(adcm: ADCMContainer) -> AsyncGenerator[ADCMClient, None]: + credentials = Credentials(username="admin", password="admin") # noqa: S106 + + async with ADCMSession( + url=adcm.url, + credentials=credentials, + timeout=TIMEOUT, + retry_interval=RETRY_INTERVAL, + retry_attempts=RETRY_ATTEMPTS, + ) as client: + yield client + + +@pytest_asyncio.fixture() +async def example_cluster(admin_client: ADCMClient) -> AsyncGenerator[Cluster, None]: + cluster_bundle = await admin_client.bundles.get(name__eq="Some Cluster", version__eq="1") + cluster = await admin_client.clusters.create(bundle=cluster_bundle, name="Example cluster") + + yield cluster + + await cluster.delete() + + +@pytest_asyncio.fixture() +async def three_hosts(admin_client: ADCMClient, simple_hostprovider_bundle: Bundle) -> list[Host]: + provider = await admin_client.hostproviders.create(bundle=simple_hostprovider_bundle, name="Hostprovider") + names = {"host-1", "host-2", "host-3"} + for name in names: + await admin_client.hosts.create(hostprovider=provider, name=name) + + return await admin_client.hosts.filter(name__in=names) diff --git a/tests/integration/examples/test_interaction_with_service.py b/tests/integration/examples/test_interaction_with_service.py new file mode 100644 index 0000000..fd4c5b7 --- /dev/null +++ b/tests/integration/examples/test_interaction_with_service.py @@ -0,0 +1,80 @@ +import pytest + +from adcm_aio_client import Filter +from adcm_aio_client.objects import Cluster, Component, Host, Service + +pytestmark = [pytest.mark.asyncio] + + +async def test_interaction_with_service(example_cluster: Cluster, three_hosts: list[Host]) -> None: + cluster = example_cluster + + # Add services "example_1" and "example_2" using Filter + await cluster.services.add(filter_=Filter(attr="name", op="iin", value=["Example_1", "example_2"])) + + # Add service with all it's dependencies + # Services "service_with_requires_my_service" and "my_service" will be added + await cluster.services.add( + filter_=Filter(attr="name", op="eq", value="service_with_requires_my_service"), with_dependencies=True + ) + + # Add service and accept license + await cluster.services.add(filter_=Filter(attr="name", op="eq", value="with_license"), accept_license=True) + + # Get all cluster's services + all_added_services: list[Service] = await cluster.services.all() # noqa: F841 + + # iterate through all cluster's services + async for service in cluster.services.iter(): # noqa: B007 + pass + + # Remove services from cluster. Leave single "example_1" service + for service in await cluster.services.filter( + name__in=["example_2", "service_with_requires_my_service", "my_service", "with_license"] + ): + await service.delete() + + # Get non-existent (already removed) service "example_2" + none_service: None = await cluster.services.get_or_none(name__eq="example_2") # pyright: ignore[reportAssignmentType] # noqa: F841 + + # Get "example_1" service using case-insensitive filter by display_name + service = await cluster.services.get(display_name__ieq="first example") # actual display_name is "First Example" + + # Turn on service's maintenance mode + service_mm = await service.maintenance_mode + await service_mm.on() + + # Sync service's data with remote state + await service.refresh() + + # Get all service's components + all_components: list[Component] = await service.components.all() # noqa: F841 + + # Get specific component + component = await service.components.get(name__eq="first") + + # Prepare data + await _map_hosts_to_component(hosts=three_hosts, component=component) + + # Get all component's hosts + component_hosts: list[Host] = await component.hosts.all() # noqa: F841 + + # Get specific host, mapped to this component + host_3_from_component = await component.hosts.get(name__eq="host-3") # noqa: F841 + + # Turn on maintenance mode on component + await (await component.maintenance_mode).on() + + # Sync component's data with remote state + await component.refresh() + + +async def _map_hosts_to_component(hosts: list[Host], component: Component) -> None: + cluster = component.cluster + + await cluster.hosts.add(host=hosts) + + mapping = await cluster.mapping + + await mapping.add(component=component, host=hosts) + await mapping.save() From 62e58e0c50845720783947d04cbad985ba9d6e05 Mon Sep 17 00:00:00 2001 From: Skrynnik Daniil Date: Tue, 21 Jan 2025 10:18:40 +0300 Subject: [PATCH 2/4] ADCM-6280: review fixes --- .../examples/test_interaction_with_service.py | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/tests/integration/examples/test_interaction_with_service.py b/tests/integration/examples/test_interaction_with_service.py index fd4c5b7..62cb5dd 100644 --- a/tests/integration/examples/test_interaction_with_service.py +++ b/tests/integration/examples/test_interaction_with_service.py @@ -1,6 +1,7 @@ import pytest from adcm_aio_client import Filter +from adcm_aio_client.errors import ObjectDoesNotExistError from adcm_aio_client.objects import Cluster, Component, Host, Service pytestmark = [pytest.mark.asyncio] @@ -8,6 +9,7 @@ async def test_interaction_with_service(example_cluster: Cluster, three_hosts: list[Host]) -> None: cluster = example_cluster + host_1, host_2, host_3 = sorted(three_hosts, key=lambda host: host.name) # Add services "example_1" and "example_2" using Filter await cluster.services.add(filter_=Filter(attr="name", op="iin", value=["Example_1", "example_2"])) @@ -53,28 +55,24 @@ async def test_interaction_with_service(example_cluster: Cluster, three_hosts: l # Get specific component component = await service.components.get(name__eq="first") - # Prepare data - await _map_hosts_to_component(hosts=three_hosts, component=component) + # Prepare data: add 3 hosts to cluster, map two of them to component + await cluster.hosts.add(host=[host_1, host_2, host_3]) + mapping = await cluster.mapping + await mapping.add(component=component, host=[host_1, host_2]) + await mapping.save() # Get all component's hosts component_hosts: list[Host] = await component.hosts.all() # noqa: F841 # Get specific host, mapped to this component - host_3_from_component = await component.hosts.get(name__eq="host-3") # noqa: F841 + host_2_from_component = await component.hosts.get(name__eq=host_2.name) # noqa: F841 + + # Trying to get host_3 from component, get error: host_3 is not mapped to component + with pytest.raises(ObjectDoesNotExistError, match="No objects found with the given filter."): + host_3_from_component = await component.hosts.get(name__eq=host_3.name) # noqa: F841 # Turn on maintenance mode on component await (await component.maintenance_mode).on() # Sync component's data with remote state await component.refresh() - - -async def _map_hosts_to_component(hosts: list[Host], component: Component) -> None: - cluster = component.cluster - - await cluster.hosts.add(host=hosts) - - mapping = await cluster.mapping - - await mapping.add(component=component, host=hosts) - await mapping.save() From 147709b95acc1cb346d8e425b41c283cd0d463db Mon Sep 17 00:00:00 2001 From: Skrynnik Daniil Date: Tue, 21 Jan 2025 15:38:25 +0300 Subject: [PATCH 3/4] ADCM-6280: small rename refactor --- .../{test_interaction_with_service.py => test_service.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/integration/examples/{test_interaction_with_service.py => test_service.py} (96%) diff --git a/tests/integration/examples/test_interaction_with_service.py b/tests/integration/examples/test_service.py similarity index 96% rename from tests/integration/examples/test_interaction_with_service.py rename to tests/integration/examples/test_service.py index 62cb5dd..4238e2f 100644 --- a/tests/integration/examples/test_interaction_with_service.py +++ b/tests/integration/examples/test_service.py @@ -7,7 +7,7 @@ pytestmark = [pytest.mark.asyncio] -async def test_interaction_with_service(example_cluster: Cluster, three_hosts: list[Host]) -> None: +async def test_service(example_cluster: Cluster, three_hosts: list[Host]) -> None: cluster = example_cluster host_1, host_2, host_3 = sorted(three_hosts, key=lambda host: host.name) From e5e8f3b275aa563d0066599063a25a4976d51995 Mon Sep 17 00:00:00 2001 From: Skrynnik Daniil Date: Tue, 21 Jan 2025 17:25:42 +0300 Subject: [PATCH 4/4] ADCM-6280: review fixes --- tests/integration/examples/test_service.py | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/integration/examples/test_service.py b/tests/integration/examples/test_service.py index 4238e2f..7201c8d 100644 --- a/tests/integration/examples/test_service.py +++ b/tests/integration/examples/test_service.py @@ -8,6 +8,22 @@ async def test_service(example_cluster: Cluster, three_hosts: list[Host]) -> None: + """ + Service (`cluster.services`) API Examples: + - adding a service to cluster + - with accepting license + - with dependencies + - retrieval with filtering / all cluster's services + - iteration through all cluster's services + - service removal + - turning on maintenance mode + - refreshing service's data + Component (`service.components`) API Examples: + - retrieval with filtering / all service's components + - retrieval hosts, mapped to component with filtering / all mapped hosts + - turning on maintenance mode + - refreshing component's data + """ cluster = example_cluster host_1, host_2, host_3 = sorted(three_hosts, key=lambda host: host.name) @@ -64,6 +80,13 @@ async def test_service(example_cluster: Cluster, three_hosts: list[Host]) -> Non # Get all component's hosts component_hosts: list[Host] = await component.hosts.all() # noqa: F841 + # Iterate through all service's components + async for comp in service.components.iter(): # noqa: B007 + pass + + # Get all hosts, mapped to component + all_component_hosts: list[Host] = await component.hosts.all() # noqa: F841 + # Get specific host, mapped to this component host_2_from_component = await component.hosts.get(name__eq=host_2.name) # noqa: F841