Skip to content

Commit 3570473

Browse files
authored
Minor adjustments pre-v2.3 (#431)
* pcu descriptor classes get as many optional fields as makes sense; changed the exception raised for non-existing pcu group id in create_database. * adjust tests to changed exception in create_db * create_database: different error for nonexistent pcu group vs. in another region * Address review comments; BUMP TO 2.3.0
1 parent 1f0c55e commit 3570473

9 files changed

Lines changed: 333 additions & 81 deletions

File tree

CHANGES

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
main
2-
====
1+
v 2.3.0
2+
=======
33
Supported Python versions are now 3.10 to 3.14:
44
- removal of Python 3.9 [Breaking change w.r.t. 2.2]
55
`AstraDatabaseAdmin`: support for listing PCU Groups.
@@ -29,13 +29,11 @@ maintenance: base and vectorize integration tests fail fast unless all non-syste
2929

3030
v 2.2.1
3131
=======
32-
3332
Suppress warning about unexpected 'pcu_types' field in `[async_]find_available_regions`.
3433
Maintenance:
3534
- Minor edits to `release.yaml` workflow (secrets; task names/comments).
3635

3736

38-
3937
v 2.2.0
4038
=======
4139
Supported Python versions are now 3.9 to 3.14:

astrapy/admin/admin.py

Lines changed: 79 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,30 +1236,53 @@ def create_database(
12361236
# If a PCU group ID is provided, try to validate it
12371237
if _definition.pcu_group_id is not None:
12381238
logger.info("PCU Group ID pre-check: starting existence check.")
1239-
pcu_groups: list[PCUGroupDescriptor] | None
1239+
pcu_groups_overall: list[PCUGroupDescriptor] | None
12401240
try:
1241-
pcu_groups = self.list_pcu_groups(
1242-
cloud_provider=_definition.cloud_provider,
1243-
region=_definition.region,
1241+
pcu_groups_overall = self.list_pcu_groups(
12441242
database_admin_timeout_ms=database_admin_timeout_ms,
12451243
request_timeout_ms=request_timeout_ms,
12461244
timeout_ms=timeout_ms,
12471245
)
12481246
except Exception as e:
1249-
pcu_groups = None
1247+
pcu_groups_overall = None
12501248
logger.info(f"PCU Group ID pre-check threw an exception: {str(e)}")
1251-
if pcu_groups is not None:
1252-
matching_pcu_groups = [
1253-
pg for pg in pcu_groups if pg.id == _definition.pcu_group_id
1249+
if pcu_groups_overall is not None:
1250+
matching_pcu_groups_overall = [
1251+
pg for pg in pcu_groups_overall if pg.id == _definition.pcu_group_id
12541252
]
1255-
logger.info(
1256-
"PCU Group ID pre-check failed. Aborting database creation."
1257-
)
1258-
if matching_pcu_groups == []:
1259-
raise ValueError(
1260-
f"Requested PCU Group ID '{_definition.pcu_group_id}' not found for cloud provider "
1261-
f"('{_definition.cloud_provider}') and region ('{_definition.region}')."
1253+
if matching_pcu_groups_overall == []:
1254+
# no such pcu group id at all: abort 1
1255+
logger.info(
1256+
"PCU Group ID pre-check did not pass (id not found). "
1257+
"Aborting database creation."
1258+
)
1259+
raise DevOpsAPIException(
1260+
f"Requested PCU Group ID '{_definition.pcu_group_id}' not "
1261+
"found for cloud provider provider/region "
1262+
f"('{_definition.cloud_provider}' / '{_definition.region}'). "
1263+
"Aborting database creation."
1264+
)
1265+
else:
1266+
# is the matching group in the right cloud provider / region?
1267+
expected_cpr = (
1268+
_definition.cloud_provider.lower(),
1269+
_definition.region.lower(),
1270+
)
1271+
found_cpr = (
1272+
matching_pcu_groups_overall[0].cloud_provider.lower(),
1273+
matching_pcu_groups_overall[0].region.lower(),
12621274
)
1275+
if expected_cpr != found_cpr:
1276+
# wrong region: abort 2
1277+
logger.info(
1278+
"PCU Group ID pre-check did not pass (wrong "
1279+
"provider/region). Aborting database creation."
1280+
)
1281+
raise DevOpsAPIException(
1282+
f"Requested PCU Group ID '{_definition.pcu_group_id}' "
1283+
f"is in another cloud provider and region ('{found_cpr[0]}' "
1284+
f"/ '{found_cpr[1]}'). Aborting database creation."
1285+
)
12631286
logger.info("PCU Group ID pre-check succeeded.")
12641287
else:
12651288
logger.info("PCU Group ID pre-check aborted.")
@@ -1513,32 +1536,55 @@ async def async_create_database(
15131536
# If a PCU group ID is provided, try to validate it
15141537
if _definition.pcu_group_id is not None:
15151538
logger.info("PCU Group ID pre-check: starting existence check, async.")
1516-
pcu_groups: list[PCUGroupDescriptor] | None
1539+
pcu_groups_overall: list[PCUGroupDescriptor] | None
15171540
try:
1518-
pcu_groups = await self.async_list_pcu_groups(
1519-
cloud_provider=_definition.cloud_provider,
1520-
region=_definition.region,
1541+
pcu_groups_overall = await self.async_list_pcu_groups(
15211542
database_admin_timeout_ms=database_admin_timeout_ms,
15221543
request_timeout_ms=request_timeout_ms,
15231544
timeout_ms=timeout_ms,
15241545
)
15251546
except Exception as e:
1526-
pcu_groups = None
1547+
pcu_groups_overall = None
15271548
logger.info(
1528-
f"PCU Group ID pre-check threw an exception, async: {str(e)}"
1549+
f"PCU Group ID pre-check threw an exception: {str(e)}, async"
15291550
)
1530-
if pcu_groups is not None:
1531-
matching_pcu_groups = [
1532-
pg for pg in pcu_groups if pg.id == _definition.pcu_group_id
1551+
if pcu_groups_overall is not None:
1552+
matching_pcu_groups_overall = [
1553+
pg for pg in pcu_groups_overall if pg.id == _definition.pcu_group_id
15331554
]
1534-
logger.info(
1535-
"PCU Group ID pre-check failed. Aborting database creation, async."
1536-
)
1537-
if matching_pcu_groups == []:
1538-
raise ValueError(
1539-
f"Requested PCU Group ID '{_definition.pcu_group_id}' not found for cloud provider "
1540-
f"('{_definition.cloud_provider}') and region ('{_definition.region}')."
1555+
if matching_pcu_groups_overall == []:
1556+
# no such pcu group id at all: abort 1
1557+
logger.info(
1558+
"PCU Group ID pre-check did not pass (id not found). "
1559+
"Aborting database creation, async."
1560+
)
1561+
raise DevOpsAPIException(
1562+
f"Requested PCU Group ID '{_definition.pcu_group_id}' "
1563+
"not found for cloud provider provider/region "
1564+
f"('{_definition.cloud_provider}' / '{_definition.region}'). "
1565+
"Aborting database creation."
1566+
)
1567+
else:
1568+
# is the matching group in the right cloud provider / region?
1569+
expected_cpr = (
1570+
_definition.cloud_provider.lower(),
1571+
_definition.region.lower(),
1572+
)
1573+
found_cpr = (
1574+
matching_pcu_groups_overall[0].cloud_provider.lower(),
1575+
matching_pcu_groups_overall[0].region.lower(),
15411576
)
1577+
if expected_cpr != found_cpr:
1578+
# wrong region: abort 2
1579+
logger.info(
1580+
"PCU Group ID pre-check did not pass (wrong "
1581+
"provider/region). Aborting database creation, async."
1582+
)
1583+
raise DevOpsAPIException(
1584+
f"Requested PCU Group ID '{_definition.pcu_group_id}' "
1585+
f"is in another cloud provider and region ('{found_cpr[0]}' "
1586+
f"/ '{found_cpr[1]}'). Aborting database creation."
1587+
)
15421588
logger.info("PCU Group ID pre-check succeeded, async.")
15431589
else:
15441590
logger.info("PCU Group ID pre-check aborted, async.")
@@ -1563,7 +1609,8 @@ async def async_create_database(
15631609
)
15641610
raise PermissionError(CANNOT_POLL_ERROR_MESSAGE)
15651611
logger.info(
1566-
f"creating database {name}/({_definition.cloud_provider}, {_definition.region}) (DevOps API), async"
1612+
f"creating database {name}/({_definition.cloud_provider}, "
1613+
f"{_definition.region}) (DevOps API), async"
15671614
)
15681615
cd_raw_response = await self._dev_ops_api_commander.async_raw_request(
15691616
caller_function_name="async_create_database",

astrapy/data/info/database_info.py

Lines changed: 70 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -545,12 +545,23 @@ class PCUGroupTypeDetailsDescriptor:
545545
disk_cache: the amount of disk cache for this PCU type.
546546
"""
547547

548-
v_cpu: int
549-
memory: str
550-
disk_cache: str
548+
v_cpu: int | None = None
549+
memory: str | None = None
550+
disk_cache: str | None = None
551551

552552
def __repr__(self) -> str:
553-
body = f"v_cpu={self.v_cpu}, memory={self.memory}, disk_cache={self.disk_cache}"
553+
pieces = [
554+
pc
555+
for pc in (
556+
f"v_cpu={self.v_cpu}" if self.v_cpu is not None else None,
557+
f"memory={self.memory}" if self.memory is not None else None,
558+
f"disk_cache={self.disk_cache}"
559+
if self.disk_cache is not None
560+
else None,
561+
)
562+
if pc is not None
563+
]
564+
body = ", ".join(pieces)
554565
return f"{self.__class__.__name__}({body})"
555566

556567
def as_dict(self) -> dict[str, Any]:
@@ -559,9 +570,13 @@ def as_dict(self) -> dict[str, Any]:
559570
"""
560571

561572
return {
562-
"vCPU": self.v_cpu,
563-
"memory": self.memory,
564-
"disk_cache": self.disk_cache,
573+
k: v
574+
for k, v in {
575+
"vCPU": self.v_cpu,
576+
"memory": self.memory,
577+
"disk_cache": self.disk_cache,
578+
}.items()
579+
if v is not None
565580
}
566581

567582
@classmethod
@@ -581,9 +596,9 @@ def _from_dict(cls, raw_dict: dict[str, Any]) -> PCUGroupTypeDetailsDescriptor:
581596
},
582597
)
583598
return PCUGroupTypeDetailsDescriptor(
584-
v_cpu=raw_dict["vCPU"],
585-
memory=raw_dict["memory"],
586-
disk_cache=raw_dict["disk_cache"],
599+
v_cpu=raw_dict.get("vCPU"),
600+
memory=raw_dict.get("memory"),
601+
disk_cache=raw_dict.get("disk_cache"),
587602
)
588603

589604

@@ -603,7 +618,7 @@ class PCUGroupTypeDescriptor:
603618
type: str
604619
region: str | None
605620
cloud_provider: str | None
606-
details: PCUGroupTypeDetailsDescriptor
621+
details: PCUGroupTypeDetailsDescriptor | None
607622

608623
def __repr__(self) -> str:
609624
pieces = [
@@ -614,7 +629,7 @@ def __repr__(self) -> str:
614629
f"cloud_provider={self.cloud_provider}"
615630
if self.cloud_provider is not None
616631
else None,
617-
"details=...",
632+
"details=..." if self.details is not None else None,
618633
)
619634
if pc is not None
620635
]
@@ -632,7 +647,7 @@ def as_dict(self) -> dict[str, Any]:
632647
"type": self.type,
633648
"region": self.region,
634649
"provider": self.cloud_provider,
635-
"details": self.details.as_dict(),
650+
"details": self.details.as_dict() if self.details is not None else None,
636651
}.items()
637652
if v is not None
638653
}
@@ -658,7 +673,9 @@ def _from_dict(cls, raw_dict: dict[str, Any]) -> PCUGroupTypeDescriptor:
658673
type=raw_dict["type"],
659674
region=raw_dict.get("region"),
660675
cloud_provider=raw_dict["provider"] if "provider" in raw_dict else None,
661-
details=PCUGroupTypeDetailsDescriptor._from_dict(raw_dict["details"]),
676+
details=PCUGroupTypeDetailsDescriptor._from_dict(raw_dict["details"])
677+
if "details" in raw_dict
678+
else None,
662679
)
663680

664681

@@ -689,26 +706,36 @@ class PCUGroupDescriptor:
689706
"""
690707

691708
id: str
692-
org_id: str
693-
title: str
709+
org_id: str | None
710+
title: str | None
694711
cloud_provider: str
695712
region: str
696-
instance_type: str
697-
pcu_type: PCUGroupTypeDescriptor
698-
provision_type: str
699-
min: int
700-
max: int
701-
description: str
713+
instance_type: str | None
714+
pcu_type: PCUGroupTypeDescriptor | None
715+
provision_type: str | None
716+
min: int | None
717+
max: int | None
718+
description: str | None
702719
created_at: datetime.datetime | None
703720
updated_at: datetime.datetime | None
704-
created_by: str
705-
updated_by: str
706-
status: str
721+
created_by: str | None
722+
updated_by: str | None
723+
status: str | None
707724
reserved: int | None = None
708725

709726
def __repr__(self) -> str:
710-
body = f"id={self.id}, org_id={self.org_id}, title={self.title}, status={self.status}, ..."
711-
return f"{self.__class__.__name__}({body})"
727+
pieces = [
728+
pc
729+
for pc in (
730+
f"id={self.id}" if self.id is not None else None,
731+
f"org_id={self.org_id}" if self.org_id is not None else None,
732+
f"title={self.title}" if self.title is not None else None,
733+
f"status={self.status}" if self.status is not None else None,
734+
)
735+
if pc is not None
736+
]
737+
body = ", ".join(pieces)
738+
return f"{self.__class__.__name__}({body}, ...)"
712739

713740
def as_dict(self) -> dict[str, Any]:
714741
"""
@@ -724,7 +751,9 @@ def as_dict(self) -> dict[str, Any]:
724751
"cloudProvider": self.cloud_provider,
725752
"region": self.region,
726753
"instanceType": self.instance_type,
727-
"pcuType": self.pcu_type.as_dict(),
754+
"pcuType": self.pcu_type.as_dict()
755+
if self.pcu_type is not None
756+
else None,
728757
"provisionType": self.provision_type,
729758
"min": self.min,
730759
"max": self.max,
@@ -775,20 +804,22 @@ def _from_dict(cls, raw_dict: dict[str, Any]) -> PCUGroupDescriptor:
775804
)
776805
return PCUGroupDescriptor(
777806
id=raw_dict["uuid"],
778-
org_id=raw_dict["orgId"],
779-
title=raw_dict["title"],
807+
org_id=raw_dict.get("orgId"),
808+
title=raw_dict.get("title"),
780809
cloud_provider=raw_dict["cloudProvider"],
781810
region=raw_dict["region"],
782-
instance_type=raw_dict["instanceType"],
783-
pcu_type=PCUGroupTypeDescriptor._from_dict(raw_dict["pcuType"]),
784-
provision_type=raw_dict["provisionType"],
785-
min=raw_dict["min"],
786-
max=raw_dict["max"],
811+
instance_type=raw_dict.get("instanceType"),
812+
pcu_type=PCUGroupTypeDescriptor._from_dict(raw_dict["pcuType"])
813+
if "pcuType" in raw_dict
814+
else None,
815+
provision_type=raw_dict.get("provisionType"),
816+
min=raw_dict.get("min"),
817+
max=raw_dict.get("max"),
787818
reserved=raw_dict.get("reserved"),
788-
description=raw_dict["description"],
819+
description=raw_dict.get("description"),
789820
created_at=_failsafe_parse_date(raw_dict.get("createdAt")),
790821
updated_at=_failsafe_parse_date(raw_dict.get("updatedAt")),
791-
created_by=raw_dict["createdBy"],
792-
updated_by=raw_dict["updatedBy"],
793-
status=raw_dict["status"],
822+
created_by=raw_dict.get("createdBy"),
823+
updated_by=raw_dict.get("updatedBy"),
824+
status=raw_dict.get("status"),
794825
)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
requires-python = ">=3.10,<4.0"
33
name = "astrapy"
4-
version = "2.2.1"
4+
version = "2.3.0"
55
description = "A Python client for the Data API on DataStax Astra DB"
66
authors = [
77
{"name" = "Stefano Lottini", "email" = "stefano.lottini@ibm.com"},

tests/base/admin_assets.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from __future__ import annotations
1616

1717
import datetime
18+
from typing import Any
1819

1920
from astrapy.info import (
2021
PCUGroupTypeDescriptor,
@@ -130,3 +131,15 @@
130131
"updated_by": "umWGYUwuxFvQKBKPAHwHjKef",
131132
"status": "CREATED",
132133
}
134+
135+
MINIMAL_PCU_GROUP_DESCRIPTOR = {
136+
"uuid": "minimal_pgd_id",
137+
"cloudProvider": "AWS",
138+
"region": "us-west-2",
139+
}
140+
141+
MINIMAL_PCU_GROUP_TYPE_DESCRIPTOR = {
142+
"type": "small",
143+
}
144+
145+
MINIMAL_PCU_GROUP_TYPE_DETAILS_DESCRIPTOR: dict[str, Any] = {}

0 commit comments

Comments
 (0)