Skip to content

Commit 99986a0

Browse files
Test operator calls purge (#174)
* Test operator calls purge * fix logging assertions --------- Co-authored-by: bertiethorpe <[email protected]>
1 parent e713d68 commit 99986a0

File tree

2 files changed

+102
-9
lines changed

2 files changed

+102
-9
lines changed

capi_janitor/openstack/operator.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -389,14 +389,13 @@ async def _on_openstackcluster_event_impl(
389389

390390
# Get the cloud credential from the cluster and use it to delete dangling
391391
# resources created by OpenStack integrations on the cluster
392-
secrets = await ekclient.api("v1").resource("secrets")
393-
try:
394-
clouds_secret = await secrets.fetch(
395-
spec["identityRef"]["name"], namespace=namespace
396-
)
397-
except easykube.ApiError as exc:
398-
if exc.status_code != 404:
399-
raise
392+
clouds_secret = await _get_clouds_secret(
393+
spec["identityRef"]["name"], namespace=namespace
394+
)
395+
if clouds_secret is None:
396+
# TODO(johngarbutt): fail better when secret not found?
397+
logger.error(f"clouds.yaml not found for: {clustername}")
398+
400399
else:
401400
clouds = yaml.safe_load(base64.b64decode(clouds_secret.data["clouds.yaml"]))
402401
if "cacert" in clouds_secret.data:
@@ -424,6 +423,7 @@ async def _on_openstackcluster_event_impl(
424423
CREDENTIAL_ANNOTATION
425424
)
426425
remove_appcred = credential_annotation_value == CREDENTIAL_ANNOTATION_DELETE
426+
427427
await purge_openstack_resources(
428428
logger,
429429
clouds,
@@ -436,7 +436,7 @@ async def _on_openstackcluster_event_impl(
436436
# If we get to here, OpenStack resources have been successfully deleted
437437
# So we can remove the appcred secret if we are the last actor
438438
if remove_appcred and len(finalizers) == 1:
439-
await secrets.delete(clouds_secret.metadata.name, namespace=namespace)
439+
await _delete_secret(clouds_secret.metadata["name"], namespace)
440440
logger.info("cloud credential secret deleted")
441441
elif remove_appcred:
442442
# If the annotation says delete but other controllers are still acting, go round again
@@ -451,7 +451,22 @@ async def _on_openstackcluster_event_impl(
451451
logger.info("removed janitor finalizer from cluster")
452452

453453

454+
async def _delete_secret(name, namespace):
455+
secrets = await ekclient.api("v1").resource("secrets")
456+
await secrets.delete(name, namespace=namespace)
457+
458+
454459
async def _get_os_cluster_client():
455460
capoapi = await ekclient.api_preferred_version(CAPO_API_GROUP)
456461
openstackclusters = await capoapi.resource("openstackclusters")
457462
return openstackclusters
463+
464+
465+
async def _get_clouds_secret(secret_name, namespace):
466+
secrets = await ekclient.api("v1").resource("secrets")
467+
try:
468+
return await secrets.fetch(secret_name, namespace=namespace)
469+
except easykube.ApiError as exc:
470+
if exc.status_code != 404:
471+
raise
472+
# TODO(johngarbutt): fail better when not found?

capi_janitor/tests/openstack/test_operator.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import base64
2+
import yaml
13
import unittest
24
from unittest import mock
35

@@ -67,3 +69,79 @@ async def test_on_openstackcluster_event_skip_no_finalizers(
6769
logger.info.assert_has_calls(
6870
[mock.call("janitor finalizer not present, skipping cleanup")]
6971
)
72+
73+
@mock.patch.object(operator, "_delete_secret")
74+
@mock.patch.object(operator, "_get_clouds_secret")
75+
@mock.patch.object(operator, "purge_openstack_resources")
76+
@mock.patch.object(operator, "patch_finalizers")
77+
@mock.patch.object(operator, "_get_os_cluster_client")
78+
async def test_on_openstackcluster_event_calls_purge(
79+
self,
80+
mock_get_client,
81+
mock_patch_finalizers,
82+
mock_purge,
83+
mock_clouds_secret,
84+
mock_delete_secret,
85+
):
86+
logger = mock.Mock()
87+
mock_get_client.return_value = "mock_client"
88+
mock_secret = mock.Mock()
89+
mock_clouds_secret.return_value = mock_secret
90+
clouds_yaml_data = {
91+
"openstack": {
92+
"auth": {
93+
"auth_url": "https://example.com:5000/v3",
94+
"username": "user",
95+
"password": "pass",
96+
"project_name": "project",
97+
"user_domain_name": "Default",
98+
"project_domain_name": "Default",
99+
}
100+
}
101+
}
102+
mock_secret.data = {
103+
"clouds.yaml": base64.b64encode(yaml.dump(clouds_yaml_data).encode("utf-8"))
104+
}
105+
mock_secret.metadata = {
106+
"annotations": {"janitor.capi.stackhpc.com/credential-policy": "delete"},
107+
"name": "appcred42",
108+
}
109+
110+
await operator._on_openstackcluster_event_impl(
111+
name="mycluster",
112+
namespace="namespace1",
113+
meta={
114+
"deletionTimestamp": "2023-10-01T00:00:00Z",
115+
"finalizers": ["janitor.capi.stackhpc.com"],
116+
"annotations": {
117+
"janitor.capi.stackhpc.com/volumes-policy": "delete",
118+
},
119+
},
120+
labels={},
121+
spec={"identityRef": {"name": "appcred42"}},
122+
logger=logger,
123+
body={},
124+
)
125+
126+
mock_purge.assert_awaited_once_with(
127+
logger,
128+
clouds_yaml_data,
129+
"openstack",
130+
None,
131+
"mycluster",
132+
True,
133+
True,
134+
)
135+
mock_delete_secret.assert_awaited_once_with("appcred42", "namespace1")
136+
mock_patch_finalizers.assert_awaited_once_with(
137+
"mock_client", "mycluster", "namespace1", []
138+
)
139+
logger.debug.assert_has_calls(
140+
[mock.call("cluster name that will be used for cleanup: 'mycluster'")]
141+
)
142+
logger.info.assert_has_calls(
143+
[
144+
mock.call("cloud credential secret deleted"),
145+
mock.call("removed janitor finalizer from cluster"),
146+
]
147+
)

0 commit comments

Comments
 (0)