From 726f625c9f157dd202e65ea8f35ac571fb4b565f Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Thu, 14 Sep 2023 14:00:42 +0200 Subject: [PATCH] Add WebHookDockerDesktopServer Docker Desktop now uses the host.docker.internal host address and this can be used to help develop hooks locally in a fashion similar to k3d and Minikube. Signed-off-by: Samuel Gaist --- docs/admission.rst | 2 ++ examples/17-admission/example.py | 3 +++ kopf/__init__.py | 1 + kopf/_cogs/configs/configuration.py | 2 +- kopf/_kits/webhooks.py | 19 +++++++++++++++++++ tests/admission/test_webhook_server.py | 4 +++- 6 files changed, 29 insertions(+), 2 deletions(-) diff --git a/docs/admission.rst b/docs/admission.rst index e542806c..45d90e4c 100644 --- a/docs/admission.rst +++ b/docs/admission.rst @@ -366,6 +366,8 @@ each with its configuration parameters (see their descriptions): accessing the server via a magical hostname ``host.k3d.internal``. * :class:`kopf.WebhookMinikubeServer` for local Minikube clusters (even in VMs), accessing the server via a magical hostname ``host.minikube.internal``. +* :class:`kopf.WebhookDockerDesktopServer` for the DockerDesktop cluster, + accessing the server via a magical hostname ``host.docker.internal``. *Webhook tunnels* forward the webhook requests through external endpoints usually to a locally running *webhook server*. diff --git a/examples/17-admission/example.py b/examples/17-admission/example.py index cfb579d7..b56e7910 100644 --- a/examples/17-admission/example.py +++ b/examples/17-admission/example.py @@ -24,6 +24,9 @@ def config(settings: kopf.OperatorSettings, **_): # Minikube-specific server that supports accessing from inside of a VM (a generated certificate): settings.admission.server = kopf.WebhookMinikubeServer(port=1234, cadump=ROOT/'ca.pem') + # DockerDesktop-specific server that supports accessing from the host: + settings.admission.server = kopf.WebhookDockerDesktopServer(port=1234) + # Tunneling Kubernetes->ngrok->local server (anonymous, auto-loaded binary): settings.admission.server = kopf.WebhookNgrokTunnel(path='/xyz', port=1234) diff --git a/kopf/__init__.py b/kopf/__init__.py index 49b0abe1..83bc3a12 100644 --- a/kopf/__init__.py +++ b/kopf/__init__.py @@ -174,6 +174,7 @@ WebhookServer, WebhookK3dServer, WebhookMinikubeServer, + WebhookDockerDesktopServer, WebhookNgrokTunnel, WebhookAutoServer, WebhookAutoTunnel, diff --git a/kopf/_cogs/configs/configuration.py b/kopf/_cogs/configs/configuration.py index bee55f90..d76c1b09 100644 --- a/kopf/_cogs/configs/configuration.py +++ b/kopf/_cogs/configs/configuration.py @@ -266,7 +266,7 @@ class AdmissionSettings: Kopf provides several webhook configs, servers, and tunnels out of the box (they also serve as examples for implementing custom tunnels). `kopf.WebhookServer`, - `kopf.WebhookK3dServer`, `kopf.WebhookMinikubeServer`, + `kopf.WebhookK3dServer`, `kopf.WebhookMinikubeServer`, `kopf.WebhookDockerDesktopServer`, `kopf.WebhookNgrokTunnel`, `kopf.WebhookInletsTunnel`. .. seealso:: diff --git a/kopf/_kits/webhooks.py b/kopf/_kits/webhooks.py index 1f934748..a7fb7ef0 100644 --- a/kopf/_kits/webhooks.py +++ b/kopf/_kits/webhooks.py @@ -439,6 +439,22 @@ class WebhookMinikubeServer(WebhookServer): DEFAULT_HOST = 'host.minikube.internal' +class WebhookDockerDesktopServer(WebhookServer): + """A tunnel from inside of Docker Desktop to its host where the operator is + running. + + With this tunnel, a developer can develop the webhooks when fully + offline, since all the traffic is local and never leaves the host + machine. + + The forwarding is maintained by Docker Desktop itself. This tunnel + only replaces the endpoints for the Kubernetes webhook and injects + an SSL certificate with proper CN/SANs --- to match Kubernetes's SSL + validity expectations. + """ + DEFAULT_HOST = "host.docker.internal" + + class WebhookNgrokTunnel(webhacks.WebhookContextManager): """ Tunnel admission webhook request via an external tunnel: ngrok_. @@ -582,6 +598,9 @@ async def guess_host() -> Optional[str]: return WebhookK3dServer.DEFAULT_HOST elif subject_cn == 'minikube' or issuer_cn == 'minikubeCA': return WebhookMinikubeServer.DEFAULT_HOST + elif any(alt_name for alt_name in certpath.first.subject_alt_name_value.native if "docker" in alt_name): + return WebhookDockerDesktopServer.DEFAULT_HOST + else: # The default timeouts & backoffs are used to retrieve the cluster # version, not those from the operator. It is too difficult to get diff --git a/tests/admission/test_webhook_server.py b/tests/admission/test_webhook_server.py index 9275448c..f1b6d20f 100644 --- a/tests/admission/test_webhook_server.py +++ b/tests/admission/test_webhook_server.py @@ -7,7 +7,8 @@ from kopf._core.engines.admission import AmbiguousResourceError, MissingDataError, \ UnknownResourceError, WebhookError -from kopf._kits.webhooks import WebhookK3dServer, WebhookMinikubeServer, WebhookServer +from kopf._kits.webhooks import WebhookDockerDesktopServer, WebhookK3dServer, \ + WebhookMinikubeServer, WebhookServer async def test_starts_as_http_ipv4(responder): @@ -72,6 +73,7 @@ async def test_webhookserver_starts_as_https_with_provided_cert( @pytest.mark.parametrize('cls, url', [ (WebhookK3dServer, 'https://host.k3d.internal:22533/p1/p2'), (WebhookMinikubeServer, 'https://host.minikube.internal:22533/p1/p2'), + (WebhookDockerDesktopServer, 'https://host.docker.internal:22533/p1/p2'), ]) async def test_webhookserver_flavours_inject_hostnames( certfile, pkeyfile, certpkey, responder, cls, url):