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

refactor: extract client utils in reusable functions #399

Merged
merged 4 commits into from
Nov 24, 2023
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
2 changes: 1 addition & 1 deletion plugins/inventory/hcloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@
from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable, Constructable
from ansible.utils.display import Display

from ..module_utils.hcloud import HAS_DATEUTIL, HAS_REQUESTS
from ..module_utils.client import HAS_DATEUTIL, HAS_REQUESTS
from ..module_utils.vendor import hcloud
from ..module_utils.vendor.hcloud.servers import Server
from ..module_utils.version import version
Expand Down
63 changes: 63 additions & 0 deletions plugins/module_utils/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Copyright: (c) 2023, Hetzner Cloud GmbH <[email protected]>

from __future__ import annotations

from ansible.module_utils.basic import missing_required_lib

from .vendor.hcloud import APIException, Client

HAS_REQUESTS = True
HAS_DATEUTIL = True

try:
import requests # pylint: disable=unused-import
except ImportError:
HAS_REQUESTS = False

try:
import dateutil # pylint: disable=unused-import
except ImportError:
HAS_DATEUTIL = False


class ClientException(Exception):
"""An error related to the client occurred."""


def client_check_required_lib():
if not HAS_REQUESTS:
raise ClientException(missing_required_lib("requests"))
if not HAS_DATEUTIL:
raise ClientException(missing_required_lib("python-dateutil"))


def _client_resource_not_found(resource: str, param: str | int):
return ClientException(f"resource ({resource.rstrip('s')}) does not exist: {param}")


def client_get_by_name_or_id(client: Client, resource: str, param: str | int):
"""
Get a resource by name, and if not found by its ID.

:param client: Client to use to make the call
:param resource: Name of the resource client that implements both `get_by_name` and `get_by_id` methods
:param param: Name or ID of the resource to query
"""
resource_client = getattr(client, resource)

result = resource_client.get_by_name(param)
if result is not None:
return result

# If the param is not a valid ID, prevent an unnecessary call to the API.
try:
int(param)
except ValueError as exception:
raise _client_resource_not_found(resource, param) from exception

try:
return resource_client.get_by_id(param)
except APIException as exception:
if exception.code == "not_found":
raise _client_resource_not_found(resource, param) from exception
raise exception
49 changes: 13 additions & 36 deletions plugins/module_utils/hcloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,14 @@
import traceback
from typing import Any

from ansible.module_utils.basic import (
AnsibleModule as AnsibleModuleBase,
env_fallback,
missing_required_lib,
)
from ansible.module_utils.basic import AnsibleModule as AnsibleModuleBase, env_fallback
from ansible.module_utils.common.text.converters import to_native

from ..module_utils.vendor.hcloud import APIException, Client, HCloudException
from ..module_utils.vendor.hcloud.actions import ActionException
from .client import ClientException, client_check_required_lib, client_get_by_name_or_id
from .vendor.hcloud import APIException, Client, HCloudException
from .vendor.hcloud.actions import ActionException
from .version import version

HAS_REQUESTS = True
HAS_DATEUTIL = True

try:
import requests # pylint: disable=unused-import
except ImportError:
HAS_REQUESTS = False

try:
import dateutil # pylint: disable=unused-import
except ImportError:
HAS_DATEUTIL = False


# Provide typing definitions to the AnsibleModule class
class AnsibleModule(AnsibleModuleBase):
Expand All @@ -49,10 +33,12 @@ def __init__(self, module: AnsibleModule):

self.module = module
self.result = {"changed": False, self.represent: None}
if not HAS_REQUESTS:
module.fail_json(msg=missing_required_lib("requests"))
if not HAS_DATEUTIL:
module.fail_json(msg=missing_required_lib("python-dateutil"))

try:
client_check_required_lib()
except ClientException as exception:
module.fail_json(msg=to_native(exception))

self._build_client()

def fail_json_hcloud(
Expand Down Expand Up @@ -100,19 +86,10 @@ def _client_get_by_name_or_id(self, resource: str, param: str | int):
:param resource: Name of the resource client that implements both `get_by_name` and `get_by_id` methods
:param param: Name or ID of the resource to query
"""
resource_client = getattr(self.client, resource)

result = resource_client.get_by_name(param)
if result is not None:
return result

# If the param is not a valid ID, prevent an unnecessary call to the API.
try:
int(param)
except ValueError:
self.module.fail_json(msg=f"resource ({resource.rstrip('s')}) does not exist: {param}")

return resource_client.get_by_id(param)
return client_get_by_name_or_id(self.client, resource, param)
except ClientException as exception:
self.module.fail_json(msg=to_native(exception))

def _mark_as_changed(self) -> None:
self.result["changed"] = True
Expand Down
Loading