feat(inventory): improve api options (#397)

##### SUMMARY

- Rename the inventory `token` option to `api_token`, use aliases for
backward compatibility.
- Rename the inventory `token_env` option to `api_token_env`, use
aliases for backward compatibility.
- Deprecate the inventory `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.
- Let ansible parse the options, remove homemade options parsing.
- Improve and document the existing `api_endpoint` option.

##### ISSUE TYPE

- Feature Pull Request

##### COMPONENT NAME

hcloud inventory
This commit is contained in:
Jonas L 2023-11-23 14:29:27 +01:00 committed by GitHub
parent 6581ed50db
commit 9905bd0e01
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 146 additions and 96 deletions

View file

@ -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.

View file

@ -2,85 +2,113 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = r""" DOCUMENTATION = r"""
name: hcloud name: hcloud
author: short_description: Ansible dynamic inventory plugin for the Hetzner Cloud.
- Lukas Kaemmerling (@lkaemmerling)
short_description: Ansible dynamic inventory plugin for the Hetzner Cloud. description:
requirements: - Reads inventories from the Hetzner Cloud API.
- python-dateutil >= 2.7.5 - Uses a YAML configuration file that ends with C(hcloud.yml) or C(hcloud.yaml).
- requests >=2.20
author:
- Lukas Kaemmerling (@lkaemmerling)
requirements:
- python-dateutil >= 2.7.5
- requests >=2.20
extends_documentation_fragment:
- constructed
- inventory_cache
options:
plugin:
description: Mark this as an C(hetzner.hcloud.hcloud) inventory instance.
required: true
choices: [hcloud, hetzner.hcloud.hcloud]
api_token:
description: description:
- Reads inventories from the Hetzner Cloud API. - The API Token for the Hetzner Cloud.
- Uses a YAML configuration file that ends with hcloud.(yml|yaml). - You can also set this option by using the C(HCLOUD_TOKEN) environment variable.
extends_documentation_fragment: type: str
- constructed required: false # TODO: Mark as required once I(api_token_env) is removed.
- inventory_cache aliases: [token]
options: env:
plugin: - name: HCLOUD_TOKEN
description: marks this as an instance of the "hcloud" plugin api_token_env:
required: true description:
choices: ["hcloud", "hetzner.hcloud.hcloud"] - Environment variable name to load the Hetzner Cloud API Token from.
token: type: str
description: The Hetzner Cloud API Token. default: HCLOUD_TOKEN
required: false aliases: [token_env]
group: deprecated:
description: The group all servers are automatically added to. why: The option is adding too much complexity, while the alternatives are preferred.
default: hcloud collection_name: hetzner.hcloud
type: str version: 3.0.0
required: false alternatives: Use the ``{{ lookup('ansible.builtin.env', 'YOUR_ENV_VAR') }}`` lookup instead.
token_env: api_endpoint:
description: Environment variable to load the Hetzner Cloud API Token from. description:
default: HCLOUD_TOKEN - The API Endpoint for the Hetzner Cloud.
type: str - You can also set this option by using the C(HCLOUD_ENDPOINT) environment variable.
required: false type: str
connect_with: default: https://api.hetzner.cloud/v1
description: | env:
Connect to the server using the value from this field. This sets the `ansible_host` - name: HCLOUD_ENDPOINT
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, group:
you can use `compose` top-level key. description: The group all servers are automatically added to.
default: public_ipv4 default: hcloud
type: str type: str
choices: required: false
- public_ipv4 connect_with:
- public_ipv6 description: |
- hostname Connect to the server using the value from this field. This sets the `ansible_host`
- ipv4_dns_ptr variable to the value indicated, if that value is available. If you need further
- private_ipv4 customization, like falling back to private ipv4 if the server has no public ipv4,
locations: you can use `compose` top-level key.
description: Populate inventory with instances in this location. default: public_ipv4
default: [] type: str
type: list choices:
elements: str - public_ipv4
required: false - public_ipv6
types: - hostname
description: Populate inventory with instances with this type. - ipv4_dns_ptr
default: [] - private_ipv4
type: list
elements: str locations:
required: false description: Populate inventory with instances in this location.
images: default: []
description: Populate inventory with instances with this image name, only available for system images. type: list
default: [] elements: str
type: list required: false
elements: str types:
required: false description: Populate inventory with instances with this type.
label_selector: default: []
description: Populate inventory with instances with this label. type: list
default: "" elements: str
type: str required: false
required: false images:
network: description: Populate inventory with instances with this image name, only available for system images.
description: Populate inventory with instances which are attached to this network name or ID. default: []
default: "" type: list
type: str elements: str
required: false required: false
status: label_selector:
description: Populate inventory with instances with this status. description: Populate inventory with instances with this label.
default: [] default: ""
type: list type: str
elements: str required: false
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""" EXAMPLES = r"""
@ -113,6 +141,7 @@ from ansible.errors import AnsibleError
from ansible.inventory.manager import InventoryData from ansible.inventory.manager import InventoryData
from ansible.module_utils.common.text.converters import to_native from ansible.module_utils.common.text.converters import to_native
from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable, Constructable 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.hcloud import HAS_DATEUTIL, HAS_REQUESTS
from ..module_utils.vendor import hcloud from ..module_utils.vendor import hcloud
@ -170,35 +199,50 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
NAME = "hetzner.hcloud.hcloud" NAME = "hetzner.hcloud.hcloud"
inventory: InventoryData inventory: InventoryData
display: Display
client: hcloud.Client
def _configure_hcloud_client(self): def _configure_hcloud_client(self):
self.token_env = self.get_option("token_env") # If api_token_env is not the default, print a deprecation warning and load the
self.templar.available_variables = self._vars # environment variable.
self.api_token = self.templar.template(self.get_option("token"), fail_on_undefined=False) or os.getenv( api_token_env = self.get_option("api_token_env")
self.token_env if api_token_env != "HCLOUD_TOKEN":
) self.display.deprecated(
if self.api_token is None: "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")
api_endpoint = self.get_option("api_endpoint")
if api_token is None: # TODO: Remove once I(api_token_env) is removed.
raise AnsibleError( raise AnsibleError(
"Please specify a token, via the option token, via environment variable HCLOUD_TOKEN " "No setting was provided for required configuration setting: "
"or via custom environment variable set by token_env option." "plugin_type: inventory "
"plugin: hetzner.hcloud.hcloud "
"setting: api_token"
) )
self.endpoint = os.getenv("HCLOUD_ENDPOINT") or "https://api.hetzner.cloud/v1" # Resolve template string
api_token = self.templar.template(api_token)
self.client = hcloud.Client( self.client = hcloud.Client(
token=self.api_token, token=api_token,
api_endpoint=self.endpoint, api_endpoint=api_endpoint,
application_name="ansible-inventory", application_name="ansible-inventory",
application_version=version, application_version=version,
) )
def _test_hcloud_token(self):
try: try:
# We test the API Token against the location API, because this is the API with the smallest result # Ensure the api token is valid
# and not controllable from the customer. self.client.locations.get_list()
self.client.locations.get_all() except hcloud.APIException as exception:
except hcloud.APIException: raise AnsibleError("Invalid Hetzner Cloud API Token.") from exception
raise AnsibleError("Invalid Hetzner Cloud API Token.")
def _get_servers(self): def _get_servers(self):
if len(self.get_option("label_selector")) > 0: if len(self.get_option("label_selector")) > 0:
@ -388,7 +432,6 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
self._read_config_data(path) self._read_config_data(path)
self._configure_hcloud_client() self._configure_hcloud_client()
self._test_hcloud_token()
self.servers, cached = self._get_cached_result(path, cache) self.servers, cached = self._get_cached_result(path, cache)
if not cached: if not cached: