aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugin/k8s_external/external_test.go4
-rw-r--r--plugin/kubernetes/controller.go8
-rw-r--r--plugin/kubernetes/external_test.go4
-rw-r--r--plugin/kubernetes/handler_test.go82
-rw-r--r--plugin/kubernetes/informer_test.go21
-rw-r--r--plugin/kubernetes/kubernetes.go38
-rw-r--r--plugin/kubernetes/kubernetes_test.go78
-rw-r--r--plugin/kubernetes/ns.go9
-rw-r--r--plugin/kubernetes/ns_test.go18
-rw-r--r--plugin/kubernetes/object/metrics.go2
-rw-r--r--plugin/kubernetes/object/service.go18
-rw-r--r--plugin/kubernetes/reverse_test.go16
-rw-r--r--plugin/kubernetes/xfr.go13
-rw-r--r--plugin/kubernetes/xfr_test.go4
14 files changed, 204 insertions, 111 deletions
diff --git a/plugin/k8s_external/external_test.go b/plugin/k8s_external/external_test.go
index 45584b6b1..e51fc6895 100644
--- a/plugin/k8s_external/external_test.go
+++ b/plugin/k8s_external/external_test.go
@@ -190,7 +190,7 @@ var svcIndexExternal = map[string][]*object.Service{
Name: "svc1",
Namespace: "testns",
Type: api.ServiceTypeClusterIP,
- ClusterIP: "10.0.0.1",
+ ClusterIPs: []string{"10.0.0.1"},
ExternalIPs: []string{"1.2.3.4"},
Ports: []api.ServicePort{{Name: "http", Protocol: "tcp", Port: 80}},
},
@@ -200,7 +200,7 @@ var svcIndexExternal = map[string][]*object.Service{
Name: "svc6",
Namespace: "testns",
Type: api.ServiceTypeClusterIP,
- ClusterIP: "10.0.0.3",
+ ClusterIPs: []string{"10.0.0.3"},
ExternalIPs: []string{"1:2::5"},
Ports: []api.ServicePort{{Name: "http", Protocol: "tcp", Port: 80}},
},
diff --git a/plugin/kubernetes/controller.go b/plugin/kubernetes/controller.go
index 2319cf203..d10d9f313 100644
--- a/plugin/kubernetes/controller.go
+++ b/plugin/kubernetes/controller.go
@@ -200,11 +200,13 @@ func svcIPIndexFunc(obj interface{}) ([]string, error) {
if !ok {
return nil, errObj
}
+ idx := make([]string, len(svc.ClusterIPs)+len(svc.ExternalIPs))
+ copy(idx, svc.ClusterIPs)
if len(svc.ExternalIPs) == 0 {
- return []string{svc.ClusterIP}, nil
+ return idx, nil
}
-
- return append([]string{svc.ClusterIP}, svc.ExternalIPs...), nil
+ copy(idx[len(svc.ClusterIPs):], svc.ExternalIPs)
+ return idx, nil
}
func svcNameNamespaceIndexFunc(obj interface{}) ([]string, error) {
diff --git a/plugin/kubernetes/external_test.go b/plugin/kubernetes/external_test.go
index 4855cfc63..560983e4c 100644
--- a/plugin/kubernetes/external_test.go
+++ b/plugin/kubernetes/external_test.go
@@ -105,7 +105,7 @@ var svcIndexExternal = map[string][]*object.Service{
Name: "svc1",
Namespace: "testns",
Type: api.ServiceTypeClusterIP,
- ClusterIP: "10.0.0.1",
+ ClusterIPs: []string{"10.0.0.1"},
ExternalIPs: []string{"1.2.3.4"},
Ports: []api.ServicePort{{Name: "http", Protocol: "tcp", Port: 80}},
},
@@ -115,7 +115,7 @@ var svcIndexExternal = map[string][]*object.Service{
Name: "svc6",
Namespace: "testns",
Type: api.ServiceTypeClusterIP,
- ClusterIP: "10.0.0.3",
+ ClusterIPs: []string{"10.0.0.3"},
ExternalIPs: []string{"1:2::5"},
Ports: []api.ServicePort{{Name: "http", Protocol: "tcp", Port: 80}},
},
diff --git a/plugin/kubernetes/handler_test.go b/plugin/kubernetes/handler_test.go
index f5630ccdd..71609a6de 100644
--- a/plugin/kubernetes/handler_test.go
+++ b/plugin/kubernetes/handler_test.go
@@ -372,6 +372,30 @@ var dnsTestCases = []test.Case{
test.SOA("cluster.local. 5 IN SOA ns.dns.cluster.local. hostmaster.cluster.local. 1499347823 7200 1800 86400 5"),
},
},
+ // Dual Stack ClusterIP Services
+ {
+ Qname: "svc-dual-stack.testns.svc.cluster.local.", Qtype: dns.TypeA,
+ Rcode: dns.RcodeSuccess,
+ Answer: []dns.RR{
+ test.A("svc-dual-stack.testns.svc.cluster.local. 5 IN A 10.0.0.3"),
+ },
+ },
+ {
+ Qname: "svc-dual-stack.testns.svc.cluster.local.", Qtype: dns.TypeAAAA,
+ Rcode: dns.RcodeSuccess,
+ Answer: []dns.RR{
+ test.AAAA("svc-dual-stack.testns.svc.cluster.local. 5 IN AAAA 10::3"),
+ },
+ },
+ {
+ Qname: "svc-dual-stack.testns.svc.cluster.local.", Qtype: dns.TypeSRV,
+ Rcode: dns.RcodeSuccess,
+ Answer: []dns.RR{test.SRV("svc-dual-stack.testns.svc.cluster.local. 5 IN SRV 0 50 80 svc-dual-stack.testns.svc.cluster.local.")},
+ Extra: []dns.RR{
+ test.A("svc-dual-stack.testns.svc.cluster.local. 5 IN A 10.0.0.3"),
+ test.AAAA("svc-dual-stack.testns.svc.cluster.local. 5 IN AAAA 10::3"),
+ },
+ },
}
func TestServeDNS(t *testing.T) {
@@ -539,10 +563,10 @@ func (APIConnServeTest) PodIndex(ip string) []*object.Pod {
var svcIndex = map[string][]*object.Service{
"svc1.testns": {
{
- Name: "svc1",
- Namespace: "testns",
- Type: api.ServiceTypeClusterIP,
- ClusterIP: "10.0.0.1",
+ Name: "svc1",
+ Namespace: "testns",
+ Type: api.ServiceTypeClusterIP,
+ ClusterIPs: []string{"10.0.0.1"},
Ports: []api.ServicePort{
{Name: "http", Protocol: "tcp", Port: 80},
},
@@ -550,10 +574,10 @@ var svcIndex = map[string][]*object.Service{
},
"svcempty.testns": {
{
- Name: "svcempty",
- Namespace: "testns",
- Type: api.ServiceTypeClusterIP,
- ClusterIP: "10.0.0.1",
+ Name: "svcempty",
+ Namespace: "testns",
+ Type: api.ServiceTypeClusterIP,
+ ClusterIPs: []string{"10.0.0.1"},
Ports: []api.ServicePort{
{Name: "http", Protocol: "tcp", Port: 80},
},
@@ -561,10 +585,10 @@ var svcIndex = map[string][]*object.Service{
},
"svc6.testns": {
{
- Name: "svc6",
- Namespace: "testns",
- Type: api.ServiceTypeClusterIP,
- ClusterIP: "1234:abcd::1",
+ Name: "svc6",
+ Namespace: "testns",
+ Type: api.ServiceTypeClusterIP,
+ ClusterIPs: []string{"1234:abcd::1"},
Ports: []api.ServicePort{
{Name: "http", Protocol: "tcp", Port: 80},
},
@@ -572,10 +596,10 @@ var svcIndex = map[string][]*object.Service{
},
"hdls1.testns": {
{
- Name: "hdls1",
- Namespace: "testns",
- Type: api.ServiceTypeClusterIP,
- ClusterIP: api.ClusterIPNone,
+ Name: "hdls1",
+ Namespace: "testns",
+ Type: api.ServiceTypeClusterIP,
+ ClusterIPs: []string{api.ClusterIPNone},
},
},
"external.testns": {
@@ -602,23 +626,33 @@ var svcIndex = map[string][]*object.Service{
},
"hdlsprtls.testns": {
{
- Name: "hdlsprtls",
- Namespace: "testns",
- Type: api.ServiceTypeClusterIP,
- ClusterIP: api.ClusterIPNone,
+ Name: "hdlsprtls",
+ Namespace: "testns",
+ Type: api.ServiceTypeClusterIP,
+ ClusterIPs: []string{api.ClusterIPNone},
},
},
"svc1.unexposedns": {
{
- Name: "svc1",
- Namespace: "unexposedns",
- Type: api.ServiceTypeClusterIP,
- ClusterIP: "10.0.0.2",
+ Name: "svc1",
+ Namespace: "unexposedns",
+ Type: api.ServiceTypeClusterIP,
+ ClusterIPs: []string{"10.0.0.2"},
Ports: []api.ServicePort{
{Name: "http", Protocol: "tcp", Port: 80},
},
},
},
+ "svc-dual-stack.testns": {
+ {
+ Name: "svc-dual-stack",
+ Namespace: "testns",
+ Type: api.ServiceTypeClusterIP,
+ ClusterIPs: []string{"10.0.0.3", "10::3"}, Ports: []api.ServicePort{
+ {Name: "http", Protocol: "tcp", Port: 80},
+ },
+ },
+ },
}
func (APIConnServeTest) SvcIndex(s string) []*object.Service { return svcIndex[s] }
diff --git a/plugin/kubernetes/informer_test.go b/plugin/kubernetes/informer_test.go
index 7aa9d1e83..ae68b5cfe 100644
--- a/plugin/kubernetes/informer_test.go
+++ b/plugin/kubernetes/informer_test.go
@@ -1,6 +1,7 @@
package kubernetes
import (
+ "fmt"
"testing"
"github.com/coredns/coredns/plugin/kubernetes/object"
@@ -21,11 +22,19 @@ func TestDefaultProcessor(t *testing.T) {
func testProcessor(t *testing.T, processor cache.ProcessFunc, idx cache.Indexer) {
obj := &api.Service{
ObjectMeta: metav1.ObjectMeta{Name: "service1", Namespace: "test1"},
- Spec: api.ServiceSpec{ClusterIP: "1.2.3.4", Ports: []api.ServicePort{{Port: 80}}},
+ Spec: api.ServiceSpec{
+ ClusterIP: "1.2.3.4",
+ ClusterIPs: []string{"1.2.3.4"},
+ Ports: []api.ServicePort{{Port: 80}},
+ },
}
obj2 := &api.Service{
ObjectMeta: metav1.ObjectMeta{Name: "service2", Namespace: "test1"},
- Spec: api.ServiceSpec{ClusterIP: "5.6.7.8", Ports: []api.ServicePort{{Port: 80}}},
+ Spec: api.ServiceSpec{
+ ClusterIP: "5.6.7.8",
+ ClusterIPs: []string{"5.6.7.8"},
+ Ports: []api.ServicePort{{Port: 80}},
+ },
}
// Add the objects
@@ -47,8 +56,8 @@ func testProcessor(t *testing.T, processor cache.ProcessFunc, idx cache.Indexer)
if !ok {
t.Fatal("object in index was incorrect type")
}
- if svc.ClusterIP != obj.Spec.ClusterIP {
- t.Fatalf("expected %v, got %v", obj.Spec.ClusterIP, svc.ClusterIP)
+ if fmt.Sprintf("%v", svc.ClusterIPs) != fmt.Sprintf("%v", obj.Spec.ClusterIPs) {
+ t.Fatalf("expected '%v', got '%v'", obj.Spec.ClusterIPs, svc.ClusterIPs)
}
// Update an object
@@ -71,8 +80,8 @@ func testProcessor(t *testing.T, processor cache.ProcessFunc, idx cache.Indexer)
if !ok {
t.Fatal("object in index was incorrect type")
}
- if svc.ClusterIP != obj.Spec.ClusterIP {
- t.Fatalf("expected %v, got %v", obj.Spec.ClusterIP, svc.ClusterIP)
+ if fmt.Sprintf("%v", svc.ClusterIPs) != fmt.Sprintf("%v", obj.Spec.ClusterIPs) {
+ t.Fatalf("expected '%v', got '%v'", obj.Spec.ClusterIPs, svc.ClusterIPs)
}
// Delete an object
diff --git a/plugin/kubernetes/kubernetes.go b/plugin/kubernetes/kubernetes.go
index 48d489330..a7fa37804 100644
--- a/plugin/kubernetes/kubernetes.go
+++ b/plugin/kubernetes/kubernetes.go
@@ -433,8 +433,7 @@ func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg.
// If "ignore empty_service" option is set and no endpoints exist, return NXDOMAIN unless
// it's a headless or externalName service (covered below).
- if k.opts.ignoreEmptyService && svc.ClusterIP != api.ClusterIPNone && svc.Type != api.ServiceTypeExternalName {
- // serve NXDOMAIN if no endpoint is able to answer
+ if k.opts.ignoreEmptyService && svc.Type != api.ServiceTypeExternalName && !svc.Headless() { // serve NXDOMAIN if no endpoint is able to answer
podsCount := 0
for _, ep := range endpointsListFunc() {
for _, eps := range ep.Subsets {
@@ -447,8 +446,20 @@ func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg.
}
}
+ // External service
+ if svc.Type == api.ServiceTypeExternalName {
+ s := msg.Service{Key: strings.Join([]string{zonePath, Svc, svc.Namespace, svc.Name}, "/"), Host: svc.ExternalName, TTL: k.ttl}
+ if t, _ := s.HostType(); t == dns.TypeCNAME {
+ s.Key = strings.Join([]string{zonePath, Svc, svc.Namespace, svc.Name}, "/")
+ services = append(services, s)
+
+ err = nil
+ }
+ continue
+ }
+
// Endpoint query or headless service
- if svc.ClusterIP == api.ClusterIPNone || r.endpoint != "" {
+ if svc.Headless() || r.endpoint != "" {
if endpointsList == nil {
endpointsList = endpointsListFunc()
}
@@ -485,18 +496,6 @@ func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg.
continue
}
- // External service
- if svc.Type == api.ServiceTypeExternalName {
- s := msg.Service{Key: strings.Join([]string{zonePath, Svc, svc.Namespace, svc.Name}, "/"), Host: svc.ExternalName, TTL: k.ttl}
- if t, _ := s.HostType(); t == dns.TypeCNAME {
- s.Key = strings.Join([]string{zonePath, Svc, svc.Namespace, svc.Name}, "/")
- services = append(services, s)
-
- err = nil
- }
- continue
- }
-
// ClusterIP service
for _, p := range svc.Ports {
if !(match(r.port, p.Name) && match(r.protocol, string(p.Protocol))) {
@@ -505,10 +504,11 @@ func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg.
err = nil
- s := msg.Service{Host: svc.ClusterIP, Port: int(p.Port), TTL: k.ttl}
- s.Key = strings.Join([]string{zonePath, Svc, svc.Namespace, svc.Name}, "/")
-
- services = append(services, s)
+ for _, ip := range svc.ClusterIPs {
+ s := msg.Service{Host: ip, Port: int(p.Port), TTL: k.ttl}
+ s.Key = strings.Join([]string{zonePath, Svc, svc.Namespace, svc.Name}, "/")
+ services = append(services, s)
+ }
}
}
return services, err
diff --git a/plugin/kubernetes/kubernetes_test.go b/plugin/kubernetes/kubernetes_test.go
index 7458a3bdc..a342d9a23 100644
--- a/plugin/kubernetes/kubernetes_test.go
+++ b/plugin/kubernetes/kubernetes_test.go
@@ -71,17 +71,25 @@ func (APIConnServiceTest) Modified() int64 { return 0
func (APIConnServiceTest) SvcIndex(string) []*object.Service {
svcs := []*object.Service{
{
- Name: "svc1",
- Namespace: "testns",
- ClusterIP: "10.0.0.1",
+ Name: "svc1",
+ Namespace: "testns",
+ ClusterIPs: []string{"10.0.0.1"},
Ports: []api.ServicePort{
{Name: "http", Protocol: "tcp", Port: 80},
},
},
{
- Name: "hdls1",
- Namespace: "testns",
- ClusterIP: api.ClusterIPNone,
+ Name: "svc-dual-stack",
+ Namespace: "testns",
+ ClusterIPs: []string{"10.0.0.2", "10::2"},
+ Ports: []api.ServicePort{
+ {Name: "http", Protocol: "tcp", Port: 80},
+ },
+ },
+ {
+ Name: "hdls1",
+ Namespace: "testns",
+ ClusterIPs: []string{api.ClusterIPNone},
},
{
Name: "external",
@@ -99,17 +107,25 @@ func (APIConnServiceTest) SvcIndex(string) []*object.Service {
func (APIConnServiceTest) ServiceList() []*object.Service {
svcs := []*object.Service{
{
- Name: "svc1",
- Namespace: "testns",
- ClusterIP: "10.0.0.1",
+ Name: "svc1",
+ Namespace: "testns",
+ ClusterIPs: []string{"10.0.0.1"},
Ports: []api.ServicePort{
{Name: "http", Protocol: "tcp", Port: 80},
},
},
{
- Name: "hdls1",
- Namespace: "testns",
- ClusterIP: api.ClusterIPNone,
+ Name: "svc-dual-stack",
+ Namespace: "testns",
+ ClusterIPs: []string{"10.0.0.2", "10::2"},
+ Ports: []api.ServicePort{
+ {Name: "http", Protocol: "tcp", Port: 80},
+ },
+ },
+ {
+ Name: "hdls1",
+ Namespace: "testns",
+ ClusterIPs: []string{api.ClusterIPNone},
},
{
Name: "external",
@@ -256,19 +272,29 @@ func TestServices(t *testing.T) {
type svcTest struct {
qname string
qtype uint16
- answer svcAns
+ answer []svcAns
}
tests := []svcTest{
// Cluster IP Services
- {qname: "svc1.testns.svc.interwebs.test.", qtype: dns.TypeA, answer: svcAns{host: "10.0.0.1", key: "/" + coredns + "/test/interwebs/svc/testns/svc1"}},
- {qname: "_http._tcp.svc1.testns.svc.interwebs.test.", qtype: dns.TypeSRV, answer: svcAns{host: "10.0.0.1", key: "/" + coredns + "/test/interwebs/svc/testns/svc1"}},
- {qname: "ep1a.svc1.testns.svc.interwebs.test.", qtype: dns.TypeA, answer: svcAns{host: "172.0.0.1", key: "/" + coredns + "/test/interwebs/svc/testns/svc1/ep1a"}},
+ {qname: "svc1.testns.svc.interwebs.test.", qtype: dns.TypeA, answer: []svcAns{{host: "10.0.0.1", key: "/" + coredns + "/test/interwebs/svc/testns/svc1"}}},
+ {qname: "_http._tcp.svc1.testns.svc.interwebs.test.", qtype: dns.TypeSRV, answer: []svcAns{{host: "10.0.0.1", key: "/" + coredns + "/test/interwebs/svc/testns/svc1"}}},
+ {qname: "ep1a.svc1.testns.svc.interwebs.test.", qtype: dns.TypeA, answer: []svcAns{{host: "172.0.0.1", key: "/" + coredns + "/test/interwebs/svc/testns/svc1/ep1a"}}},
+
+ // Dual-Stack Cluster IP Service
+ {
+ qname: "_http._tcp.svc-dual-stack.testns.svc.interwebs.test.",
+ qtype: dns.TypeSRV,
+ answer: []svcAns{
+ {host: "10.0.0.2", key: "/" + coredns + "/test/interwebs/svc/testns/svc-dual-stack"},
+ {host: "10::2", key: "/" + coredns + "/test/interwebs/svc/testns/svc-dual-stack"},
+ },
+ },
// External Services
- {qname: "external.testns.svc.interwebs.test.", qtype: dns.TypeCNAME, answer: svcAns{host: "coredns.io", key: "/" + coredns + "/test/interwebs/svc/testns/external"}},
+ {qname: "external.testns.svc.interwebs.test.", qtype: dns.TypeCNAME, answer: []svcAns{{host: "coredns.io", key: "/" + coredns + "/test/interwebs/svc/testns/external"}}},
// Headless Services
- {qname: "hdls1.testns.svc.interwebs.test.", qtype: dns.TypeA, answer: svcAns{host: "172.0.0.2", key: "/" + coredns + "/test/interwebs/svc/testns/hdls1/172-0-0-2"}},
+ {qname: "hdls1.testns.svc.interwebs.test.", qtype: dns.TypeA, answer: []svcAns{{host: "172.0.0.2", key: "/" + coredns + "/test/interwebs/svc/testns/hdls1/172-0-0-2"}}},
}
for i, test := range tests {
@@ -281,16 +307,18 @@ func TestServices(t *testing.T) {
t.Errorf("Test %d: got error '%v'", i, e)
continue
}
- if len(svcs) != 1 {
- t.Errorf("Test %d, expected 1 answer, got %v", i, len(svcs))
+ if len(svcs) != len(test.answer) {
+ t.Errorf("Test %d, expected %v answer, got %v", i, len(test.answer), len(svcs))
continue
}
- if test.answer.host != svcs[0].Host {
- t.Errorf("Test %d, expected host '%v', got '%v'", i, test.answer.host, svcs[0].Host)
- }
- if test.answer.key != svcs[0].Key {
- t.Errorf("Test %d, expected key '%v', got '%v'", i, test.answer.key, svcs[0].Key)
+ for j := range svcs {
+ if test.answer[j].host != svcs[j].Host {
+ t.Errorf("Test %d, expected host '%v', got '%v'", i, test.answer[j].host, svcs[j].Host)
+ }
+ if test.answer[j].key != svcs[j].Key {
+ t.Errorf("Test %d, expected key '%v', got '%v'", i, test.answer[j].key, svcs[j].Key)
+ }
}
}
}
diff --git a/plugin/kubernetes/ns.go b/plugin/kubernetes/ns.go
index ba687cb13..84f4e344b 100644
--- a/plugin/kubernetes/ns.go
+++ b/plugin/kubernetes/ns.go
@@ -5,7 +5,6 @@ import (
"strings"
"github.com/miekg/dns"
- api "k8s.io/api/core/v1"
)
func isDefaultNS(name, zone string) bool {
@@ -37,7 +36,7 @@ func (k *Kubernetes) nsAddrs(external bool, zone string) []dns.RR {
continue
}
svcName := strings.Join([]string{svc.Name, svc.Namespace, Svc, zone}, ".")
- if svc.ClusterIP == api.ClusterIPNone {
+ if svc.Headless() {
// For a headless service, use the endpoints IPs
for _, s := range endpoint.Subsets {
for _, a := range s.Addresses {
@@ -46,8 +45,10 @@ func (k *Kubernetes) nsAddrs(external bool, zone string) []dns.RR {
}
}
} else {
- svcNames = append(svcNames, svcName)
- svcIPs = append(svcIPs, net.ParseIP(svc.ClusterIP))
+ for _, clusterIP := range svc.ClusterIPs {
+ svcNames = append(svcNames, svcName)
+ svcIPs = append(svcIPs, net.ParseIP(clusterIP))
+ }
}
}
}
diff --git a/plugin/kubernetes/ns_test.go b/plugin/kubernetes/ns_test.go
index 682303104..6370675cd 100644
--- a/plugin/kubernetes/ns_test.go
+++ b/plugin/kubernetes/ns_test.go
@@ -37,19 +37,19 @@ func (a APIConnTest) SvcIndex(s string) []*object.Service {
func (APIConnTest) ServiceList() []*object.Service {
svcs := []*object.Service{
{
- Name: "dns-service",
- Namespace: "kube-system",
- ClusterIP: "10.0.0.111",
+ Name: "dns-service",
+ Namespace: "kube-system",
+ ClusterIPs: []string{"10.0.0.111"},
},
{
- Name: "hdls-dns-service",
- Namespace: "kube-system",
- ClusterIP: api.ClusterIPNone,
+ Name: "hdls-dns-service",
+ Namespace: "kube-system",
+ ClusterIPs: []string{api.ClusterIPNone},
},
{
- Name: "dns6-service",
- Namespace: "kube-system",
- ClusterIP: "10::111",
+ Name: "dns6-service",
+ Namespace: "kube-system",
+ ClusterIPs: []string{"10::111"},
},
}
return svcs
diff --git a/plugin/kubernetes/object/metrics.go b/plugin/kubernetes/object/metrics.go
index 929925cf1..f39744b8a 100644
--- a/plugin/kubernetes/object/metrics.go
+++ b/plugin/kubernetes/object/metrics.go
@@ -67,7 +67,7 @@ func (l *EndpointLatencyRecorder) record() {
// don't change very often (comparing to much more frequent endpoints changes), cases when this method
// will return wrong answer should be relatively rare. Because of that we intentionally accept this
// flaw to keep the solution simple.
- isHeadless := len(l.Services) == 1 && l.Services[0].ClusterIP == api.ClusterIPNone
+ isHeadless := len(l.Services) == 1 && l.Services[0].Headless()
if !isHeadless || l.TT.IsZero() {
return
diff --git a/plugin/kubernetes/object/service.go b/plugin/kubernetes/object/service.go
index be1404ea0..812b272e2 100644
--- a/plugin/kubernetes/object/service.go
+++ b/plugin/kubernetes/object/service.go
@@ -15,7 +15,7 @@ type Service struct {
Name string
Namespace string
Index string
- ClusterIP string
+ ClusterIPs []string
Type api.ServiceType
ExternalName string
Ports []api.ServicePort
@@ -40,13 +40,19 @@ func ToService(obj meta.Object) (meta.Object, error) {
Name: svc.GetName(),
Namespace: svc.GetNamespace(),
Index: ServiceKey(svc.GetName(), svc.GetNamespace()),
- ClusterIP: svc.Spec.ClusterIP,
Type: svc.Spec.Type,
ExternalName: svc.Spec.ExternalName,
ExternalIPs: make([]string, len(svc.Status.LoadBalancer.Ingress)+len(svc.Spec.ExternalIPs)),
}
+ if len(svc.Spec.ClusterIPs) > 0 {
+ s.ClusterIPs = make([]string, len(svc.Spec.ClusterIPs))
+ copy(s.ClusterIPs, svc.Spec.ClusterIPs)
+ } else {
+ s.ClusterIPs = []string{svc.Spec.ClusterIP}
+ }
+
if len(svc.Spec.Ports) == 0 {
// Add sentinel if there are no ports.
s.Ports = []api.ServicePort{{Port: -1}}
@@ -70,6 +76,11 @@ func ToService(obj meta.Object) (meta.Object, error) {
return s, nil
}
+// Headless returns true if the service is headless
+func (s *Service) Headless() bool {
+ return s.ClusterIPs[0] == api.ClusterIPNone
+}
+
var _ runtime.Object = &Service{}
// DeepCopyObject implements the ObjectKind interface.
@@ -79,12 +90,13 @@ func (s *Service) DeepCopyObject() runtime.Object {
Name: s.Name,
Namespace: s.Namespace,
Index: s.Index,
- ClusterIP: s.ClusterIP,
Type: s.Type,
ExternalName: s.ExternalName,
+ ClusterIPs: make([]string, len(s.ClusterIPs)),
Ports: make([]api.ServicePort, len(s.Ports)),
ExternalIPs: make([]string, len(s.ExternalIPs)),
}
+ copy(s1.ClusterIPs, s.ClusterIPs)
copy(s1.Ports, s.Ports)
copy(s1.ExternalIPs, s.ExternalIPs)
return s1
diff --git a/plugin/kubernetes/reverse_test.go b/plugin/kubernetes/reverse_test.go
index 3b3b6872b..2e888f064 100644
--- a/plugin/kubernetes/reverse_test.go
+++ b/plugin/kubernetes/reverse_test.go
@@ -30,10 +30,10 @@ func (APIConnReverseTest) SvcIndex(svc string) []*object.Service {
}
svcs := []*object.Service{
{
- Name: "svc1",
- Namespace: "testns",
- ClusterIP: "192.168.1.100",
- Ports: []api.ServicePort{{Name: "http", Protocol: "tcp", Port: 80}},
+ Name: "svc1",
+ Namespace: "testns",
+ ClusterIPs: []string{"192.168.1.100"},
+ Ports: []api.ServicePort{{Name: "http", Protocol: "tcp", Port: 80}},
},
}
return svcs
@@ -46,10 +46,10 @@ func (APIConnReverseTest) SvcIndexReverse(ip string) []*object.Service {
}
svcs := []*object.Service{
{
- Name: "svc1",
- Namespace: "testns",
- ClusterIP: "192.168.1.100",
- Ports: []api.ServicePort{{Name: "http", Protocol: "tcp", Port: 80}},
+ Name: "svc1",
+ Namespace: "testns",
+ ClusterIPs: []string{"192.168.1.100"},
+ Ports: []api.ServicePort{{Name: "http", Protocol: "tcp", Port: 80}},
},
}
return svcs
diff --git a/plugin/kubernetes/xfr.go b/plugin/kubernetes/xfr.go
index 550a70dfd..812604966 100644
--- a/plugin/kubernetes/xfr.go
+++ b/plugin/kubernetes/xfr.go
@@ -50,13 +50,16 @@ func (k *Kubernetes) Transfer(zone string, serial uint32) (<-chan []dns.RR, erro
switch svc.Type {
case api.ServiceTypeClusterIP, api.ServiceTypeNodePort, api.ServiceTypeLoadBalancer:
- clusterIP := net.ParseIP(svc.ClusterIP)
+ clusterIP := net.ParseIP(svc.ClusterIPs[0])
if clusterIP != nil {
- s := msg.Service{Host: svc.ClusterIP, TTL: k.ttl}
- s.Key = strings.Join(svcBase, "/")
+ var host string
+ for _, ip := range svc.ClusterIPs {
+ s := msg.Service{Host: ip, TTL: k.ttl}
+ s.Key = strings.Join(svcBase, "/")
- // Change host from IP to Name for SRV records
- host := emitAddressRecord(ch, s)
+ // Change host from IP to Name for SRV records
+ host = emitAddressRecord(ch, s)
+ }
for _, p := range svc.Ports {
s := msg.Service{Host: host, Port: int(p.Port), TTL: k.ttl}
diff --git a/plugin/kubernetes/xfr_test.go b/plugin/kubernetes/xfr_test.go
index b5f13ad6e..39c4ed226 100644
--- a/plugin/kubernetes/xfr_test.go
+++ b/plugin/kubernetes/xfr_test.go
@@ -113,6 +113,10 @@ hdls1.testns.svc.cluster.local. 5 IN AAAA 5678:abcd::2
_http._tcp.hdls1.testns.svc.cluster.local. 5 IN SRV 0 16 80 5678-abcd--2.hdls1.testns.svc.cluster.local.
hdlsprtls.testns.svc.cluster.local. 5 IN A 172.0.0.20
172-0-0-20.hdlsprtls.testns.svc.cluster.local. 5 IN A 172.0.0.20
+svc-dual-stack.testns.svc.cluster.local. 5 IN A 10.0.0.3
+svc-dual-stack.testns.svc.cluster.local. 5 IN AAAA 10::3
+svc-dual-stack.testns.svc.cluster.local. 5 IN SRV 0 100 80 svc-dual-stack.testns.svc.cluster.local.
+_http._tcp.svc-dual-stack.testns.svc.cluster.local. 5 IN SRV 0 100 80 svc-dual-stack.testns.svc.cluster.local.
svc1.testns.svc.cluster.local. 5 IN A 10.0.0.1
svc1.testns.svc.cluster.local. 5 IN SRV 0 100 80 svc1.testns.svc.cluster.local.
_http._tcp.svc1.testns.svc.cluster.local. 5 IN SRV 0 100 80 svc1.testns.svc.cluster.local.