etcd3 lookup - improve HTTPS connection handling and docs (#11861)

* etcd3 lookup - improve HTTPS connection handling and documentation

Improve user experience when connecting to HTTPS etcd3 endpoints:
- Strip URL scheme from host option when present, with a warning
- Warn when HTTPS endpoint is specified but ca_cert is not provided
- Document that ca_cert is required to enable TLS
- Add HTTPS connection example
- Fix minor doc markup issue in notes section

Fixes #1664

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add changelog fragment for PR 11861

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Alexei Znamensky
2026-04-17 23:56:57 +12:00
committed by GitHub
parent 342a76d5dd
commit 076bc4e03b
2 changed files with 42 additions and 2 deletions

View File

@@ -0,0 +1,6 @@
bugfixes:
- >-
etcd3 lookup plugin - improve HTTPS endpoint handling by stripping URL schemes from the ``host`` option
and warning when ``ca_cert`` is not provided for HTTPS endpoints
(https://github.com/ansible-collections/community.general/issues/1664,
https://github.com/ansible-collections/community.general/pull/11861).

View File

@@ -34,6 +34,8 @@ options:
V(https://hostname:2379), or V(<host>:<port>) form.
- The V(host) part is overwritten by O(host) option, if defined.
- The V(port) part is overwritten by O(port) option, if defined.
- Note that specifying V(https://) in the endpoint URL does not by itself enable TLS. To connect to an HTTPS etcd3
endpoint, you must provide O(ca_cert) (and optionally O(cert_cert) and O(cert_key)).
env:
- name: ETCDCTL_ENDPOINTS
default: '127.0.0.1:2379'
@@ -42,6 +44,8 @@ options:
description:
- Etcd3 listening client host.
- Takes precedence over O(endpoints).
- Must be a bare hostname or IP address without a URL scheme. If a V(https://) or V(http://) prefix is present,
it will be stripped automatically.
type: str
port:
description:
@@ -51,6 +55,7 @@ options:
ca_cert:
description:
- Etcd3 CA authority.
- Required to enable TLS when connecting to an HTTPS etcd3 endpoint.
env:
- name: ETCDCTL_CACERT
type: str
@@ -87,9 +92,11 @@ options:
type: str
notes:
- O(host) and O(port) options take precedence over (endpoints) option.
- O(host) and O(port) options take precedence over O(endpoints) option.
- The recommended way to connect to etcd3 server is using E(ETCDCTL_ENDPOINT) environment variable and keep O(endpoints),
O(host), and O(port) unused.
- To connect to an HTTPS etcd3 endpoint, the O(ca_cert) option must be provided. Merely specifying V(https://) in
O(endpoints) is not sufficient to enable TLS.
seealso:
- module: community.general.etcd3
- plugin: community.general.etcd
@@ -115,6 +122,10 @@ EXAMPLES = r"""
- name: "connect to etcd3 with a client certificate"
ansible.builtin.debug:
msg: "{{ lookup('community.general.etcd3', 'foo/bar', cert_cert='/etc/ssl/etcd/client.pem', cert_key='/etc/ssl/etcd/client.key') }}"
- name: "connect to etcd3 over HTTPS"
ansible.builtin.debug:
msg: "{{ lookup('community.general.etcd3', 'foo/bar', endpoints='https://etcd.example.com:2379', ca_cert='/etc/ssl/etcd/ca.pem') }}"
"""
RETURN = r"""
@@ -185,19 +196,42 @@ class LookupModule(LookupBase):
# etcd3 class expects host and port as connection parameters, so endpoints
# must be mangled a bit to fit in this scheme.
# so here we use a regex to extract server and port
is_https = False
match = re.compile(
r"^(https?://)?(?P<host>(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([-_\d\w\.]+))(:(?P<port>\d{1,5}))?/?$"
r"^(?P<scheme>https?://)?(?P<host>(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([-_\d\w\.]+))(:(?P<port>\d{1,5}))?/?$"
).match(self.get_option("endpoints"))
if match:
if match.group("host"):
client_params["host"] = match.group("host")
if match.group("port"):
client_params["port"] = match.group("port")
if match.group("scheme") and match.group("scheme").startswith("https"):
is_https = True
for opt in etcd3_cnx_opts:
if self.get_option(opt):
client_params[opt] = self.get_option(opt)
# strip URL scheme from host if present
if "host" in client_params:
host_match = re.match(r"^(?P<scheme>https?://)(?P<host>.+)$", client_params["host"])
if host_match:
display.warning(
f"The host option contained a URL scheme '{host_match.group('scheme')}' which has been removed. "
"Use a bare hostname or IP address for the host option. "
"To enable TLS, provide the ca_cert option."
)
client_params["host"] = host_match.group("host")
if host_match.group("scheme").startswith("https"):
is_https = True
if is_https and not self.get_option("ca_cert"):
display.warning(
"An HTTPS endpoint was specified but ca_cert was not provided. "
"The connection will be attempted without TLS. "
"To enable TLS, provide the ca_cert option."
)
cnx_log = dict(client_params)
if "password" in cnx_log:
cnx_log["password"] = "<redacted>"