Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Ansible module 'WaitForCustomResourceModule' #178

Merged
merged 1 commit into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions cpo/lib/ansible/modules/abstract_module.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2022 IBM Corporation
# Copyright 2022, 2024 IBM Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -87,13 +87,13 @@ def _wait_for_custom_resource(
self,
kind_metadata: KindMetadata,
log_callback: Callable[[int, str], None],
success_callback: Callable[..., bool],
success_callback: Callable[..., Optional[CustomResourceEventResult]],
**kwargs,
):
) -> CustomResourceEventResult:
custom_objects_api = client.CustomObjectsApi()
succeeded = False
custom_resource_event_result: Optional[CustomResourceEventResult] = None

while not succeeded:
while custom_resource_event_result is None:
try:
resource_version: Optional[str] = None
w = watch.Watch()
Expand All @@ -106,16 +106,18 @@ def _wait_for_custom_resource(
resource_version=resource_version,
):
resource_version = cpo.lib.jmespath.get_jmespath_string("object.metadata.resourceVersion", event)
succeeded = success_callback(event, **kwargs)
custom_resource_event_result = success_callback(event, kind_metadata=kind_metadata, **kwargs)

if succeeded:
if custom_resource_event_result is not None:
w.stop()
except client.ApiException as exception:
self._handle_api_exception(exception, log_callback)
resource_version = None
except urllib3.exceptions.ProtocolError as exception:
self._handle_protocol_error(exception, log_callback)

return custom_resource_event_result

def _wait_for_namespaced_custom_resource(
self,
project: str,
Expand Down
130 changes: 130 additions & 0 deletions cpo/lib/ansible/modules/wait_for_custom_resource.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Copyright 2024 IBM Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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.

import logging

from dataclasses import dataclass
from typing import Any, Optional

from ansible.module_utils.basic import AnsibleModule

import cpo.lib.jmespath

from cpo.lib.ansible.modules.abstract_module import AbstractModule
from cpo.lib.ansible.modules.custom_resource_event_result import CustomResourceEventResult
from cpo.lib.jmespath import JmespathPathExpressionNotFoundException
from cpo.lib.openshift.types.kind_metadata import KindMetadata


@dataclass
class CustomResourceEventData:
custom_resource_name: str


class WaitForCustomResourceModule(AbstractModule):
def __init__(self):
argument_spec = {
"custom_resource_name": {
"type": "str",
"required": True,
},
"group": {
"type": "str",
"required": True,
},
"jmespath_expression": {
"type": "str",
"required": False,
},
"kind": {
"type": "str",
"required": True,
},
"kubeconfig": {
"type": "dict",
"required": True,
},
"plural": {
"type": "str",
"required": True,
},
"version": {
"type": "str",
"required": True,
},
}

self._module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
super().__init__(self._module.params["kubeconfig"]) # type: ignore

self._custom_resource_name: str = self._module.params["custom_resource_name"] # type: ignore
self._jmespath_expression: Optional[str] = self._module.params["jmespath_expression"] # type: ignore
self._kind_metadata = KindMetadata(
self._module.params["group"], # type: ignore
self._module.params["kind"], # type: ignore
self._module.params["plural"], # type: ignore
self._module.params["version"], # type: ignore
)

# override
def get_module(self) -> AnsibleModule:
return self._module

# override
def run(self):
result: Optional[dict]

try:
self._wait_for_custom_resource(
self._kind_metadata,
self._log,
self._success_callback,
# passed to _add_event_indicates_custom_resource_definitions_are_created
custom_resource_event_data=CustomResourceEventData(custom_resource_name=self._custom_resource_name),
)

result = {"changed": False}
except Exception as exception:
self._log(logging.ERROR, str(exception))

result = {"changed": False, "failed": True}

self._module.exit_json(**result)

def _success_callback(
self, event: Any, kind_metadata: KindMetadata, custom_resource_event_data: CustomResourceEventData
) -> Optional[CustomResourceEventResult]:
custom_resource_event_result: Optional[CustomResourceEventResult] = None

if (event["type"] == "ADDED") or (event["type"] == "MODIFIED"):
resource_name = cpo.lib.jmespath.get_jmespath_string("object.metadata.name", event)

try:
if resource_name == custom_resource_event_data.custom_resource_name:
if (self._jmespath_expression is None) or cpo.lib.jmespath.get_jmespath_bool(
self._jmespath_expression, event
):
custom_resource_event_result = CustomResourceEventResult(True)
except JmespathPathExpressionNotFoundException:
pass

return custom_resource_event_result


def main():
WaitForCustomResourceModule().run()


if __name__ == "__main__":
main()
23 changes: 15 additions & 8 deletions cpo/lib/ansible/modules/wait_for_custom_resource_definitions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2022, 2023 IBM Corporation
# Copyright 2022, 2024 IBM Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -22,6 +22,7 @@
import cpo.lib.jmespath

from cpo.lib.ansible.modules.abstract_module import AbstractModule
from cpo.lib.ansible.modules.custom_resource_event_result import CustomResourceEventResult
from cpo.lib.openshift.types.kind_metadata import KindMetadata


Expand Down Expand Up @@ -77,14 +78,19 @@ def run(self):
)

result = {"changed": False}
except Exception:
except Exception as exception:
self._log(logging.ERROR, str(exception))

result = {"changed": False, "failed": True}

self._module.exit_json(**result)

def _add_event_indicates_custom_resource_definitions_are_created(
self, event: Any, custom_resource_definitions_event_data: CustomResourceDefinitionsEventData
) -> bool:
self,
event: Any,
kind_metadata: KindMetadata,
custom_resource_definitions_event_data: CustomResourceDefinitionsEventData,
) -> Optional[CustomResourceEventResult]:
"""Callback for checking whether the given set of expected custom
resource definitions was created

Expand All @@ -106,7 +112,7 @@ def _add_event_indicates_custom_resource_definitions_are_created(
created
"""

custom_resource_definitions_are_created = False
custom_resource_event_result: Optional[CustomResourceEventResult] = None

if event["type"] == "ADDED":
encountered_crd_kind = cpo.lib.jmespath.get_jmespath_string("object.spec.names.kind", event)
Expand All @@ -115,12 +121,13 @@ def _add_event_indicates_custom_resource_definitions_are_created(
self._log(logging.DEBUG, f"Detected creation of custom resource definition '{encountered_crd_kind}'")
custom_resource_definitions_event_data.encountered_crd_kinds.add(encountered_crd_kind)

custom_resource_definitions_are_created = (
if (
custom_resource_definitions_event_data.encountered_crd_kinds
== custom_resource_definitions_event_data.expected_crd_kinds
)
):
custom_resource_event_result = CustomResourceEventResult(True)

return custom_resource_definitions_are_created
return custom_resource_event_result


def main():
Expand Down
Loading