diff --git a/plugin/traffic/README.md b/plugin/traffic/README.md index a759e6ba8..8b29445ca 100644 --- a/plugin/traffic/README.md +++ b/plugin/traffic/README.md @@ -42,7 +42,7 @@ traffic TO... The extended syntax is available is you want more control. ~~~ -traffic { +traffic TO... { server SERVER [SERVER]... node ID } @@ -60,9 +60,11 @@ names. For example if the Server Block specifies `lb.example.org` as one of the ## Examples -~~~ corefile +~~~ lb.example.org { - traffic grpc://127.0.0.1:18000 + traffic grpc://127.0.0.1:18000 { + node test-id + } debug log } diff --git a/plugin/traffic/setup.go b/plugin/traffic/setup.go index debeb7912..bdd61a381 100644 --- a/plugin/traffic/setup.go +++ b/plugin/traffic/setup.go @@ -55,8 +55,14 @@ func setup(c *caddy.Controller) error { func parseTraffic(c *caddy.Controller) (*Traffic, error) { node := "coredns" toHosts := []string{} + t := &Traffic{} var err error + t.origins = make([]string, len(c.ServerBlockKeys)) + for i := range c.ServerBlockKeys { + t.origins[i] = plugin.Host(c.ServerBlockKeys[i]).Normalize() + } + for c.Next() { args := c.RemainingArgs() if len(args) < 1 { @@ -70,8 +76,8 @@ func parseTraffic(c *caddy.Controller) (*Traffic, error) { if !strings.HasPrefix(toHosts[i], transport.GRPC+"://") { return nil, fmt.Errorf("not a %s scheme: %s", transport.GRPC, toHosts[i]) } - // now cut the prefix off again, because the dialer needs to see normal address strings. All this - // grpc:// stuff is to enfore uniformaty accross plugins and future proofing for other protocols. + // now cut the prefix off again, because the dialler needs to see normal address strings. All this + // grpc:// stuff is to enforce uniform across plugins and future proofing for other protocols. toHosts[i] = toHosts[i][len(transport.GRPC+"://"):] } for c.NextBlock() { @@ -88,12 +94,10 @@ func parseTraffic(c *caddy.Controller) (*Traffic, error) { } } - // TODO: only the first host is used. - x, err := xds.New(toHosts[0], node) - if err != nil { + // TODO: only the first host is used, need to figure out how to reconcile multiple upstream providers. + if t.c, err = xds.New(toHosts[0], node); err != nil { return nil, err } - t := &Traffic{c: x} return t, nil } diff --git a/plugin/traffic/traffic.go b/plugin/traffic/traffic.go index 61384ab17..693b33e31 100644 --- a/plugin/traffic/traffic.go +++ b/plugin/traffic/traffic.go @@ -2,6 +2,8 @@ package traffic import ( "context" + "strings" + "time" "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/pkg/dnsutil" @@ -13,9 +15,10 @@ import ( // Traffic is a plugin that load balances according to assignments. type Traffic struct { - c *xds.Client - id string - Next plugin.Handler + c *xds.Client + id string + origins []string + Next plugin.Handler } // shutdown closes the connection to the managment endpoints and stops any running goroutines. @@ -25,26 +28,71 @@ func (t *Traffic) shutdown() { t.c.Close() } func (t *Traffic) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { state := request.Request{Req: r, W: w} - cluster, _ := dnsutil.TrimZone(state.Name(), "example.org") - addr := t.c.Select(cluster) - if addr == nil { + cluster := "" + for _, o := range t.origins { + println(o, state.Name()) + if strings.HasSuffix(state.Name(), o) { + cluster, _ = dnsutil.TrimZone(state.Name(), o) + state.Zone = o + break + } + } + if cluster == "" { + // TODO(miek): can this actually happen? return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r) } - log.Debugf("Found address %q for %q", addr, cluster) + addr := t.c.Select(cluster) + if addr != nil { + log.Debugf("Found endpoint %q for %q", addr, cluster) + } else { + log.Debugf("No healthy endpoints found for %q", cluster) + } - // assemble reply m := new(dns.Msg) m.SetReply(r) - m.Answer = []dns.RR{&dns.A{ - Hdr: dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 5}, - A: addr, - }} + if addr == nil { + m.Ns = soa(state.Zone) + w.WriteMsg(m) + return 0, nil + } + + switch state.QType() { + case dns.TypeA: + if addr.To4() == nil { // it's an IPv6 address, return nodata in that case. + m.Ns = soa(state.Zone) + break + } + m.Answer = []dns.RR{&dns.A{Hdr: dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 5}, A: addr}} + + case dns.TypeAAAA: + if addr.To4() != nil { // it's an IPv4 address, return nodata in that case. + m.Ns = soa(state.Zone) + break + } + m.Answer = []dns.RR{&dns.AAAA{Hdr: dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 5}, AAAA: addr}} + default: + m.Ns = soa(state.Zone) + } w.WriteMsg(m) return 0, nil } +// soa returns a synthetic so for this zone. +func soa(z string) []dns.RR { + return []dns.RR{&dns.SOA{ + Hdr: dns.RR_Header{Name: z, Rrtype: dns.TypeSOA, Class: dns.ClassINET, Ttl: 5}, + Ns: dnsutil.Join("ns", z), + Mbox: dnsutil.Join("coredns", z), + Serial: uint32(time.Now().UTC().Unix()), + Refresh: 14400, + Retry: 3600, + Expire: 604800, + Minttl: 5, + }} +} + // Name implements the plugin.Handler interface. func (t *Traffic) Name() string { return "traffic" }