mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 17:46:57 +02:00
Merge pull request #4469 from thameezb/feat-support-dual-stack-gateway-api
feat: support dual stack for gateway api
This commit is contained in:
commit
ccab9a95c5
@ -5,7 +5,7 @@ sources create DNS entries based on their respective `gateway.networking.k8s.io`
|
|||||||
|
|
||||||
## Filtering the Routes considered
|
## Filtering the Routes considered
|
||||||
|
|
||||||
These sources support the `--label-filter` flag, which filters *Route resources
|
These sources support the `--label-filter` flag, which filters \*Route resources
|
||||||
by a set of labels.
|
by a set of labels.
|
||||||
|
|
||||||
## Domain names
|
## Domain names
|
||||||
@ -16,62 +16,62 @@ of [domain names from the *Route](#domain-names-from-route).
|
|||||||
It then iterates over each of the `status.parents` with
|
It then iterates over each of the `status.parents` with
|
||||||
a [matching Gateway](#matching-gateways) and at least one [matching listener](#matching-listeners).
|
a [matching Gateway](#matching-gateways) and at least one [matching listener](#matching-listeners).
|
||||||
For each matching listener, if the
|
For each matching listener, if the
|
||||||
listener has a `hostname`, it narrows the set of domain names from the *Route to the portion
|
listener has a `hostname`, it narrows the set of domain names from the \*Route to the portion
|
||||||
that overlaps the `hostname`. If a matching listener does not have a `hostname`, it uses
|
that overlaps the `hostname`. If a matching listener does not have a `hostname`, it uses
|
||||||
the un-narrowed set of domain names.
|
the un-narrowed set of domain names.
|
||||||
|
|
||||||
### Domain names from Route
|
### Domain names from Route
|
||||||
|
|
||||||
The set of domain names from a *Route is sourced from the following places:
|
The set of domain names from a \*Route is sourced from the following places:
|
||||||
|
|
||||||
* If the *Route is a GRPCRoute, HTTPRoute, or TLSRoute, adds each of the`spec.hostnames`.
|
- If the \*Route is a GRPCRoute, HTTPRoute, or TLSRoute, adds each of the`spec.hostnames`.
|
||||||
|
|
||||||
* Adds the hostnames from any `external-dns.alpha.kubernetes.io/hostname` annotation on the *Route.
|
- Adds the hostnames from any `external-dns.alpha.kubernetes.io/hostname` annotation on the \*Route.
|
||||||
This behavior is suppressed if the `--ignore-hostname-annotation` flag was specified.
|
This behavior is suppressed if the `--ignore-hostname-annotation` flag was specified.
|
||||||
|
|
||||||
* If no endpoints were produced by the previous steps
|
- If no endpoints were produced by the previous steps
|
||||||
or the `--combine-fqdn-annotation` flag was specified, then adds hostnames
|
or the `--combine-fqdn-annotation` flag was specified, then adds hostnames
|
||||||
generated from any`--fqdn-template` flag.
|
generated from any`--fqdn-template` flag.
|
||||||
|
|
||||||
* If no endpoints were produced by the previous steps, each
|
- If no endpoints were produced by the previous steps, each
|
||||||
attached Gateway listener will use its `hostname`, if present.
|
attached Gateway listener will use its `hostname`, if present.
|
||||||
|
|
||||||
### Matching Gateways
|
### Matching Gateways
|
||||||
|
|
||||||
Matching Gateways are discovered by iterating over the *Route's `status.parents`:
|
Matching Gateways are discovered by iterating over the \*Route's `status.parents`:
|
||||||
|
|
||||||
* Ignores parents with a `parentRef.group` other than
|
- Ignores parents with a `parentRef.group` other than
|
||||||
`gateway.networking.k8s.io` or a `parentRef.kind` other than `Gateway`.
|
`gateway.networking.k8s.io` or a `parentRef.kind` other than `Gateway`.
|
||||||
|
|
||||||
* If the `--gateway-namespace` flag was specified, ignores parents with a `parentRef.namespace` other
|
- If the `--gateway-namespace` flag was specified, ignores parents with a `parentRef.namespace` other
|
||||||
than the specified value.
|
than the specified value.
|
||||||
|
|
||||||
* If the `--gateway-label-filter` flag was specified, ignores parents whose Gateway does not match the
|
- If the `--gateway-label-filter` flag was specified, ignores parents whose Gateway does not match the
|
||||||
specified label filter.
|
specified label filter.
|
||||||
|
|
||||||
* Ignores parents whose Gateway either does not exist or has not accepted the route.
|
- Ignores parents whose Gateway either does not exist or has not accepted the route.
|
||||||
|
|
||||||
### Matching listeners
|
### Matching listeners
|
||||||
|
|
||||||
Iterates over all listeners for the parent's `parentRef.sectionName`:
|
Iterates over all listeners for the parent's `parentRef.sectionName`:
|
||||||
|
|
||||||
* Ignores listeners whose `protocol` field does not match the kind of the *Route per the following table:
|
- Ignores listeners whose `protocol` field does not match the kind of the \*Route per the following table:
|
||||||
|
|
||||||
| kind | protocols |
|
| kind | protocols |
|
||||||
|------------|-------------|
|
| --------- | ----------- |
|
||||||
| GRPCRoute | HTTP, HTTPS |
|
| GRPCRoute | HTTP, HTTPS |
|
||||||
| HTTPRoute | HTTP, HTTPS |
|
| HTTPRoute | HTTP, HTTPS |
|
||||||
| TCPRoute | TCP |
|
| TCPRoute | TCP |
|
||||||
| TLSRoute | TLS |
|
| TLSRoute | TLS |
|
||||||
| UDPRoute | UDP |
|
| UDPRoute | UDP |
|
||||||
|
|
||||||
* If the parent's `parentRef.port` port is specified, ignores listeners without a matching `port`.
|
- If the parent's `parentRef.port` port is specified, ignores listeners without a matching `port`.
|
||||||
|
|
||||||
* Ignores listeners which specify an `allowedRoutes` which does not allow the route.
|
- Ignores listeners which specify an `allowedRoutes` which does not allow the route.
|
||||||
|
|
||||||
## Targets
|
## Targets
|
||||||
|
|
||||||
The targets of the DNS entries created from a *Route are sourced from the following places:
|
The targets of the DNS entries created from a \*Route are sourced from the following places:
|
||||||
|
|
||||||
1. If a matching parent Gateway has an `external-dns.alpha.kubernetes.io/target` annotation, uses
|
1. If a matching parent Gateway has an `external-dns.alpha.kubernetes.io/target` annotation, uses
|
||||||
the values from that.
|
the values from that.
|
||||||
@ -79,4 +79,40 @@ the values from that.
|
|||||||
2. Otherwise, iterates over that parent Gateway's `status.addresses`,
|
2. Otherwise, iterates over that parent Gateway's `status.addresses`,
|
||||||
adding each address's `value`.
|
adding each address's `value`.
|
||||||
|
|
||||||
The targets from each parent Gateway matching the *Route are then combined and de-duplicated.
|
The targets from each parent Gateway matching the \*Route are then combined and de-duplicated.
|
||||||
|
|
||||||
|
## Dualstack Routes
|
||||||
|
|
||||||
|
Gateway resources may be served from an external-loadbalancer which may support both IPv4 and "dualstack" (both IPv4 and IPv6) interfaces.
|
||||||
|
External DNS Controller uses the `external-dns.alpha.kubernetes.io/dualstack` annotation to determine this. If this annotation is
|
||||||
|
set to `true` then ExternalDNS will create two records (one A record
|
||||||
|
and one AAAA record) for each hostname associated with the Route resource.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1
|
||||||
|
kind: HTTPRoute
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
external-dns.alpha.kubernetes.io/dualstack: "true"
|
||||||
|
name: echo
|
||||||
|
spec:
|
||||||
|
hostnames:
|
||||||
|
- echoserver.example.org
|
||||||
|
rules:
|
||||||
|
- backendRefs:
|
||||||
|
- group: ""
|
||||||
|
kind: Service
|
||||||
|
name: echo
|
||||||
|
port: 1027
|
||||||
|
weight: 1
|
||||||
|
matches:
|
||||||
|
- path:
|
||||||
|
type: PathPrefix
|
||||||
|
value: /echo
|
||||||
|
```
|
||||||
|
|
||||||
|
The above HTTPRoute resource is backed by a dualstack Gateway.
|
||||||
|
ExternalDNS will create both an A `echoserver.example.org` record and
|
||||||
|
an AAAA record of the same name, that each are aliases for the same LB.
|
||||||
|
@ -45,6 +45,10 @@ import (
|
|||||||
const (
|
const (
|
||||||
gatewayGroup = "gateway.networking.k8s.io"
|
gatewayGroup = "gateway.networking.k8s.io"
|
||||||
gatewayKind = "Gateway"
|
gatewayKind = "Gateway"
|
||||||
|
// gatewayAPIDualstackAnnotationKey is the annotation used for determining if a Gateway Route is dualstack
|
||||||
|
gatewayAPIDualstackAnnotationKey = "external-dns.alpha.kubernetes.io/dualstack"
|
||||||
|
// gatewayAPIDualstackAnnotationValue is the value of the Gateway Route dualstack annotation that indicates it is dualstack
|
||||||
|
gatewayAPIDualstackAnnotationValue = "true"
|
||||||
)
|
)
|
||||||
|
|
||||||
type gatewayRoute interface {
|
type gatewayRoute interface {
|
||||||
@ -236,6 +240,7 @@ func (src *gatewayRouteSource) Endpoints(ctx context.Context) ([]*endpoint.Endpo
|
|||||||
for host, targets := range hostTargets {
|
for host, targets := range hostTargets {
|
||||||
endpoints = append(endpoints, endpointsForHostname(host, targets, ttl, providerSpecific, setIdentifier, resource)...)
|
endpoints = append(endpoints, endpointsForHostname(host, targets, ttl, providerSpecific, setIdentifier, resource)...)
|
||||||
}
|
}
|
||||||
|
setDualstackLabel(rt, endpoints)
|
||||||
log.Debugf("Endpoints generated from %s %s/%s: %v", src.rtKind, meta.Namespace, meta.Name, endpoints)
|
log.Debugf("Endpoints generated from %s %s/%s: %v", src.rtKind, meta.Namespace, meta.Name, endpoints)
|
||||||
}
|
}
|
||||||
return endpoints, nil
|
return endpoints, nil
|
||||||
@ -610,3 +615,13 @@ func selectorsEqual(a, b labels.Selector) bool {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setDualstackLabel(rt gatewayRoute, endpoints []*endpoint.Endpoint) {
|
||||||
|
val, ok := rt.Metadata().Annotations[gatewayAPIDualstackAnnotationKey]
|
||||||
|
if ok && val == gatewayAPIDualstackAnnotationValue {
|
||||||
|
log.Debugf("Adding dualstack label to GatewayRoute %s/%s.", rt.Metadata().Namespace, rt.Metadata().Name)
|
||||||
|
for _, ep := range endpoints {
|
||||||
|
ep.Labels[endpoint.DualstackLabelKey] = "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/external-dns/endpoint"
|
||||||
v1 "sigs.k8s.io/gateway-api/apis/v1"
|
v1 "sigs.k8s.io/gateway-api/apis/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -245,3 +246,47 @@ func TestIsDNS1123Domain(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDualStackLabel(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
in map[string](string)
|
||||||
|
setsLabel bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "empty-annotation",
|
||||||
|
setsLabel: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "correct-annotation-key-and-value",
|
||||||
|
in: map[string]string{gatewayAPIDualstackAnnotationKey: gatewayAPIDualstackAnnotationValue},
|
||||||
|
setsLabel: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "correct-annotation-key-incorrect-value",
|
||||||
|
in: map[string]string{gatewayAPIDualstackAnnotationKey: "foo"},
|
||||||
|
setsLabel: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "incorrect-annotation-key-correct-value",
|
||||||
|
in: map[string]string{"FOO": gatewayAPIDualstackAnnotationValue},
|
||||||
|
setsLabel: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
|
endpoints := make([]*endpoint.Endpoint, 0)
|
||||||
|
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeA, "10.0.0.2", "10.0.0.3"))
|
||||||
|
|
||||||
|
rt := &gatewayHTTPRoute{}
|
||||||
|
rt.Metadata().Annotations = tt.in
|
||||||
|
|
||||||
|
setDualstackLabel(rt, endpoints)
|
||||||
|
got := endpoints[0].Labels[endpoint.DualstackLabelKey] == "true"
|
||||||
|
|
||||||
|
if got != tt.setsLabel {
|
||||||
|
t.Errorf("setDualstackLabel(%q); got: %v; want: %v", tt.in, got, tt.setsLabel)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user