Repository: kubernetes-sigs/external-dns (language: go)

Issue:
Release chart for `v0.14.0`
Hopefully with https://github.com/kubernetes-sigs/external-dns/pull/3966 sorted as well 😄 


Discussion / hints:

Fix patch:
diff --git a/charts/external-dns/CHANGELOG.md b/charts/external-dns/CHANGELOG.md
index 6415fc6990..f92f22a9b0 100644
--- a/charts/external-dns/CHANGELOG.md
+++ b/charts/external-dns/CHANGELOG.md
@@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 - Added the option to explicitly enable or disable service account token automounting. ([#3983](https://github.com/kubernetes-sigs/external-dns/pull/3983)) [@gilles-gosuin](https://github.com/gilles-gosuin)
 - Added the option to configure revisionHistoryLimit on the K8s Deployment resource. ([#4008](https://github.com/kubernetes-sigs/external-dns/pull/4008)) [@arnisoph](https://github.com/arnisoph)
+- Added support for webhook providers, as a sidecar. ([#4032](https://github.com/kubernetes-sigs/external-dns/pull/4032) [@mloiseleur](https://github.com/mloiseleur)
 - Added the option to configure ipFamilyPolicy and ipFamilies of external-dns Service.  ([#4153](https://github.com/kubernetes-sigs/external-dns/pull/4153)) [@dongjiang1989](https://github.com/dongjiang1989)
 
 ### Changed
diff --git a/charts/external-dns/README.md b/charts/external-dns/README.md
index 8ac2a3cac9..8bee5d828a 100644
--- a/charts/external-dns/README.md
+++ b/charts/external-dns/README.md
@@ -32,15 +32,14 @@ helm upgrade --install external-dns external-dns/external-dns --version 1.13.1
 
 ## Providers
 
-Configuring the _ExternalDNS_ provider should be done via the `provider.name` value with provider specific configuration being set via the
-`provider.<name>.<key>` values, where supported, and the `extraArgs` value. For legacy support `provider` can be set to the name of the
-provider with all additional configuration being set via the `extraArgs` value.
+Configuring the _ExternalDNS_ provider should be done via the `provider.name` value with provider specific configuration being set via the `provider.<name>.<key>` values, where supported, and the `extraArgs` value. For legacy support `provider` can be set to the name of the provider with all additional configuration being set via the `extraArgs` value.
+See [documentation](https://kubernetes-sigs.github.io/external-dns/#new-providers) for more info on available providers and tutorials.
 
 ### Providers with Specific Configuration Support
 
 | Provider               | Supported  |
 |------------------------|------------|
-| `webhook`              | ❌         |
+| `webhook`              | ✅         |
 
 ## Namespaced Scoped Installation
 
@@ -108,10 +107,21 @@ If `namespaced` is set to `true`, please ensure that `sources` my only contains
 | podSecurityContext | object | See _values.yaml_ | [Pod security context](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#podsecuritycontext-v1-core), this supports full customisation. |
 | policy | string | `"upsert-only"` | How DNS records are synchronized between sources and providers; available values are `sync` & `upsert-only`. |
 | priorityClassName | string | `nil` | Priority class name for the `Pod`. |
-| provider.name | string | `"aws"` | _ExternalDNS_ provider name; for the available providers and how to configure them see the [README](https://github.com/kubernetes-sigs/external-dns#deploying-to-a-cluster). |
+| provider.name | string | `"aws"` | _ExternalDNS_ provider name; for the available providers and how to configure them see [README](https://github.com/kubernetes-sigs/external-dns/blob/master/charts/external-dns/README.md#providers). |
+| provider.webhook.args | list | `[]` | Extra arguments to provide for the `webhook` container. |
+| provider.webhook.env | list | `[]` | [Environment variables](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) for the `webhook` container. |
+| provider.webhook.extraVolumeMounts | list | `[]` | Extra [volume mounts](https://kubernetes.io/docs/concepts/storage/volumes/) for the `webhook` container. |
+| provider.webhook.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy for the `webhook` container. |
+| provider.webhook.image.repository | string | `nil` | Image repository for the `webhook` container. |
+| provider.webhook.image.tag | string | `nil` | Image tag for the `webhook` container. |
+| provider.webhook.livenessProbe | object | See _values.yaml_ | [Liveness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) configuration for the `external-dns` container. |
+| provider.webhook.readinessProbe | object | See _values.yaml_ | [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) configuration for the `webhook` container. |
+| provider.webhook.resources | object | `{}` | [Resources](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the `webhook` container. |
+| provider.webhook.securityContext | object | See _values.yaml_ | [Pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) for the `webhook` container. |
+| provider.webhook.serviceMonitor | object | See _values.yaml_ | Optional [Service Monitor](https://prometheus-operator.dev/docs/operator/design/#servicemonitor) configuration for the `webhook` container. |
 | rbac.additionalPermissions | list | `[]` | Additional rules to add to the `ClusterRole`. |
 | rbac.create | bool | `true` | If `true`, create a `ClusterRole` & `ClusterRoleBinding` with access to the Kubernetes API. |
-| readinessProbe | object | See _values.yaml_ | Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) configuration for the `external-dns` container. |
+| readinessProbe | object | See _values.yaml_ | [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) configuration for the `external-dns` container. |
 | registry | string | `"txt"` | Specify the registry for storing ownership and labels. Valid values are `txt`, `aws-sd`, `dynamodb` & `noop`. |
 | resources | object | `{}` | [Resources](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the `external-dns` container. |
 | revisionHistoryLimit | int | `nil` | Specify the number of old `ReplicaSets` to retain to allow rollback of the `Deployment``. |
@@ -119,7 +129,7 @@ If `namespaced` is set to `true`, please ensure that `sources` my only contains
 | secretConfiguration.enabled | bool | `false` | If `true`, create a `Secret` to store sensitive provider configuration (**DEPRECATED**). |
 | secretConfiguration.mountPath | string | `nil` | Mount path for the `Secret`, this can be templated. |
 | secretConfiguration.subPath | string | `nil` | Sub-path for mounting the `Secret`, this can be templated. |
-| securityContext | object | See _values.yaml_ | [Security context](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#securitycontext-v1-core) for the `external-dns` container. |
+| securityContext | object | See _values.yaml_ | [Security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) for the `external-dns` container. |
 | service.annotations | object | `{}` | Service annotations. |
 | service.ipFamilies | list | `[]` | Service IP families. |
 | service.ipFamilyPolicy | string | `nil` | Service IP family policy. |
diff --git a/charts/external-dns/README.md.gotmpl b/charts/external-dns/README.md.gotmpl
index 40dd65f125..675835de8f 100644
--- a/charts/external-dns/README.md.gotmpl
+++ b/charts/external-dns/README.md.gotmpl
@@ -27,15 +27,14 @@ helm upgrade --install {{ template "chart.name" . }} external-dns/{{ template "c
 
 ## Providers
 
-Configuring the _ExternalDNS_ provider should be done via the `provider.name` value with provider specific configuration being set via the
-`provider.<name>.<key>` values, where supported, and the `extraArgs` value. For legacy support `provider` can be set to the name of the
-provider with all additional configuration being set via the `extraArgs` value.
+Configuring the _ExternalDNS_ provider should be done via the `provider.name` value with provider specific configuration being set via the `provider.<name>.<key>` values, where supported, and the `extraArgs` value. For legacy support `provider` can be set to the name of the provider with all additional configuration being set via the `extraArgs` value.
+See [documentation](https://kubernetes-sigs.github.io/external-dns/#new-providers) for more info on available providers and tutorials.
 
 ### Providers with Specific Configuration Support
 
 | Provider               | Supported  |
 |------------------------|------------|
-| `webhook`              | ❌         |
+| `webhook`              | ✅         |
 
 ## Namespaced Scoped Installation
 
diff --git a/charts/external-dns/templates/_helpers.tpl b/charts/external-dns/templates/_helpers.tpl
index a24eec53f6..3ce55cd8a3 100644
--- a/charts/external-dns/templates/_helpers.tpl
+++ b/charts/external-dns/templates/_helpers.tpl
@@ -81,3 +81,15 @@ Provider name, Keeps backward compatibility on provider
 {{- .Values.provider.name }}
 {{- end }}
 {{- end }}
+
+{{/*
+The image to use for optional webhook sidecar
+*/}}
+{{- define "external-dns.webhookImage" -}}
+{{- with .image }}
+{{- if or (empty .repository) (empty .tag) }}
+{{- fail "ERROR: webhook provider needs an image repository and a tag" }}
+{{- end }}
+{{- printf "%s:%s" .repository .tag }}
+{{- end }}
+{{- end }}
diff --git a/charts/external-dns/templates/deployment.yaml b/charts/external-dns/templates/deployment.yaml
index 214fca9fa7..7a9eead35d 100644
--- a/charts/external-dns/templates/deployment.yaml
+++ b/charts/external-dns/templates/deployment.yaml
@@ -1,3 +1,4 @@
+{{- $providerName := include "external-dns.providerName" . }}
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -103,7 +104,7 @@ spec:
             {{- range .Values.domainFilters }}
             - --domain-filter={{ . }}
             {{- end }}
-            - --provider={{ include "external-dns.providerName" . }}
+            - --provider={{ $providerName }}
           {{- range .Values.extraArgs }}
             - {{ tpl . $ }}
           {{- end }}
@@ -132,6 +133,43 @@ spec:
           resources:
             {{- toYaml . | nindent 12 }}
           {{- end }}
+        {{- if eq $providerName "webhook" }}
+        {{- with .Values.provider.webhook }}
+        - name: webhook
+          image: {{ include "external-dns.webhookImage" . }}
+          imagePullPolicy: {{ $.Values.image.pullPolicy }}
+          {{- with .env }}
+          env:
+            {{- toYaml . | nindent 12 }}
+          {{- end }}
+          {{- with .args }}
+          args:
+            - {{ toYaml . | nindent 12 }}
+          {{- end }}
+          ports:
+            - name: http-webhook
+              protocol: TCP
+              containerPort: 8888
+            - name: http-webhook-metrics
+              protocol: TCP
+              containerPort: 8080
+          livenessProbe:
+            {{- toYaml .livenessProbe | nindent 12 }}
+          readinessProbe:
+            {{- toYaml .readinessProbe | nindent 12 }}
+          {{- if .extraVolumeMounts }}
+          volumeMounts:
+            {{- with .extraVolumeMounts }}
+            {{- toYaml . | nindent 12 }}
+            {{- end }}
+          {{- end }}
+          {{- with .securityContext }}
+          securityContext:
+            {{- toYaml . | nindent 12 }}
+          {{- end }}
+        {{- end }}
+        {{- end }}
+
       {{- if or .Values.secretConfiguration.enabled .Values.extraVolumes }}
       volumes:
         {{- if .Values.secretConfiguration.enabled }}
diff --git a/charts/external-dns/templates/servicemonitor.yaml b/charts/external-dns/templates/servicemonitor.yaml
index ad11a99084..318c0fe529 100644
--- a/charts/external-dns/templates/servicemonitor.yaml
+++ b/charts/external-dns/templates/servicemonitor.yaml
@@ -1,4 +1,5 @@
 {{- if .Values.serviceMonitor.enabled -}}
+{{- $providerName := include "external-dns.providerName" . }}
 apiVersion: monitoring.coreos.com/v1
 kind: ServiceMonitor
 metadata:
@@ -48,6 +49,36 @@ spec:
       relabelings:
         {{- toYaml . | nindent 8 }}
       {{- end }}
+    {{- if eq $providerName "webhook" }}
+    {{- with .Values.provider.webhook.serviceMonitor }}
+    - port: webhook-metrics
+      path: /metrics
+      {{- with .interval }}
+      interval: {{ . }}
+      {{- end }}
+      {{- with .scheme }}
+      scheme: {{ . }}
+      {{- end }}
+      {{- with .bearerTokenFile }}
+      bearerTokenFile: {{ . }}
+      {{- end }}
+      {{- with .tlsConfig }}
+      tlsConfig:
+        {{- toYaml .| nindent 8 }}
+      {{- end }}
+      {{- with .scrapeTimeout }}
+      scrapeTimeout: {{ . }}
+      {{- end }}
+      {{- with .metricRelabelings }}
+      metricRelabelings:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .relabelings }}
+      relabelings:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+    {{- end }}
+    {{- end }}
   {{- with .Values.serviceMonitor.targetLabels }}
   targetLabels:
     {{- toYaml . | nindent 4 }}
diff --git a/charts/external-dns/values.yaml b/charts/external-dns/values.yaml
index 2c3a976ee6..277a485ce2 100644
--- a/charts/external-dns/values.yaml
+++ b/charts/external-dns/values.yaml
@@ -92,7 +92,7 @@ dnsPolicy:
 # -- [Init containers](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) to add to the `Pod` definition.
 initContainers: []
 
-# -- [Security context](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#securitycontext-v1-core) for the `external-dns` container.
+# -- [Security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) for the `external-dns` container.
 # @default -- See _values.yaml_
 securityContext:
   privileged: false
@@ -119,7 +119,7 @@ livenessProbe:
   failureThreshold: 2
   successThreshold: 1
 
-# -- Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) configuration for the `external-dns` container.
+# -- [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) configuration for the `external-dns` container.
 # @default -- See _values.yaml_
 readinessProbe:
   httpGet:
@@ -217,8 +217,59 @@ txtSuffix:
 domainFilters: []
 
 provider:
-  # -- _ExternalDNS_ provider name; for the available providers and how to configure them see the [README](https://github.com/kubernetes-sigs/external-dns#deploying-to-a-cluster).
+  # -- _ExternalDNS_ provider name; for the available providers and how to configure them see [README](https://github.com/kubernetes-sigs/external-dns/blob/master/charts/external-dns/README.md#providers).
   name: aws
+  webhook:
+    image:
+      # -- (string) Image repository for the `webhook` container.
+      repository:
+      # -- (string) Image tag for the `webhook` container.
+      tag:
+      # -- Image pull policy for the `webhook` container.
+      pullPolicy: IfNotPresent
+    # -- [Environment variables](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) for the `webhook` container.
+    env: []
+    # -- Extra arguments to provide for the `webhook` container.
+    args: []
+    # -- Extra [volume mounts](https://kubernetes.io/docs/concepts/storage/volumes/) for the `webhook` container.
+    extraVolumeMounts: []
+    # -- [Resources](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the `webhook` container.
+    resources: {}
+    # -- [Pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) for the `webhook` container.
+    # @default -- See _values.yaml_
+    securityContext: {}
+    # -- [Liveness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) configuration for the `external-dns` container.
+    # @default -- See _values.yaml_
+    livenessProbe:
+      httpGet:
+        path: /healthz
+        port: http-webhook
+      initialDelaySeconds: 10
+      periodSeconds: 10
+      timeoutSeconds: 5
+      failureThreshold: 2
+      successThreshold: 1
+    # -- [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) configuration for the `webhook` container.
+    # @default -- See _values.yaml_
+    readinessProbe:
+      httpGet:
+        path: /healthz
+        port: http-webhook
+      initialDelaySeconds: 5
+      periodSeconds: 10
+      timeoutSeconds: 5
+      failureThreshold: 6
+      successThreshold: 1
+    # -- Optional [Service Monitor](https://prometheus-operator.dev/docs/operator/design/#servicemonitor) configuration for the `webhook` container.
+    # @default -- See _values.yaml_
+    serviceMonitor:
+      interval:
+      scheme:
+      tlsConfig: {}
+      bearerTokenFile:
+      scrapeTimeout:
+      metricRelabelings: []
+      relabelings: []
 
 # -- Extra arguments to provide to _ExternalDNS_.
 extraArgs: []
diff --git a/docs/tutorials/webhook-provider.md b/docs/tutorials/webhook-provider.md
index 8951240c70..dd07e727f6 100644
--- a/docs/tutorials/webhook-provider.md
+++ b/docs/tutorials/webhook-provider.md
@@ -16,18 +16,26 @@ Providers implementing the HTTP API have to keep in sync with changes to the JSO
 
 The following table represents the methods to implement mapped to their HTTP method and route.
 
-| Provider method | HTTP Method | Route |
-| --- | --- | --- |
-| Records | GET | /records |
-| AdjustEndpoints | POST | /adjustendpoints |
-| ApplyChanges | POST | /records |
+| Provider method | HTTP Method | Route            |
+| ---             | ---         | ---              |
+| Records         | GET         | /records         |
+| AdjustEndpoints | POST        | /adjustendpoints |
+| ApplyChanges    | POST        | /records         |
+| K8s probe       | GET         | /healthz         |
 
 ExternalDNS will also make requests to the `/` endpoint for negotiation and for deserialization of the `DomainFilter`.
 
 The server needs to respond to those requests by reading the `Accept` header and responding with a corresponding `Content-Type` header specifying the supported media type format and version.
 
+The default recommended port is 8888, and should listen only on localhost (ie: only accessible for k8s probes and external-dns).
+
 **NOTE**: only `5xx` responses will be retried and only `20x` will be considered as successful. All status codes different from those will be considered a failure on ExternalDNS's side.
 
+
+## Metrics support
+
+The metrics should listen ":8080" on `/metrics` following [Open Metrics](https://github.com/OpenObservability/OpenMetrics) format.
+
 ## Provider registry
 
 To simplify the discovery of providers, we will accept pull requests that will add links to providers in the [README](../../README.md) file. This list will only serve the purpose of simplifying finding providers and will not constitute an official endorsement of any of the externally implemented providers unless otherwise stated.


Test patch: