From a72fcd0b022268b60b35129a405022282a1ac522 Mon Sep 17 00:00:00 2001 From: jo Date: Mon, 20 Nov 2023 15:53:50 +0100 Subject: [PATCH 1/9] style: format inventory documentation --- plugins/inventory/hcloud.py | 163 +++++++++++++++++++----------------- 1 file changed, 84 insertions(+), 79 deletions(-) diff --git a/plugins/inventory/hcloud.py b/plugins/inventory/hcloud.py index 267b8946..58bebb87 100644 --- a/plugins/inventory/hcloud.py +++ b/plugins/inventory/hcloud.py @@ -2,85 +2,90 @@ # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) DOCUMENTATION = r""" - name: hcloud - author: - - Lukas Kaemmerling (@lkaemmerling) - short_description: Ansible dynamic inventory plugin for the Hetzner Cloud. - requirements: - - python-dateutil >= 2.7.5 - - requests >=2.20 - description: - - Reads inventories from the Hetzner Cloud API. - - Uses a YAML configuration file that ends with hcloud.(yml|yaml). - extends_documentation_fragment: - - constructed - - inventory_cache - options: - plugin: - description: marks this as an instance of the "hcloud" plugin - required: true - choices: ["hcloud", "hetzner.hcloud.hcloud"] - token: - description: The Hetzner Cloud API Token. - required: false - group: - description: The group all servers are automatically added to. - default: hcloud - type: str - required: false - token_env: - description: Environment variable to load the Hetzner Cloud API Token from. - default: HCLOUD_TOKEN - type: str - required: false - connect_with: - description: | - Connect to the server using the value from this field. This sets the `ansible_host` - variable to the value indicated, if that value is available. If you need further - customization, like falling back to private ipv4 if the server has no public ipv4, - you can use `compose` top-level key. - default: public_ipv4 - type: str - choices: - - public_ipv4 - - public_ipv6 - - hostname - - ipv4_dns_ptr - - private_ipv4 - locations: - description: Populate inventory with instances in this location. - default: [] - type: list - elements: str - required: false - types: - description: Populate inventory with instances with this type. - default: [] - type: list - elements: str - required: false - images: - description: Populate inventory with instances with this image name, only available for system images. - default: [] - type: list - elements: str - required: false - label_selector: - description: Populate inventory with instances with this label. - default: "" - type: str - required: false - network: - description: Populate inventory with instances which are attached to this network name or ID. - default: "" - type: str - required: false - status: - description: Populate inventory with instances with this status. - default: [] - type: list - elements: str - required: false +name: hcloud +short_description: Ansible dynamic inventory plugin for the Hetzner Cloud. + +description: + - Reads inventories from the Hetzner Cloud API. + - Uses a YAML configuration file that ends with hcloud.(yml|yaml). + +author: + - Lukas Kaemmerling (@lkaemmerling) + +requirements: + - python-dateutil >= 2.7.5 + - requests >=2.20 + +extends_documentation_fragment: + - constructed + - inventory_cache + +options: + plugin: + description: marks this as an instance of the "hcloud" plugin + required: true + choices: ["hcloud", "hetzner.hcloud.hcloud"] + token: + description: The Hetzner Cloud API Token. + required: false + group: + description: The group all servers are automatically added to. + default: hcloud + type: str + required: false + token_env: + description: Environment variable to load the Hetzner Cloud API Token from. + default: HCLOUD_TOKEN + type: str + required: false + connect_with: + description: | + Connect to the server using the value from this field. This sets the `ansible_host` + variable to the value indicated, if that value is available. If you need further + customization, like falling back to private ipv4 if the server has no public ipv4, + you can use `compose` top-level key. + default: public_ipv4 + type: str + choices: + - public_ipv4 + - public_ipv6 + - hostname + - ipv4_dns_ptr + - private_ipv4 + locations: + description: Populate inventory with instances in this location. + default: [] + type: list + elements: str + required: false + types: + description: Populate inventory with instances with this type. + default: [] + type: list + elements: str + required: false + images: + description: Populate inventory with instances with this image name, only available for system images. + default: [] + type: list + elements: str + required: false + label_selector: + description: Populate inventory with instances with this label. + default: "" + type: str + required: false + network: + description: Populate inventory with instances which are attached to this network name or ID. + default: "" + type: str + required: false + status: + description: Populate inventory with instances with this status. + default: [] + type: list + elements: str + required: false """ EXAMPLES = r""" From 200eeb041c7cd7d2b6f4bfddde152bb7a1739525 Mon Sep 17 00:00:00 2001 From: jo Date: Tue, 21 Nov 2023 19:47:51 +0100 Subject: [PATCH 2/9] docs: improve inventory documentation --- plugins/inventory/hcloud.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/inventory/hcloud.py b/plugins/inventory/hcloud.py index 58bebb87..ae54f855 100644 --- a/plugins/inventory/hcloud.py +++ b/plugins/inventory/hcloud.py @@ -7,7 +7,7 @@ description: - Reads inventories from the Hetzner Cloud API. - - Uses a YAML configuration file that ends with hcloud.(yml|yaml). + - Uses a YAML configuration file that ends with C(hcloud.yml) or C(hcloud.yaml). author: - Lukas Kaemmerling (@lkaemmerling) @@ -22,9 +22,10 @@ options: plugin: - description: marks this as an instance of the "hcloud" plugin + description: Mark this as an C(hetzner.hcloud.hcloud) inventory instance. required: true - choices: ["hcloud", "hetzner.hcloud.hcloud"] + choices: [hcloud, hetzner.hcloud.hcloud] + token: description: The Hetzner Cloud API Token. required: false @@ -52,6 +53,7 @@ - hostname - ipv4_dns_ptr - private_ipv4 + locations: description: Populate inventory with instances in this location. default: [] From 42db87519e090db3802d7a11fb249d14eefa89d5 Mon Sep 17 00:00:00 2001 From: jo Date: Tue, 21 Nov 2023 19:49:12 +0100 Subject: [PATCH 3/9] refactor: reorder inventory options --- plugins/inventory/hcloud.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/inventory/hcloud.py b/plugins/inventory/hcloud.py index ae54f855..9628a678 100644 --- a/plugins/inventory/hcloud.py +++ b/plugins/inventory/hcloud.py @@ -29,16 +29,17 @@ token: description: The Hetzner Cloud API Token. required: false - group: - description: The group all servers are automatically added to. - default: hcloud - type: str - required: false token_env: description: Environment variable to load the Hetzner Cloud API Token from. default: HCLOUD_TOKEN type: str required: false + + group: + description: The group all servers are automatically added to. + default: hcloud + type: str + required: false connect_with: description: | Connect to the server using the value from this field. This sets the `ansible_host` From 9c771bbf5145af61d69d5c355548f379d0ca425b Mon Sep 17 00:00:00 2001 From: jo Date: Tue, 21 Nov 2023 20:23:21 +0100 Subject: [PATCH 4/9] refactor: add inventory properties type hints --- plugins/inventory/hcloud.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/inventory/hcloud.py b/plugins/inventory/hcloud.py index 9628a678..3d59bb85 100644 --- a/plugins/inventory/hcloud.py +++ b/plugins/inventory/hcloud.py @@ -121,6 +121,7 @@ from ansible.inventory.manager import InventoryData from ansible.module_utils.common.text.converters import to_native 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.vendor import hcloud @@ -178,6 +179,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): NAME = "hetzner.hcloud.hcloud" inventory: InventoryData + display: Display + + client: hcloud.Client def _configure_hcloud_client(self): self.token_env = self.get_option("token_env") From 2bea0d8dc7dc8455000310e01de941e89be43c6d Mon Sep 17 00:00:00 2001 From: jo Date: Tue, 21 Nov 2023 20:33:54 +0100 Subject: [PATCH 5/9] refactor: merge _test_hcloud_token into _configure_hcloud_client --- plugins/inventory/hcloud.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/plugins/inventory/hcloud.py b/plugins/inventory/hcloud.py index 3d59bb85..95194c51 100644 --- a/plugins/inventory/hcloud.py +++ b/plugins/inventory/hcloud.py @@ -204,13 +204,11 @@ def _configure_hcloud_client(self): application_version=version, ) - def _test_hcloud_token(self): try: - # We test the API Token against the location API, because this is the API with the smallest result - # and not controllable from the customer. - self.client.locations.get_all() - except hcloud.APIException: - raise AnsibleError("Invalid Hetzner Cloud API Token.") + # Ensure the api token is valid + self.client.locations.get_list() + except hcloud.APIException as exception: + raise AnsibleError("Invalid Hetzner Cloud API Token.") from exception def _get_servers(self): if len(self.get_option("label_selector")) > 0: @@ -400,7 +398,6 @@ def parse(self, inventory, loader, path, cache=True): self._read_config_data(path) self._configure_hcloud_client() - self._test_hcloud_token() self.servers, cached = self._get_cached_result(path, cache) if not cached: From 887b0922a490ed79fbef95708d4c3538e4821c75 Mon Sep 17 00:00:00 2001 From: jo Date: Tue, 21 Nov 2023 20:35:04 +0100 Subject: [PATCH 6/9] feat: rename inventory api_token options - Rename `token` option to `api_token`, use aliases for backward compatibility. - Rename `token_env` option to `api_token_env`, use aliases for backward compatibility. - Deprecate the `api_token_env` option, suggest using a lookup plugin or use the well-known `HCLOUD_TOKEN` environment variable name. --- plugins/inventory/hcloud.py | 61 ++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/plugins/inventory/hcloud.py b/plugins/inventory/hcloud.py index 95194c51..53245011 100644 --- a/plugins/inventory/hcloud.py +++ b/plugins/inventory/hcloud.py @@ -26,14 +26,27 @@ required: true choices: [hcloud, hetzner.hcloud.hcloud] - token: - description: The Hetzner Cloud API Token. - required: false - token_env: - description: Environment variable to load the Hetzner Cloud API Token from. - default: HCLOUD_TOKEN + api_token: + description: + - The API Token for the Hetzner Cloud. + - You can also set this option by using the C(HCLOUD_TOKEN) environment variable. type: str - required: false + required: false # TODO: Mark as required once I(api_token_env) is removed. + aliases: [token] + env: + - name: HCLOUD_TOKEN + - name: I(api_token_env) # TODO: Remove once I(api_token_env) is removed. + api_token_env: + description: + - Environment variable name to load the Hetzner Cloud API Token from. + type: str + default: HCLOUD_TOKEN + aliases: [token_env] + deprecated: + why: The option is adding too much complexity, while the alternatives are preferred. + collection_name: hetzner.hcloud + version: 3.0.0 + alternatives: Use the ``{{ lookup('ansible.builtin.env', 'YOUR_ENV_VAR') }}`` lookup instead. group: description: The group all servers are automatically added to. @@ -184,21 +197,35 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): client: hcloud.Client def _configure_hcloud_client(self): - self.token_env = self.get_option("token_env") - self.templar.available_variables = self._vars - self.api_token = self.templar.template(self.get_option("token"), fail_on_undefined=False) or os.getenv( - self.token_env - ) - if self.api_token is None: - raise AnsibleError( - "Please specify a token, via the option token, via environment variable HCLOUD_TOKEN " - "or via custom environment variable set by token_env option." + # If api_token_env is not the default, print a deprecation warning and load the + # environment variable. + api_token_env = self.get_option("api_token_env") + if api_token_env != "HCLOUD_TOKEN": + self.display.deprecated( + "The 'api_token_env' option is deprecated, please use the `HCLOUD_TOKEN` " + "environment variable or use the 'ansible.builtin.env' lookup instead.", + version="3.0.0", + collection_name="hetzner.hcloud", ) + if api_token_env in os.environ: + self.set_option("api_token", os.environ.get(api_token_env)) + api_token = self.get_option("api_token") self.endpoint = os.getenv("HCLOUD_ENDPOINT") or "https://api.hetzner.cloud/v1" + if api_token is None: # TODO: Remove once I(api_token_env) is removed. + raise AnsibleError( + "No setting was provided for required configuration setting: " + "plugin_type: inventory " + "plugin: hetzner.hcloud.hcloud " + "setting: api_token" + ) + + # Resolve template string + api_token = self.templar.template(api_token) + self.client = hcloud.Client( - token=self.api_token, + token=api_token, api_endpoint=self.endpoint, application_name="ansible-inventory", application_version=version, From aaafe7023a7dda18e93851f4112807749f6b4b51 Mon Sep 17 00:00:00 2001 From: jo Date: Tue, 21 Nov 2023 20:37:21 +0100 Subject: [PATCH 7/9] feat: add api_endpoint option to inventory plugin --- plugins/inventory/hcloud.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/inventory/hcloud.py b/plugins/inventory/hcloud.py index 53245011..fea7f4ff 100644 --- a/plugins/inventory/hcloud.py +++ b/plugins/inventory/hcloud.py @@ -47,6 +47,14 @@ collection_name: hetzner.hcloud version: 3.0.0 alternatives: Use the ``{{ lookup('ansible.builtin.env', 'YOUR_ENV_VAR') }}`` lookup instead. + api_endpoint: + description: + - The API Endpoint for the Hetzner Cloud. + - You can also set this option by using the C(HCLOUD_ENDPOINT) environment variable. + type: str + default: https://api.hetzner.cloud/v1 + env: + - name: HCLOUD_ENDPOINT group: description: The group all servers are automatically added to. @@ -211,7 +219,7 @@ def _configure_hcloud_client(self): self.set_option("api_token", os.environ.get(api_token_env)) api_token = self.get_option("api_token") - self.endpoint = os.getenv("HCLOUD_ENDPOINT") or "https://api.hetzner.cloud/v1" + api_endpoint = self.get_option("api_endpoint") if api_token is None: # TODO: Remove once I(api_token_env) is removed. raise AnsibleError( @@ -226,7 +234,7 @@ def _configure_hcloud_client(self): self.client = hcloud.Client( token=api_token, - api_endpoint=self.endpoint, + api_endpoint=api_endpoint, application_name="ansible-inventory", application_version=version, ) From b18f44a472e82c00056179fbdbd62dcfd9da4407 Mon Sep 17 00:00:00 2001 From: jo Date: Tue, 21 Nov 2023 20:46:18 +0100 Subject: [PATCH 8/9] chore: add changelog fragment --- changelogs/fragments/improve-inventory-api-options.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 changelogs/fragments/improve-inventory-api-options.yml diff --git a/changelogs/fragments/improve-inventory-api-options.yml b/changelogs/fragments/improve-inventory-api-options.yml new file mode 100644 index 00000000..4a6c019c --- /dev/null +++ b/changelogs/fragments/improve-inventory-api-options.yml @@ -0,0 +1,7 @@ +minor_changes: + - hcloud inventory - Rename the `token` option to `api_token`, use aliases for backward compatibility. + - hcloud inventory - Rename the `token_env` option to `api_token_env`, use aliases for backward compatibility. + - hcloud inventory - Add the `api_endpoint` option. + - hcloud inventory - Deprecate the `api_token_env` option, suggest using a lookup + plugin (`{{ lookup('ansible.builtin.env', 'YOUR_ENV_VAR') }}`) or use the + well-known `HCLOUD_TOKEN` environment variable name. From 5c4d39a625fc51c6710b151417492da6223ca0c3 Mon Sep 17 00:00:00 2001 From: jo Date: Thu, 23 Nov 2023 14:18:56 +0100 Subject: [PATCH 9/9] fix: remove dynamic env name from api_token --- plugins/inventory/hcloud.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/inventory/hcloud.py b/plugins/inventory/hcloud.py index fea7f4ff..f82d128e 100644 --- a/plugins/inventory/hcloud.py +++ b/plugins/inventory/hcloud.py @@ -35,7 +35,6 @@ aliases: [token] env: - name: HCLOUD_TOKEN - - name: I(api_token_env) # TODO: Remove once I(api_token_env) is removed. api_token_env: description: - Environment variable name to load the Hetzner Cloud API Token from.