Skip to content

Commit c8bdf85

Browse files
committed
Add resource.update_status
Functions that set status on the composite resource have to wrap their status in {"status": ...} and call resource.update. This is a small but universal bit of boilerplate — nearly every composition function writes status. This commit adds resource.update_status, which accepts a dict or Pydantic model and nests it under the status key. Signed-off-by: Nic Cope <nicc@rk0n.org>
1 parent 629331f commit c8bdf85

2 files changed

Lines changed: 69 additions & 0 deletions

File tree

crossplane/function/resource.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,25 @@ def update(r: fnv1.Resource, source: dict | structpb.Struct | pydantic.BaseModel
6060
raise TypeError(msg)
6161

6262

63+
def update_status(
64+
r: fnv1.Resource,
65+
status: dict | pydantic.BaseModel,
66+
) -> None:
67+
"""Update a resource's status.
68+
69+
Args:
70+
r: A composite or composed resource to update.
71+
status: The status to set, as a dictionary or Pydantic model.
72+
73+
Sets ``r.resource.status`` from the supplied status. When the status
74+
is a Pydantic model, fields set to their default value are excluded,
75+
matching the behavior of :func:`update`.
76+
"""
77+
if isinstance(status, pydantic.BaseModel):
78+
status = status.model_dump(exclude_defaults=True, warnings=False)
79+
update(r, {"status": status})
80+
81+
6382
def dict_to_struct(d: dict) -> structpb.Struct:
6483
"""Create a Struct well-known type from the supplied dict.
6584

tests/test_resource.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,56 @@ class TestResource(unittest.TestCase):
2929
def setUp(self) -> None:
3030
logging.configure(level=logging.Level.DISABLED)
3131

32+
def test_update_status(self) -> None:
33+
@dataclasses.dataclass
34+
class TestCase:
35+
reason: str
36+
r: fnv1.Resource
37+
status: dict | pydantic.BaseModel
38+
want: dict
39+
40+
cases = [
41+
TestCase(
42+
reason="Setting status from a dict should work.",
43+
r=fnv1.Resource(
44+
resource=resource.dict_to_struct(
45+
{"apiVersion": "example.org", "kind": "XR"}
46+
),
47+
),
48+
status={"ready": True},
49+
want={
50+
"apiVersion": "example.org",
51+
"kind": "XR",
52+
"status": {"ready": True},
53+
},
54+
),
55+
TestCase(
56+
reason="Setting status from a Pydantic model should work.",
57+
r=fnv1.Resource(
58+
resource=resource.dict_to_struct(
59+
{"apiVersion": "example.org", "kind": "XR"}
60+
),
61+
),
62+
status=v1beta2.ForProvider(region="us-west-2"),
63+
want={
64+
"apiVersion": "example.org",
65+
"kind": "XR",
66+
"status": {"region": "us-west-2"},
67+
},
68+
),
69+
TestCase(
70+
reason="Setting status on an empty resource should work.",
71+
r=fnv1.Resource(),
72+
status={"replicas": 3},
73+
want={"status": {"replicas": 3}},
74+
),
75+
]
76+
77+
for case in cases:
78+
resource.update_status(case.r, case.status)
79+
got = resource.struct_to_dict(case.r.resource)
80+
self.assertEqual(case.want, got, case.reason)
81+
3282
def test_add(self) -> None:
3383
@dataclasses.dataclass
3484
class TestCase:

0 commit comments

Comments
 (0)