diff options
author | 2017-09-14 09:36:06 +0100 | |
---|---|---|
committer | 2017-09-14 09:36:06 +0100 | |
commit | d8714e64e400ef873c2adc4d929a07d7890727b9 (patch) | |
tree | c9fa4c157e6af12eb1517654f8d23ca5d5619513 /plugin/trace | |
parent | b984aa45595dc95253b91191afe7d3ee29e71b48 (diff) | |
download | coredns-d8714e64e400ef873c2adc4d929a07d7890727b9.tar.gz coredns-d8714e64e400ef873c2adc4d929a07d7890727b9.tar.zst coredns-d8714e64e400ef873c2adc4d929a07d7890727b9.zip |
Remove the word middleware (#1067)
* Rename middleware to plugin
first pass; mostly used 'sed', few spots where I manually changed
text.
This still builds a coredns binary.
* fmt error
* Rename AddMiddleware to AddPlugin
* Readd AddMiddleware to remain backwards compat
Diffstat (limited to 'plugin/trace')
-rw-r--r-- | plugin/trace/README.md | 73 | ||||
-rw-r--r-- | plugin/trace/setup.go | 113 | ||||
-rw-r--r-- | plugin/trace/setup_test.go | 60 | ||||
-rw-r--r-- | plugin/trace/trace.go | 84 | ||||
-rw-r--r-- | plugin/trace/trace_test.go | 33 |
5 files changed, 363 insertions, 0 deletions
diff --git a/plugin/trace/README.md b/plugin/trace/README.md new file mode 100644 index 000000000..62e6d463d --- /dev/null +++ b/plugin/trace/README.md @@ -0,0 +1,73 @@ +# trace + +This module enables OpenTracing-based tracing of DNS requests as they go through the +plugin chain. + +## Syntax + +The simplest form is just: + +~~~ +trace [ENDPOINT-TYPE] [ENDPOINT] +~~~ + +* **ENDPOINT-TYPE** is the type of tracing destination. Currently only `zipkin` is supported + and that is what it defaults to. +* **ENDPOINT** is the tracing destination, and defaults to `localhost:9411`. For Zipkin, if + ENDPOINT does not begin with `http`, then it will be transformed to `http://ENDPOINT/api/v1/spans`. + +With this form, all queries will be traced. + +Additional features can be enabled with this syntax: + +~~~ +trace [ENDPOINT-TYPE] [ENDPOINT] { + every AMOUNT + service NAME + client_server +} +~~~ + +* `every` **AMOUNT** will only trace one query of each AMOUNT queries. For example, to trace 1 in every + 100 queries, use AMOUNT of 100. The default is 1. +* `service` **NAME** allows you to specify the service name reported to the tracing server. + Default is `coredns`. +* `client_server` will enable the `ClientServerSameSpan` OpenTracing feature. + +## Zipkin +You can run Zipkin on a Docker host like this: + +``` +docker run -d -p 9411:9411 openzipkin/zipkin +``` + +## Examples + +Use an alternative Zipkin address: + +~~~ +trace tracinghost:9253 +~~~ + +or + +~~~ +trace zipkin tracinghost:9253 +~~~ + +If for some reason you are using an API reverse proxy or something and need to remap +the standard Zipkin URL you can do something like: + +~~~ +trace http://tracinghost:9411/zipkin/api/v1/spans +~~~ + +Trace one query every 10000 queries, rename the service, and enable same span: + +~~~ +trace tracinghost:9411 { + every 10000 + service dnsproxy + client_server +} +~~~ diff --git a/plugin/trace/setup.go b/plugin/trace/setup.go new file mode 100644 index 000000000..5c6e473c3 --- /dev/null +++ b/plugin/trace/setup.go @@ -0,0 +1,113 @@ +package trace + +import ( + "fmt" + "strconv" + "strings" + + "github.com/coredns/coredns/core/dnsserver" + "github.com/coredns/coredns/plugin" + + "github.com/mholt/caddy" +) + +func init() { + caddy.RegisterPlugin("trace", caddy.Plugin{ + ServerType: "dns", + Action: setup, + }) +} + +func setup(c *caddy.Controller) error { + t, err := traceParse(c) + if err != nil { + return plugin.Error("trace", err) + } + + dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { + t.Next = next + return t + }) + + c.OnStartup(t.OnStartup) + + return nil +} + +func traceParse(c *caddy.Controller) (*trace, error) { + var ( + tr = &trace{Endpoint: defEP, EndpointType: defEpType, every: 1, serviceName: defServiceName} + err error + ) + + cfg := dnsserver.GetConfig(c) + tr.ServiceEndpoint = cfg.ListenHost + ":" + cfg.Port + for c.Next() { // trace + var err error + args := c.RemainingArgs() + switch len(args) { + case 0: + tr.Endpoint, err = normalizeEndpoint(tr.EndpointType, defEP) + case 1: + tr.Endpoint, err = normalizeEndpoint(defEpType, args[0]) + case 2: + tr.EndpointType = strings.ToLower(args[0]) + tr.Endpoint, err = normalizeEndpoint(tr.EndpointType, args[1]) + default: + err = c.ArgErr() + } + if err != nil { + return tr, err + } + for c.NextBlock() { + switch c.Val() { + case "every": + args := c.RemainingArgs() + if len(args) != 1 { + return nil, c.ArgErr() + } + tr.every, err = strconv.ParseUint(args[0], 10, 64) + if err != nil { + return nil, err + } + case "service": + args := c.RemainingArgs() + if len(args) != 1 { + return nil, c.ArgErr() + } + tr.serviceName = args[0] + case "client_server": + args := c.RemainingArgs() + if len(args) > 1 { + return nil, c.ArgErr() + } + tr.clientServer = true + if len(args) == 1 { + tr.clientServer, err = strconv.ParseBool(args[0]) + } + if err != nil { + return nil, err + } + } + } + } + return tr, err +} + +func normalizeEndpoint(epType, ep string) (string, error) { + switch epType { + case "zipkin": + if !strings.Contains(ep, "http") { + ep = "http://" + ep + "/api/v1/spans" + } + return ep, nil + default: + return "", fmt.Errorf("tracing endpoint type '%s' is not supported", epType) + } +} + +const ( + defEP = "localhost:9411" + defEpType = "zipkin" + defServiceName = "coredns" +) diff --git a/plugin/trace/setup_test.go b/plugin/trace/setup_test.go new file mode 100644 index 000000000..3c12b76e4 --- /dev/null +++ b/plugin/trace/setup_test.go @@ -0,0 +1,60 @@ +package trace + +import ( + "testing" + + "github.com/mholt/caddy" +) + +func TestTraceParse(t *testing.T) { + tests := []struct { + input string + shouldErr bool + endpoint string + every uint64 + serviceName string + clientServer bool + }{ + // oks + {`trace`, false, "http://localhost:9411/api/v1/spans", 1, `coredns`, false}, + {`trace localhost:1234`, false, "http://localhost:1234/api/v1/spans", 1, `coredns`, false}, + {`trace http://localhost:1234/somewhere/else`, false, "http://localhost:1234/somewhere/else", 1, `coredns`, false}, + {`trace zipkin localhost:1234`, false, "http://localhost:1234/api/v1/spans", 1, `coredns`, false}, + {`trace zipkin http://localhost:1234/somewhere/else`, false, "http://localhost:1234/somewhere/else", 1, `coredns`, false}, + {"trace {\n every 100\n}", false, "http://localhost:9411/api/v1/spans", 100, `coredns`, false}, + {"trace {\n every 100\n service foobar\nclient_server\n}", false, "http://localhost:9411/api/v1/spans", 100, `foobar`, true}, + {"trace {\n every 2\n client_server true\n}", false, "http://localhost:9411/api/v1/spans", 2, `coredns`, true}, + {"trace {\n client_server false\n}", false, "http://localhost:9411/api/v1/spans", 1, `coredns`, false}, + // fails + {`trace footype localhost:4321`, true, "", 1, "", false}, + {"trace {\n every 2\n client_server junk\n}", true, "", 1, "", false}, + } + for i, test := range tests { + c := caddy.NewTestController("dns", test.input) + m, err := traceParse(c) + if test.shouldErr && err == nil { + t.Errorf("Test %v: Expected error but found nil", i) + continue + } else if !test.shouldErr && err != nil { + t.Errorf("Test %v: Expected no error but found error: %v", i, err) + continue + } + + if test.shouldErr { + continue + } + + if test.endpoint != m.Endpoint { + t.Errorf("Test %v: Expected endpoint %s but found: %s", i, test.endpoint, m.Endpoint) + } + if test.every != m.every { + t.Errorf("Test %v: Expected every %d but found: %d", i, test.every, m.every) + } + if test.serviceName != m.serviceName { + t.Errorf("Test %v: Expected service name %s but found: %s", i, test.serviceName, m.serviceName) + } + if test.clientServer != m.clientServer { + t.Errorf("Test %v: Expected client_server %t but found: %t", i, test.clientServer, m.clientServer) + } + } +} diff --git a/plugin/trace/trace.go b/plugin/trace/trace.go new file mode 100644 index 000000000..fa561945e --- /dev/null +++ b/plugin/trace/trace.go @@ -0,0 +1,84 @@ +// Package trace implements OpenTracing-based tracing +package trace + +import ( + "fmt" + "sync" + "sync/atomic" + + "github.com/coredns/coredns/plugin" + // Plugin the trace package. + _ "github.com/coredns/coredns/plugin/pkg/trace" + + "github.com/miekg/dns" + ot "github.com/opentracing/opentracing-go" + zipkin "github.com/openzipkin/zipkin-go-opentracing" + "golang.org/x/net/context" +) + +type trace struct { + Next plugin.Handler + ServiceEndpoint string + Endpoint string + EndpointType string + tracer ot.Tracer + serviceName string + clientServer bool + every uint64 + count uint64 + Once sync.Once +} + +func (t *trace) Tracer() ot.Tracer { + return t.tracer +} + +// OnStartup sets up the tracer +func (t *trace) OnStartup() error { + var err error + t.Once.Do(func() { + switch t.EndpointType { + case "zipkin": + err = t.setupZipkin() + default: + err = fmt.Errorf("unknown endpoint type: %s", t.EndpointType) + } + }) + return err +} + +func (t *trace) setupZipkin() error { + + collector, err := zipkin.NewHTTPCollector(t.Endpoint) + if err != nil { + return err + } + + recorder := zipkin.NewRecorder(collector, false, t.ServiceEndpoint, t.serviceName) + t.tracer, err = zipkin.NewTracer(recorder, zipkin.ClientServerSameSpan(t.clientServer)) + + return err +} + +// Name implements the Handler interface. +func (t *trace) Name() string { + return "trace" +} + +// ServeDNS implements the plugin.Handle interface. +func (t *trace) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + trace := false + if t.every > 0 { + queryNr := atomic.AddUint64(&t.count, 1) + + if queryNr%t.every == 0 { + trace = true + } + } + if span := ot.SpanFromContext(ctx); span == nil && trace { + span := t.Tracer().StartSpan("servedns") + defer span.Finish() + ctx = ot.ContextWithSpan(ctx, span) + } + return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r) +} diff --git a/plugin/trace/trace_test.go b/plugin/trace/trace_test.go new file mode 100644 index 000000000..b006009c3 --- /dev/null +++ b/plugin/trace/trace_test.go @@ -0,0 +1,33 @@ +package trace + +import ( + "testing" + + "github.com/mholt/caddy" +) + +// createTestTrace creates a trace plugin to be used in tests +func createTestTrace(config string) (*caddy.Controller, *trace, error) { + c := caddy.NewTestController("dns", config) + m, err := traceParse(c) + return c, m, err +} + +func TestTrace(t *testing.T) { + _, m, err := createTestTrace(`trace`) + if err != nil { + t.Errorf("Error parsing test input: %s", err) + return + } + if m.Name() != "trace" { + t.Errorf("Wrong name from GetName: %s", m.Name()) + } + err = m.OnStartup() + if err != nil { + t.Errorf("Error starting tracing plugin: %s", err) + return + } + if m.Tracer() == nil { + t.Errorf("Error, no tracer created") + } +} |