mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 09:36:58 +02:00
Merge afc7788578
into 0e7c3af221
This commit is contained in:
commit
980abef294
@ -33,6 +33,10 @@ a Pod that has a non-empty `spec.hostname` field, additional DNS entries are cre
|
|||||||
For each domain name created for the Service, the additional DNS entry for the Pod has that domain name prefixed with
|
For each domain name created for the Service, the additional DNS entry for the Pod has that domain name prefixed with
|
||||||
the value of the Pod's `spec.hostname` field and a `.`.
|
the value of the Pod's `spec.hostname` field and a `.`.
|
||||||
|
|
||||||
|
Another way to create per-pod DNS entries is to annotate headless service with
|
||||||
|
`external-dns.alpha.kubernetes.io/service-pod-endpoints` and values `pod-name` or `fqdn-template`. The former prefixes
|
||||||
|
service domain name with pod name, the latter uses `--fqdn-template` to generate the domain name for each pod in the service.
|
||||||
|
|
||||||
## Targets
|
## Targets
|
||||||
|
|
||||||
If the Service has an `external-dns.alpha.kubernetes.io/target` annotation, uses
|
If the Service has an `external-dns.alpha.kubernetes.io/target` annotation, uses
|
||||||
|
@ -55,4 +55,6 @@ const (
|
|||||||
ControllerValue = "dns-controller"
|
ControllerValue = "dns-controller"
|
||||||
// The annotation used for defining the desired hostname
|
// The annotation used for defining the desired hostname
|
||||||
InternalHostnameKey = AnnotationKeyPrefix + "internal-hostname"
|
InternalHostnameKey = AnnotationKeyPrefix + "internal-hostname"
|
||||||
|
// When set on a service, per-pod DNS entries will be created.
|
||||||
|
ServicePodEndpoints = AnnotationKeyPrefix + "service-pod-endpoints"
|
||||||
)
|
)
|
||||||
|
@ -314,6 +314,8 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri
|
|||||||
publishPodIPs := endpointsType != EndpointsTypeNodeExternalIP && endpointsType != EndpointsTypeHostIP && !sc.publishHostIP
|
publishPodIPs := endpointsType != EndpointsTypeNodeExternalIP && endpointsType != EndpointsTypeHostIP && !sc.publishHostIP
|
||||||
publishNotReadyAddresses := svc.Spec.PublishNotReadyAddresses || sc.alwaysPublishNotReadyAddresses
|
publishNotReadyAddresses := svc.Spec.PublishNotReadyAddresses || sc.alwaysPublishNotReadyAddresses
|
||||||
|
|
||||||
|
perPodDNSMode, perPodDNS := svc.Annotations[servicePodEndpointsKey]
|
||||||
|
|
||||||
targetsByHeadlessDomainAndType := make(map[endpoint.EndpointKey]endpoint.Targets)
|
targetsByHeadlessDomainAndType := make(map[endpoint.EndpointKey]endpoint.Targets)
|
||||||
for _, endpointSlice := range endpointSlices {
|
for _, endpointSlice := range endpointSlices {
|
||||||
for _, ep := range endpointSlice.Endpoints {
|
for _, ep := range endpointSlice.Endpoints {
|
||||||
@ -350,6 +352,21 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri
|
|||||||
headlessDomains = append(headlessDomains, fmt.Sprintf("%s.%s", pod.Spec.Hostname, hostname))
|
headlessDomains = append(headlessDomains, fmt.Sprintf("%s.%s", pod.Spec.Hostname, hostname))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if perPodDNS {
|
||||||
|
switch perPodDNSMode {
|
||||||
|
case ServicePodEndpointsPodName:
|
||||||
|
headlessDomains = append(headlessDomains, fmt.Sprintf("%s.%s", pod.Name, hostname))
|
||||||
|
case ServicePodEndpointsFqdnTemplate:
|
||||||
|
if hostnames, err := fqdn.ExecTemplate(sc.fqdnTemplate, pod); err == nil {
|
||||||
|
headlessDomains = append(headlessDomains, hostnames...)
|
||||||
|
} else {
|
||||||
|
log.Errorf("Error executing template for pod %s: %v", pod.Name, err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Errorf("Unknown `service-pod-endpoints` value %s", perPodDNSMode)
|
||||||
|
return endpoints
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, headlessDomain := range headlessDomains {
|
for _, headlessDomain := range headlessDomains {
|
||||||
targets := annotations.TargetsFromTargetAnnotation(pod.Annotations)
|
targets := annotations.TargetsFromTargetAnnotation(pod.Annotations)
|
||||||
if len(targets) == 0 {
|
if len(targets) == 0 {
|
||||||
|
@ -3053,6 +3053,149 @@ func TestHeadlessServices(t *testing.T) {
|
|||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"annotated Headless services create DNS name for each pod",
|
||||||
|
"",
|
||||||
|
"testing",
|
||||||
|
"foo",
|
||||||
|
v1.ServiceTypeClusterIP,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
map[string]string{"component": "foo"},
|
||||||
|
map[string]string{
|
||||||
|
servicePodEndpointsKey: ServicePodEndpointsPodName,
|
||||||
|
hostnameAnnotationKey: "service.example.org",
|
||||||
|
endpointsTypeAnnotationKey: EndpointsTypeNodeExternalIP,
|
||||||
|
},
|
||||||
|
map[string]string{},
|
||||||
|
v1.ClusterIPNone,
|
||||||
|
[]string{"1.1.1.1", "1.1.1.2", "1.1.1.3"},
|
||||||
|
[]string{"", "", ""},
|
||||||
|
map[string]string{
|
||||||
|
"component": "foo",
|
||||||
|
},
|
||||||
|
[]string{},
|
||||||
|
[]string{"foo1", "foo2", "foo3"},
|
||||||
|
[]string{"", "", ""},
|
||||||
|
[]bool{true, true, true},
|
||||||
|
false,
|
||||||
|
[]v1.Node{
|
||||||
|
{
|
||||||
|
Status: v1.NodeStatus{
|
||||||
|
Addresses: []v1.NodeAddress{
|
||||||
|
{
|
||||||
|
Type: v1.NodeExternalIP,
|
||||||
|
Address: "1.2.3.4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: v1.NodeInternalIP,
|
||||||
|
Address: "2001:db8::4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]*endpoint.Endpoint{
|
||||||
|
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||||
|
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
|
||||||
|
{DNSName: "foo1.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||||
|
{DNSName: "foo1.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
|
||||||
|
{DNSName: "foo2.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||||
|
{DNSName: "foo2.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
|
||||||
|
{DNSName: "foo3.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||||
|
{DNSName: "foo3.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"annotated Headless services create DNS name for each pod using fqdn template",
|
||||||
|
"",
|
||||||
|
"testing",
|
||||||
|
"foo",
|
||||||
|
v1.ServiceTypeClusterIP,
|
||||||
|
"",
|
||||||
|
"{{ .Name }}-{{ .Namespace }}.example.org",
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
map[string]string{"component": "foo"},
|
||||||
|
map[string]string{
|
||||||
|
servicePodEndpointsKey: ServicePodEndpointsFqdnTemplate,
|
||||||
|
hostnameAnnotationKey: "service.example.org",
|
||||||
|
endpointsTypeAnnotationKey: EndpointsTypeNodeExternalIP,
|
||||||
|
},
|
||||||
|
map[string]string{},
|
||||||
|
v1.ClusterIPNone,
|
||||||
|
[]string{"1.1.1.1", "1.1.1.2", "1.1.1.3"},
|
||||||
|
[]string{"", "", ""},
|
||||||
|
map[string]string{
|
||||||
|
"component": "foo",
|
||||||
|
},
|
||||||
|
[]string{},
|
||||||
|
[]string{"foo1", "foo2", "foo3"},
|
||||||
|
[]string{"", "", ""},
|
||||||
|
[]bool{true, true, true},
|
||||||
|
false,
|
||||||
|
[]v1.Node{
|
||||||
|
{
|
||||||
|
Status: v1.NodeStatus{
|
||||||
|
Addresses: []v1.NodeAddress{
|
||||||
|
{
|
||||||
|
Type: v1.NodeExternalIP,
|
||||||
|
Address: "1.2.3.4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: v1.NodeInternalIP,
|
||||||
|
Address: "2001:db8::4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]*endpoint.Endpoint{
|
||||||
|
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||||
|
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
|
||||||
|
{DNSName: "foo1-testing.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||||
|
{DNSName: "foo1-testing.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
|
||||||
|
{DNSName: "foo2-testing.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||||
|
{DNSName: "foo2-testing.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
|
||||||
|
{DNSName: "foo3-testing.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||||
|
{DNSName: "foo3-testing.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"annotated Headless service returns error if incorrect `service-pod-endpoints` value is set",
|
||||||
|
"",
|
||||||
|
"testing",
|
||||||
|
"foo",
|
||||||
|
v1.ServiceTypeClusterIP,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
map[string]string{"component": "foo"},
|
||||||
|
map[string]string{
|
||||||
|
servicePodEndpointsKey: "not-valid",
|
||||||
|
hostnameAnnotationKey: "service.example.org",
|
||||||
|
},
|
||||||
|
map[string]string{},
|
||||||
|
v1.ClusterIPNone,
|
||||||
|
[]string{"1.1.1.1", "1.1.1.2", "1.1.1.3"},
|
||||||
|
[]string{"", "", ""},
|
||||||
|
map[string]string{
|
||||||
|
"component": "foo",
|
||||||
|
},
|
||||||
|
[]string{},
|
||||||
|
[]string{"foo1", "foo2", "foo3"},
|
||||||
|
[]string{"", "", ""},
|
||||||
|
[]bool{true, true, true},
|
||||||
|
false,
|
||||||
|
[]v1.Node{{}},
|
||||||
|
[]*endpoint.Endpoint{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"annotated Headless services return dual-stack targets from node external IP if endpoints-type annotation is set and exposeInternalIPv6 flag set",
|
"annotated Headless services return dual-stack targets from node external IP if endpoints-type annotation is set and exposeInternalIPv6 flag set",
|
||||||
"",
|
"",
|
||||||
|
@ -38,9 +38,13 @@ const (
|
|||||||
ingressHostnameSourceKey = annotations.IngressHostnameSourceKey
|
ingressHostnameSourceKey = annotations.IngressHostnameSourceKey
|
||||||
controllerAnnotationValue = annotations.ControllerValue
|
controllerAnnotationValue = annotations.ControllerValue
|
||||||
internalHostnameAnnotationKey = annotations.InternalHostnameKey
|
internalHostnameAnnotationKey = annotations.InternalHostnameKey
|
||||||
|
servicePodEndpointsKey = annotations.ServicePodEndpoints
|
||||||
|
|
||||||
EndpointsTypeNodeExternalIP = "NodeExternalIP"
|
EndpointsTypeNodeExternalIP = "NodeExternalIP"
|
||||||
EndpointsTypeHostIP = "HostIP"
|
EndpointsTypeHostIP = "HostIP"
|
||||||
|
|
||||||
|
ServicePodEndpointsPodName = "pod-name"
|
||||||
|
ServicePodEndpointsFqdnTemplate = "fqdn-template"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Source defines the interface Endpoint sources should implement.
|
// Source defines the interface Endpoint sources should implement.
|
||||||
|
Loading…
Reference in New Issue
Block a user