mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 09:36:58 +02:00
chore(fqdn-template): fqdn templating move to specific folder and update documentation (#5354)
* chore(fqdn): fqdn move to specific folder and update documentation * chore(fqdn): fqdn move to specific folder and update documentation Co-authored-by: Michel Loiseleur <97035654+mloiseleur@users.noreply.github.com> * chore(fqdn): fqdn move to specific folder and update documentation Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * chore(fqdn): fqdn move to specific folder and update documentation Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * chore(fqdn): fqdn move to specific folder and update documentation Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> --------- Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> Co-authored-by: Michel Loiseleur <97035654+mloiseleur@users.noreply.github.com>
This commit is contained in:
parent
b5a7ae1626
commit
51d063ad28
@ -164,4 +164,3 @@ tests:
|
||||
asserts:
|
||||
- failedTemplate:
|
||||
errorMessage: "'txtPrefix' and 'txtSuffix' are mutually exclusive"
|
||||
|
||||
|
306
docs/advanced/fqdn-templating.md
Normal file
306
docs/advanced/fqdn-templating.md
Normal file
@ -0,0 +1,306 @@
|
||||
# FQDN Templating Guide
|
||||
|
||||
## What is FQDN Templating?
|
||||
|
||||
**FQDN templating** is a feature that allows to dynamically construct Fully Qualified Domain Names (FQDNs) using a Go templating engine.
|
||||
Instead of relying solely on annotations or static names, you can use metadata from Kubernetes objects—such as service names, namespaces, and labels—to generate DNS records programmatically and dynamically.
|
||||
|
||||
This is useful for:
|
||||
|
||||
- Creating consistent naming conventions across environments.
|
||||
- Reducing boilerplate annotations.
|
||||
- Supporting multi-tenant or dynamic environments.
|
||||
- Migrating from one DNS scheme to another
|
||||
- Supporting multiple variants, such as a regional one and then one that doesn't or similar.
|
||||
|
||||
## How It Works
|
||||
|
||||
ExternalDNS has a flag: `--fqdn-template`, which defines a Go template for rendering the desired DNS names.
|
||||
|
||||
The template uses the following data from the source object (e.g., a `Service` or `Ingress`):
|
||||
|
||||
| Field | Description |
|
||||
|:--------------|:------------------------------------------------------------------|
|
||||
| `Name` | Name of the object (e.g., service) |
|
||||
| `Namespace` | Namespace of the object |
|
||||
| `Labels` | Map of labels applied to the object |
|
||||
| `Annotations` | Map of annotations |
|
||||
| `TargetName` | For `Service`, it's the service name; for `Ingress`, the hostname |
|
||||
| `Endpoint` | Contains more contextual endpoint info, such as IP/target |
|
||||
| `Controller` | Controller type (optional) |
|
||||
|
||||
## Supported Sources
|
||||
|
||||
<!-- TODO: generate from code -->
|
||||
|
||||
| Source | Description | FQDN Supported |
|
||||
|:-----------------------|:----------------------------------------------------------------|:--------------:|
|
||||
| `ambassador-host` | Queries Ambassador Host resources for endpoints. | ❌ |
|
||||
| `cloudfoundry` | Queries Cloud Foundry resources for endpoints. | ❌ |
|
||||
| `connector` | Queries a custom connector source for endpoints. | ❌ |
|
||||
| `contour-httpproxy` | Queries Contour HTTPProxy resources for endpoints. | ✅ |
|
||||
| `crd` | Queries Custom Resource Definitions (CRDs) for endpoints. | ❌ |
|
||||
| `empty` | Uses an empty source, typically for testing or no-op scenarios. | ❌ |
|
||||
| `f5-transportserver` | Queries F5 TransportServer resources for endpoints. | ❌ |
|
||||
| `f5-virtualserver` | Queries F5 VirtualServer resources for endpoints. | ❌ |
|
||||
| `fake` | Uses a fake source for testing purposes. | ❌ |
|
||||
| `gateway-grpcroute` | Queries GRPCRoute resources from the Gateway API. | ✅ |
|
||||
| `gateway-httproute` | Queries HTTPRoute resources from the Gateway API. | ✅ |
|
||||
| `gateway-tcproute` | Queries TCPRoute resources from the Gateway API. | ✅ |
|
||||
| `gateway-tlsroute` | Queries TLSRoute resources from the Gateway API. | ❌ |
|
||||
| `gateway-udproute` | Queries UDPRoute resources from the Gateway API. | ❌ |
|
||||
| `gloo-proxy` | Queries Gloo Proxy resources for endpoints. | ❌ |
|
||||
| `ingress` | Queries Kubernetes Ingress resources for endpoints. | ✅ |
|
||||
| `istio-gateway` | Queries Istio Gateway resources for endpoints. | ✅ |
|
||||
| `istio-virtualservice` | Queries Istio VirtualService resources for endpoints. | ✅ |
|
||||
| `kong-tcpingress` | Queries Kong TCPIngress resources for endpoints. | ❌ |
|
||||
| `node` | Queries Kubernetes Node resources for endpoints. | ✅ |
|
||||
| `openshift-route` | Queries OpenShift Route resources for endpoints. | ✅ |
|
||||
| `pod` | Queries Kubernetes Pod resources for endpoints. | ❌ |
|
||||
| `service` | Queries Kubernetes Service resources for endpoints. | ✅ |
|
||||
| `skipper-routegroup` | Queries Skipper RouteGroup resources for endpoints. | ✅ |
|
||||
| `traefik-proxy` | Queries Traefik Proxy resources for endpoints. | ❌ |
|
||||
|
||||
## Custom Functions
|
||||
|
||||
<!-- TODO: generate from code -->
|
||||
|
||||
| Function | Description |
|
||||
|:-------------|:-----------------------------------------------------------------------------------------|
|
||||
| `trimPrefix` | Function from the `strings` package. Returns `string` without the provided leading prefix. |
|
||||
|
||||
---
|
||||
|
||||
## Example Usage
|
||||
|
||||
> These examples should provide a solid foundation for implementing FQDN templating in your ExternalDNS setup.
|
||||
> If you have specific requirements or encounter issues, feel free to explore the issues or update this guide.
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```yml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: my-service
|
||||
namespace: my-namespace
|
||||
```
|
||||
|
||||
```sh
|
||||
external-dns \
|
||||
--provider=aws \
|
||||
--source=service \
|
||||
--fqdn-template="{{ .Name }}.example.com,{{ .Name }}.{{ .Namespace }}.example.tld"
|
||||
|
||||
# This will result in DNS entries like
|
||||
>route53> my-service.example.com
|
||||
>route53> my-service.my-namespace.example.tld
|
||||
```
|
||||
|
||||
### With Namespace
|
||||
|
||||
```yml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: my-service
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: other-service
|
||||
namespace: kube-system
|
||||
```
|
||||
|
||||
```yml
|
||||
args:
|
||||
--fqdn-template="{{.Name}}.{{.Namespace}}.example.com"
|
||||
|
||||
# This will result in DNS entries like
|
||||
# route53> my-service.default.example.com
|
||||
# route53> other-service.kube-system.example.com
|
||||
```
|
||||
|
||||
### Using Labels in Templates
|
||||
|
||||
You can also utilize labels in your FQDN templates to create more dynamic DNS entries. Assuming your service has:
|
||||
|
||||
```yml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: my-service
|
||||
labels:
|
||||
environment: staging
|
||||
```
|
||||
|
||||
```yml
|
||||
args:
|
||||
--fqdn-template="{{ .Labels.environment }}.{{ .Name }}.example.com"
|
||||
|
||||
# This will result in DNS entries like
|
||||
# route53> staging.my-service.example.com
|
||||
```
|
||||
|
||||
### Multiple FQDN Templates
|
||||
|
||||
ExternalDNS allows specifying multiple FQDN templates, which can be useful when you want to create multiple DNS entries for a single service or ingress.
|
||||
|
||||
> Be cautious, as this will create multiple DNS records per resource, potentially increasing the number of API calls to your DNS provider.
|
||||
|
||||
```yml
|
||||
args:
|
||||
--fqdn-template={{.Name}}.example.com,{{.Name}}.svc.example.com
|
||||
```
|
||||
|
||||
### Conditional Templating combined with Annotations processing
|
||||
|
||||
In scenarios where you want to conditionally generate FQDNs based on annotations, you can use Go template functions like or to provide defaults.
|
||||
|
||||
```yml
|
||||
args:
|
||||
- --combine-fqdn-annotation # this is required to combine FQDN templating and annotation processing
|
||||
- --fqdn-template={{ or .Annotations.dns "invalid" }}.example.com
|
||||
- --exclude-domains=invalid.example.com
|
||||
```
|
||||
|
||||
### Using Annotations for FQDN Templating
|
||||
|
||||
This example demonstrates how to use annotations in Kubernetes objects to dynamically generate Fully Qualified Domain Names (FQDNs) using the --fqdn-template flag in ExternalDNS.
|
||||
|
||||
The Service object includes an annotation dns.company.com/label with the value my-org-tld-v2. This annotation is used as part of the FQDN template to construct the DNS name.
|
||||
|
||||
```yml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx-v2
|
||||
namespace: my-namespace
|
||||
annotations:
|
||||
dns.company.com/label: my-org-tld-v2
|
||||
spec:
|
||||
type: ClusterIP
|
||||
clusterIP: None
|
||||
```
|
||||
|
||||
The --fqdn-template flag is configured to use the annotation value (dns.company.com/label) and append the namespace and a custom domain (company.local) to generate the FQDN.
|
||||
|
||||
```yml
|
||||
args:
|
||||
--source=service
|
||||
--fqdn-template='{{ index .ObjectMeta.Annotations "dns.company.com/label" }}.{{ .Namespace }}.company.local'
|
||||
|
||||
# For the given Service object, the resulting FQDN will be:
|
||||
# route53> my-org-tld-v2.my-namespace.company.local
|
||||
```
|
||||
|
||||
### DNS Scheme Migration
|
||||
|
||||
If you're transitioning from one naming convention to another (e.g., from svc.cluster.local to svc.example.com), --fqdn-template allows you to generate the new records alongside or in place of the old ones — without requiring changes to your Kubernetes manifests.
|
||||
|
||||
```yml
|
||||
args:
|
||||
- --fqdn-template='{{.Name}}.new-dns.example.com'
|
||||
```
|
||||
|
||||
This helps automate DNS record migration while maintaining service continuity.
|
||||
|
||||
### Multi-Variant Domain Support
|
||||
|
||||
You can also support regional variants or multi-tenant architectures, where the same service is deployed to different regions or environments:
|
||||
|
||||
```yaml
|
||||
--fqdn-template='{{ .Name }}.{{ .Labels.env }}.{{ .Labels.region }}.example.com, {{ if eq .Labels.env "prod" }}{{ .Name }}.my-company.tld{{ end }}'
|
||||
```
|
||||
|
||||
With additional context (e.g., annotations), this can produce FQDNs like:
|
||||
|
||||
```yml
|
||||
api.prod.us-east-1.example.com
|
||||
api.my-company.tld
|
||||
```
|
||||
|
||||
This is helpful in scenarios such as:
|
||||
|
||||
- Blue/green deployments across domains
|
||||
- Staging vs. production resolution
|
||||
- Multi-cloud or multi-region failover strategies
|
||||
|
||||
## Tips
|
||||
|
||||
- If `--fqdn-template` is specified, ExternalDNS ignores any `external-dns.alpha.kubernetes.io/hostname` annotations.
|
||||
- You must still ensure the resulting FQDN is valid and unique.
|
||||
- Since Go templates can be error-prone, test your template with simple examples before deploying. Mismatched field names or nil values (e.g., missing labels) will result in errors or skipped entries.
|
||||
|
||||
## FaQ
|
||||
|
||||
### Can I specify multiple global FQDN templates?
|
||||
|
||||
Yes, you can. Pass in a comma separated list to --fqdn-template. Beware this will double (triple, etc) the amount of DNS entries based on how many services, ingresses and so on you have and will get you faster towards the API request limit of your DNS provider.
|
||||
|
||||
### Where to find template syntax
|
||||
|
||||
- [Go template syntax](https://pkg.go.dev/text/template)
|
||||
- [Go func builtins](https://github.com/golang/go/blob/master/src/text/template/funcs.go#L39-L63)
|
||||
|
||||
### FQDN Templating, Helm and improper templating syntax
|
||||
|
||||
The user encountered errors due to improper templating syntax:
|
||||
|
||||
```yml
|
||||
extraArgs:
|
||||
- --fqdn-template={{name}}.uat.example.com
|
||||
```
|
||||
|
||||
The correct syntax should include a dot prefix: `{{ .Name }}`.
|
||||
Additionally, when using Helm's `tpl` function, it's necessary to escape the braces to prevent premature evaluation:
|
||||
|
||||
```yml
|
||||
extraArgs:
|
||||
- --fqdn-template={{ `{{ .Name }}.uat.example.com` }}
|
||||
```
|
||||
|
||||
### Handling Subdomain-Only Hostnames
|
||||
|
||||
In [Issue #1872](https://github.com/kubernetes-sigs/external-dns/issues/1872), it was observed that ExternalDNS ignores the `--fqdn-template` when the ingress host field is set to a subdomain (e.g., foo) without a full domain.
|
||||
The expectation was that the template would still apply, generating entries like `foo.bar.example.com.`
|
||||
This highlights a limitation to be aware of when designing FQDN templates.
|
||||
|
||||
> :warning: This is currently not supported ! User would expect external-dns to generate a dns record according to the fqdnTemplate
|
||||
> e.g. if the ingress name: foo and host: foo is created while fqdnTemplate={{.Name}}.bar.example.com then a dns record foo.bar.example.com should be created
|
||||
|
||||
```yml
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: foo
|
||||
spec:
|
||||
rules:
|
||||
- host: foo
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: foo
|
||||
servicePort: 80
|
||||
path: /
|
||||
```
|
||||
|
||||
### Combining FQDN Template with Annotations
|
||||
|
||||
In [Issue #3318](https://github.com/kubernetes-sigs/external-dns/issues/3318), a question was raised about the interaction between --fqdn-template and --combine-fqdn-annotation.
|
||||
The discussion clarified that when both flags are used, ExternalDNS combines the FQDN generated from the template with the annotation value, providing flexibility in DNS name construction.
|
||||
|
||||
### Using Annotations for Dynamic FQDNs
|
||||
|
||||
In [Issue #2627](https://github.com/kubernetes-sigs/external-dns/issues/2627), a user aimed to generate DNS entries based on ingress annotations:
|
||||
|
||||
```yml
|
||||
args:
|
||||
- --fqdn-template={{.Annotations.hostname}}.example.com
|
||||
- --combine-fqdn-annotation
|
||||
- --domain-filter=example.com
|
||||
```
|
||||
|
||||
By setting the hostname annotation in the ingress resource, ExternalDNS constructs the FQDN accordingly. This approach allows for dynamic DNS entries without hardcoding hostnames.
|
@ -50,10 +50,6 @@ There are three sources of information for ExternalDNS to decide on DNS name. Ex
|
||||
|
||||
3. If `--fqdn-template` flag is specified, e.g. `--fqdn-template={{.Name}}.my-org.com`, ExternalDNS will use service/ingress specifications for the provided template to generate DNS name.
|
||||
|
||||
## Can I specify multiple global FQDN templates?
|
||||
|
||||
Yes, you can. Pass in a comma separated list to `--fqdn-template`. Beaware this will double (triple, etc) the amount of DNS entries based on how many services, ingresses and so on you have and will get you faster towards the API request limit of your DNS provider.
|
||||
|
||||
## Which Service and Ingress controllers are supported?
|
||||
|
||||
Regarding Services, we'll support the OSI Layer 4 load balancers that Kubernetes creates on AWS and Google Kubernetes Engine, and possibly other clusters running on Google Compute Engine.
|
||||
|
@ -29,9 +29,10 @@ nav:
|
||||
- Leader Election: docs/proposal/001-leader-election.md
|
||||
- Monitoring: docs/monitoring/*
|
||||
- MultiTarget: docs/proposal/multi-target.md
|
||||
- NAT64: docs/nat64.md
|
||||
- Rate Limits: docs/rate-limits.md
|
||||
- TTL: docs/ttl.md
|
||||
- NAT64: docs/advanced/nat64.md
|
||||
- Rate Limits: docs/advanced/rate-limits.md
|
||||
- TTL: docs/advanced/ttl.md
|
||||
- FQDN Templating: docs/advanced/fqdn-templating.md
|
||||
- Contributing:
|
||||
- Kubernetes Contributions: CONTRIBUTING.md
|
||||
- Release: docs/release.
|
||||
|
@ -116,7 +116,7 @@ function main() {
|
||||
helm_unittest
|
||||
;;
|
||||
--helm-template)
|
||||
helm_unittest
|
||||
helm_template
|
||||
;;
|
||||
-d|--diff)
|
||||
diff_schema
|
||||
|
@ -33,6 +33,8 @@ import (
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"sigs.k8s.io/external-dns/source/fqdn"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
@ -60,7 +62,7 @@ func NewContourHTTPProxySource(
|
||||
combineFqdnAnnotation bool,
|
||||
ignoreHostnameAnnotation bool,
|
||||
) (Source, error) {
|
||||
tmpl, err := parseTemplate(fqdnTemplate)
|
||||
tmpl, err := fqdn.ParseTemplate(fqdnTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
32
source/fqdn/fqdn.go
Normal file
32
source/fqdn/fqdn.go
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright 2025 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fqdn
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func ParseTemplate(fqdnTemplate string) (tmpl *template.Template, err error) {
|
||||
if fqdnTemplate == "" {
|
||||
return nil, nil
|
||||
}
|
||||
funcs := template.FuncMap{
|
||||
"trimPrefix": strings.TrimPrefix,
|
||||
}
|
||||
return template.New("endpoint").Funcs(funcs).Parse(fqdnTemplate)
|
||||
}
|
73
source/fqdn/fqdn_test.go
Normal file
73
source/fqdn/fqdn_test.go
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2025 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fqdn
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseTemplate(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
annotationFilter string
|
||||
fqdnTemplate string
|
||||
combineFQDNAndAnnotation bool
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "invalid template",
|
||||
expectError: true,
|
||||
fqdnTemplate: "{{.Name",
|
||||
},
|
||||
{
|
||||
name: "valid empty template",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "valid template",
|
||||
expectError: false,
|
||||
fqdnTemplate: "{{.Name}}-{{.Namespace}}.ext-dns.test.com",
|
||||
},
|
||||
{
|
||||
name: "valid template",
|
||||
expectError: false,
|
||||
fqdnTemplate: "{{.Name}}-{{.Namespace}}.ext-dns.test.com, {{.Name}}-{{.Namespace}}.ext-dna.test.com",
|
||||
},
|
||||
{
|
||||
name: "valid template",
|
||||
expectError: false,
|
||||
fqdnTemplate: "{{.Name}}-{{.Namespace}}.ext-dns.test.com, {{.Name}}-{{.Namespace}}.ext-dna.test.com",
|
||||
combineFQDNAndAnnotation: true,
|
||||
},
|
||||
{
|
||||
name: "non-empty annotation filter label",
|
||||
expectError: false,
|
||||
annotationFilter: "kubernetes.io/ingress.class=nginx",
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := ParseTemplate(tt.fqdnTemplate)
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -40,6 +40,7 @@ import (
|
||||
informers_v1beta1 "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions/apis/v1beta1"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/source/fqdn"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -117,7 +118,7 @@ func newGatewayRouteSource(clients ClientGenerator, config *Config, kind string,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tmpl, err := parseTemplate(config.FQDNTemplate)
|
||||
tmpl, err := fqdn.ParseTemplate(config.FQDNTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import (
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/source/fqdn"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -64,7 +65,7 @@ type ingressSource struct {
|
||||
|
||||
// NewIngressSource creates a new ingressSource with the given config.
|
||||
func NewIngressSource(ctx context.Context, kubeClient kubernetes.Interface, namespace, annotationFilter string, fqdnTemplate string, combineFqdnAnnotation bool, ignoreHostnameAnnotation bool, ignoreIngressTLSSpec bool, ignoreIngressRulesSpec bool, labelSelector labels.Selector, ingressClassNames []string) (Source, error) {
|
||||
tmpl, err := parseTemplate(fqdnTemplate)
|
||||
tmpl, err := fqdn.ParseTemplate(fqdnTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -35,6 +35,8 @@ import (
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"sigs.k8s.io/external-dns/source/fqdn"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
@ -68,7 +70,7 @@ func NewIstioGatewaySource(
|
||||
combineFQDNAnnotation bool,
|
||||
ignoreHostnameAnnotation bool,
|
||||
) (Source, error) {
|
||||
tmpl, err := parseTemplate(fqdnTemplate)
|
||||
tmpl, err := fqdn.ParseTemplate(fqdnTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ import (
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/source/fqdn"
|
||||
)
|
||||
|
||||
// IstioMeshGateway is the built in gateway for all sidecars
|
||||
@ -69,7 +70,7 @@ func NewIstioVirtualServiceSource(
|
||||
combineFQDNAnnotation bool,
|
||||
ignoreHostnameAnnotation bool,
|
||||
) (Source, error) {
|
||||
tmpl, err := parseTemplate(fqdnTemplate)
|
||||
tmpl, err := fqdn.ParseTemplate(fqdnTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import (
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/source/fqdn"
|
||||
)
|
||||
|
||||
const warningMsg = "The default behavior of exposing internal IPv6 addresses will change in the next minor version. Use --no-expose-internal-ipv6 flag to opt-in to the new behavior."
|
||||
@ -47,7 +48,7 @@ type nodeSource struct {
|
||||
|
||||
// NewNodeSource creates a new nodeSource with the given config.
|
||||
func NewNodeSource(ctx context.Context, kubeClient kubernetes.Interface, annotationFilter, fqdnTemplate string, labelSelector labels.Selector, exposeInternalIPv6 bool, excludeUnschedulable bool) (Source, error) {
|
||||
tmpl, err := parseTemplate(fqdnTemplate)
|
||||
tmpl, err := fqdn.ParseTemplate(fqdnTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import (
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/source/fqdn"
|
||||
)
|
||||
|
||||
// ocpRouteSource is an implementation of Source for OpenShift Route objects.
|
||||
@ -65,7 +66,7 @@ func NewOcpRouteSource(
|
||||
labelSelector labels.Selector,
|
||||
ocpRouterName string,
|
||||
) (Source, error) {
|
||||
tmpl, err := parseTemplate(fqdnTemplate)
|
||||
tmpl, err := fqdn.ParseTemplate(fqdnTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import (
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/source/fqdn"
|
||||
)
|
||||
|
||||
// serviceSource is an implementation of Source for Kubernetes service objects.
|
||||
@ -66,7 +67,7 @@ type serviceSource struct {
|
||||
|
||||
// NewServiceSource creates a new serviceSource with the given config.
|
||||
func NewServiceSource(ctx context.Context, kubeClient kubernetes.Interface, namespace, annotationFilter, fqdnTemplate string, combineFqdnAnnotation bool, compatibility string, publishInternal, publishHostIP, alwaysPublishNotReadyAddresses bool, serviceTypeFilter []string, ignoreHostnameAnnotation bool, labelSelector labels.Selector, resolveLoadBalancerHostname, listenEndpointEvents bool) (Source, error) {
|
||||
tmpl, err := parseTemplate(fqdnTemplate)
|
||||
tmpl, err := fqdn.ParseTemplate(fqdnTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/source/fqdn"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -192,7 +193,7 @@ func (cli *routeGroupClient) do(req *http.Request) (*http.Response, error) {
|
||||
|
||||
// NewRouteGroupSource creates a new routeGroupSource with the given config.
|
||||
func NewRouteGroupSource(timeout time.Duration, token, tokenPath, apiServerURL, namespace, annotationFilter, fqdnTemplate, routegroupVersion string, combineFqdnAnnotation, ignoreHostnameAnnotation bool) (Source, error) {
|
||||
tmpl, err := parseTemplate(fqdnTemplate)
|
||||
tmpl, err := fqdn.ParseTemplate(fqdnTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/source/fqdn"
|
||||
)
|
||||
|
||||
func createTestRouteGroup(ns, name string, annotations map[string]string, hosts []string, destinations []routeGroupLoadBalancer) *routeGroup {
|
||||
@ -788,7 +788,7 @@ func TestRouteGroupsEndpoints(t *testing.T) {
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.fqdnTemplate != "" {
|
||||
tmpl, err := parseTemplate(tt.fqdnTemplate)
|
||||
tmpl, err := fqdn.ParseTemplate(tt.fqdnTemplate)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse template: %v", err)
|
||||
}
|
||||
@ -836,53 +836,3 @@ func TestResourceLabelIsSet(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTemplate(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
annotationFilter string
|
||||
fqdnTemplate string
|
||||
combineFQDNAndAnnotation bool
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "invalid template",
|
||||
expectError: true,
|
||||
fqdnTemplate: "{{.Name",
|
||||
},
|
||||
{
|
||||
name: "valid empty template",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "valid template",
|
||||
expectError: false,
|
||||
fqdnTemplate: "{{.Name}}-{{.Namespace}}.ext-dns.test.com",
|
||||
},
|
||||
{
|
||||
name: "valid template",
|
||||
expectError: false,
|
||||
fqdnTemplate: "{{.Name}}-{{.Namespace}}.ext-dns.test.com, {{.Name}}-{{.Namespace}}.ext-dna.test.com",
|
||||
},
|
||||
{
|
||||
name: "valid template",
|
||||
expectError: false,
|
||||
fqdnTemplate: "{{.Name}}-{{.Namespace}}.ext-dns.test.com, {{.Name}}-{{.Namespace}}.ext-dna.test.com",
|
||||
combineFQDNAndAnnotation: true,
|
||||
},
|
||||
{
|
||||
name: "non-empty annotation filter label",
|
||||
expectError: false,
|
||||
annotationFilter: "kubernetes.io/ingress.class=nginx",
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := parseTemplate(tt.fqdnTemplate)
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -145,16 +145,6 @@ func execTemplate(tmpl *template.Template, obj kubeObject) (hostnames []string,
|
||||
return hostnames, nil
|
||||
}
|
||||
|
||||
func parseTemplate(fqdnTemplate string) (tmpl *template.Template, err error) {
|
||||
if fqdnTemplate == "" {
|
||||
return nil, nil
|
||||
}
|
||||
funcs := template.FuncMap{
|
||||
"trimPrefix": strings.TrimPrefix,
|
||||
}
|
||||
return template.New("endpoint").Funcs(funcs).Parse(fqdnTemplate)
|
||||
}
|
||||
|
||||
func getHostnamesFromAnnotations(annotations map[string]string) []string {
|
||||
hostnameAnnotation, exists := annotations[hostnameAnnotationKey]
|
||||
if !exists {
|
||||
|
Loading…
Reference in New Issue
Block a user