* Add forwardcrd plugin README.md Co-authored-by: Aidan Obley <aobley@vmware.com> Signed-off-by: Christian Ang <angc@vmware.com> * Create forwardcrd plugin - Place forwardcrd before forward plugin in plugin list. This will avoid forward from preventing the forwardcrd plugin from handling any queries in the case of having a default upstream forwarder in a server block (as is the case in the default kubernetes Corefile). Co-authored-by: Aidan Obley <aobley@vmware.com> Signed-off-by: Christian Ang <angc@vmware.com> * Add Forward CRD Signed-off-by: Christian Ang <angc@vmware.com> * Add NewWithConfig to forward plugin - allows external packages to instanciate forward plugins Co-authored-by: Aidan Obley <aobley@vmware.com> Signed-off-by: Christian Ang <angc@vmware.com> * ForwardCRD plugin handles requests for Forward CRs - add a Kubernetes controller that can read Forward CRs - instances of the forward plugin are created based on Forward CRs from the Kubernetes controller - DNS requests are handled by calling matching Forward plugin instances based on zone name - Defaults to the kube-system namespace to align with Corefile RBAC Signed-off-by: Christian Ang <angc@vmware.com> Use klog v2 in forwardcrd plugin * Refactor forward setup to use NewWithConfig Co-authored-by: Christian Ang <angc@vmware.com> Signed-off-by: Edwin Xie <exie@vmware.com> * Use ParseInt instead of Atoi - to ensure that the bitsize is 32 for later casting to uint32 Signed-off-by: Christian Ang <angc@vmware.com> * Add @christianang to CODEOWNERS for forwardcrd Signed-off-by: Christian Ang <angc@vmware.com> Co-authored-by: Edwin Xie <exie@vmware.com>
183 lines
5.2 KiB
Go
183 lines
5.2 KiB
Go
package forwardcrd
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/coredns/caddy"
|
|
"github.com/coredns/coredns/plugin/forward"
|
|
"github.com/coredns/coredns/plugin/pkg/dnstest"
|
|
"github.com/coredns/coredns/plugin/test"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
func TestDNSRequestForZone(t *testing.T) {
|
|
k, closeAll := setupForwardCRDTestcase(t, "")
|
|
defer closeAll()
|
|
|
|
m := new(dns.Msg)
|
|
m.SetQuestion("crd.test.", dns.TypeA)
|
|
rec := dnstest.NewRecorder(&test.ResponseWriter{})
|
|
if _, err := k.ServeDNS(context.Background(), rec, m); err != nil {
|
|
t.Fatalf("Expected not to error: %s", err)
|
|
}
|
|
|
|
if rec.Msg == nil || len(rec.Msg.Answer) != 1 {
|
|
t.Fatal("Expected an answer")
|
|
}
|
|
|
|
if x := rec.Msg.Answer[0].Header().Name; x != "crd.test." {
|
|
t.Fatalf("Expected answer header name to be: %s, but got: %s", "crd.test.", x)
|
|
}
|
|
|
|
if x := rec.Msg.Answer[0].(*dns.A).A.String(); x != "1.2.3.4" {
|
|
t.Fatalf("Expected answer ip to be: %s, but got: %s", "1.2.3.4", x)
|
|
}
|
|
|
|
m = new(dns.Msg)
|
|
m.SetQuestion("other.test.", dns.TypeA)
|
|
rec = dnstest.NewRecorder(&test.ResponseWriter{})
|
|
if _, err := k.ServeDNS(context.Background(), rec, m); err != nil {
|
|
t.Fatalf("Expected not to error: %s", err)
|
|
}
|
|
|
|
if rec.Msg == nil || len(rec.Msg.Answer) != 1 {
|
|
t.Fatal("Expected an answer")
|
|
}
|
|
|
|
if x := rec.Msg.Answer[0].Header().Name; x != "other.test." {
|
|
t.Fatalf("Expected answer header name to be: %s, but got: %s", "other.test.", x)
|
|
}
|
|
|
|
if x := rec.Msg.Answer[0].(*dns.A).A.String(); x != "1.2.3.4" {
|
|
t.Fatalf("Expected answer ip to be: %s, but got: %s", "1.2.3.4", x)
|
|
}
|
|
}
|
|
|
|
func TestDNSRequestForSubdomain(t *testing.T) {
|
|
k, closeAll := setupForwardCRDTestcase(t, "")
|
|
defer closeAll()
|
|
|
|
m := new(dns.Msg)
|
|
m.SetQuestion("foo.crd.test.", dns.TypeA)
|
|
rec := dnstest.NewRecorder(&test.ResponseWriter{})
|
|
if _, err := k.ServeDNS(context.Background(), rec, m); err != nil {
|
|
t.Fatalf("Expected not to error: %s", err)
|
|
}
|
|
|
|
if rec.Msg == nil || len(rec.Msg.Answer) != 1 {
|
|
t.Fatal("Expected an answer")
|
|
}
|
|
|
|
if x := rec.Msg.Answer[0].Header().Name; x != "foo.crd.test." {
|
|
t.Fatalf("Expected answer header name to be: %s, but got: %s", "foo.crd.test.", x)
|
|
}
|
|
|
|
if x := rec.Msg.Answer[0].(*dns.A).A.String(); x != "1.2.3.4" {
|
|
t.Fatalf("Expected answer ip to be: %s, but got: %s", "1.2.3.4", x)
|
|
}
|
|
}
|
|
|
|
func TestDNSRequestForNonexistantZone(t *testing.T) {
|
|
k, closeAll := setupForwardCRDTestcase(t, "")
|
|
defer closeAll()
|
|
|
|
m := new(dns.Msg)
|
|
m.SetQuestion("non-existant-zone.test.", dns.TypeA)
|
|
rec := dnstest.NewRecorder(&test.ResponseWriter{})
|
|
if rcode, err := k.ServeDNS(context.Background(), rec, m); err == nil || rcode != dns.RcodeServerFailure {
|
|
t.Fatalf("Expected to return rcode: %d and to error: %s", rcode, err)
|
|
}
|
|
}
|
|
|
|
func TestDNSRequestForLimitedZones(t *testing.T) {
|
|
k, closeAll := setupForwardCRDTestcase(t, "crd.test.")
|
|
defer closeAll()
|
|
|
|
m := new(dns.Msg)
|
|
m.SetQuestion("crd.test.", dns.TypeA)
|
|
rec := dnstest.NewRecorder(&test.ResponseWriter{})
|
|
if _, err := k.ServeDNS(context.Background(), rec, m); err != nil {
|
|
t.Fatalf("Expected not to error: %s", err)
|
|
}
|
|
|
|
if rec.Msg == nil || len(rec.Msg.Answer) != 1 {
|
|
t.Fatal("Expected an answer")
|
|
}
|
|
|
|
m = new(dns.Msg)
|
|
m.SetQuestion("sub.crd.test.", dns.TypeA)
|
|
rec = dnstest.NewRecorder(&test.ResponseWriter{})
|
|
if _, err := k.ServeDNS(context.Background(), rec, m); err != nil {
|
|
t.Fatalf("Expected not to error: %s", err)
|
|
}
|
|
|
|
if rec.Msg == nil || len(rec.Msg.Answer) != 1 {
|
|
t.Fatal("Expected an answer")
|
|
}
|
|
|
|
m = new(dns.Msg)
|
|
m.SetQuestion("other.test.", dns.TypeA)
|
|
rec = dnstest.NewRecorder(&test.ResponseWriter{})
|
|
if rcode, err := k.ServeDNS(context.Background(), rec, m); err == nil || rcode != dns.RcodeServerFailure {
|
|
t.Fatalf("Expected to return rcode: %d and to error: %s", rcode, err)
|
|
}
|
|
|
|
m = new(dns.Msg)
|
|
m.SetQuestion("test.", dns.TypeA)
|
|
rec = dnstest.NewRecorder(&test.ResponseWriter{})
|
|
if rcode, err := k.ServeDNS(context.Background(), rec, m); err == nil || rcode != dns.RcodeServerFailure {
|
|
t.Fatalf("Expected to return rcode: %d and to error: %s", rcode, err)
|
|
}
|
|
}
|
|
|
|
func setupForwardCRDTestcase(t *testing.T, zone string) (*ForwardCRD, func()) {
|
|
s := dnstest.NewServer(func(w dns.ResponseWriter, r *dns.Msg) {
|
|
ret := new(dns.Msg)
|
|
ret.SetReply(r)
|
|
ret.Answer = append(ret.Answer, test.A(fmt.Sprintf("%s IN A 1.2.3.4", strings.ToLower(r.Question[0].Name))))
|
|
w.WriteMsg(ret)
|
|
})
|
|
|
|
c := caddy.NewTestController("dns", fmt.Sprintf("forwardcrd %s", zone))
|
|
c.ServerBlockKeys = []string{"."}
|
|
k, err := parseForwardCRD(c)
|
|
if err != nil {
|
|
t.Errorf("Expected not to error: %s", err)
|
|
}
|
|
|
|
k.APIConn = &TestController{}
|
|
|
|
forwardCRDTest, err := forward.NewWithConfig(forward.ForwardConfig{
|
|
From: "crd.test",
|
|
To: []string{s.Addr},
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Expected not to error: %s", err)
|
|
}
|
|
|
|
forwardCRDTest.OnStartup()
|
|
|
|
forwardOtherTest, err := forward.NewWithConfig(forward.ForwardConfig{
|
|
From: "other.test.",
|
|
To: []string{s.Addr},
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Expected not to error: %s", err)
|
|
}
|
|
|
|
forwardOtherTest.OnStartup()
|
|
|
|
k.pluginInstanceMap.Upsert("default/crd-test", "crd.test", forwardCRDTest)
|
|
k.pluginInstanceMap.Upsert("default/other-test", "other.test", forwardOtherTest)
|
|
|
|
closeAll := func() {
|
|
s.Close()
|
|
forwardCRDTest.OnShutdown()
|
|
forwardOtherTest.OnShutdown()
|
|
}
|
|
return k, closeAll
|
|
}
|