Move federation plugin to github.com/coredns/federation (#3139)
* Remove federation Signed-off-by: Yong Tang <yong.tang.github@outlook.com> * Rebuild and point to github.com/coredns/federation Signed-off-by: Yong Tang <yong.tang.github@outlook.com> * Export `localNodeName` => `LocalNodeName`, to be used by federation (until deprecation) Signed-off-by: Yong Tang <yong.tang.github@outlook.com> * Remove plugin/kubernetes/federation.go (=> kubernetes/federation repo) Signed-off-by: Yong Tang <yong.tang.github@outlook.com> * Update github.com/coredns/federation Signed-off-by: Yong Tang <yong.tang.github@outlook.com> * sticker-ci fix Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
This commit is contained in:
parent
bbf148360b
commit
6402cef337
14 changed files with 7 additions and 669 deletions
|
@ -20,7 +20,6 @@ import (
|
|||
_ "github.com/coredns/coredns/plugin/erratic"
|
||||
_ "github.com/coredns/coredns/plugin/errors"
|
||||
_ "github.com/coredns/coredns/plugin/etcd"
|
||||
_ "github.com/coredns/coredns/plugin/federation"
|
||||
_ "github.com/coredns/coredns/plugin/file"
|
||||
_ "github.com/coredns/coredns/plugin/forward"
|
||||
_ "github.com/coredns/coredns/plugin/grpc"
|
||||
|
@ -45,4 +44,5 @@ import (
|
|||
_ "github.com/coredns/coredns/plugin/tls"
|
||||
_ "github.com/coredns/coredns/plugin/trace"
|
||||
_ "github.com/coredns/coredns/plugin/whoami"
|
||||
_ "github.com/coredns/federation"
|
||||
)
|
||||
|
|
1
go.mod
1
go.mod
|
@ -12,6 +12,7 @@ require (
|
|||
github.com/Shopify/sarama v1.21.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.22.3
|
||||
github.com/caddyserver/caddy v1.0.1
|
||||
github.com/coredns/federation v0.0.0-20190818181423-e032b096babe
|
||||
github.com/coreos/bbolt v1.3.2 // indirect
|
||||
github.com/coreos/etcd v3.3.13+incompatible
|
||||
github.com/coreos/go-semver v0.2.0 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -78,6 +78,8 @@ github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXG
|
|||
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coredns/federation v0.0.0-20190818181423-e032b096babe h1:ND08lR/TclI9W4dScCwdRESOacCCdF3FkuB5pBIOv1U=
|
||||
github.com/coredns/federation v0.0.0-20190818181423-e032b096babe/go.mod h1:MoqTEFX8GlnKkyq8eBCF94VzkNAOgjdlCJ+Pz/oCLPk=
|
||||
github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=
|
||||
|
|
|
@ -47,7 +47,7 @@ hosts:hosts
|
|||
route53:route53
|
||||
azure:azure
|
||||
clouddns:clouddns
|
||||
federation:federation
|
||||
federation:github.com/coredns/federation
|
||||
k8s_external:k8s_external
|
||||
kubernetes:kubernetes
|
||||
file:file
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
reviewers:
|
||||
- chrisohaver
|
||||
- miekg
|
||||
approvers:
|
||||
- chrisohaver
|
||||
- miekg
|
|
@ -1,39 +0,0 @@
|
|||
# federation
|
||||
|
||||
## Name
|
||||
|
||||
*federation* - enables federated queries to be resolved via the kubernetes plugin.
|
||||
|
||||
## Description
|
||||
|
||||
Enabling this plugin allows
|
||||
[Federated](https://kubernetes.io/docs/tasks/federation/federation-service-discovery/) queries to be
|
||||
resolved via the kubernetes plugin.
|
||||
|
||||
Enabling *federation* without also having *kubernetes* is a noop.
|
||||
|
||||
## Syntax
|
||||
|
||||
~~~
|
||||
federation [ZONES...] {
|
||||
NAME DOMAIN
|
||||
}
|
||||
~~~
|
||||
|
||||
* Each **NAME** and **DOMAIN** defines federation membership. One entry for each. A duplicate
|
||||
**NAME** will silently overwrite any previous value.
|
||||
|
||||
## Examples
|
||||
|
||||
Here we handle all service requests in the `prod` and `stage` federations.
|
||||
|
||||
~~~
|
||||
. {
|
||||
kubernetes cluster.local
|
||||
federation cluster.local {
|
||||
prod prod.feddomain.com
|
||||
staging staging.feddomain.com
|
||||
}
|
||||
forward . 192.168.1.12
|
||||
}
|
||||
~~~
|
|
@ -1,147 +0,0 @@
|
|||
/*
|
||||
Package federation implements kubernetes federation. It checks if the qname matches
|
||||
a possible federation. If this is the case and the captured answer is an NXDOMAIN,
|
||||
federation is performed. If this is not the case the original answer is returned.
|
||||
|
||||
The federation label is always the 2nd to last once the zone is chopped of. For
|
||||
instance "nginx.mynamespace.myfederation.svc.example.com" has "myfederation" as
|
||||
the federation label. For federation to work we do a normal k8s lookup
|
||||
*without* that label, if that comes back with NXDOMAIN or NODATA(??) we create
|
||||
a federation record and return that.
|
||||
|
||||
Federation is only useful in conjunction with the kubernetes plugin, without it is a noop.
|
||||
*/
|
||||
package federation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/coredns/coredns/plugin"
|
||||
"github.com/coredns/coredns/plugin/etcd/msg"
|
||||
"github.com/coredns/coredns/plugin/pkg/dnsutil"
|
||||
"github.com/coredns/coredns/plugin/pkg/nonwriter"
|
||||
"github.com/coredns/coredns/plugin/pkg/upstream"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Federation contains the name to zone mapping used for federation in kubernetes.
|
||||
type Federation struct {
|
||||
f map[string]string
|
||||
zones []string
|
||||
Upstream *upstream.Upstream
|
||||
|
||||
Next plugin.Handler
|
||||
Federations Func
|
||||
}
|
||||
|
||||
// Func needs to be implemented by any plugin that implements
|
||||
// federation. Right now this is only the kubernetes plugin.
|
||||
type Func func(state request.Request, fname, fzone string) (msg.Service, error)
|
||||
|
||||
// New returns a new federation.
|
||||
func New() *Federation {
|
||||
return &Federation{f: make(map[string]string)}
|
||||
}
|
||||
|
||||
// ServeDNS implements the plugin.Handle interface.
|
||||
func (f *Federation) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||
if f.Federations == nil {
|
||||
return plugin.NextOrFailure(f.Name(), f.Next, ctx, w, r)
|
||||
}
|
||||
|
||||
state := request.Request{W: w, Req: r}
|
||||
|
||||
zone := plugin.Zones(f.zones).Matches(state.Name())
|
||||
if zone == "" {
|
||||
return plugin.NextOrFailure(f.Name(), f.Next, ctx, w, r)
|
||||
}
|
||||
|
||||
state.Zone = zone
|
||||
|
||||
// Remove the federation label from the qname to see if something exists.
|
||||
without, label := f.isNameFederation(state.Name(), state.Zone)
|
||||
if without == "" {
|
||||
return plugin.NextOrFailure(f.Name(), f.Next, ctx, w, r)
|
||||
}
|
||||
|
||||
qname := r.Question[0].Name
|
||||
r.Question[0].Name = without
|
||||
state.Clear()
|
||||
|
||||
// Start the next plugin, but with a nowriter, capture the result, if NXDOMAIN
|
||||
// perform federation, otherwise just write the result.
|
||||
nw := nonwriter.New(w)
|
||||
ret, err := plugin.NextOrFailure(f.Name(), f.Next, ctx, nw, r)
|
||||
|
||||
if !plugin.ClientWrite(ret) {
|
||||
// something went wrong
|
||||
r.Question[0].Name = qname
|
||||
return ret, err
|
||||
}
|
||||
|
||||
if m := nw.Msg; m.Rcode != dns.RcodeNameError {
|
||||
// If positive answer we need to substitute the original qname in the answer.
|
||||
m.Question[0].Name = qname
|
||||
for _, a := range m.Answer {
|
||||
a.Header().Name = qname
|
||||
}
|
||||
|
||||
w.WriteMsg(m)
|
||||
|
||||
return dns.RcodeSuccess, nil
|
||||
}
|
||||
|
||||
// Still here, we've seen NXDOMAIN and need to perform federation.
|
||||
service, err := f.Federations(state, label, f.f[label]) // state references Req which has updated qname
|
||||
if err != nil {
|
||||
r.Question[0].Name = qname
|
||||
return dns.RcodeServerFailure, err
|
||||
}
|
||||
|
||||
r.Question[0].Name = qname
|
||||
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(r)
|
||||
m.Authoritative = true
|
||||
|
||||
m.Answer = []dns.RR{service.NewCNAME(state.QName(), service.Host)}
|
||||
|
||||
if f.Upstream != nil {
|
||||
aRecord, err := f.Upstream.Lookup(ctx, state, service.Host, state.QType())
|
||||
if err == nil && aRecord != nil && len(aRecord.Answer) > 0 {
|
||||
m.Answer = append(m.Answer, aRecord.Answer...)
|
||||
}
|
||||
}
|
||||
|
||||
w.WriteMsg(m)
|
||||
return dns.RcodeSuccess, nil
|
||||
}
|
||||
|
||||
// Name implements the plugin.Handle interface.
|
||||
func (f *Federation) Name() string { return "federation" }
|
||||
|
||||
// IsNameFederation checks the qname to see if it is a potential federation. The federation
|
||||
// label is always the 2nd to last once the zone is chopped of. For instance
|
||||
// "nginx.mynamespace.myfederation.svc.example.com" has "myfederation" as the federation label.
|
||||
// IsNameFederation returns a new qname with the federation label and the label itself or two
|
||||
// empty strings if there wasn't a hit.
|
||||
func (f *Federation) isNameFederation(name, zone string) (string, string) {
|
||||
base, _ := dnsutil.TrimZone(name, zone)
|
||||
|
||||
// TODO(miek): dns.PrevLabel is better for memory, or dns.Split.
|
||||
labels := dns.SplitDomainName(base)
|
||||
ll := len(labels)
|
||||
if ll < 2 {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
fed := labels[ll-2]
|
||||
|
||||
if _, ok := f.f[fed]; ok {
|
||||
without := dnsutil.Join(labels[:ll-2]...) + labels[ll-1] + "." + zone
|
||||
return without, fed
|
||||
}
|
||||
return "", ""
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
package federation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/coredns/coredns/plugin/kubernetes"
|
||||
"github.com/coredns/coredns/plugin/pkg/dnstest"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func TestIsNameFederation(t *testing.T) {
|
||||
tests := []struct {
|
||||
fed string
|
||||
qname string
|
||||
expectedZone string
|
||||
}{
|
||||
{"prod", "nginx.mynamespace.prod.svc.example.com.", "nginx.mynamespace.svc.example.com."},
|
||||
{"prod", "nginx.mynamespace.staging.svc.example.com.", ""},
|
||||
{"prod", "nginx.mynamespace.example.com.", ""},
|
||||
{"prod", "example.com.", ""},
|
||||
{"prod", "com.", ""},
|
||||
}
|
||||
|
||||
fed := New()
|
||||
for i, tc := range tests {
|
||||
fed.f[tc.fed] = "test-name"
|
||||
if x, _ := fed.isNameFederation(tc.qname, "example.com."); x != tc.expectedZone {
|
||||
t.Errorf("Test %d, failed to get zone, expected %s, got %s", i, tc.expectedZone, x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFederationKubernetes(t *testing.T) {
|
||||
tests := []test.Case{
|
||||
{
|
||||
// service exists so we return the IP address associated with it.
|
||||
Qname: "svc1.testns.prod.svc.cluster.local.", Qtype: dns.TypeA,
|
||||
Rcode: dns.RcodeSuccess,
|
||||
Answer: []dns.RR{
|
||||
test.A("svc1.testns.prod.svc.cluster.local. 303 IN A 10.0.0.1"),
|
||||
},
|
||||
},
|
||||
{
|
||||
// service does not exist, do the federation dance.
|
||||
Qname: "svc0.testns.prod.svc.cluster.local.", Qtype: dns.TypeA,
|
||||
Rcode: dns.RcodeSuccess,
|
||||
Answer: []dns.RR{
|
||||
test.CNAME("svc0.testns.prod.svc.cluster.local. 303 IN CNAME svc0.testns.prod.svc.fd-az.fd-r.federal.example."),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
k := kubernetes.New([]string{"cluster.local."})
|
||||
k.APIConn = &APIConnFederationTest{zone: "fd-az", region: "fd-r"}
|
||||
|
||||
fed := New()
|
||||
fed.zones = []string{"cluster.local."}
|
||||
fed.Federations = k.Federations
|
||||
fed.Next = k
|
||||
fed.f = map[string]string{
|
||||
"prod": "federal.example.",
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
for i, tc := range tests {
|
||||
m := tc.Msg()
|
||||
|
||||
rec := dnstest.NewRecorder(&test.ResponseWriter{})
|
||||
_, err := fed.ServeDNS(ctx, rec, m)
|
||||
if err != nil {
|
||||
t.Errorf("Test %d, expected no error, got %v", i, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp := rec.Msg
|
||||
if err := test.SortAndCheck(resp, tc); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFederationKubernetesMissingLabels(t *testing.T) {
|
||||
tests := []test.Case{
|
||||
{
|
||||
// service does not exist, do the federation dance.
|
||||
Qname: "svc0.testns.prod.svc.cluster.local.", Qtype: dns.TypeA,
|
||||
Rcode: dns.RcodeSuccess,
|
||||
Answer: []dns.RR{
|
||||
test.CNAME("svc0.testns.prod.svc.cluster.local. 303 IN CNAME svc0.testns.prod.svc.fd-az.fd-r.federal.example."),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
k := kubernetes.New([]string{"cluster.local."})
|
||||
k.APIConn = &APIConnFederationTest{zone: "", region: ""}
|
||||
|
||||
fed := New()
|
||||
fed.zones = []string{"cluster.local."}
|
||||
fed.Federations = k.Federations
|
||||
fed.Next = k
|
||||
fed.f = map[string]string{
|
||||
"prod": "federal.example.",
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
for _, tc := range tests {
|
||||
m := tc.Msg()
|
||||
|
||||
rec := dnstest.NewRecorder(&test.ResponseWriter{})
|
||||
_, err := fed.ServeDNS(ctx, rec, m)
|
||||
if err == nil {
|
||||
t.Errorf("Expected an error")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
package federation
|
||||
|
||||
import (
|
||||
"github.com/coredns/coredns/plugin/kubernetes"
|
||||
"github.com/coredns/coredns/plugin/kubernetes/object"
|
||||
|
||||
api "k8s.io/api/core/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type APIConnFederationTest struct {
|
||||
zone, region string
|
||||
}
|
||||
|
||||
func (APIConnFederationTest) HasSynced() bool { return true }
|
||||
func (APIConnFederationTest) Run() { return }
|
||||
func (APIConnFederationTest) Stop() error { return nil }
|
||||
func (APIConnFederationTest) SvcIndexReverse(string) []*object.Service { return nil }
|
||||
func (APIConnFederationTest) EpIndexReverse(string) []*object.Endpoints { return nil }
|
||||
func (APIConnFederationTest) Modified() int64 { return 0 }
|
||||
|
||||
func (APIConnFederationTest) PodIndex(string) []*object.Pod {
|
||||
return []*object.Pod{
|
||||
{Namespace: "podns", PodIP: "10.240.0.1"}, // Remote IP set in test.ResponseWriter
|
||||
}
|
||||
}
|
||||
|
||||
func (APIConnFederationTest) SvcIndex(string) []*object.Service {
|
||||
svcs := []*object.Service{
|
||||
{
|
||||
Name: "svc1",
|
||||
Namespace: "testns",
|
||||
ClusterIP: "10.0.0.1",
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "http", Protocol: "tcp", Port: 80},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "hdls1",
|
||||
Namespace: "testns",
|
||||
ClusterIP: api.ClusterIPNone,
|
||||
},
|
||||
{
|
||||
Name: "external",
|
||||
Namespace: "testns",
|
||||
ExternalName: "ext.interwebs.test",
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "http", Protocol: "tcp", Port: 80},
|
||||
},
|
||||
},
|
||||
}
|
||||
return svcs
|
||||
}
|
||||
|
||||
func (APIConnFederationTest) ServiceList() []*object.Service {
|
||||
svcs := []*object.Service{
|
||||
{
|
||||
Name: "svc1",
|
||||
Namespace: "testns",
|
||||
ClusterIP: "10.0.0.1",
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "http", Protocol: "tcp", Port: 80},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "hdls1",
|
||||
Namespace: "testns",
|
||||
ClusterIP: api.ClusterIPNone,
|
||||
},
|
||||
{
|
||||
Name: "external",
|
||||
Namespace: "testns",
|
||||
ExternalName: "ext.interwebs.test",
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "http", Protocol: "tcp", Port: 80},
|
||||
},
|
||||
},
|
||||
}
|
||||
return svcs
|
||||
}
|
||||
|
||||
func (APIConnFederationTest) EpIndex(string) []*object.Endpoints {
|
||||
eps := []*object.Endpoints{
|
||||
{
|
||||
Subsets: []object.EndpointSubset{
|
||||
{
|
||||
Addresses: []object.EndpointAddress{
|
||||
{IP: "172.0.0.1", Hostname: "ep1a"},
|
||||
},
|
||||
Ports: []object.EndpointPort{
|
||||
{Port: 80, Protocol: "tcp", Name: "http"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Name: "svc1",
|
||||
Namespace: "testns",
|
||||
},
|
||||
}
|
||||
return eps
|
||||
}
|
||||
|
||||
func (APIConnFederationTest) EndpointsList() []*object.Endpoints {
|
||||
eps := []*object.Endpoints{
|
||||
{
|
||||
Subsets: []object.EndpointSubset{
|
||||
{
|
||||
Addresses: []object.EndpointAddress{
|
||||
{IP: "172.0.0.1", Hostname: "ep1a"},
|
||||
},
|
||||
Ports: []object.EndpointPort{
|
||||
{Port: 80, Protocol: "tcp", Name: "http"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Name: "svc1",
|
||||
Namespace: "testns",
|
||||
},
|
||||
}
|
||||
return eps
|
||||
}
|
||||
|
||||
func (a APIConnFederationTest) GetNodeByName(name string) (*api.Node, error) {
|
||||
return &api.Node{
|
||||
ObjectMeta: meta.ObjectMeta{
|
||||
Name: "test.node.foo.bar",
|
||||
Labels: map[string]string{
|
||||
kubernetes.LabelRegion: a.region,
|
||||
kubernetes.LabelZone: a.zone,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (APIConnFederationTest) GetNamespaceByName(name string) (*api.Namespace, error) {
|
||||
return &api.Namespace{
|
||||
ObjectMeta: meta.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
}, nil
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package federation
|
||||
|
||||
import clog "github.com/coredns/coredns/plugin/pkg/log"
|
||||
|
||||
func init() { clog.Discard() }
|
|
@ -1,94 +0,0 @@
|
|||
package federation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/coredns/coredns/core/dnsserver"
|
||||
"github.com/coredns/coredns/plugin"
|
||||
"github.com/coredns/coredns/plugin/kubernetes"
|
||||
"github.com/coredns/coredns/plugin/pkg/upstream"
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"github.com/caddyserver/caddy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterPlugin("federation", caddy.Plugin{
|
||||
ServerType: "dns",
|
||||
Action: setup,
|
||||
})
|
||||
}
|
||||
|
||||
func setup(c *caddy.Controller) error {
|
||||
fed, err := federationParse(c)
|
||||
if err != nil {
|
||||
return plugin.Error("federation", err)
|
||||
}
|
||||
|
||||
// Do this in OnStartup, so all plugin has been initialized.
|
||||
c.OnStartup(func() error {
|
||||
m := dnsserver.GetConfig(c).Handler("kubernetes")
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
if x, ok := m.(*kubernetes.Kubernetes); ok {
|
||||
fed.Federations = x.Federations
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
|
||||
fed.Next = next
|
||||
return fed
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func federationParse(c *caddy.Controller) (*Federation, error) {
|
||||
fed := New()
|
||||
fed.Upstream = upstream.New()
|
||||
|
||||
for c.Next() {
|
||||
// federation [zones..]
|
||||
zones := c.RemainingArgs()
|
||||
var origins []string
|
||||
if len(zones) > 0 {
|
||||
origins = make([]string, len(zones))
|
||||
copy(origins, zones)
|
||||
} else {
|
||||
origins = make([]string, len(c.ServerBlockKeys))
|
||||
copy(origins, c.ServerBlockKeys)
|
||||
}
|
||||
|
||||
for c.NextBlock() {
|
||||
x := c.Val()
|
||||
switch x {
|
||||
case "upstream":
|
||||
// remove soon
|
||||
c.RemainingArgs()
|
||||
default:
|
||||
args := c.RemainingArgs()
|
||||
if x := len(args); x != 1 {
|
||||
return fed, fmt.Errorf("need two arguments for federation, got %d", x)
|
||||
}
|
||||
|
||||
fed.f[x] = dns.Fqdn(args[0])
|
||||
}
|
||||
}
|
||||
|
||||
for i := range origins {
|
||||
origins[i] = plugin.Host(origins[i]).Normalize()
|
||||
}
|
||||
|
||||
fed.zones = origins
|
||||
|
||||
if len(fed.f) == 0 {
|
||||
return fed, fmt.Errorf("at least one name to zone federation expected")
|
||||
}
|
||||
|
||||
return fed, nil
|
||||
}
|
||||
|
||||
return fed, nil
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
package federation
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/caddyserver/caddy"
|
||||
)
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
shouldErr bool
|
||||
expectedLen int
|
||||
expectedNameZone []string // contains only entry for now
|
||||
}{
|
||||
// ok
|
||||
{`federation {
|
||||
prod prod.example.org
|
||||
}`, false, 1, []string{"prod", "prod.example.org."}},
|
||||
|
||||
{`federation {
|
||||
staging staging.example.org
|
||||
prod prod.example.org
|
||||
}`, false, 2, []string{"prod", "prod.example.org."}},
|
||||
{`federation {
|
||||
staging staging.example.org
|
||||
prod prod.example.org
|
||||
}`, false, 2, []string{"staging", "staging.example.org."}},
|
||||
{`federation example.com {
|
||||
staging staging.example.org
|
||||
prod prod.example.org
|
||||
}`, false, 2, []string{"staging", "staging.example.org."}},
|
||||
// errors
|
||||
{`federation {
|
||||
}`, true, 0, []string{}},
|
||||
{`federation {
|
||||
staging
|
||||
}`, true, 0, []string{}},
|
||||
}
|
||||
for i, test := range tests {
|
||||
c := caddy.NewTestController("dns", test.input)
|
||||
fed, err := federationParse(c)
|
||||
if test.shouldErr && err == nil {
|
||||
t.Errorf("Test %v: Expected error but found nil", i)
|
||||
continue
|
||||
} else if !test.shouldErr && err != nil {
|
||||
t.Errorf("Test %v: Expected no error but found error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if test.shouldErr && err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if x := len(fed.f); x != test.expectedLen {
|
||||
t.Errorf("Test %v: Expected map length of %d, got: %d", i, test.expectedLen, x)
|
||||
}
|
||||
if x, ok := fed.f[test.expectedNameZone[0]]; !ok {
|
||||
t.Errorf("Test %v: Expected name for %s, got nothing", i, test.expectedNameZone[0])
|
||||
} else {
|
||||
if x != test.expectedNameZone[1] {
|
||||
t.Errorf("Test %v: Expected zone: %s, got %s", i, test.expectedNameZone[1], x)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package kubernetes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/coredns/coredns/plugin/etcd/msg"
|
||||
"github.com/coredns/coredns/plugin/pkg/dnsutil"
|
||||
"github.com/coredns/coredns/request"
|
||||
)
|
||||
|
||||
// The federation node.Labels keys used.
|
||||
const (
|
||||
// TODO: Do not hardcode these labels. Pull them out of the API instead.
|
||||
//
|
||||
// We can get them via ....
|
||||
// import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
// metav1.LabelZoneFailureDomain
|
||||
// metav1.LabelZoneRegion
|
||||
//
|
||||
// But importing above breaks coredns with flag collision of 'log_dir'
|
||||
|
||||
LabelZone = "failure-domain.beta.kubernetes.io/zone"
|
||||
LabelRegion = "failure-domain.beta.kubernetes.io/region"
|
||||
)
|
||||
|
||||
// Federations is used from the federations plugin to return the service that should be
|
||||
// returned as a CNAME for federation(s) to work.
|
||||
func (k *Kubernetes) Federations(state request.Request, fname, fzone string) (msg.Service, error) {
|
||||
nodeName := k.localNodeName()
|
||||
node, err := k.APIConn.GetNodeByName(nodeName)
|
||||
if err != nil {
|
||||
return msg.Service{}, err
|
||||
}
|
||||
r, err := parseRequest(state)
|
||||
if err != nil {
|
||||
return msg.Service{}, err
|
||||
}
|
||||
|
||||
lz := node.Labels[LabelZone]
|
||||
lr := node.Labels[LabelRegion]
|
||||
|
||||
if lz == "" || lr == "" {
|
||||
return msg.Service{}, errors.New("local node missing zone/region labels")
|
||||
}
|
||||
|
||||
if r.endpoint == "" {
|
||||
return msg.Service{Host: dnsutil.Join(r.service, r.namespace, fname, r.podOrSvc, lz, lr, fzone)}, nil
|
||||
}
|
||||
|
||||
return msg.Service{Host: dnsutil.Join(r.endpoint, r.service, r.namespace, fname, r.podOrSvc, lz, lr, fzone)}, nil
|
||||
}
|
|
@ -21,7 +21,8 @@ func localPodIP() net.IP {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (k *Kubernetes) localNodeName() string {
|
||||
// LocalNodeName is exclusively used in federation plugin, will be deprecated later.
|
||||
func (k *Kubernetes) LocalNodeName() string {
|
||||
localIP := k.interfaceAddrsFunc()
|
||||
if localIP == nil {
|
||||
return ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue