mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-05 17:16:59 +02:00
feat(source/node): fqdn support combineFQDNAnnotation (#5526)
Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>
This commit is contained in:
parent
8ce0b3d609
commit
0aababa741
@ -54,7 +54,7 @@ The template uses the following data from the source object (e.g., a `Service` o
|
||||
| `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. | ✅ | ❌ |
|
||||
| `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. | ✅ | ✅ |
|
||||
|
@ -38,9 +38,11 @@ import (
|
||||
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."
|
||||
|
||||
type nodeSource struct {
|
||||
client kubernetes.Interface
|
||||
annotationFilter string
|
||||
fqdnTemplate *template.Template
|
||||
client kubernetes.Interface
|
||||
annotationFilter string
|
||||
fqdnTemplate *template.Template
|
||||
combineFQDNAnnotation bool
|
||||
|
||||
nodeInformer coreinformers.NodeInformer
|
||||
labelSelector labels.Selector
|
||||
excludeUnschedulable bool
|
||||
@ -48,7 +50,14 @@ 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, excludeUnschedulable bool) (Source, error) {
|
||||
func NewNodeSource(
|
||||
ctx context.Context,
|
||||
kubeClient kubernetes.Interface,
|
||||
annotationFilter, fqdnTemplate string,
|
||||
labelSelector labels.Selector,
|
||||
exposeInternalIPv6,
|
||||
excludeUnschedulable bool,
|
||||
combineFQDNAnnotation bool) (Source, error) {
|
||||
tmpl, err := fqdn.ParseTemplate(fqdnTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -76,18 +85,19 @@ func NewNodeSource(ctx context.Context, kubeClient kubernetes.Interface, annotat
|
||||
}
|
||||
|
||||
return &nodeSource{
|
||||
client: kubeClient,
|
||||
annotationFilter: annotationFilter,
|
||||
fqdnTemplate: tmpl,
|
||||
nodeInformer: nodeInformer,
|
||||
labelSelector: labelSelector,
|
||||
excludeUnschedulable: excludeUnschedulable,
|
||||
exposeInternalIPv6: exposeInternalIPv6,
|
||||
client: kubeClient,
|
||||
annotationFilter: annotationFilter,
|
||||
fqdnTemplate: tmpl,
|
||||
combineFQDNAnnotation: combineFQDNAnnotation,
|
||||
nodeInformer: nodeInformer,
|
||||
labelSelector: labelSelector,
|
||||
excludeUnschedulable: excludeUnschedulable,
|
||||
exposeInternalIPv6: exposeInternalIPv6,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Endpoints returns endpoint objects for each service that should be processed.
|
||||
func (ns *nodeSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) {
|
||||
func (ns *nodeSource) Endpoints(_ context.Context) ([]*endpoint.Endpoint, error) {
|
||||
nodes, err := ns.nodeInformer.Lister().List(ns.labelSelector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -127,21 +137,9 @@ func (ns *nodeSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, erro
|
||||
}
|
||||
}
|
||||
|
||||
dnsNames := make(map[string]bool)
|
||||
|
||||
if ns.fqdnTemplate != nil {
|
||||
hostnames, err := fqdn.ExecTemplate(ns.fqdnTemplate, node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, name := range hostnames {
|
||||
dnsNames[name] = true
|
||||
log.Debugf("applied template for %s, converting to %s", node.Name, name)
|
||||
}
|
||||
} else {
|
||||
dnsNames[node.Name] = true
|
||||
log.Debugf("not applying template for %s", node.Name)
|
||||
dnsNames, err := ns.collectDNSNames(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for dns := range dnsNames {
|
||||
@ -233,3 +231,36 @@ func (ns *nodeSource) filterByAnnotations(nodes []*v1.Node) ([]*v1.Node, error)
|
||||
|
||||
return filteredList, nil
|
||||
}
|
||||
|
||||
// collectDNSNames returns a set of DNS names associated with the given Kubernetes Node.
|
||||
// If an FQDN template is configured, it renders the template using the Node object
|
||||
// to generate one or more DNS names.
|
||||
// If combineFQDNAnnotation is enabled, the Node's name is also included alongside
|
||||
// the templated names. If no FQDN template is provided, the result will include only
|
||||
// the Node's name.
|
||||
//
|
||||
// Returns an error if template rendering fails.
|
||||
func (ns *nodeSource) collectDNSNames(node *v1.Node) (map[string]bool, error) {
|
||||
dnsNames := make(map[string]bool)
|
||||
// If no FQDN template is configured, fallback to the node name
|
||||
if ns.fqdnTemplate == nil {
|
||||
dnsNames[node.Name] = true
|
||||
return dnsNames, nil
|
||||
}
|
||||
|
||||
names, err := fqdn.ExecTemplate(ns.fqdnTemplate, node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
dnsNames[name] = true
|
||||
log.Debugf("applied template for %s, converting to %s", node.Name, name)
|
||||
}
|
||||
|
||||
if ns.combineFQDNAnnotation {
|
||||
dnsNames[node.Name] = true
|
||||
}
|
||||
|
||||
return dnsNames, nil
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ func TestNodeSourceNewNodeSourceWithFqdn(t *testing.T) {
|
||||
labels.Everything(),
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
@ -80,6 +81,7 @@ func TestNodeSourceFqdnTemplatingExamples(t *testing.T) {
|
||||
nodes []*v1.Node
|
||||
fqdnTemplate string
|
||||
expected []*endpoint.Endpoint
|
||||
combineFQDN bool
|
||||
}{
|
||||
{
|
||||
title: "templating expansion with multiple domains",
|
||||
@ -293,6 +295,32 @@ func TestNodeSourceFqdnTemplatingExamples(t *testing.T) {
|
||||
{DNSName: "node-name-2.domain.tld", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"243.186.136.178"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "templating with shared all domain and fqdn combination annotation",
|
||||
combineFQDN: true,
|
||||
nodes: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "node-name-1"},
|
||||
Status: v1.NodeStatus{
|
||||
Addresses: []v1.NodeAddress{{Type: v1.NodeExternalIP, Address: "243.186.136.160"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "node-name-2"},
|
||||
Status: v1.NodeStatus{
|
||||
Addresses: []v1.NodeAddress{{Type: v1.NodeExternalIP, Address: "243.186.136.178"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
fqdnTemplate: "{{ .Name }}.domain.tld,all.example.com",
|
||||
expected: []*endpoint.Endpoint{
|
||||
{DNSName: "all.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"243.186.136.160", "243.186.136.178"}},
|
||||
{DNSName: "node-name-1.domain.tld", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"243.186.136.160"}},
|
||||
{DNSName: "node-name-2.domain.tld", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"243.186.136.178"}},
|
||||
{DNSName: "node-name-1", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"243.186.136.160"}},
|
||||
{DNSName: "node-name-2", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"243.186.136.178"}},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tt.title, func(t *testing.T) {
|
||||
kubeClient := fake.NewClientset()
|
||||
@ -310,6 +338,7 @@ func TestNodeSourceFqdnTemplatingExamples(t *testing.T) {
|
||||
labels.Everything(),
|
||||
true,
|
||||
true,
|
||||
tt.combineFQDN,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -95,6 +95,7 @@ func testNodeSourceNewNodeSource(t *testing.T) {
|
||||
labels.Everything(),
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
|
||||
if ti.expectError {
|
||||
@ -440,6 +441,7 @@ func testNodeSourceEndpoints(t *testing.T) {
|
||||
labelSelector,
|
||||
tc.exposeInternalIPv6,
|
||||
tc.excludeUnschedulable,
|
||||
false,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -557,6 +559,7 @@ func testNodeEndpointsWithIPv6(t *testing.T) {
|
||||
labelSelector,
|
||||
tc.exposeInternalIPv6,
|
||||
tc.excludeUnschedulable,
|
||||
false,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -604,6 +607,7 @@ func TestResourceLabelIsSetForEachNodeEndpoint(t *testing.T) {
|
||||
labels.Everything(),
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -267,7 +267,7 @@ func BuildWithConfig(ctx context.Context, source string, p ClientGenerator, cfg
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewNodeSource(ctx, client, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.LabelFilter, cfg.ExposeInternalIPv6, cfg.ExcludeUnschedulable)
|
||||
return NewNodeSource(ctx, client, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.LabelFilter, cfg.ExposeInternalIPv6, cfg.ExcludeUnschedulable, cfg.CombineFQDNAndAnnotation)
|
||||
case "service":
|
||||
client, err := p.KubeClient()
|
||||
if err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user