diff options
author | 2021-11-12 13:19:16 -0500 | |
---|---|---|
committer | 2021-11-12 13:19:16 -0500 | |
commit | 3288b111b24fbe01094d5e380bbd2214427e00a4 (patch) | |
tree | 4cd98dfc2314f4a56926a5ab5b0627e1eb040e9f /plugin | |
parent | 2e6953c7dbd1d6b359911e1ce92e2567df07ca8c (diff) | |
download | coredns-3288b111b24fbe01094d5e380bbd2214427e00a4.tar.gz coredns-3288b111b24fbe01094d5e380bbd2214427e00a4.tar.zst coredns-3288b111b24fbe01094d5e380bbd2214427e00a4.zip |
Revert "Initial implementation of ForwardCRD plugin (#4512)" (#4981)
This reverts commit 2e6953c7dbd1d6b359911e1ce92e2567df07ca8c.
Diffstat (limited to 'plugin')
-rw-r--r-- | plugin/forward/forward.go | 118 | ||||
-rw-r--r-- | plugin/forward/forward_test.go | 284 | ||||
-rw-r--r-- | plugin/forward/setup.go | 116 | ||||
-rw-r--r-- | plugin/forwardcrd/Makefile | 25 | ||||
-rw-r--r-- | plugin/forwardcrd/README.md | 256 | ||||
-rw-r--r-- | plugin/forwardcrd/apis/coredns/v1alpha1/forward_types.go | 39 | ||||
-rw-r--r-- | plugin/forwardcrd/apis/coredns/v1alpha1/groupversion_info.go | 36 | ||||
-rw-r--r-- | plugin/forwardcrd/apis/coredns/v1alpha1/zz_generated.deepcopy.go | 103 | ||||
-rw-r--r-- | plugin/forwardcrd/controller.go | 238 | ||||
-rw-r--r-- | plugin/forwardcrd/controller_test.go | 333 | ||||
-rw-r--r-- | plugin/forwardcrd/fakes_test.go | 86 | ||||
-rw-r--r-- | plugin/forwardcrd/forwardcrd.go | 162 | ||||
-rw-r--r-- | plugin/forwardcrd/forwardcrd_test.go | 183 | ||||
-rw-r--r-- | plugin/forwardcrd/manifests/crds/coredns.io_forwards.yaml | 66 | ||||
-rw-r--r-- | plugin/forwardcrd/plugin_map.go | 83 | ||||
-rw-r--r-- | plugin/forwardcrd/plugin_map_test.go | 89 | ||||
-rw-r--r-- | plugin/forwardcrd/setup.go | 147 | ||||
-rw-r--r-- | plugin/forwardcrd/setup_test.go | 194 |
18 files changed, 91 insertions, 2467 deletions
diff --git a/plugin/forward/forward.go b/plugin/forward/forward.go index 707ad31e1..19a469c72 100644 --- a/plugin/forward/forward.go +++ b/plugin/forward/forward.go @@ -8,7 +8,6 @@ import ( "context" "crypto/tls" "errors" - "fmt" "sync/atomic" "time" @@ -17,8 +16,6 @@ import ( "github.com/coredns/coredns/plugin/dnstap" "github.com/coredns/coredns/plugin/metadata" clog "github.com/coredns/coredns/plugin/pkg/log" - "github.com/coredns/coredns/plugin/pkg/parse" - "github.com/coredns/coredns/plugin/pkg/transport" "github.com/coredns/coredns/request" "github.com/miekg/dns" @@ -57,127 +54,12 @@ type Forward struct { Next plugin.Handler } -// ForwardConfig represents the configuration of the Forward Plugin. This can -// be used with NewWithConfig to create a new configured instance of the -// Forward Plugin. -type ForwardConfig struct { - From string - To []string - Except []string - MaxFails *uint32 - HealthCheck *time.Duration - HealthCheckNoRec bool - ForceTCP bool - PreferUDP bool - TLSConfig *tls.Config - TLSServerName string - Expire *time.Duration - MaxConcurrent *int64 - Policy string - TapPlugin *dnstap.Dnstap -} - // New returns a new Forward. func New() *Forward { f := &Forward{maxfails: 2, tlsConfig: new(tls.Config), expire: defaultExpire, p: new(random), from: ".", hcInterval: hcInterval, opts: options{forceTCP: false, preferUDP: false, hcRecursionDesired: true}} return f } -// NewWithConfig returns a new Forward configured by the provided -// ForwardConfig. -func NewWithConfig(config ForwardConfig) (*Forward, error) { - f := New() - if config.From != "" { - zones := plugin.Host(config.From).NormalizeExact() - f.from = zones[0] // there can only be one here, won't work with non-octet reverse - - if len(zones) > 1 { - log.Warningf("Unsupported CIDR notation: '%s' expands to multiple zones. Using only '%s'.", config.From, f.from) - } - } - for i := 0; i < len(config.Except); i++ { - f.ignored = append(f.ignored, plugin.Host(config.Except[i]).NormalizeExact()...) - } - if config.MaxFails != nil { - f.maxfails = *config.MaxFails - } - if config.HealthCheck != nil { - if *config.HealthCheck < 0 { - return nil, fmt.Errorf("health_check can't be negative: %s", *config.HealthCheck) - } - f.hcInterval = *config.HealthCheck - } - f.opts.hcRecursionDesired = !config.HealthCheckNoRec - f.opts.forceTCP = config.ForceTCP - f.opts.preferUDP = config.PreferUDP - if config.TLSConfig != nil { - f.tlsConfig = config.TLSConfig - } - f.tlsServerName = config.TLSServerName - if f.tlsServerName != "" { - f.tlsConfig.ServerName = f.tlsServerName - } - if config.Expire != nil { - f.expire = *config.Expire - if *config.Expire < 0 { - return nil, fmt.Errorf("expire can't be negative: %s", *config.Expire) - } - } - if config.MaxConcurrent != nil { - if *config.MaxConcurrent < 0 { - return f, fmt.Errorf("max_concurrent can't be negative: %d", *config.MaxConcurrent) - } - f.ErrLimitExceeded = fmt.Errorf("concurrent queries exceeded maximum %d", *config.MaxConcurrent) - f.maxConcurrent = *config.MaxConcurrent - } - if config.Policy != "" { - switch config.Policy { - case "random": - f.p = &random{} - case "round_robin": - f.p = &roundRobin{} - case "sequential": - f.p = &sequential{} - default: - return f, fmt.Errorf("unknown policy '%s'", config.Policy) - } - } - f.tapPlugin = config.TapPlugin - - toHosts, err := parse.HostPortOrFile(config.To...) - if err != nil { - return f, err - } - - transports := make([]string, len(toHosts)) - allowedTrans := map[string]bool{"dns": true, "tls": true} - for i, host := range toHosts { - trans, h := parse.Transport(host) - - if !allowedTrans[trans] { - return f, fmt.Errorf("'%s' is not supported as a destination protocol in forward: %s", trans, host) - } - p := NewProxy(h, trans) - f.proxies = append(f.proxies, p) - transports[i] = trans - } - - // Initialize ClientSessionCache in tls.Config. This may speed up a TLS handshake - // in upcoming connections to the same TLS server. - f.tlsConfig.ClientSessionCache = tls.NewLRUClientSessionCache(len(f.proxies)) - - for i := range f.proxies { - // Only set this for proxies that need it. - if transports[i] == transport.TLS { - f.proxies[i].SetTLSConfig(f.tlsConfig) - } - f.proxies[i].SetExpire(f.expire) - f.proxies[i].health.SetRecursionDesired(f.opts.hcRecursionDesired) - - } - return f, nil -} - // SetProxy appends p to the proxy list and starts healthchecking. func (f *Forward) SetProxy(p *Proxy) { f.proxies = append(f.proxies, p) diff --git a/plugin/forward/forward_test.go b/plugin/forward/forward_test.go index 1159e0a85..b0ef47ba9 100644 --- a/plugin/forward/forward_test.go +++ b/plugin/forward/forward_test.go @@ -1,13 +1,7 @@ package forward import ( - "crypto/tls" - "fmt" - "reflect" "testing" - "time" - - "github.com/coredns/coredns/plugin/dnstap" ) func TestList(t *testing.T) { @@ -28,281 +22,3 @@ func TestList(t *testing.T) { } } } - -func TestNewWithConfig(t *testing.T) { - expectedExcept := []string{"foo.com.", "example.com."} - expectedMaxFails := uint32(5) - expectedHealthCheck := 5 * time.Second - expectedServerName := "test" - expectedExpire := 20 * time.Second - expectedMaxConcurrent := int64(5) - expectedDnstap := dnstap.Dnstap{} - - f, err := NewWithConfig(ForwardConfig{ - From: "test", - To: []string{"1.2.3.4:3053", "tls://4.5.6.7"}, - Except: []string{"FOO.com", "example.com"}, - MaxFails: &expectedMaxFails, - HealthCheck: &expectedHealthCheck, - HealthCheckNoRec: true, - ForceTCP: true, - PreferUDP: true, - TLSConfig: &tls.Config{NextProtos: []string{"some-proto"}}, - TLSServerName: expectedServerName, - Expire: &expectedExpire, - MaxConcurrent: &expectedMaxConcurrent, - TapPlugin: &expectedDnstap, - }) - if err != nil { - t.Fatalf("Expected not to error: %s", err) - } - - if f.from != "test." { - t.Fatalf("Expected from to be %s, got: %s", "test.", f.from) - } - - if len(f.proxies) != 2 { - t.Fatalf("Expected proxies to have len of %d, got: %d", 2, len(f.proxies)) - } - - if f.proxies[0].addr != "1.2.3.4:3053" { - t.Fatalf("Expected proxy to have addr of %s, got: %s", "1.2.3.4:3053", f.proxies[0].addr) - } - - if f.proxies[1].addr != "4.5.6.7:853" { - t.Fatalf("Expected proxy to have addr of %s, got: %s", "4.5.6.7:853", f.proxies[1].addr) - } - - if !reflect.DeepEqual(f.ignored, expectedExcept) { - t.Fatalf("Expected ignored to consist of %#v, got: %#v", expectedExcept, f.ignored) - } - - if f.maxfails != 5 { - t.Fatalf("Expected maxfails to be %d, got: %d", expectedMaxFails, f.maxfails) - } - - if f.hcInterval != 5*time.Second { - t.Fatalf("Expected hcInterval to be %s, got: %s", expectedHealthCheck, f.hcInterval) - } - - if f.opts.hcRecursionDesired { - t.Fatalf("Expected hcRecursionDesired to be false") - } - - if !f.opts.forceTCP { - t.Fatalf("Expected forceTCP to be true") - } - - if !f.opts.preferUDP { - t.Fatalf("Expected preferUDP to be true") - } - - if len(f.tlsConfig.NextProtos) != 1 || f.tlsConfig.NextProtos[0] != "some-proto" { - t.Fatalf("Expected tlsConfig to have NextProtos to consist of %s, got: %s", "some-proto", f.tlsConfig.NextProtos) - } - - if f.tlsConfig.ServerName != expectedServerName { - t.Fatalf("Expected tlsConfig to have ServerName to be %s, got: %s", expectedServerName, f.tlsConfig.ServerName) - } - - if f.tlsServerName != "test" { - t.Fatalf("Expected tlsSeverName to be %s, got: %s", expectedServerName, f.tlsServerName) - } - - if f.expire != 20*time.Second { - t.Fatalf("Expected expire to be %s, got: %s", expectedExpire, f.expire) - } - - if f.ErrLimitExceeded == nil || f.ErrLimitExceeded.Error() != "concurrent queries exceeded maximum 5" { - t.Fatalf("Expected ErrLimitExceeded to be %s, got: %s", "concurrent queries exceeded maximum 5", f.ErrLimitExceeded) - } - - if f.maxConcurrent != 5 { - t.Fatalf("Expected maxConcurrent to be %d, got: %d", 5, f.maxConcurrent) - } - - if fmt.Sprintf("%T", f.tlsConfig.ClientSessionCache) != "*tls.lruSessionCache" { - t.Fatalf("Expected tlsConfig.ClientSessionCache to be type %s, got: %T", "*tls.lruSessionCache", f.tlsConfig.ClientSessionCache) - } - - if f.proxies[0].transport.expire != f.expire { - t.Fatalf("Expected proxy.transport.expire to be %s, got: %s", f.expire, f.proxies[0].transport.expire) - } - - if f.proxies[1].transport.expire != f.expire { - t.Fatalf("Expected proxy.transport.expire to be %s, got: %s", f.expire, f.proxies[1].transport.expire) - } - - if f.proxies[0].health.GetRecursionDesired() != f.opts.hcRecursionDesired { - t.Fatalf("Expected proxy.health.GetRecursionDesired to be %t, got: %t", f.opts.hcRecursionDesired, f.proxies[0].health.GetRecursionDesired()) - } - - if f.proxies[1].health.GetRecursionDesired() != f.opts.hcRecursionDesired { - t.Fatalf("Expected proxy.health.GetRecursionDesired to be %t, got: %t", f.opts.hcRecursionDesired, f.proxies[1].health.GetRecursionDesired()) - } - - if f.proxies[0].transport.tlsConfig == f.tlsConfig { - t.Fatalf("Expected proxy.transport.tlsConfig to be nil, got: %#v", f.proxies[0].transport.tlsConfig) - } - - if f.proxies[1].transport.tlsConfig != f.tlsConfig { - t.Fatalf("Expected proxy.transport.tlsConfig to be %#v, got: %#v", f.tlsConfig, f.proxies[1].transport.tlsConfig) - } - - if f.tapPlugin != &expectedDnstap { - t.Fatalf("Expcted tapPlugin to be %p, got: %p", &expectedDnstap, f.tapPlugin) - } -} - -func TestNewWithConfigNegativeHealthCheck(t *testing.T) { - healthCheck, _ := time.ParseDuration("-5s") - - _, err := NewWithConfig(ForwardConfig{ - To: []string{"1.2.3.4:3053", "4.5.6.7"}, - HealthCheck: &healthCheck, - }) - if err == nil || err.Error() != "health_check can't be negative: -5s" { - t.Fatalf("Expected error to be %s, got: %s", "health_check can't be negative: -5s", err) - } -} - -func TestNewWithConfigNegativeExpire(t *testing.T) { - expire, _ := time.ParseDuration("-5s") - - _, err := NewWithConfig(ForwardConfig{ - To: []string{"1.2.3.4:3053", "4.5.6.7"}, - Expire: &expire, - }) - if err == nil || err.Error() != "expire can't be negative: -5s" { - t.Fatalf("Expected error to be %s, got: %s", "expire can't be negative: -5s", err) - } -} - -func TestNewWithConfigNegativeMaxConcurrent(t *testing.T) { - maxConcurrent := int64(-5) - - _, err := NewWithConfig(ForwardConfig{ - To: []string{"1.2.3.4:3053", "4.5.6.7"}, - MaxConcurrent: &maxConcurrent, - }) - if err == nil || err.Error() != "max_concurrent can't be negative: -5" { - t.Fatalf("Expected error to be %s, got: %s", "max_concurrent can't be negative: -5", err) - } -} - -func TestNewWithConfigPolicy(t *testing.T) { - config := ForwardConfig{ - To: []string{"1.2.3.4:3053", "4.5.6.7"}, - } - - config.Policy = "random" - f, err := NewWithConfig(config) - if err != nil { - t.Fatalf("Expected not to error: %s", err) - } - - if _, ok := f.p.(*random); !ok { - t.Fatalf("Expect p to be of type %s, got: %T", "random", f.p) - } - - config.Policy = "round_robin" - f, err = NewWithConfig(config) - if err != nil { - t.Fatalf("Expected not to error: %s", err) - } - - if _, ok := f.p.(*roundRobin); !ok { - t.Fatalf("Expect p to be of type %s, got: %T", "roundRobin", f.p) - } - - config.Policy = "sequential" - f, err = NewWithConfig(config) - if err != nil { - t.Fatalf("Expected not to error: %s", err) - } - - if _, ok := f.p.(*sequential); !ok { - t.Fatalf("Expect p to be of type %s, got: %T", "sequential", f.p) - } - - config.Policy = "invalid_policy" - _, err = NewWithConfig(config) - if err == nil { - t.Fatalf("Expected error %s, got: %s", "unknown policy 'invalid_policy'", err) - } -} - -func TestNewWithConfigServerNameDefault(t *testing.T) { - f, err := NewWithConfig(ForwardConfig{ - To: []string{"1.2.3.4"}, - TLSConfig: &tls.Config{ServerName: "some-server-name"}, - }) - if err != nil { - t.Fatalf("Expected not to error: %s", err) - } - - if f.tlsConfig.ServerName != "some-server-name" { - t.Fatalf("Expect tlsConfig.ServerName to be %s, got: %s", "some-server-name", f.tlsConfig.ServerName) - } -} - -func TestNewWithConfigWithDefaults(t *testing.T) { - f, err := NewWithConfig(ForwardConfig{ - To: []string{"1.2.3.4"}, - }) - if err != nil { - t.Fatalf("Expected not to error: %s", err) - } - - if f.from != "." { - t.Fatalf("Expected from to be %s, got: %s", ".", f.from) - } - - if f.ignored != nil { - t.Fatalf("Expected ignored to be nil but was %#v", f.ignored) - } - - if f.maxfails != 2 { - t.Fatalf("Expected maxfails to be %d, got: %d", 2, f.maxfails) - } - - if f.hcInterval != 500*time.Millisecond { - t.Fatalf("Expected hcInterval to be %s, got: %s", 500*time.Millisecond, f.hcInterval) - } - - if !f.opts.hcRecursionDesired { - t.Fatalf("Expected hcRecursionDesired to be true") - } - - if f.opts.forceTCP { - t.Fatalf("Expected forceTCP to be false") - } - - if f.opts.preferUDP { - t.Fatalf("Expected preferUDP to be false") - } - - if f.tlsConfig == nil { - t.Fatalf("Expected tlsConfig to be non nil") - } - - if f.tlsServerName != "" { - t.Fatalf("Expected tlsServerName to be empty") - } - - if f.expire != defaultExpire { - t.Fatalf("Expected expire to be %s, got: %s", defaultExpire, f.expire) - } - - if f.ErrLimitExceeded != nil { - t.Fatalf("Expected ErrLimitExceeded to be nil") - } - - if f.maxConcurrent != 0 { - t.Fatalf("Expected maxConcurrent to be %d, got: %d", 0, f.maxConcurrent) - } - - if _, ok := f.p.(*random); !ok { - t.Fatalf("Expect p to be of type %s, got: %T", "random", f.p) - } -} diff --git a/plugin/forward/setup.go b/plugin/forward/setup.go index baf80f12b..1f88daba5 100644 --- a/plugin/forward/setup.go +++ b/plugin/forward/setup.go @@ -1,6 +1,8 @@ package forward import ( + "crypto/tls" + "errors" "fmt" "strconv" "time" @@ -9,7 +11,9 @@ import ( "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/dnstap" + "github.com/coredns/coredns/plugin/pkg/parse" pkgtls "github.com/coredns/coredns/plugin/pkg/tls" + "github.com/coredns/coredns/plugin/pkg/transport" ) func init() { plugin.Register("forward", setup) } @@ -83,46 +87,90 @@ func parseForward(c *caddy.Controller) (*Forward, error) { } func parseStanza(c *caddy.Controller) (*Forward, error) { - cfg := ForwardConfig{} + f := New() - if !c.Args(&cfg.From) { - return nil, c.ArgErr() + if !c.Args(&f.from) { + return f, c.ArgErr() } + origFrom := f.from + zones := plugin.Host(f.from).NormalizeExact() + f.from = zones[0] // there can only be one here, won't work with non-octet reverse - cfg.To = c.RemainingArgs() - if len(cfg.To) == 0 { - return nil, c.ArgErr() + if len(zones) > 1 { + log.Warningf("Unsupported CIDR notation: '%s' expands to multiple zones. Using only '%s'.", origFrom, f.from) + } + + to := c.RemainingArgs() + if len(to) == 0 { + return f, c.ArgErr() + } + + toHosts, err := parse.HostPortOrFile(to...) + if err != nil { + return f, err + } + + transports := make([]string, len(toHosts)) + allowedTrans := map[string]bool{"dns": true, "tls": true} + for i, host := range toHosts { + trans, h := parse.Transport(host) + + if !allowedTrans[trans] { + return f, fmt.Errorf("'%s' is not supported as a destination protocol in forward: %s", trans, host) + } + p := NewProxy(h, trans) + f.proxies = append(f.proxies, p) + transports[i] = trans } for c.NextBlock() { - if err := parseBlock(c, &cfg); err != nil { - return nil, err + if err := parseBlock(c, f); err != nil { + return f, err + } + } + + if f.tlsServerName != "" { + f.tlsConfig.ServerName = f.tlsServerName + } + + // Initialize ClientSessionCache in tls.Config. This may speed up a TLS handshake + // in upcoming connections to the same TLS server. + f.tlsConfig.ClientSessionCache = tls.NewLRUClientSessionCache(len(f.proxies)) + + for i := range f.proxies { + // Only set this for proxies that need it. + if transports[i] == transport.TLS { + f.proxies[i].SetTLSConfig(f.tlsConfig) } + f.proxies[i].SetExpire(f.expire) + f.proxies[i].health.SetRecursionDesired(f.opts.hcRecursionDesired) } - return NewWithConfig(cfg) + return f, nil } -func parseBlock(c *caddy.Controller, cfg *ForwardConfig) error { +func parseBlock(c *caddy.Controller, f *Forward) error { switch c.Val() { case "except": - cfg.Except = c.RemainingArgs() - if len(cfg.Except) == 0 { + ignore := c.RemainingArgs() + if len(ignore) == 0 { return c.ArgErr() } + for i := 0; i < len(ignore); i++ { + f.ignored = append(f.ignored, plugin.Host(ignore[i]).NormalizeExact()...) + } case "max_fails": if !c.NextArg() { return c.ArgErr() } - n, err := strconv.ParseInt(c.Val(), 10, 32) + n, err := strconv.Atoi(c.Val()) if err != nil { return err } if n < 0 { return fmt.Errorf("max_fails can't be negative: %d", n) } - maxFails := uint32(n) - cfg.MaxFails = &maxFails + f.maxfails = uint32(n) case "health_check": if !c.NextArg() { return c.ArgErr() @@ -131,12 +179,15 @@ func parseBlock(c *caddy.Controller, cfg *ForwardConfig) error { if err != nil { return err } - cfg.HealthCheck = &dur + if dur < 0 { + return fmt.Errorf("health_check can't be negative: %d", dur) + } + f.hcInterval = dur for c.NextArg() { switch hcOpts := c.Val(); hcOpts { case "no_rec": - cfg.HealthCheckNoRec = true + f.opts.hcRecursionDesired = false default: return fmt.Errorf("health_check: unknown option %s", hcOpts) } @@ -146,12 +197,12 @@ func parseBlock(c *caddy.Controller, cfg *ForwardConfig) error { if c.NextArg() { return c.ArgErr() } - cfg.ForceTCP = true + f.opts.forceTCP = true case "prefer_udp": if c.NextArg() { return c.ArgErr() } - cfg.PreferUDP = true + f.opts.preferUDP = true case "tls": args := c.RemainingArgs() if len(args) > 3 { @@ -162,12 +213,12 @@ func parseBlock(c *caddy.Controller, cfg *ForwardConfig) error { if err != nil { return err } - cfg.TLSConfig = tlsConfig + f.tlsConfig = tlsConfig case "tls_servername": if !c.NextArg() { return c.ArgErr() } - cfg.TLSServerName = c.Val() + f.tlsServerName = c.Val() case "expire": if !c.NextArg() { return c.ArgErr() @@ -176,12 +227,24 @@ func parseBlock(c *caddy.Controller, cfg *ForwardConfig) error { if err != nil { return err } - cfg.Expire = &dur + if dur < 0 { + return fmt.Errorf("expire can't be negative: %s", dur) + } + f.expire = dur case "policy": if !c.NextArg() { return c.ArgErr() } - cfg.Policy = c.Val() + switch x := c.Val(); x { + case "random": + f.p = &random{} + case "round_robin": + f.p = &roundRobin{} + case "sequential": + f.p = &sequential{} + default: + return c.Errf("unknown policy '%s'", x) + } case "max_concurrent": if !c.NextArg() { return c.ArgErr() @@ -190,8 +253,11 @@ func parseBlock(c *caddy.Controller, cfg *ForwardConfig) error { if err != nil { return err } - maxConcurrent := int64(n) - cfg.MaxConcurrent = &maxConcurrent + if n < 0 { + return fmt.Errorf("max_concurrent can't be negative: %d", n) + } + f.ErrLimitExceeded = errors.New("concurrent queries exceeded maximum " + c.Val()) + f.maxConcurrent = int64(n) default: return c.Errf("unknown property '%s'", c.Val()) diff --git a/plugin/forwardcrd/Makefile b/plugin/forwardcrd/Makefile deleted file mode 100644 index b777e11c6..000000000 --- a/plugin/forwardcrd/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -.PHONY: generate -generate: controller-gen - $(CONTROLLER_GEN) \ - object \ - paths="./apis/..." - $(CONTROLLER_GEN) \ - crd \ - paths="./apis/..." \ - output:crd:artifacts:config=manifests/crds/ - -.PHONY: controller-gen -controller-gen: -ifeq (, $(shell which controller-gen)) - @{ \ - set -e ;\ - CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\ - cd $$CONTROLLER_GEN_TMP_DIR ;\ - go mod init tmp ;\ - go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1 ;\ - rm -rf $$CONTROLLER_GEN_TMP_DIR ;\ - } -CONTROLLER_GEN=$(shell go env GOPATH)/bin/controller-gen -else -CONTROLLER_GEN=$(shell which controller-gen) -endif diff --git a/plugin/forwardcrd/README.md b/plugin/forwardcrd/README.md deleted file mode 100644 index c2073f28e..000000000 --- a/plugin/forwardcrd/README.md +++ /dev/null @@ -1,256 +0,0 @@ -# forwardcrd - -## Name - -*forwardcrd* - enables proxying DNS messages to upstream resolvers by reading -the `Forward` CRD from a Kubernetes cluster - -## Description - -The *forwardcrd* plugin is used to dynamically configure stub-domains by -reading a `Forward` CRD within a Kubernetes cluster. - -See [Configuring Private DNS Zones and Upstream Nameservers in -Kubernetes](https://kubernetes.io/blog/2017/04/configuring-private-dns-zones-upstream-nameservers-kubernetes/) -for a description of stub-domains within Kubernetes. - -This plugin can only be used once per Server Block. - -## Security - -This plugin gives users of Kubernetes another avenue of modifying the CoreDNS -server other than the `coredns` configmap. Therefore, it is important that you -limit the RBAC and the `namespace` the plugin reads from to reduce the surface -area a malicious actor can use. Ideally, the level of access to create `Forward` -resources is at the same level as the access to the `coredns` configmap. - -## Syntax - -~~~ -forwardcrd [ZONES...] -~~~ - -With only the plugin specified, the *forwardcrd* plugin will default to the -zone specified in the server's block. It will allow any `Forward` resource that -matches or includes the zone as a suffix. If **ZONES** are specified it allows -any zone listed as a suffix. - -``` -forwardcrd [ZONES...] { - endpoint URL - tls CERT KEY CACERT - kubeconfig KUBECONFIG [CONTEXT] - namespace [NAMESPACE] -} -``` - -* `endpoint` specifies the **URL** for a remote k8s API endpoint. If omitted, - it will connect to k8s in-cluster using the cluster service account. -* `tls` **CERT** **KEY** **CACERT** are the TLS cert, key and the CA cert file - names for remote k8s connection. This option is ignored if connecting - in-cluster (i.e. endpoint is not specified). -* `kubeconfig` **KUBECONFIG [CONTEXT]** authenticates the connection to a remote - k8s cluster using a kubeconfig file. **[CONTEXT]** is optional, if not set, - then the current context specified in kubeconfig will be used. It supports - TLS, username and password, or token-based authentication. This option is - ignored if connecting in-cluster (i.e., the endpoint is not specified). -* `namespace` **[NAMESPACE]** only reads `Forward` resources from the namespace - listed. If this option is omitted then it will read from the default - namespace, `kube-system`. If this option is specified without any namespaces - listed it will read from all namespaces. **Note**: It is recommended to limit - the namespace (e.g to `kube-system`) because this can be potentially misused. - It is ideal to keep the level of write access similar to the `coredns` - configmap in the `kube-system` namespace. - -## Ready - -This plugin reports readiness to the ready plugin. This will happen after it has -synced to the Kubernetes API. - -## Ordering - -Forward behavior can be defined in three ways, via a Server Block, via the -*forwardcrd* plugin, and via the *forward* plugin. If more than one of these -methods is employed and a query falls within the zone of more than one, CoreDNS -selects which one to use based on the following precedence: -Corefile Server Block -> *forwardcrd* plugin -> *forward* plugin. - -When `Forward` CRDs and Server Blocks define stub domains that are used, -domains defined in the Corefile take precedence (in the event of zone overlap). -e.g. if the -domain `example.com` is defined in the Corefile as a stub domain, and a -`Forward` CRD record defined for `sub.example.com`, then `sub.example.com` would -get forwarded to the upstream defined in the Corefile, not the `Forward` CRD. - -When using *forwardcrd* and *forward* plugins in the same Server Block, `Forward` CRDs -take precedence over the *forward* plugin defined in the same Server Block. -e.g. if a `Forward` CRD is defined for `.`, then no queries would be -forwarded to the upstream defined in the *forward* plugin of the same Server Block. - -## Metrics - -`Forward` CRD metrics are all labeled in a single zone (the zone of the enclosing -Server Block). - -## Forward CRD - -The `Forward` CRD has the following spec properties: - -* **from** is the base domain to match for the request to be forwarded. -* **to** are the destination endpoints to forward to. The **to** syntax allows - you to specify a protocol, `tls://9.9.9.9` or `dns://` (or no protocol) for - plain DNS. The number of upstreams is limited to 15. - -## Examples - -The following is an example of how you might modify the `coredns` ConfigMap of -your cluster to enable the *forwardcrd* plugin. The following configuration will -watch and read any `Forward` CRD records in the `kube-system` namespace for -*any* zone name. This means you are able to able to create a `Forward` CRD -record that overlaps an existing zone. - -```yaml ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: coredns - namespace: kube-system -data: - Corefile: | - .:53 { - errors - health { - lameduck 5s - } - ready - kubernetes cluster.local in-addr.arpa ip6.arpa { - pods insecure - fallthrough in-addr.arpa ip6.arpa - ttl 30 - } - forwardcrd - prometheus :9153 - forward . /etc/resolv.conf - cache 30 - loop - reload - loadbalance - } -``` - -When you want to enable the *forwardcrd* plugin, you will need to apply the CRD -as well. - -``` -kubectl apply -f ./manifests/crds/coredns.io_forwards.yaml -``` - -Also note that the `ClusterRole` for CoreDNS must include: -In addition, you will need to modify the `system:coredns` ClusterRole in the -`kube-system` namespace to include the following: - -```yaml -rules: -- apiGroups: - - coredns.io - resources: - - forwards - verbs: - - list - - watch -``` - -This will allow CoreDNS to watch and list `Forward` CRD records from the -Kubernetes API. - -Now you can configure stubdomains by creating `Forward` CRD records in the -`kube-system` namespace. - -For example, if a cluster operator has a [Consul](https://www.consul.io/) domain -server located at 10.150.0.1, and all Consul names have the suffix -.consul.local. To configure this, the cluster administrator creates the -following record: - -```yaml ---- -apiVersion: coredns.io/v1alpha1 -kind: Forward -metadata: - name: consul-local - namespace: kube-system -spec: - from: consul.local - to: - - 10.150.0.1 -``` - -### Additional examples - -Allow `Forward` resources to be created for any zone and only read `Forward` -resources from the `kube-system` namespace: - -~~~ txt -. { - forwardcrd -} -~~~ - -Allow `Forward` resources to be created for the `.local` zone and only read -`Forward` resources from the `kube-system` namespace: - - -~~~ txt -. { - forwardcrd local -} -~~~ - -or: - -~~~ txt -local { - forwardcrd -} -~~~ - -Only read `Forward` resources from the `dns-system` namespace: - -~~~ txt -. { - forwardcrd { - namespace dns-system - } -} -~~~ - -Read `Forward` resources from all namespaces: - -~~~ txt -. { - forwardcrd { - namespace - } -} -~~~ - -Connect to Kubernetes with CoreDNS running outside the cluster: - -~~~ txt -. { - forwardcrd { - endpoint https://k8s-endpoint:8443 - tls cert key cacert - } -} -~~~ - -or: - -~~~ txt -. { - forwardcrd { - kubeconfig ./kubeconfig - } -} -~~~ diff --git a/plugin/forwardcrd/apis/coredns/v1alpha1/forward_types.go b/plugin/forwardcrd/apis/coredns/v1alpha1/forward_types.go deleted file mode 100644 index dabdbe70d..000000000 --- a/plugin/forwardcrd/apis/coredns/v1alpha1/forward_types.go +++ /dev/null @@ -1,39 +0,0 @@ -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// ForwardSpec represents the spec of a Forward -type ForwardSpec struct { - From string `json:"from,omitempty"` - To []string `json:"to,omitempty"` -} - -// ForwardStatus represents the status of a Forward -type ForwardStatus struct { -} - -// +kubebuilder:object:root=true -// +kubebuilder:printcolumn:name="From",type=string,JSONPath=`.spec.from` -// +kubebuilder:printcolumn:name="To",type=string,JSONPath=`.spec.to` - -// Forward represents a zone that should have its DNS requests forwarded to an -// upstream DNS server within CoreDNS -type Forward struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec ForwardSpec `json:"spec,omitempty"` - Status ForwardStatus `json:"status,omitempty"` -} - -// +kubebuilder:object:root=true - -// ForwardList represents a list of Forwards -type ForwardList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - - Items []Forward `json:"items"` -} diff --git a/plugin/forwardcrd/apis/coredns/v1alpha1/groupversion_info.go b/plugin/forwardcrd/apis/coredns/v1alpha1/groupversion_info.go deleted file mode 100644 index 2432d0300..000000000 --- a/plugin/forwardcrd/apis/coredns/v1alpha1/groupversion_info.go +++ /dev/null @@ -1,36 +0,0 @@ -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -// +kubebuilder:object:generate=true -// +groupName=coredns.io - -var ( - // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "coredns.io", Version: "v1alpha1"} - - // SchemeBuilder is used to add go types to the GroupVersionKind scheme - SchemeBuilder = &runtime.SchemeBuilder{} - - // AddToScheme adds the types in this group-version to the given scheme. - AddToScheme = SchemeBuilder.AddToScheme -) - -func init() { - SchemeBuilder.Register(addKnownTypes) -} - -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(GroupVersion, - &Forward{}, - &ForwardList{}, - ) - - metav1.AddToGroupVersion(scheme, GroupVersion) - - return nil -} diff --git a/plugin/forwardcrd/apis/coredns/v1alpha1/zz_generated.deepcopy.go b/plugin/forwardcrd/apis/coredns/v1alpha1/zz_generated.deepcopy.go deleted file mode 100644 index 914148871..000000000 --- a/plugin/forwardcrd/apis/coredns/v1alpha1/zz_generated.deepcopy.go +++ /dev/null @@ -1,103 +0,0 @@ -// +build !ignore_autogenerated - -// Code generated by controller-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Forward) DeepCopyInto(out *Forward) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Forward. -func (in *Forward) DeepCopy() *Forward { - if in == nil { - return nil - } - out := new(Forward) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Forward) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ForwardList) DeepCopyInto(out *ForwardList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Forward, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ForwardList. -func (in *ForwardList) DeepCopy() *ForwardList { - if in == nil { - return nil - } - out := new(ForwardList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ForwardList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ForwardSpec) DeepCopyInto(out *ForwardSpec) { - *out = *in - if in.To != nil { - in, out := &in.To, &out.To - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ForwardSpec. -func (in *ForwardSpec) DeepCopy() *ForwardSpec { - if in == nil { - return nil - } - out := new(ForwardSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ForwardStatus) DeepCopyInto(out *ForwardStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ForwardStatus. -func (in *ForwardStatus) DeepCopy() *ForwardStatus { - if in == nil { - return nil - } - out := new(ForwardStatus) - in.DeepCopyInto(out) - return out -} diff --git a/plugin/forwardcrd/controller.go b/plugin/forwardcrd/controller.go deleted file mode 100644 index b35e77b62..000000000 --- a/plugin/forwardcrd/controller.go +++ /dev/null @@ -1,238 +0,0 @@ -package forwardcrd - -import ( - "context" - "errors" - "fmt" - "sync" - "time" - - "github.com/coredns/coredns/plugin" - "github.com/coredns/coredns/plugin/dnstap" - "github.com/coredns/coredns/plugin/forward" - corednsv1alpha1 "github.com/coredns/coredns/plugin/forwardcrd/apis/coredns/v1alpha1" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/util/workqueue" -) - -const defaultResyncPeriod = 0 - -type forwardCRDController interface { - Run(threads int) - HasSynced() bool - Stop() error -} - -type forwardCRDControl struct { - client dynamic.Interface - scheme *runtime.Scheme - forwardController cache.Controller - forwardLister cache.Store - workqueue workqueue.RateLimitingInterface - pluginMap *PluginInstanceMap - instancer pluginInstancer - tapPlugin *dnstap.Dnstap - namespace string - - // stopLock is used to enforce only a single call to Stop is active. - // Needed because we allow stopping through an http endpoint and - // allowing concurrent stoppers leads to stack traces. - stopLock sync.Mutex - shutdown bool - stopCh chan struct{} -} - -type lifecyclePluginHandler interface { - plugin.Handler - OnStartup() error - OnShutdown() error -} - -type pluginInstancer func(forward.ForwardConfig) (lifecyclePluginHandler, error) - -func newForwardCRDController(ctx context.Context, client dynamic.Interface, scheme *runtime.Scheme, namespace string, pluginMap *PluginInstanceMap, instancer pluginInstancer) forwardCRDController { - controller := forwardCRDControl{ - client: client, - scheme: scheme, - stopCh: make(chan struct{}), - namespace: namespace, - pluginMap: pluginMap, - instancer: instancer, - workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "ForwardCRD"), - } - - controller.forwardLister, controller.forwardController = cache.NewInformer( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - if namespace != "" { - return controller.client.Resource(corednsv1alpha1.GroupVersion.WithResource("forwards")).Namespace(namespace).List(ctx, options) - } - return controller.client.Resource(corednsv1alpha1.GroupVersion.WithResource("forwards")).List(ctx, options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - if namespace != "" { - return controller.client.Resource(corednsv1alpha1.GroupVersion.WithResource("forwards")).Namespace(namespace).Watch(ctx, options) - } - return controller.client.Resource(corednsv1alpha1.GroupVersion.WithResource("forwards")).Watch(ctx, options) - }, - }, - &unstructured.Unstructured{}, - defaultResyncPeriod, - cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - key, err := cache.MetaNamespaceKeyFunc(obj) - if err == nil { - controller.workqueue.Add(key) - } - }, - UpdateFunc: func(oldObj, newObj interface{}) { - key, err := cache.MetaNamespaceKeyFunc(newObj) - if err == nil { - controller.workqueue.Add(key) - } - }, - DeleteFunc: func(obj interface{}) { - key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) - if err == nil { - controller.workqueue.Add(key) - } - }, - }, - ) - - return &controller -} - -// Run starts the controller. Threads is the number of workers that can process -// work on the workqueue in parallel. -func (d *forwardCRDControl) Run(threads int) { - defer utilruntime.HandleCrash() - defer d.workqueue.ShutDown() - - go d.forwardController.Run(d.stopCh) - - if !cache.WaitForCacheSync(d.stopCh, d.forwardController.HasSynced) { - utilruntime.HandleError(errors.New("Timed out waiting for caches to sync")) - return - } - - for i := 0; i < threads; i++ { - go wait.Until(d.runWorker, time.Second, d.stopCh) - } - - <-d.stopCh - - // Shutdown all plugins - for _, plugin := range d.pluginMap.List() { - plugin.OnShutdown() - } -} - -// HasSynced returns true once the controller has completed an initial resource -// listing. -func (d *forwardCRDControl) HasSynced() bool { - return d.forwardController.HasSynced() -} - -// Stop stops the controller. -func (d *forwardCRDControl) Stop() error { - d.stopLock.Lock() - defer d.stopLock.Unlock() - - // Only try draining the workqueue if we haven't already. - if !d.shutdown { - close(d.stopCh) - d.shutdown = true - - return nil - } - - return fmt.Errorf("shutdown already in progress") -} - -func (d *forwardCRDControl) runWorker() { - for d.processNextItem() { - } -} - -func (d *forwardCRDControl) processNextItem() bool { - key, quit := d.workqueue.Get() - if quit { - return false - } - - defer d.workqueue.Done(key) - - err := d.sync(key.(string)) - if err != nil { - log.Errorf("Error syncing Forward %v: %v", key, err) - d.workqueue.AddRateLimited(key) - return true - } - - d.workqueue.Forget(key) - - return true -} - -func (d *forwardCRDControl) sync(key string) error { - obj, exists, err := d.forwardLister.GetByKey(key) - if err != nil { - return err - } - - if !exists { - plugin := d.pluginMap.Delete(key) - if plugin != nil { - plugin.OnShutdown() - } - } else { - f, err := d.convertToForward(obj.(runtime.Object)) - if err != nil { - return err - } - forwardConfig := forward.ForwardConfig{ - From: f.Spec.From, - To: f.Spec.To, - TapPlugin: d.tapPlugin, - } - plugin, err := d.instancer(forwardConfig) - if err != nil { - return err - } - err = plugin.OnStartup() - if err != nil { - return err - } - oldPlugin, updated := d.pluginMap.Upsert(key, f.Spec.From, plugin) - if updated { - oldPlugin.OnShutdown() - } - } - - return nil -} - -func (d *forwardCRDControl) convertToForward(obj runtime.Object) (*corednsv1alpha1.Forward, error) { - unstructured, ok := obj.(*unstructured.Unstructured) - if !ok { - return nil, fmt.Errorf("object was not Unstructured") - } - - switch unstructured.GetKind() { - case "Forward": - forward := &corednsv1alpha1.Forward{} - err := d.scheme.Convert(unstructured, forward, nil) - return forward, err - default: - return nil, fmt.Errorf("unsupported object type: %T", unstructured) - } -} diff --git a/plugin/forwardcrd/controller_test.go b/plugin/forwardcrd/controller_test.go deleted file mode 100644 index 8531eacc2..000000000 --- a/plugin/forwardcrd/controller_test.go +++ /dev/null @@ -1,333 +0,0 @@ -package forwardcrd - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/coredns/coredns/plugin/forward" - corednsv1alpha1 "github.com/coredns/coredns/plugin/forwardcrd/apis/coredns/v1alpha1" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/dynamic/fake" -) - -func TestCreateForward(t *testing.T) { - controller, client, testPluginInstancer, pluginInstanceMap := setupControllerTestcase(t, "") - forward := &corednsv1alpha1.Forward{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-dns-zone", - Namespace: "default", - }, - Spec: corednsv1alpha1.ForwardSpec{ - From: "crd.test", - To: []string{"127.0.0.2", "127.0.0.3"}, - }, - } - - _, err := client.Resource(corednsv1alpha1.GroupVersion.WithResource("forwards")). - Namespace("default"). - Create(context.Background(), mustForwardToUnstructured(forward), metav1.CreateOptions{}) - if err != nil { - t.Fatalf("Expected not to error: %s", err) - } - - err = wait.Poll(time.Second, time.Second*5, func() (bool, error) { - return testPluginInstancer.NewWithConfigCallCount() == 1, nil - }) - if err != nil { - t.Fatalf("Expected plugin instance to have been called: %s", err) - } - - handler := testPluginInstancer.NewWithConfigArgsForCall(0) - if handler.ReceivedConfig.From != "crd.test" { - t.Fatalf("Expected plugin to be created for zone: %s but was: %s", "crd.test", handler.ReceivedConfig.From) - } - - if len(handler.ReceivedConfig.To) != 2 { - t.Fatalf("Expected plugin to contain exactly 2 servers to forward to but contains: %#v", handler.ReceivedConfig.To) - } - - if handler.ReceivedConfig.To[0] != "127.0.0.2" { - t.Fatalf("Expected plugin to be created to forward to: %s but was: %s", "127.0.0.2", handler.ReceivedConfig.To[0]) - } - - if handler.ReceivedConfig.To[1] != "127.0.0.3" { - t.Fatalf("Expected plugin to be created to forward to: %s but was: %s", "127.0.0.3", handler.ReceivedConfig.To[1]) - } - - pluginHandler, ok := pluginInstanceMap.Get("crd.test") - if !ok { - t.Fatal("Expected plugin lookup to succeed") - } - - if pluginHandler != handler { - t.Fatalf("Exepcted plugin lookup to match what the instancer provided: %#v but was %#v", handler, pluginHandler) - } - - if testPluginInstancer.testPluginHandlers[0].OnStartupCallCount() != 1 { - t.Fatalf("Expected plugin OnStartup to have been called once, but got: %d", testPluginInstancer.testPluginHandlers[0].OnStartupCallCount()) - } - - if err := controller.Stop(); err != nil { - t.Fatalf("Expected no error: %s", err) - } - - err = wait.Poll(time.Second, time.Second*5, func() (bool, error) { - return testPluginInstancer.testPluginHandlers[0].OnShutdownCallCount() == 1, nil - }) - if err != nil { - t.Fatalf("Expected plugin OnShutdown to have been called once, but got: %d", testPluginInstancer.testPluginHandlers[0].OnShutdownCallCount()) - } -} - -func TestUpdateForward(t *testing.T) { - controller, client, testPluginInstancer, pluginInstanceMap := setupControllerTestcase(t, "") - forward := &corednsv1alpha1.Forward{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-dns-zone", - Namespace: "default", - }, - Spec: corednsv1alpha1.ForwardSpec{ - From: "crd.test", - To: []string{"127.0.0.2"}, - }, - } - - unstructuredForward, err := client.Resource(corednsv1alpha1.GroupVersion.WithResource("forwards")). - Namespace("default"). - Create(context.Background(), mustForwardToUnstructured(forward), metav1.CreateOptions{}) - if err != nil { - t.Fatalf("Expected not to error: %s", err) - } - - err = wait.Poll(time.Second, time.Second*5, func() (bool, error) { - return testPluginInstancer.NewWithConfigCallCount() == 1, nil - }) - if err != nil { - t.Fatalf("Expected plugin instance to have been called: %s", err) - } - - forward = mustUnstructuredToForward(unstructuredForward) - forward.Spec.From = "other.test" - forward.Spec.To = []string{"127.0.0.3"} - - _, err = client.Resource(corednsv1alpha1.GroupVersion.WithResource("forwards")). - Namespace("default"). - Update(context.Background(), mustForwardToUnstructured(forward), metav1.UpdateOptions{}) - if err != nil { - t.Fatalf("Expected not to error: %s", err) - } - - err = wait.Poll(time.Second, time.Second*5, func() (bool, error) { - return testPluginInstancer.NewWithConfigCallCount() == 2, nil - }) - if err != nil { - t.Fatalf("Expected plugin instance to have been called: %s", err) - } - - handler := testPluginInstancer.NewWithConfigArgsForCall(1) - if handler.ReceivedConfig.From != "other.test" { - t.Fatalf("Expected plugin to be created for zone: %s but was: %s", "other.test", handler.ReceivedConfig.From) - } - - if len(handler.ReceivedConfig.To) != 1 { - t.Fatalf("Expected plugin to contain exactly 1 server to forward to but contains: %#v", handler.ReceivedConfig.To) - } - - if handler.ReceivedConfig.To[0] != "127.0.0.3" { - t.Fatalf("Expected plugin to be created to forward to: %s but was: %s", "127.0.0.3", handler.ReceivedConfig.To[0]) - } - - pluginHandler, ok := pluginInstanceMap.Get("other.test") - if !ok { - t.Fatal("Expected plugin lookup to succeed") - } - - if pluginHandler != handler { - t.Fatalf("Exepcted plugin lookup to match what the instancer provided: %#v but was %#v", handler, pluginHandler) - } - - _, ok = pluginInstanceMap.Get("crd.test") - if ok { - t.Fatal("Expected lookup for crd.test to fail") - } - - if testPluginInstancer.testPluginHandlers[0].OnShutdownCallCount() != 1 { - t.Fatalf("Expected plugin OnShutdown to have been called once, but got: %d", testPluginInstancer.testPluginHandlers[0].OnShutdownCallCount()) - } - - if err := controller.Stop(); err != nil { - t.Fatalf("Expected no error: %s", err) - } -} - -func TestDeleteForward(t *testing.T) { - controller, client, testPluginInstancer, pluginInstanceMap := setupControllerTestcase(t, "") - forward := &corednsv1alpha1.Forward{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-dns-zone", - Namespace: "default", - }, - Spec: corednsv1alpha1.ForwardSpec{ - From: "crd.test", - To: []string{"127.0.0.2"}, - }, - } - - _, err := client.Resource(corednsv1alpha1.GroupVersion.WithResource("forwards")). - Namespace("default"). - Create(context.Background(), mustForwardToUnstructured(forward), metav1.CreateOptions{}) - if err != nil { - t.Fatalf("Expected not to error: %s", err) - } - - err = wait.Poll(time.Second, time.Second*5, func() (bool, error) { - return testPluginInstancer.NewWithConfigCallCount() == 1, nil - }) - if err != nil { - t.Fatalf("Expected plugin instance to have been called: %s", err) - } - - err = client.Resource(corednsv1alpha1.GroupVersion.WithResource("forwards")). - Namespace("default"). - Delete(context.Background(), "test-dns-zone", metav1.DeleteOptions{}) - if err != nil { - t.Fatalf("Expected not to error: %s", err) - } - - err = wait.Poll(time.Second, time.Second*5, func() (bool, error) { - _, ok := pluginInstanceMap.Get("crd.test") - return !ok, nil - }) - if err != nil { - t.Fatalf("Expected lookup for crd.test to fail: %s", err) - } - - if testPluginInstancer.testPluginHandlers[0].OnShutdownCallCount() != 1 { - t.Fatalf("Expected plugin OnShutdown to have been called once, but got: %d", testPluginInstancer.testPluginHandlers[0].OnShutdownCallCount()) - } - - if err := controller.Stop(); err != nil { - t.Fatalf("Expected no error: %s", err) - } -} - -func TestForwardLimitNamespace(t *testing.T) { - controller, client, testPluginInstancer, pluginInstanceMap := setupControllerTestcase(t, "kube-system") - forward := &corednsv1alpha1.Forward{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-dns-zone", - Namespace: "default", - }, - Spec: corednsv1alpha1.ForwardSpec{ - From: "crd.test", - To: []string{"127.0.0.2"}, - }, - } - - _, err := client.Resource(corednsv1alpha1.GroupVersion.WithResource("forwards")). - Namespace("default"). - Create(context.Background(), mustForwardToUnstructured(forward), metav1.CreateOptions{}) - if err != nil { - t.Fatalf("Expected not to error: %s", err) - } - - kubeSystemForward := &corednsv1alpha1.Forward{ - ObjectMeta: metav1.ObjectMeta{ - Name: "system-dns-zone", - Namespace: "kube-system", - }, - Spec: corednsv1alpha1.ForwardSpec{ - From: "system.test", - To: []string{"127.0.0.3"}, - }, - } - - _, err = client.Resource(corednsv1alpha1.GroupVersion.WithResource("forwards")). - Namespace("kube-system"). - Create(context.Background(), mustForwardToUnstructured(kubeSystemForward), metav1.CreateOptions{}) - if err != nil { - t.Fatalf("Expected not to error: %s", err) - } - - err = wait.Poll(time.Second, time.Second*5, func() (bool, error) { - return testPluginInstancer.NewWithConfigCallCount() == 1, nil - }) - if err != nil { - t.Fatalf("Expected plugin instance to have been called exactly once: %s, plugin instance call count: %d", err, testPluginInstancer.NewWithConfigCallCount()) - } - - handler := testPluginInstancer.NewWithConfigArgsForCall(0) - if handler.ReceivedConfig.From != "system.test" { - t.Fatalf("Expected plugin to be created for zone: %s but was: %s", "system.test", handler.ReceivedConfig.From) - } - - _, ok := pluginInstanceMap.Get("system.test") - if !ok { - t.Fatal("Expected plugin lookup to succeed") - } - - _, ok = pluginInstanceMap.Get("crd.test") - if ok { - t.Fatal("Expected plugin lookup to fail") - } - - if err := controller.Stop(); err != nil { - t.Fatalf("Expected no error: %s", err) - } -} - -func setupControllerTestcase(t *testing.T, namespace string) (forwardCRDController, *fake.FakeDynamicClient, *TestPluginInstancer, *PluginInstanceMap) { - scheme := runtime.NewScheme() - scheme.AddKnownTypes(corednsv1alpha1.GroupVersion, &corednsv1alpha1.Forward{}) - customListKinds := map[schema.GroupVersionResource]string{ - corednsv1alpha1.GroupVersion.WithResource("forwards"): "ForwardList", - } - client := fake.NewSimpleDynamicClientWithCustomListKinds(scheme, customListKinds) - pluginMap := NewPluginInstanceMap() - testPluginInstancer := &TestPluginInstancer{} - controller := newForwardCRDController(context.Background(), client, scheme, namespace, pluginMap, func(cfg forward.ForwardConfig) (lifecyclePluginHandler, error) { - return testPluginInstancer.NewWithConfig(cfg) - }) - - go controller.Run(1) - - err := wait.Poll(time.Second, time.Second*5, func() (bool, error) { - return controller.HasSynced(), nil - }) - if err != nil { - t.Fatalf("Expected controller to have synced: %s", err) - } - - return controller, client, testPluginInstancer, pluginMap -} - -func mustForwardToUnstructured(forward *corednsv1alpha1.Forward) *unstructured.Unstructured { - forward.TypeMeta = metav1.TypeMeta{ - Kind: "Forward", - APIVersion: "coredns.io/v1alpha1", - } - - obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(forward) - if err != nil { - panic(fmt.Sprintf("coding error: unable to convert to unstructured: %s", err)) - } - return &unstructured.Unstructured{ - Object: obj, - } -} - -func mustUnstructuredToForward(obj *unstructured.Unstructured) *corednsv1alpha1.Forward { - forward := &corednsv1alpha1.Forward{} - err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, forward) - if err != nil { - panic(fmt.Sprintf("coding error: unable to convert from unstructured: %s", err)) - } - return forward -} diff --git a/plugin/forwardcrd/fakes_test.go b/plugin/forwardcrd/fakes_test.go deleted file mode 100644 index 9b6cb0bcf..000000000 --- a/plugin/forwardcrd/fakes_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package forwardcrd - -import ( - "context" - "sync" - - "github.com/coredns/coredns/plugin/forward" - - "github.com/miekg/dns" -) - -type TestPluginHandler struct { - mutex sync.Mutex - ReceivedConfig forward.ForwardConfig - onStartupCallCount int - onShutdownCallCount int -} - -func (t *TestPluginHandler) ServeDNS(context.Context, dns.ResponseWriter, *dns.Msg) (int, error) { - return 0, nil -} - -func (t *TestPluginHandler) Name() string { return "" } - -func (t *TestPluginHandler) OnStartup() error { - t.mutex.Lock() - defer t.mutex.Unlock() - t.onStartupCallCount++ - return nil -} - -func (t *TestPluginHandler) OnShutdown() error { - t.mutex.Lock() - defer t.mutex.Unlock() - t.onShutdownCallCount++ - return nil -} - -func (t *TestPluginHandler) OnStartupCallCount() int { - t.mutex.Lock() - defer t.mutex.Unlock() - return t.onStartupCallCount -} - -func (t *TestPluginHandler) OnShutdownCallCount() int { - t.mutex.Lock() - defer t.mutex.Unlock() - return t.onShutdownCallCount -} - -type TestPluginInstancer struct { - mutex sync.Mutex - testPluginHandlers []*TestPluginHandler -} - -func (t *TestPluginInstancer) NewWithConfig(config forward.ForwardConfig) (lifecyclePluginHandler, error) { - t.mutex.Lock() - defer t.mutex.Unlock() - - testPluginHandler := &TestPluginHandler{ - ReceivedConfig: config, - } - t.testPluginHandlers = append(t.testPluginHandlers, testPluginHandler) - return testPluginHandler, nil -} - -func (t *TestPluginInstancer) NewWithConfigArgsForCall(index int) *TestPluginHandler { - t.mutex.Lock() - defer t.mutex.Unlock() - - return t.testPluginHandlers[index] -} - -func (t *TestPluginInstancer) NewWithConfigCallCount() int { - t.mutex.Lock() - defer t.mutex.Unlock() - - return len(t.testPluginHandlers) -} - -type TestController struct { -} - -func (t *TestController) Run(threads int) {} -func (t *TestController) HasSynced() bool { return true } -func (t *TestController) Stop() error { return nil } diff --git a/plugin/forwardcrd/forwardcrd.go b/plugin/forwardcrd/forwardcrd.go deleted file mode 100644 index 533152a33..000000000 --- a/plugin/forwardcrd/forwardcrd.go +++ /dev/null @@ -1,162 +0,0 @@ -package forwardcrd - -import ( - "context" - "fmt" - "strings" - - "github.com/coredns/coredns/plugin" - "github.com/coredns/coredns/plugin/forward" - corednsv1alpha1 "github.com/coredns/coredns/plugin/forwardcrd/apis/coredns/v1alpha1" - "github.com/coredns/coredns/request" - - "github.com/miekg/dns" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" - clientcmdapi "k8s.io/client-go/tools/clientcmd/api" -) - -// ForwardCRD represents a plugin instance that can watch Forward CRDs -// within a Kubernetes clusters to dynamically configure stub-domains to proxy -// requests to an upstream resolver. -type ForwardCRD struct { - Zones []string - APIServerEndpoint string - APIClientCert string - APIClientKey string - APICertAuth string - Namespace string - ClientConfig clientcmd.ClientConfig - APIConn forwardCRDController - Next plugin.Handler - - pluginInstanceMap *PluginInstanceMap -} - -// New returns a new ForwardCRD instance. -func New() *ForwardCRD { - return &ForwardCRD{ - Namespace: "kube-system", - - pluginInstanceMap: NewPluginInstanceMap(), - } -} - -// Name implements plugin.Handler. -func (k *ForwardCRD) Name() string { return "forwardcrd" } - -// ServeDNS implements plugin.Handler. -func (k *ForwardCRD) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { - question := strings.ToLower(r.Question[0].Name) - - state := request.Request{W: w, Req: r} - if !k.match(state) { - return plugin.NextOrFailure(k.Name(), k.Next, ctx, w, r) - } - - var ( - offset int - end bool - ) - - for { - p, ok := k.pluginInstanceMap.Get(question[offset:]) - if ok { - a, b := p.ServeDNS(ctx, w, r) - return a, b - } - - offset, end = dns.NextLabel(question, offset) - if end { - break - } - } - - return plugin.NextOrFailure(k.Name(), k.Next, ctx, w, r) -} - -// Ready implements the ready.Readiness interface -func (k *ForwardCRD) Ready() bool { - return k.APIConn.HasSynced() -} - -// InitKubeCache initializes a new Kubernetes cache. -func (k *ForwardCRD) InitKubeCache(ctx context.Context) error { - config, err := k.getClientConfig() - if err != nil { - return err - } - - dynamicKubeClient, err := dynamic.NewForConfig(config) - if err != nil { - return fmt.Errorf("failed to create forwardcrd controller: %q", err) - } - - scheme := runtime.NewScheme() - err = corednsv1alpha1.AddToScheme(scheme) - if err != nil { - return fmt.Errorf("failed to create forwardcrd controller: %q", err) - } - - k.APIConn = newForwardCRDController(ctx, dynamicKubeClient, scheme, k.Namespace, k.pluginInstanceMap, func(cfg forward.ForwardConfig) (lifecyclePluginHandler, error) { - return forward.NewWithConfig(cfg) - }) - - return nil -} - -func (k *ForwardCRD) getClientConfig() (*rest.Config, error) { - if k.ClientConfig != nil { - return k.ClientConfig.ClientConfig() - } - loadingRules := &clientcmd.ClientConfigLoadingRules{} - overrides := &clientcmd.ConfigOverrides{} - clusterinfo := clientcmdapi.Cluster{} - authinfo := clientcmdapi.AuthInfo{} - - // Connect to API from in cluster - if k.APIServerEndpoint == "" { - cc, err := rest.InClusterConfig() - if err != nil { - return nil, err - } - cc.ContentType = "application/vnd.kubernetes.protobuf" - return cc, err - } - - // Connect to API from out of cluster - clusterinfo.Server = k.APIServerEndpoint - - if len(k.APICertAuth) > 0 { - clusterinfo.CertificateAuthority = k.APICertAuth - } - if len(k.APIClientCert) > 0 { - authinfo.ClientCertificate = k.APIClientCert - } - if len(k.APIClientKey) > 0 { - authinfo.ClientKey = k.APIClientKey - } - - overrides.ClusterInfo = clusterinfo - overrides.AuthInfo = authinfo - clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides) - - cc, err := clientConfig.ClientConfig() - if err != nil { - return nil, err - } - cc.ContentType = "application/vnd.kubernetes.protobuf" - return cc, err -} - -func (k *ForwardCRD) match(state request.Request) bool { - for _, zone := range k.Zones { - if plugin.Name(zone).Matches(state.Name()) || dns.Name(state.Name()) == dns.Name(zone) { - return true - } - } - - return false -} diff --git a/plugin/forwardcrd/forwardcrd_test.go b/plugin/forwardcrd/forwardcrd_test.go deleted file mode 100644 index 852bc14ee..000000000 --- a/plugin/forwardcrd/forwardcrd_test.go +++ /dev/null @@ -1,183 +0,0 @@ -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 -} diff --git a/plugin/forwardcrd/manifests/crds/coredns.io_forwards.yaml b/plugin/forwardcrd/manifests/crds/coredns.io_forwards.yaml deleted file mode 100644 index af6d6ab62..000000000 --- a/plugin/forwardcrd/manifests/crds/coredns.io_forwards.yaml +++ /dev/null @@ -1,66 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.4.1 - creationTimestamp: null - name: forwards.coredns.io -spec: - group: coredns.io - names: - kind: Forward - listKind: ForwardList - plural: forwards - singular: forward - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.from - name: From - type: string - - jsonPath: .spec.to - name: To - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: Forward represents a zone that should have its DNS requests forwarded - to an upstream DNS server within CoreDNS - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: ForwardSpec represents the spec of a Forward - properties: - from: - type: string - to: - items: - type: string - type: array - type: object - status: - description: ForwardStatus represents the status of a Forward - type: object - type: object - served: true - storage: true - subresources: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/plugin/forwardcrd/plugin_map.go b/plugin/forwardcrd/plugin_map.go deleted file mode 100644 index 176e7c96b..000000000 --- a/plugin/forwardcrd/plugin_map.go +++ /dev/null @@ -1,83 +0,0 @@ -package forwardcrd - -import ( - "sync" - - "github.com/coredns/coredns/plugin" -) - -// PluginInstanceMap represents a map of zones to coredns plugin instances that -// is thread-safe. It enables the forwardcrd plugin to save the state of -// which plugin instances should be delegated to for a given zone. -type PluginInstanceMap struct { - mutex *sync.RWMutex - zonesToPlugins map[string]lifecyclePluginHandler - keyToZones map[string]string -} - -// NewPluginInstanceMap returns a new instance of PluginInstanceMap. -func NewPluginInstanceMap() *PluginInstanceMap { - return &PluginInstanceMap{ - mutex: &sync.RWMutex{}, - zonesToPlugins: make(map[string]lifecyclePluginHandler), - keyToZones: make(map[string]string), - } -} - -// Upsert adds or updates the map with a zone to plugin handler mapping. If the -// same key is provided it will overwrite the old zone for that key with the -// new zone. Returns the plugin instance and true if the upsert was an update -// operation and not a create operation. -func (p *PluginInstanceMap) Upsert(key, zone string, handler lifecyclePluginHandler) (lifecyclePluginHandler, bool) { - var isUpdate bool - var oldPlugin lifecyclePluginHandler - p.mutex.Lock() - normalizedZone := plugin.Host(zone).NormalizeExact()[0] // there can only be one here, won't work with non-octet reverse - oldZone, ok := p.keyToZones[key] - if ok { - oldPlugin = p.zonesToPlugins[oldZone] - isUpdate = true - delete(p.zonesToPlugins, oldZone) - } - - p.keyToZones[key] = normalizedZone - p.zonesToPlugins[normalizedZone] = handler - p.mutex.Unlock() - return oldPlugin, isUpdate -} - -// Get gets the plugin handler provided a zone name. It will return true if the -// plugin handler exists and false if it does not exist. -func (p *PluginInstanceMap) Get(zone string) (lifecyclePluginHandler, bool) { - p.mutex.RLock() - normalizedZone := plugin.Host(zone).NormalizeExact()[0] // there can only be one here, won't work with non-octet reverse - handler, ok := p.zonesToPlugins[normalizedZone] - p.mutex.RUnlock() - return handler, ok -} - -// List lists all the plugin instances in the map. -func (p *PluginInstanceMap) List() []lifecyclePluginHandler { - p.mutex.RLock() - plugins := make([]lifecyclePluginHandler, len(p.zonesToPlugins)) - var i int - for _, v := range p.zonesToPlugins { - plugins[i] = v - i++ - } - p.mutex.RUnlock() - return plugins -} - -// Delete deletes the zone and plugin handler from the map. Returns the plugin -// instance that was deleted, useful for shutting down. Returns nil if no -// plugin was found. -func (p *PluginInstanceMap) Delete(key string) lifecyclePluginHandler { - p.mutex.Lock() - zone := p.keyToZones[key] - plugin := p.zonesToPlugins[zone] - delete(p.zonesToPlugins, zone) - delete(p.keyToZones, key) - p.mutex.Unlock() - return plugin -} diff --git a/plugin/forwardcrd/plugin_map_test.go b/plugin/forwardcrd/plugin_map_test.go deleted file mode 100644 index eef0caf7d..000000000 --- a/plugin/forwardcrd/plugin_map_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package forwardcrd - -import ( - "sync" - "testing" - - "github.com/coredns/coredns/plugin/forward" -) - -func TestPluginMap(t *testing.T) { - pluginInstanceMap := NewPluginInstanceMap() - - zone1ForwardPlugin := forward.New() - zone2ForwardPlugin := forward.New() - - // Testing concurrency to ensure map is thread-safe - // i.e should run with `go test -race` - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - pluginInstanceMap.Upsert("default/some-dns-zone", "zone-1.test", zone1ForwardPlugin) - wg.Done() - }() - wg.Add(1) - go func() { - pluginInstanceMap.Upsert("default/another-dns-zone", "zone-2.test", zone2ForwardPlugin) - wg.Done() - }() - wg.Wait() - - if plugin, exists := pluginInstanceMap.Get("zone-1.test."); exists && plugin != zone1ForwardPlugin { - t.Fatalf("Expected plugin instance map to get plugin with address: %p but was: %p", zone1ForwardPlugin, plugin) - } - - if plugin, exists := pluginInstanceMap.Get("zone-2.test"); exists && plugin != zone2ForwardPlugin { - t.Fatalf("Expected plugin instance map to get plugin with address: %p but was: %p", zone2ForwardPlugin, plugin) - } - - if _, exists := pluginInstanceMap.Get("non-existant-zone.test"); exists { - t.Fatal("Expected plugin instance map to not return a plugin") - } - - // list - - plugins := pluginInstanceMap.List() - if len(plugins) != 2 { - t.Fatalf("Expected plugin instance map to have len %d, got: %d", 2, len(plugins)) - } - - if plugins[0] != zone1ForwardPlugin && plugins[0] != zone2ForwardPlugin { - t.Fatalf("Expected plugin instance map to list plugin[0] with address: %p or %p but was: %p", zone1ForwardPlugin, zone2ForwardPlugin, plugins[0]) - } - - if plugins[1] != zone1ForwardPlugin && plugins[1] != zone2ForwardPlugin { - t.Fatalf("Expected plugin instance map to list plugin[1] with address: %p or %p but was: %p", zone1ForwardPlugin, zone2ForwardPlugin, plugins[1]) - } - - // update record with the same key - - oldPlugin, update := pluginInstanceMap.Upsert("default/some-dns-zone", "new-zone-1.test", zone1ForwardPlugin) - - if !update { - t.Fatalf("Expected Upsert to be an update") - } - - if oldPlugin != zone1ForwardPlugin { - t.Fatalf("Expected Upsert to return the old plugin %#v, got: %#v", zone1ForwardPlugin, oldPlugin) - } - - if plugin, exists := pluginInstanceMap.Get("new-zone-1.test"); exists && plugin != zone1ForwardPlugin { - t.Fatalf("Expected plugin instance map to get plugin with address: %p but was: %p", zone1ForwardPlugin, plugin) - - } - if _, exists := pluginInstanceMap.Get("zone-1.test"); exists { - t.Fatalf("Expected plugin instance map to not get plugin with zone: %s", "zone-1.test") - } - - // delete record by key - - deletedPlugin := pluginInstanceMap.Delete("default/some-dns-zone") - - if _, exists := pluginInstanceMap.Get("new-zone-1.test"); exists { - t.Fatalf("Expected plugin instance map to not get plugin with zone: %s", "new-zone-1.test") - } - - if deletedPlugin == nil || deletedPlugin != zone1ForwardPlugin { - t.Fatalf("Expected Delete to return the deleted plugin %#v, got: %#v", zone1ForwardPlugin, deletedPlugin) - } -} diff --git a/plugin/forwardcrd/setup.go b/plugin/forwardcrd/setup.go deleted file mode 100644 index 6be322bc6..000000000 --- a/plugin/forwardcrd/setup.go +++ /dev/null @@ -1,147 +0,0 @@ -package forwardcrd - -import ( - "context" - "os" - "time" - - "github.com/coredns/caddy" - "github.com/coredns/coredns/core/dnsserver" - "github.com/coredns/coredns/plugin" - "github.com/coredns/coredns/plugin/dnstap" - clog "github.com/coredns/coredns/plugin/pkg/log" - - "k8s.io/client-go/tools/clientcmd" - "k8s.io/klog/v2" -) - -const pluginName = "forwardcrd" - -var log = clog.NewWithPlugin(pluginName) - -func init() { - plugin.Register(pluginName, setup) -} - -func setup(c *caddy.Controller) error { - klog.SetOutput(os.Stdout) - - k, err := parseForwardCRD(c) - if err != nil { - return plugin.Error(pluginName, err) - } - - err = k.InitKubeCache(context.Background()) - if err != nil { - return plugin.Error(pluginName, err) - } - - dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { - k.Next = next - return k - }) - - c.OnStartup(func() error { - go k.APIConn.Run(1) - - timeout := time.After(5 * time.Second) - ticker := time.NewTicker(100 * time.Millisecond) - for { - select { - case <-ticker.C: - if k.APIConn.HasSynced() { - return nil - } - case <-timeout: - return nil - } - } - }) - - c.OnStartup(func() error { - if taph := dnsserver.GetConfig(c).Handler("dnstap"); taph != nil { - if tapPlugin, ok := taph.(dnstap.Dnstap); ok { - k.APIConn.(*forwardCRDControl).tapPlugin = &tapPlugin - } - } - return nil - }) - - c.OnShutdown(func() error { - return k.APIConn.Stop() - }) - - return nil -} - -func parseForwardCRD(c *caddy.Controller) (*ForwardCRD, error) { - var ( - k *ForwardCRD - err error - i int - ) - - for c.Next() { - if i > 0 { - return nil, plugin.ErrOnce - } - i++ - k, err = parseStanza(c) - if err != nil { - return nil, err - } - } - - return k, nil -} - -func parseStanza(c *caddy.Controller) (*ForwardCRD, error) { - k := New() - - args := c.RemainingArgs() - k.Zones = plugin.OriginsFromArgsOrServerBlock(args, c.ServerBlockKeys) - - for c.NextBlock() { - switch c.Val() { - case "endpoint": - args := c.RemainingArgs() - if len(args) != 1 { - return nil, c.ArgErr() - } - k.APIServerEndpoint = args[0] - case "tls": - args := c.RemainingArgs() - if len(args) != 3 { - return nil, c.ArgErr() - } - k.APIClientCert, k.APIClientKey, k.APICertAuth = args[0], args[1], args[2] - case "kubeconfig": - args := c.RemainingArgs() - if len(args) != 1 && len(args) != 2 { - return nil, c.ArgErr() - } - overrides := &clientcmd.ConfigOverrides{} - if len(args) == 2 { - overrides.CurrentContext = args[1] - } - config := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( - &clientcmd.ClientConfigLoadingRules{ExplicitPath: args[0]}, - overrides, - ) - k.ClientConfig = config - case "namespace": - args := c.RemainingArgs() - if len(args) == 0 { - k.Namespace = "" - } else if len(args) == 1 { - k.Namespace = args[0] - } else { - return nil, c.ArgErr() - } - default: - return nil, c.Errf("unknown property '%s'", c.Val()) - } - } - - return k, nil -} diff --git a/plugin/forwardcrd/setup_test.go b/plugin/forwardcrd/setup_test.go deleted file mode 100644 index e5c7cb592..000000000 --- a/plugin/forwardcrd/setup_test.go +++ /dev/null @@ -1,194 +0,0 @@ -package forwardcrd - -import ( - "strings" - "testing" - - "github.com/coredns/caddy" - "github.com/coredns/coredns/plugin" -) - -func TestForwardCRDParse(t *testing.T) { - c := caddy.NewTestController("dns", `forwardcrd`) - k, err := parseForwardCRD(c) - if err != nil { - t.Fatalf("Expected no errors, but got: %v", err) - } - if k.Namespace != "kube-system" { - t.Errorf("Expected Namespace to be: %s\n but was: %s\n", "kube-system", k.Namespace) - } - - c = caddy.NewTestController("dns", `forwardcrd { - endpoint http://localhost:9090 - }`) - k, err = parseForwardCRD(c) - if err != nil { - t.Fatalf("Expected no errors, but got: %v", err) - } - if k.APIServerEndpoint != "http://localhost:9090" { - t.Errorf("Expected APIServerEndpoint to be: %s\n but was: %s\n", "http://localhost:9090", k.APIServerEndpoint) - } - - c = caddy.NewTestController("dns", `forwardcrd { - tls cert.crt key.key cacert.crt - }`) - k, err = parseForwardCRD(c) - if err != nil { - t.Fatalf("Expected no errors, but got: %v", err) - } - if k.APIClientCert != "cert.crt" { - t.Errorf("Expected APIClientCert to be: %s\n but was: %s\n", "cert.crt", k.APIClientCert) - } - if k.APIClientKey != "key.key" { - t.Errorf("Expected APIClientCert to be: %s\n but was: %s\n", "key.key", k.APIClientKey) - } - if k.APICertAuth != "cacert.crt" { - t.Errorf("Expected APICertAuth to be: %s\n but was: %s\n", "cacert.crt", k.APICertAuth) - } - - c = caddy.NewTestController("dns", `forwardcrd { - kubeconfig foo.kubeconfig - }`) - _, err = parseForwardCRD(c) - if err != nil { - t.Fatalf("Expected no errors, but got: %v", err) - } - - c = caddy.NewTestController("dns", `forwardcrd { - kubeconfig foo.kubeconfig context - }`) - _, err = parseForwardCRD(c) - if err != nil { - t.Fatalf("Expected no errors, but got: %v", err) - } - - c = caddy.NewTestController("dns", `forwardcrd example.org`) - k, err = parseForwardCRD(c) - if err != nil { - t.Fatalf("Expected no errors, but got: %v", err) - } - if len(k.Zones) != 1 || k.Zones[0] != "example.org." { - t.Fatalf("Expected Zones to consist of \"example.org.\" but was %v", k.Zones) - } - - c = caddy.NewTestController("dns", `forwardcrd`) - c.ServerBlockKeys = []string{"example.org"} - k, err = parseForwardCRD(c) - if err != nil { - t.Fatalf("Expected no errors, but got: %v", err) - } - if len(k.Zones) != 1 || k.Zones[0] != "example.org." { - t.Fatalf("Expected Zones to consist of \"example.org.\" but was %v", k.Zones) - } - - c = caddy.NewTestController("dns", `forwardcrd { - namespace - }`) - k, err = parseForwardCRD(c) - if err != nil { - t.Fatalf("Expected no errors, but got: %v", err) - } - if k.Namespace != "" { - t.Errorf("Expected Namespace to be: %q\n but was: %q\n", "", k.Namespace) - } - - c = caddy.NewTestController("dns", `forwardcrd { - namespace dns-system - }`) - k, err = parseForwardCRD(c) - if err != nil { - t.Fatalf("Expected no errors, but got: %v", err) - } - if k.Namespace != "dns-system" { - t.Errorf("Expected Namespace to be: %s\n but was: %s\n", "dns-system", k.Namespace) - } - - // negative - - c = caddy.NewTestController("dns", `forwardcrd { - endpoint http://localhost:9090 http://foo.bar:1024 - }`) - _, err = parseForwardCRD(c) - if err == nil { - t.Fatalf("Expected errors, but got nil") - } - if !strings.Contains(err.Error(), "Wrong argument count") { - t.Fatalf("Expected error containing \"Wrong argument count\", but got: %v", err.Error()) - } - - c = caddy.NewTestController("dns", `forwardcrd { - endpoint - }`) - _, err = parseForwardCRD(c) - if err == nil { - t.Fatalf("Expected errors, but got nil") - } - if !strings.Contains(err.Error(), "Wrong argument count") { - t.Fatalf("Expected error containing \"Wrong argument count\", but got: %v", err.Error()) - } - - c = caddy.NewTestController("dns", `forwardcrd { - tls foo bar - }`) - _, err = parseForwardCRD(c) - if err == nil { - t.Fatalf("Expected errors, but got nil") - } - if !strings.Contains(err.Error(), "Wrong argument count") { - t.Fatalf("Expected error containing \"Wrong argument count\", but got: %v", err.Error()) - } - - c = caddy.NewTestController("dns", `forwardcrd { - kubeconfig - }`) - _, err = parseForwardCRD(c) - if err == nil { - t.Fatalf("Expected errors, but got nil") - } - if !strings.Contains(err.Error(), "Wrong argument count") { - t.Fatalf("Expected error containing \"Wrong argument count\", but got: %v", err.Error()) - } - - c = caddy.NewTestController("dns", `forwardcrd { - kubeconfig too many args - }`) - _, err = parseForwardCRD(c) - if err == nil { - t.Fatalf("Expected errors, but got nil") - } - if !strings.Contains(err.Error(), "Wrong argument count") { - t.Fatalf("Expected error containing \"Wrong argument count\", but got: %v", err.Error()) - } - - c = caddy.NewTestController("dns", `forwardcrd { - namespace too many args - }`) - _, err = parseForwardCRD(c) - if err == nil { - t.Fatalf("Expected errors, but got nil") - } - if !strings.Contains(err.Error(), "Wrong argument count") { - t.Fatalf("Expected error containing \"Wrong argument count\", but got: %v", err.Error()) - } - - c = caddy.NewTestController("dns", `forwardcrd { - invalid - }`) - _, err = parseForwardCRD(c) - if err == nil { - t.Fatalf("Expected errors, but got nil") - } - if !strings.Contains(err.Error(), "unknown property") { - t.Fatalf("Expected error containing \"unknown property\", but got: %v", err.Error()) - } - - c = caddy.NewTestController("dns", `forwardcrd -forwardcrd`) - _, err = parseForwardCRD(c) - if err == nil { - t.Fatalf("Expected errors, but got nil") - } - if !strings.Contains(err.Error(), plugin.ErrOnce.Error()) { - t.Fatalf("Expected error containing \"%s\", but got: %v", plugin.ErrOnce.Error(), err.Error()) - } -} |