From 1877fc5fb0935c98bf169da7f48d17bf891af1f7 Mon Sep 17 00:00:00 2001 From: Pavithra Ramesh Date: Thu, 30 May 2019 09:16:30 -0700 Subject: [PATCH 1/2] Revert "Merge pull request #301 from woopstar/upgrade-coredns-fix" This reverts commit 97d55005ab1d38b9443485d5c95dc424d90b354b, reversing changes made to 24b1708afb24325e42b2f40130f1eff4dbaf0c3d. --- Gopkg.lock | 24 +- Gopkg.toml | 12 +- .../coredns/coredns/plugin/dnstap/OWNERS | 6 - .../coredns/coredns/plugin/dnstap/README.md | 93 --- .../plugin/dnstap/dnstapio/dnstap_encoder.go | 92 --- .../coredns/plugin/dnstap/dnstapio/io.go | 146 ---- .../coredns/plugin/dnstap/gocontext.go | 23 - .../coredns/coredns/plugin/dnstap/handler.go | 92 --- .../coredns/coredns/plugin/dnstap/msg/msg.go | 159 ---- .../coredns/coredns/plugin/dnstap/setup.go | 96 --- .../coredns/plugin/dnstap/taprw/writer.go | 79 -- .../coredns/coredns/plugin/metadata/OWNERS | 8 - .../coredns/coredns/plugin/metadata/README.md | 49 -- .../coredns/plugin/metadata/metadata.go | 39 - .../coredns/plugin/metadata/provider.go | 122 --- .../coredns/coredns/plugin/metadata/setup.go | 61 -- vendor/github.com/davecgh/go-spew/LICENSE | 2 +- .../github.com/davecgh/go-spew/spew/bypass.go | 187 +++-- .../davecgh/go-spew/spew/bypasssafe.go | 2 +- .../github.com/davecgh/go-spew/spew/common.go | 2 +- .../github.com/davecgh/go-spew/spew/dump.go | 10 +- .../github.com/davecgh/go-spew/spew/format.go | 4 +- .../dnstap/golang-dnstap/.gitignore | 1 - .../github.com/dnstap/golang-dnstap/COPYRIGHT | 14 - .../dnstap/golang-dnstap/FrameStreamInput.go | 73 -- .../dnstap/golang-dnstap/FrameStreamOutput.go | 74 -- .../golang-dnstap/FrameStreamSockInput.go | 64 -- .../github.com/dnstap/golang-dnstap/LICENSE | 202 ----- .../dnstap/golang-dnstap/QuietTextFormat.go | 172 ----- vendor/github.com/dnstap/golang-dnstap/README | 15 - .../dnstap/golang-dnstap/TextOutput.go | 86 --- .../dnstap/golang-dnstap/YamlFormat.go | 116 --- .../dnstap/golang-dnstap/debian/copyright | 26 - .../github.com/dnstap/golang-dnstap/dnstap.go | 32 - .../dnstap/golang-dnstap/dnstap.pb.go | 448 ----------- .../dnstap/golang-dnstap/dnstap.pb/LICENSE | 121 --- .../farsightsec/golang-framestream/.gitignore | 1 - .../farsightsec/golang-framestream/COPYRIGHT | 13 - .../farsightsec/golang-framestream/Control.go | 156 ---- .../farsightsec/golang-framestream/Decoder.go | 160 ---- .../farsightsec/golang-framestream/Encoder.go | 102 --- .../farsightsec/golang-framestream/LICENSE | 202 ----- .../farsightsec/golang-framestream/README.md | 13 - .../golang-framestream/framestream.go | 32 - .../farsightsec/golang-framestream/timeout.go | 67 -- .../prometheus/client_golang/AUTHORS.md | 18 + .../client_golang/prometheus/collector.go | 73 +- .../client_golang/prometheus/counter.go | 191 ++--- .../client_golang/prometheus/desc.go | 51 +- .../client_golang/prometheus/doc.go | 94 +-- .../client_golang/prometheus/fnv.go | 13 - .../client_golang/prometheus/gauge.go | 204 +---- .../client_golang/prometheus/go_collector.go | 74 +- .../client_golang/prometheus/histogram.go | 304 ++------ .../client_golang/prometheus/http.go | 252 +++--- .../prometheus/internal/metric.go | 85 --- .../client_golang/prometheus/labels.go | 87 --- .../client_golang/prometheus/metric.go | 90 +-- .../client_golang/prometheus/observer.go | 52 -- .../prometheus/process_collector.go | 220 ++---- .../prometheus/promhttp/delegator.go | 199 ----- .../prometheus/promhttp/delegator_1_8.go | 181 ----- .../prometheus/promhttp/delegator_pre_1_8.go | 44 -- .../client_golang/prometheus/promhttp/http.go | 250 ++---- .../prometheus/promhttp/instrument_client.go | 97 --- .../promhttp/instrument_client_1_8.go | 144 ---- .../prometheus/promhttp/instrument_server.go | 447 ----------- .../client_golang/prometheus/registry.go | 719 +++++++----------- .../client_golang/prometheus/summary.go | 194 ++--- .../client_golang/prometheus/timer.go | 54 -- .../client_golang/prometheus/untyped.go | 102 ++- .../client_golang/prometheus/value.go | 94 ++- .../client_golang/prometheus/vec.go | 494 ++++++------ .../client_golang/prometheus/wrap.go | 179 ----- .../ugorji/go/codec/gen-helper.generated.go | 2 +- .../ugorji/go/codec/gen-helper.go.tmpl | 2 +- vendor/github.com/ugorji/go/codec/gen.go | 12 +- vendor/github.com/ugorji/go/codec/simple.go | 2 +- vendor/github.com/ugorji/go/codec/tests.sh | 24 +- 79 files changed, 1445 insertions(+), 7101 deletions(-) delete mode 100644 vendor/github.com/coredns/coredns/plugin/dnstap/OWNERS delete mode 100644 vendor/github.com/coredns/coredns/plugin/dnstap/README.md delete mode 100644 vendor/github.com/coredns/coredns/plugin/dnstap/dnstapio/dnstap_encoder.go delete mode 100644 vendor/github.com/coredns/coredns/plugin/dnstap/dnstapio/io.go delete mode 100644 vendor/github.com/coredns/coredns/plugin/dnstap/gocontext.go delete mode 100644 vendor/github.com/coredns/coredns/plugin/dnstap/handler.go delete mode 100644 vendor/github.com/coredns/coredns/plugin/dnstap/msg/msg.go delete mode 100644 vendor/github.com/coredns/coredns/plugin/dnstap/setup.go delete mode 100644 vendor/github.com/coredns/coredns/plugin/dnstap/taprw/writer.go delete mode 100644 vendor/github.com/coredns/coredns/plugin/metadata/OWNERS delete mode 100644 vendor/github.com/coredns/coredns/plugin/metadata/README.md delete mode 100644 vendor/github.com/coredns/coredns/plugin/metadata/metadata.go delete mode 100644 vendor/github.com/coredns/coredns/plugin/metadata/provider.go delete mode 100644 vendor/github.com/coredns/coredns/plugin/metadata/setup.go delete mode 100644 vendor/github.com/dnstap/golang-dnstap/.gitignore delete mode 100644 vendor/github.com/dnstap/golang-dnstap/COPYRIGHT delete mode 100644 vendor/github.com/dnstap/golang-dnstap/FrameStreamInput.go delete mode 100644 vendor/github.com/dnstap/golang-dnstap/FrameStreamOutput.go delete mode 100644 vendor/github.com/dnstap/golang-dnstap/FrameStreamSockInput.go delete mode 100644 vendor/github.com/dnstap/golang-dnstap/LICENSE delete mode 100644 vendor/github.com/dnstap/golang-dnstap/QuietTextFormat.go delete mode 100644 vendor/github.com/dnstap/golang-dnstap/README delete mode 100644 vendor/github.com/dnstap/golang-dnstap/TextOutput.go delete mode 100644 vendor/github.com/dnstap/golang-dnstap/YamlFormat.go delete mode 100644 vendor/github.com/dnstap/golang-dnstap/debian/copyright delete mode 100644 vendor/github.com/dnstap/golang-dnstap/dnstap.go delete mode 100644 vendor/github.com/dnstap/golang-dnstap/dnstap.pb.go delete mode 100644 vendor/github.com/dnstap/golang-dnstap/dnstap.pb/LICENSE delete mode 100644 vendor/github.com/farsightsec/golang-framestream/.gitignore delete mode 100644 vendor/github.com/farsightsec/golang-framestream/COPYRIGHT delete mode 100644 vendor/github.com/farsightsec/golang-framestream/Control.go delete mode 100644 vendor/github.com/farsightsec/golang-framestream/Decoder.go delete mode 100644 vendor/github.com/farsightsec/golang-framestream/Encoder.go delete mode 100644 vendor/github.com/farsightsec/golang-framestream/LICENSE delete mode 100644 vendor/github.com/farsightsec/golang-framestream/README.md delete mode 100644 vendor/github.com/farsightsec/golang-framestream/framestream.go delete mode 100644 vendor/github.com/farsightsec/golang-framestream/timeout.go create mode 100644 vendor/github.com/prometheus/client_golang/AUTHORS.md delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/internal/metric.go delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/labels.go delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/observer.go delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_1_8.go delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_pre_1_8.go delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client_1_8.go delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/timer.go delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/wrap.go diff --git a/Gopkg.lock b/Gopkg.lock index dcd64fabf..35d25b6e2 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -25,18 +25,6 @@ pruneopts = "UT" revision = "3a771d992973f24aa725d07868b467d1ddfceafb" -[[projects]] - name = "github.com/dnstap/golang-dnstap" - packages = ["."] - revision = "6b9665e675009e360f1c34b4526c2b215bc2ff68" - version = "tags/v0.1.0" - -[[projects]] - name = "github.com/farsightsec/golang-framestream" - packages = ["."] - revision = "fa4b164d59b87c744f49643b5d435effba516c4e" - version = "tags/v0.2.0" - [[projects]] digest = "1:705c40022f5c03bf96ffeb6477858d88565064485a513abcd0f11a0911546cb6" name = "github.com/blang/semver" @@ -57,17 +45,12 @@ "plugin/cache", "plugin/cache/freq", "plugin/debug", - "plugin/dnstap", - "plugin/dnstap/dnstapio", - "plugin/dnstap/msg", - "plugin/dnstap/taprw", "plugin/errors", "plugin/etcd/msg", "plugin/forward", "plugin/health", "plugin/log", "plugin/loop", - "plugin/metadata", "plugin/metrics", "plugin/metrics/vars", "plugin/pkg/cache", @@ -419,11 +402,10 @@ packages = [ "prometheus", "prometheus/promhttp", - "prometheus/internal" ] pruneopts = "UT" - revision = "505eaef017263e299324067d40ca2c48f6a2cf50" - version = "v0.9.2" + revision = "c5b7fccd204277076155f10851dad72b76a49317" + version = "v0.8.0" [[projects]] branch = "master" @@ -943,4 +925,4 @@ "k8s.io/kubernetes/pkg/version/prometheus", ] solver-name = "gps-cdcl" - solver-version = 1 \ No newline at end of file + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index bd07967e6..767783241 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -47,7 +47,7 @@ [[constraint]] name = "github.com/prometheus/client_golang" - version = "0.9.2" + version = "0.8.0" [[constraint]] branch = "master" @@ -85,18 +85,10 @@ name = "github.com/ugorji/go" revision = "9c7f9b7a2bc3a520f7c7b30b34b7f85f47fe27b6" -[[override]] - name = "github.com/dnstap/golang-dnstap" - revision = "6b9665e675009e360f1c34b4526c2b215bc2ff68" - -[[override]] - name = "github.com/farsightsec/golang-framestream" - revision = "fa4b164d59b87c744f49643b5d435effba516c4e" - [prune] go-tests = true unused-packages = true # Do not prune unused files in ginkgo. Those are needed to build ginkgo [[prune.project]] name = "github.com/onsi/ginkgo" - unused-packages = false \ No newline at end of file + unused-packages = false diff --git a/vendor/github.com/coredns/coredns/plugin/dnstap/OWNERS b/vendor/github.com/coredns/coredns/plugin/dnstap/OWNERS deleted file mode 100644 index 6f6724297..000000000 --- a/vendor/github.com/coredns/coredns/plugin/dnstap/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -reviewers: - - varyoo - - yongtang -approvers: - - varyoo - - yongtang diff --git a/vendor/github.com/coredns/coredns/plugin/dnstap/README.md b/vendor/github.com/coredns/coredns/plugin/dnstap/README.md deleted file mode 100644 index 81152e742..000000000 --- a/vendor/github.com/coredns/coredns/plugin/dnstap/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# dnstap - -## Name - -*dnstap* - enable logging to dnstap. - -## Description - -dnstap is a flexible, structured binary log format for DNS software: http://dnstap.info. With this -plugin you make CoreDNS output dnstap logging. - -Note that there is an internal buffer, so expect at least 13 requests before the server sends its -dnstap messages to the socket. - -## Syntax - -~~~ txt -dnstap SOCKET [full] -~~~ - -* **SOCKET** is the socket path supplied to the dnstap command line tool. -* `full` to include the wire-format DNS message. - -## Examples - -Log information about client requests and responses to */tmp/dnstap.sock*. - -~~~ txt -dnstap /tmp/dnstap.sock -~~~ - -Log information including the wire-format DNS message about client requests and responses to */tmp/dnstap.sock*. - -~~~ txt -dnstap unix:///tmp/dnstap.sock full -~~~ - -Log to a remote endpoint. - -~~~ txt -dnstap tcp://127.0.0.1:6000 full -~~~ - -## Command Line Tool - -Dnstap has a command line tool that can be used to inspect the logging. The tool can be found -at Github: . It's written in Go. - -The following command listens on the given socket and decodes messages to stdout. - -~~~ sh -$ dnstap -u /tmp/dnstap.sock -~~~ - -The following command listens on the given socket and saves message payloads to a binary dnstap-format log file. - -~~~ sh -$ dnstap -u /tmp/dnstap.sock -w /tmp/test.dnstap -~~~ - -Listen for dnstap messages on port 6000. - -~~~ sh -$ dnstap -l 127.0.0.1:6000 -~~~ - -## Using Dnstap in your plugin - -~~~ Go -import ( - "github.com/coredns/coredns/plugin/dnstap" - "github.com/coredns/coredns/plugin/dnstap/msg" -) - -func (h Dnstap) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { - // log client query to Dnstap - if t := dnstap.TapperFromContext(ctx); t != nil { - b := msg.New().Time(time.Now()).Addr(w.RemoteAddr()) - if t.Pack() { - b.Msg(r) - } - if m, err := b.ToClientQuery(); err == nil { - t.TapMessage(m) - } - } - - // ... -} -~~~ - -## See Also - -[dnstap.info](http://dnstap.info). diff --git a/vendor/github.com/coredns/coredns/plugin/dnstap/dnstapio/dnstap_encoder.go b/vendor/github.com/coredns/coredns/plugin/dnstap/dnstapio/dnstap_encoder.go deleted file mode 100644 index 07dfc8413..000000000 --- a/vendor/github.com/coredns/coredns/plugin/dnstap/dnstapio/dnstap_encoder.go +++ /dev/null @@ -1,92 +0,0 @@ -package dnstapio - -import ( - "encoding/binary" - "fmt" - "io" - - tap "github.com/dnstap/golang-dnstap" - fs "github.com/farsightsec/golang-framestream" - "github.com/golang/protobuf/proto" -) - -const ( - frameLenSize = 4 - protobufSize = 1024 * 1024 -) - -type dnstapEncoder struct { - fse *fs.Encoder - opts *fs.EncoderOptions - writer io.Writer - buffer *proto.Buffer -} - -func newDnstapEncoder(o *fs.EncoderOptions) *dnstapEncoder { - return &dnstapEncoder{ - opts: o, - buffer: proto.NewBuffer(make([]byte, 0, protobufSize)), - } -} - -func (enc *dnstapEncoder) resetWriter(w io.Writer) error { - fse, err := fs.NewEncoder(w, enc.opts) - if err != nil { - return err - } - if err = fse.Flush(); err != nil { - return err - } - enc.fse = fse - enc.writer = w - return nil -} - -func (enc *dnstapEncoder) writeMsg(msg *tap.Dnstap) error { - if len(enc.buffer.Bytes()) >= protobufSize { - if err := enc.flushBuffer(); err != nil { - return err - } - } - bufLen := len(enc.buffer.Bytes()) - // add placeholder for frame length - if err := enc.buffer.EncodeFixed32(0); err != nil { - enc.buffer.SetBuf(enc.buffer.Bytes()[:bufLen]) - return err - } - if err := enc.buffer.Marshal(msg); err != nil { - enc.buffer.SetBuf(enc.buffer.Bytes()[:bufLen]) - return err - } - enc.encodeFrameLen(enc.buffer.Bytes()[bufLen:]) - return nil -} - -func (enc *dnstapEncoder) flushBuffer() error { - if enc.fse == nil || enc.writer == nil { - return fmt.Errorf("no writer") - } - - buf := enc.buffer.Bytes() - written := 0 - for written < len(buf) { - n, err := enc.writer.Write(buf[written:]) - written += n - if err != nil { - return err - } - } - enc.buffer.Reset() - return nil -} - -func (enc *dnstapEncoder) encodeFrameLen(buf []byte) { - binary.BigEndian.PutUint32(buf, uint32(len(buf)-4)) -} - -func (enc *dnstapEncoder) close() error { - if enc.fse != nil { - return enc.fse.Close() - } - return nil -} diff --git a/vendor/github.com/coredns/coredns/plugin/dnstap/dnstapio/io.go b/vendor/github.com/coredns/coredns/plugin/dnstap/dnstapio/io.go deleted file mode 100644 index 65e2e222e..000000000 --- a/vendor/github.com/coredns/coredns/plugin/dnstap/dnstapio/io.go +++ /dev/null @@ -1,146 +0,0 @@ -package dnstapio - -import ( - "net" - "sync/atomic" - "time" - - clog "github.com/coredns/coredns/plugin/pkg/log" - - tap "github.com/dnstap/golang-dnstap" - fs "github.com/farsightsec/golang-framestream" -) - -var log = clog.NewWithPlugin("dnstap") - -const ( - tcpWriteBufSize = 1024 * 1024 - tcpTimeout = 4 * time.Second - flushTimeout = 1 * time.Second - queueSize = 10000 -) - -type dnstapIO struct { - endpoint string - socket bool - conn net.Conn - enc *dnstapEncoder - queue chan tap.Dnstap - dropped uint32 - quit chan struct{} -} - -// New returns a new and initialized DnstapIO. -func New(endpoint string, socket bool) DnstapIO { - return &dnstapIO{ - endpoint: endpoint, - socket: socket, - enc: newDnstapEncoder(&fs.EncoderOptions{ - ContentType: []byte("protobuf:dnstap.Dnstap"), - Bidirectional: true, - }), - queue: make(chan tap.Dnstap, queueSize), - quit: make(chan struct{}), - } -} - -// DnstapIO interface -type DnstapIO interface { - Connect() - Dnstap(payload tap.Dnstap) - Close() -} - -func (dio *dnstapIO) newConnect() error { - var err error - if dio.socket { - if dio.conn, err = net.Dial("unix", dio.endpoint); err != nil { - return err - } - } else { - if dio.conn, err = net.DialTimeout("tcp", dio.endpoint, tcpTimeout); err != nil { - return err - } - if tcpConn, ok := dio.conn.(*net.TCPConn); ok { - tcpConn.SetWriteBuffer(tcpWriteBufSize) - tcpConn.SetNoDelay(false) - } - } - - return dio.enc.resetWriter(dio.conn) -} - -// Connect connects to the dnstop endpoint. -func (dio *dnstapIO) Connect() { - if err := dio.newConnect(); err != nil { - log.Error("No connection to dnstap endpoint") - } - go dio.serve() -} - -// Dnstap enqueues the payload for log. -func (dio *dnstapIO) Dnstap(payload tap.Dnstap) { - select { - case dio.queue <- payload: - default: - atomic.AddUint32(&dio.dropped, 1) - } -} - -func (dio *dnstapIO) closeConnection() { - dio.enc.close() - if dio.conn != nil { - dio.conn.Close() - dio.conn = nil - } -} - -// Close waits until the I/O routine is finished to return. -func (dio *dnstapIO) Close() { - close(dio.quit) -} - -func (dio *dnstapIO) flushBuffer() { - if dio.conn == nil { - if err := dio.newConnect(); err != nil { - return - } - log.Info("Reconnected to dnstap") - } - - if err := dio.enc.flushBuffer(); err != nil { - log.Warningf("Connection lost: %s", err) - dio.closeConnection() - if err := dio.newConnect(); err != nil { - log.Errorf("Cannot connect to dnstap: %s", err) - } else { - log.Info("Reconnected to dnstap") - } - } -} - -func (dio *dnstapIO) write(payload *tap.Dnstap) { - if err := dio.enc.writeMsg(payload); err != nil { - atomic.AddUint32(&dio.dropped, 1) - } -} - -func (dio *dnstapIO) serve() { - timeout := time.After(flushTimeout) - for { - select { - case <-dio.quit: - dio.flushBuffer() - dio.closeConnection() - return - case payload := <-dio.queue: - dio.write(&payload) - case <-timeout: - if dropped := atomic.SwapUint32(&dio.dropped, 0); dropped > 0 { - log.Warningf("Dropped dnstap messages: %d", dropped) - } - dio.flushBuffer() - timeout = time.After(flushTimeout) - } - } -} diff --git a/vendor/github.com/coredns/coredns/plugin/dnstap/gocontext.go b/vendor/github.com/coredns/coredns/plugin/dnstap/gocontext.go deleted file mode 100644 index a8cc2c2b4..000000000 --- a/vendor/github.com/coredns/coredns/plugin/dnstap/gocontext.go +++ /dev/null @@ -1,23 +0,0 @@ -package dnstap - -import "context" - -type contextKey struct{} - -var dnstapKey = contextKey{} - -// ContextWithTapper returns a new `context.Context` that holds a reference to -// `t`'s Tapper. -func ContextWithTapper(ctx context.Context, t Tapper) context.Context { - return context.WithValue(ctx, dnstapKey, t) -} - -// TapperFromContext returns the `Tapper` previously associated with `ctx`, or -// `nil` if no such `Tapper` could be found. -func TapperFromContext(ctx context.Context) Tapper { - val := ctx.Value(dnstapKey) - if sp, ok := val.(Tapper); ok { - return sp - } - return nil -} diff --git a/vendor/github.com/coredns/coredns/plugin/dnstap/handler.go b/vendor/github.com/coredns/coredns/plugin/dnstap/handler.go deleted file mode 100644 index 1178dad79..000000000 --- a/vendor/github.com/coredns/coredns/plugin/dnstap/handler.go +++ /dev/null @@ -1,92 +0,0 @@ -package dnstap - -import ( - "context" - "time" - - "github.com/coredns/coredns/plugin" - "github.com/coredns/coredns/plugin/dnstap/taprw" - - tap "github.com/dnstap/golang-dnstap" - "github.com/miekg/dns" -) - -// Dnstap is the dnstap handler. -type Dnstap struct { - Next plugin.Handler - IO IORoutine - - // Set to true to include the relevant raw DNS message into the dnstap messages. - JoinRawMessage bool -} - -type ( - // IORoutine is the dnstap I/O thread as defined by: . - IORoutine interface { - Dnstap(tap.Dnstap) - } - // Tapper is implemented by the Context passed by the dnstap handler. - Tapper interface { - TapMessage(message *tap.Message) - Pack() bool - } - tapContext struct { - context.Context - Dnstap - } -) - -// ContextKey defines the type of key that is used to save data into the context -type ContextKey string - -const ( - // DnstapSendOption specifies the Dnstap message to be send. Default is sent all. - DnstapSendOption ContextKey = "dnstap-send-option" -) - -// TapMessage implements Tapper. -func (h Dnstap) TapMessage(m *tap.Message) { - t := tap.Dnstap_MESSAGE - h.IO.Dnstap(tap.Dnstap{ - Type: &t, - Message: m, - }) -} - -// Pack returns true if the raw DNS message should be included into the dnstap messages. -func (h Dnstap) Pack() bool { - return h.JoinRawMessage -} - -// ServeDNS logs the client query and response to dnstap and passes the dnstap Context. -func (h Dnstap) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { - - // Add send option into context so other plugin can decide on which DNSTap - // message to be sent out - sendOption := taprw.SendOption{Cq: true, Cr: true} - newCtx := context.WithValue(ctx, DnstapSendOption, &sendOption) - newCtx = ContextWithTapper(newCtx, h) - - rw := &taprw.ResponseWriter{ - ResponseWriter: w, - Tapper: &h, - Query: r, - Send: &sendOption, - QueryEpoch: time.Now(), - } - - code, err := plugin.NextOrFailure(h.Name(), h.Next, newCtx, rw, r) - if err != nil { - // ignore dnstap errors - return code, err - } - - if err = rw.DnstapError(); err != nil { - return code, plugin.Error("dnstap", err) - } - - return code, nil -} - -// Name returns dnstap. -func (h Dnstap) Name() string { return "dnstap" } diff --git a/vendor/github.com/coredns/coredns/plugin/dnstap/msg/msg.go b/vendor/github.com/coredns/coredns/plugin/dnstap/msg/msg.go deleted file mode 100644 index d96fc6c9a..000000000 --- a/vendor/github.com/coredns/coredns/plugin/dnstap/msg/msg.go +++ /dev/null @@ -1,159 +0,0 @@ -package msg - -import ( - "errors" - "net" - "strconv" - "time" - - tap "github.com/dnstap/golang-dnstap" - "github.com/miekg/dns" -) - -// Builder helps to build a Dnstap message. -type Builder struct { - Packed []byte - SocketProto tap.SocketProtocol - SocketFam tap.SocketFamily - Address net.IP - Port uint32 - TimeSec uint64 - TimeNsec uint32 - - err error -} - -// New returns a new Builder -func New() *Builder { - return &Builder{} -} - -// Addr adds the remote address to the message. -func (b *Builder) Addr(remote net.Addr) *Builder { - if b.err != nil { - return b - } - - switch addr := remote.(type) { - case *net.TCPAddr: - b.Address = addr.IP - b.Port = uint32(addr.Port) - b.SocketProto = tap.SocketProtocol_TCP - case *net.UDPAddr: - b.Address = addr.IP - b.Port = uint32(addr.Port) - b.SocketProto = tap.SocketProtocol_UDP - default: - b.err = errors.New("unknown remote address type") - return b - } - - if b.Address.To4() != nil { - b.SocketFam = tap.SocketFamily_INET - } else { - b.SocketFam = tap.SocketFamily_INET6 - } - return b -} - -// Msg adds the raw DNS message to the dnstap message. -func (b *Builder) Msg(m *dns.Msg) *Builder { - if b.err != nil { - return b - } - - b.Packed, b.err = m.Pack() - return b -} - -// HostPort adds the remote address as encoded by dnsutil.ParseHostPortOrFile to the message. -func (b *Builder) HostPort(addr string) *Builder { - ip, port, err := net.SplitHostPort(addr) - if err != nil { - b.err = err - return b - } - p, err := strconv.ParseUint(port, 10, 32) - if err != nil { - b.err = err - return b - } - b.Port = uint32(p) - - if ip := net.ParseIP(ip); ip != nil { - b.Address = []byte(ip) - if ip := ip.To4(); ip != nil { - b.SocketFam = tap.SocketFamily_INET - } else { - b.SocketFam = tap.SocketFamily_INET6 - } - return b - } - b.err = errors.New("not an ip address") - return b -} - -// Time adds the timestamp to the message. -func (b *Builder) Time(ts time.Time) *Builder { - b.TimeSec = uint64(ts.Unix()) - b.TimeNsec = uint32(ts.Nanosecond()) - return b -} - -// ToClientResponse transforms Data into a client response message. -func (b *Builder) ToClientResponse() (*tap.Message, error) { - t := tap.Message_CLIENT_RESPONSE - return &tap.Message{ - Type: &t, - SocketFamily: &b.SocketFam, - SocketProtocol: &b.SocketProto, - ResponseTimeSec: &b.TimeSec, - ResponseTimeNsec: &b.TimeNsec, - ResponseMessage: b.Packed, - QueryAddress: b.Address, - QueryPort: &b.Port, - }, b.err -} - -// ToClientQuery transforms Data into a client query message. -func (b *Builder) ToClientQuery() (*tap.Message, error) { - t := tap.Message_CLIENT_QUERY - return &tap.Message{ - Type: &t, - SocketFamily: &b.SocketFam, - SocketProtocol: &b.SocketProto, - QueryTimeSec: &b.TimeSec, - QueryTimeNsec: &b.TimeNsec, - QueryMessage: b.Packed, - QueryAddress: b.Address, - QueryPort: &b.Port, - }, b.err -} - -// ToOutsideQuery transforms the data into a forwarder or resolver query message. -func (b *Builder) ToOutsideQuery(t tap.Message_Type) (*tap.Message, error) { - return &tap.Message{ - Type: &t, - SocketFamily: &b.SocketFam, - SocketProtocol: &b.SocketProto, - QueryTimeSec: &b.TimeSec, - QueryTimeNsec: &b.TimeNsec, - QueryMessage: b.Packed, - ResponseAddress: b.Address, - ResponsePort: &b.Port, - }, b.err -} - -// ToOutsideResponse transforms the data into a forwarder or resolver response message. -func (b *Builder) ToOutsideResponse(t tap.Message_Type) (*tap.Message, error) { - return &tap.Message{ - Type: &t, - SocketFamily: &b.SocketFam, - SocketProtocol: &b.SocketProto, - ResponseTimeSec: &b.TimeSec, - ResponseTimeNsec: &b.TimeNsec, - ResponseMessage: b.Packed, - ResponseAddress: b.Address, - ResponsePort: &b.Port, - }, b.err -} diff --git a/vendor/github.com/coredns/coredns/plugin/dnstap/setup.go b/vendor/github.com/coredns/coredns/plugin/dnstap/setup.go deleted file mode 100644 index 70896722a..000000000 --- a/vendor/github.com/coredns/coredns/plugin/dnstap/setup.go +++ /dev/null @@ -1,96 +0,0 @@ -package dnstap - -import ( - "strings" - - "github.com/coredns/coredns/core/dnsserver" - "github.com/coredns/coredns/plugin" - "github.com/coredns/coredns/plugin/dnstap/dnstapio" - clog "github.com/coredns/coredns/plugin/pkg/log" - "github.com/coredns/coredns/plugin/pkg/parse" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyfile" -) - -var log = clog.NewWithPlugin("dnstap") - -func init() { - caddy.RegisterPlugin("dnstap", caddy.Plugin{ - ServerType: "dns", - Action: wrapSetup, - }) -} - -func wrapSetup(c *caddy.Controller) error { - if err := setup(c); err != nil { - return plugin.Error("dnstap", err) - } - return nil -} - -type config struct { - target string - socket bool - full bool -} - -func parseConfig(d *caddyfile.Dispenser) (c config, err error) { - d.Next() // directive name - - if !d.Args(&c.target) { - return c, d.ArgErr() - } - - if strings.HasPrefix(c.target, "tcp://") { - // remote IP endpoint - servers, err := parse.HostPortOrFile(c.target[6:]) - if err != nil { - return c, d.ArgErr() - } - c.target = servers[0] - } else { - // default to UNIX socket - if strings.HasPrefix(c.target, "unix://") { - c.target = c.target[7:] - } - c.socket = true - } - - c.full = d.NextArg() && d.Val() == "full" - - return -} - -func setup(c *caddy.Controller) error { - conf, err := parseConfig(&c.Dispenser) - if err != nil { - return err - } - - dio := dnstapio.New(conf.target, conf.socket) - dnstap := Dnstap{IO: dio, JoinRawMessage: conf.full} - - c.OnStartup(func() error { - dio.Connect() - return nil - }) - - c.OnRestart(func() error { - dio.Close() - return nil - }) - - c.OnFinalShutdown(func() error { - dio.Close() - return nil - }) - - dnsserver.GetConfig(c).AddPlugin( - func(next plugin.Handler) plugin.Handler { - dnstap.Next = next - return dnstap - }) - - return nil -} diff --git a/vendor/github.com/coredns/coredns/plugin/dnstap/taprw/writer.go b/vendor/github.com/coredns/coredns/plugin/dnstap/taprw/writer.go deleted file mode 100644 index 06e6c941d..000000000 --- a/vendor/github.com/coredns/coredns/plugin/dnstap/taprw/writer.go +++ /dev/null @@ -1,79 +0,0 @@ -// Package taprw takes a query and intercepts the response. -// It will log both after the response is written. -package taprw - -import ( - "fmt" - "time" - - "github.com/coredns/coredns/plugin/dnstap/msg" - - tap "github.com/dnstap/golang-dnstap" - "github.com/miekg/dns" -) - -// SendOption stores the flag to indicate whether a certain DNSTap message to -// be sent out or not. -type SendOption struct { - Cq bool - Cr bool -} - -// Tapper is what ResponseWriter needs to log to dnstap. -type Tapper interface { - TapMessage(*tap.Message) - Pack() bool -} - -// ResponseWriter captures the client response and logs the query to dnstap. -// Single request use. -// SendOption configures Dnstap to selectively send Dnstap messages. Default is send all. -type ResponseWriter struct { - QueryEpoch time.Time - Query *dns.Msg - dns.ResponseWriter - Tapper - Send *SendOption - - dnstapErr error -} - -// DnstapError check if a dnstap error occurred during Write and returns it. -func (w *ResponseWriter) DnstapError() error { - return w.dnstapErr -} - -// WriteMsg writes back the response to the client and THEN works on logging the request -// and response to dnstap. -func (w *ResponseWriter) WriteMsg(resp *dns.Msg) (writeErr error) { - writeErr = w.ResponseWriter.WriteMsg(resp) - writeEpoch := time.Now() - - b := msg.New().Time(w.QueryEpoch).Addr(w.RemoteAddr()) - - if w.Send == nil || w.Send.Cq { - if w.Pack() { - b.Msg(w.Query) - } - if m, err := b.ToClientQuery(); err != nil { - w.dnstapErr = fmt.Errorf("client query: %s", err) - } else { - w.TapMessage(m) - } - } - - if w.Send == nil || w.Send.Cr { - if writeErr == nil { - if w.Pack() { - b.Msg(resp) - } - if m, err := b.Time(writeEpoch).ToClientResponse(); err != nil { - w.dnstapErr = fmt.Errorf("client response: %s", err) - } else { - w.TapMessage(m) - } - } - } - - return writeErr -} diff --git a/vendor/github.com/coredns/coredns/plugin/metadata/OWNERS b/vendor/github.com/coredns/coredns/plugin/metadata/OWNERS deleted file mode 100644 index 9355c9e98..000000000 --- a/vendor/github.com/coredns/coredns/plugin/metadata/OWNERS +++ /dev/null @@ -1,8 +0,0 @@ -reviewers: - - ekleiner - - fturib - - miekg -approvers: - - ekleiner - - fturib - - miekg diff --git a/vendor/github.com/coredns/coredns/plugin/metadata/README.md b/vendor/github.com/coredns/coredns/plugin/metadata/README.md deleted file mode 100644 index c69650927..000000000 --- a/vendor/github.com/coredns/coredns/plugin/metadata/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# metadata - -## Name - -*metadata* - enable a meta data collector. - -## Description - -By enabling *metadata* any plugin that implements [metadata.Provider -interface](https://godoc.org/github.com/coredns/coredns/plugin/metadata#Provider) will be called for -each DNS query, at beginning of the process for that query, in order to add it's own meta data to -context. - -The meta data collected will be available for all plugins, via the Context parameter provided in the -ServeDNS function. The package (code) documentation has examples on how to inspect and retrieve -metadata a plugin might be interested in. - -The meta data is added by setting a label with a value in the context. These labels should be named -`plugin/NAME`, where **NAME** is something descriptive. The only hard requirement the *metadata* -plugin enforces is that the labels contains a slash. See the documentation for -`metadata.SetValueFunc`. - -The value stored is a string. The empty string signals "no meta data". See the documentation for -`metadata.ValueFunc` on how to retrieve this. - -## Syntax - -~~~ -metadata [ZONES... ] -~~~ - -* **ZONES** zones metadata should be invoked for. - -## Plugins - -`metadata.Provider` interface needs to be implemented by each plugin willing to provide metadata -information for other plugins. It will be called by metadata and gather the information from all -plugins in context. - -Note: this method should work quickly, because it is called for every request. - -## Examples - -The *rewrite* plugin uses meta data to rewrite requests. - -## Also See - -The [Provider interface](https://godoc.org/github.com/coredns/coredns/plugin/metadata#Provider) and -the [package level](https://godoc.org/github.com/coredns/coredns/plugin/metadata) documentation. diff --git a/vendor/github.com/coredns/coredns/plugin/metadata/metadata.go b/vendor/github.com/coredns/coredns/plugin/metadata/metadata.go deleted file mode 100644 index 4abe57ddf..000000000 --- a/vendor/github.com/coredns/coredns/plugin/metadata/metadata.go +++ /dev/null @@ -1,39 +0,0 @@ -package metadata - -import ( - "context" - - "github.com/coredns/coredns/plugin" - "github.com/coredns/coredns/request" - - "github.com/miekg/dns" -) - -// Metadata implements collecting metadata information from all plugins that -// implement the Provider interface. -type Metadata struct { - Zones []string - Providers []Provider - Next plugin.Handler -} - -// Name implements the Handler interface. -func (m *Metadata) Name() string { return "metadata" } - -// ServeDNS implements the plugin.Handler interface. -func (m *Metadata) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { - - ctx = context.WithValue(ctx, key{}, md{}) - - state := request.Request{W: w, Req: r} - if plugin.Zones(m.Zones).Matches(state.Name()) != "" { - // Go through all Providers and collect metadata. - for _, p := range m.Providers { - ctx = p.Metadata(ctx, state) - } - } - - rcode, err := plugin.NextOrFailure(m.Name(), m.Next, ctx, w, r) - - return rcode, err -} diff --git a/vendor/github.com/coredns/coredns/plugin/metadata/provider.go b/vendor/github.com/coredns/coredns/plugin/metadata/provider.go deleted file mode 100644 index b22064200..000000000 --- a/vendor/github.com/coredns/coredns/plugin/metadata/provider.go +++ /dev/null @@ -1,122 +0,0 @@ -// Package metadata provides an API that allows plugins to add metadata to the context. -// Each metadata is stored under a label that has the form /. Each metadata -// is returned as a Func. When Func is called the metadata is returned. If Func is expensive to -// execute it is its responsibility to provide some form of caching. During the handling of a -// query it is expected the metadata stays constant. -// -// Basic example: -// -// Implement the Provider interface for a plugin p: -// -// func (p P) Metadata(ctx context.Context, state request.Request) context.Context { -// metadata.SetValueFunc(ctx, "test/something", func() string { return "myvalue" }) -// return ctx -// } -// -// Basic example with caching: -// -// func (p P) Metadata(ctx context.Context, state request.Request) context.Context { -// cached := "" -// f := func() string { -// if cached != "" { -// return cached -// } -// cached = expensiveFunc() -// return cached -// } -// metadata.SetValueFunc(ctx, "test/something", f) -// return ctx -// } -// -// If you need access to this metadata from another plugin: -// -// // ... -// valueFunc := metadata.ValueFunc(ctx, "test/something") -// value := valueFunc() -// // use 'value' -// -package metadata - -import ( - "context" - "strings" - - "github.com/coredns/coredns/request" -) - -// Provider interface needs to be implemented by each plugin willing to provide -// metadata information for other plugins. -type Provider interface { - // Metadata adds metadata to the context and returns a (potentially) new context. - // Note: this method should work quickly, because it is called for every request - // from the metadata plugin. - Metadata(ctx context.Context, state request.Request) context.Context -} - -// Func is the type of function in the metadata, when called they return the value of the label. -type Func func() string - -// IsLabel checks that the provided name is a valid label name, i.e. two words separated by a slash. -func IsLabel(label string) bool { - p := strings.Index(label, "/") - if p <= 0 || p >= len(label)-1 { - // cannot accept namespace empty nor label empty - return false - } - if strings.LastIndex(label, "/") != p { - // several slash in the Label - return false - } - return true - -} - -// Labels returns all metadata keys stored in the context. These label names should be named -// as: plugin/NAME, where NAME is something descriptive. -func Labels(ctx context.Context) []string { - if metadata := ctx.Value(key{}); metadata != nil { - if m, ok := metadata.(md); ok { - return keys(m) - } - } - return nil -} - -// ValueFunc returns the value function of label. If none can be found nil is returned. Calling the -// function returns the value of the label. -func ValueFunc(ctx context.Context, label string) Func { - if metadata := ctx.Value(key{}); metadata != nil { - if m, ok := metadata.(md); ok { - return m[label] - } - } - return nil -} - -// SetValueFunc set the metadata label to the value function. If no metadata can be found this is a noop and -// false is returned. Any existing value is overwritten. -func SetValueFunc(ctx context.Context, label string, f Func) bool { - if metadata := ctx.Value(key{}); metadata != nil { - if m, ok := metadata.(md); ok { - m[label] = f - return true - } - } - return false -} - -// md is metadata information storage. -type md map[string]Func - -// key defines the type of key that is used to save metadata into the context. -type key struct{} - -func keys(m map[string]Func) []string { - s := make([]string, len(m)) - i := 0 - for k := range m { - s[i] = k - i++ - } - return s -} diff --git a/vendor/github.com/coredns/coredns/plugin/metadata/setup.go b/vendor/github.com/coredns/coredns/plugin/metadata/setup.go deleted file mode 100644 index 282bcf7d9..000000000 --- a/vendor/github.com/coredns/coredns/plugin/metadata/setup.go +++ /dev/null @@ -1,61 +0,0 @@ -package metadata - -import ( - "github.com/coredns/coredns/core/dnsserver" - "github.com/coredns/coredns/plugin" - - "github.com/mholt/caddy" -) - -func init() { - caddy.RegisterPlugin("metadata", caddy.Plugin{ - ServerType: "dns", - Action: setup, - }) -} - -func setup(c *caddy.Controller) error { - m, err := metadataParse(c) - if err != nil { - return err - } - dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { - m.Next = next - return m - }) - - c.OnStartup(func() error { - plugins := dnsserver.GetConfig(c).Handlers() - for _, p := range plugins { - if met, ok := p.(Provider); ok { - m.Providers = append(m.Providers, met) - } - } - return nil - }) - - return nil -} - -func metadataParse(c *caddy.Controller) (*Metadata, error) { - m := &Metadata{} - c.Next() - zones := c.RemainingArgs() - - if len(zones) != 0 { - m.Zones = zones - for i := 0; i < len(m.Zones); i++ { - m.Zones[i] = plugin.Host(m.Zones[i]).Normalize() - } - } else { - m.Zones = make([]string, len(c.ServerBlockKeys)) - for i := 0; i < len(c.ServerBlockKeys); i++ { - m.Zones[i] = plugin.Host(c.ServerBlockKeys[i]).Normalize() - } - } - - if c.NextBlock() || c.Next() { - return nil, plugin.Error("metadata", c.ArgErr()) - } - return m, nil -} diff --git a/vendor/github.com/davecgh/go-spew/LICENSE b/vendor/github.com/davecgh/go-spew/LICENSE index c83641619..bc52e96f2 100644 --- a/vendor/github.com/davecgh/go-spew/LICENSE +++ b/vendor/github.com/davecgh/go-spew/LICENSE @@ -2,7 +2,7 @@ ISC License Copyright (c) 2012-2016 Dave Collins -Permission to use, copy, modify, and distribute this software for any +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. diff --git a/vendor/github.com/davecgh/go-spew/spew/bypass.go b/vendor/github.com/davecgh/go-spew/spew/bypass.go index 8a4a6589a..792994785 100644 --- a/vendor/github.com/davecgh/go-spew/spew/bypass.go +++ b/vendor/github.com/davecgh/go-spew/spew/bypass.go @@ -16,7 +16,9 @@ // when the code is not running on Google App Engine, compiled by GopherJS, and // "-tags safe" is not added to the go build command line. The "disableunsafe" // tag is deprecated and thus should not be used. -// +build !js,!appengine,!safe,!disableunsafe +// Go versions prior to 1.4 are disabled because they use a different layout +// for interfaces which make the implementation of unsafeReflectValue more complex. +// +build !js,!appengine,!safe,!disableunsafe,go1.4 package spew @@ -34,80 +36,49 @@ const ( ptrSize = unsafe.Sizeof((*byte)(nil)) ) +type flag uintptr + var ( - // offsetPtr, offsetScalar, and offsetFlag are the offsets for the - // internal reflect.Value fields. These values are valid before golang - // commit ecccf07e7f9d which changed the format. The are also valid - // after commit 82f48826c6c7 which changed the format again to mirror - // the original format. Code in the init function updates these offsets - // as necessary. - offsetPtr = uintptr(ptrSize) - offsetScalar = uintptr(0) - offsetFlag = uintptr(ptrSize * 2) - - // flagKindWidth and flagKindShift indicate various bits that the - // reflect package uses internally to track kind information. - // - // flagRO indicates whether or not the value field of a reflect.Value is - // read-only. - // - // flagIndir indicates whether the value field of a reflect.Value is - // the actual data or a pointer to the data. - // - // These values are valid before golang commit 90a7c3c86944 which - // changed their positions. Code in the init function updates these - // flags as necessary. - flagKindWidth = uintptr(5) - flagKindShift = uintptr(flagKindWidth - 1) - flagRO = uintptr(1 << 0) - flagIndir = uintptr(1 << 1) + // flagRO indicates whether the value field of a reflect.Value + // is read-only. + flagRO flag + + // flagAddr indicates whether the address of the reflect.Value's + // value may be taken. + flagAddr flag ) -func init() { - // Older versions of reflect.Value stored small integers directly in the - // ptr field (which is named val in the older versions). Versions - // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named - // scalar for this purpose which unfortunately came before the flag - // field, so the offset of the flag field is different for those - // versions. - // - // This code constructs a new reflect.Value from a known small integer - // and checks if the size of the reflect.Value struct indicates it has - // the scalar field. When it does, the offsets are updated accordingly. - vv := reflect.ValueOf(0xf00) - if unsafe.Sizeof(vv) == (ptrSize * 4) { - offsetScalar = ptrSize * 2 - offsetFlag = ptrSize * 3 - } +// flagKindMask holds the bits that make up the kind +// part of the flags field. In all the supported versions, +// it is in the lower 5 bits. +const flagKindMask = flag(0x1f) - // Commit 90a7c3c86944 changed the flag positions such that the low - // order bits are the kind. This code extracts the kind from the flags - // field and ensures it's the correct type. When it's not, the flag - // order has been changed to the newer format, so the flags are updated - // accordingly. - upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag) - upfv := *(*uintptr)(upf) - flagKindMask := uintptr((1<>flagKindShift != uintptr(reflect.Int) { - flagKindShift = 0 - flagRO = 1 << 5 - flagIndir = 1 << 6 - - // Commit adf9b30e5594 modified the flags to separate the - // flagRO flag into two bits which specifies whether or not the - // field is embedded. This causes flagIndir to move over a bit - // and means that flagRO is the combination of either of the - // original flagRO bit and the new bit. - // - // This code detects the change by extracting what used to be - // the indirect bit to ensure it's set. When it's not, the flag - // order has been changed to the newer format, so the flags are - // updated accordingly. - if upfv&flagIndir == 0 { - flagRO = 3 << 5 - flagIndir = 1 << 7 - } +// Different versions of Go have used different +// bit layouts for the flags type. This table +// records the known combinations. +var okFlags = []struct { + ro, addr flag +}{{ + // From Go 1.4 to 1.5 + ro: 1 << 5, + addr: 1 << 7, +}, { + // Up to Go tip. + ro: 1<<5 | 1<<6, + addr: 1 << 8, +}} + +var flagValOffset = func() uintptr { + field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") + if !ok { + panic("reflect.Value has no flag field") } + return field.Offset +}() + +// flagField returns a pointer to the flag field of a reflect.Value. +func flagField(v *reflect.Value) *flag { + return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) } // unsafeReflectValue converts the passed reflect.Value into a one that bypasses @@ -119,34 +90,56 @@ func init() { // This allows us to check for implementations of the Stringer and error // interfaces to be used for pretty printing ordinarily unaddressable and // inaccessible values such as unexported struct fields. -func unsafeReflectValue(v reflect.Value) (rv reflect.Value) { - indirects := 1 - vt := v.Type() - upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr) - rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag)) - if rvf&flagIndir != 0 { - vt = reflect.PtrTo(v.Type()) - indirects++ - } else if offsetScalar != 0 { - // The value is in the scalar field when it's not one of the - // reference types. - switch vt.Kind() { - case reflect.Uintptr: - case reflect.Chan: - case reflect.Func: - case reflect.Map: - case reflect.Ptr: - case reflect.UnsafePointer: - default: - upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + - offsetScalar) - } +func unsafeReflectValue(v reflect.Value) reflect.Value { + if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { + return v } + flagFieldPtr := flagField(&v) + *flagFieldPtr &^= flagRO + *flagFieldPtr |= flagAddr + return v +} - pv := reflect.NewAt(vt, upv) - rv = pv - for i := 0; i < indirects; i++ { - rv = rv.Elem() +// Sanity checks against future reflect package changes +// to the type or semantics of the Value.flag field. +func init() { + field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") + if !ok { + panic("reflect.Value has no flag field") + } + if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { + panic("reflect.Value flag field has changed kind") + } + type t0 int + var t struct { + A t0 + // t0 will have flagEmbedRO set. + t0 + // a will have flagStickyRO set + a t0 + } + vA := reflect.ValueOf(t).FieldByName("A") + va := reflect.ValueOf(t).FieldByName("a") + vt0 := reflect.ValueOf(t).FieldByName("t0") + + // Infer flagRO from the difference between the flags + // for the (otherwise identical) fields in t. + flagPublic := *flagField(&vA) + flagWithRO := *flagField(&va) | *flagField(&vt0) + flagRO = flagPublic ^ flagWithRO + + // Infer flagAddr from the difference between a value + // taken from a pointer and not. + vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") + flagNoPtr := *flagField(&vA) + flagPtr := *flagField(&vPtrA) + flagAddr = flagNoPtr ^ flagPtr + + // Check that the inferred flags tally with one of the known versions. + for _, f := range okFlags { + if flagRO == f.ro && flagAddr == f.addr { + return + } } - return rv + panic("reflect.Value read-only flag has changed semantics") } diff --git a/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go b/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go index 1fe3cf3d5..205c28d68 100644 --- a/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go +++ b/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go @@ -16,7 +16,7 @@ // when the code is running on Google App Engine, compiled by GopherJS, or // "-tags safe" is added to the go build command line. The "disableunsafe" // tag is deprecated and thus should not be used. -// +build js appengine safe disableunsafe +// +build js appengine safe disableunsafe !go1.4 package spew diff --git a/vendor/github.com/davecgh/go-spew/spew/common.go b/vendor/github.com/davecgh/go-spew/spew/common.go index 7c519ff47..1be8ce945 100644 --- a/vendor/github.com/davecgh/go-spew/spew/common.go +++ b/vendor/github.com/davecgh/go-spew/spew/common.go @@ -180,7 +180,7 @@ func printComplex(w io.Writer, c complex128, floatPrecision int) { w.Write(closeParenBytes) } -// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x' +// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x' // prefix to Writer w. func printHexPtr(w io.Writer, p uintptr) { // Null pointer. diff --git a/vendor/github.com/davecgh/go-spew/spew/dump.go b/vendor/github.com/davecgh/go-spew/spew/dump.go index df1d582a7..f78d89fc1 100644 --- a/vendor/github.com/davecgh/go-spew/spew/dump.go +++ b/vendor/github.com/davecgh/go-spew/spew/dump.go @@ -35,16 +35,16 @@ var ( // cCharRE is a regular expression that matches a cgo char. // It is used to detect character arrays to hexdump them. - cCharRE = regexp.MustCompile("^.*\\._Ctype_char$") + cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`) // cUnsignedCharRE is a regular expression that matches a cgo unsigned // char. It is used to detect unsigned character arrays to hexdump // them. - cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$") + cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`) // cUint8tCharRE is a regular expression that matches a cgo uint8_t. // It is used to detect uint8_t arrays to hexdump them. - cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$") + cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`) ) // dumpState contains information about the state of a dump operation. @@ -143,10 +143,10 @@ func (d *dumpState) dumpPtr(v reflect.Value) { // Display dereferenced value. d.w.Write(openParenBytes) switch { - case nilFound == true: + case nilFound: d.w.Write(nilAngleBytes) - case cycleFound == true: + case cycleFound: d.w.Write(circularBytes) default: diff --git a/vendor/github.com/davecgh/go-spew/spew/format.go b/vendor/github.com/davecgh/go-spew/spew/format.go index c49875bac..b04edb7d7 100644 --- a/vendor/github.com/davecgh/go-spew/spew/format.go +++ b/vendor/github.com/davecgh/go-spew/spew/format.go @@ -182,10 +182,10 @@ func (f *formatState) formatPtr(v reflect.Value) { // Display dereferenced value. switch { - case nilFound == true: + case nilFound: f.fs.Write(nilAngleBytes) - case cycleFound == true: + case cycleFound: f.fs.Write(circularShortBytes) default: diff --git a/vendor/github.com/dnstap/golang-dnstap/.gitignore b/vendor/github.com/dnstap/golang-dnstap/.gitignore deleted file mode 100644 index 1377554eb..000000000 --- a/vendor/github.com/dnstap/golang-dnstap/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.swp diff --git a/vendor/github.com/dnstap/golang-dnstap/COPYRIGHT b/vendor/github.com/dnstap/golang-dnstap/COPYRIGHT deleted file mode 100644 index 169e84827..000000000 --- a/vendor/github.com/dnstap/golang-dnstap/COPYRIGHT +++ /dev/null @@ -1,14 +0,0 @@ -Copyright (c) 2013-2014 by Farsight Security, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - diff --git a/vendor/github.com/dnstap/golang-dnstap/FrameStreamInput.go b/vendor/github.com/dnstap/golang-dnstap/FrameStreamInput.go deleted file mode 100644 index 5d58aa5b6..000000000 --- a/vendor/github.com/dnstap/golang-dnstap/FrameStreamInput.go +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2013-2014 by Farsight Security, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dnstap - -import ( - "io" - "log" - "os" - - "github.com/farsightsec/golang-framestream" -) - -type FrameStreamInput struct { - wait chan bool - decoder *framestream.Decoder -} - -func NewFrameStreamInput(r io.ReadWriter, bi bool) (input *FrameStreamInput, err error) { - input = new(FrameStreamInput) - decoderOptions := framestream.DecoderOptions{ - ContentType: FSContentType, - Bidirectional: bi, - } - input.decoder, err = framestream.NewDecoder(r, &decoderOptions) - if err != nil { - return - } - input.wait = make(chan bool) - return -} - -func NewFrameStreamInputFromFilename(fname string) (input *FrameStreamInput, err error) { - file, err := os.Open(fname) - if err != nil { - return nil, err - } - input, err = NewFrameStreamInput(file, false) - return -} - -func (input *FrameStreamInput) ReadInto(output chan []byte) { - for { - buf, err := input.decoder.Decode() - if err != nil { - if err != io.EOF { - log.Printf("framestream.Decoder.Decode() failed: %s\n", err) - } - break - } - newbuf := make([]byte, len(buf)) - copy(newbuf, buf) - output <- newbuf - } - close(input.wait) -} - -func (input *FrameStreamInput) Wait() { - <-input.wait -} diff --git a/vendor/github.com/dnstap/golang-dnstap/FrameStreamOutput.go b/vendor/github.com/dnstap/golang-dnstap/FrameStreamOutput.go deleted file mode 100644 index d00bba892..000000000 --- a/vendor/github.com/dnstap/golang-dnstap/FrameStreamOutput.go +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2014 by Farsight Security, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dnstap - -import ( - "io" - "log" - "os" - - "github.com/farsightsec/golang-framestream" -) - -type FrameStreamOutput struct { - outputChannel chan []byte - wait chan bool - enc *framestream.Encoder -} - -func NewFrameStreamOutput(w io.Writer) (o *FrameStreamOutput, err error) { - o = new(FrameStreamOutput) - o.outputChannel = make(chan []byte, outputChannelSize) - o.enc, err = framestream.NewEncoder(w, &framestream.EncoderOptions{ContentType: FSContentType}) - if err != nil { - return - } - o.wait = make(chan bool) - return -} - -func NewFrameStreamOutputFromFilename(fname string) (o *FrameStreamOutput, err error) { - if fname == "" || fname == "-" { - return NewFrameStreamOutput(os.Stdout) - } - w, err := os.Create(fname) - if err != nil { - return - } - return NewFrameStreamOutput(w) -} - -func (o *FrameStreamOutput) GetOutputChannel() chan []byte { - return o.outputChannel -} - -func (o *FrameStreamOutput) RunOutputLoop() { - for frame := range o.outputChannel { - if _, err := o.enc.Write(frame); err != nil { - log.Fatalf("framestream.Encoder.Write() failed: %s\n", err) - break - } - } - close(o.wait) -} - -func (o *FrameStreamOutput) Close() { - close(o.outputChannel) - <-o.wait - o.enc.Flush() - o.enc.Close() -} diff --git a/vendor/github.com/dnstap/golang-dnstap/FrameStreamSockInput.go b/vendor/github.com/dnstap/golang-dnstap/FrameStreamSockInput.go deleted file mode 100644 index 443e46cdb..000000000 --- a/vendor/github.com/dnstap/golang-dnstap/FrameStreamSockInput.go +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2013-2014 by Farsight Security, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dnstap - -import ( - "log" - "net" - "os" -) - -type FrameStreamSockInput struct { - wait chan bool - listener net.Listener -} - -func NewFrameStreamSockInput(listener net.Listener) (input *FrameStreamSockInput) { - input = new(FrameStreamSockInput) - input.listener = listener - return -} - -func NewFrameStreamSockInputFromPath(socketPath string) (input *FrameStreamSockInput, err error) { - os.Remove(socketPath) - listener, err := net.Listen("unix", socketPath) - if err != nil { - return - } - return NewFrameStreamSockInput(listener), nil -} - -func (input *FrameStreamSockInput) ReadInto(output chan []byte) { - for { - conn, err := input.listener.Accept() - if err != nil { - log.Printf("net.Listener.Accept() failed: %s\n", err) - continue - } - i, err := NewFrameStreamInput(conn, true) - if err != nil { - log.Printf("dnstap.NewFrameStreamInput() failed: %s\n", err) - continue - } - log.Printf("dnstap.FrameStreamSockInput: accepted a socket connection\n") - go i.ReadInto(output) - } -} - -func (input *FrameStreamSockInput) Wait() { - select {} -} diff --git a/vendor/github.com/dnstap/golang-dnstap/LICENSE b/vendor/github.com/dnstap/golang-dnstap/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/vendor/github.com/dnstap/golang-dnstap/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/dnstap/golang-dnstap/QuietTextFormat.go b/vendor/github.com/dnstap/golang-dnstap/QuietTextFormat.go deleted file mode 100644 index 69e6fe8d7..000000000 --- a/vendor/github.com/dnstap/golang-dnstap/QuietTextFormat.go +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2013-2014 by Farsight Security, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dnstap - -import ( - "bytes" - "fmt" - "net" - "strconv" - "time" - - "github.com/miekg/dns" -) - -const quietTimeFormat = "15:04:05" - -func textConvertTime(s *bytes.Buffer, secs *uint64, nsecs *uint32) { - if secs != nil { - s.WriteString(time.Unix(int64(*secs), 0).Format(quietTimeFormat)) - } else { - s.WriteString("??:??:??") - } - if nsecs != nil { - s.WriteString(fmt.Sprintf(".%06d", *nsecs/1000)) - } else { - s.WriteString(".??????") - } -} - -func textConvertIP(s *bytes.Buffer, ip []byte) { - if ip != nil { - s.WriteString(net.IP(ip).String()) - } else { - s.WriteString("MISSING_ADDRESS") - } -} - -func textConvertMessage(m *Message, s *bytes.Buffer) { - isQuery := false - printQueryAddress := false - - switch *m.Type { - case Message_CLIENT_QUERY, - Message_RESOLVER_QUERY, - Message_AUTH_QUERY, - Message_FORWARDER_QUERY, - Message_TOOL_QUERY: - isQuery = true - case Message_CLIENT_RESPONSE, - Message_RESOLVER_RESPONSE, - Message_AUTH_RESPONSE, - Message_FORWARDER_RESPONSE, - Message_TOOL_RESPONSE: - isQuery = false - default: - s.WriteString("[unhandled Message.Type]\n") - return - } - - if isQuery { - textConvertTime(s, m.QueryTimeSec, m.QueryTimeNsec) - } else { - textConvertTime(s, m.ResponseTimeSec, m.ResponseTimeNsec) - } - s.WriteString(" ") - - switch *m.Type { - case Message_CLIENT_QUERY, - Message_CLIENT_RESPONSE: - { - s.WriteString("C") - } - case Message_RESOLVER_QUERY, - Message_RESOLVER_RESPONSE: - { - s.WriteString("R") - } - case Message_AUTH_QUERY, - Message_AUTH_RESPONSE: - { - s.WriteString("A") - } - case Message_FORWARDER_QUERY, - Message_FORWARDER_RESPONSE: - { - s.WriteString("F") - } - case Message_STUB_QUERY, - Message_STUB_RESPONSE: - { - s.WriteString("S") - } - case Message_TOOL_QUERY, - Message_TOOL_RESPONSE: - { - s.WriteString("T") - } - } - - if isQuery { - s.WriteString("Q ") - } else { - s.WriteString("R ") - } - - switch *m.Type { - case Message_CLIENT_QUERY, - Message_CLIENT_RESPONSE, - Message_AUTH_QUERY, - Message_AUTH_RESPONSE: - printQueryAddress = true - } - - if printQueryAddress { - textConvertIP(s, m.QueryAddress) - } else { - textConvertIP(s, m.ResponseAddress) - } - s.WriteString(" ") - - if m.SocketProtocol != nil { - s.WriteString(m.SocketProtocol.String()) - } - s.WriteString(" ") - - var err error - msg := new(dns.Msg) - if isQuery { - s.WriteString(strconv.Itoa(len(m.QueryMessage))) - s.WriteString("b ") - err = msg.Unpack(m.QueryMessage) - } else { - s.WriteString(strconv.Itoa(len(m.ResponseMessage))) - s.WriteString("b ") - err = msg.Unpack(m.ResponseMessage) - } - - if err != nil { - s.WriteString("X ") - } else { - s.WriteString("\"" + msg.Question[0].Name + "\" ") - s.WriteString(dns.Class(msg.Question[0].Qclass).String() + " ") - s.WriteString(dns.Type(msg.Question[0].Qtype).String()) - } - - s.WriteString("\n") -} - -func TextFormat(dt *Dnstap) (out []byte, ok bool) { - var s bytes.Buffer - - if *dt.Type == Dnstap_MESSAGE { - textConvertMessage(dt.Message, &s) - return s.Bytes(), true - } - - return nil, false -} diff --git a/vendor/github.com/dnstap/golang-dnstap/README b/vendor/github.com/dnstap/golang-dnstap/README deleted file mode 100644 index e59effb8d..000000000 --- a/vendor/github.com/dnstap/golang-dnstap/README +++ /dev/null @@ -1,15 +0,0 @@ -dnstap: flexible, structured event replication format for DNS servers ---------------------------------------------------------------------- - -dnstap implements an encoding format for DNS server events. It uses a -lightweight framing on top of event payloads encoded using Protocol Buffers and -is transport neutral. - -dnstap can represent internal state inside a DNS server that is difficult to -obtain using techniques based on traditional packet capture or unstructured -textual format logging. - -This repository contains a command-line tool named "dnstap" developed in the -Go programming language. It can be installed with the following command: - - go get -u github.com/dnstap/golang-dnstap/dnstap diff --git a/vendor/github.com/dnstap/golang-dnstap/TextOutput.go b/vendor/github.com/dnstap/golang-dnstap/TextOutput.go deleted file mode 100644 index 5fa2c00e0..000000000 --- a/vendor/github.com/dnstap/golang-dnstap/TextOutput.go +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2014 by Farsight Security, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dnstap - -import ( - "bufio" - "io" - "log" - "os" - - "github.com/golang/protobuf/proto" -) - -type TextFormatFunc func(*Dnstap) ([]byte, bool) - -type TextOutput struct { - format TextFormatFunc - outputChannel chan []byte - wait chan bool - writer *bufio.Writer -} - -func NewTextOutput(writer io.Writer, format TextFormatFunc) (o *TextOutput) { - o = new(TextOutput) - o.format = format - o.outputChannel = make(chan []byte, outputChannelSize) - o.writer = bufio.NewWriter(writer) - o.wait = make(chan bool) - return -} - -func NewTextOutputFromFilename(fname string, format TextFormatFunc) (o *TextOutput, err error) { - if fname == "" || fname == "-" { - return NewTextOutput(os.Stdout, format), nil - } - writer, err := os.Create(fname) - if err != nil { - return - } - return NewTextOutput(writer, format), nil -} - -func (o *TextOutput) GetOutputChannel() chan []byte { - return o.outputChannel -} - -func (o *TextOutput) RunOutputLoop() { - dt := &Dnstap{} - for frame := range o.outputChannel { - if err := proto.Unmarshal(frame, dt); err != nil { - log.Fatalf("dnstap.TextOutput: proto.Unmarshal() failed: %s\n", err) - break - } - buf, ok := o.format(dt) - if !ok { - log.Fatalf("dnstap.TextOutput: text format function failed\n") - break - } - if _, err := o.writer.Write(buf); err != nil { - log.Fatalf("dnstap.TextOutput: write failed: %s\n", err) - break - } - o.writer.Flush() - } - close(o.wait) -} - -func (o *TextOutput) Close() { - close(o.outputChannel) - <-o.wait - o.writer.Flush() -} diff --git a/vendor/github.com/dnstap/golang-dnstap/YamlFormat.go b/vendor/github.com/dnstap/golang-dnstap/YamlFormat.go deleted file mode 100644 index c63a4a3a1..000000000 --- a/vendor/github.com/dnstap/golang-dnstap/YamlFormat.go +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2013-2014 by Farsight Security, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dnstap - -import ( - "bytes" - "fmt" - "net" - "strconv" - "strings" - "time" - - "github.com/miekg/dns" -) - -const yamlTimeFormat = "2006-01-02 15:04:05.999999999" - -func yamlConvertMessage(m *Message, s *bytes.Buffer) { - s.WriteString(fmt.Sprint(" type: ", m.Type, "\n")) - - if m.QueryTimeSec != nil && m.QueryTimeNsec != nil { - t := time.Unix(int64(*m.QueryTimeSec), int64(*m.QueryTimeNsec)).UTC() - s.WriteString(fmt.Sprint(" query_time: !!timestamp ", t.Format(yamlTimeFormat), "\n")) - } - - if m.ResponseTimeSec != nil && m.ResponseTimeNsec != nil { - t := time.Unix(int64(*m.ResponseTimeSec), int64(*m.ResponseTimeNsec)).UTC() - s.WriteString(fmt.Sprint(" response_time: !!timestamp ", t.Format(yamlTimeFormat), "\n")) - } - - if m.SocketFamily != nil { - s.WriteString(fmt.Sprint(" socket_family: ", m.SocketFamily, "\n")) - } - - if m.SocketProtocol != nil { - s.WriteString(fmt.Sprint(" socket_protocol: ", m.SocketProtocol, "\n")) - } - - if m.QueryAddress != nil { - s.WriteString(fmt.Sprint(" query_address: ", net.IP(m.QueryAddress), "\n")) - } - - if m.ResponseAddress != nil { - s.WriteString(fmt.Sprint(" response_address: ", net.IP(m.ResponseAddress), "\n")) - } - - if m.QueryPort != nil { - s.WriteString(fmt.Sprint(" query_port: ", *m.QueryPort, "\n")) - } - - if m.ResponsePort != nil { - s.WriteString(fmt.Sprint(" response_port: ", *m.ResponsePort, "\n")) - } - - if m.QueryZone != nil { - name, _, err := dns.UnpackDomainName(m.QueryZone, 0) - if err != nil { - s.WriteString(" # query_zone: parse failed\n") - } else { - s.WriteString(fmt.Sprint(" query_zone: ", strconv.Quote(name), "\n")) - } - } - - if m.QueryMessage != nil { - msg := new(dns.Msg) - err := msg.Unpack(m.QueryMessage) - if err != nil { - s.WriteString(" # query_message: parse failed\n") - } else { - s.WriteString(" query_message: |\n") - s.WriteString(" " + strings.Replace(strings.TrimSpace(msg.String()), "\n", "\n ", -1) + "\n") - } - } - if m.ResponseMessage != nil { - msg := new(dns.Msg) - err := msg.Unpack(m.ResponseMessage) - if err != nil { - s.WriteString(fmt.Sprint(" # response_message: parse failed: ", err, "\n")) - } else { - s.WriteString(" response_message: |\n") - s.WriteString(" " + strings.Replace(strings.TrimSpace(msg.String()), "\n", "\n ", -1) + "\n") - } - } - s.WriteString("---\n") -} - -func YamlFormat(dt *Dnstap) (out []byte, ok bool) { - var s bytes.Buffer - - s.WriteString(fmt.Sprint("type: ", dt.Type, "\n")) - if dt.Identity != nil { - s.WriteString(fmt.Sprint("identity: ", strconv.Quote(string(dt.Identity)), "\n")) - } - if dt.Version != nil { - s.WriteString(fmt.Sprint("version: ", strconv.Quote(string(dt.Version)), "\n")) - } - if *dt.Type == Dnstap_MESSAGE { - s.WriteString("message:\n") - yamlConvertMessage(dt.Message, &s) - } - return s.Bytes(), true -} diff --git a/vendor/github.com/dnstap/golang-dnstap/debian/copyright b/vendor/github.com/dnstap/golang-dnstap/debian/copyright deleted file mode 100644 index fdeb5b208..000000000 --- a/vendor/github.com/dnstap/golang-dnstap/debian/copyright +++ /dev/null @@ -1,26 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: golang-dnstap -Source: - -Files: * -Copyright: Copyright (c) 2013, 2014, 2016, 2017 by Farsight Security, Inc. -License: Apache-2.0 - -Files: debian/* -Copyright: 2018 Farsight Security, Inc. -License: Apache-2.0 - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - . - https://www.apache.org/licenses/LICENSE-2.0 - . - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - . - On Debian systems, the complete text of the Apache version 2.0 license - can be found in "/usr/share/common-licenses/Apache-2.0". - diff --git a/vendor/github.com/dnstap/golang-dnstap/dnstap.go b/vendor/github.com/dnstap/golang-dnstap/dnstap.go deleted file mode 100644 index d4f786ae7..000000000 --- a/vendor/github.com/dnstap/golang-dnstap/dnstap.go +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2014 by Farsight Security, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dnstap - -const outputChannelSize = 32 - -var FSContentType = []byte("protobuf:dnstap.Dnstap") - -type Input interface { - ReadInto(chan []byte) - Wait() -} - -type Output interface { - GetOutputChannel() chan []byte - RunOutputLoop() - Close() -} diff --git a/vendor/github.com/dnstap/golang-dnstap/dnstap.pb.go b/vendor/github.com/dnstap/golang-dnstap/dnstap.pb.go deleted file mode 100644 index 43b5fd10a..000000000 --- a/vendor/github.com/dnstap/golang-dnstap/dnstap.pb.go +++ /dev/null @@ -1,448 +0,0 @@ -// Code generated by protoc-gen-go. -// source: dnstap.proto -// DO NOT EDIT! - -/* -Package dnstap is a generated protocol buffer package. - -It is generated from these files: - dnstap.proto - -It has these top-level messages: - Dnstap - Message -*/ -package dnstap - -import proto "github.com/golang/protobuf/proto" -import json "encoding/json" -import math "math" - -// Reference proto, json, and math imports to suppress error if they are not otherwise used. -var _ = proto.Marshal -var _ = &json.SyntaxError{} -var _ = math.Inf - -// SocketFamily: the network protocol family of a socket. This specifies how -// to interpret "network address" fields. -type SocketFamily int32 - -const ( - SocketFamily_INET SocketFamily = 1 - SocketFamily_INET6 SocketFamily = 2 -) - -var SocketFamily_name = map[int32]string{ - 1: "INET", - 2: "INET6", -} -var SocketFamily_value = map[string]int32{ - "INET": 1, - "INET6": 2, -} - -func (x SocketFamily) Enum() *SocketFamily { - p := new(SocketFamily) - *p = x - return p -} -func (x SocketFamily) String() string { - return proto.EnumName(SocketFamily_name, int32(x)) -} -func (x *SocketFamily) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(SocketFamily_value, data, "SocketFamily") - if err != nil { - return err - } - *x = SocketFamily(value) - return nil -} - -// SocketProtocol: the transport protocol of a socket. This specifies how to -// interpret "transport port" fields. -type SocketProtocol int32 - -const ( - SocketProtocol_UDP SocketProtocol = 1 - SocketProtocol_TCP SocketProtocol = 2 -) - -var SocketProtocol_name = map[int32]string{ - 1: "UDP", - 2: "TCP", -} -var SocketProtocol_value = map[string]int32{ - "UDP": 1, - "TCP": 2, -} - -func (x SocketProtocol) Enum() *SocketProtocol { - p := new(SocketProtocol) - *p = x - return p -} -func (x SocketProtocol) String() string { - return proto.EnumName(SocketProtocol_name, int32(x)) -} -func (x *SocketProtocol) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(SocketProtocol_value, data, "SocketProtocol") - if err != nil { - return err - } - *x = SocketProtocol(value) - return nil -} - -// Identifies which field below is filled in. -type Dnstap_Type int32 - -const ( - Dnstap_MESSAGE Dnstap_Type = 1 -) - -var Dnstap_Type_name = map[int32]string{ - 1: "MESSAGE", -} -var Dnstap_Type_value = map[string]int32{ - "MESSAGE": 1, -} - -func (x Dnstap_Type) Enum() *Dnstap_Type { - p := new(Dnstap_Type) - *p = x - return p -} -func (x Dnstap_Type) String() string { - return proto.EnumName(Dnstap_Type_name, int32(x)) -} -func (x *Dnstap_Type) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(Dnstap_Type_value, data, "Dnstap_Type") - if err != nil { - return err - } - *x = Dnstap_Type(value) - return nil -} - -type Message_Type int32 - -const ( - // AUTH_QUERY is a DNS query message received from a resolver by an - // authoritative name server, from the perspective of the authorative - // name server. - Message_AUTH_QUERY Message_Type = 1 - // AUTH_RESPONSE is a DNS response message sent from an authoritative - // name server to a resolver, from the perspective of the authoritative - // name server. - Message_AUTH_RESPONSE Message_Type = 2 - // RESOLVER_QUERY is a DNS query message sent from a resolver to an - // authoritative name server, from the perspective of the resolver. - // Resolvers typically clear the RD (recursion desired) bit when - // sending queries. - Message_RESOLVER_QUERY Message_Type = 3 - // RESOLVER_RESPONSE is a DNS response message received from an - // authoritative name server by a resolver, from the perspective of - // the resolver. - Message_RESOLVER_RESPONSE Message_Type = 4 - // CLIENT_QUERY is a DNS query message sent from a client to a DNS - // server which is expected to perform further recursion, from the - // perspective of the DNS server. The client may be a stub resolver or - // forwarder or some other type of software which typically sets the RD - // (recursion desired) bit when querying the DNS server. The DNS server - // may be a simple forwarding proxy or it may be a full recursive - // resolver. - Message_CLIENT_QUERY Message_Type = 5 - // CLIENT_RESPONSE is a DNS response message sent from a DNS server to - // a client, from the perspective of the DNS server. The DNS server - // typically sets the RA (recursion available) bit when responding. - Message_CLIENT_RESPONSE Message_Type = 6 - // FORWARDER_QUERY is a DNS query message sent from a downstream DNS - // server to an upstream DNS server which is expected to perform - // further recursion, from the perspective of the downstream DNS - // server. - Message_FORWARDER_QUERY Message_Type = 7 - // FORWARDER_RESPONSE is a DNS response message sent from an upstream - // DNS server performing recursion to a downstream DNS server, from the - // perspective of the downstream DNS server. - Message_FORWARDER_RESPONSE Message_Type = 8 - // STUB_QUERY is a DNS query message sent from a stub resolver to a DNS - // server, from the perspective of the stub resolver. - Message_STUB_QUERY Message_Type = 9 - // STUB_RESPONSE is a DNS response message sent from a DNS server to a - // stub resolver, from the perspective of the stub resolver. - Message_STUB_RESPONSE Message_Type = 10 - // TOOL_QUERY is a DNS query message sent from a DNS software tool to a - // DNS server, from the perspective of the tool. - Message_TOOL_QUERY Message_Type = 11 - // TOOL_RESPONSE is a DNS response message received by a DNS software - // tool from a DNS server, from the perspective of the tool. - Message_TOOL_RESPONSE Message_Type = 12 -) - -var Message_Type_name = map[int32]string{ - 1: "AUTH_QUERY", - 2: "AUTH_RESPONSE", - 3: "RESOLVER_QUERY", - 4: "RESOLVER_RESPONSE", - 5: "CLIENT_QUERY", - 6: "CLIENT_RESPONSE", - 7: "FORWARDER_QUERY", - 8: "FORWARDER_RESPONSE", - 9: "STUB_QUERY", - 10: "STUB_RESPONSE", - 11: "TOOL_QUERY", - 12: "TOOL_RESPONSE", -} -var Message_Type_value = map[string]int32{ - "AUTH_QUERY": 1, - "AUTH_RESPONSE": 2, - "RESOLVER_QUERY": 3, - "RESOLVER_RESPONSE": 4, - "CLIENT_QUERY": 5, - "CLIENT_RESPONSE": 6, - "FORWARDER_QUERY": 7, - "FORWARDER_RESPONSE": 8, - "STUB_QUERY": 9, - "STUB_RESPONSE": 10, - "TOOL_QUERY": 11, - "TOOL_RESPONSE": 12, -} - -func (x Message_Type) Enum() *Message_Type { - p := new(Message_Type) - *p = x - return p -} -func (x Message_Type) String() string { - return proto.EnumName(Message_Type_name, int32(x)) -} -func (x *Message_Type) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(Message_Type_value, data, "Message_Type") - if err != nil { - return err - } - *x = Message_Type(value) - return nil -} - -// "Dnstap": this is the top-level dnstap type, which is a "union" type that -// contains other kinds of dnstap payloads, although currently only one type -// of dnstap payload is defined. -// See: https://developers.google.com/protocol-buffers/docs/techniques#union -type Dnstap struct { - // DNS server identity. - // If enabled, this is the identity string of the DNS server which generated - // this message. Typically this would be the same string as returned by an - // "NSID" (RFC 5001) query. - Identity []byte `protobuf:"bytes,1,opt,name=identity" json:"identity,omitempty"` - // DNS server version. - // If enabled, this is the version string of the DNS server which generated - // this message. Typically this would be the same string as returned by a - // "version.bind" query. - Version []byte `protobuf:"bytes,2,opt,name=version" json:"version,omitempty"` - // Extra data for this payload. - // This field can be used for adding an arbitrary byte-string annotation to - // the payload. No encoding or interpretation is applied or enforced. - Extra []byte `protobuf:"bytes,3,opt,name=extra" json:"extra,omitempty"` - Type *Dnstap_Type `protobuf:"varint,15,req,name=type,enum=dnstap.Dnstap_Type" json:"type,omitempty"` - // One of the following will be filled in. - Message *Message `protobuf:"bytes,14,opt,name=message" json:"message,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *Dnstap) Reset() { *m = Dnstap{} } -func (m *Dnstap) String() string { return proto.CompactTextString(m) } -func (*Dnstap) ProtoMessage() {} - -func (m *Dnstap) GetIdentity() []byte { - if m != nil { - return m.Identity - } - return nil -} - -func (m *Dnstap) GetVersion() []byte { - if m != nil { - return m.Version - } - return nil -} - -func (m *Dnstap) GetExtra() []byte { - if m != nil { - return m.Extra - } - return nil -} - -func (m *Dnstap) GetType() Dnstap_Type { - if m != nil && m.Type != nil { - return *m.Type - } - return Dnstap_MESSAGE -} - -func (m *Dnstap) GetMessage() *Message { - if m != nil { - return m.Message - } - return nil -} - -// Message: a wire-format (RFC 1035 section 4) DNS message and associated -// metadata. Applications generating "Message" payloads should follow -// certain requirements based on the MessageType, see below. -type Message struct { - // One of the Type values described above. - Type *Message_Type `protobuf:"varint,1,req,name=type,enum=dnstap.Message_Type" json:"type,omitempty"` - // One of the SocketFamily values described above. - SocketFamily *SocketFamily `protobuf:"varint,2,opt,name=socket_family,enum=dnstap.SocketFamily" json:"socket_family,omitempty"` - // One of the SocketProtocol values described above. - SocketProtocol *SocketProtocol `protobuf:"varint,3,opt,name=socket_protocol,enum=dnstap.SocketProtocol" json:"socket_protocol,omitempty"` - // The network address of the message initiator. - // For SocketFamily INET, this field is 4 octets (IPv4 address). - // For SocketFamily INET6, this field is 16 octets (IPv6 address). - QueryAddress []byte `protobuf:"bytes,4,opt,name=query_address" json:"query_address,omitempty"` - // The network address of the message responder. - // For SocketFamily INET, this field is 4 octets (IPv4 address). - // For SocketFamily INET6, this field is 16 octets (IPv6 address). - ResponseAddress []byte `protobuf:"bytes,5,opt,name=response_address" json:"response_address,omitempty"` - // The transport port of the message initiator. - // This is a 16-bit UDP or TCP port number, depending on SocketProtocol. - QueryPort *uint32 `protobuf:"varint,6,opt,name=query_port" json:"query_port,omitempty"` - // The transport port of the message responder. - // This is a 16-bit UDP or TCP port number, depending on SocketProtocol. - ResponsePort *uint32 `protobuf:"varint,7,opt,name=response_port" json:"response_port,omitempty"` - // The time at which the DNS query message was sent or received, depending - // on whether this is an AUTH_QUERY, RESOLVER_QUERY, or CLIENT_QUERY. - // This is the number of seconds since the UNIX epoch. - QueryTimeSec *uint64 `protobuf:"varint,8,opt,name=query_time_sec" json:"query_time_sec,omitempty"` - // The time at which the DNS query message was sent or received. - // This is the seconds fraction, expressed as a count of nanoseconds. - QueryTimeNsec *uint32 `protobuf:"fixed32,9,opt,name=query_time_nsec" json:"query_time_nsec,omitempty"` - // The initiator's original wire-format DNS query message, verbatim. - QueryMessage []byte `protobuf:"bytes,10,opt,name=query_message" json:"query_message,omitempty"` - // The "zone" or "bailiwick" pertaining to the DNS query message. - // This is a wire-format DNS domain name. - QueryZone []byte `protobuf:"bytes,11,opt,name=query_zone" json:"query_zone,omitempty"` - // The time at which the DNS response message was sent or received, - // depending on whether this is an AUTH_RESPONSE, RESOLVER_RESPONSE, or - // CLIENT_RESPONSE. - // This is the number of seconds since the UNIX epoch. - ResponseTimeSec *uint64 `protobuf:"varint,12,opt,name=response_time_sec" json:"response_time_sec,omitempty"` - // The time at which the DNS response message was sent or received. - // This is the seconds fraction, expressed as a count of nanoseconds. - ResponseTimeNsec *uint32 `protobuf:"fixed32,13,opt,name=response_time_nsec" json:"response_time_nsec,omitempty"` - // The responder's original wire-format DNS response message, verbatim. - ResponseMessage []byte `protobuf:"bytes,14,opt,name=response_message" json:"response_message,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *Message) Reset() { *m = Message{} } -func (m *Message) String() string { return proto.CompactTextString(m) } -func (*Message) ProtoMessage() {} - -func (m *Message) GetType() Message_Type { - if m != nil && m.Type != nil { - return *m.Type - } - return Message_AUTH_QUERY -} - -func (m *Message) GetSocketFamily() SocketFamily { - if m != nil && m.SocketFamily != nil { - return *m.SocketFamily - } - return SocketFamily_INET -} - -func (m *Message) GetSocketProtocol() SocketProtocol { - if m != nil && m.SocketProtocol != nil { - return *m.SocketProtocol - } - return SocketProtocol_UDP -} - -func (m *Message) GetQueryAddress() []byte { - if m != nil { - return m.QueryAddress - } - return nil -} - -func (m *Message) GetResponseAddress() []byte { - if m != nil { - return m.ResponseAddress - } - return nil -} - -func (m *Message) GetQueryPort() uint32 { - if m != nil && m.QueryPort != nil { - return *m.QueryPort - } - return 0 -} - -func (m *Message) GetResponsePort() uint32 { - if m != nil && m.ResponsePort != nil { - return *m.ResponsePort - } - return 0 -} - -func (m *Message) GetQueryTimeSec() uint64 { - if m != nil && m.QueryTimeSec != nil { - return *m.QueryTimeSec - } - return 0 -} - -func (m *Message) GetQueryTimeNsec() uint32 { - if m != nil && m.QueryTimeNsec != nil { - return *m.QueryTimeNsec - } - return 0 -} - -func (m *Message) GetQueryMessage() []byte { - if m != nil { - return m.QueryMessage - } - return nil -} - -func (m *Message) GetQueryZone() []byte { - if m != nil { - return m.QueryZone - } - return nil -} - -func (m *Message) GetResponseTimeSec() uint64 { - if m != nil && m.ResponseTimeSec != nil { - return *m.ResponseTimeSec - } - return 0 -} - -func (m *Message) GetResponseTimeNsec() uint32 { - if m != nil && m.ResponseTimeNsec != nil { - return *m.ResponseTimeNsec - } - return 0 -} - -func (m *Message) GetResponseMessage() []byte { - if m != nil { - return m.ResponseMessage - } - return nil -} - -func init() { - proto.RegisterEnum("dnstap.SocketFamily", SocketFamily_name, SocketFamily_value) - proto.RegisterEnum("dnstap.SocketProtocol", SocketProtocol_name, SocketProtocol_value) - proto.RegisterEnum("dnstap.Dnstap_Type", Dnstap_Type_name, Dnstap_Type_value) - proto.RegisterEnum("dnstap.Message_Type", Message_Type_name, Message_Type_value) -} diff --git a/vendor/github.com/dnstap/golang-dnstap/dnstap.pb/LICENSE b/vendor/github.com/dnstap/golang-dnstap/dnstap.pb/LICENSE deleted file mode 100644 index 0e259d42c..000000000 --- a/vendor/github.com/dnstap/golang-dnstap/dnstap.pb/LICENSE +++ /dev/null @@ -1,121 +0,0 @@ -Creative Commons Legal Code - -CC0 1.0 Universal - - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS - PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM - THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED - HEREUNDER. - -Statement of Purpose - -The laws of most jurisdictions throughout the world automatically confer -exclusive Copyright and Related Rights (defined below) upon the creator -and subsequent owner(s) (each and all, an "owner") of an original work of -authorship and/or a database (each, a "Work"). - -Certain owners wish to permanently relinquish those rights to a Work for -the purpose of contributing to a commons of creative, cultural and -scientific works ("Commons") that the public can reliably and without fear -of later claims of infringement build upon, modify, incorporate in other -works, reuse and redistribute as freely as possible in any form whatsoever -and for any purposes, including without limitation commercial purposes. -These owners may contribute to the Commons to promote the ideal of a free -culture and the further production of creative, cultural and scientific -works, or to gain reputation or greater distribution for their Work in -part through the use and efforts of others. - -For these and/or other purposes and motivations, and without any -expectation of additional consideration or compensation, the person -associating CC0 with a Work (the "Affirmer"), to the extent that he or she -is an owner of Copyright and Related Rights in the Work, voluntarily -elects to apply CC0 to the Work and publicly distribute the Work under its -terms, with knowledge of his or her Copyright and Related Rights in the -Work and the meaning and intended legal effect of CC0 on those rights. - -1. Copyright and Related Rights. A Work made available under CC0 may be -protected by copyright and related or neighboring rights ("Copyright and -Related Rights"). Copyright and Related Rights include, but are not -limited to, the following: - - i. the right to reproduce, adapt, distribute, perform, display, - communicate, and translate a Work; - ii. moral rights retained by the original author(s) and/or performer(s); -iii. publicity and privacy rights pertaining to a person's image or - likeness depicted in a Work; - iv. rights protecting against unfair competition in regards to a Work, - subject to the limitations in paragraph 4(a), below; - v. rights protecting the extraction, dissemination, use and reuse of data - in a Work; - vi. database rights (such as those arising under Directive 96/9/EC of the - European Parliament and of the Council of 11 March 1996 on the legal - protection of databases, and under any national implementation - thereof, including any amended or successor version of such - directive); and -vii. other similar, equivalent or corresponding rights throughout the - world based on applicable law or treaty, and any national - implementations thereof. - -2. Waiver. To the greatest extent permitted by, but not in contravention -of, applicable law, Affirmer hereby overtly, fully, permanently, -irrevocably and unconditionally waives, abandons, and surrenders all of -Affirmer's Copyright and Related Rights and associated claims and causes -of action, whether now known or unknown (including existing as well as -future claims and causes of action), in the Work (i) in all territories -worldwide, (ii) for the maximum duration provided by applicable law or -treaty (including future time extensions), (iii) in any current or future -medium and for any number of copies, and (iv) for any purpose whatsoever, -including without limitation commercial, advertising or promotional -purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each -member of the public at large and to the detriment of Affirmer's heirs and -successors, fully intending that such Waiver shall not be subject to -revocation, rescission, cancellation, termination, or any other legal or -equitable action to disrupt the quiet enjoyment of the Work by the public -as contemplated by Affirmer's express Statement of Purpose. - -3. Public License Fallback. Should any part of the Waiver for any reason -be judged legally invalid or ineffective under applicable law, then the -Waiver shall be preserved to the maximum extent permitted taking into -account Affirmer's express Statement of Purpose. In addition, to the -extent the Waiver is so judged Affirmer hereby grants to each affected -person a royalty-free, non transferable, non sublicensable, non exclusive, -irrevocable and unconditional license to exercise Affirmer's Copyright and -Related Rights in the Work (i) in all territories worldwide, (ii) for the -maximum duration provided by applicable law or treaty (including future -time extensions), (iii) in any current or future medium and for any number -of copies, and (iv) for any purpose whatsoever, including without -limitation commercial, advertising or promotional purposes (the -"License"). The License shall be deemed effective as of the date CC0 was -applied by Affirmer to the Work. Should any part of the License for any -reason be judged legally invalid or ineffective under applicable law, such -partial invalidity or ineffectiveness shall not invalidate the remainder -of the License, and in such case Affirmer hereby affirms that he or she -will not (i) exercise any of his or her remaining Copyright and Related -Rights in the Work or (ii) assert any associated claims and causes of -action with respect to the Work, in either case contrary to Affirmer's -express Statement of Purpose. - -4. Limitations and Disclaimers. - - a. No trademark or patent rights held by Affirmer are waived, abandoned, - surrendered, licensed or otherwise affected by this document. - b. Affirmer offers the Work as-is and makes no representations or - warranties of any kind concerning the Work, express, implied, - statutory or otherwise, including without limitation warranties of - title, merchantability, fitness for a particular purpose, non - infringement, or the absence of latent or other defects, accuracy, or - the present or absence of errors, whether or not discoverable, all to - the greatest extent permissible under applicable law. - c. Affirmer disclaims responsibility for clearing rights of other persons - that may apply to the Work or any use thereof, including without - limitation any person's Copyright and Related Rights in the Work. - Further, Affirmer disclaims responsibility for obtaining any necessary - consents, permissions or other rights required for any use of the - Work. - d. Affirmer understands and acknowledges that Creative Commons is not a - party to this document and has no duty or obligation with respect to - this CC0 or use of the Work. diff --git a/vendor/github.com/farsightsec/golang-framestream/.gitignore b/vendor/github.com/farsightsec/golang-framestream/.gitignore deleted file mode 100644 index 9e8224b7f..000000000 --- a/vendor/github.com/farsightsec/golang-framestream/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.*swp diff --git a/vendor/github.com/farsightsec/golang-framestream/COPYRIGHT b/vendor/github.com/farsightsec/golang-framestream/COPYRIGHT deleted file mode 100644 index a11f18efa..000000000 --- a/vendor/github.com/farsightsec/golang-framestream/COPYRIGHT +++ /dev/null @@ -1,13 +0,0 @@ -Copyright (c) 2014 by Farsight Security, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/github.com/farsightsec/golang-framestream/Control.go b/vendor/github.com/farsightsec/golang-framestream/Control.go deleted file mode 100644 index 6345b2c26..000000000 --- a/vendor/github.com/farsightsec/golang-framestream/Control.go +++ /dev/null @@ -1,156 +0,0 @@ -package framestream - -import ( - "bufio" - "bytes" - "encoding/binary" - "io" -) - -const CONTROL_ACCEPT = 0x01 -const CONTROL_START = 0x02 -const CONTROL_STOP = 0x03 -const CONTROL_READY = 0x04 -const CONTROL_FINISH = 0x05 - -const CONTROL_FIELD_CONTENT_TYPE = 0x01 - -type ControlFrame struct { - ControlType uint32 - ContentTypes [][]byte -} - -var ControlStart = ControlFrame{ControlType: CONTROL_START} -var ControlStop = ControlFrame{ControlType: CONTROL_STOP} -var ControlReady = ControlFrame{ControlType: CONTROL_READY} -var ControlAccept = ControlFrame{ControlType: CONTROL_ACCEPT} -var ControlFinish = ControlFrame{ControlType: CONTROL_FINISH} - -func (c *ControlFrame) Encode(w io.Writer) (err error) { - var buf bytes.Buffer - err = binary.Write(&buf, binary.BigEndian, c.ControlType) - if err != nil { - return - } - for _, ctype := range c.ContentTypes { - err = binary.Write(&buf, binary.BigEndian, uint32(CONTROL_FIELD_CONTENT_TYPE)) - if err != nil { - return - } - - err = binary.Write(&buf, binary.BigEndian, uint32(len(ctype))) - if err != nil { - return - } - - _, err = buf.Write(ctype) - if err != nil { - return - } - } - - err = binary.Write(w, binary.BigEndian, uint32(0)) - if err != nil { - return - } - err = binary.Write(w, binary.BigEndian, uint32(buf.Len())) - if err != nil { - return - } - _, err = buf.WriteTo(w) - return -} - -func (c *ControlFrame) EncodeFlush(w *bufio.Writer) error { - if err := c.Encode(w); err != nil { - return err - } - return w.Flush() -} - -func (c *ControlFrame) Decode(r io.Reader) (err error) { - var cflen uint32 - err = binary.Read(r, binary.BigEndian, &cflen) - if err != nil { - return - } - - err = binary.Read(r, binary.BigEndian, &c.ControlType) - if err != nil { - return - } - - cflen -= 4 - if cflen > 0 { - cfields := make([]byte, int(cflen)) - _, err = io.ReadFull(r, cfields) - if err != nil { - return - } - - for len(cfields) > 8 { - cftype := binary.BigEndian.Uint32(cfields[:4]) - cfields = cfields[4:] - if cftype != CONTROL_FIELD_CONTENT_TYPE { - return ErrDecode - } - - cflen := int(binary.BigEndian.Uint32(cfields[:4])) - cfields = cfields[4:] - if cflen > len(cfields) { - return ErrDecode - } - - c.ContentTypes = append(c.ContentTypes, cfields[:cflen]) - cfields = cfields[cflen:] - } - - if len(cfields) > 0 { - return ErrDecode - } - } - return -} - -func (c *ControlFrame) DecodeEscape(r io.Reader) error { - var zero uint32 - err := binary.Read(r, binary.BigEndian, &zero) - if err != nil { - return err - } - if zero != 0 { - return ErrDecode - } - return c.Decode(r) -} - -func (c *ControlFrame) DecodeTypeEscape(r io.Reader, ctype uint32) error { - err := c.DecodeEscape(r) - if err != nil { - return err - } - - if ctype != c.ControlType { - return ErrDecode - } - - return nil -} - -func (c *ControlFrame) MatchContentType(ctype []byte) bool { - if ctype == nil { - return true - } - for _, cfctype := range c.ContentTypes { - if bytes.Compare(ctype, cfctype) == 0 { - return true - } - } - return false -} - -func (c *ControlFrame) SetContentType(ctype []byte) { - if ctype != nil { - c.ContentTypes = [][]byte{ctype} - } -} diff --git a/vendor/github.com/farsightsec/golang-framestream/Decoder.go b/vendor/github.com/farsightsec/golang-framestream/Decoder.go deleted file mode 100644 index 1ee6296fa..000000000 --- a/vendor/github.com/farsightsec/golang-framestream/Decoder.go +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2014 by Farsight Security, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package framestream - -import ( - "bufio" - "encoding/binary" - "io" - "time" -) - -type DecoderOptions struct { - MaxPayloadSize uint32 - ContentType []byte - Bidirectional bool - Timeout time.Duration -} - -type Decoder struct { - buf []byte - opt DecoderOptions - reader *bufio.Reader - writer *bufio.Writer - stopped bool -} - -func NewDecoder(r io.Reader, opt *DecoderOptions) (dec *Decoder, err error) { - if opt == nil { - opt = &DecoderOptions{} - } - if opt.MaxPayloadSize == 0 { - opt.MaxPayloadSize = DEFAULT_MAX_PAYLOAD_SIZE - } - r = timeoutReader(r, opt) - dec = &Decoder{ - buf: make([]byte, opt.MaxPayloadSize), - opt: *opt, - reader: bufio.NewReader(r), - writer: nil, - } - - var cf ControlFrame - if opt.Bidirectional { - w, ok := r.(io.Writer) - if !ok { - return dec, ErrType - } - dec.writer = bufio.NewWriter(w) - - // Read the ready control frame. - err = cf.DecodeTypeEscape(dec.reader, CONTROL_READY) - if err != nil { - return - } - - // Check content type. - if !cf.MatchContentType(dec.opt.ContentType) { - return dec, ErrContentTypeMismatch - } - - // Send the accept control frame. - accept := ControlAccept - accept.SetContentType(dec.opt.ContentType) - err = accept.EncodeFlush(dec.writer) - if err != nil { - return - } - } - - // Read the start control frame. - err = cf.DecodeTypeEscape(dec.reader, CONTROL_START) - if err != nil { - return - } - - // Disable the read timeout to prevent killing idle connections. - disableReadTimeout(r) - - // Check content type. - if !cf.MatchContentType(dec.opt.ContentType) { - return dec, ErrContentTypeMismatch - } - - return -} - -func (dec *Decoder) readFrame(frameLen uint32) (err error) { - // Enforce limits on frame size. - if frameLen > dec.opt.MaxPayloadSize { - err = ErrDataFrameTooLarge - return - } - - // Read the frame. - n, err := io.ReadFull(dec.reader, dec.buf[0:frameLen]) - if err != nil || uint32(n) != frameLen { - return - } - return -} - -func (dec *Decoder) Decode() (frameData []byte, err error) { - if dec.stopped { - err = EOF - return - } - - // Read the frame length. - var frameLen uint32 - err = binary.Read(dec.reader, binary.BigEndian, &frameLen) - if err != nil { - return - } - if frameLen == 0 { - // This is a control frame. - var cf ControlFrame - err = cf.Decode(dec.reader) - if err != nil { - return - } - if cf.ControlType == CONTROL_STOP { - dec.stopped = true - if dec.opt.Bidirectional { - ff := &ControlFrame{ControlType: CONTROL_FINISH} - err = ff.EncodeFlush(dec.writer) - if err != nil { - return - } - } - return nil, EOF - } - if err != nil { - return nil, err - } - - } else { - // This is a data frame. - err = dec.readFrame(frameLen) - if err != nil { - return - } - frameData = dec.buf[0:frameLen] - } - - return -} diff --git a/vendor/github.com/farsightsec/golang-framestream/Encoder.go b/vendor/github.com/farsightsec/golang-framestream/Encoder.go deleted file mode 100644 index 878751d3f..000000000 --- a/vendor/github.com/farsightsec/golang-framestream/Encoder.go +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2014 by Farsight Security, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package framestream - -import ( - "bufio" - "encoding/binary" - "io" - "time" -) - -type EncoderOptions struct { - ContentType []byte - Bidirectional bool - Timeout time.Duration -} - -type Encoder struct { - writer *bufio.Writer - reader *bufio.Reader - opt EncoderOptions - buf []byte -} - -func NewEncoder(w io.Writer, opt *EncoderOptions) (enc *Encoder, err error) { - if opt == nil { - opt = &EncoderOptions{} - } - w = timeoutWriter(w, opt) - enc = &Encoder{ - writer: bufio.NewWriter(w), - opt: *opt, - } - - if opt.Bidirectional { - r, ok := w.(io.Reader) - if !ok { - return nil, ErrType - } - enc.reader = bufio.NewReader(r) - ready := ControlReady - ready.SetContentType(opt.ContentType) - if err = ready.EncodeFlush(enc.writer); err != nil { - return - } - - var accept ControlFrame - if err = accept.DecodeTypeEscape(enc.reader, CONTROL_ACCEPT); err != nil { - return - } - - if !accept.MatchContentType(opt.ContentType) { - return nil, ErrContentTypeMismatch - } - } - - // Write the start control frame. - start := ControlStart - start.SetContentType(opt.ContentType) - err = start.EncodeFlush(enc.writer) - if err != nil { - return - } - - return -} - -func (enc *Encoder) Close() (err error) { - err = ControlStop.EncodeFlush(enc.writer) - if err != nil || !enc.opt.Bidirectional { - return - } - - var finish ControlFrame - return finish.DecodeTypeEscape(enc.reader, CONTROL_FINISH) -} - -func (enc *Encoder) Write(frame []byte) (n int, err error) { - err = binary.Write(enc.writer, binary.BigEndian, uint32(len(frame))) - if err != nil { - return - } - return enc.writer.Write(frame) -} - -func (enc *Encoder) Flush() error { - return enc.writer.Flush() -} diff --git a/vendor/github.com/farsightsec/golang-framestream/LICENSE b/vendor/github.com/farsightsec/golang-framestream/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/vendor/github.com/farsightsec/golang-framestream/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/farsightsec/golang-framestream/README.md b/vendor/github.com/farsightsec/golang-framestream/README.md deleted file mode 100644 index 9d1fd76dc..000000000 --- a/vendor/github.com/farsightsec/golang-framestream/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Frame Streams implementation in Go - -https://github.com/farsightsec/golang-framestream - -Frame Streams is a lightweight, binary-clean protocol that allows -for the transport of arbitrarily encoded data payload sequences with -minimal framing overhead. - -This package provides a pure Golang implementation. The Frame Streams -implementation in C is at https://github.com/farsightsec/fstrm/. - -The example framestream_dump program reads a Frame Streams formatted -input file and prints the data frames and frame byte counts. diff --git a/vendor/github.com/farsightsec/golang-framestream/framestream.go b/vendor/github.com/farsightsec/golang-framestream/framestream.go deleted file mode 100644 index 49656981c..000000000 --- a/vendor/github.com/farsightsec/golang-framestream/framestream.go +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2014 by Farsight Security, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package framestream - -import ( - "errors" - "io" -) - -const DEFAULT_MAX_PAYLOAD_SIZE = 1048576 -const MAX_CONTROL_FRAME_SIZE = 512 - -var EOF = io.EOF -var ErrContentTypeMismatch = errors.New("content type mismatch") -var ErrDataFrameTooLarge = errors.New("data frame too large") -var ErrShortRead = errors.New("short read") -var ErrDecode = errors.New("decoding error") -var ErrType = errors.New("invalid type") diff --git a/vendor/github.com/farsightsec/golang-framestream/timeout.go b/vendor/github.com/farsightsec/golang-framestream/timeout.go deleted file mode 100644 index ce3d69e1f..000000000 --- a/vendor/github.com/farsightsec/golang-framestream/timeout.go +++ /dev/null @@ -1,67 +0,0 @@ -package framestream - -import ( - "io" - "net" - "time" -) - -type timeoutConn struct { - conn net.Conn - readTimeout, writeTimeout time.Duration -} - -func (toc *timeoutConn) Write(b []byte) (int, error) { - if toc.writeTimeout != 0 { - toc.conn.SetWriteDeadline(time.Now().Add(toc.writeTimeout)) - } - return toc.conn.Write(b) -} - -func (toc *timeoutConn) Read(b []byte) (int, error) { - if toc.readTimeout != 0 { - toc.conn.SetReadDeadline(time.Now().Add(toc.readTimeout)) - } - return toc.conn.Read(b) -} - -func timeoutWriter(w io.Writer, opt *EncoderOptions) io.Writer { - if !opt.Bidirectional { - return w - } - if opt.Timeout == 0 { - return w - } - if c, ok := w.(net.Conn); ok { - return &timeoutConn{ - conn: c, - readTimeout: opt.Timeout, - writeTimeout: opt.Timeout, - } - } - return w -} - -func timeoutReader(r io.Reader, opt *DecoderOptions) io.Reader { - if !opt.Bidirectional { - return r - } - if opt.Timeout == 0 { - return r - } - if c, ok := r.(net.Conn); ok { - return &timeoutConn{ - conn: c, - readTimeout: opt.Timeout, - writeTimeout: opt.Timeout, - } - } - return r -} - -func disableReadTimeout(r io.Reader) { - if tc, ok := r.(*timeoutConn); ok { - tc.readTimeout = 0 - tc.conn.SetReadDeadline(time.Time{}) - } -} diff --git a/vendor/github.com/prometheus/client_golang/AUTHORS.md b/vendor/github.com/prometheus/client_golang/AUTHORS.md new file mode 100644 index 000000000..c5275d5ab --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/AUTHORS.md @@ -0,0 +1,18 @@ +The Prometheus project was started by Matt T. Proud (emeritus) and +Julius Volz in 2012. + +Maintainers of this repository: + +* Björn Rabenstein + +The following individuals have contributed code to this repository +(listed in alphabetical order): + +* Bernerd Schaefer +* Björn Rabenstein +* Daniel Bornkessel +* Jeff Younker +* Julius Volz +* Matt T. Proud +* Tobias Schmidt + diff --git a/vendor/github.com/prometheus/client_golang/prometheus/collector.go b/vendor/github.com/prometheus/client_golang/prometheus/collector.go index c0d70b2fa..623d3d83f 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/collector.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/collector.go @@ -29,72 +29,27 @@ type Collector interface { // collected by this Collector to the provided channel and returns once // the last descriptor has been sent. The sent descriptors fulfill the // consistency and uniqueness requirements described in the Desc - // documentation. - // - // It is valid if one and the same Collector sends duplicate - // descriptors. Those duplicates are simply ignored. However, two - // different Collectors must not send duplicate descriptors. - // - // Sending no descriptor at all marks the Collector as “unchecked”, - // i.e. no checks will be performed at registration time, and the - // Collector may yield any Metric it sees fit in its Collect method. - // - // This method idempotently sends the same descriptors throughout the - // lifetime of the Collector. It may be called concurrently and - // therefore must be implemented in a concurrency safe way. - // - // If a Collector encounters an error while executing this method, it - // must send an invalid descriptor (created with NewInvalidDesc) to - // signal the error to the registry. + // documentation. (It is valid if one and the same Collector sends + // duplicate descriptors. Those duplicates are simply ignored. However, + // two different Collectors must not send duplicate descriptors.) This + // method idempotently sends the same descriptors throughout the + // lifetime of the Collector. If a Collector encounters an error while + // executing this method, it must send an invalid descriptor (created + // with NewInvalidDesc) to signal the error to the registry. Describe(chan<- *Desc) // Collect is called by the Prometheus registry when collecting // metrics. The implementation sends each collected metric via the // provided channel and returns once the last metric has been sent. The - // descriptor of each sent metric is one of those returned by Describe - // (unless the Collector is unchecked, see above). Returned metrics that - // share the same descriptor must differ in their variable label - // values. - // - // This method may be called concurrently and must therefore be - // implemented in a concurrency safe way. Blocking occurs at the expense - // of total performance of rendering all registered metrics. Ideally, - // Collector implementations support concurrent readers. + // descriptor of each sent metric is one of those returned by + // Describe. Returned metrics that share the same descriptor must differ + // in their variable label values. This method may be called + // concurrently and must therefore be implemented in a concurrency safe + // way. Blocking occurs at the expense of total performance of rendering + // all registered metrics. Ideally, Collector implementations support + // concurrent readers. Collect(chan<- Metric) } -// DescribeByCollect is a helper to implement the Describe method of a custom -// Collector. It collects the metrics from the provided Collector and sends -// their descriptors to the provided channel. -// -// If a Collector collects the same metrics throughout its lifetime, its -// Describe method can simply be implemented as: -// -// func (c customCollector) Describe(ch chan<- *Desc) { -// DescribeByCollect(c, ch) -// } -// -// However, this will not work if the metrics collected change dynamically over -// the lifetime of the Collector in a way that their combined set of descriptors -// changes as well. The shortcut implementation will then violate the contract -// of the Describe method. If a Collector sometimes collects no metrics at all -// (for example vectors like CounterVec, GaugeVec, etc., which only collect -// metrics after a metric with a fully specified label set has been accessed), -// it might even get registered as an unchecked Collecter (cf. the Register -// method of the Registerer interface). Hence, only use this shortcut -// implementation of Describe if you are certain to fulfill the contract. -// -// The Collector example demonstrates a use of DescribeByCollect. -func DescribeByCollect(c Collector, descs chan<- *Desc) { - metrics := make(chan Metric) - go func() { - c.Collect(metrics) - close(metrics) - }() - for m := range metrics { - descs <- m.Desc() - } -} - // selfCollector implements Collector for a single Metric so that the Metric // collects itself. Add it as an anonymous field to a struct that implements // Metric, and call init with the Metric itself as an argument. diff --git a/vendor/github.com/prometheus/client_golang/prometheus/counter.go b/vendor/github.com/prometheus/client_golang/prometheus/counter.go index d463e36d3..ee37949ad 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/counter.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/counter.go @@ -15,10 +15,6 @@ package prometheus import ( "errors" - "math" - "sync/atomic" - - dto "github.com/prometheus/client_model/go" ) // Counter is a Metric that represents a single numerical value that only ever @@ -34,8 +30,16 @@ type Counter interface { Metric Collector - // Inc increments the counter by 1. Use Add to increment it by arbitrary - // non-negative values. + // Set is used to set the Counter to an arbitrary value. It is only used + // if you have to transfer a value from an external counter into this + // Prometheus metric. Do not use it for regular handling of a + // Prometheus counter (as it can be used to break the contract of + // monotonically increasing values). + // + // Deprecated: Use NewConstMetric to create a counter for an external + // value. A Counter should never be set. + Set(float64) + // Inc increments the counter by 1. Inc() // Add adds the given value to the counter. It panics if the value is < // 0. @@ -46,14 +50,6 @@ type Counter interface { type CounterOpts Opts // NewCounter creates a new Counter based on the provided CounterOpts. -// -// The returned implementation tracks the counter value in two separate -// variables, a float64 and a uint64. The latter is used to track calls of the -// Inc method and calls of the Add method with a value that can be represented -// as a uint64. This allows atomic increments of the counter with optimal -// performance. (It is common to have an Inc call in very hot execution paths.) -// Both internal tracking values are added up in the Write method. This has to -// be taken into account when it comes to precision and overflow behavior. func NewCounter(opts CounterOpts) Counter { desc := NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), @@ -61,58 +57,20 @@ func NewCounter(opts CounterOpts) Counter { nil, opts.ConstLabels, ) - result := &counter{desc: desc, labelPairs: desc.constLabelPairs} + result := &counter{value: value{desc: desc, valType: CounterValue, labelPairs: desc.constLabelPairs}} result.init(result) // Init self-collection. return result } type counter struct { - // valBits contains the bits of the represented float64 value, while - // valInt stores values that are exact integers. Both have to go first - // in the struct to guarantee alignment for atomic operations. - // http://golang.org/pkg/sync/atomic/#pkg-note-BUG - valBits uint64 - valInt uint64 - - selfCollector - desc *Desc - - labelPairs []*dto.LabelPair -} - -func (c *counter) Desc() *Desc { - return c.desc + value } func (c *counter) Add(v float64) { if v < 0 { panic(errors.New("counter cannot decrease in value")) } - ival := uint64(v) - if float64(ival) == v { - atomic.AddUint64(&c.valInt, ival) - return - } - - for { - oldBits := atomic.LoadUint64(&c.valBits) - newBits := math.Float64bits(math.Float64frombits(oldBits) + v) - if atomic.CompareAndSwapUint64(&c.valBits, oldBits, newBits) { - return - } - } -} - -func (c *counter) Inc() { - atomic.AddUint64(&c.valInt, 1) -} - -func (c *counter) Write(out *dto.Metric) error { - fval := math.Float64frombits(atomic.LoadUint64(&c.valBits)) - ival := atomic.LoadUint64(&c.valInt) - val := fval + float64(ival) - - return populateMetric(CounterValue, val, c.labelPairs, out) + c.value.Add(v) } // CounterVec is a Collector that bundles a set of Counters that all share the @@ -120,12 +78,16 @@ func (c *counter) Write(out *dto.Metric) error { // if you want to count the same thing partitioned by various dimensions // (e.g. number of HTTP requests, partitioned by response code and // method). Create instances with NewCounterVec. +// +// CounterVec embeds MetricVec. See there for a full list of methods with +// detailed documentation. type CounterVec struct { - *metricVec + *MetricVec } // NewCounterVec creates a new CounterVec based on the provided CounterOpts and -// partitioned by the given label names. +// partitioned by the given label names. At least one label name must be +// provided. func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec { desc := NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), @@ -134,62 +96,34 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec { opts.ConstLabels, ) return &CounterVec{ - metricVec: newMetricVec(desc, func(lvs ...string) Metric { - if len(lvs) != len(desc.variableLabels) { - panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs)) - } - result := &counter{desc: desc, labelPairs: makeLabelPairs(desc, lvs)} + MetricVec: newMetricVec(desc, func(lvs ...string) Metric { + result := &counter{value: value{ + desc: desc, + valType: CounterValue, + labelPairs: makeLabelPairs(desc, lvs), + }} result.init(result) // Init self-collection. return result }), } } -// GetMetricWithLabelValues returns the Counter for the given slice of label -// values (same order as the VariableLabels in Desc). If that combination of -// label values is accessed for the first time, a new Counter is created. -// -// It is possible to call this method without using the returned Counter to only -// create the new Counter but leave it at its starting value 0. See also the -// SummaryVec example. -// -// Keeping the Counter for later use is possible (and should be considered if -// performance is critical), but keep in mind that Reset, DeleteLabelValues and -// Delete can be used to delete the Counter from the CounterVec. In that case, -// the Counter will still exist, but it will not be exported anymore, even if a -// Counter with the same label values is created later. -// -// An error is returned if the number of label values is not the same as the -// number of VariableLabels in Desc (minus any curried labels). -// -// Note that for more than one label value, this method is prone to mistakes -// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as -// an alternative to avoid that type of mistake. For higher label numbers, the -// latter has a much more readable (albeit more verbose) syntax, but it comes -// with a performance overhead (for creating and processing the Labels map). -// See also the GaugeVec example. -func (v *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) { - metric, err := v.metricVec.getMetricWithLabelValues(lvs...) +// GetMetricWithLabelValues replaces the method of the same name in +// MetricVec. The difference is that this method returns a Counter and not a +// Metric so that no type conversion is required. +func (m *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) { + metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...) if metric != nil { return metric.(Counter), err } return nil, err } -// GetMetricWith returns the Counter for the given Labels map (the label names -// must match those of the VariableLabels in Desc). If that label map is -// accessed for the first time, a new Counter is created. Implications of -// creating a Counter without using it and keeping the Counter for later use are -// the same as for GetMetricWithLabelValues. -// -// An error is returned if the number and names of the Labels are inconsistent -// with those of the VariableLabels in Desc (minus any curried labels). -// -// This method is used for the same purpose as -// GetMetricWithLabelValues(...string). See there for pros and cons of the two -// methods. -func (v *CounterVec) GetMetricWith(labels Labels) (Counter, error) { - metric, err := v.metricVec.getMetricWith(labels) +// GetMetricWith replaces the method of the same name in MetricVec. The +// difference is that this method returns a Counter and not a Metric so that no +// type conversion is required. +func (m *CounterVec) GetMetricWith(labels Labels) (Counter, error) { + metric, err := m.MetricVec.GetMetricWith(labels) if metric != nil { return metric.(Counter), err } @@ -197,57 +131,18 @@ func (v *CounterVec) GetMetricWith(labels Labels) (Counter, error) { } // WithLabelValues works as GetMetricWithLabelValues, but panics where -// GetMetricWithLabelValues would have returned an error. Not returning an -// error allows shortcuts like +// GetMetricWithLabelValues would have returned an error. By not returning an +// error, WithLabelValues allows shortcuts like // myVec.WithLabelValues("404", "GET").Add(42) -func (v *CounterVec) WithLabelValues(lvs ...string) Counter { - c, err := v.GetMetricWithLabelValues(lvs...) - if err != nil { - panic(err) - } - return c +func (m *CounterVec) WithLabelValues(lvs ...string) Counter { + return m.MetricVec.WithLabelValues(lvs...).(Counter) } // With works as GetMetricWith, but panics where GetMetricWithLabels would have -// returned an error. Not returning an error allows shortcuts like -// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42) -func (v *CounterVec) With(labels Labels) Counter { - c, err := v.GetMetricWith(labels) - if err != nil { - panic(err) - } - return c -} - -// CurryWith returns a vector curried with the provided labels, i.e. the -// returned vector has those labels pre-set for all labeled operations performed -// on it. The cardinality of the curried vector is reduced accordingly. The -// order of the remaining labels stays the same (just with the curried labels -// taken out of the sequence – which is relevant for the -// (GetMetric)WithLabelValues methods). It is possible to curry a curried -// vector, but only with labels not yet used for currying before. -// -// The metrics contained in the CounterVec are shared between the curried and -// uncurried vectors. They are just accessed differently. Curried and uncurried -// vectors behave identically in terms of collection. Only one must be -// registered with a given registry (usually the uncurried version). The Reset -// method deletes all metrics, even if called on a curried vector. -func (v *CounterVec) CurryWith(labels Labels) (*CounterVec, error) { - vec, err := v.curryWith(labels) - if vec != nil { - return &CounterVec{vec}, err - } - return nil, err -} - -// MustCurryWith works as CurryWith but panics where CurryWith would have -// returned an error. -func (v *CounterVec) MustCurryWith(labels Labels) *CounterVec { - vec, err := v.CurryWith(labels) - if err != nil { - panic(err) - } - return vec +// returned an error. By not returning an error, With allows shortcuts like +// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42) +func (m *CounterVec) With(labels Labels) Counter { + return m.MetricVec.With(labels).(Counter) } // CounterFunc is a Counter whose value is determined at collect time by calling a diff --git a/vendor/github.com/prometheus/client_golang/prometheus/desc.go b/vendor/github.com/prometheus/client_golang/prometheus/desc.go index 1d034f871..77f4b30e8 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/desc.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/desc.go @@ -16,15 +16,33 @@ package prometheus import ( "errors" "fmt" + "regexp" "sort" "strings" "github.com/golang/protobuf/proto" - "github.com/prometheus/common/model" dto "github.com/prometheus/client_model/go" ) +var ( + metricNameRE = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_:]*$`) + labelNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$") +) + +// reservedLabelPrefix is a prefix which is not legal in user-supplied +// label names. +const reservedLabelPrefix = "__" + +// Labels represents a collection of label name -> value mappings. This type is +// commonly used with the With(Labels) and GetMetricWith(Labels) methods of +// metric vector Collectors, e.g.: +// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42) +// +// The other use-case is the specification of constant label pairs in Opts or to +// create a Desc. +type Labels map[string]string + // Desc is the descriptor used by every Prometheus Metric. It is essentially // the immutable meta-data of a Metric. The normal Metric implementations // included in this package manage their Desc under the hood. Users only have to @@ -60,27 +78,32 @@ type Desc struct { // Help string. Each Desc with the same fqName must have the same // dimHash. dimHash uint64 - // err is an error that occurred during construction. It is reported on + // err is an error that occured during construction. It is reported on // registration time. err error } // NewDesc allocates and initializes a new Desc. Errors are recorded in the Desc // and will be reported on registration time. variableLabels and constLabels can -// be nil if no such labels should be set. fqName must not be empty. +// be nil if no such labels should be set. fqName and help must not be empty. // // variableLabels only contain the label names. Their label values are variable // and therefore not part of the Desc. (They are managed within the Metric.) // // For constLabels, the label values are constant. Therefore, they are fully -// specified in the Desc. See the Collector example for a usage pattern. +// specified in the Desc. See the Opts documentation for the implications of +// constant labels. func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *Desc { d := &Desc{ fqName: fqName, help: help, variableLabels: variableLabels, } - if !model.IsValidMetricName(model.LabelValue(fqName)) { + if help == "" { + d.err = errors.New("empty help string") + return d + } + if !metricNameRE.MatchString(fqName) { d.err = fmt.Errorf("%q is not a valid metric name", fqName) return d } @@ -93,7 +116,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) * // First add only the const label names and sort them... for labelName := range constLabels { if !checkLabelName(labelName) { - d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName) + d.err = fmt.Errorf("%q is not a valid label name", labelName) return d } labelNames = append(labelNames, labelName) @@ -104,18 +127,12 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) * for _, labelName := range labelNames { labelValues = append(labelValues, constLabels[labelName]) } - // Validate the const label values. They can't have a wrong cardinality, so - // use in len(labelValues) as expectedNumberOfValues. - if err := validateLabelValues(labelValues, len(labelValues)); err != nil { - d.err = err - return d - } // Now add the variable label names, but prefix them with something that // cannot be in a regular label name. That prevents matching the label // dimension with a different mix between preset and variable labels. for _, labelName := range variableLabels { if !checkLabelName(labelName) { - d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName) + d.err = fmt.Errorf("%q is not a valid label name", labelName) return d } labelNames = append(labelNames, "$"+labelName) @@ -125,7 +142,6 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) * d.err = errors.New("duplicate label names") return d } - vh := hashNew() for _, val := range labelValues { vh = hashAdd(vh, val) @@ -152,7 +168,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) * Value: proto.String(v), }) } - sort.Sort(labelPairSorter(d.constLabelPairs)) + sort.Sort(LabelPairSorter(d.constLabelPairs)) return d } @@ -182,3 +198,8 @@ func (d *Desc) String() string { d.variableLabels, ) } + +func checkLabelName(l string) bool { + return labelNameRE.MatchString(l) && + !strings.HasPrefix(l, reservedLabelPrefix) +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/doc.go b/vendor/github.com/prometheus/client_golang/prometheus/doc.go index 5d9525def..b15a2d3b9 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/doc.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/doc.go @@ -11,15 +11,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package prometheus is the core instrumentation package. It provides metrics -// primitives to instrument code for monitoring. It also offers a registry for -// metrics. Sub-packages allow to expose the registered metrics via HTTP -// (package promhttp) or push them to a Pushgateway (package push). There is -// also a sub-package promauto, which provides metrics constructors with -// automatic registration. +// Package prometheus provides metrics primitives to instrument code for +// monitoring. It also offers a registry for metrics. Sub-packages allow to +// expose the registered metrics via HTTP (package promhttp) or push them to a +// Pushgateway (package push). // // All exported functions and methods are safe to be used concurrently unless -// specified otherwise. +//specified otherwise. // // A Basic Example // @@ -28,7 +26,6 @@ // package main // // import ( -// "log" // "net/http" // // "github.com/prometheus/client_golang/prometheus" @@ -62,7 +59,7 @@ // // The Handler function provides a default handler to expose metrics // // via an HTTP server. "/metrics" is the usual endpoint for that. // http.Handle("/metrics", promhttp.Handler()) -// log.Fatal(http.ListenAndServe(":8080", nil)) +// http.ListenAndServe(":8080", nil) // } // // @@ -72,12 +69,9 @@ // Metrics // // The number of exported identifiers in this package might appear a bit -// overwhelming. However, in addition to the basic plumbing shown in the example +// overwhelming. Hovever, in addition to the basic plumbing shown in the example // above, you only need to understand the different metric types and their -// vector versions for basic usage. Furthermore, if you are not concerned with -// fine-grained control of when and how to register metrics with the registry, -// have a look at the promauto package, which will effectively allow you to -// ignore registration altogether in simple cases. +// vector versions for basic usage. // // Above, you have already touched the Counter and the Gauge. There are two more // advanced metric types: the Summary and Histogram. A more thorough description @@ -101,8 +95,8 @@ // SummaryVec, HistogramVec, and UntypedVec are not. // // To create instances of Metrics and their vector versions, you need a suitable -// …Opts struct, i.e. GaugeOpts, CounterOpts, SummaryOpts, HistogramOpts, or -// UntypedOpts. +// …Opts struct, i.e. GaugeOpts, CounterOpts, SummaryOpts, +// HistogramOpts, or UntypedOpts. // // Custom Collectors and constant Metrics // @@ -120,18 +114,8 @@ // Metric instances “on the fly” using NewConstMetric, NewConstHistogram, and // NewConstSummary (and their respective Must… versions). That will happen in // the Collect method. The Describe method has to return separate Desc -// instances, representative of the “throw-away” metrics to be created later. -// NewDesc comes in handy to create those Desc instances. Alternatively, you -// could return no Desc at all, which will marke the Collector “unchecked”. No -// checks are porformed at registration time, but metric consistency will still -// be ensured at scrape time, i.e. any inconsistencies will lead to scrape -// errors. Thus, with unchecked Collectors, the responsibility to not collect -// metrics that lead to inconsistencies in the total scrape result lies with the -// implementer of the Collector. While this is not a desirable state, it is -// sometimes necessary. The typical use case is a situatios where the exact -// metrics to be returned by a Collector cannot be predicted at registration -// time, but the implementer has sufficient knowledge of the whole system to -// guarantee metric consistency. +// instances, representative of the “throw-away” metrics to be created +// later. NewDesc comes in handy to create those Desc instances. // // The Collector example illustrates the use case. You can also look at the // source code of the processCollector (mirroring process metrics), the @@ -145,34 +129,34 @@ // Advanced Uses of the Registry // // While MustRegister is the by far most common way of registering a Collector, -// sometimes you might want to handle the errors the registration might cause. -// As suggested by the name, MustRegister panics if an error occurs. With the -// Register function, the error is returned and can be handled. +// sometimes you might want to handle the errors the registration might +// cause. As suggested by the name, MustRegister panics if an error occurs. With +// the Register function, the error is returned and can be handled. // // An error is returned if the registered Collector is incompatible or // inconsistent with already registered metrics. The registry aims for -// consistency of the collected metrics according to the Prometheus data model. -// Inconsistencies are ideally detected at registration time, not at collect -// time. The former will usually be detected at start-up time of a program, -// while the latter will only happen at scrape time, possibly not even on the -// first scrape if the inconsistency only becomes relevant later. That is the -// main reason why a Collector and a Metric have to describe themselves to the -// registry. +// consistency of the collected metrics according to the Prometheus data +// model. Inconsistencies are ideally detected at registration time, not at +// collect time. The former will usually be detected at start-up time of a +// program, while the latter will only happen at scrape time, possibly not even +// on the first scrape if the inconsistency only becomes relevant later. That is +// the main reason why a Collector and a Metric have to describe themselves to +// the registry. // // So far, everything we did operated on the so-called default registry, as it -// can be found in the global DefaultRegisterer variable. With NewRegistry, you +// can be found in the global DefaultRegistry variable. With NewRegistry, you // can create a custom registry, or you can even implement the Registerer or -// Gatherer interfaces yourself. The methods Register and Unregister work in the -// same way on a custom registry as the global functions Register and Unregister -// on the default registry. -// -// There are a number of uses for custom registries: You can use registries with -// special properties, see NewPedanticRegistry. You can avoid global state, as -// it is imposed by the DefaultRegisterer. You can use multiple registries at -// the same time to expose different metrics in different ways. You can use +// Gatherer interfaces yourself. The methods Register and Unregister work in +// the same way on a custom registry as the global functions Register and +// Unregister on the default registry. +// +// There are a number of uses for custom registries: You can use registries +// with special properties, see NewPedanticRegistry. You can avoid global state, +// as it is imposed by the DefaultRegistry. You can use multiple registries at +// the same time to expose different metrics in different ways. You can use // separate registries for testing purposes. // -// Also note that the DefaultRegisterer comes registered with a Collector for Go +// Also note that the DefaultRegistry comes registered with a Collector for Go // runtime metrics (via NewGoCollector) and a Collector for process metrics (via // NewProcessCollector). With a custom registry, you are in control and decide // yourself about the Collectors to register. @@ -182,20 +166,16 @@ // The Registry implements the Gatherer interface. The caller of the Gather // method can then expose the gathered metrics in some way. Usually, the metrics // are served via HTTP on the /metrics endpoint. That's happening in the example -// above. The tools to expose metrics via HTTP are in the promhttp sub-package. -// (The top-level functions in the prometheus package are deprecated.) +// above. The tools to expose metrics via HTTP are in the promhttp +// sub-package. (The top-level functions in the prometheus package are +// deprecated.) // // Pushing to the Pushgateway // // Function for pushing to the Pushgateway can be found in the push sub-package. // -// Graphite Bridge -// -// Functions and examples to push metrics from a Gatherer to Graphite can be -// found in the graphite sub-package. -// // Other Means of Exposition // -// More ways of exposing metrics can easily be added by following the approaches -// of the existing implementations. +// More ways of exposing metrics can easily be added. Sending metrics to +// Graphite would be an example that will soon be implemented. package prometheus diff --git a/vendor/github.com/prometheus/client_golang/prometheus/fnv.go b/vendor/github.com/prometheus/client_golang/prometheus/fnv.go index 3d383a735..e3b67df8a 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/fnv.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/fnv.go @@ -1,16 +1,3 @@ -// Copyright 2018 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package prometheus // Inline and byte-free variant of hash/fnv's fnv64a. diff --git a/vendor/github.com/prometheus/client_golang/prometheus/gauge.go b/vendor/github.com/prometheus/client_golang/prometheus/gauge.go index 71d406bd9..8b70e5141 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/gauge.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/gauge.go @@ -13,14 +13,6 @@ package prometheus -import ( - "math" - "sync/atomic" - "time" - - dto "github.com/prometheus/client_model/go" -) - // Gauge is a Metric that represents a single numerical value that can // arbitrarily go up and down. // @@ -35,95 +27,29 @@ type Gauge interface { // Set sets the Gauge to an arbitrary value. Set(float64) - // Inc increments the Gauge by 1. Use Add to increment it by arbitrary - // values. + // Inc increments the Gauge by 1. Inc() - // Dec decrements the Gauge by 1. Use Sub to decrement it by arbitrary - // values. + // Dec decrements the Gauge by 1. Dec() - // Add adds the given value to the Gauge. (The value can be negative, - // resulting in a decrease of the Gauge.) + // Add adds the given value to the Gauge. (The value can be + // negative, resulting in a decrease of the Gauge.) Add(float64) // Sub subtracts the given value from the Gauge. (The value can be // negative, resulting in an increase of the Gauge.) Sub(float64) - - // SetToCurrentTime sets the Gauge to the current Unix time in seconds. - SetToCurrentTime() } // GaugeOpts is an alias for Opts. See there for doc comments. type GaugeOpts Opts // NewGauge creates a new Gauge based on the provided GaugeOpts. -// -// The returned implementation is optimized for a fast Set method. If you have a -// choice for managing the value of a Gauge via Set vs. Inc/Dec/Add/Sub, pick -// the former. For example, the Inc method of the returned Gauge is slower than -// the Inc method of a Counter returned by NewCounter. This matches the typical -// scenarios for Gauges and Counters, where the former tends to be Set-heavy and -// the latter Inc-heavy. func NewGauge(opts GaugeOpts) Gauge { - desc := NewDesc( + return newValue(NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, nil, opts.ConstLabels, - ) - result := &gauge{desc: desc, labelPairs: desc.constLabelPairs} - result.init(result) // Init self-collection. - return result -} - -type gauge struct { - // valBits contains the bits of the represented float64 value. It has - // to go first in the struct to guarantee alignment for atomic - // operations. http://golang.org/pkg/sync/atomic/#pkg-note-BUG - valBits uint64 - - selfCollector - - desc *Desc - labelPairs []*dto.LabelPair -} - -func (g *gauge) Desc() *Desc { - return g.desc -} - -func (g *gauge) Set(val float64) { - atomic.StoreUint64(&g.valBits, math.Float64bits(val)) -} - -func (g *gauge) SetToCurrentTime() { - g.Set(float64(time.Now().UnixNano()) / 1e9) -} - -func (g *gauge) Inc() { - g.Add(1) -} - -func (g *gauge) Dec() { - g.Add(-1) -} - -func (g *gauge) Add(val float64) { - for { - oldBits := atomic.LoadUint64(&g.valBits) - newBits := math.Float64bits(math.Float64frombits(oldBits) + val) - if atomic.CompareAndSwapUint64(&g.valBits, oldBits, newBits) { - return - } - } -} - -func (g *gauge) Sub(val float64) { - g.Add(val * -1) -} - -func (g *gauge) Write(out *dto.Metric) error { - val := math.Float64frombits(atomic.LoadUint64(&g.valBits)) - return populateMetric(GaugeValue, val, g.labelPairs, out) + ), GaugeValue, 0) } // GaugeVec is a Collector that bundles a set of Gauges that all share the same @@ -132,11 +58,12 @@ func (g *gauge) Write(out *dto.Metric) error { // (e.g. number of operations queued, partitioned by user and operation // type). Create instances with NewGaugeVec. type GaugeVec struct { - *metricVec + *MetricVec } // NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and -// partitioned by the given label names. +// partitioned by the given label names. At least one label name must be +// provided. func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec { desc := NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), @@ -145,62 +72,28 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec { opts.ConstLabels, ) return &GaugeVec{ - metricVec: newMetricVec(desc, func(lvs ...string) Metric { - if len(lvs) != len(desc.variableLabels) { - panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs)) - } - result := &gauge{desc: desc, labelPairs: makeLabelPairs(desc, lvs)} - result.init(result) // Init self-collection. - return result + MetricVec: newMetricVec(desc, func(lvs ...string) Metric { + return newValue(desc, GaugeValue, 0, lvs...) }), } } -// GetMetricWithLabelValues returns the Gauge for the given slice of label -// values (same order as the VariableLabels in Desc). If that combination of -// label values is accessed for the first time, a new Gauge is created. -// -// It is possible to call this method without using the returned Gauge to only -// create the new Gauge but leave it at its starting value 0. See also the -// SummaryVec example. -// -// Keeping the Gauge for later use is possible (and should be considered if -// performance is critical), but keep in mind that Reset, DeleteLabelValues and -// Delete can be used to delete the Gauge from the GaugeVec. In that case, the -// Gauge will still exist, but it will not be exported anymore, even if a -// Gauge with the same label values is created later. See also the CounterVec -// example. -// -// An error is returned if the number of label values is not the same as the -// number of VariableLabels in Desc (minus any curried labels). -// -// Note that for more than one label value, this method is prone to mistakes -// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as -// an alternative to avoid that type of mistake. For higher label numbers, the -// latter has a much more readable (albeit more verbose) syntax, but it comes -// with a performance overhead (for creating and processing the Labels map). -func (v *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) { - metric, err := v.metricVec.getMetricWithLabelValues(lvs...) +// GetMetricWithLabelValues replaces the method of the same name in +// MetricVec. The difference is that this method returns a Gauge and not a +// Metric so that no type conversion is required. +func (m *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) { + metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...) if metric != nil { return metric.(Gauge), err } return nil, err } -// GetMetricWith returns the Gauge for the given Labels map (the label names -// must match those of the VariableLabels in Desc). If that label map is -// accessed for the first time, a new Gauge is created. Implications of -// creating a Gauge without using it and keeping the Gauge for later use are -// the same as for GetMetricWithLabelValues. -// -// An error is returned if the number and names of the Labels are inconsistent -// with those of the VariableLabels in Desc (minus any curried labels). -// -// This method is used for the same purpose as -// GetMetricWithLabelValues(...string). See there for pros and cons of the two -// methods. -func (v *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) { - metric, err := v.metricVec.getMetricWith(labels) +// GetMetricWith replaces the method of the same name in MetricVec. The +// difference is that this method returns a Gauge and not a Metric so that no +// type conversion is required. +func (m *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) { + metric, err := m.MetricVec.GetMetricWith(labels) if metric != nil { return metric.(Gauge), err } @@ -208,57 +101,18 @@ func (v *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) { } // WithLabelValues works as GetMetricWithLabelValues, but panics where -// GetMetricWithLabelValues would have returned an error. Not returning an -// error allows shortcuts like +// GetMetricWithLabelValues would have returned an error. By not returning an +// error, WithLabelValues allows shortcuts like // myVec.WithLabelValues("404", "GET").Add(42) -func (v *GaugeVec) WithLabelValues(lvs ...string) Gauge { - g, err := v.GetMetricWithLabelValues(lvs...) - if err != nil { - panic(err) - } - return g +func (m *GaugeVec) WithLabelValues(lvs ...string) Gauge { + return m.MetricVec.WithLabelValues(lvs...).(Gauge) } // With works as GetMetricWith, but panics where GetMetricWithLabels would have -// returned an error. Not returning an error allows shortcuts like -// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42) -func (v *GaugeVec) With(labels Labels) Gauge { - g, err := v.GetMetricWith(labels) - if err != nil { - panic(err) - } - return g -} - -// CurryWith returns a vector curried with the provided labels, i.e. the -// returned vector has those labels pre-set for all labeled operations performed -// on it. The cardinality of the curried vector is reduced accordingly. The -// order of the remaining labels stays the same (just with the curried labels -// taken out of the sequence – which is relevant for the -// (GetMetric)WithLabelValues methods). It is possible to curry a curried -// vector, but only with labels not yet used for currying before. -// -// The metrics contained in the GaugeVec are shared between the curried and -// uncurried vectors. They are just accessed differently. Curried and uncurried -// vectors behave identically in terms of collection. Only one must be -// registered with a given registry (usually the uncurried version). The Reset -// method deletes all metrics, even if called on a curried vector. -func (v *GaugeVec) CurryWith(labels Labels) (*GaugeVec, error) { - vec, err := v.curryWith(labels) - if vec != nil { - return &GaugeVec{vec}, err - } - return nil, err -} - -// MustCurryWith works as CurryWith but panics where CurryWith would have -// returned an error. -func (v *GaugeVec) MustCurryWith(labels Labels) *GaugeVec { - vec, err := v.CurryWith(labels) - if err != nil { - panic(err) - } - return vec +// returned an error. By not returning an error, With allows shortcuts like +// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42) +func (m *GaugeVec) With(labels Labels) Gauge { + return m.MetricVec.With(labels).(Gauge) } // GaugeFunc is a Gauge whose value is determined at collect time by calling a diff --git a/vendor/github.com/prometheus/client_golang/prometheus/go_collector.go b/vendor/github.com/prometheus/client_golang/prometheus/go_collector.go index ba3b9333e..abc9d4ec4 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/go_collector.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/go_collector.go @@ -1,16 +1,3 @@ -// Copyright 2018 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package prometheus import ( @@ -21,39 +8,26 @@ import ( ) type goCollector struct { - goroutinesDesc *Desc - threadsDesc *Desc - gcDesc *Desc - goInfoDesc *Desc + goroutines Gauge + gcDesc *Desc // metrics to describe and collect metrics memStatsMetrics } -// NewGoCollector returns a collector which exports metrics about the current Go -// process. This includes memory stats. To collect those, runtime.ReadMemStats -// is called. This causes a stop-the-world, which is very short with Go1.9+ -// (~25µs). However, with older Go versions, the stop-the-world duration depends -// on the heap size and can be quite significant (~1.7 ms/GiB as per -// https://go-review.googlesource.com/c/go/+/34937). +// NewGoCollector returns a collector which exports metrics about the current +// go process. func NewGoCollector() Collector { return &goCollector{ - goroutinesDesc: NewDesc( - "go_goroutines", - "Number of goroutines that currently exist.", - nil, nil), - threadsDesc: NewDesc( - "go_threads", - "Number of OS threads created.", - nil, nil), + goroutines: NewGauge(GaugeOpts{ + Namespace: "go", + Name: "goroutines", + Help: "Number of goroutines that currently exist.", + }), gcDesc: NewDesc( "go_gc_duration_seconds", "A summary of the GC invocation durations.", nil, nil), - goInfoDesc: NewDesc( - "go_info", - "Information about the Go environment.", - nil, Labels{"version": runtime.Version()}), metrics: memStatsMetrics{ { desc: NewDesc( @@ -74,7 +48,7 @@ func NewGoCollector() Collector { }, { desc: NewDesc( memstatNamespace("sys_bytes"), - "Number of bytes obtained from system.", + "Number of bytes obtained by system. Sum of all system allocations.", nil, nil, ), eval: func(ms *runtime.MemStats) float64 { return float64(ms.Sys) }, @@ -137,12 +111,12 @@ func NewGoCollector() Collector { valType: GaugeValue, }, { desc: NewDesc( - memstatNamespace("heap_released_bytes"), - "Number of heap bytes released to OS.", + memstatNamespace("heap_released_bytes_total"), + "Total number of heap bytes released to OS.", nil, nil, ), eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapReleased) }, - valType: GaugeValue, + valType: CounterValue, }, { desc: NewDesc( memstatNamespace("heap_objects"), @@ -239,14 +213,6 @@ func NewGoCollector() Collector { ), eval: func(ms *runtime.MemStats) float64 { return float64(ms.LastGC) / 1e9 }, valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("gc_cpu_fraction"), - "The fraction of this program's available CPU time used by the GC since the program started.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return ms.GCCPUFraction }, - valType: GaugeValue, }, }, } @@ -258,10 +224,9 @@ func memstatNamespace(s string) string { // Describe returns all descriptions of the collector. func (c *goCollector) Describe(ch chan<- *Desc) { - ch <- c.goroutinesDesc - ch <- c.threadsDesc + ch <- c.goroutines.Desc() ch <- c.gcDesc - ch <- c.goInfoDesc + for _, i := range c.metrics { ch <- i.desc } @@ -269,9 +234,8 @@ func (c *goCollector) Describe(ch chan<- *Desc) { // Collect returns the current state of all metrics of the collector. func (c *goCollector) Collect(ch chan<- Metric) { - ch <- MustNewConstMetric(c.goroutinesDesc, GaugeValue, float64(runtime.NumGoroutine())) - n, _ := runtime.ThreadCreateProfile(nil) - ch <- MustNewConstMetric(c.threadsDesc, GaugeValue, float64(n)) + c.goroutines.Set(float64(runtime.NumGoroutine())) + ch <- c.goroutines var stats debug.GCStats stats.PauseQuantiles = make([]time.Duration, 5) @@ -282,9 +246,7 @@ func (c *goCollector) Collect(ch chan<- Metric) { quantiles[float64(idx+1)/float64(len(stats.PauseQuantiles)-1)] = pq.Seconds() } quantiles[0.0] = stats.PauseQuantiles[0].Seconds() - ch <- MustNewConstSummary(c.gcDesc, uint64(stats.NumGC), stats.PauseTotal.Seconds(), quantiles) - - ch <- MustNewConstMetric(c.goInfoDesc, GaugeValue, 1) + ch <- MustNewConstSummary(c.gcDesc, uint64(stats.NumGC), float64(stats.PauseTotal.Seconds()), quantiles) ms := &runtime.MemStats{} runtime.ReadMemStats(ms) diff --git a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go index f88da707b..9719e8fac 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go @@ -16,9 +16,7 @@ package prometheus import ( "fmt" "math" - "runtime" "sort" - "sync" "sync/atomic" "github.com/golang/protobuf/proto" @@ -110,9 +108,8 @@ func ExponentialBuckets(start, factor float64, count int) []float64 { } // HistogramOpts bundles the options for creating a Histogram metric. It is -// mandatory to set Name to a non-empty string. All other fields are optional -// and can safely be left at their zero value, although it is strongly -// encouraged to set a Help string. +// mandatory to set Name and Help to a non-empty string. All other fields are +// optional and can safely be left at their zero value. type HistogramOpts struct { // Namespace, Subsystem, and Name are components of the fully-qualified // name of the Histogram (created by joining these components with @@ -123,22 +120,29 @@ type HistogramOpts struct { Subsystem string Name string - // Help provides information about this Histogram. + // Help provides information about this Histogram. Mandatory! // // Metrics with the same fully-qualified name must have the same Help // string. Help string - // ConstLabels are used to attach fixed labels to this metric. Metrics - // with the same fully-qualified name must have the same label names in - // their ConstLabels. + // ConstLabels are used to attach fixed labels to this + // Histogram. Histograms with the same fully-qualified name must have the + // same label names in their ConstLabels. // - // ConstLabels are only used rarely. In particular, do not use them to - // attach the same labels to all your metrics. Those use cases are - // better covered by target labels set by the scraping Prometheus - // server, or by one specific metric (e.g. a build_info or a - // machine_role metric). See also - // https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels + // Note that in most cases, labels have a value that varies during the + // lifetime of a process. Those labels are usually managed with a + // HistogramVec. ConstLabels serve only special purposes. One is for the + // special case where the value of a label does not change during the + // lifetime of a process, e.g. if the revision of the running binary is + // put into a label. Another, more advanced purpose is if more than one + // Collector needs to collect Histograms with the same fully-qualified + // name. In that case, those Summaries must differ in the values of + // their ConstLabels. See the Collector examples. + // + // If the value of a label never changes (not even between binaries), + // that label most likely should not be a label at all (but part of the + // metric name). ConstLabels Labels // Buckets defines the buckets into which observations are counted. Each @@ -165,7 +169,7 @@ func NewHistogram(opts HistogramOpts) Histogram { func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram { if len(desc.variableLabels) != len(labelValues) { - panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, labelValues)) + panic(errInconsistentCardinality) } for _, n := range desc.variableLabels { @@ -187,7 +191,6 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr desc: desc, upperBounds: opts.Buckets, labelPairs: makeLabelPairs(desc, labelValues), - counts: [2]*histogramCounts{&histogramCounts{}, &histogramCounts{}}, } for i, upperBound := range h.upperBounds { if i < len(h.upperBounds)-1 { @@ -204,53 +207,28 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr } } } - // Finally we know the final length of h.upperBounds and can make counts - // for both states: - h.counts[0].buckets = make([]uint64, len(h.upperBounds)) - h.counts[1].buckets = make([]uint64, len(h.upperBounds)) + // Finally we know the final length of h.upperBounds and can make counts. + h.counts = make([]uint64, len(h.upperBounds)) h.init(h) // Init self-collection. return h } -type histogramCounts struct { +type histogram struct { // sumBits contains the bits of the float64 representing the sum of all // observations. sumBits and count have to go first in the struct to // guarantee alignment for atomic operations. // http://golang.org/pkg/sync/atomic/#pkg-note-BUG sumBits uint64 count uint64 - buckets []uint64 -} - -type histogram struct { - // countAndHotIdx is a complicated one. For lock-free yet atomic - // observations, we need to save the total count of observations again, - // combined with the index of the currently-hot counts struct, so that - // we can perform the operation on both values atomically. The least - // significant bit defines the hot counts struct. The remaining 63 bits - // represent the total count of observations. This happens under the - // assumption that the 63bit count will never overflow. Rationale: An - // observations takes about 30ns. Let's assume it could happen in - // 10ns. Overflowing the counter will then take at least (2^63)*10ns, - // which is about 3000 years. - // - // This has to be first in the struct for 64bit alignment. See - // http://golang.org/pkg/sync/atomic/#pkg-note-BUG - countAndHotIdx uint64 selfCollector - desc *Desc - writeMtx sync.Mutex // Only used in the Write method. + // Note that there is no mutex required. - upperBounds []float64 + desc *Desc - // Two counts, one is "hot" for lock-free observations, the other is - // "cold" for writing out a dto.Metric. It has to be an array of - // pointers to guarantee 64bit alignment of the histogramCounts, see - // http://golang.org/pkg/sync/atomic/#pkg-note-BUG. - counts [2]*histogramCounts - hotIdx int // Index of currently-hot counts. Only used within Write. + upperBounds []float64 + counts []uint64 labelPairs []*dto.LabelPair } @@ -270,113 +248,36 @@ func (h *histogram) Observe(v float64) { // 100 buckets: 78.1 ns/op linear - binary 54.9 ns/op // 300 buckets: 154 ns/op linear - binary 61.6 ns/op i := sort.SearchFloat64s(h.upperBounds, v) - - // We increment h.countAndHotIdx by 2 so that the counter in the upper - // 63 bits gets incremented by 1. At the same time, we get the new value - // back, which we can use to find the currently-hot counts. - n := atomic.AddUint64(&h.countAndHotIdx, 2) - hotCounts := h.counts[n%2] - - if i < len(h.upperBounds) { - atomic.AddUint64(&hotCounts.buckets[i], 1) + if i < len(h.counts) { + atomic.AddUint64(&h.counts[i], 1) } + atomic.AddUint64(&h.count, 1) for { - oldBits := atomic.LoadUint64(&hotCounts.sumBits) + oldBits := atomic.LoadUint64(&h.sumBits) newBits := math.Float64bits(math.Float64frombits(oldBits) + v) - if atomic.CompareAndSwapUint64(&hotCounts.sumBits, oldBits, newBits) { + if atomic.CompareAndSwapUint64(&h.sumBits, oldBits, newBits) { break } } - // Increment count last as we take it as a signal that the observation - // is complete. - atomic.AddUint64(&hotCounts.count, 1) } func (h *histogram) Write(out *dto.Metric) error { - var ( - his = &dto.Histogram{} - buckets = make([]*dto.Bucket, len(h.upperBounds)) - hotCounts, coldCounts *histogramCounts - count uint64 - ) - - // For simplicity, we mutex the rest of this method. It is not in the - // hot path, i.e. Observe is called much more often than Write. The - // complication of making Write lock-free isn't worth it. - h.writeMtx.Lock() - defer h.writeMtx.Unlock() - - // This is a bit arcane, which is why the following spells out this if - // clause in English: - // - // If the currently-hot counts struct is #0, we atomically increment - // h.countAndHotIdx by 1 so that from now on Observe will use the counts - // struct #1. Furthermore, the atomic increment gives us the new value, - // which, in its most significant 63 bits, tells us the count of - // observations done so far up to and including currently ongoing - // observations still using the counts struct just changed from hot to - // cold. To have a normal uint64 for the count, we bitshift by 1 and - // save the result in count. We also set h.hotIdx to 1 for the next - // Write call, and we will refer to counts #1 as hotCounts and to counts - // #0 as coldCounts. - // - // If the currently-hot counts struct is #1, we do the corresponding - // things the other way round. We have to _decrement_ h.countAndHotIdx - // (which is a bit arcane in itself, as we have to express -1 with an - // unsigned int...). - if h.hotIdx == 0 { - count = atomic.AddUint64(&h.countAndHotIdx, 1) >> 1 - h.hotIdx = 1 - hotCounts = h.counts[1] - coldCounts = h.counts[0] - } else { - count = atomic.AddUint64(&h.countAndHotIdx, ^uint64(0)) >> 1 // Decrement. - h.hotIdx = 0 - hotCounts = h.counts[0] - coldCounts = h.counts[1] - } - - // Now we have to wait for the now-declared-cold counts to actually cool - // down, i.e. wait for all observations still using it to finish. That's - // the case once the count in the cold counts struct is the same as the - // one atomically retrieved from the upper 63bits of h.countAndHotIdx. - for { - if count == atomic.LoadUint64(&coldCounts.count) { - break - } - runtime.Gosched() // Let observations get work done. - } + his := &dto.Histogram{} + buckets := make([]*dto.Bucket, len(h.upperBounds)) - his.SampleCount = proto.Uint64(count) - his.SampleSum = proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))) - var cumCount uint64 + his.SampleSum = proto.Float64(math.Float64frombits(atomic.LoadUint64(&h.sumBits))) + his.SampleCount = proto.Uint64(atomic.LoadUint64(&h.count)) + var count uint64 for i, upperBound := range h.upperBounds { - cumCount += atomic.LoadUint64(&coldCounts.buckets[i]) + count += atomic.LoadUint64(&h.counts[i]) buckets[i] = &dto.Bucket{ - CumulativeCount: proto.Uint64(cumCount), + CumulativeCount: proto.Uint64(count), UpperBound: proto.Float64(upperBound), } } - his.Bucket = buckets out.Histogram = his out.Label = h.labelPairs - - // Finally add all the cold counts to the new hot counts and reset the cold counts. - atomic.AddUint64(&hotCounts.count, count) - atomic.StoreUint64(&coldCounts.count, 0) - for { - oldBits := atomic.LoadUint64(&hotCounts.sumBits) - newBits := math.Float64bits(math.Float64frombits(oldBits) + his.GetSampleSum()) - if atomic.CompareAndSwapUint64(&hotCounts.sumBits, oldBits, newBits) { - atomic.StoreUint64(&coldCounts.sumBits, 0) - break - } - } - for i := range h.upperBounds { - atomic.AddUint64(&hotCounts.buckets[i], atomic.LoadUint64(&coldCounts.buckets[i])) - atomic.StoreUint64(&coldCounts.buckets[i], 0) - } return nil } @@ -386,11 +287,12 @@ func (h *histogram) Write(out *dto.Metric) error { // (e.g. HTTP request latencies, partitioned by status code and method). Create // instances with NewHistogramVec. type HistogramVec struct { - *metricVec + *MetricVec } // NewHistogramVec creates a new HistogramVec based on the provided HistogramOpts and -// partitioned by the given label names. +// partitioned by the given label names. At least one label name must be +// provided. func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec { desc := NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), @@ -399,116 +301,47 @@ func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec { opts.ConstLabels, ) return &HistogramVec{ - metricVec: newMetricVec(desc, func(lvs ...string) Metric { + MetricVec: newMetricVec(desc, func(lvs ...string) Metric { return newHistogram(desc, opts, lvs...) }), } } -// GetMetricWithLabelValues returns the Histogram for the given slice of label -// values (same order as the VariableLabels in Desc). If that combination of -// label values is accessed for the first time, a new Histogram is created. -// -// It is possible to call this method without using the returned Histogram to only -// create the new Histogram but leave it at its starting value, a Histogram without -// any observations. -// -// Keeping the Histogram for later use is possible (and should be considered if -// performance is critical), but keep in mind that Reset, DeleteLabelValues and -// Delete can be used to delete the Histogram from the HistogramVec. In that case, the -// Histogram will still exist, but it will not be exported anymore, even if a -// Histogram with the same label values is created later. See also the CounterVec -// example. -// -// An error is returned if the number of label values is not the same as the -// number of VariableLabels in Desc (minus any curried labels). -// -// Note that for more than one label value, this method is prone to mistakes -// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as -// an alternative to avoid that type of mistake. For higher label numbers, the -// latter has a much more readable (albeit more verbose) syntax, but it comes -// with a performance overhead (for creating and processing the Labels map). -// See also the GaugeVec example. -func (v *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) { - metric, err := v.metricVec.getMetricWithLabelValues(lvs...) +// GetMetricWithLabelValues replaces the method of the same name in +// MetricVec. The difference is that this method returns a Histogram and not a +// Metric so that no type conversion is required. +func (m *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Histogram, error) { + metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...) if metric != nil { - return metric.(Observer), err + return metric.(Histogram), err } return nil, err } -// GetMetricWith returns the Histogram for the given Labels map (the label names -// must match those of the VariableLabels in Desc). If that label map is -// accessed for the first time, a new Histogram is created. Implications of -// creating a Histogram without using it and keeping the Histogram for later use -// are the same as for GetMetricWithLabelValues. -// -// An error is returned if the number and names of the Labels are inconsistent -// with those of the VariableLabels in Desc (minus any curried labels). -// -// This method is used for the same purpose as -// GetMetricWithLabelValues(...string). See there for pros and cons of the two -// methods. -func (v *HistogramVec) GetMetricWith(labels Labels) (Observer, error) { - metric, err := v.metricVec.getMetricWith(labels) +// GetMetricWith replaces the method of the same name in MetricVec. The +// difference is that this method returns a Histogram and not a Metric so that no +// type conversion is required. +func (m *HistogramVec) GetMetricWith(labels Labels) (Histogram, error) { + metric, err := m.MetricVec.GetMetricWith(labels) if metric != nil { - return metric.(Observer), err + return metric.(Histogram), err } return nil, err } // WithLabelValues works as GetMetricWithLabelValues, but panics where -// GetMetricWithLabelValues would have returned an error. Not returning an -// error allows shortcuts like +// GetMetricWithLabelValues would have returned an error. By not returning an +// error, WithLabelValues allows shortcuts like // myVec.WithLabelValues("404", "GET").Observe(42.21) -func (v *HistogramVec) WithLabelValues(lvs ...string) Observer { - h, err := v.GetMetricWithLabelValues(lvs...) - if err != nil { - panic(err) - } - return h -} - -// With works as GetMetricWith but panics where GetMetricWithLabels would have -// returned an error. Not returning an error allows shortcuts like -// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21) -func (v *HistogramVec) With(labels Labels) Observer { - h, err := v.GetMetricWith(labels) - if err != nil { - panic(err) - } - return h -} - -// CurryWith returns a vector curried with the provided labels, i.e. the -// returned vector has those labels pre-set for all labeled operations performed -// on it. The cardinality of the curried vector is reduced accordingly. The -// order of the remaining labels stays the same (just with the curried labels -// taken out of the sequence – which is relevant for the -// (GetMetric)WithLabelValues methods). It is possible to curry a curried -// vector, but only with labels not yet used for currying before. -// -// The metrics contained in the HistogramVec are shared between the curried and -// uncurried vectors. They are just accessed differently. Curried and uncurried -// vectors behave identically in terms of collection. Only one must be -// registered with a given registry (usually the uncurried version). The Reset -// method deletes all metrics, even if called on a curried vector. -func (v *HistogramVec) CurryWith(labels Labels) (ObserverVec, error) { - vec, err := v.curryWith(labels) - if vec != nil { - return &HistogramVec{vec}, err - } - return nil, err +func (m *HistogramVec) WithLabelValues(lvs ...string) Histogram { + return m.MetricVec.WithLabelValues(lvs...).(Histogram) } -// MustCurryWith works as CurryWith but panics where CurryWith would have -// returned an error. -func (v *HistogramVec) MustCurryWith(labels Labels) ObserverVec { - vec, err := v.CurryWith(labels) - if err != nil { - panic(err) - } - return vec +// With works as GetMetricWith, but panics where GetMetricWithLabels would have +// returned an error. By not returning an error, With allows shortcuts like +// myVec.With(Labels{"code": "404", "method": "GET"}).Observe(42.21) +func (m *HistogramVec) With(labels Labels) Histogram { + return m.MetricVec.With(labels).(Histogram) } type constHistogram struct { @@ -560,7 +393,7 @@ func (h *constHistogram) Write(out *dto.Metric) error { // bucket. // // NewConstHistogram returns an error if the length of labelValues is not -// consistent with the variable labels in Desc or if Desc is invalid. +// consistent with the variable labels in Desc. func NewConstHistogram( desc *Desc, count uint64, @@ -568,11 +401,8 @@ func NewConstHistogram( buckets map[float64]uint64, labelValues ...string, ) (Metric, error) { - if desc.err != nil { - return nil, desc.err - } - if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil { - return nil, err + if len(desc.variableLabels) != len(labelValues) { + return nil, errInconsistentCardinality } return &constHistogram{ desc: desc, diff --git a/vendor/github.com/prometheus/client_golang/prometheus/http.go b/vendor/github.com/prometheus/client_golang/prometheus/http.go index 9f0875bfc..67ee5ac79 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/http.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/http.go @@ -15,7 +15,9 @@ package prometheus import ( "bufio" + "bytes" "compress/gzip" + "fmt" "io" "net" "net/http" @@ -39,10 +41,19 @@ const ( acceptEncodingHeader = "Accept-Encoding" ) -var gzipPool = sync.Pool{ - New: func() interface{} { - return gzip.NewWriter(nil) - }, +var bufPool sync.Pool + +func getBuf() *bytes.Buffer { + buf := bufPool.Get() + if buf == nil { + return &bytes.Buffer{} + } + return buf.(*bytes.Buffer) +} + +func giveBuf(buf *bytes.Buffer) { + buf.Reset() + bufPool.Put(buf) } // Handler returns an HTTP handler for the DefaultGatherer. It is @@ -50,50 +61,68 @@ var gzipPool = sync.Pool{ // name). // // Deprecated: Please note the issues described in the doc comment of -// InstrumentHandler. You might want to consider using promhttp.Handler instead. +// InstrumentHandler. You might want to consider using promhttp.Handler instead +// (which is non instrumented). func Handler() http.Handler { return InstrumentHandler("prometheus", UninstrumentedHandler()) } // UninstrumentedHandler returns an HTTP handler for the DefaultGatherer. // -// Deprecated: Use promhttp.HandlerFor(DefaultGatherer, promhttp.HandlerOpts{}) -// instead. See there for further documentation. +// Deprecated: Use promhttp.Handler instead. See there for further documentation. func UninstrumentedHandler() http.Handler { - return http.HandlerFunc(func(rsp http.ResponseWriter, req *http.Request) { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { mfs, err := DefaultGatherer.Gather() if err != nil { - httpError(rsp, err) + http.Error(w, "An error has occurred during metrics collection:\n\n"+err.Error(), http.StatusInternalServerError) return } contentType := expfmt.Negotiate(req.Header) - header := rsp.Header() - header.Set(contentTypeHeader, string(contentType)) - - w := io.Writer(rsp) - if gzipAccepted(req.Header) { - header.Set(contentEncodingHeader, "gzip") - gz := gzipPool.Get().(*gzip.Writer) - defer gzipPool.Put(gz) - - gz.Reset(w) - defer gz.Close() - - w = gz - } - - enc := expfmt.NewEncoder(w, contentType) - + buf := getBuf() + defer giveBuf(buf) + writer, encoding := decorateWriter(req, buf) + enc := expfmt.NewEncoder(writer, contentType) + var lastErr error for _, mf := range mfs { if err := enc.Encode(mf); err != nil { - httpError(rsp, err) + lastErr = err + http.Error(w, "An error has occurred during metrics encoding:\n\n"+err.Error(), http.StatusInternalServerError) return } } + if closer, ok := writer.(io.Closer); ok { + closer.Close() + } + if lastErr != nil && buf.Len() == 0 { + http.Error(w, "No metrics encoded, last error:\n\n"+err.Error(), http.StatusInternalServerError) + return + } + header := w.Header() + header.Set(contentTypeHeader, string(contentType)) + header.Set(contentLengthHeader, fmt.Sprint(buf.Len())) + if encoding != "" { + header.Set(contentEncodingHeader, encoding) + } + w.Write(buf.Bytes()) }) } +// decorateWriter wraps a writer to handle gzip compression if requested. It +// returns the decorated writer and the appropriate "Content-Encoding" header +// (which is empty if no compression is enabled). +func decorateWriter(request *http.Request, writer io.Writer) (io.Writer, string) { + header := request.Header.Get(acceptEncodingHeader) + parts := strings.Split(header, ",") + for _, part := range parts { + part := strings.TrimSpace(part) + if part == "gzip" || strings.HasPrefix(part, "gzip;") { + return gzip.NewWriter(writer), "gzip" + } + } + return writer, "" +} + var instLabels = []string{"method", "code"} type nower interface { @@ -110,6 +139,16 @@ var now nower = nowFunc(func() time.Time { return time.Now() }) +func nowSeries(t ...time.Time) nower { + return nowFunc(func() time.Time { + defer func() { + t = t[1:] + }() + + return t[0] + }) +} + // InstrumentHandler wraps the given HTTP handler for instrumentation. It // registers four metric collectors (if not already done) and reports HTTP // metrics to the (newly or already) registered collectors: http_requests_total @@ -119,16 +158,23 @@ var now nower = nowFunc(func() time.Time { // value. http_requests_total is a metric vector partitioned by HTTP method // (label name "method") and HTTP status code (label name "code"). // -// Deprecated: InstrumentHandler has several issues. Use the tooling provided in -// package promhttp instead. The issues are the following: (1) It uses Summaries -// rather than Histograms. Summaries are not useful if aggregation across -// multiple instances is required. (2) It uses microseconds as unit, which is -// deprecated and should be replaced by seconds. (3) The size of the request is -// calculated in a separate goroutine. Since this calculator requires access to -// the request header, it creates a race with any writes to the header performed -// during request handling. httputil.ReverseProxy is a prominent example for a -// handler performing such writes. (4) It has additional issues with HTTP/2, cf. -// https://github.com/prometheus/client_golang/issues/272. +// Deprecated: InstrumentHandler has several issues: +// +// - It uses Summaries rather than Histograms. Summaries are not useful if +// aggregation across multiple instances is required. +// +// - It uses microseconds as unit, which is deprecated and should be replaced by +// seconds. +// +// - The size of the request is calculated in a separate goroutine. Since this +// calculator requires access to the request header, it creates a race with +// any writes to the header performed during request handling. +// httputil.ReverseProxy is a prominent example for a handler +// performing such writes. +// +// Upcoming versions of this package will provide ways of instrumenting HTTP +// handlers that are more flexible and have fewer issues. Please prefer direct +// instrumentation in the meantime. func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFunc { return InstrumentHandlerFunc(handlerName, handler.ServeHTTP) } @@ -138,13 +184,12 @@ func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFun // issues). // // Deprecated: InstrumentHandlerFunc is deprecated for the same reasons as -// InstrumentHandler is. Use the tooling provided in package promhttp instead. +// InstrumentHandler is. func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc { return InstrumentHandlerFuncWithOpts( SummaryOpts{ Subsystem: "http", ConstLabels: Labels{"handler": handlerName}, - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, }, handlerFunc, ) @@ -177,7 +222,7 @@ func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWri // SummaryOpts. // // Deprecated: InstrumentHandlerWithOpts is deprecated for the same reasons as -// InstrumentHandler is. Use the tooling provided in package promhttp instead. +// InstrumentHandler is. func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.HandlerFunc { return InstrumentHandlerFuncWithOpts(opts, handler.ServeHTTP) } @@ -188,7 +233,7 @@ func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.Hand // SummaryOpts are used. // // Deprecated: InstrumentHandlerFuncWithOpts is deprecated for the same reasons -// as InstrumentHandler is. Use the tooling provided in package promhttp instead. +// as InstrumentHandler is. func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc { reqCnt := NewCounterVec( CounterOpts{ @@ -200,52 +245,34 @@ func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.Respo }, instLabels, ) - if err := Register(reqCnt); err != nil { - if are, ok := err.(AlreadyRegisteredError); ok { - reqCnt = are.ExistingCollector.(*CounterVec) - } else { - panic(err) - } - } opts.Name = "request_duration_microseconds" opts.Help = "The HTTP request latencies in microseconds." reqDur := NewSummary(opts) - if err := Register(reqDur); err != nil { - if are, ok := err.(AlreadyRegisteredError); ok { - reqDur = are.ExistingCollector.(Summary) - } else { - panic(err) - } - } opts.Name = "request_size_bytes" opts.Help = "The HTTP request sizes in bytes." reqSz := NewSummary(opts) - if err := Register(reqSz); err != nil { - if are, ok := err.(AlreadyRegisteredError); ok { - reqSz = are.ExistingCollector.(Summary) - } else { - panic(err) - } - } opts.Name = "response_size_bytes" opts.Help = "The HTTP response sizes in bytes." resSz := NewSummary(opts) - if err := Register(resSz); err != nil { - if are, ok := err.(AlreadyRegisteredError); ok { - resSz = are.ExistingCollector.(Summary) - } else { - panic(err) - } - } + + regReqCnt := MustRegisterOrGet(reqCnt).(*CounterVec) + regReqDur := MustRegisterOrGet(reqDur).(Summary) + regReqSz := MustRegisterOrGet(reqSz).(Summary) + regResSz := MustRegisterOrGet(resSz).(Summary) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { now := time.Now() delegate := &responseWriterDelegator{ResponseWriter: w} - out := computeApproximateRequestSize(r) + out := make(chan int) + urlLen := 0 + if r.URL != nil { + urlLen = len(r.URL.String()) + } + go computeApproximateRequestSize(r, out, urlLen) _, cn := w.(http.CloseNotifier) _, fl := w.(http.Flusher) @@ -263,52 +290,39 @@ func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.Respo method := sanitizeMethod(r.Method) code := sanitizeCode(delegate.status) - reqCnt.WithLabelValues(method, code).Inc() - reqDur.Observe(elapsed) - resSz.Observe(float64(delegate.written)) - reqSz.Observe(float64(<-out)) + regReqCnt.WithLabelValues(method, code).Inc() + regReqDur.Observe(elapsed) + regResSz.Observe(float64(delegate.written)) + regReqSz.Observe(float64(<-out)) }) } -func computeApproximateRequestSize(r *http.Request) <-chan int { - // Get URL length in current goroutine for avoiding a race condition. - // HandlerFunc that runs in parallel may modify the URL. - s := 0 - if r.URL != nil { - s += len(r.URL.String()) - } - - out := make(chan int, 1) - - go func() { - s += len(r.Method) - s += len(r.Proto) - for name, values := range r.Header { - s += len(name) - for _, value := range values { - s += len(value) - } +func computeApproximateRequestSize(r *http.Request, out chan int, s int) { + s += len(r.Method) + s += len(r.Proto) + for name, values := range r.Header { + s += len(name) + for _, value := range values { + s += len(value) } - s += len(r.Host) - - // N.B. r.Form and r.MultipartForm are assumed to be included in r.URL. + } + s += len(r.Host) - if r.ContentLength != -1 { - s += int(r.ContentLength) - } - out <- s - close(out) - }() + // N.B. r.Form and r.MultipartForm are assumed to be included in r.URL. - return out + if r.ContentLength != -1 { + s += int(r.ContentLength) + } + out <- s } type responseWriterDelegator struct { http.ResponseWriter - status int - written int64 - wroteHeader bool + handler, method string + status int + written int64 + wroteHeader bool } func (r *responseWriterDelegator) WriteHeader(code int) { @@ -474,31 +488,3 @@ func sanitizeCode(s int) string { return strconv.Itoa(s) } } - -// gzipAccepted returns whether the client will accept gzip-encoded content. -func gzipAccepted(header http.Header) bool { - a := header.Get(acceptEncodingHeader) - parts := strings.Split(a, ",") - for _, part := range parts { - part = strings.TrimSpace(part) - if part == "gzip" || strings.HasPrefix(part, "gzip;") { - return true - } - } - return false -} - -// httpError removes any content-encoding header and then calls http.Error with -// the provided error and http.StatusInternalServerErrer. Error contents is -// supposed to be uncompressed plain text. However, same as with a plain -// http.Error, any header settings will be void if the header has already been -// sent. The error message will still be written to the writer, but it will -// probably be of limited use. -func httpError(rsp http.ResponseWriter, err error) { - rsp.Header().Del(contentEncodingHeader) - http.Error( - rsp, - "An error has occurred while serving metrics:\n\n"+err.Error(), - http.StatusInternalServerError, - ) -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/internal/metric.go b/vendor/github.com/prometheus/client_golang/prometheus/internal/metric.go deleted file mode 100644 index 351c26e1a..000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/internal/metric.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2018 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package internal - -import ( - "sort" - - dto "github.com/prometheus/client_model/go" -) - -// metricSorter is a sortable slice of *dto.Metric. -type metricSorter []*dto.Metric - -func (s metricSorter) Len() int { - return len(s) -} - -func (s metricSorter) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -func (s metricSorter) Less(i, j int) bool { - if len(s[i].Label) != len(s[j].Label) { - // This should not happen. The metrics are - // inconsistent. However, we have to deal with the fact, as - // people might use custom collectors or metric family injection - // to create inconsistent metrics. So let's simply compare the - // number of labels in this case. That will still yield - // reproducible sorting. - return len(s[i].Label) < len(s[j].Label) - } - for n, lp := range s[i].Label { - vi := lp.GetValue() - vj := s[j].Label[n].GetValue() - if vi != vj { - return vi < vj - } - } - - // We should never arrive here. Multiple metrics with the same - // label set in the same scrape will lead to undefined ingestion - // behavior. However, as above, we have to provide stable sorting - // here, even for inconsistent metrics. So sort equal metrics - // by their timestamp, with missing timestamps (implying "now") - // coming last. - if s[i].TimestampMs == nil { - return false - } - if s[j].TimestampMs == nil { - return true - } - return s[i].GetTimestampMs() < s[j].GetTimestampMs() -} - -// NormalizeMetricFamilies returns a MetricFamily slice with empty -// MetricFamilies pruned and the remaining MetricFamilies sorted by name within -// the slice, with the contained Metrics sorted within each MetricFamily. -func NormalizeMetricFamilies(metricFamiliesByName map[string]*dto.MetricFamily) []*dto.MetricFamily { - for _, mf := range metricFamiliesByName { - sort.Sort(metricSorter(mf.Metric)) - } - names := make([]string, 0, len(metricFamiliesByName)) - for name, mf := range metricFamiliesByName { - if len(mf.Metric) > 0 { - names = append(names, name) - } - } - sort.Strings(names) - result := make([]*dto.MetricFamily, 0, len(names)) - for _, name := range names { - result = append(result, metricFamiliesByName[name]) - } - return result -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/labels.go b/vendor/github.com/prometheus/client_golang/prometheus/labels.go deleted file mode 100644 index 2744443ac..000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/labels.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2018 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus - -import ( - "errors" - "fmt" - "strings" - "unicode/utf8" - - "github.com/prometheus/common/model" -) - -// Labels represents a collection of label name -> value mappings. This type is -// commonly used with the With(Labels) and GetMetricWith(Labels) methods of -// metric vector Collectors, e.g.: -// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42) -// -// The other use-case is the specification of constant label pairs in Opts or to -// create a Desc. -type Labels map[string]string - -// reservedLabelPrefix is a prefix which is not legal in user-supplied -// label names. -const reservedLabelPrefix = "__" - -var errInconsistentCardinality = errors.New("inconsistent label cardinality") - -func makeInconsistentCardinalityError(fqName string, labels, labelValues []string) error { - return fmt.Errorf( - "%s: %q has %d variable labels named %q but %d values %q were provided", - errInconsistentCardinality, fqName, - len(labels), labels, - len(labelValues), labelValues, - ) -} - -func validateValuesInLabels(labels Labels, expectedNumberOfValues int) error { - if len(labels) != expectedNumberOfValues { - return fmt.Errorf( - "%s: expected %d label values but got %d in %#v", - errInconsistentCardinality, expectedNumberOfValues, - len(labels), labels, - ) - } - - for name, val := range labels { - if !utf8.ValidString(val) { - return fmt.Errorf("label %s: value %q is not valid UTF-8", name, val) - } - } - - return nil -} - -func validateLabelValues(vals []string, expectedNumberOfValues int) error { - if len(vals) != expectedNumberOfValues { - return fmt.Errorf( - "%s: expected %d label values but got %d in %#v", - errInconsistentCardinality, expectedNumberOfValues, - len(vals), vals, - ) - } - - for _, val := range vals { - if !utf8.ValidString(val) { - return fmt.Errorf("label value %q is not valid UTF-8", val) - } - } - - return nil -} - -func checkLabelName(l string) bool { - return model.LabelName(l).IsValid() && !strings.HasPrefix(l, reservedLabelPrefix) -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/metric.go b/vendor/github.com/prometheus/client_golang/prometheus/metric.go index 55e6d86d5..d4063d98f 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/metric.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/metric.go @@ -15,9 +15,6 @@ package prometheus import ( "strings" - "time" - - "github.com/golang/protobuf/proto" dto "github.com/prometheus/client_model/go" ) @@ -46,8 +43,9 @@ type Metric interface { // While populating dto.Metric, it is the responsibility of the // implementation to ensure validity of the Metric protobuf (like valid // UTF-8 strings or syntactically valid metric and label names). It is - // recommended to sort labels lexicographically. Callers of Write should - // still make sure of sorting if they depend on it. + // recommended to sort labels lexicographically. (Implementers may find + // LabelPairSorter useful for that.) Callers of Write should still make + // sure of sorting if they depend on it. Write(*dto.Metric) error // TODO(beorn7): The original rationale of passing in a pre-allocated // dto.Metric protobuf to save allocations has disappeared. The @@ -59,9 +57,8 @@ type Metric interface { // implementation XXX has its own XXXOpts type, but in most cases, it is just be // an alias of this type (which might change when the requirement arises.) // -// It is mandatory to set Name to a non-empty string. All other fields are -// optional and can safely be left at their zero value, although it is strongly -// encouraged to set a Help string. +// It is mandatory to set Name and Help to a non-empty string. All other fields +// are optional and can safely be left at their zero value. type Opts struct { // Namespace, Subsystem, and Name are components of the fully-qualified // name of the Metric (created by joining these components with @@ -72,7 +69,7 @@ type Opts struct { Subsystem string Name string - // Help provides information about this metric. + // Help provides information about this metric. Mandatory! // // Metrics with the same fully-qualified name must have the same Help // string. @@ -82,12 +79,20 @@ type Opts struct { // with the same fully-qualified name must have the same label names in // their ConstLabels. // - // ConstLabels are only used rarely. In particular, do not use them to - // attach the same labels to all your metrics. Those use cases are - // better covered by target labels set by the scraping Prometheus - // server, or by one specific metric (e.g. a build_info or a - // machine_role metric). See also - // https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels + // Note that in most cases, labels have a value that varies during the + // lifetime of a process. Those labels are usually managed with a metric + // vector collector (like CounterVec, GaugeVec, UntypedVec). ConstLabels + // serve only special purposes. One is for the special case where the + // value of a label does not change during the lifetime of a process, + // e.g. if the revision of the running binary is put into a + // label. Another, more advanced purpose is if more than one Collector + // needs to collect Metrics with the same fully-qualified name. In that + // case, those Metrics must differ in the values of their + // ConstLabels. See the Collector examples. + // + // If the value of a label never changes (not even between binaries), + // that label most likely should not be a label at all (but part of the + // metric name). ConstLabels Labels } @@ -113,22 +118,37 @@ func BuildFQName(namespace, subsystem, name string) string { return name } -// labelPairSorter implements sort.Interface. It is used to sort a slice of -// dto.LabelPair pointers. -type labelPairSorter []*dto.LabelPair +// LabelPairSorter implements sort.Interface. It is used to sort a slice of +// dto.LabelPair pointers. This is useful for implementing the Write method of +// custom metrics. +type LabelPairSorter []*dto.LabelPair -func (s labelPairSorter) Len() int { +func (s LabelPairSorter) Len() int { return len(s) } -func (s labelPairSorter) Swap(i, j int) { +func (s LabelPairSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s labelPairSorter) Less(i, j int) bool { +func (s LabelPairSorter) Less(i, j int) bool { return s[i].GetName() < s[j].GetName() } +type hashSorter []uint64 + +func (s hashSorter) Len() int { + return len(s) +} + +func (s hashSorter) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s hashSorter) Less(i, j int) bool { + return s[i] < s[j] +} + type invalidMetric struct { desc *Desc err error @@ -144,31 +164,3 @@ func NewInvalidMetric(desc *Desc, err error) Metric { func (m *invalidMetric) Desc() *Desc { return m.desc } func (m *invalidMetric) Write(*dto.Metric) error { return m.err } - -type timestampedMetric struct { - Metric - t time.Time -} - -func (m timestampedMetric) Write(pb *dto.Metric) error { - e := m.Metric.Write(pb) - pb.TimestampMs = proto.Int64(m.t.Unix()*1000 + int64(m.t.Nanosecond()/1000000)) - return e -} - -// NewMetricWithTimestamp returns a new Metric wrapping the provided Metric in a -// way that it has an explicit timestamp set to the provided Time. This is only -// useful in rare cases as the timestamp of a Prometheus metric should usually -// be set by the Prometheus server during scraping. Exceptions include mirroring -// metrics with given timestamps from other metric -// sources. -// -// NewMetricWithTimestamp works best with MustNewConstMetric, -// MustNewConstHistogram, and MustNewConstSummary, see example. -// -// Currently, the exposition formats used by Prometheus are limited to -// millisecond resolution. Thus, the provided time will be rounded down to the -// next full millisecond value. -func NewMetricWithTimestamp(t time.Time, m Metric) Metric { - return timestampedMetric{Metric: m, t: t} -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/observer.go b/vendor/github.com/prometheus/client_golang/prometheus/observer.go deleted file mode 100644 index 5806cd09e..000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/observer.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2017 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus - -// Observer is the interface that wraps the Observe method, which is used by -// Histogram and Summary to add observations. -type Observer interface { - Observe(float64) -} - -// The ObserverFunc type is an adapter to allow the use of ordinary -// functions as Observers. If f is a function with the appropriate -// signature, ObserverFunc(f) is an Observer that calls f. -// -// This adapter is usually used in connection with the Timer type, and there are -// two general use cases: -// -// The most common one is to use a Gauge as the Observer for a Timer. -// See the "Gauge" Timer example. -// -// The more advanced use case is to create a function that dynamically decides -// which Observer to use for observing the duration. See the "Complex" Timer -// example. -type ObserverFunc func(float64) - -// Observe calls f(value). It implements Observer. -func (f ObserverFunc) Observe(value float64) { - f(value) -} - -// ObserverVec is an interface implemented by `HistogramVec` and `SummaryVec`. -type ObserverVec interface { - GetMetricWith(Labels) (Observer, error) - GetMetricWithLabelValues(lvs ...string) (Observer, error) - With(Labels) Observer - WithLabelValues(...string) Observer - CurryWith(Labels) (ObserverVec, error) - MustCurryWith(Labels) ObserverVec - - Collector -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector.go index 55176d58c..e31e62e78 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/process_collector.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector.go @@ -13,139 +13,89 @@ package prometheus -import ( - "errors" - "os" - - "github.com/prometheus/procfs" -) +import "github.com/prometheus/procfs" type processCollector struct { + pid int collectFn func(chan<- Metric) pidFn func() (int, error) - reportErrors bool - cpuTotal *Desc - openFDs, maxFDs *Desc - vsize, maxVsize *Desc - rss *Desc - startTime *Desc -} - -// ProcessCollectorOpts defines the behavior of a process metrics collector -// created with NewProcessCollector. -type ProcessCollectorOpts struct { - // PidFn returns the PID of the process the collector collects metrics - // for. It is called upon each collection. By default, the PID of the - // current process is used, as determined on construction time by - // calling os.Getpid(). - PidFn func() (int, error) - // If non-empty, each of the collected metrics is prefixed by the - // provided string and an underscore ("_"). - Namespace string - // If true, any error encountered during collection is reported as an - // invalid metric (see NewInvalidMetric). Otherwise, errors are ignored - // and the collected metrics will be incomplete. (Possibly, no metrics - // will be collected at all.) While that's usually not desired, it is - // appropriate for the common "mix-in" of process metrics, where process - // metrics are nice to have, but failing to collect them should not - // disrupt the collection of the remaining metrics. - ReportErrors bool + cpuTotal Counter + openFDs, maxFDs Gauge + vsize, rss Gauge + startTime Gauge } // NewProcessCollector returns a collector which exports the current state of -// process metrics including CPU, memory and file descriptor usage as well as -// the process start time. The detailed behavior is defined by the provided -// ProcessCollectorOpts. The zero value of ProcessCollectorOpts creates a -// collector for the current process with an empty namespace string and no error -// reporting. -// -// Currently, the collector depends on a Linux-style proc filesystem and -// therefore only exports metrics for Linux. -// -// Note: An older version of this function had the following signature: -// -// NewProcessCollector(pid int, namespace string) Collector -// -// Most commonly, it was called as -// -// NewProcessCollector(os.Getpid(), "") -// -// The following call of the current version is equivalent to the above: -// -// NewProcessCollector(ProcessCollectorOpts{}) -func NewProcessCollector(opts ProcessCollectorOpts) Collector { - ns := "" - if len(opts.Namespace) > 0 { - ns = opts.Namespace + "_" - } - - c := &processCollector{ - reportErrors: opts.ReportErrors, - cpuTotal: NewDesc( - ns+"process_cpu_seconds_total", - "Total user and system CPU time spent in seconds.", - nil, nil, - ), - openFDs: NewDesc( - ns+"process_open_fds", - "Number of open file descriptors.", - nil, nil, - ), - maxFDs: NewDesc( - ns+"process_max_fds", - "Maximum number of open file descriptors.", - nil, nil, - ), - vsize: NewDesc( - ns+"process_virtual_memory_bytes", - "Virtual memory size in bytes.", - nil, nil, - ), - maxVsize: NewDesc( - ns+"process_virtual_memory_max_bytes", - "Maximum amount of virtual memory available in bytes.", - nil, nil, - ), - rss: NewDesc( - ns+"process_resident_memory_bytes", - "Resident memory size in bytes.", - nil, nil, - ), - startTime: NewDesc( - ns+"process_start_time_seconds", - "Start time of the process since unix epoch in seconds.", - nil, nil, - ), - } +// process metrics including cpu, memory and file descriptor usage as well as +// the process start time for the given process id under the given namespace. +func NewProcessCollector(pid int, namespace string) Collector { + return NewProcessCollectorPIDFn( + func() (int, error) { return pid, nil }, + namespace, + ) +} - if opts.PidFn == nil { - pid := os.Getpid() - c.pidFn = func() (int, error) { return pid, nil } - } else { - c.pidFn = opts.PidFn +// NewProcessCollectorPIDFn returns a collector which exports the current state +// of process metrics including cpu, memory and file descriptor usage as well +// as the process start time under the given namespace. The given pidFn is +// called on each collect and is used to determine the process to export +// metrics for. +func NewProcessCollectorPIDFn( + pidFn func() (int, error), + namespace string, +) Collector { + c := processCollector{ + pidFn: pidFn, + collectFn: func(chan<- Metric) {}, + + cpuTotal: NewCounter(CounterOpts{ + Namespace: namespace, + Name: "process_cpu_seconds_total", + Help: "Total user and system CPU time spent in seconds.", + }), + openFDs: NewGauge(GaugeOpts{ + Namespace: namespace, + Name: "process_open_fds", + Help: "Number of open file descriptors.", + }), + maxFDs: NewGauge(GaugeOpts{ + Namespace: namespace, + Name: "process_max_fds", + Help: "Maximum number of open file descriptors.", + }), + vsize: NewGauge(GaugeOpts{ + Namespace: namespace, + Name: "process_virtual_memory_bytes", + Help: "Virtual memory size in bytes.", + }), + rss: NewGauge(GaugeOpts{ + Namespace: namespace, + Name: "process_resident_memory_bytes", + Help: "Resident memory size in bytes.", + }), + startTime: NewGauge(GaugeOpts{ + Namespace: namespace, + Name: "process_start_time_seconds", + Help: "Start time of the process since unix epoch in seconds.", + }), } // Set up process metric collection if supported by the runtime. if _, err := procfs.NewStat(); err == nil { c.collectFn = c.processCollect - } else { - c.collectFn = func(ch chan<- Metric) { - c.reportError(ch, nil, errors.New("process metrics not supported on this platform")) - } } - return c + return &c } // Describe returns all descriptions of the collector. func (c *processCollector) Describe(ch chan<- *Desc) { - ch <- c.cpuTotal - ch <- c.openFDs - ch <- c.maxFDs - ch <- c.vsize - ch <- c.maxVsize - ch <- c.rss - ch <- c.startTime + ch <- c.cpuTotal.Desc() + ch <- c.openFDs.Desc() + ch <- c.maxFDs.Desc() + ch <- c.vsize.Desc() + ch <- c.rss.Desc() + ch <- c.startTime.Desc() } // Collect returns the current state of all metrics of the collector. @@ -153,52 +103,40 @@ func (c *processCollector) Collect(ch chan<- Metric) { c.collectFn(ch) } +// TODO(ts): Bring back error reporting by reverting 7faf9e7 as soon as the +// client allows users to configure the error behavior. func (c *processCollector) processCollect(ch chan<- Metric) { pid, err := c.pidFn() if err != nil { - c.reportError(ch, nil, err) return } p, err := procfs.NewProc(pid) if err != nil { - c.reportError(ch, nil, err) return } if stat, err := p.NewStat(); err == nil { - ch <- MustNewConstMetric(c.cpuTotal, CounterValue, stat.CPUTime()) - ch <- MustNewConstMetric(c.vsize, GaugeValue, float64(stat.VirtualMemory())) - ch <- MustNewConstMetric(c.rss, GaugeValue, float64(stat.ResidentMemory())) + c.cpuTotal.Set(stat.CPUTime()) + ch <- c.cpuTotal + c.vsize.Set(float64(stat.VirtualMemory())) + ch <- c.vsize + c.rss.Set(float64(stat.ResidentMemory())) + ch <- c.rss + if startTime, err := stat.StartTime(); err == nil { - ch <- MustNewConstMetric(c.startTime, GaugeValue, startTime) - } else { - c.reportError(ch, c.startTime, err) + c.startTime.Set(startTime) + ch <- c.startTime } - } else { - c.reportError(ch, nil, err) } if fds, err := p.FileDescriptorsLen(); err == nil { - ch <- MustNewConstMetric(c.openFDs, GaugeValue, float64(fds)) - } else { - c.reportError(ch, c.openFDs, err) + c.openFDs.Set(float64(fds)) + ch <- c.openFDs } if limits, err := p.NewLimits(); err == nil { - ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(limits.OpenFiles)) - ch <- MustNewConstMetric(c.maxVsize, GaugeValue, float64(limits.AddressSpace)) - } else { - c.reportError(ch, nil, err) - } -} - -func (c *processCollector) reportError(ch chan<- Metric, desc *Desc, err error) { - if !c.reportErrors { - return - } - if desc == nil { - desc = NewInvalidDesc(err) + c.maxFDs.Set(float64(limits.OpenFiles)) + ch <- c.maxFDs } - ch <- NewInvalidMetric(desc, err) } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go deleted file mode 100644 index 67b56d37c..000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2017 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package promhttp - -import ( - "bufio" - "io" - "net" - "net/http" -) - -const ( - closeNotifier = 1 << iota - flusher - hijacker - readerFrom - pusher -) - -type delegator interface { - http.ResponseWriter - - Status() int - Written() int64 -} - -type responseWriterDelegator struct { - http.ResponseWriter - - handler, method string - status int - written int64 - wroteHeader bool - observeWriteHeader func(int) -} - -func (r *responseWriterDelegator) Status() int { - return r.status -} - -func (r *responseWriterDelegator) Written() int64 { - return r.written -} - -func (r *responseWriterDelegator) WriteHeader(code int) { - r.status = code - r.wroteHeader = true - r.ResponseWriter.WriteHeader(code) - if r.observeWriteHeader != nil { - r.observeWriteHeader(code) - } -} - -func (r *responseWriterDelegator) Write(b []byte) (int, error) { - if !r.wroteHeader { - r.WriteHeader(http.StatusOK) - } - n, err := r.ResponseWriter.Write(b) - r.written += int64(n) - return n, err -} - -type closeNotifierDelegator struct{ *responseWriterDelegator } -type flusherDelegator struct{ *responseWriterDelegator } -type hijackerDelegator struct{ *responseWriterDelegator } -type readerFromDelegator struct{ *responseWriterDelegator } - -func (d closeNotifierDelegator) CloseNotify() <-chan bool { - return d.ResponseWriter.(http.CloseNotifier).CloseNotify() -} -func (d flusherDelegator) Flush() { - d.ResponseWriter.(http.Flusher).Flush() -} -func (d hijackerDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) { - return d.ResponseWriter.(http.Hijacker).Hijack() -} -func (d readerFromDelegator) ReadFrom(re io.Reader) (int64, error) { - if !d.wroteHeader { - d.WriteHeader(http.StatusOK) - } - n, err := d.ResponseWriter.(io.ReaderFrom).ReadFrom(re) - d.written += n - return n, err -} - -var pickDelegator = make([]func(*responseWriterDelegator) delegator, 32) - -func init() { - // TODO(beorn7): Code generation would help here. - pickDelegator[0] = func(d *responseWriterDelegator) delegator { // 0 - return d - } - pickDelegator[closeNotifier] = func(d *responseWriterDelegator) delegator { // 1 - return closeNotifierDelegator{d} - } - pickDelegator[flusher] = func(d *responseWriterDelegator) delegator { // 2 - return flusherDelegator{d} - } - pickDelegator[flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 3 - return struct { - *responseWriterDelegator - http.Flusher - http.CloseNotifier - }{d, flusherDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[hijacker] = func(d *responseWriterDelegator) delegator { // 4 - return hijackerDelegator{d} - } - pickDelegator[hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 5 - return struct { - *responseWriterDelegator - http.Hijacker - http.CloseNotifier - }{d, hijackerDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 6 - return struct { - *responseWriterDelegator - http.Hijacker - http.Flusher - }{d, hijackerDelegator{d}, flusherDelegator{d}} - } - pickDelegator[hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 7 - return struct { - *responseWriterDelegator - http.Hijacker - http.Flusher - http.CloseNotifier - }{d, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[readerFrom] = func(d *responseWriterDelegator) delegator { // 8 - return readerFromDelegator{d} - } - pickDelegator[readerFrom+closeNotifier] = func(d *responseWriterDelegator) delegator { // 9 - return struct { - *responseWriterDelegator - io.ReaderFrom - http.CloseNotifier - }{d, readerFromDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[readerFrom+flusher] = func(d *responseWriterDelegator) delegator { // 10 - return struct { - *responseWriterDelegator - io.ReaderFrom - http.Flusher - }{d, readerFromDelegator{d}, flusherDelegator{d}} - } - pickDelegator[readerFrom+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 11 - return struct { - *responseWriterDelegator - io.ReaderFrom - http.Flusher - http.CloseNotifier - }{d, readerFromDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[readerFrom+hijacker] = func(d *responseWriterDelegator) delegator { // 12 - return struct { - *responseWriterDelegator - io.ReaderFrom - http.Hijacker - }{d, readerFromDelegator{d}, hijackerDelegator{d}} - } - pickDelegator[readerFrom+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 13 - return struct { - *responseWriterDelegator - io.ReaderFrom - http.Hijacker - http.CloseNotifier - }{d, readerFromDelegator{d}, hijackerDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[readerFrom+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 14 - return struct { - *responseWriterDelegator - io.ReaderFrom - http.Hijacker - http.Flusher - }{d, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}} - } - pickDelegator[readerFrom+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 15 - return struct { - *responseWriterDelegator - io.ReaderFrom - http.Hijacker - http.Flusher - http.CloseNotifier - }{d, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} - } -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_1_8.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_1_8.go deleted file mode 100644 index 31a706956..000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_1_8.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2017 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build go1.8 - -package promhttp - -import ( - "io" - "net/http" -) - -type pusherDelegator struct{ *responseWriterDelegator } - -func (d pusherDelegator) Push(target string, opts *http.PushOptions) error { - return d.ResponseWriter.(http.Pusher).Push(target, opts) -} - -func init() { - pickDelegator[pusher] = func(d *responseWriterDelegator) delegator { // 16 - return pusherDelegator{d} - } - pickDelegator[pusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 17 - return struct { - *responseWriterDelegator - http.Pusher - http.CloseNotifier - }{d, pusherDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[pusher+flusher] = func(d *responseWriterDelegator) delegator { // 18 - return struct { - *responseWriterDelegator - http.Pusher - http.Flusher - }{d, pusherDelegator{d}, flusherDelegator{d}} - } - pickDelegator[pusher+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 19 - return struct { - *responseWriterDelegator - http.Pusher - http.Flusher - http.CloseNotifier - }{d, pusherDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[pusher+hijacker] = func(d *responseWriterDelegator) delegator { // 20 - return struct { - *responseWriterDelegator - http.Pusher - http.Hijacker - }{d, pusherDelegator{d}, hijackerDelegator{d}} - } - pickDelegator[pusher+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 21 - return struct { - *responseWriterDelegator - http.Pusher - http.Hijacker - http.CloseNotifier - }{d, pusherDelegator{d}, hijackerDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[pusher+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 22 - return struct { - *responseWriterDelegator - http.Pusher - http.Hijacker - http.Flusher - }{d, pusherDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}} - } - pickDelegator[pusher+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { //23 - return struct { - *responseWriterDelegator - http.Pusher - http.Hijacker - http.Flusher - http.CloseNotifier - }{d, pusherDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[pusher+readerFrom] = func(d *responseWriterDelegator) delegator { // 24 - return struct { - *responseWriterDelegator - http.Pusher - io.ReaderFrom - }{d, pusherDelegator{d}, readerFromDelegator{d}} - } - pickDelegator[pusher+readerFrom+closeNotifier] = func(d *responseWriterDelegator) delegator { // 25 - return struct { - *responseWriterDelegator - http.Pusher - io.ReaderFrom - http.CloseNotifier - }{d, pusherDelegator{d}, readerFromDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[pusher+readerFrom+flusher] = func(d *responseWriterDelegator) delegator { // 26 - return struct { - *responseWriterDelegator - http.Pusher - io.ReaderFrom - http.Flusher - }{d, pusherDelegator{d}, readerFromDelegator{d}, flusherDelegator{d}} - } - pickDelegator[pusher+readerFrom+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 27 - return struct { - *responseWriterDelegator - http.Pusher - io.ReaderFrom - http.Flusher - http.CloseNotifier - }{d, pusherDelegator{d}, readerFromDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[pusher+readerFrom+hijacker] = func(d *responseWriterDelegator) delegator { // 28 - return struct { - *responseWriterDelegator - http.Pusher - io.ReaderFrom - http.Hijacker - }{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}} - } - pickDelegator[pusher+readerFrom+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 29 - return struct { - *responseWriterDelegator - http.Pusher - io.ReaderFrom - http.Hijacker - http.CloseNotifier - }{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}, closeNotifierDelegator{d}} - } - pickDelegator[pusher+readerFrom+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 30 - return struct { - *responseWriterDelegator - http.Pusher - io.ReaderFrom - http.Hijacker - http.Flusher - }{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}} - } - pickDelegator[pusher+readerFrom+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 31 - return struct { - *responseWriterDelegator - http.Pusher - io.ReaderFrom - http.Hijacker - http.Flusher - http.CloseNotifier - }{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} - } -} - -func newDelegator(w http.ResponseWriter, observeWriteHeaderFunc func(int)) delegator { - d := &responseWriterDelegator{ - ResponseWriter: w, - observeWriteHeader: observeWriteHeaderFunc, - } - - id := 0 - if _, ok := w.(http.CloseNotifier); ok { - id += closeNotifier - } - if _, ok := w.(http.Flusher); ok { - id += flusher - } - if _, ok := w.(http.Hijacker); ok { - id += hijacker - } - if _, ok := w.(io.ReaderFrom); ok { - id += readerFrom - } - if _, ok := w.(http.Pusher); ok { - id += pusher - } - - return pickDelegator[id](d) -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_pre_1_8.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_pre_1_8.go deleted file mode 100644 index 8bb9b8b68..000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_pre_1_8.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2017 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !go1.8 - -package promhttp - -import ( - "io" - "net/http" -) - -func newDelegator(w http.ResponseWriter, observeWriteHeaderFunc func(int)) delegator { - d := &responseWriterDelegator{ - ResponseWriter: w, - observeWriteHeader: observeWriteHeaderFunc, - } - - id := 0 - if _, ok := w.(http.CloseNotifier); ok { - id += closeNotifier - } - if _, ok := w.(http.Flusher); ok { - id += flusher - } - if _, ok := w.(http.Hijacker); ok { - id += hijacker - } - if _, ok := w.(io.ReaderFrom); ok { - id += readerFrom - } - - return pickDelegator[id](d) -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go index 668eb6b3c..b6dd5a266 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go @@ -11,34 +11,31 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package promhttp provides tooling around HTTP servers and clients. +// Copyright (c) 2013, The Prometheus Authors +// All rights reserved. // -// First, the package allows the creation of http.Handler instances to expose -// Prometheus metrics via HTTP. promhttp.Handler acts on the -// prometheus.DefaultGatherer. With HandlerFor, you can create a handler for a -// custom registry or anything that implements the Gatherer interface. It also -// allows the creation of handlers that act differently on errors or allow to -// log errors. -// -// Second, the package provides tooling to instrument instances of http.Handler -// via middleware. Middleware wrappers follow the naming scheme -// InstrumentHandlerX, where X describes the intended use of the middleware. -// See each function's doc comment for specific details. +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file. + +// Package promhttp contains functions to create http.Handler instances to +// expose Prometheus metrics via HTTP. In later versions of this package, it +// will also contain tooling to instrument instances of http.Handler and +// http.RoundTripper. // -// Finally, the package allows for an http.RoundTripper to be instrumented via -// middleware. Middleware wrappers follow the naming scheme -// InstrumentRoundTripperX, where X describes the intended use of the -// middleware. See each function's doc comment for specific details. +// promhttp.Handler acts on the prometheus.DefaultGatherer. With HandlerFor, +// you can create a handler for a custom registry or anything that implements +// the Gatherer interface. It also allows to create handlers that act +// differently on errors or allow to log errors. package promhttp import ( + "bytes" "compress/gzip" "fmt" "io" "net/http" "strings" "sync" - "time" "github.com/prometheus/common/expfmt" @@ -52,56 +49,36 @@ const ( acceptEncodingHeader = "Accept-Encoding" ) -var gzipPool = sync.Pool{ - New: func() interface{} { - return gzip.NewWriter(nil) - }, +var bufPool sync.Pool + +func getBuf() *bytes.Buffer { + buf := bufPool.Get() + if buf == nil { + return &bytes.Buffer{} + } + return buf.(*bytes.Buffer) } -// Handler returns an http.Handler for the prometheus.DefaultGatherer, using -// default HandlerOpts, i.e. it reports the first error as an HTTP error, it has -// no error logging, and it applies compression if requested by the client. -// -// The returned http.Handler is already instrumented using the -// InstrumentMetricHandler function and the prometheus.DefaultRegisterer. If you -// create multiple http.Handlers by separate calls of the Handler function, the -// metrics used for instrumentation will be shared between them, providing -// global scrape counts. +func giveBuf(buf *bytes.Buffer) { + buf.Reset() + bufPool.Put(buf) +} + +// Handler returns an HTTP handler for the prometheus.DefaultGatherer. The +// Handler uses the default HandlerOpts, i.e. report the first error as an HTTP +// error, no error logging, and compression if requested by the client. // -// This function is meant to cover the bulk of basic use cases. If you are doing -// anything that requires more customization (including using a non-default -// Gatherer, different instrumentation, and non-default HandlerOpts), use the -// HandlerFor function. See there for details. +// If you want to create a Handler for the DefaultGatherer with different +// HandlerOpts, create it with HandlerFor with prometheus.DefaultGatherer and +// your desired HandlerOpts. func Handler() http.Handler { - return InstrumentMetricHandler( - prometheus.DefaultRegisterer, HandlerFor(prometheus.DefaultGatherer, HandlerOpts{}), - ) + return HandlerFor(prometheus.DefaultGatherer, HandlerOpts{}) } -// HandlerFor returns an uninstrumented http.Handler for the provided -// Gatherer. The behavior of the Handler is defined by the provided -// HandlerOpts. Thus, HandlerFor is useful to create http.Handlers for custom -// Gatherers, with non-default HandlerOpts, and/or with custom (or no) -// instrumentation. Use the InstrumentMetricHandler function to apply the same -// kind of instrumentation as it is used by the Handler function. +// HandlerFor returns an http.Handler for the provided Gatherer. The behavior +// of the Handler is defined by the provided HandlerOpts. func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler { - var inFlightSem chan struct{} - if opts.MaxRequestsInFlight > 0 { - inFlightSem = make(chan struct{}, opts.MaxRequestsInFlight) - } - - h := http.HandlerFunc(func(rsp http.ResponseWriter, req *http.Request) { - if inFlightSem != nil { - select { - case inFlightSem <- struct{}{}: // All good, carry on. - defer func() { <-inFlightSem }() - default: - http.Error(rsp, fmt.Sprintf( - "Limit of concurrent requests reached (%d), try again later.", opts.MaxRequestsInFlight, - ), http.StatusServiceUnavailable) - return - } - } + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { mfs, err := reg.Gather() if err != nil { if opts.ErrorLog != nil { @@ -112,40 +89,26 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler { panic(err) case ContinueOnError: if len(mfs) == 0 { - // Still report the error if no metrics have been gathered. - httpError(rsp, err) + http.Error(w, "No metrics gathered, last error:\n\n"+err.Error(), http.StatusInternalServerError) return } case HTTPErrorOnError: - httpError(rsp, err) + http.Error(w, "An error has occurred during metrics gathering:\n\n"+err.Error(), http.StatusInternalServerError) return } } contentType := expfmt.Negotiate(req.Header) - header := rsp.Header() - header.Set(contentTypeHeader, string(contentType)) - - w := io.Writer(rsp) - if !opts.DisableCompression && gzipAccepted(req.Header) { - header.Set(contentEncodingHeader, "gzip") - gz := gzipPool.Get().(*gzip.Writer) - defer gzipPool.Put(gz) - - gz.Reset(w) - defer gz.Close() - - w = gz - } - - enc := expfmt.NewEncoder(w, contentType) - + buf := getBuf() + defer giveBuf(buf) + writer, encoding := decorateWriter(req, buf, opts.DisableCompression) + enc := expfmt.NewEncoder(writer, contentType) var lastErr error for _, mf := range mfs { if err := enc.Encode(mf); err != nil { lastErr = err if opts.ErrorLog != nil { - opts.ErrorLog.Println("error encoding and sending metric family:", err) + opts.ErrorLog.Println("error encoding metric family:", err) } switch opts.ErrorHandling { case PanicOnError: @@ -153,75 +116,27 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler { case ContinueOnError: // Handled later. case HTTPErrorOnError: - httpError(rsp, err) + http.Error(w, "An error has occurred during metrics encoding:\n\n"+err.Error(), http.StatusInternalServerError) return } } } - - if lastErr != nil { - httpError(rsp, lastErr) + if closer, ok := writer.(io.Closer); ok { + closer.Close() } - }) - - if opts.Timeout <= 0 { - return h - } - return http.TimeoutHandler(h, opts.Timeout, fmt.Sprintf( - "Exceeded configured timeout of %v.\n", - opts.Timeout, - )) -} - -// InstrumentMetricHandler is usually used with an http.Handler returned by the -// HandlerFor function. It instruments the provided http.Handler with two -// metrics: A counter vector "promhttp_metric_handler_requests_total" to count -// scrapes partitioned by HTTP status code, and a gauge -// "promhttp_metric_handler_requests_in_flight" to track the number of -// simultaneous scrapes. This function idempotently registers collectors for -// both metrics with the provided Registerer. It panics if the registration -// fails. The provided metrics are useful to see how many scrapes hit the -// monitored target (which could be from different Prometheus servers or other -// scrapers), and how often they overlap (which would result in more than one -// scrape in flight at the same time). Note that the scrapes-in-flight gauge -// will contain the scrape by which it is exposed, while the scrape counter will -// only get incremented after the scrape is complete (as only then the status -// code is known). For tracking scrape durations, use the -// "scrape_duration_seconds" gauge created by the Prometheus server upon each -// scrape. -func InstrumentMetricHandler(reg prometheus.Registerer, handler http.Handler) http.Handler { - cnt := prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "promhttp_metric_handler_requests_total", - Help: "Total number of scrapes by HTTP status code.", - }, - []string{"code"}, - ) - // Initialize the most likely HTTP status codes. - cnt.WithLabelValues("200") - cnt.WithLabelValues("500") - cnt.WithLabelValues("503") - if err := reg.Register(cnt); err != nil { - if are, ok := err.(prometheus.AlreadyRegisteredError); ok { - cnt = are.ExistingCollector.(*prometheus.CounterVec) - } else { - panic(err) + if lastErr != nil && buf.Len() == 0 { + http.Error(w, "No metrics encoded, last error:\n\n"+err.Error(), http.StatusInternalServerError) + return } - } - - gge := prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "promhttp_metric_handler_requests_in_flight", - Help: "Current number of scrapes being served.", - }) - if err := reg.Register(gge); err != nil { - if are, ok := err.(prometheus.AlreadyRegisteredError); ok { - gge = are.ExistingCollector.(prometheus.Gauge) - } else { - panic(err) + header := w.Header() + header.Set(contentTypeHeader, string(contentType)) + header.Set(contentLengthHeader, fmt.Sprint(buf.Len())) + if encoding != "" { + header.Set(contentEncodingHeader, encoding) } - } - - return InstrumentHandlerCounter(cnt, InstrumentHandlerInFlight(gge, handler)) + w.Write(buf.Bytes()) + // TODO(beorn7): Consider streaming serving of metrics. + }) } // HandlerErrorHandling defines how a Handler serving metrics will handle @@ -265,47 +180,22 @@ type HandlerOpts struct { // If DisableCompression is true, the handler will never compress the // response, even if requested by the client. DisableCompression bool - // The number of concurrent HTTP requests is limited to - // MaxRequestsInFlight. Additional requests are responded to with 503 - // Service Unavailable and a suitable message in the body. If - // MaxRequestsInFlight is 0 or negative, no limit is applied. - MaxRequestsInFlight int - // If handling a request takes longer than Timeout, it is responded to - // with 503 ServiceUnavailable and a suitable Message. No timeout is - // applied if Timeout is 0 or negative. Note that with the current - // implementation, reaching the timeout simply ends the HTTP requests as - // described above (and even that only if sending of the body hasn't - // started yet), while the bulk work of gathering all the metrics keeps - // running in the background (with the eventual result to be thrown - // away). Until the implementation is improved, it is recommended to - // implement a separate timeout in potentially slow Collectors. - Timeout time.Duration } -// gzipAccepted returns whether the client will accept gzip-encoded content. -func gzipAccepted(header http.Header) bool { - a := header.Get(acceptEncodingHeader) - parts := strings.Split(a, ",") +// decorateWriter wraps a writer to handle gzip compression if requested. It +// returns the decorated writer and the appropriate "Content-Encoding" header +// (which is empty if no compression is enabled). +func decorateWriter(request *http.Request, writer io.Writer, compressionDisabled bool) (io.Writer, string) { + if compressionDisabled { + return writer, "" + } + header := request.Header.Get(acceptEncodingHeader) + parts := strings.Split(header, ",") for _, part := range parts { - part = strings.TrimSpace(part) + part := strings.TrimSpace(part) if part == "gzip" || strings.HasPrefix(part, "gzip;") { - return true + return gzip.NewWriter(writer), "gzip" } } - return false -} - -// httpError removes any content-encoding header and then calls http.Error with -// the provided error and http.StatusInternalServerErrer. Error contents is -// supposed to be uncompressed plain text. However, same as with a plain -// http.Error, any header settings will be void if the header has already been -// sent. The error message will still be written to the writer, but it will -// probably be of limited use. -func httpError(rsp http.ResponseWriter, err error) { - rsp.Header().Del(contentEncodingHeader) - http.Error( - rsp, - "An error has occurred while serving metrics:\n\n"+err.Error(), - http.StatusInternalServerError, - ) + return writer, "" } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go deleted file mode 100644 index 86fd56447..000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2017 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package promhttp - -import ( - "net/http" - "time" - - "github.com/prometheus/client_golang/prometheus" -) - -// The RoundTripperFunc type is an adapter to allow the use of ordinary -// functions as RoundTrippers. If f is a function with the appropriate -// signature, RountTripperFunc(f) is a RoundTripper that calls f. -type RoundTripperFunc func(req *http.Request) (*http.Response, error) - -// RoundTrip implements the RoundTripper interface. -func (rt RoundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) { - return rt(r) -} - -// InstrumentRoundTripperInFlight is a middleware that wraps the provided -// http.RoundTripper. It sets the provided prometheus.Gauge to the number of -// requests currently handled by the wrapped http.RoundTripper. -// -// See the example for ExampleInstrumentRoundTripperDuration for example usage. -func InstrumentRoundTripperInFlight(gauge prometheus.Gauge, next http.RoundTripper) RoundTripperFunc { - return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { - gauge.Inc() - defer gauge.Dec() - return next.RoundTrip(r) - }) -} - -// InstrumentRoundTripperCounter is a middleware that wraps the provided -// http.RoundTripper to observe the request result with the provided CounterVec. -// The CounterVec must have zero, one, or two non-const non-curried labels. For -// those, the only allowed label names are "code" and "method". The function -// panics otherwise. Partitioning of the CounterVec happens by HTTP status code -// and/or HTTP method if the respective instance label names are present in the -// CounterVec. For unpartitioned counting, use a CounterVec with zero labels. -// -// If the wrapped RoundTripper panics or returns a non-nil error, the Counter -// is not incremented. -// -// See the example for ExampleInstrumentRoundTripperDuration for example usage. -func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.RoundTripper) RoundTripperFunc { - code, method := checkLabels(counter) - - return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { - resp, err := next.RoundTrip(r) - if err == nil { - counter.With(labels(code, method, r.Method, resp.StatusCode)).Inc() - } - return resp, err - }) -} - -// InstrumentRoundTripperDuration is a middleware that wraps the provided -// http.RoundTripper to observe the request duration with the provided -// ObserverVec. The ObserverVec must have zero, one, or two non-const -// non-curried labels. For those, the only allowed label names are "code" and -// "method". The function panics otherwise. The Observe method of the Observer -// in the ObserverVec is called with the request duration in -// seconds. Partitioning happens by HTTP status code and/or HTTP method if the -// respective instance label names are present in the ObserverVec. For -// unpartitioned observations, use an ObserverVec with zero labels. Note that -// partitioning of Histograms is expensive and should be used judiciously. -// -// If the wrapped RoundTripper panics or returns a non-nil error, no values are -// reported. -// -// Note that this method is only guaranteed to never observe negative durations -// if used with Go1.9+. -func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundTripper) RoundTripperFunc { - code, method := checkLabels(obs) - - return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { - start := time.Now() - resp, err := next.RoundTrip(r) - if err == nil { - obs.With(labels(code, method, r.Method, resp.StatusCode)).Observe(time.Since(start).Seconds()) - } - return resp, err - }) -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client_1_8.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client_1_8.go deleted file mode 100644 index a034d1ec0..000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client_1_8.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2017 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build go1.8 - -package promhttp - -import ( - "context" - "crypto/tls" - "net/http" - "net/http/httptrace" - "time" -) - -// InstrumentTrace is used to offer flexibility in instrumenting the available -// httptrace.ClientTrace hook functions. Each function is passed a float64 -// representing the time in seconds since the start of the http request. A user -// may choose to use separately buckets Histograms, or implement custom -// instance labels on a per function basis. -type InstrumentTrace struct { - GotConn func(float64) - PutIdleConn func(float64) - GotFirstResponseByte func(float64) - Got100Continue func(float64) - DNSStart func(float64) - DNSDone func(float64) - ConnectStart func(float64) - ConnectDone func(float64) - TLSHandshakeStart func(float64) - TLSHandshakeDone func(float64) - WroteHeaders func(float64) - Wait100Continue func(float64) - WroteRequest func(float64) -} - -// InstrumentRoundTripperTrace is a middleware that wraps the provided -// RoundTripper and reports times to hook functions provided in the -// InstrumentTrace struct. Hook functions that are not present in the provided -// InstrumentTrace struct are ignored. Times reported to the hook functions are -// time since the start of the request. Only with Go1.9+, those times are -// guaranteed to never be negative. (Earlier Go versions are not using a -// monotonic clock.) Note that partitioning of Histograms is expensive and -// should be used judiciously. -// -// For hook functions that receive an error as an argument, no observations are -// made in the event of a non-nil error value. -// -// See the example for ExampleInstrumentRoundTripperDuration for example usage. -func InstrumentRoundTripperTrace(it *InstrumentTrace, next http.RoundTripper) RoundTripperFunc { - return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { - start := time.Now() - - trace := &httptrace.ClientTrace{ - GotConn: func(_ httptrace.GotConnInfo) { - if it.GotConn != nil { - it.GotConn(time.Since(start).Seconds()) - } - }, - PutIdleConn: func(err error) { - if err != nil { - return - } - if it.PutIdleConn != nil { - it.PutIdleConn(time.Since(start).Seconds()) - } - }, - DNSStart: func(_ httptrace.DNSStartInfo) { - if it.DNSStart != nil { - it.DNSStart(time.Since(start).Seconds()) - } - }, - DNSDone: func(_ httptrace.DNSDoneInfo) { - if it.DNSDone != nil { - it.DNSDone(time.Since(start).Seconds()) - } - }, - ConnectStart: func(_, _ string) { - if it.ConnectStart != nil { - it.ConnectStart(time.Since(start).Seconds()) - } - }, - ConnectDone: func(_, _ string, err error) { - if err != nil { - return - } - if it.ConnectDone != nil { - it.ConnectDone(time.Since(start).Seconds()) - } - }, - GotFirstResponseByte: func() { - if it.GotFirstResponseByte != nil { - it.GotFirstResponseByte(time.Since(start).Seconds()) - } - }, - Got100Continue: func() { - if it.Got100Continue != nil { - it.Got100Continue(time.Since(start).Seconds()) - } - }, - TLSHandshakeStart: func() { - if it.TLSHandshakeStart != nil { - it.TLSHandshakeStart(time.Since(start).Seconds()) - } - }, - TLSHandshakeDone: func(_ tls.ConnectionState, err error) { - if err != nil { - return - } - if it.TLSHandshakeDone != nil { - it.TLSHandshakeDone(time.Since(start).Seconds()) - } - }, - WroteHeaders: func() { - if it.WroteHeaders != nil { - it.WroteHeaders(time.Since(start).Seconds()) - } - }, - Wait100Continue: func() { - if it.Wait100Continue != nil { - it.Wait100Continue(time.Since(start).Seconds()) - } - }, - WroteRequest: func(_ httptrace.WroteRequestInfo) { - if it.WroteRequest != nil { - it.WroteRequest(time.Since(start).Seconds()) - } - }, - } - r = r.WithContext(httptrace.WithClientTrace(context.Background(), trace)) - - return next.RoundTrip(r) - }) -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go deleted file mode 100644 index 9db243805..000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go +++ /dev/null @@ -1,447 +0,0 @@ -// Copyright 2017 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package promhttp - -import ( - "errors" - "net/http" - "strconv" - "strings" - "time" - - dto "github.com/prometheus/client_model/go" - - "github.com/prometheus/client_golang/prometheus" -) - -// magicString is used for the hacky label test in checkLabels. Remove once fixed. -const magicString = "zZgWfBxLqvG8kc8IMv3POi2Bb0tZI3vAnBx+gBaFi9FyPzB/CzKUer1yufDa" - -// InstrumentHandlerInFlight is a middleware that wraps the provided -// http.Handler. It sets the provided prometheus.Gauge to the number of -// requests currently handled by the wrapped http.Handler. -// -// See the example for InstrumentHandlerDuration for example usage. -func InstrumentHandlerInFlight(g prometheus.Gauge, next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - g.Inc() - defer g.Dec() - next.ServeHTTP(w, r) - }) -} - -// InstrumentHandlerDuration is a middleware that wraps the provided -// http.Handler to observe the request duration with the provided ObserverVec. -// The ObserverVec must have zero, one, or two non-const non-curried labels. For -// those, the only allowed label names are "code" and "method". The function -// panics otherwise. The Observe method of the Observer in the ObserverVec is -// called with the request duration in seconds. Partitioning happens by HTTP -// status code and/or HTTP method if the respective instance label names are -// present in the ObserverVec. For unpartitioned observations, use an -// ObserverVec with zero labels. Note that partitioning of Histograms is -// expensive and should be used judiciously. -// -// If the wrapped Handler does not set a status code, a status code of 200 is assumed. -// -// If the wrapped Handler panics, no values are reported. -// -// Note that this method is only guaranteed to never observe negative durations -// if used with Go1.9+. -func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc { - code, method := checkLabels(obs) - - if code { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - now := time.Now() - d := newDelegator(w, nil) - next.ServeHTTP(d, r) - - obs.With(labels(code, method, r.Method, d.Status())).Observe(time.Since(now).Seconds()) - }) - } - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - now := time.Now() - next.ServeHTTP(w, r) - obs.With(labels(code, method, r.Method, 0)).Observe(time.Since(now).Seconds()) - }) -} - -// InstrumentHandlerCounter is a middleware that wraps the provided http.Handler -// to observe the request result with the provided CounterVec. The CounterVec -// must have zero, one, or two non-const non-curried labels. For those, the only -// allowed label names are "code" and "method". The function panics -// otherwise. Partitioning of the CounterVec happens by HTTP status code and/or -// HTTP method if the respective instance label names are present in the -// CounterVec. For unpartitioned counting, use a CounterVec with zero labels. -// -// If the wrapped Handler does not set a status code, a status code of 200 is assumed. -// -// If the wrapped Handler panics, the Counter is not incremented. -// -// See the example for InstrumentHandlerDuration for example usage. -func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler) http.HandlerFunc { - code, method := checkLabels(counter) - - if code { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - d := newDelegator(w, nil) - next.ServeHTTP(d, r) - counter.With(labels(code, method, r.Method, d.Status())).Inc() - }) - } - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - next.ServeHTTP(w, r) - counter.With(labels(code, method, r.Method, 0)).Inc() - }) -} - -// InstrumentHandlerTimeToWriteHeader is a middleware that wraps the provided -// http.Handler to observe with the provided ObserverVec the request duration -// until the response headers are written. The ObserverVec must have zero, one, -// or two non-const non-curried labels. For those, the only allowed label names -// are "code" and "method". The function panics otherwise. The Observe method of -// the Observer in the ObserverVec is called with the request duration in -// seconds. Partitioning happens by HTTP status code and/or HTTP method if the -// respective instance label names are present in the ObserverVec. For -// unpartitioned observations, use an ObserverVec with zero labels. Note that -// partitioning of Histograms is expensive and should be used judiciously. -// -// If the wrapped Handler panics before calling WriteHeader, no value is -// reported. -// -// Note that this method is only guaranteed to never observe negative durations -// if used with Go1.9+. -// -// See the example for InstrumentHandlerDuration for example usage. -func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc { - code, method := checkLabels(obs) - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - now := time.Now() - d := newDelegator(w, func(status int) { - obs.With(labels(code, method, r.Method, status)).Observe(time.Since(now).Seconds()) - }) - next.ServeHTTP(d, r) - }) -} - -// InstrumentHandlerRequestSize is a middleware that wraps the provided -// http.Handler to observe the request size with the provided ObserverVec. The -// ObserverVec must have zero, one, or two non-const non-curried labels. For -// those, the only allowed label names are "code" and "method". The function -// panics otherwise. The Observe method of the Observer in the ObserverVec is -// called with the request size in bytes. Partitioning happens by HTTP status -// code and/or HTTP method if the respective instance label names are present in -// the ObserverVec. For unpartitioned observations, use an ObserverVec with zero -// labels. Note that partitioning of Histograms is expensive and should be used -// judiciously. -// -// If the wrapped Handler does not set a status code, a status code of 200 is assumed. -// -// If the wrapped Handler panics, no values are reported. -// -// See the example for InstrumentHandlerDuration for example usage. -func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc { - code, method := checkLabels(obs) - - if code { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - d := newDelegator(w, nil) - next.ServeHTTP(d, r) - size := computeApproximateRequestSize(r) - obs.With(labels(code, method, r.Method, d.Status())).Observe(float64(size)) - }) - } - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - next.ServeHTTP(w, r) - size := computeApproximateRequestSize(r) - obs.With(labels(code, method, r.Method, 0)).Observe(float64(size)) - }) -} - -// InstrumentHandlerResponseSize is a middleware that wraps the provided -// http.Handler to observe the response size with the provided ObserverVec. The -// ObserverVec must have zero, one, or two non-const non-curried labels. For -// those, the only allowed label names are "code" and "method". The function -// panics otherwise. The Observe method of the Observer in the ObserverVec is -// called with the response size in bytes. Partitioning happens by HTTP status -// code and/or HTTP method if the respective instance label names are present in -// the ObserverVec. For unpartitioned observations, use an ObserverVec with zero -// labels. Note that partitioning of Histograms is expensive and should be used -// judiciously. -// -// If the wrapped Handler does not set a status code, a status code of 200 is assumed. -// -// If the wrapped Handler panics, no values are reported. -// -// See the example for InstrumentHandlerDuration for example usage. -func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler) http.Handler { - code, method := checkLabels(obs) - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - d := newDelegator(w, nil) - next.ServeHTTP(d, r) - obs.With(labels(code, method, r.Method, d.Status())).Observe(float64(d.Written())) - }) -} - -func checkLabels(c prometheus.Collector) (code bool, method bool) { - // TODO(beorn7): Remove this hacky way to check for instance labels - // once Descriptors can have their dimensionality queried. - var ( - desc *prometheus.Desc - m prometheus.Metric - pm dto.Metric - lvs []string - ) - - // Get the Desc from the Collector. - descc := make(chan *prometheus.Desc, 1) - c.Describe(descc) - - select { - case desc = <-descc: - default: - panic("no description provided by collector") - } - select { - case <-descc: - panic("more than one description provided by collector") - default: - } - - close(descc) - - // Create a ConstMetric with the Desc. Since we don't know how many - // variable labels there are, try for as long as it needs. - for err := errors.New("dummy"); err != nil; lvs = append(lvs, magicString) { - m, err = prometheus.NewConstMetric(desc, prometheus.UntypedValue, 0, lvs...) - } - - // Write out the metric into a proto message and look at the labels. - // If the value is not the magicString, it is a constLabel, which doesn't interest us. - // If the label is curried, it doesn't interest us. - // In all other cases, only "code" or "method" is allowed. - if err := m.Write(&pm); err != nil { - panic("error checking metric for labels") - } - for _, label := range pm.Label { - name, value := label.GetName(), label.GetValue() - if value != magicString || isLabelCurried(c, name) { - continue - } - switch name { - case "code": - code = true - case "method": - method = true - default: - panic("metric partitioned with non-supported labels") - } - } - return -} - -func isLabelCurried(c prometheus.Collector, label string) bool { - // This is even hackier than the label test above. - // We essentially try to curry again and see if it works. - // But for that, we need to type-convert to the two - // types we use here, ObserverVec or *CounterVec. - switch v := c.(type) { - case *prometheus.CounterVec: - if _, err := v.CurryWith(prometheus.Labels{label: "dummy"}); err == nil { - return false - } - case prometheus.ObserverVec: - if _, err := v.CurryWith(prometheus.Labels{label: "dummy"}); err == nil { - return false - } - default: - panic("unsupported metric vec type") - } - return true -} - -// emptyLabels is a one-time allocation for non-partitioned metrics to avoid -// unnecessary allocations on each request. -var emptyLabels = prometheus.Labels{} - -func labels(code, method bool, reqMethod string, status int) prometheus.Labels { - if !(code || method) { - return emptyLabels - } - labels := prometheus.Labels{} - - if code { - labels["code"] = sanitizeCode(status) - } - if method { - labels["method"] = sanitizeMethod(reqMethod) - } - - return labels -} - -func computeApproximateRequestSize(r *http.Request) int { - s := 0 - if r.URL != nil { - s += len(r.URL.String()) - } - - s += len(r.Method) - s += len(r.Proto) - for name, values := range r.Header { - s += len(name) - for _, value := range values { - s += len(value) - } - } - s += len(r.Host) - - // N.B. r.Form and r.MultipartForm are assumed to be included in r.URL. - - if r.ContentLength != -1 { - s += int(r.ContentLength) - } - return s -} - -func sanitizeMethod(m string) string { - switch m { - case "GET", "get": - return "get" - case "PUT", "put": - return "put" - case "HEAD", "head": - return "head" - case "POST", "post": - return "post" - case "DELETE", "delete": - return "delete" - case "CONNECT", "connect": - return "connect" - case "OPTIONS", "options": - return "options" - case "NOTIFY", "notify": - return "notify" - default: - return strings.ToLower(m) - } -} - -// If the wrapped http.Handler has not set a status code, i.e. the value is -// currently 0, santizeCode will return 200, for consistency with behavior in -// the stdlib. -func sanitizeCode(s int) string { - switch s { - case 100: - return "100" - case 101: - return "101" - - case 200, 0: - return "200" - case 201: - return "201" - case 202: - return "202" - case 203: - return "203" - case 204: - return "204" - case 205: - return "205" - case 206: - return "206" - - case 300: - return "300" - case 301: - return "301" - case 302: - return "302" - case 304: - return "304" - case 305: - return "305" - case 307: - return "307" - - case 400: - return "400" - case 401: - return "401" - case 402: - return "402" - case 403: - return "403" - case 404: - return "404" - case 405: - return "405" - case 406: - return "406" - case 407: - return "407" - case 408: - return "408" - case 409: - return "409" - case 410: - return "410" - case 411: - return "411" - case 412: - return "412" - case 413: - return "413" - case 414: - return "414" - case 415: - return "415" - case 416: - return "416" - case 417: - return "417" - case 418: - return "418" - - case 500: - return "500" - case 501: - return "501" - case 502: - return "502" - case 503: - return "503" - case 504: - return "504" - case 505: - return "505" - - case 428: - return "428" - case 429: - return "429" - case 431: - return "431" - case 511: - return "511" - - default: - return strconv.Itoa(s) - } -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/registry.go b/vendor/github.com/prometheus/client_golang/prometheus/registry.go index b5e70b93f..32a3986b0 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/registry.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/registry.go @@ -15,22 +15,15 @@ package prometheus import ( "bytes" + "errors" "fmt" - "io/ioutil" "os" - "path/filepath" - "runtime" "sort" - "strings" "sync" - "unicode/utf8" "github.com/golang/protobuf/proto" - "github.com/prometheus/common/expfmt" dto "github.com/prometheus/client_model/go" - - "github.com/prometheus/client_golang/prometheus/internal" ) const ( @@ -42,14 +35,13 @@ const ( // DefaultRegisterer and DefaultGatherer are the implementations of the // Registerer and Gatherer interface a number of convenience functions in this // package act on. Initially, both variables point to the same Registry, which -// has a process collector (currently on Linux only, see NewProcessCollector) -// and a Go collector (see NewGoCollector, in particular the note about -// stop-the-world implication with Go versions older than 1.9) already -// registered. This approach to keep default instances as global state mirrors -// the approach of other packages in the Go standard library. Note that there -// are caveats. Change the variables with caution and only if you understand the -// consequences. Users who want to avoid global state altogether should not use -// the convenience functions and act on custom instances instead. +// has a process collector (see NewProcessCollector) and a Go collector (see +// NewGoCollector) already registered. This approach to keep default instances +// as global state mirrors the approach of other packages in the Go standard +// library. Note that there are caveats. Change the variables with caution and +// only if you understand the consequences. Users who want to avoid global state +// altogether should not use the convenience function and act on custom +// instances instead. var ( defaultRegistry = NewRegistry() DefaultRegisterer Registerer = defaultRegistry @@ -57,7 +49,7 @@ var ( ) func init() { - MustRegister(NewProcessCollector(ProcessCollectorOpts{})) + MustRegister(NewProcessCollector(os.Getpid(), "")) MustRegister(NewGoCollector()) } @@ -73,8 +65,7 @@ func NewRegistry() *Registry { // NewPedanticRegistry returns a registry that checks during collection if each // collected Metric is consistent with its reported Desc, and if the Desc has -// actually been registered with the registry. Unchecked Collectors (those whose -// Describe methed does not yield any descriptors) are excluded from the check. +// actually been registered with the registry. // // Usually, a Registry will be happy as long as the union of all collected // Metrics is consistent and valid even if some metrics are not consistent with @@ -89,7 +80,7 @@ func NewPedanticRegistry() *Registry { // Registerer is the interface for the part of a registry in charge of // registering and unregistering. Users of custom registries should use -// Registerer as type for registration purposes (rather than the Registry type +// Registerer as type for registration purposes (rather then the Registry type // directly). In that way, they are free to use custom Registerer implementation // (e.g. for testing purposes). type Registerer interface { @@ -104,13 +95,8 @@ type Registerer interface { // returned error is an instance of AlreadyRegisteredError, which // contains the previously registered Collector. // - // A Collector whose Describe method does not yield any Desc is treated - // as unchecked. Registration will always succeed. No check for - // re-registering (see previous paragraph) is performed. Thus, the - // caller is responsible for not double-registering the same unchecked - // Collector, and for providing a Collector that will not cause - // inconsistent metrics on collection. (This would lead to scrape - // errors.) + // It is in general not safe to register the same Collector multiple + // times concurrently. Register(Collector) error // MustRegister works like Register but registers any number of // Collectors and panics upon the first registration that causes an @@ -119,9 +105,7 @@ type Registerer interface { // Unregister unregisters the Collector that equals the Collector passed // in as an argument. (Two Collectors are considered equal if their // Describe method yields the same set of descriptors.) The function - // returns whether a Collector was unregistered. Note that an unchecked - // Collector cannot be unregistered (as its Describe method does not - // yield any descriptor). + // returns whether a Collector was unregistered. // // Note that even after unregistering, it will not be possible to // register a new Collector that is inconsistent with the unregistered @@ -139,23 +123,15 @@ type Registerer interface { type Gatherer interface { // Gather calls the Collect method of the registered Collectors and then // gathers the collected metrics into a lexicographically sorted slice - // of uniquely named MetricFamily protobufs. Gather ensures that the - // returned slice is valid and self-consistent so that it can be used - // for valid exposition. As an exception to the strict consistency - // requirements described for metric.Desc, Gather will tolerate - // different sets of label names for metrics of the same metric family. - // - // Even if an error occurs, Gather attempts to gather as many metrics as - // possible. Hence, if a non-nil error is returned, the returned - // MetricFamily slice could be nil (in case of a fatal error that - // prevented any meaningful metric collection) or contain a number of - // MetricFamily protobufs, some of which might be incomplete, and some - // might be missing altogether. The returned error (which might be a - // MultiError) explains the details. Note that this is mostly useful for - // debugging purposes. If the gathered protobufs are to be used for - // exposition in actual monitoring, it is almost always better to not - // expose an incomplete result and instead disregard the returned - // MetricFamily protobufs in case the returned error is non-nil. + // of MetricFamily protobufs. Even if an error occurs, Gather attempts + // to gather as many metrics as possible. Hence, if a non-nil error is + // returned, the returned MetricFamily slice could be nil (in case of a + // fatal error that prevented any meaningful metric collection) or + // contain a number of MetricFamily protobufs, some of which might be + // incomplete, and some might be missing altogether. The returned error + // (which might be a MultiError) explains the details. In scenarios + // where complete collection is critical, the returned MetricFamily + // protobufs should be disregarded if the returned error is non-nil. Gather() ([]*dto.MetricFamily, error) } @@ -176,6 +152,38 @@ func MustRegister(cs ...Collector) { DefaultRegisterer.MustRegister(cs...) } +// RegisterOrGet registers the provided Collector with the DefaultRegisterer and +// returns the Collector, unless an equal Collector was registered before, in +// which case that Collector is returned. +// +// Deprecated: RegisterOrGet is merely a convenience function for the +// implementation as described in the documentation for +// AlreadyRegisteredError. As the use case is relatively rare, this function +// will be removed in a future version of this package to clean up the +// namespace. +func RegisterOrGet(c Collector) (Collector, error) { + if err := Register(c); err != nil { + if are, ok := err.(AlreadyRegisteredError); ok { + return are.ExistingCollector, nil + } + return nil, err + } + return c, nil +} + +// MustRegisterOrGet behaves like RegisterOrGet but panics instead of returning +// an error. +// +// Deprecated: This is deprecated for the same reason RegisterOrGet is. See +// there for details. +func MustRegisterOrGet(c Collector) Collector { + c, err := RegisterOrGet(c) + if err != nil { + panic(err) + } + return c +} + // Unregister removes the registration of the provided Collector from the // DefaultRegisterer. // @@ -193,6 +201,25 @@ func (gf GathererFunc) Gather() ([]*dto.MetricFamily, error) { return gf() } +// SetMetricFamilyInjectionHook replaces the DefaultGatherer with one that +// gathers from the previous DefaultGatherers but then merges the MetricFamily +// protobufs returned from the provided hook function with the MetricFamily +// protobufs returned from the original DefaultGatherer. +// +// Deprecated: This function manipulates the DefaultGatherer variable. Consider +// the implications, i.e. don't do this concurrently with any uses of the +// DefaultGatherer. In the rare cases where you need to inject MetricFamily +// protobufs directly, it is recommended to use a custom Registry and combine it +// with a custom Gatherer using the Gatherers type (see +// there). SetMetricFamilyInjectionHook only exists for compatibility reasons +// with previous versions of this package. +func SetMetricFamilyInjectionHook(hook func() []*dto.MetricFamily) { + DefaultGatherer = Gatherers{ + DefaultGatherer, + GathererFunc(func() ([]*dto.MetricFamily, error) { return hook(), nil }), + } +} + // AlreadyRegisteredError is returned by the Register method if the Collector to // be registered has already been registered before, or a different Collector // that collects the same metrics has been registered before. Registration fails @@ -225,13 +252,6 @@ func (errs MultiError) Error() string { return buf.String() } -// Append appends the provided error if it is not nil. -func (errs *MultiError) Append(err error) { - if err != nil { - *errs = append(*errs, err) - } -} - // MaybeUnwrap returns nil if len(errs) is 0. It returns the first and only // contained error as error if len(errs is 1). In all other cases, it returns // the MultiError directly. This is helpful for returning a MultiError in a way @@ -256,7 +276,6 @@ type Registry struct { collectorsByID map[uint64]Collector // ID is a hash of the descIDs. descIDs map[uint64]struct{} dimHashesByName map[string]uint64 - uncheckedCollectors []Collector pedanticChecksEnabled bool } @@ -274,13 +293,8 @@ func (r *Registry) Register(c Collector) error { close(descChan) }() r.mtx.Lock() - defer func() { - // Drain channel in case of premature return to not leak a goroutine. - for range descChan { - } - r.mtx.Unlock() - }() - // Conduct various tests... + defer r.mtx.Unlock() + // Coduct various tests... for desc := range descChan { // Is the descriptor valid at all? @@ -319,10 +333,9 @@ func (r *Registry) Register(c Collector) error { } } } - // A Collector yielding no Desc at all is considered unchecked. + // Did anything happen at all? if len(newDescIDs) == 0 { - r.uncheckedCollectors = append(r.uncheckedCollectors, c) - return nil + return errors.New("collector has no descriptors") } if existing, exists := r.collectorsByID[collectorID]; exists { return AlreadyRegisteredError{ @@ -396,25 +409,31 @@ func (r *Registry) MustRegister(cs ...Collector) { // Gather implements Gatherer. func (r *Registry) Gather() ([]*dto.MetricFamily, error) { var ( - checkedMetricChan = make(chan Metric, capMetricChan) - uncheckedMetricChan = make(chan Metric, capMetricChan) - metricHashes = map[uint64]struct{}{} - wg sync.WaitGroup - errs MultiError // The collected errors to return in the end. - registeredDescIDs map[uint64]struct{} // Only used for pedantic checks + metricChan = make(chan Metric, capMetricChan) + metricHashes = map[uint64]struct{}{} + dimHashes = map[string]uint64{} + wg sync.WaitGroup + errs MultiError // The collected errors to return in the end. + registeredDescIDs map[uint64]struct{} // Only used for pedantic checks ) r.mtx.RLock() - goroutineBudget := len(r.collectorsByID) + len(r.uncheckedCollectors) metricFamiliesByName := make(map[string]*dto.MetricFamily, len(r.dimHashesByName)) - checkedCollectors := make(chan Collector, len(r.collectorsByID)) - uncheckedCollectors := make(chan Collector, len(r.uncheckedCollectors)) + + // Scatter. + // (Collectors could be complex and slow, so we call them all at once.) + wg.Add(len(r.collectorsByID)) + go func() { + wg.Wait() + close(metricChan) + }() for _, collector := range r.collectorsByID { - checkedCollectors <- collector - } - for _, collector := range r.uncheckedCollectors { - uncheckedCollectors <- collector + go func(collector Collector) { + defer wg.Done() + collector.Collect(metricChan) + }(collector) } + // In case pedantic checks are enabled, we have to copy the map before // giving up the RLock. if r.pedanticChecksEnabled { @@ -423,258 +442,127 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { registeredDescIDs[id] = struct{}{} } } - r.mtx.RUnlock() - - wg.Add(goroutineBudget) - collectWorker := func() { - for { - select { - case collector := <-checkedCollectors: - collector.Collect(checkedMetricChan) - case collector := <-uncheckedCollectors: - collector.Collect(uncheckedMetricChan) - default: - return - } - wg.Done() - } - } - - // Start the first worker now to make sure at least one is running. - go collectWorker() - goroutineBudget-- - - // Close checkedMetricChan and uncheckedMetricChan once all collectors - // are collected. - go func() { - wg.Wait() - close(checkedMetricChan) - close(uncheckedMetricChan) - }() + r.mtx.RUnlock() - // Drain checkedMetricChan and uncheckedMetricChan in case of premature return. + // Drain metricChan in case of premature return. defer func() { - if checkedMetricChan != nil { - for range checkedMetricChan { - } - } - if uncheckedMetricChan != nil { - for range uncheckedMetricChan { - } + for _ = range metricChan { } }() - // Copy the channel references so we can nil them out later to remove - // them from the select statements below. - cmc := checkedMetricChan - umc := uncheckedMetricChan - - for { - select { - case metric, ok := <-cmc: - if !ok { - cmc = nil - break - } - errs.Append(processMetric( - metric, metricFamiliesByName, - metricHashes, - registeredDescIDs, + // Gather. + for metric := range metricChan { + // This could be done concurrently, too, but it required locking + // of metricFamiliesByName (and of metricHashes if checks are + // enabled). Most likely not worth it. + desc := metric.Desc() + dtoMetric := &dto.Metric{} + if err := metric.Write(dtoMetric); err != nil { + errs = append(errs, fmt.Errorf( + "error collecting metric %v: %s", desc, err, )) - case metric, ok := <-umc: - if !ok { - umc = nil - break + continue + } + metricFamily, ok := metricFamiliesByName[desc.fqName] + if ok { + if metricFamily.GetHelp() != desc.help { + errs = append(errs, fmt.Errorf( + "collected metric %s %s has help %q but should have %q", + desc.fqName, dtoMetric, desc.help, metricFamily.GetHelp(), + )) + continue } - errs.Append(processMetric( - metric, metricFamiliesByName, - metricHashes, - nil, - )) - default: - if goroutineBudget <= 0 || len(checkedCollectors)+len(uncheckedCollectors) == 0 { - // All collectors are already being worked on or - // we have already as many goroutines started as - // there are collectors. Do the same as above, - // just without the default. - select { - case metric, ok := <-cmc: - if !ok { - cmc = nil - break - } - errs.Append(processMetric( - metric, metricFamiliesByName, - metricHashes, - registeredDescIDs, + // TODO(beorn7): Simplify switch once Desc has type. + switch metricFamily.GetType() { + case dto.MetricType_COUNTER: + if dtoMetric.Counter == nil { + errs = append(errs, fmt.Errorf( + "collected metric %s %s should be a Counter", + desc.fqName, dtoMetric, )) - case metric, ok := <-umc: - if !ok { - umc = nil - break - } - errs.Append(processMetric( - metric, metricFamiliesByName, - metricHashes, - nil, + continue + } + case dto.MetricType_GAUGE: + if dtoMetric.Gauge == nil { + errs = append(errs, fmt.Errorf( + "collected metric %s %s should be a Gauge", + desc.fqName, dtoMetric, )) + continue } - break - } - // Start more workers. - go collectWorker() - goroutineBudget-- - runtime.Gosched() - } - // Once both checkedMetricChan and uncheckdMetricChan are closed - // and drained, the contraption above will nil out cmc and umc, - // and then we can leave the collect loop here. - if cmc == nil && umc == nil { - break - } - } - return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap() -} - -// WriteToTextfile calls Gather on the provided Gatherer, encodes the result in the -// Prometheus text format, and writes it to a temporary file. Upon success, the -// temporary file is renamed to the provided filename. -// -// This is intended for use with the textfile collector of the node exporter. -// Note that the node exporter expects the filename to be suffixed with ".prom". -func WriteToTextfile(filename string, g Gatherer) error { - tmp, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename)) - if err != nil { - return err - } - defer os.Remove(tmp.Name()) - - mfs, err := g.Gather() - if err != nil { - return err - } - for _, mf := range mfs { - if _, err := expfmt.MetricFamilyToText(tmp, mf); err != nil { - return err - } - } - if err := tmp.Close(); err != nil { - return err - } - - if err := os.Chmod(tmp.Name(), 0644); err != nil { - return err - } - return os.Rename(tmp.Name(), filename) -} - -// processMetric is an internal helper method only used by the Gather method. -func processMetric( - metric Metric, - metricFamiliesByName map[string]*dto.MetricFamily, - metricHashes map[uint64]struct{}, - registeredDescIDs map[uint64]struct{}, -) error { - desc := metric.Desc() - // Wrapped metrics collected by an unchecked Collector can have an - // invalid Desc. - if desc.err != nil { - return desc.err - } - dtoMetric := &dto.Metric{} - if err := metric.Write(dtoMetric); err != nil { - return fmt.Errorf("error collecting metric %v: %s", desc, err) - } - metricFamily, ok := metricFamiliesByName[desc.fqName] - if ok { // Existing name. - if metricFamily.GetHelp() != desc.help { - return fmt.Errorf( - "collected metric %s %s has help %q but should have %q", - desc.fqName, dtoMetric, desc.help, metricFamily.GetHelp(), - ) - } - // TODO(beorn7): Simplify switch once Desc has type. - switch metricFamily.GetType() { - case dto.MetricType_COUNTER: - if dtoMetric.Counter == nil { - return fmt.Errorf( - "collected metric %s %s should be a Counter", - desc.fqName, dtoMetric, - ) - } - case dto.MetricType_GAUGE: - if dtoMetric.Gauge == nil { - return fmt.Errorf( - "collected metric %s %s should be a Gauge", - desc.fqName, dtoMetric, - ) + case dto.MetricType_SUMMARY: + if dtoMetric.Summary == nil { + errs = append(errs, fmt.Errorf( + "collected metric %s %s should be a Summary", + desc.fqName, dtoMetric, + )) + continue + } + case dto.MetricType_UNTYPED: + if dtoMetric.Untyped == nil { + errs = append(errs, fmt.Errorf( + "collected metric %s %s should be Untyped", + desc.fqName, dtoMetric, + )) + continue + } + case dto.MetricType_HISTOGRAM: + if dtoMetric.Histogram == nil { + errs = append(errs, fmt.Errorf( + "collected metric %s %s should be a Histogram", + desc.fqName, dtoMetric, + )) + continue + } + default: + panic("encountered MetricFamily with invalid type") } - case dto.MetricType_SUMMARY: - if dtoMetric.Summary == nil { - return fmt.Errorf( - "collected metric %s %s should be a Summary", - desc.fqName, dtoMetric, - ) + } else { + metricFamily = &dto.MetricFamily{} + metricFamily.Name = proto.String(desc.fqName) + metricFamily.Help = proto.String(desc.help) + // TODO(beorn7): Simplify switch once Desc has type. + switch { + case dtoMetric.Gauge != nil: + metricFamily.Type = dto.MetricType_GAUGE.Enum() + case dtoMetric.Counter != nil: + metricFamily.Type = dto.MetricType_COUNTER.Enum() + case dtoMetric.Summary != nil: + metricFamily.Type = dto.MetricType_SUMMARY.Enum() + case dtoMetric.Untyped != nil: + metricFamily.Type = dto.MetricType_UNTYPED.Enum() + case dtoMetric.Histogram != nil: + metricFamily.Type = dto.MetricType_HISTOGRAM.Enum() + default: + errs = append(errs, fmt.Errorf( + "empty metric collected: %s", dtoMetric, + )) + continue } - case dto.MetricType_UNTYPED: - if dtoMetric.Untyped == nil { - return fmt.Errorf( - "collected metric %s %s should be Untyped", - desc.fqName, dtoMetric, - ) + metricFamiliesByName[desc.fqName] = metricFamily + } + if err := checkMetricConsistency(metricFamily, dtoMetric, metricHashes, dimHashes); err != nil { + errs = append(errs, err) + continue + } + if r.pedanticChecksEnabled { + // Is the desc registered at all? + if _, exist := registeredDescIDs[desc.id]; !exist { + errs = append(errs, fmt.Errorf( + "collected metric %s %s with unregistered descriptor %s", + metricFamily.GetName(), dtoMetric, desc, + )) + continue } - case dto.MetricType_HISTOGRAM: - if dtoMetric.Histogram == nil { - return fmt.Errorf( - "collected metric %s %s should be a Histogram", - desc.fqName, dtoMetric, - ) + if err := checkDescConsistency(metricFamily, dtoMetric, desc); err != nil { + errs = append(errs, err) + continue } - default: - panic("encountered MetricFamily with invalid type") - } - } else { // New name. - metricFamily = &dto.MetricFamily{} - metricFamily.Name = proto.String(desc.fqName) - metricFamily.Help = proto.String(desc.help) - // TODO(beorn7): Simplify switch once Desc has type. - switch { - case dtoMetric.Gauge != nil: - metricFamily.Type = dto.MetricType_GAUGE.Enum() - case dtoMetric.Counter != nil: - metricFamily.Type = dto.MetricType_COUNTER.Enum() - case dtoMetric.Summary != nil: - metricFamily.Type = dto.MetricType_SUMMARY.Enum() - case dtoMetric.Untyped != nil: - metricFamily.Type = dto.MetricType_UNTYPED.Enum() - case dtoMetric.Histogram != nil: - metricFamily.Type = dto.MetricType_HISTOGRAM.Enum() - default: - return fmt.Errorf("empty metric collected: %s", dtoMetric) - } - if err := checkSuffixCollisions(metricFamily, metricFamiliesByName); err != nil { - return err } - metricFamiliesByName[desc.fqName] = metricFamily + metricFamily.Metric = append(metricFamily.Metric, dtoMetric) } - if err := checkMetricConsistency(metricFamily, dtoMetric, metricHashes); err != nil { - return err - } - if registeredDescIDs != nil { - // Is the desc registered at all? - if _, exist := registeredDescIDs[desc.id]; !exist { - return fmt.Errorf( - "collected metric %s %s with unregistered descriptor %s", - metricFamily.GetName(), dtoMetric, desc, - ) - } - if err := checkDescConsistency(metricFamily, dtoMetric, desc); err != nil { - return err - } - } - metricFamily.Metric = append(metricFamily.Metric, dtoMetric) - return nil + return normalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap() } // Gatherers is a slice of Gatherer instances that implements the Gatherer @@ -700,6 +588,7 @@ func (gs Gatherers) Gather() ([]*dto.MetricFamily, error) { var ( metricFamiliesByName = map[string]*dto.MetricFamily{} metricHashes = map[uint64]struct{}{} + dimHashes = map[string]uint64{} errs MultiError // The collected errors to return in the end. ) @@ -736,14 +625,10 @@ func (gs Gatherers) Gather() ([]*dto.MetricFamily, error) { existingMF.Name = mf.Name existingMF.Help = mf.Help existingMF.Type = mf.Type - if err := checkSuffixCollisions(existingMF, metricFamiliesByName); err != nil { - errs = append(errs, err) - continue - } metricFamiliesByName[mf.GetName()] = existingMF } for _, m := range mf.Metric { - if err := checkMetricConsistency(existingMF, m, metricHashes); err != nil { + if err := checkMetricConsistency(existingMF, m, metricHashes, dimHashes); err != nil { errs = append(errs, err) continue } @@ -751,80 +636,88 @@ func (gs Gatherers) Gather() ([]*dto.MetricFamily, error) { } } } - return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap() + return normalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap() } -// checkSuffixCollisions checks for collisions with the “magic” suffixes the -// Prometheus text format and the internal metric representation of the -// Prometheus server add while flattening Summaries and Histograms. -func checkSuffixCollisions(mf *dto.MetricFamily, mfs map[string]*dto.MetricFamily) error { - var ( - newName = mf.GetName() - newType = mf.GetType() - newNameWithoutSuffix = "" - ) - switch { - case strings.HasSuffix(newName, "_count"): - newNameWithoutSuffix = newName[:len(newName)-6] - case strings.HasSuffix(newName, "_sum"): - newNameWithoutSuffix = newName[:len(newName)-4] - case strings.HasSuffix(newName, "_bucket"): - newNameWithoutSuffix = newName[:len(newName)-7] - } - if newNameWithoutSuffix != "" { - if existingMF, ok := mfs[newNameWithoutSuffix]; ok { - switch existingMF.GetType() { - case dto.MetricType_SUMMARY: - if !strings.HasSuffix(newName, "_bucket") { - return fmt.Errorf( - "collected metric named %q collides with previously collected summary named %q", - newName, newNameWithoutSuffix, - ) - } - case dto.MetricType_HISTOGRAM: - return fmt.Errorf( - "collected metric named %q collides with previously collected histogram named %q", - newName, newNameWithoutSuffix, - ) - } - } +// metricSorter is a sortable slice of *dto.Metric. +type metricSorter []*dto.Metric + +func (s metricSorter) Len() int { + return len(s) +} + +func (s metricSorter) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s metricSorter) Less(i, j int) bool { + if len(s[i].Label) != len(s[j].Label) { + // This should not happen. The metrics are + // inconsistent. However, we have to deal with the fact, as + // people might use custom collectors or metric family injection + // to create inconsistent metrics. So let's simply compare the + // number of labels in this case. That will still yield + // reproducible sorting. + return len(s[i].Label) < len(s[j].Label) + } + for n, lp := range s[i].Label { + vi := lp.GetValue() + vj := s[j].Label[n].GetValue() + if vi != vj { + return vi < vj + } + } + + // We should never arrive here. Multiple metrics with the same + // label set in the same scrape will lead to undefined ingestion + // behavior. However, as above, we have to provide stable sorting + // here, even for inconsistent metrics. So sort equal metrics + // by their timestamp, with missing timestamps (implying "now") + // coming last. + if s[i].TimestampMs == nil { + return false } - if newType == dto.MetricType_SUMMARY || newType == dto.MetricType_HISTOGRAM { - if _, ok := mfs[newName+"_count"]; ok { - return fmt.Errorf( - "collected histogram or summary named %q collides with previously collected metric named %q", - newName, newName+"_count", - ) - } - if _, ok := mfs[newName+"_sum"]; ok { - return fmt.Errorf( - "collected histogram or summary named %q collides with previously collected metric named %q", - newName, newName+"_sum", - ) - } + if s[j].TimestampMs == nil { + return true } - if newType == dto.MetricType_HISTOGRAM { - if _, ok := mfs[newName+"_bucket"]; ok { - return fmt.Errorf( - "collected histogram named %q collides with previously collected metric named %q", - newName, newName+"_bucket", - ) + return s[i].GetTimestampMs() < s[j].GetTimestampMs() +} + +// normalizeMetricFamilies returns a MetricFamily slice whith empty +// MetricFamilies pruned and the remaining MetricFamilies sorted by name within +// the slice, with the contained Metrics sorted within each MetricFamily. +func normalizeMetricFamilies(metricFamiliesByName map[string]*dto.MetricFamily) []*dto.MetricFamily { + for _, mf := range metricFamiliesByName { + sort.Sort(metricSorter(mf.Metric)) + } + names := make([]string, 0, len(metricFamiliesByName)) + for name, mf := range metricFamiliesByName { + if len(mf.Metric) > 0 { + names = append(names, name) } } - return nil + sort.Strings(names) + result := make([]*dto.MetricFamily, 0, len(names)) + for _, name := range names { + result = append(result, metricFamiliesByName[name]) + } + return result } // checkMetricConsistency checks if the provided Metric is consistent with the -// provided MetricFamily. It also hashes the Metric labels and the MetricFamily -// name. If the resulting hash is already in the provided metricHashes, an error -// is returned. If not, it is added to metricHashes. +// provided MetricFamily. It also hashed the Metric labels and the MetricFamily +// name. If the resulting hash is alread in the provided metricHashes, an error +// is returned. If not, it is added to metricHashes. The provided dimHashes maps +// MetricFamily names to their dimHash (hashed sorted label names). If dimHashes +// doesn't yet contain a hash for the provided MetricFamily, it is +// added. Otherwise, an error is returned if the existing dimHashes in not equal +// the calculated dimHash. func checkMetricConsistency( metricFamily *dto.MetricFamily, dtoMetric *dto.Metric, metricHashes map[uint64]struct{}, + dimHashes map[string]uint64, ) error { - name := metricFamily.GetName() - // Type consistency with metric family. if metricFamily.GetType() == dto.MetricType_GAUGE && dtoMetric.Gauge == nil || metricFamily.GetType() == dto.MetricType_COUNTER && dtoMetric.Counter == nil || @@ -832,65 +725,41 @@ func checkMetricConsistency( metricFamily.GetType() == dto.MetricType_HISTOGRAM && dtoMetric.Histogram == nil || metricFamily.GetType() == dto.MetricType_UNTYPED && dtoMetric.Untyped == nil { return fmt.Errorf( - "collected metric %q { %s} is not a %s", - name, dtoMetric, metricFamily.GetType(), + "collected metric %s %s is not a %s", + metricFamily.GetName(), dtoMetric, metricFamily.GetType(), ) } - previousLabelName := "" - for _, labelPair := range dtoMetric.GetLabel() { - labelName := labelPair.GetName() - if labelName == previousLabelName { - return fmt.Errorf( - "collected metric %q { %s} has two or more labels with the same name: %s", - name, dtoMetric, labelName, - ) - } - if !checkLabelName(labelName) { - return fmt.Errorf( - "collected metric %q { %s} has a label with an invalid name: %s", - name, dtoMetric, labelName, - ) - } - if dtoMetric.Summary != nil && labelName == quantileLabel { - return fmt.Errorf( - "collected metric %q { %s} must not have an explicit %q label", - name, dtoMetric, quantileLabel, - ) - } - if !utf8.ValidString(labelPair.GetValue()) { - return fmt.Errorf( - "collected metric %q { %s} has a label named %q whose value is not utf8: %#v", - name, dtoMetric, labelName, labelPair.GetValue()) - } - previousLabelName = labelName - } - - // Is the metric unique (i.e. no other metric with the same name and the same labels)? + // Is the metric unique (i.e. no other metric with the same name and the same label values)? h := hashNew() - h = hashAdd(h, name) + h = hashAdd(h, metricFamily.GetName()) h = hashAddByte(h, separatorByte) + dh := hashNew() // Make sure label pairs are sorted. We depend on it for the consistency // check. - if !sort.IsSorted(labelPairSorter(dtoMetric.Label)) { - // We cannot sort dtoMetric.Label in place as it is immutable by contract. - copiedLabels := make([]*dto.LabelPair, len(dtoMetric.Label)) - copy(copiedLabels, dtoMetric.Label) - sort.Sort(labelPairSorter(copiedLabels)) - dtoMetric.Label = copiedLabels - } + sort.Sort(LabelPairSorter(dtoMetric.Label)) for _, lp := range dtoMetric.Label { - h = hashAdd(h, lp.GetName()) - h = hashAddByte(h, separatorByte) h = hashAdd(h, lp.GetValue()) h = hashAddByte(h, separatorByte) + dh = hashAdd(dh, lp.GetName()) + dh = hashAddByte(dh, separatorByte) } if _, exists := metricHashes[h]; exists { return fmt.Errorf( - "collected metric %q { %s} was collected before with the same name and label values", - name, dtoMetric, + "collected metric %s %s was collected before with the same name and label values", + metricFamily.GetName(), dtoMetric, ) } + if dimHash, ok := dimHashes[metricFamily.GetName()]; ok { + if dimHash != dh { + return fmt.Errorf( + "collected metric %s %s has label dimensions inconsistent with previously collected metrics in the same metric family", + metricFamily.GetName(), dtoMetric, + ) + } + } else { + dimHashes[metricFamily.GetName()] = dh + } metricHashes[h] = struct{}{} return nil } @@ -909,8 +778,8 @@ func checkDescConsistency( } // Is the desc consistent with the content of the metric? - lpsFromDesc := make([]*dto.LabelPair, len(desc.constLabelPairs), len(dtoMetric.Label)) - copy(lpsFromDesc, desc.constLabelPairs) + lpsFromDesc := make([]*dto.LabelPair, 0, len(dtoMetric.Label)) + lpsFromDesc = append(lpsFromDesc, desc.constLabelPairs...) for _, l := range desc.variableLabels { lpsFromDesc = append(lpsFromDesc, &dto.LabelPair{ Name: proto.String(l), @@ -922,7 +791,7 @@ func checkDescConsistency( metricFamily.GetName(), dtoMetric, desc, ) } - sort.Sort(labelPairSorter(lpsFromDesc)) + sort.Sort(LabelPairSorter(lpsFromDesc)) for i, lpFromDesc := range lpsFromDesc { lpFromMetric := dtoMetric.Label[i] if lpFromDesc.GetName() != lpFromMetric.GetName() || diff --git a/vendor/github.com/prometheus/client_golang/prometheus/summary.go b/vendor/github.com/prometheus/client_golang/prometheus/summary.go index 2980614df..bce05bf9a 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/summary.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/summary.go @@ -36,10 +36,7 @@ const quantileLabel = "quantile" // // A typical use-case is the observation of request latencies. By default, a // Summary provides the median, the 90th and the 99th percentile of the latency -// as rank estimations. However, the default behavior will change in the -// upcoming v0.10 of the library. There will be no rank estimations at all by -// default. For a sane transition, it is recommended to set the desired rank -// estimations explicitly. +// as rank estimations. // // Note that the rank estimations cannot be aggregated in a meaningful way with // the Prometheus query language (i.e. you cannot average or add them). If you @@ -57,9 +54,6 @@ type Summary interface { } // DefObjectives are the default Summary quantile values. -// -// Deprecated: DefObjectives will not be used as the default objectives in -// v0.10 of the library. The default Summary will have no quantiles then. var ( DefObjectives = map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001} @@ -81,10 +75,8 @@ const ( ) // SummaryOpts bundles the options for creating a Summary metric. It is -// mandatory to set Name to a non-empty string. While all other fields are -// optional and can safely be left at their zero value, it is recommended to set -// a help string and to explicitly set the Objectives field to the desired value -// as the default value will change in the upcoming v0.10 of the library. +// mandatory to set Name and Help to a non-empty string. All other fields are +// optional and can safely be left at their zero value. type SummaryOpts struct { // Namespace, Subsystem, and Name are components of the fully-qualified // name of the Summary (created by joining these components with @@ -95,39 +87,35 @@ type SummaryOpts struct { Subsystem string Name string - // Help provides information about this Summary. + // Help provides information about this Summary. Mandatory! // // Metrics with the same fully-qualified name must have the same Help // string. Help string - // ConstLabels are used to attach fixed labels to this metric. Metrics - // with the same fully-qualified name must have the same label names in - // their ConstLabels. + // ConstLabels are used to attach fixed labels to this + // Summary. Summaries with the same fully-qualified name must have the + // same label names in their ConstLabels. // - // Due to the way a Summary is represented in the Prometheus text format - // and how it is handled by the Prometheus server internally, “quantile” - // is an illegal label name. Construction of a Summary or SummaryVec - // will panic if this label name is used in ConstLabels. + // Note that in most cases, labels have a value that varies during the + // lifetime of a process. Those labels are usually managed with a + // SummaryVec. ConstLabels serve only special purposes. One is for the + // special case where the value of a label does not change during the + // lifetime of a process, e.g. if the revision of the running binary is + // put into a label. Another, more advanced purpose is if more than one + // Collector needs to collect Summaries with the same fully-qualified + // name. In that case, those Summaries must differ in the values of + // their ConstLabels. See the Collector examples. // - // ConstLabels are only used rarely. In particular, do not use them to - // attach the same labels to all your metrics. Those use cases are - // better covered by target labels set by the scraping Prometheus - // server, or by one specific metric (e.g. a build_info or a - // machine_role metric). See also - // https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels + // If the value of a label never changes (not even between binaries), + // that label most likely should not be a label at all (but part of the + // metric name). ConstLabels Labels // Objectives defines the quantile rank estimates with their respective - // absolute error. If Objectives[q] = e, then the value reported for q - // will be the φ-quantile value for some φ between q-e and q+e. The - // default value is DefObjectives. It is used if Objectives is left at - // its zero value (i.e. nil). To create a Summary without Objectives, - // set it to an empty map (i.e. map[float64]float64{}). - // - // Deprecated: Note that the current value of DefObjectives is - // deprecated. It will be replaced by an empty map in v0.10 of the - // library. Please explicitly set Objectives to the desired value. + // absolute error. If Objectives[q] = e, then the value reported + // for q will be the φ-quantile value for some φ between q-e and q+e. + // The default value is DefObjectives. Objectives map[float64]float64 // MaxAge defines the duration for which an observation stays relevant @@ -181,7 +169,7 @@ func NewSummary(opts SummaryOpts) Summary { func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary { if len(desc.variableLabels) != len(labelValues) { - panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, labelValues)) + panic(errInconsistentCardinality) } for _, n := range desc.variableLabels { @@ -195,7 +183,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary { } } - if opts.Objectives == nil { + if len(opts.Objectives) == 0 { opts.Objectives = DefObjectives } @@ -402,21 +390,13 @@ func (s quantSort) Less(i, j int) bool { // (e.g. HTTP request latencies, partitioned by status code and method). Create // instances with NewSummaryVec. type SummaryVec struct { - *metricVec + *MetricVec } // NewSummaryVec creates a new SummaryVec based on the provided SummaryOpts and -// partitioned by the given label names. -// -// Due to the way a Summary is represented in the Prometheus text format and how -// it is handled by the Prometheus server internally, “quantile” is an illegal -// label name. NewSummaryVec will panic if this label name is used. +// partitioned by the given label names. At least one label name must be +// provided. func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec { - for _, ln := range labelNames { - if ln == quantileLabel { - panic(errQuantileLabelNotAllowed) - } - } desc := NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, @@ -424,116 +404,47 @@ func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec { opts.ConstLabels, ) return &SummaryVec{ - metricVec: newMetricVec(desc, func(lvs ...string) Metric { + MetricVec: newMetricVec(desc, func(lvs ...string) Metric { return newSummary(desc, opts, lvs...) }), } } -// GetMetricWithLabelValues returns the Summary for the given slice of label -// values (same order as the VariableLabels in Desc). If that combination of -// label values is accessed for the first time, a new Summary is created. -// -// It is possible to call this method without using the returned Summary to only -// create the new Summary but leave it at its starting value, a Summary without -// any observations. -// -// Keeping the Summary for later use is possible (and should be considered if -// performance is critical), but keep in mind that Reset, DeleteLabelValues and -// Delete can be used to delete the Summary from the SummaryVec. In that case, -// the Summary will still exist, but it will not be exported anymore, even if a -// Summary with the same label values is created later. See also the CounterVec -// example. -// -// An error is returned if the number of label values is not the same as the -// number of VariableLabels in Desc (minus any curried labels). -// -// Note that for more than one label value, this method is prone to mistakes -// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as -// an alternative to avoid that type of mistake. For higher label numbers, the -// latter has a much more readable (albeit more verbose) syntax, but it comes -// with a performance overhead (for creating and processing the Labels map). -// See also the GaugeVec example. -func (v *SummaryVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) { - metric, err := v.metricVec.getMetricWithLabelValues(lvs...) +// GetMetricWithLabelValues replaces the method of the same name in +// MetricVec. The difference is that this method returns a Summary and not a +// Metric so that no type conversion is required. +func (m *SummaryVec) GetMetricWithLabelValues(lvs ...string) (Summary, error) { + metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...) if metric != nil { - return metric.(Observer), err + return metric.(Summary), err } return nil, err } -// GetMetricWith returns the Summary for the given Labels map (the label names -// must match those of the VariableLabels in Desc). If that label map is -// accessed for the first time, a new Summary is created. Implications of -// creating a Summary without using it and keeping the Summary for later use are -// the same as for GetMetricWithLabelValues. -// -// An error is returned if the number and names of the Labels are inconsistent -// with those of the VariableLabels in Desc (minus any curried labels). -// -// This method is used for the same purpose as -// GetMetricWithLabelValues(...string). See there for pros and cons of the two -// methods. -func (v *SummaryVec) GetMetricWith(labels Labels) (Observer, error) { - metric, err := v.metricVec.getMetricWith(labels) +// GetMetricWith replaces the method of the same name in MetricVec. The +// difference is that this method returns a Summary and not a Metric so that no +// type conversion is required. +func (m *SummaryVec) GetMetricWith(labels Labels) (Summary, error) { + metric, err := m.MetricVec.GetMetricWith(labels) if metric != nil { - return metric.(Observer), err + return metric.(Summary), err } return nil, err } // WithLabelValues works as GetMetricWithLabelValues, but panics where -// GetMetricWithLabelValues would have returned an error. Not returning an -// error allows shortcuts like +// GetMetricWithLabelValues would have returned an error. By not returning an +// error, WithLabelValues allows shortcuts like // myVec.WithLabelValues("404", "GET").Observe(42.21) -func (v *SummaryVec) WithLabelValues(lvs ...string) Observer { - s, err := v.GetMetricWithLabelValues(lvs...) - if err != nil { - panic(err) - } - return s +func (m *SummaryVec) WithLabelValues(lvs ...string) Summary { + return m.MetricVec.WithLabelValues(lvs...).(Summary) } // With works as GetMetricWith, but panics where GetMetricWithLabels would have -// returned an error. Not returning an error allows shortcuts like -// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21) -func (v *SummaryVec) With(labels Labels) Observer { - s, err := v.GetMetricWith(labels) - if err != nil { - panic(err) - } - return s -} - -// CurryWith returns a vector curried with the provided labels, i.e. the -// returned vector has those labels pre-set for all labeled operations performed -// on it. The cardinality of the curried vector is reduced accordingly. The -// order of the remaining labels stays the same (just with the curried labels -// taken out of the sequence – which is relevant for the -// (GetMetric)WithLabelValues methods). It is possible to curry a curried -// vector, but only with labels not yet used for currying before. -// -// The metrics contained in the SummaryVec are shared between the curried and -// uncurried vectors. They are just accessed differently. Curried and uncurried -// vectors behave identically in terms of collection. Only one must be -// registered with a given registry (usually the uncurried version). The Reset -// method deletes all metrics, even if called on a curried vector. -func (v *SummaryVec) CurryWith(labels Labels) (ObserverVec, error) { - vec, err := v.curryWith(labels) - if vec != nil { - return &SummaryVec{vec}, err - } - return nil, err -} - -// MustCurryWith works as CurryWith but panics where CurryWith would have -// returned an error. -func (v *SummaryVec) MustCurryWith(labels Labels) ObserverVec { - vec, err := v.CurryWith(labels) - if err != nil { - panic(err) - } - return vec +// returned an error. By not returning an error, With allows shortcuts like +// myVec.With(Labels{"code": "404", "method": "GET"}).Observe(42.21) +func (m *SummaryVec) With(labels Labels) Summary { + return m.MetricVec.With(labels).(Summary) } type constSummary struct { @@ -586,7 +497,7 @@ func (s *constSummary) Write(out *dto.Metric) error { // map[float64]float64{0.5: 0.23, 0.99: 0.56} // // NewConstSummary returns an error if the length of labelValues is not -// consistent with the variable labels in Desc or if Desc is invalid. +// consistent with the variable labels in Desc. func NewConstSummary( desc *Desc, count uint64, @@ -594,11 +505,8 @@ func NewConstSummary( quantiles map[float64]float64, labelValues ...string, ) (Metric, error) { - if desc.err != nil { - return nil, desc.err - } - if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil { - return nil, err + if len(desc.variableLabels) != len(labelValues) { + return nil, errInconsistentCardinality } return &constSummary{ desc: desc, diff --git a/vendor/github.com/prometheus/client_golang/prometheus/timer.go b/vendor/github.com/prometheus/client_golang/prometheus/timer.go deleted file mode 100644 index 8d5f10523..000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/timer.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2016 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus - -import "time" - -// Timer is a helper type to time functions. Use NewTimer to create new -// instances. -type Timer struct { - begin time.Time - observer Observer -} - -// NewTimer creates a new Timer. The provided Observer is used to observe a -// duration in seconds. Timer is usually used to time a function call in the -// following way: -// func TimeMe() { -// timer := NewTimer(myHistogram) -// defer timer.ObserveDuration() -// // Do actual work. -// } -func NewTimer(o Observer) *Timer { - return &Timer{ - begin: time.Now(), - observer: o, - } -} - -// ObserveDuration records the duration passed since the Timer was created with -// NewTimer. It calls the Observe method of the Observer provided during -// construction with the duration in seconds as an argument. The observed -// duration is also returned. ObserveDuration is usually called with a defer -// statement. -// -// Note that this method is only guaranteed to never observe negative durations -// if used with Go1.9+. -func (t *Timer) ObserveDuration() time.Duration { - d := time.Since(t.begin) - if t.observer != nil { - t.observer.Observe(d.Seconds()) - } - return d -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/untyped.go b/vendor/github.com/prometheus/client_golang/prometheus/untyped.go index 0f9ce63f4..5faf7e6e3 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/untyped.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/untyped.go @@ -13,12 +13,108 @@ package prometheus +// Untyped is a Metric that represents a single numerical value that can +// arbitrarily go up and down. +// +// An Untyped metric works the same as a Gauge. The only difference is that to +// no type information is implied. +// +// To create Untyped instances, use NewUntyped. +type Untyped interface { + Metric + Collector + + // Set sets the Untyped metric to an arbitrary value. + Set(float64) + // Inc increments the Untyped metric by 1. + Inc() + // Dec decrements the Untyped metric by 1. + Dec() + // Add adds the given value to the Untyped metric. (The value can be + // negative, resulting in a decrease.) + Add(float64) + // Sub subtracts the given value from the Untyped metric. (The value can + // be negative, resulting in an increase.) + Sub(float64) +} + // UntypedOpts is an alias for Opts. See there for doc comments. type UntypedOpts Opts -// UntypedFunc works like GaugeFunc but the collected metric is of type -// "Untyped". UntypedFunc is useful to mirror an external metric of unknown -// type. +// NewUntyped creates a new Untyped metric from the provided UntypedOpts. +func NewUntyped(opts UntypedOpts) Untyped { + return newValue(NewDesc( + BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), + opts.Help, + nil, + opts.ConstLabels, + ), UntypedValue, 0) +} + +// UntypedVec is a Collector that bundles a set of Untyped metrics that all +// share the same Desc, but have different values for their variable +// labels. This is used if you want to count the same thing partitioned by +// various dimensions. Create instances with NewUntypedVec. +type UntypedVec struct { + *MetricVec +} + +// NewUntypedVec creates a new UntypedVec based on the provided UntypedOpts and +// partitioned by the given label names. At least one label name must be +// provided. +func NewUntypedVec(opts UntypedOpts, labelNames []string) *UntypedVec { + desc := NewDesc( + BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), + opts.Help, + labelNames, + opts.ConstLabels, + ) + return &UntypedVec{ + MetricVec: newMetricVec(desc, func(lvs ...string) Metric { + return newValue(desc, UntypedValue, 0, lvs...) + }), + } +} + +// GetMetricWithLabelValues replaces the method of the same name in +// MetricVec. The difference is that this method returns an Untyped and not a +// Metric so that no type conversion is required. +func (m *UntypedVec) GetMetricWithLabelValues(lvs ...string) (Untyped, error) { + metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...) + if metric != nil { + return metric.(Untyped), err + } + return nil, err +} + +// GetMetricWith replaces the method of the same name in MetricVec. The +// difference is that this method returns an Untyped and not a Metric so that no +// type conversion is required. +func (m *UntypedVec) GetMetricWith(labels Labels) (Untyped, error) { + metric, err := m.MetricVec.GetMetricWith(labels) + if metric != nil { + return metric.(Untyped), err + } + return nil, err +} + +// WithLabelValues works as GetMetricWithLabelValues, but panics where +// GetMetricWithLabelValues would have returned an error. By not returning an +// error, WithLabelValues allows shortcuts like +// myVec.WithLabelValues("404", "GET").Add(42) +func (m *UntypedVec) WithLabelValues(lvs ...string) Untyped { + return m.MetricVec.WithLabelValues(lvs...).(Untyped) +} + +// With works as GetMetricWith, but panics where GetMetricWithLabels would have +// returned an error. By not returning an error, With allows shortcuts like +// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42) +func (m *UntypedVec) With(labels Labels) Untyped { + return m.MetricVec.With(labels).(Untyped) +} + +// UntypedFunc is an Untyped whose value is determined at collect time by +// calling a provided function. // // To create UntypedFunc instances, use NewUntypedFunc. type UntypedFunc interface { diff --git a/vendor/github.com/prometheus/client_golang/prometheus/value.go b/vendor/github.com/prometheus/client_golang/prometheus/value.go index eb248f108..a944c3775 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/value.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/value.go @@ -14,12 +14,15 @@ package prometheus import ( + "errors" "fmt" + "math" "sort" - - "github.com/golang/protobuf/proto" + "sync/atomic" dto "github.com/prometheus/client_model/go" + + "github.com/golang/protobuf/proto" ) // ValueType is an enumeration of metric types that represent a simple value. @@ -33,6 +36,77 @@ const ( UntypedValue ) +var errInconsistentCardinality = errors.New("inconsistent label cardinality") + +// value is a generic metric for simple values. It implements Metric, Collector, +// Counter, Gauge, and Untyped. Its effective type is determined by +// ValueType. This is a low-level building block used by the library to back the +// implementations of Counter, Gauge, and Untyped. +type value struct { + // valBits containst the bits of the represented float64 value. It has + // to go first in the struct to guarantee alignment for atomic + // operations. http://golang.org/pkg/sync/atomic/#pkg-note-BUG + valBits uint64 + + selfCollector + + desc *Desc + valType ValueType + labelPairs []*dto.LabelPair +} + +// newValue returns a newly allocated value with the given Desc, ValueType, +// sample value and label values. It panics if the number of label +// values is different from the number of variable labels in Desc. +func newValue(desc *Desc, valueType ValueType, val float64, labelValues ...string) *value { + if len(labelValues) != len(desc.variableLabels) { + panic(errInconsistentCardinality) + } + result := &value{ + desc: desc, + valType: valueType, + valBits: math.Float64bits(val), + labelPairs: makeLabelPairs(desc, labelValues), + } + result.init(result) + return result +} + +func (v *value) Desc() *Desc { + return v.desc +} + +func (v *value) Set(val float64) { + atomic.StoreUint64(&v.valBits, math.Float64bits(val)) +} + +func (v *value) Inc() { + v.Add(1) +} + +func (v *value) Dec() { + v.Add(-1) +} + +func (v *value) Add(val float64) { + for { + oldBits := atomic.LoadUint64(&v.valBits) + newBits := math.Float64bits(math.Float64frombits(oldBits) + val) + if atomic.CompareAndSwapUint64(&v.valBits, oldBits, newBits) { + return + } + } +} + +func (v *value) Sub(val float64) { + v.Add(val * -1) +} + +func (v *value) Write(out *dto.Metric) error { + val := math.Float64frombits(atomic.LoadUint64(&v.valBits)) + return populateMetric(v.valType, val, v.labelPairs, out) +} + // valueFunc is a generic metric for simple values retrieved on collect time // from a function. It implements Metric and Collector. Its effective type is // determined by ValueType. This is a low-level building block used by the @@ -77,14 +151,10 @@ func (v *valueFunc) Write(out *dto.Metric) error { // operations. However, when implementing custom Collectors, it is useful as a // throw-away metric that is generated on the fly to send it to Prometheus in // the Collect method. NewConstMetric returns an error if the length of -// labelValues is not consistent with the variable labels in Desc or if Desc is -// invalid. +// labelValues is not consistent with the variable labels in Desc. func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) (Metric, error) { - if desc.err != nil { - return nil, desc.err - } - if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil { - return nil, err + if len(desc.variableLabels) != len(labelValues) { + return nil, errInconsistentCardinality } return &constMetric{ desc: desc, @@ -156,7 +226,9 @@ func makeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair { Value: proto.String(labelValues[i]), }) } - labelPairs = append(labelPairs, desc.constLabelPairs...) - sort.Sort(labelPairSorter(labelPairs)) + for _, lp := range desc.constLabelPairs { + labelPairs = append(labelPairs, lp) + } + sort.Sort(LabelPairSorter(labelPairs)) return labelPairs } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/vec.go b/vendor/github.com/prometheus/client_golang/prometheus/vec.go index 14ed9e856..7f3eef9a4 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/vec.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/vec.go @@ -20,253 +20,200 @@ import ( "github.com/prometheus/common/model" ) -// metricVec is a Collector to bundle metrics of the same name that differ in -// their label values. metricVec is not used directly (and therefore -// unexported). It is used as a building block for implementations of vectors of -// a given metric type, like GaugeVec, CounterVec, SummaryVec, and HistogramVec. -// It also handles label currying. It uses basicMetricVec internally. -type metricVec struct { - *metricMap - - curry []curriedLabelValue - - // hashAdd and hashAddByte can be replaced for testing collision handling. - hashAdd func(h uint64, s string) uint64 +// MetricVec is a Collector to bundle metrics of the same name that +// differ in their label values. MetricVec is usually not used directly but as a +// building block for implementations of vectors of a given metric +// type. GaugeVec, CounterVec, SummaryVec, and UntypedVec are examples already +// provided in this package. +type MetricVec struct { + mtx sync.RWMutex // Protects the children. + children map[uint64][]metricWithLabelValues + desc *Desc + + newMetric func(labelValues ...string) Metric + hashAdd func(h uint64, s string) uint64 // replace hash function for testing collision handling hashAddByte func(h uint64, b byte) uint64 } -// newMetricVec returns an initialized metricVec. -func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *metricVec { - return &metricVec{ - metricMap: &metricMap{ - metrics: map[uint64][]metricWithLabelValues{}, - desc: desc, - newMetric: newMetric, - }, +// newMetricVec returns an initialized MetricVec. The concrete value is +// returned for embedding into another struct. +func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec { + return &MetricVec{ + children: map[uint64][]metricWithLabelValues{}, + desc: desc, + newMetric: newMetric, hashAdd: hashAdd, hashAddByte: hashAddByte, } } -// DeleteLabelValues removes the metric where the variable labels are the same -// as those passed in as labels (same order as the VariableLabels in Desc). It -// returns true if a metric was deleted. -// -// It is not an error if the number of label values is not the same as the -// number of VariableLabels in Desc. However, such inconsistent label count can -// never match an actual metric, so the method will always return false in that -// case. -// -// Note that for more than one label value, this method is prone to mistakes -// caused by an incorrect order of arguments. Consider Delete(Labels) as an -// alternative to avoid that type of mistake. For higher label numbers, the -// latter has a much more readable (albeit more verbose) syntax, but it comes -// with a performance overhead (for creating and processing the Labels map). -// See also the CounterVec example. -func (m *metricVec) DeleteLabelValues(lvs ...string) bool { - h, err := m.hashLabelValues(lvs) - if err != nil { - return false - } - - return m.metricMap.deleteByHashWithLabelValues(h, lvs, m.curry) +// metricWithLabelValues provides the metric and its label values for +// disambiguation on hash collision. +type metricWithLabelValues struct { + values []string + metric Metric } -// Delete deletes the metric where the variable labels are the same as those -// passed in as labels. It returns true if a metric was deleted. -// -// It is not an error if the number and names of the Labels are inconsistent -// with those of the VariableLabels in Desc. However, such inconsistent Labels -// can never match an actual metric, so the method will always return false in -// that case. -// -// This method is used for the same purpose as DeleteLabelValues(...string). See -// there for pros and cons of the two methods. -func (m *metricVec) Delete(labels Labels) bool { - h, err := m.hashLabels(labels) - if err != nil { - return false - } - - return m.metricMap.deleteByHashWithLabels(h, labels, m.curry) +// Describe implements Collector. The length of the returned slice +// is always one. +func (m *MetricVec) Describe(ch chan<- *Desc) { + ch <- m.desc } -func (m *metricVec) curryWith(labels Labels) (*metricVec, error) { - var ( - newCurry []curriedLabelValue - oldCurry = m.curry - iCurry int - ) - for i, label := range m.desc.variableLabels { - val, ok := labels[label] - if iCurry < len(oldCurry) && oldCurry[iCurry].index == i { - if ok { - return nil, fmt.Errorf("label name %q is already curried", label) - } - newCurry = append(newCurry, oldCurry[iCurry]) - iCurry++ - } else { - if !ok { - continue // Label stays uncurried. - } - newCurry = append(newCurry, curriedLabelValue{i, val}) +// Collect implements Collector. +func (m *MetricVec) Collect(ch chan<- Metric) { + m.mtx.RLock() + defer m.mtx.RUnlock() + + for _, metrics := range m.children { + for _, metric := range metrics { + ch <- metric.metric } } - if l := len(oldCurry) + len(labels) - len(newCurry); l > 0 { - return nil, fmt.Errorf("%d unknown label(s) found during currying", l) - } - - return &metricVec{ - metricMap: m.metricMap, - curry: newCurry, - hashAdd: m.hashAdd, - hashAddByte: m.hashAddByte, - }, nil } -func (m *metricVec) getMetricWithLabelValues(lvs ...string) (Metric, error) { +// GetMetricWithLabelValues returns the Metric for the given slice of label +// values (same order as the VariableLabels in Desc). If that combination of +// label values is accessed for the first time, a new Metric is created. +// +// It is possible to call this method without using the returned Metric to only +// create the new Metric but leave it at its start value (e.g. a Summary or +// Histogram without any observations). See also the SummaryVec example. +// +// Keeping the Metric for later use is possible (and should be considered if +// performance is critical), but keep in mind that Reset, DeleteLabelValues and +// Delete can be used to delete the Metric from the MetricVec. In that case, the +// Metric will still exist, but it will not be exported anymore, even if a +// Metric with the same label values is created later. See also the CounterVec +// example. +// +// An error is returned if the number of label values is not the same as the +// number of VariableLabels in Desc. +// +// Note that for more than one label value, this method is prone to mistakes +// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as +// an alternative to avoid that type of mistake. For higher label numbers, the +// latter has a much more readable (albeit more verbose) syntax, but it comes +// with a performance overhead (for creating and processing the Labels map). +// See also the GaugeVec example. +func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) { h, err := m.hashLabelValues(lvs) if err != nil { return nil, err } - return m.metricMap.getOrCreateMetricWithLabelValues(h, lvs, m.curry), nil + return m.getOrCreateMetricWithLabelValues(h, lvs), nil } -func (m *metricVec) getMetricWith(labels Labels) (Metric, error) { +// GetMetricWith returns the Metric for the given Labels map (the label names +// must match those of the VariableLabels in Desc). If that label map is +// accessed for the first time, a new Metric is created. Implications of +// creating a Metric without using it and keeping the Metric for later use are +// the same as for GetMetricWithLabelValues. +// +// An error is returned if the number and names of the Labels are inconsistent +// with those of the VariableLabels in Desc. +// +// This method is used for the same purpose as +// GetMetricWithLabelValues(...string). See there for pros and cons of the two +// methods. +func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) { h, err := m.hashLabels(labels) if err != nil { return nil, err } - return m.metricMap.getOrCreateMetricWithLabels(h, labels, m.curry), nil + return m.getOrCreateMetricWithLabels(h, labels), nil } -func (m *metricVec) hashLabelValues(vals []string) (uint64, error) { - if err := validateLabelValues(vals, len(m.desc.variableLabels)-len(m.curry)); err != nil { - return 0, err - } - - var ( - h = hashNew() - curry = m.curry - iVals, iCurry int - ) - for i := 0; i < len(m.desc.variableLabels); i++ { - if iCurry < len(curry) && curry[iCurry].index == i { - h = m.hashAdd(h, curry[iCurry].value) - iCurry++ - } else { - h = m.hashAdd(h, vals[iVals]) - iVals++ - } - h = m.hashAddByte(h, model.SeparatorByte) +// WithLabelValues works as GetMetricWithLabelValues, but panics if an error +// occurs. The method allows neat syntax like: +// httpReqs.WithLabelValues("404", "POST").Inc() +func (m *MetricVec) WithLabelValues(lvs ...string) Metric { + metric, err := m.GetMetricWithLabelValues(lvs...) + if err != nil { + panic(err) } - return h, nil + return metric } -func (m *metricVec) hashLabels(labels Labels) (uint64, error) { - if err := validateValuesInLabels(labels, len(m.desc.variableLabels)-len(m.curry)); err != nil { - return 0, err - } - - var ( - h = hashNew() - curry = m.curry - iCurry int - ) - for i, label := range m.desc.variableLabels { - val, ok := labels[label] - if iCurry < len(curry) && curry[iCurry].index == i { - if ok { - return 0, fmt.Errorf("label name %q is already curried", label) - } - h = m.hashAdd(h, curry[iCurry].value) - iCurry++ - } else { - if !ok { - return 0, fmt.Errorf("label name %q missing in label map", label) - } - h = m.hashAdd(h, val) - } - h = m.hashAddByte(h, model.SeparatorByte) +// With works as GetMetricWith, but panics if an error occurs. The method allows +// neat syntax like: +// httpReqs.With(Labels{"status":"404", "method":"POST"}).Inc() +func (m *MetricVec) With(labels Labels) Metric { + metric, err := m.GetMetricWith(labels) + if err != nil { + panic(err) } - return h, nil -} - -// metricWithLabelValues provides the metric and its label values for -// disambiguation on hash collision. -type metricWithLabelValues struct { - values []string - metric Metric -} - -// curriedLabelValue sets the curried value for a label at the given index. -type curriedLabelValue struct { - index int - value string -} - -// metricMap is a helper for metricVec and shared between differently curried -// metricVecs. -type metricMap struct { - mtx sync.RWMutex // Protects metrics. - metrics map[uint64][]metricWithLabelValues - desc *Desc - newMetric func(labelValues ...string) Metric -} - -// Describe implements Collector. It will send exactly one Desc to the provided -// channel. -func (m *metricMap) Describe(ch chan<- *Desc) { - ch <- m.desc + return metric } -// Collect implements Collector. -func (m *metricMap) Collect(ch chan<- Metric) { - m.mtx.RLock() - defer m.mtx.RUnlock() +// DeleteLabelValues removes the metric where the variable labels are the same +// as those passed in as labels (same order as the VariableLabels in Desc). It +// returns true if a metric was deleted. +// +// It is not an error if the number of label values is not the same as the +// number of VariableLabels in Desc. However, such inconsistent label count can +// never match an actual Metric, so the method will always return false in that +// case. +// +// Note that for more than one label value, this method is prone to mistakes +// caused by an incorrect order of arguments. Consider Delete(Labels) as an +// alternative to avoid that type of mistake. For higher label numbers, the +// latter has a much more readable (albeit more verbose) syntax, but it comes +// with a performance overhead (for creating and processing the Labels map). +// See also the CounterVec example. +func (m *MetricVec) DeleteLabelValues(lvs ...string) bool { + m.mtx.Lock() + defer m.mtx.Unlock() - for _, metrics := range m.metrics { - for _, metric := range metrics { - ch <- metric.metric - } + h, err := m.hashLabelValues(lvs) + if err != nil { + return false } + return m.deleteByHashWithLabelValues(h, lvs) } -// Reset deletes all metrics in this vector. -func (m *metricMap) Reset() { +// Delete deletes the metric where the variable labels are the same as those +// passed in as labels. It returns true if a metric was deleted. +// +// It is not an error if the number and names of the Labels are inconsistent +// with those of the VariableLabels in the Desc of the MetricVec. However, such +// inconsistent Labels can never match an actual Metric, so the method will +// always return false in that case. +// +// This method is used for the same purpose as DeleteLabelValues(...string). See +// there for pros and cons of the two methods. +func (m *MetricVec) Delete(labels Labels) bool { m.mtx.Lock() defer m.mtx.Unlock() - for h := range m.metrics { - delete(m.metrics, h) + h, err := m.hashLabels(labels) + if err != nil { + return false } + + return m.deleteByHashWithLabels(h, labels) } // deleteByHashWithLabelValues removes the metric from the hash bucket h. If // there are multiple matches in the bucket, use lvs to select a metric and // remove only that metric. -func (m *metricMap) deleteByHashWithLabelValues( - h uint64, lvs []string, curry []curriedLabelValue, -) bool { - m.mtx.Lock() - defer m.mtx.Unlock() - - metrics, ok := m.metrics[h] +func (m *MetricVec) deleteByHashWithLabelValues(h uint64, lvs []string) bool { + metrics, ok := m.children[h] if !ok { return false } - i := findMetricWithLabelValues(metrics, lvs, curry) + i := m.findMetricWithLabelValues(metrics, lvs) if i >= len(metrics) { return false } if len(metrics) > 1 { - m.metrics[h] = append(metrics[:i], metrics[i+1:]...) + m.children[h] = append(metrics[:i], metrics[i+1:]...) } else { - delete(m.metrics, h) + delete(m.children, h) } return true } @@ -274,38 +221,69 @@ func (m *metricMap) deleteByHashWithLabelValues( // deleteByHashWithLabels removes the metric from the hash bucket h. If there // are multiple matches in the bucket, use lvs to select a metric and remove // only that metric. -func (m *metricMap) deleteByHashWithLabels( - h uint64, labels Labels, curry []curriedLabelValue, -) bool { - m.mtx.Lock() - defer m.mtx.Unlock() - - metrics, ok := m.metrics[h] +func (m *MetricVec) deleteByHashWithLabels(h uint64, labels Labels) bool { + metrics, ok := m.children[h] if !ok { return false } - i := findMetricWithLabels(m.desc, metrics, labels, curry) + i := m.findMetricWithLabels(metrics, labels) if i >= len(metrics) { return false } if len(metrics) > 1 { - m.metrics[h] = append(metrics[:i], metrics[i+1:]...) + m.children[h] = append(metrics[:i], metrics[i+1:]...) } else { - delete(m.metrics, h) + delete(m.children, h) } return true } +// Reset deletes all metrics in this vector. +func (m *MetricVec) Reset() { + m.mtx.Lock() + defer m.mtx.Unlock() + + for h := range m.children { + delete(m.children, h) + } +} + +func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) { + if len(vals) != len(m.desc.variableLabels) { + return 0, errInconsistentCardinality + } + h := hashNew() + for _, val := range vals { + h = m.hashAdd(h, val) + h = m.hashAddByte(h, model.SeparatorByte) + } + return h, nil +} + +func (m *MetricVec) hashLabels(labels Labels) (uint64, error) { + if len(labels) != len(m.desc.variableLabels) { + return 0, errInconsistentCardinality + } + h := hashNew() + for _, label := range m.desc.variableLabels { + val, ok := labels[label] + if !ok { + return 0, fmt.Errorf("label name %q missing in label map", label) + } + h = m.hashAdd(h, val) + h = m.hashAddByte(h, model.SeparatorByte) + } + return h, nil +} + // getOrCreateMetricWithLabelValues retrieves the metric by hash and label value // or creates it and returns the new one. // // This function holds the mutex. -func (m *metricMap) getOrCreateMetricWithLabelValues( - hash uint64, lvs []string, curry []curriedLabelValue, -) Metric { +func (m *MetricVec) getOrCreateMetricWithLabelValues(hash uint64, lvs []string) Metric { m.mtx.RLock() - metric, ok := m.getMetricWithHashAndLabelValues(hash, lvs, curry) + metric, ok := m.getMetricWithLabelValues(hash, lvs) m.mtx.RUnlock() if ok { return metric @@ -313,11 +291,13 @@ func (m *metricMap) getOrCreateMetricWithLabelValues( m.mtx.Lock() defer m.mtx.Unlock() - metric, ok = m.getMetricWithHashAndLabelValues(hash, lvs, curry) + metric, ok = m.getMetricWithLabelValues(hash, lvs) if !ok { - inlinedLVs := inlineLabelValues(lvs, curry) - metric = m.newMetric(inlinedLVs...) - m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: inlinedLVs, metric: metric}) + // Copy to avoid allocation in case wo don't go down this code path. + copiedLVs := make([]string, len(lvs)) + copy(copiedLVs, lvs) + metric = m.newMetric(copiedLVs...) + m.children[hash] = append(m.children[hash], metricWithLabelValues{values: copiedLVs, metric: metric}) } return metric } @@ -326,11 +306,9 @@ func (m *metricMap) getOrCreateMetricWithLabelValues( // or creates it and returns the new one. // // This function holds the mutex. -func (m *metricMap) getOrCreateMetricWithLabels( - hash uint64, labels Labels, curry []curriedLabelValue, -) Metric { +func (m *MetricVec) getOrCreateMetricWithLabels(hash uint64, labels Labels) Metric { m.mtx.RLock() - metric, ok := m.getMetricWithHashAndLabels(hash, labels, curry) + metric, ok := m.getMetricWithLabels(hash, labels) m.mtx.RUnlock() if ok { return metric @@ -338,37 +316,33 @@ func (m *metricMap) getOrCreateMetricWithLabels( m.mtx.Lock() defer m.mtx.Unlock() - metric, ok = m.getMetricWithHashAndLabels(hash, labels, curry) + metric, ok = m.getMetricWithLabels(hash, labels) if !ok { - lvs := extractLabelValues(m.desc, labels, curry) + lvs := m.extractLabelValues(labels) metric = m.newMetric(lvs...) - m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: lvs, metric: metric}) + m.children[hash] = append(m.children[hash], metricWithLabelValues{values: lvs, metric: metric}) } return metric } -// getMetricWithHashAndLabelValues gets a metric while handling possible -// collisions in the hash space. Must be called while holding the read mutex. -func (m *metricMap) getMetricWithHashAndLabelValues( - h uint64, lvs []string, curry []curriedLabelValue, -) (Metric, bool) { - metrics, ok := m.metrics[h] +// getMetricWithLabelValues gets a metric while handling possible collisions in +// the hash space. Must be called while holding read mutex. +func (m *MetricVec) getMetricWithLabelValues(h uint64, lvs []string) (Metric, bool) { + metrics, ok := m.children[h] if ok { - if i := findMetricWithLabelValues(metrics, lvs, curry); i < len(metrics) { + if i := m.findMetricWithLabelValues(metrics, lvs); i < len(metrics) { return metrics[i].metric, true } } return nil, false } -// getMetricWithHashAndLabels gets a metric while handling possible collisions in +// getMetricWithLabels gets a metric while handling possible collisions in // the hash space. Must be called while holding read mutex. -func (m *metricMap) getMetricWithHashAndLabels( - h uint64, labels Labels, curry []curriedLabelValue, -) (Metric, bool) { - metrics, ok := m.metrics[h] +func (m *MetricVec) getMetricWithLabels(h uint64, labels Labels) (Metric, bool) { + metrics, ok := m.children[h] if ok { - if i := findMetricWithLabels(m.desc, metrics, labels, curry); i < len(metrics) { + if i := m.findMetricWithLabels(metrics, labels); i < len(metrics) { return metrics[i].metric, true } } @@ -377,11 +351,9 @@ func (m *metricMap) getMetricWithHashAndLabels( // findMetricWithLabelValues returns the index of the matching metric or // len(metrics) if not found. -func findMetricWithLabelValues( - metrics []metricWithLabelValues, lvs []string, curry []curriedLabelValue, -) int { +func (m *MetricVec) findMetricWithLabelValues(metrics []metricWithLabelValues, lvs []string) int { for i, metric := range metrics { - if matchLabelValues(metric.values, lvs, curry) { + if m.matchLabelValues(metric.values, lvs) { return i } } @@ -390,51 +362,32 @@ func findMetricWithLabelValues( // findMetricWithLabels returns the index of the matching metric or len(metrics) // if not found. -func findMetricWithLabels( - desc *Desc, metrics []metricWithLabelValues, labels Labels, curry []curriedLabelValue, -) int { +func (m *MetricVec) findMetricWithLabels(metrics []metricWithLabelValues, labels Labels) int { for i, metric := range metrics { - if matchLabels(desc, metric.values, labels, curry) { + if m.matchLabels(metric.values, labels) { return i } } return len(metrics) } -func matchLabelValues(values []string, lvs []string, curry []curriedLabelValue) bool { - if len(values) != len(lvs)+len(curry) { +func (m *MetricVec) matchLabelValues(values []string, lvs []string) bool { + if len(values) != len(lvs) { return false } - var iLVs, iCurry int for i, v := range values { - if iCurry < len(curry) && curry[iCurry].index == i { - if v != curry[iCurry].value { - return false - } - iCurry++ - continue - } - if v != lvs[iLVs] { + if v != lvs[i] { return false } - iLVs++ } return true } -func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool { - if len(values) != len(labels)+len(curry) { +func (m *MetricVec) matchLabels(values []string, labels Labels) bool { + if len(labels) != len(values) { return false } - iCurry := 0 - for i, k := range desc.variableLabels { - if iCurry < len(curry) && curry[iCurry].index == i { - if values[i] != curry[iCurry].value { - return false - } - iCurry++ - continue - } + for i, k := range m.desc.variableLabels { if values[i] != labels[k] { return false } @@ -442,31 +395,10 @@ func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabe return true } -func extractLabelValues(desc *Desc, labels Labels, curry []curriedLabelValue) []string { - labelValues := make([]string, len(labels)+len(curry)) - iCurry := 0 - for i, k := range desc.variableLabels { - if iCurry < len(curry) && curry[iCurry].index == i { - labelValues[i] = curry[iCurry].value - iCurry++ - continue - } +func (m *MetricVec) extractLabelValues(labels Labels) []string { + labelValues := make([]string, len(labels)) + for i, k := range m.desc.variableLabels { labelValues[i] = labels[k] } return labelValues } - -func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string { - labelValues := make([]string, len(lvs)+len(curry)) - var iCurry, iLVs int - for i := range labelValues { - if iCurry < len(curry) && curry[iCurry].index == i { - labelValues[i] = curry[iCurry].value - iCurry++ - continue - } - labelValues[i] = lvs[iLVs] - iLVs++ - } - return labelValues -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/wrap.go b/vendor/github.com/prometheus/client_golang/prometheus/wrap.go deleted file mode 100644 index 49159bf3e..000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/wrap.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2018 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus - -import ( - "fmt" - "sort" - - "github.com/golang/protobuf/proto" - - dto "github.com/prometheus/client_model/go" -) - -// WrapRegistererWith returns a Registerer wrapping the provided -// Registerer. Collectors registered with the returned Registerer will be -// registered with the wrapped Registerer in a modified way. The modified -// Collector adds the provided Labels to all Metrics it collects (as -// ConstLabels). The Metrics collected by the unmodified Collector must not -// duplicate any of those labels. -// -// WrapRegistererWith provides a way to add fixed labels to a subset of -// Collectors. It should not be used to add fixed labels to all metrics exposed. -// -// The Collector example demonstrates a use of WrapRegistererWith. -func WrapRegistererWith(labels Labels, reg Registerer) Registerer { - return &wrappingRegisterer{ - wrappedRegisterer: reg, - labels: labels, - } -} - -// WrapRegistererWithPrefix returns a Registerer wrapping the provided -// Registerer. Collectors registered with the returned Registerer will be -// registered with the wrapped Registerer in a modified way. The modified -// Collector adds the provided prefix to the name of all Metrics it collects. -// -// WrapRegistererWithPrefix is useful to have one place to prefix all metrics of -// a sub-system. To make this work, register metrics of the sub-system with the -// wrapping Registerer returned by WrapRegistererWithPrefix. It is rarely useful -// to use the same prefix for all metrics exposed. In particular, do not prefix -// metric names that are standardized across applications, as that would break -// horizontal monitoring, for example the metrics provided by the Go collector -// (see NewGoCollector) and the process collector (see NewProcessCollector). (In -// fact, those metrics are already prefixed with “go_” or “process_”, -// respectively.) -func WrapRegistererWithPrefix(prefix string, reg Registerer) Registerer { - return &wrappingRegisterer{ - wrappedRegisterer: reg, - prefix: prefix, - } -} - -type wrappingRegisterer struct { - wrappedRegisterer Registerer - prefix string - labels Labels -} - -func (r *wrappingRegisterer) Register(c Collector) error { - return r.wrappedRegisterer.Register(&wrappingCollector{ - wrappedCollector: c, - prefix: r.prefix, - labels: r.labels, - }) -} - -func (r *wrappingRegisterer) MustRegister(cs ...Collector) { - for _, c := range cs { - if err := r.Register(c); err != nil { - panic(err) - } - } -} - -func (r *wrappingRegisterer) Unregister(c Collector) bool { - return r.wrappedRegisterer.Unregister(&wrappingCollector{ - wrappedCollector: c, - prefix: r.prefix, - labels: r.labels, - }) -} - -type wrappingCollector struct { - wrappedCollector Collector - prefix string - labels Labels -} - -func (c *wrappingCollector) Collect(ch chan<- Metric) { - wrappedCh := make(chan Metric) - go func() { - c.wrappedCollector.Collect(wrappedCh) - close(wrappedCh) - }() - for m := range wrappedCh { - ch <- &wrappingMetric{ - wrappedMetric: m, - prefix: c.prefix, - labels: c.labels, - } - } -} - -func (c *wrappingCollector) Describe(ch chan<- *Desc) { - wrappedCh := make(chan *Desc) - go func() { - c.wrappedCollector.Describe(wrappedCh) - close(wrappedCh) - }() - for desc := range wrappedCh { - ch <- wrapDesc(desc, c.prefix, c.labels) - } -} - -type wrappingMetric struct { - wrappedMetric Metric - prefix string - labels Labels -} - -func (m *wrappingMetric) Desc() *Desc { - return wrapDesc(m.wrappedMetric.Desc(), m.prefix, m.labels) -} - -func (m *wrappingMetric) Write(out *dto.Metric) error { - if err := m.wrappedMetric.Write(out); err != nil { - return err - } - if len(m.labels) == 0 { - // No wrapping labels. - return nil - } - for ln, lv := range m.labels { - out.Label = append(out.Label, &dto.LabelPair{ - Name: proto.String(ln), - Value: proto.String(lv), - }) - } - sort.Sort(labelPairSorter(out.Label)) - return nil -} - -func wrapDesc(desc *Desc, prefix string, labels Labels) *Desc { - constLabels := Labels{} - for _, lp := range desc.constLabelPairs { - constLabels[*lp.Name] = *lp.Value - } - for ln, lv := range labels { - if _, alreadyUsed := constLabels[ln]; alreadyUsed { - return &Desc{ - fqName: desc.fqName, - help: desc.help, - variableLabels: desc.variableLabels, - constLabelPairs: desc.constLabelPairs, - err: fmt.Errorf("attempted wrapping with already existing label name %q", ln), - } - } - constLabels[ln] = lv - } - // NewDesc will do remaining validations. - newDesc := NewDesc(prefix+desc.fqName, desc.help, desc.variableLabels, constLabels) - // Propagate errors if there was any. This will override any errer - // created by NewDesc above, i.e. earlier errors get precedence. - if desc.err != nil { - newDesc.err = desc.err - } - return newDesc -} diff --git a/vendor/github.com/ugorji/go/codec/gen-helper.generated.go b/vendor/github.com/ugorji/go/codec/gen-helper.generated.go index 96a6b61d9..eb0bdad35 100644 --- a/vendor/github.com/ugorji/go/codec/gen-helper.generated.go +++ b/vendor/github.com/ugorji/go/codec/gen-helper.generated.go @@ -1,4 +1,4 @@ -// // +build ignore +/* // +build ignore */ // Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved. // Use of this source code is governed by a MIT license found in the LICENSE file. diff --git a/vendor/github.com/ugorji/go/codec/gen-helper.go.tmpl b/vendor/github.com/ugorji/go/codec/gen-helper.go.tmpl index 9b276d420..ad99f6671 100644 --- a/vendor/github.com/ugorji/go/codec/gen-helper.go.tmpl +++ b/vendor/github.com/ugorji/go/codec/gen-helper.go.tmpl @@ -1,4 +1,4 @@ -// // +build ignore +/* // +build ignore */ // Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved. // Use of this source code is governed by a MIT license found in the LICENSE file. diff --git a/vendor/github.com/ugorji/go/codec/gen.go b/vendor/github.com/ugorji/go/codec/gen.go index 1a27675fd..c4944dbff 100644 --- a/vendor/github.com/ugorji/go/codec/gen.go +++ b/vendor/github.com/ugorji/go/codec/gen.go @@ -165,15 +165,9 @@ type genRunner struct { // // Library users: *DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.* func Gen(w io.Writer, buildTags, pkgName, uid string, useUnsafe bool, ti *TypeInfos, typ ...reflect.Type) { - // trim out all types which already implement Selfer - typ2 := make([]reflect.Type, 0, len(typ)) - for _, t := range typ { - if reflect.PtrTo(t).Implements(selferTyp) || t.Implements(selferTyp) { - continue - } - typ2 = append(typ2, t) - } - typ = typ2 + // All types passed to this method do not have a codec.Selfer method implemented directly. + // codecgen already checks the AST and skips any types that define the codec.Selfer methods. + // Consequently, there's no need to check and trim them if they implement codec.Selfer if len(typ) == 0 { return diff --git a/vendor/github.com/ugorji/go/codec/simple.go b/vendor/github.com/ugorji/go/codec/simple.go index 231d503a2..d07208c87 100644 --- a/vendor/github.com/ugorji/go/codec/simple.go +++ b/vendor/github.com/ugorji/go/codec/simple.go @@ -347,7 +347,7 @@ func (d *simpleDecDriver) decLen() int { } return int(ui) } - d.d.errorf("decLen: Cannot read length: bd%8 must be in range 0..4. Got: %d", d.bd%8) + d.d.errorf("decLen: Cannot read length: bd%%8 must be in range 0..4. Got: %d", d.bd%8) return -1 } diff --git a/vendor/github.com/ugorji/go/codec/tests.sh b/vendor/github.com/ugorji/go/codec/tests.sh index 00857b620..342f336df 100755 --- a/vendor/github.com/ugorji/go/codec/tests.sh +++ b/vendor/github.com/ugorji/go/codec/tests.sh @@ -59,7 +59,7 @@ _run() { } # echo ">>>>>>> RUNNING VARIATIONS OF TESTS" -if [[ "x$@" = "x" ]]; then +if [[ "x$@" = "x" || "x$@" = "x-A" ]]; then # All: r, x, g, gu _run "-_tcinsed_ml" # regular _run "-_tcinsed_ml_z" # regular with reset @@ -75,6 +75,28 @@ elif [[ "x$@" = "x-F" ]]; then # regular with notfastpath _run "-_tcinsed_ml_f" # regular _run "-_tcinsed_ml_zf" # regular with reset +elif [[ "x$@" = "x-C" ]]; then + # codecgen + _run "-gx_tcinsed_ml" # codecgen: requires external + _run "-gxu_tcinsed_ml" # codecgen + unsafe +elif [[ "x$@" = "x-X" ]]; then + # external + _run "-x_tcinsed_ml" # external +elif [[ "x$@" = "x-h" || "x$@" = "x-?" ]]; then + cat < Date: Thu, 30 May 2019 09:17:16 -0700 Subject: [PATCH 2/2] Revert "Merge pull request #300 from woopstar/upgrade-coredns" This reverts commit 24b1708afb24325e42b2f40130f1eff4dbaf0c3d, reversing changes made to d8f203cd26ce6820b697559dbfc0a6cacde90d67. --- Gopkg.lock | 4 +- Gopkg.toml | 2 +- .../coredns/coredns/.github/CONTRIBUTING.md | 1 - .../coredns/coredns/core/dnsserver/address.go | 11 +- .../coredns/coredns/core/dnsserver/config.go | 2 +- .../coredns/core/dnsserver/listen_go111.go | 1 - .../coredns/coredns/core/dnsserver/server.go | 128 +++--- .../coredns/core/dnsserver/server_grpc.go | 15 +- .../coredns/core/dnsserver/server_https.go | 8 - .../coredns/coredns/core/dnsserver/watch.go | 18 + .../coredns/core/dnsserver/zdirectives.go | 5 +- .../coredns/coredns/coremain/run.go | 19 +- .../coredns/coredns/coremain/version.go | 2 +- vendor/github.com/coredns/coredns/pb/Makefile | 8 +- .../github.com/coredns/coredns/pb/dns.pb.go | 379 +++++++++++++++--- .../github.com/coredns/coredns/pb/dns.proto | 37 ++ .../coredns/coredns/plugin/backend.go | 8 +- .../coredns/coredns/plugin/backend_lookup.go | 115 +++--- .../coredns/coredns/plugin/cache/README.md | 7 +- .../coredns/coredns/plugin/cache/cache.go | 2 +- .../github.com/coredns/coredns/plugin/done.go | 14 - .../coredns/coredns/plugin/errors/README.md | 2 +- .../coredns/coredns/plugin/errors/errors.go | 17 +- .../coredns/coredns/plugin/etcd/msg/path.go | 2 +- .../coredns/plugin/etcd/msg/service.go | 21 +- .../coredns/coredns/plugin/forward/README.md | 23 +- .../coredns/coredns/plugin/forward/connect.go | 33 +- .../coredns/coredns/plugin/forward/dnstap.go | 61 --- .../coredns/coredns/plugin/forward/forward.go | 15 +- .../coredns/coredns/plugin/forward/lookup.go | 89 ++++ .../coredns/plugin/forward/persistent.go | 12 +- .../coredns/coredns/plugin/forward/proxy.go | 4 +- .../coredns/plugin/forward/truncated.go | 29 ++ .../coredns/coredns/plugin/health/README.md | 39 +- .../coredns/coredns/plugin/health/health.go | 28 +- .../coredns/coredns/plugin/health/healther.go | 36 ++ .../coredns/coredns/plugin/health/setup.go | 27 ++ .../coredns/coredns/plugin/log/README.md | 28 +- .../coredns/coredns/plugin/log/log.go | 40 +- .../coredns/coredns/plugin/log/setup.go | 66 ++- .../coredns/coredns/plugin/loop/README.md | 56 ++- .../coredns/coredns/plugin/loop/loop.go | 15 +- .../coredns/coredns/plugin/loop/setup.go | 1 - .../coredns/coredns/plugin/metrics/README.md | 8 +- .../coredns/coredns/plugin/metrics/context.go | 10 +- .../coredns/coredns/plugin/metrics/handler.go | 2 +- .../coredns/coredns/plugin/metrics/metrics.go | 12 +- .../coredns/coredns/plugin/metrics/setup.go | 18 - .../coredns/plugin/metrics/vars/report.go | 55 ++- .../coredns/plugin/metrics/vars/vars.go | 6 - .../coredns/coredns/plugin/normalize.go | 4 +- .../coredns/coredns/plugin/pkg/edns/edns.go | 28 -- .../coredns/coredns/plugin/pkg/log/log.go | 2 +- .../coredns/coredns/plugin/pkg/log/plugin.go | 11 +- .../coredns/plugin/pkg/replacer/replacer.go | 186 ++++----- .../coredns/coredns/plugin/pkg/up/up.go | 24 +- .../coredns/coredns/plugin/pkg/watch/watch.go | 23 ++ .../coredns/plugin/pkg/watch/watcher.go | 178 ++++++++ .../coredns/coredns/plugin/plugin.go | 5 +- .../coredns/coredns/plugin/reload/README.md | 7 - .../coredns/coredns/plugin/reload/reload.go | 44 +- .../coredns/coredns/plugin/reload/setup.go | 11 +- .../coredns/coredns/plugin/test/helpers.go | 125 +++--- .../coredns/coredns/plugin/test/scrape.go | 266 ------------ .../coredns/coredns/request/edns0.go | 31 -- .../coredns/coredns/request/request.go | 55 ++- .../coredns/coredns/request/writer.go | 4 +- 67 files changed, 1410 insertions(+), 1135 deletions(-) delete mode 120000 vendor/github.com/coredns/coredns/.github/CONTRIBUTING.md create mode 100644 vendor/github.com/coredns/coredns/core/dnsserver/watch.go delete mode 100644 vendor/github.com/coredns/coredns/plugin/done.go delete mode 100644 vendor/github.com/coredns/coredns/plugin/forward/dnstap.go create mode 100644 vendor/github.com/coredns/coredns/plugin/forward/lookup.go create mode 100644 vendor/github.com/coredns/coredns/plugin/forward/truncated.go create mode 100644 vendor/github.com/coredns/coredns/plugin/health/healther.go create mode 100644 vendor/github.com/coredns/coredns/plugin/pkg/watch/watch.go create mode 100644 vendor/github.com/coredns/coredns/plugin/pkg/watch/watcher.go delete mode 100644 vendor/github.com/coredns/coredns/plugin/test/scrape.go delete mode 100644 vendor/github.com/coredns/coredns/request/edns0.go diff --git a/Gopkg.lock b/Gopkg.lock index 35d25b6e2..c0874e295 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -76,8 +76,8 @@ "request", ] pruneopts = "UT" - revision = "e3f9a80b1d5a112907a012acee276e9080b32852" - version = "v1.5.0" + revision = "756749c5cad4cd40453b82f70592bc19bb9ccb15" + version = "v1.2.6" [[projects]] digest = "1:d2bf3686993c6674049180c5b3c608ceeae8fe99df6b9675594306fba9373b77" diff --git a/Gopkg.toml b/Gopkg.toml index 767783241..c8961f697 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -79,7 +79,7 @@ [[constraint]] name = "github.com/coredns/coredns" - version = "v1.5.0" + version = "v1.2.6" [[override]] name = "github.com/ugorji/go" diff --git a/vendor/github.com/coredns/coredns/.github/CONTRIBUTING.md b/vendor/github.com/coredns/coredns/.github/CONTRIBUTING.md deleted file mode 120000 index 44fcc6343..000000000 --- a/vendor/github.com/coredns/coredns/.github/CONTRIBUTING.md +++ /dev/null @@ -1 +0,0 @@ -../CONTRIBUTING.md \ No newline at end of file diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/address.go b/vendor/github.com/coredns/coredns/core/dnsserver/address.go index 1a69c33b8..5d3d2b393 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/address.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/address.go @@ -20,7 +20,7 @@ type zoneAddr struct { Address string // used for bound zoneAddr - validation of overlapping } -// String returns the string representation of z. +// String return the string representation of z. func (z zoneAddr) String() string { s := z.Transport + "://" + z.Zone + ":" + z.Port if z.Address != "" { @@ -29,10 +29,13 @@ func (z zoneAddr) String() string { return s } -// normalizeZone parses a zone string into a structured format with separate +// normalizeZone parses an zone string into a structured format with separate // host, and port portions, as well as the original input string. func normalizeZone(str string) (zoneAddr, error) { - trans, str := parse.Transport(str) + var err error + + var trans string + trans, str = parse.Transport(str) host, port, ipnet, err := plugin.SplitHostPort(str) if err != nil { @@ -72,7 +75,7 @@ func SplitProtocolHostPort(address string) (protocol string, ip string, port str type zoneOverlap struct { registeredAddr map[zoneAddr]zoneAddr // each zoneAddr is registered once by its key - unboundOverlap map[zoneAddr]zoneAddr // the "no bind" equiv ZoneAddr is registered by its original key + unboundOverlap map[zoneAddr]zoneAddr // the "no bind" equiv ZoneAdddr is registered by its original key } func newOverlapZone() *zoneOverlap { diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/config.go b/vendor/github.com/coredns/coredns/core/dnsserver/config.go index 811d39426..a63058d43 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/config.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/config.go @@ -21,7 +21,7 @@ type Config struct { // The port to listen on. Port string - // Root points to a base directory we find user defined "things". + // Root points to a base directory we we find user defined "things". // First consumer is the file plugin to looks for zone files in this place. Root string diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/listen_go111.go b/vendor/github.com/coredns/coredns/core/dnsserver/listen_go111.go index 573988b33..d71c9292b 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/listen_go111.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/listen_go111.go @@ -9,7 +9,6 @@ import ( "syscall" "github.com/coredns/coredns/plugin/pkg/log" - "golang.org/x/sys/unix" ) diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/server.go b/vendor/github.com/coredns/coredns/core/dnsserver/server.go index c55f4db5f..5ca826b0a 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/server.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/server.go @@ -33,12 +33,12 @@ type Server struct { server [2]*dns.Server // 0 is a net.Listener, 1 is a net.PacketConn (a *UDPConn) in our case. m sync.Mutex // protects the servers - zones map[string]*Config // zones keyed by their address - dnsWg sync.WaitGroup // used to wait on outstanding connections - graceTimeout time.Duration // the maximum duration of a graceful shutdown - trace trace.Trace // the trace plugin for the server - debug bool // disable recover() - classChaos bool // allow non-INET class queries + zones map[string]*Config // zones keyed by their address + dnsWg sync.WaitGroup // used to wait on outstanding connections + connTimeout time.Duration // the maximum duration of a graceful shutdown + trace trace.Trace // the trace plugin for the server + debug bool // disable recover() + classChaos bool // allow non-INET class queries } // NewServer returns a new CoreDNS server and compiles all plugins in to it. By default CH class @@ -46,9 +46,9 @@ type Server struct { func NewServer(addr string, group []*Config) (*Server, error) { s := &Server{ - Addr: addr, - zones: make(map[string]*Config), - graceTimeout: 5 * time.Second, + Addr: addr, + zones: make(map[string]*Config), + connTimeout: 5 * time.Second, // TODO(miek): was configurable } // We have to bound our wg with one increment @@ -66,8 +66,22 @@ func NewServer(addr string, group []*Config) (*Server, error) { } // set the config per zone s.zones[site.Zone] = site - // compile custom plugin for everything + if site.registry != nil { + // this config is already computed with the chain of plugin + // set classChaos in accordance with previously registered plugins + for name := range enableChaos { + if _, ok := site.registry[name]; ok { + s.classChaos = true + break + } + } + // set trace handler in accordance with previously registered "trace" plugin + if handler, ok := site.registry["trace"]; ok { + s.trace = handler.(trace.Trace) + } + continue + } var stack plugin.Handler for i := len(site.Plugin) - 1; i >= 0; i-- { stack = site.Plugin[i](stack) @@ -83,7 +97,7 @@ func NewServer(addr string, group []*Config) (*Server, error) { } } // Unblock CH class queries when any of these plugins are loaded. - if _, ok := EnableChaos[stack.Name()]; ok { + if _, ok := enableChaos[stack.Name()]; ok { s.classChaos = true } } @@ -128,11 +142,6 @@ func (s *Server) Listen() (net.Listener, error) { return l, nil } -// WrapListener Listen implements caddy.GracefulServer interface. -func (s *Server) WrapListener(ln net.Listener) net.Listener { - return ln -} - // ListenPacket implements caddy.UDPServer interface. func (s *Server) ListenPacket() (net.PacketConn, error) { p, err := listenPacket("udp", s.Addr[len(transport.DNS+"://"):]) @@ -163,7 +172,7 @@ func (s *Server) Stop() (err error) { // Wait for remaining connections to finish or // force them all to close after timeout select { - case <-time.After(s.graceTimeout): + case <-time.After(s.connTimeout): case <-done: } } @@ -191,7 +200,7 @@ func (s *Server) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) // The default dns.Mux checks the question section size, but we have our // own mux here. Check if we have a question section. If not drop them here. if r == nil || len(r.Question) == 0 { - errorAndMetricsFunc(s.Addr, w, r, dns.RcodeServerFailure) + DefaultErrorFunc(ctx, w, r, dns.RcodeServerFailure) return } @@ -201,13 +210,13 @@ func (s *Server) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) // need to make sure that we stay alive up here if rec := recover(); rec != nil { vars.Panic.Inc() - errorAndMetricsFunc(s.Addr, w, r, dns.RcodeServerFailure) + DefaultErrorFunc(ctx, w, r, dns.RcodeServerFailure) } }() } if !s.classChaos && r.Question[0].Qclass != dns.ClassINET { - errorAndMetricsFunc(s.Addr, w, r, dns.RcodeRefused) + DefaultErrorFunc(ctx, w, r, dns.RcodeRefused) return } @@ -216,6 +225,12 @@ func (s *Server) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) return } + ctx, err := incrementDepthAndCheck(ctx) + if err != nil { + DefaultErrorFunc(ctx, w, r, dns.RcodeServerFailure) + return + } + q := r.Question[0].Name b := make([]byte, len(q)) var off int @@ -237,11 +252,16 @@ func (s *Server) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) } if h, ok := s.zones[string(b[:l])]; ok { + + // Set server's address in the context so plugins can reference back to this, + // This will makes those metrics unique. + ctx = context.WithValue(ctx, plugin.ServerCtx{}, s.Addr) + if r.Question[0].Qtype != dns.TypeDS { if h.FilterFunc == nil { rcode, _ := h.pluginChain.ServeDNS(ctx, w, r) if !plugin.ClientWrite(rcode) { - errorFunc(s.Addr, w, r, rcode) + DefaultErrorFunc(ctx, w, r, rcode) } return } @@ -250,7 +270,7 @@ func (s *Server) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) if h.FilterFunc(q) { rcode, _ := h.pluginChain.ServeDNS(ctx, w, r) if !plugin.ClientWrite(rcode) { - errorFunc(s.Addr, w, r, rcode) + DefaultErrorFunc(ctx, w, r, rcode) } return } @@ -272,22 +292,26 @@ func (s *Server) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) // DS request, and we found a zone, use the handler for the query. rcode, _ := dshandler.pluginChain.ServeDNS(ctx, w, r) if !plugin.ClientWrite(rcode) { - errorFunc(s.Addr, w, r, rcode) + DefaultErrorFunc(ctx, w, r, rcode) } return } // Wildcard match, if we have found nothing try the root zone as a last resort. if h, ok := s.zones["."]; ok && h.pluginChain != nil { + + // See comment above. + ctx = context.WithValue(ctx, plugin.ServerCtx{}, s.Addr) + rcode, _ := h.pluginChain.ServeDNS(ctx, w, r) if !plugin.ClientWrite(rcode) { - errorFunc(s.Addr, w, r, rcode) + DefaultErrorFunc(ctx, w, r, rcode) } return } // Still here? Error out with REFUSED. - errorAndMetricsFunc(s.Addr, w, r, dns.RcodeRefused) + DefaultErrorFunc(ctx, w, r, dns.RcodeRefused) } // OnStartupComplete lists the sites served by this server @@ -313,42 +337,56 @@ func (s *Server) Tracer() ot.Tracer { return s.trace.Tracer() } -// errorFunc responds to an DNS request with an error. -func errorFunc(server string, w dns.ResponseWriter, r *dns.Msg, rc int) { +// DefaultErrorFunc responds to an DNS request with an error. +func DefaultErrorFunc(ctx context.Context, w dns.ResponseWriter, r *dns.Msg, rc int) { state := request.Request{W: w, Req: r} answer := new(dns.Msg) answer.SetRcode(r, rc) + state.SizeAndDo(answer) + vars.Report(ctx, state, vars.Dropped, rcode.ToString(rc), answer.Len(), time.Now()) + w.WriteMsg(answer) } -func errorAndMetricsFunc(server string, w dns.ResponseWriter, r *dns.Msg, rc int) { - state := request.Request{W: w, Req: r} - - answer := new(dns.Msg) - answer.SetRcode(r, rc) - state.SizeAndDo(answer) - - vars.Report(server, state, vars.Dropped, rcode.ToString(rc), answer.Len(), time.Now()) +// incrementDepthAndCheck increments the loop counter in the context, and returns an error if +// the counter exceeds the max number of re-entries +func incrementDepthAndCheck(ctx context.Context) (context.Context, error) { + // Loop counter for self directed lookups + loop := ctx.Value(loopKey{}) + if loop == nil { + ctx = context.WithValue(ctx, loopKey{}, 0) + return ctx, nil + } - w.WriteMsg(answer) + iloop := loop.(int) + 1 + if iloop > maxreentries { + return ctx, fmt.Errorf("too deep") + } + ctx = context.WithValue(ctx, loopKey{}, iloop) + return ctx, nil } const ( - tcp = 0 - udp = 1 + tcp = 0 + udp = 1 + maxreentries = 10 ) -// Key is the context key for the current server added to the context. -type Key struct{} +type ( + // Key is the context key for the current server + Key struct{} + loopKey struct{} // loopKey is the context key for counting self loops +) -// EnableChaos is a map with plugin names for which we should open CH class queries as we block these by default. -var EnableChaos = map[string]struct{}{ - "chaos": struct{}{}, - "forward": struct{}{}, - "proxy": struct{}{}, +// enableChaos is a map with plugin names for which we should open CH class queries as +// we block these by default. +var enableChaos = map[string]bool{ + "chaos": true, + "forward": true, + "proxy": true, } // Quiet mode will not show any informative output on initialization. diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/server_grpc.go b/vendor/github.com/coredns/coredns/core/dnsserver/server_grpc.go index e26eaebfb..7de36a5fd 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/server_grpc.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/server_grpc.go @@ -9,6 +9,7 @@ import ( "github.com/coredns/coredns/pb" "github.com/coredns/coredns/plugin/pkg/transport" + "github.com/coredns/coredns/plugin/pkg/watch" "github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc" "github.com/miekg/dns" @@ -23,6 +24,7 @@ type ServergRPC struct { grpcServer *grpc.Server listenAddr net.Addr tlsConfig *tls.Config + watch watch.Watcher } // NewServergRPC returns a new CoreDNS GRPC server and compiles all plugin in to it. @@ -39,7 +41,7 @@ func NewServergRPC(addr string, group []*Config) (*ServergRPC, error) { tlsConfig = conf.TLSConfig } - return &ServergRPC{Server: s, tlsConfig: tlsConfig}, nil + return &ServergRPC{Server: s, tlsConfig: tlsConfig, watch: watch.NewWatcher(watchables(s.zones))}, nil } // Serve implements caddy.TCPServer interface. @@ -101,6 +103,9 @@ func (s *ServergRPC) OnStartupComplete() { func (s *ServergRPC) Stop() (err error) { s.m.Lock() defer s.m.Unlock() + if s.watch != nil { + s.watch.Stop() + } if s.grpcServer != nil { s.grpcServer.GracefulStop() } @@ -139,6 +144,12 @@ func (s *ServergRPC) Query(ctx context.Context, in *pb.DnsPacket) (*pb.DnsPacket return &pb.DnsPacket{Msg: packed}, nil } +// Watch is the entrypoint called by the gRPC layer when the user asks +// to watch a query. +func (s *ServergRPC) Watch(stream pb.DnsService_WatchServer) error { + return s.watch.Watch(stream) +} + // Shutdown stops the server (non gracefully). func (s *ServergRPC) Shutdown() error { if s.grpcServer != nil { @@ -154,7 +165,7 @@ type gRPCresponse struct { } // Write is the hack that makes this work. It does not actually write the message -// but returns the bytes we need to write in r. We can then pick this up in Query +// but returns the bytes we need to to write in r. We can then pick this up in Query // and write a proper protobuf back to the client. func (r *gRPCresponse) Write(b []byte) (int, error) { r.Msg = new(dns.Msg) diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/server_https.go b/vendor/github.com/coredns/coredns/core/dnsserver/server_https.go index 6ae6ce6a6..1e184e044 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/server_https.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/server_https.go @@ -119,14 +119,6 @@ func (s *ServerHTTPS) ServeHTTP(w http.ResponseWriter, r *http.Request) { // We should expect a packet to be returned that we can send to the client. s.ServeDNS(context.Background(), dw, msg) - // See section 4.2.1 of RFC 8484. - // We are using code 500 to indicate an unexpected situation when the chain - // handler has not provided any response message. - if dw.Msg == nil { - http.Error(w, "No response", http.StatusInternalServerError) - return - } - buf, _ := dw.Msg.Pack() mt, _ := response.Typify(dw.Msg, time.Now().UTC()) diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/watch.go b/vendor/github.com/coredns/coredns/core/dnsserver/watch.go new file mode 100644 index 000000000..590bac144 --- /dev/null +++ b/vendor/github.com/coredns/coredns/core/dnsserver/watch.go @@ -0,0 +1,18 @@ +package dnsserver + +import ( + "github.com/coredns/coredns/plugin/pkg/watch" +) + +func watchables(zones map[string]*Config) []watch.Watchable { + var w []watch.Watchable + for _, config := range zones { + plugins := config.Handlers() + for _, p := range plugins { + if x, ok := p.(watch.Watchable); ok { + w = append(w, x) + } + } + } + return w +} diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/zdirectives.go b/vendor/github.com/coredns/coredns/core/dnsserver/zdirectives.go index 0420672f0..280c03144 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/zdirectives.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/zdirectives.go @@ -11,7 +11,6 @@ package dnsserver // care what plugin above them are doing. var Directives = []string{ "metadata", - "cancel", "tls", "reload", "nsid", @@ -19,7 +18,6 @@ var Directives = []string{ "bind", "debug", "trace", - "ready", "health", "pprof", "prometheus", @@ -36,7 +34,6 @@ var Directives = []string{ "hosts", "route53", "federation", - "k8s_external", "kubernetes", "file", "auto", @@ -44,7 +41,7 @@ var Directives = []string{ "etcd", "loop", "forward", - "grpc", + "proxy", "erratic", "whoami", "on", diff --git a/vendor/github.com/coredns/coredns/coremain/run.go b/vendor/github.com/coredns/coredns/coremain/run.go index 1c7e475ee..63ac6f354 100644 --- a/vendor/github.com/coredns/coredns/coremain/run.go +++ b/vendor/github.com/coredns/coredns/coremain/run.go @@ -95,6 +95,9 @@ func Run() { showVersion() } + // Execute instantiation events + caddy.EmitEvent(caddy.InstanceStartupEvent, instance) + // Twiddle your thumbs instance.Wait() } @@ -254,14 +257,14 @@ var ( ) // flagsBlacklist removes flags with these names from our flagset. -var flagsBlacklist = map[string]struct{}{ - "logtostderr": struct{}{}, - "alsologtostderr": struct{}{}, - "v": struct{}{}, - "stderrthreshold": struct{}{}, - "vmodule": struct{}{}, - "log_backtrace_at": struct{}{}, - "log_dir": struct{}{}, +var flagsBlacklist = map[string]bool{ + "logtostderr": true, + "alsologtostderr": true, + "v": true, + "stderrthreshold": true, + "vmodule": true, + "log_backtrace_at": true, + "log_dir": true, } var flagsToKeep []*flag.Flag diff --git a/vendor/github.com/coredns/coredns/coremain/version.go b/vendor/github.com/coredns/coredns/coremain/version.go index d0ac1dae0..ae274821d 100644 --- a/vendor/github.com/coredns/coredns/coremain/version.go +++ b/vendor/github.com/coredns/coredns/coremain/version.go @@ -2,7 +2,7 @@ package coremain // Various CoreDNS constants. const ( - CoreVersion = "1.5.0" + CoreVersion = "1.2.6" coreName = "CoreDNS" serverType = "dns" ) diff --git a/vendor/github.com/coredns/coredns/pb/Makefile b/vendor/github.com/coredns/coredns/pb/Makefile index a666d6c15..bdfc8e3b0 100644 --- a/vendor/github.com/coredns/coredns/pb/Makefile +++ b/vendor/github.com/coredns/coredns/pb/Makefile @@ -6,8 +6,6 @@ all: dns.pb.go dns.pb.go: dns.proto - protoc --go_out=plugins=grpc:. dns.proto - -.PHONY: clean -clean: - rm dns.pb.go + protoc --go_out=plugins=grpc:. dns.proto && \ + sed -e s?golang.org/x/net/context?context? < dns.pb.go > dns.pb.go.tmp && \ + mv dns.pb.go.tmp dns.pb.go diff --git a/vendor/github.com/coredns/coredns/pb/dns.pb.go b/vendor/github.com/coredns/coredns/pb/dns.pb.go index 919f0e7d7..d79e24f6d 100644 --- a/vendor/github.com/coredns/coredns/pb/dns.pb.go +++ b/vendor/github.com/coredns/coredns/pb/dns.pb.go @@ -1,14 +1,27 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // source: dns.proto +/* +Package pb is a generated protocol buffer package. + +It is generated from these files: + dns.proto + +It has these top-level messages: + DnsPacket + WatchRequest + WatchCreateRequest + WatchCancelRequest + WatchResponse +*/ package pb +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + import ( context "context" - fmt "fmt" - math "math" - - proto "github.com/golang/protobuf/proto" grpc "google.golang.org/grpc" ) @@ -17,70 +30,245 @@ var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf -/* Miek: disabled this manually, because I don't know what the heck */ -/* // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package -*/ +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package type DnsPacket struct { - Msg []byte `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Msg []byte `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"` } -func (m *DnsPacket) Reset() { *m = DnsPacket{} } -func (m *DnsPacket) String() string { return proto.CompactTextString(m) } -func (*DnsPacket) ProtoMessage() {} -func (*DnsPacket) Descriptor() ([]byte, []int) { - return fileDescriptor_638ff8d8aaf3d8ae, []int{0} +func (m *DnsPacket) Reset() { *m = DnsPacket{} } +func (m *DnsPacket) String() string { return proto.CompactTextString(m) } +func (*DnsPacket) ProtoMessage() {} +func (*DnsPacket) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *DnsPacket) GetMsg() []byte { + if m != nil { + return m.Msg + } + return nil } -func (m *DnsPacket) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_DnsPacket.Unmarshal(m, b) +type WatchRequest struct { + // request_union is a request to either create a new watcher or cancel an existing watcher. + // + // Types that are valid to be assigned to RequestUnion: + // *WatchRequest_CreateRequest + // *WatchRequest_CancelRequest + RequestUnion isWatchRequest_RequestUnion `protobuf_oneof:"request_union"` } -func (m *DnsPacket) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_DnsPacket.Marshal(b, m, deterministic) + +func (m *WatchRequest) Reset() { *m = WatchRequest{} } +func (m *WatchRequest) String() string { return proto.CompactTextString(m) } +func (*WatchRequest) ProtoMessage() {} +func (*WatchRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +type isWatchRequest_RequestUnion interface { + isWatchRequest_RequestUnion() } -func (m *DnsPacket) XXX_Merge(src proto.Message) { - xxx_messageInfo_DnsPacket.Merge(m, src) + +type WatchRequest_CreateRequest struct { + CreateRequest *WatchCreateRequest `protobuf:"bytes,1,opt,name=create_request,json=createRequest,oneof"` } -func (m *DnsPacket) XXX_Size() int { - return xxx_messageInfo_DnsPacket.Size(m) +type WatchRequest_CancelRequest struct { + CancelRequest *WatchCancelRequest `protobuf:"bytes,2,opt,name=cancel_request,json=cancelRequest,oneof"` } -func (m *DnsPacket) XXX_DiscardUnknown() { - xxx_messageInfo_DnsPacket.DiscardUnknown(m) + +func (*WatchRequest_CreateRequest) isWatchRequest_RequestUnion() {} +func (*WatchRequest_CancelRequest) isWatchRequest_RequestUnion() {} + +func (m *WatchRequest) GetRequestUnion() isWatchRequest_RequestUnion { + if m != nil { + return m.RequestUnion + } + return nil } -var xxx_messageInfo_DnsPacket proto.InternalMessageInfo +func (m *WatchRequest) GetCreateRequest() *WatchCreateRequest { + if x, ok := m.GetRequestUnion().(*WatchRequest_CreateRequest); ok { + return x.CreateRequest + } + return nil +} -func (m *DnsPacket) GetMsg() []byte { +func (m *WatchRequest) GetCancelRequest() *WatchCancelRequest { + if x, ok := m.GetRequestUnion().(*WatchRequest_CancelRequest); ok { + return x.CancelRequest + } + return nil +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*WatchRequest) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _WatchRequest_OneofMarshaler, _WatchRequest_OneofUnmarshaler, _WatchRequest_OneofSizer, []interface{}{ + (*WatchRequest_CreateRequest)(nil), + (*WatchRequest_CancelRequest)(nil), + } +} + +func _WatchRequest_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*WatchRequest) + // request_union + switch x := m.RequestUnion.(type) { + case *WatchRequest_CreateRequest: + b.EncodeVarint(1<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.CreateRequest); err != nil { + return err + } + case *WatchRequest_CancelRequest: + b.EncodeVarint(2<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.CancelRequest); err != nil { + return err + } + case nil: + default: + return fmt.Errorf("WatchRequest.RequestUnion has unexpected type %T", x) + } + return nil +} + +func _WatchRequest_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*WatchRequest) + switch tag { + case 1: // request_union.create_request + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(WatchCreateRequest) + err := b.DecodeMessage(msg) + m.RequestUnion = &WatchRequest_CreateRequest{msg} + return true, err + case 2: // request_union.cancel_request + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(WatchCancelRequest) + err := b.DecodeMessage(msg) + m.RequestUnion = &WatchRequest_CancelRequest{msg} + return true, err + default: + return false, nil + } +} + +func _WatchRequest_OneofSizer(msg proto.Message) (n int) { + m := msg.(*WatchRequest) + // request_union + switch x := m.RequestUnion.(type) { + case *WatchRequest_CreateRequest: + s := proto.Size(x.CreateRequest) + n += proto.SizeVarint(1<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *WatchRequest_CancelRequest: + s := proto.Size(x.CancelRequest) + n += proto.SizeVarint(2<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +type WatchCreateRequest struct { + Query *DnsPacket `protobuf:"bytes,1,opt,name=query" json:"query,omitempty"` +} + +func (m *WatchCreateRequest) Reset() { *m = WatchCreateRequest{} } +func (m *WatchCreateRequest) String() string { return proto.CompactTextString(m) } +func (*WatchCreateRequest) ProtoMessage() {} +func (*WatchCreateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *WatchCreateRequest) GetQuery() *DnsPacket { if m != nil { - return m.Msg + return m.Query } return nil } -func init() { - proto.RegisterType((*DnsPacket)(nil), "coredns.dns.DnsPacket") +type WatchCancelRequest struct { + // watch_id is the watcher id to cancel + WatchId int64 `protobuf:"varint,1,opt,name=watch_id,json=watchId" json:"watch_id,omitempty"` } -func init() { proto.RegisterFile("dns.proto", fileDescriptor_638ff8d8aaf3d8ae) } +func (m *WatchCancelRequest) Reset() { *m = WatchCancelRequest{} } +func (m *WatchCancelRequest) String() string { return proto.CompactTextString(m) } +func (*WatchCancelRequest) ProtoMessage() {} +func (*WatchCancelRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *WatchCancelRequest) GetWatchId() int64 { + if m != nil { + return m.WatchId + } + return 0 +} -var fileDescriptor_638ff8d8aaf3d8ae = []byte{ - // 120 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4c, 0xc9, 0x2b, 0xd6, - 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x4e, 0xce, 0x2f, 0x4a, 0x05, 0x71, 0x53, 0xf2, 0x8a, - 0x95, 0x64, 0xb9, 0x38, 0x5d, 0xf2, 0x8a, 0x03, 0x12, 0x93, 0xb3, 0x53, 0x4b, 0x84, 0x04, 0xb8, - 0x98, 0x73, 0x8b, 0xd3, 0x25, 0x18, 0x15, 0x18, 0x35, 0x78, 0x82, 0x40, 0x4c, 0x23, 0x57, 0x2e, - 0x2e, 0x97, 0xbc, 0xe2, 0xe0, 0xd4, 0xa2, 0xb2, 0xcc, 0xe4, 0x54, 0x21, 0x73, 0x2e, 0xd6, 0xc0, - 0xd2, 0xd4, 0xa2, 0x4a, 0x21, 0x31, 0x3d, 0x24, 0x33, 0xf4, 0xe0, 0x06, 0x48, 0xe1, 0x10, 0x77, - 0x62, 0x89, 0x62, 0x2a, 0x48, 0x4a, 0x62, 0x03, 0xdb, 0x6f, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, - 0xf5, 0xd1, 0x3f, 0x26, 0x8c, 0x00, 0x00, 0x00, +type WatchResponse struct { + // watch_id is the ID of the watcher that corresponds to the response. + WatchId int64 `protobuf:"varint,1,opt,name=watch_id,json=watchId" json:"watch_id,omitempty"` + // created is set to true if the response is for a create watch request. + // The client should record the watch_id and expect to receive DNS replies + // from the same stream. + // All replies sent to the created watcher will attach with the same watch_id. + Created bool `protobuf:"varint,2,opt,name=created" json:"created,omitempty"` + // canceled is set to true if the response is for a cancel watch request. + // No further events will be sent to the canceled watcher. + Canceled bool `protobuf:"varint,3,opt,name=canceled" json:"canceled,omitempty"` + Qname string `protobuf:"bytes,4,opt,name=qname" json:"qname,omitempty"` + Err string `protobuf:"bytes,5,opt,name=err" json:"err,omitempty"` +} + +func (m *WatchResponse) Reset() { *m = WatchResponse{} } +func (m *WatchResponse) String() string { return proto.CompactTextString(m) } +func (*WatchResponse) ProtoMessage() {} +func (*WatchResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *WatchResponse) GetWatchId() int64 { + if m != nil { + return m.WatchId + } + return 0 +} + +func (m *WatchResponse) GetCreated() bool { + if m != nil { + return m.Created + } + return false +} + +func (m *WatchResponse) GetCanceled() bool { + if m != nil { + return m.Canceled + } + return false +} + +func (m *WatchResponse) GetQname() string { + if m != nil { + return m.Qname + } + return "" +} + +func (m *WatchResponse) GetErr() string { + if m != nil { + return m.Err + } + return "" +} + +func init() { + proto.RegisterType((*DnsPacket)(nil), "coredns.dns.DnsPacket") + proto.RegisterType((*WatchRequest)(nil), "coredns.dns.WatchRequest") + proto.RegisterType((*WatchCreateRequest)(nil), "coredns.dns.WatchCreateRequest") + proto.RegisterType((*WatchCancelRequest)(nil), "coredns.dns.WatchCancelRequest") + proto.RegisterType((*WatchResponse)(nil), "coredns.dns.WatchResponse") } // Reference imports to suppress errors if they are not otherwise used. @@ -91,11 +279,11 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// DnsServiceClient is the client API for DnsService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +// Client API for DnsService service + type DnsServiceClient interface { Query(ctx context.Context, in *DnsPacket, opts ...grpc.CallOption) (*DnsPacket, error) + Watch(ctx context.Context, opts ...grpc.CallOption) (DnsService_WatchClient, error) } type dnsServiceClient struct { @@ -108,16 +296,49 @@ func NewDnsServiceClient(cc *grpc.ClientConn) DnsServiceClient { func (c *dnsServiceClient) Query(ctx context.Context, in *DnsPacket, opts ...grpc.CallOption) (*DnsPacket, error) { out := new(DnsPacket) - err := c.cc.Invoke(ctx, "/coredns.dns.DnsService/Query", in, out, opts...) + err := grpc.Invoke(ctx, "/coredns.dns.DnsService/Query", in, out, c.cc, opts...) if err != nil { return nil, err } return out, nil } -// DnsServiceServer is the server API for DnsService service. +func (c *dnsServiceClient) Watch(ctx context.Context, opts ...grpc.CallOption) (DnsService_WatchClient, error) { + stream, err := grpc.NewClientStream(ctx, &_DnsService_serviceDesc.Streams[0], c.cc, "/coredns.dns.DnsService/Watch", opts...) + if err != nil { + return nil, err + } + x := &dnsServiceWatchClient{stream} + return x, nil +} + +type DnsService_WatchClient interface { + Send(*WatchRequest) error + Recv() (*WatchResponse, error) + grpc.ClientStream +} + +type dnsServiceWatchClient struct { + grpc.ClientStream +} + +func (x *dnsServiceWatchClient) Send(m *WatchRequest) error { + return x.ClientStream.SendMsg(m) +} + +func (x *dnsServiceWatchClient) Recv() (*WatchResponse, error) { + m := new(WatchResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// Server API for DnsService service + type DnsServiceServer interface { Query(context.Context, *DnsPacket) (*DnsPacket, error) + Watch(DnsService_WatchServer) error } func RegisterDnsServiceServer(s *grpc.Server, srv DnsServiceServer) { @@ -142,6 +363,32 @@ func _DnsService_Query_Handler(srv interface{}, ctx context.Context, dec func(in return interceptor(ctx, in, info, handler) } +func _DnsService_Watch_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(DnsServiceServer).Watch(&dnsServiceWatchServer{stream}) +} + +type DnsService_WatchServer interface { + Send(*WatchResponse) error + Recv() (*WatchRequest, error) + grpc.ServerStream +} + +type dnsServiceWatchServer struct { + grpc.ServerStream +} + +func (x *dnsServiceWatchServer) Send(m *WatchResponse) error { + return x.ServerStream.SendMsg(m) +} + +func (x *dnsServiceWatchServer) Recv() (*WatchRequest, error) { + m := new(WatchRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + var _DnsService_serviceDesc = grpc.ServiceDesc{ ServiceName: "coredns.dns.DnsService", HandlerType: (*DnsServiceServer)(nil), @@ -151,6 +398,40 @@ var _DnsService_serviceDesc = grpc.ServiceDesc{ Handler: _DnsService_Query_Handler, }, }, - Streams: []grpc.StreamDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "Watch", + Handler: _DnsService_Watch_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, Metadata: "dns.proto", } + +func init() { proto.RegisterFile("dns.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 333 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x52, 0x41, 0x4f, 0x32, 0x31, + 0x14, 0xfc, 0x0a, 0xec, 0x07, 0x3c, 0x40, 0xcd, 0x8b, 0x31, 0xcb, 0x26, 0x46, 0xb2, 0x27, 0x0e, + 0x06, 0x0d, 0x1e, 0xbc, 0xaf, 0x1c, 0xf0, 0xa6, 0xf5, 0x60, 0xe2, 0x85, 0x2c, 0xdd, 0x17, 0x25, + 0x4a, 0x17, 0xda, 0x45, 0xe3, 0x3f, 0xd0, 0xdf, 0xe3, 0x1f, 0x34, 0xfb, 0xba, 0x90, 0x35, 0x88, + 0xb7, 0xce, 0x74, 0x3a, 0xed, 0xcc, 0x2b, 0x34, 0x13, 0x6d, 0x07, 0x0b, 0x93, 0x66, 0x29, 0xb6, + 0x54, 0x6a, 0x28, 0x87, 0x89, 0xb6, 0xe1, 0x31, 0x34, 0x47, 0xda, 0xde, 0xc4, 0xea, 0x99, 0x32, + 0x3c, 0x80, 0xea, 0xdc, 0x3e, 0xfa, 0xa2, 0x27, 0xfa, 0x6d, 0x99, 0x2f, 0xc3, 0x2f, 0x01, 0xed, + 0xfb, 0x38, 0x53, 0x4f, 0x92, 0x96, 0x2b, 0xb2, 0x19, 0x8e, 0x61, 0x4f, 0x19, 0x8a, 0x33, 0x9a, + 0x18, 0xc7, 0xb0, 0xba, 0x35, 0x3c, 0x19, 0x94, 0x5c, 0x07, 0x7c, 0xe4, 0x8a, 0x75, 0xc5, 0xc1, + 0xf1, 0x3f, 0xd9, 0x51, 0x65, 0x82, 0x9d, 0x62, 0xad, 0xe8, 0x65, 0xe3, 0x54, 0xd9, 0xe9, 0xc4, + 0xba, 0xb2, 0x53, 0x99, 0x88, 0xf6, 0xa1, 0x53, 0x58, 0x4c, 0x56, 0x7a, 0x96, 0xea, 0x30, 0x02, + 0xdc, 0x7e, 0x01, 0x9e, 0x82, 0xb7, 0x5c, 0x91, 0x79, 0x2f, 0x5e, 0x7c, 0xf4, 0xe3, 0x9e, 0x4d, + 0x09, 0xd2, 0x89, 0xc2, 0xb3, 0xb5, 0x47, 0xf9, 0x2a, 0xec, 0x42, 0xe3, 0x2d, 0x67, 0x27, 0xb3, + 0x84, 0x6d, 0xaa, 0xb2, 0xce, 0xf8, 0x3a, 0x09, 0x3f, 0x04, 0x74, 0x8a, 0xaa, 0xec, 0x22, 0xd5, + 0x96, 0xfe, 0x10, 0xa3, 0x0f, 0x75, 0xd7, 0x46, 0xc2, 0xa9, 0x1b, 0x72, 0x0d, 0x31, 0x80, 0x86, + 0x4b, 0x47, 0x89, 0x5f, 0xe5, 0xad, 0x0d, 0xc6, 0x43, 0xf0, 0x96, 0x3a, 0x9e, 0x93, 0x5f, 0xeb, + 0x89, 0x7e, 0x53, 0x3a, 0x90, 0x4f, 0x8d, 0x8c, 0xf1, 0x3d, 0xe6, 0xf2, 0xe5, 0xf0, 0x53, 0x00, + 0x8c, 0xb4, 0xbd, 0x23, 0xf3, 0x3a, 0x53, 0x84, 0x97, 0xe0, 0xdd, 0xe6, 0x99, 0x70, 0x47, 0xe4, + 0x60, 0x07, 0x8f, 0x11, 0x78, 0x9c, 0x08, 0xbb, 0xdb, 0x33, 0x29, 0x1a, 0x09, 0x82, 0xdf, 0xb6, + 0x5c, 0x01, 0x7d, 0x71, 0x2e, 0xa2, 0xda, 0x43, 0x65, 0x31, 0x9d, 0xfe, 0xe7, 0xaf, 0x77, 0xf1, + 0x1d, 0x00, 0x00, 0xff, 0xff, 0xd2, 0x5b, 0x8c, 0xe1, 0x87, 0x02, 0x00, 0x00, +} diff --git a/vendor/github.com/coredns/coredns/pb/dns.proto b/vendor/github.com/coredns/coredns/pb/dns.proto index 8461f01e6..e4ac2eb2c 100644 --- a/vendor/github.com/coredns/coredns/pb/dns.proto +++ b/vendor/github.com/coredns/coredns/pb/dns.proto @@ -9,4 +9,41 @@ message DnsPacket { service DnsService { rpc Query (DnsPacket) returns (DnsPacket); + rpc Watch (stream WatchRequest) returns (stream WatchResponse); +} + +message WatchRequest { + // request_union is a request to either create a new watcher or cancel an existing watcher. + oneof request_union { + WatchCreateRequest create_request = 1; + WatchCancelRequest cancel_request = 2; + } +} + +message WatchCreateRequest { + DnsPacket query = 1; +} + +message WatchCancelRequest { + // watch_id is the watcher id to cancel + int64 watch_id = 1; +} + +message WatchResponse { + // watch_id is the ID of the watcher that corresponds to the response. + int64 watch_id = 1; + + // created is set to true if the response is for a create watch request. + // The client should record the watch_id and expect to receive DNS replies + // from the same stream. + // All replies sent to the created watcher will attach with the same watch_id. + bool created = 2; + + // canceled is set to true if the response is for a cancel watch request. + // No further events will be sent to the canceled watcher. + bool canceled = 3; + + string qname = 4; + + string err = 5; } diff --git a/vendor/github.com/coredns/coredns/plugin/backend.go b/vendor/github.com/coredns/coredns/plugin/backend.go index 32443a955..b2d4df19e 100644 --- a/vendor/github.com/coredns/coredns/plugin/backend.go +++ b/vendor/github.com/coredns/coredns/plugin/backend.go @@ -13,18 +13,18 @@ import ( type ServiceBackend interface { // Services communicates with the backend to retrieve the service definitions. Exact indicates // on exact match should be returned. - Services(ctx context.Context, state request.Request, exact bool, opt Options) ([]msg.Service, error) + Services(state request.Request, exact bool, opt Options) ([]msg.Service, error) // Reverse communicates with the backend to retrieve service definition based on a IP address // instead of a name. I.e. a reverse DNS lookup. - Reverse(ctx context.Context, state request.Request, exact bool, opt Options) ([]msg.Service, error) + Reverse(state request.Request, exact bool, opt Options) ([]msg.Service, error) // Lookup is used to find records else where. - Lookup(ctx context.Context, state request.Request, name string, typ uint16) (*dns.Msg, error) + Lookup(state request.Request, name string, typ uint16) (*dns.Msg, error) // Returns _all_ services that matches a certain name. // Note: it does not implement a specific service. - Records(ctx context.Context, state request.Request, exact bool) ([]msg.Service, error) + Records(state request.Request, exact bool) ([]msg.Service, error) // IsNameError return true if err indicated a record not found condition IsNameError(err error) bool diff --git a/vendor/github.com/coredns/coredns/plugin/backend_lookup.go b/vendor/github.com/coredns/coredns/plugin/backend_lookup.go index 096cf806b..77e08174f 100644 --- a/vendor/github.com/coredns/coredns/plugin/backend_lookup.go +++ b/vendor/github.com/coredns/coredns/plugin/backend_lookup.go @@ -1,7 +1,6 @@ package plugin import ( - "context" "fmt" "math" "net" @@ -14,13 +13,13 @@ import ( ) // A returns A records from Backend or an error. -func A(ctx context.Context, b ServiceBackend, zone string, state request.Request, previousRecords []dns.RR, opt Options) (records []dns.RR, err error) { - services, err := checkForApex(ctx, b, zone, state, opt) +func A(b ServiceBackend, zone string, state request.Request, previousRecords []dns.RR, opt Options) (records []dns.RR, err error) { + services, err := checkForApex(b, zone, state, opt) if err != nil { return nil, err } - dup := make(map[string]struct{}) + dup := make(map[string]bool) for _, serv := range services { @@ -44,7 +43,7 @@ func A(ctx context.Context, b ServiceBackend, zone string, state request.Request if dns.IsSubDomain(zone, dns.Fqdn(serv.Host)) { state1 := state.NewWithQuestion(serv.Host, state.QType()) state1.Zone = zone - nextRecords, err := A(ctx, b, zone, state1, append(previousRecords, newRecord), opt) + nextRecords, err := A(b, zone, state1, append(previousRecords, newRecord), opt) if err == nil { // Not only have we found something we should add the CNAME and the IP addresses. @@ -58,7 +57,7 @@ func A(ctx context.Context, b ServiceBackend, zone string, state request.Request // This means we can not complete the CNAME, try to look else where. target := newRecord.Target // Lookup - m1, e1 := b.Lookup(ctx, state, target, state.QType()) + m1, e1 := b.Lookup(state, target, state.QType()) if e1 != nil { continue } @@ -69,7 +68,7 @@ func A(ctx context.Context, b ServiceBackend, zone string, state request.Request case dns.TypeA: if _, ok := dup[serv.Host]; !ok { - dup[serv.Host] = struct{}{} + dup[serv.Host] = true records = append(records, serv.NewA(state.QName(), ip)) } @@ -81,13 +80,13 @@ func A(ctx context.Context, b ServiceBackend, zone string, state request.Request } // AAAA returns AAAA records from Backend or an error. -func AAAA(ctx context.Context, b ServiceBackend, zone string, state request.Request, previousRecords []dns.RR, opt Options) (records []dns.RR, err error) { - services, err := checkForApex(ctx, b, zone, state, opt) +func AAAA(b ServiceBackend, zone string, state request.Request, previousRecords []dns.RR, opt Options) (records []dns.RR, err error) { + services, err := checkForApex(b, zone, state, opt) if err != nil { return nil, err } - dup := make(map[string]struct{}) + dup := make(map[string]bool) for _, serv := range services { @@ -112,7 +111,7 @@ func AAAA(ctx context.Context, b ServiceBackend, zone string, state request.Requ if dns.IsSubDomain(zone, dns.Fqdn(serv.Host)) { state1 := state.NewWithQuestion(serv.Host, state.QType()) state1.Zone = zone - nextRecords, err := AAAA(ctx, b, zone, state1, append(previousRecords, newRecord), opt) + nextRecords, err := AAAA(b, zone, state1, append(previousRecords, newRecord), opt) if err == nil { // Not only have we found something we should add the CNAME and the IP addresses. @@ -125,7 +124,7 @@ func AAAA(ctx context.Context, b ServiceBackend, zone string, state request.Requ } // This means we can not complete the CNAME, try to look else where. target := newRecord.Target - m1, e1 := b.Lookup(ctx, state, target, state.QType()) + m1, e1 := b.Lookup(state, target, state.QType()) if e1 != nil { continue } @@ -140,7 +139,7 @@ func AAAA(ctx context.Context, b ServiceBackend, zone string, state request.Requ case dns.TypeAAAA: if _, ok := dup[serv.Host]; !ok { - dup[serv.Host] = struct{}{} + dup[serv.Host] = true records = append(records, serv.NewAAAA(state.QName(), ip)) } } @@ -150,14 +149,14 @@ func AAAA(ctx context.Context, b ServiceBackend, zone string, state request.Requ // SRV returns SRV records from the Backend. // If the Target is not a name but an IP address, a name is created on the fly. -func SRV(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) (records, extra []dns.RR, err error) { - services, err := b.Services(ctx, state, false, opt) +func SRV(b ServiceBackend, zone string, state request.Request, opt Options) (records, extra []dns.RR, err error) { + services, err := b.Services(state, false, opt) if err != nil { return nil, nil, err } - dup := make(map[item]struct{}) - lookup := make(map[string]struct{}) + dup := make(map[item]bool) + lookup := make(map[string]bool) // Looping twice to get the right weight vs priority. This might break because we may drop duplicate SRV records latter on. w := make(map[int]int) @@ -197,15 +196,15 @@ func SRV(ctx context.Context, b ServiceBackend, zone string, state request.Reque break } - lookup[srv.Target] = struct{}{} + lookup[srv.Target] = true if !dns.IsSubDomain(zone, srv.Target) { - m1, e1 := b.Lookup(ctx, state, srv.Target, dns.TypeA) + m1, e1 := b.Lookup(state, srv.Target, dns.TypeA) if e1 == nil { extra = append(extra, m1.Answer...) } - m1, e1 = b.Lookup(ctx, state, srv.Target, dns.TypeAAAA) + m1, e1 = b.Lookup(state, srv.Target, dns.TypeAAAA) if e1 == nil { // If we have seen CNAME's we *assume* that they are already added. for _, a := range m1.Answer { @@ -219,7 +218,7 @@ func SRV(ctx context.Context, b ServiceBackend, zone string, state request.Reque // Internal name, we should have some info on them, either v4 or v6 // Clients expect a complete answer, because we are a recursor in their view. state1 := state.NewWithQuestion(srv.Target, dns.TypeA) - addr, e1 := A(ctx, b, zone, state1, nil, opt) + addr, e1 := A(b, zone, state1, nil, opt) if e1 == nil { extra = append(extra, addr...) } @@ -243,14 +242,14 @@ func SRV(ctx context.Context, b ServiceBackend, zone string, state request.Reque } // MX returns MX records from the Backend. If the Target is not a name but an IP address, a name is created on the fly. -func MX(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) (records, extra []dns.RR, err error) { - services, err := b.Services(ctx, state, false, opt) +func MX(b ServiceBackend, zone string, state request.Request, opt Options) (records, extra []dns.RR, err error) { + services, err := b.Services(state, false, opt) if err != nil { return nil, nil, err } - dup := make(map[item]struct{}) - lookup := make(map[string]struct{}) + dup := make(map[item]bool) + lookup := make(map[string]bool) for _, serv := range services { if !serv.Mail { continue @@ -264,15 +263,15 @@ func MX(ctx context.Context, b ServiceBackend, zone string, state request.Reques break } - lookup[mx.Mx] = struct{}{} + lookup[mx.Mx] = true if !dns.IsSubDomain(zone, mx.Mx) { - m1, e1 := b.Lookup(ctx, state, mx.Mx, dns.TypeA) + m1, e1 := b.Lookup(state, mx.Mx, dns.TypeA) if e1 == nil { extra = append(extra, m1.Answer...) } - m1, e1 = b.Lookup(ctx, state, mx.Mx, dns.TypeAAAA) + m1, e1 = b.Lookup(state, mx.Mx, dns.TypeAAAA) if e1 == nil { // If we have seen CNAME's we *assume* that they are already added. for _, a := range m1.Answer { @@ -285,7 +284,7 @@ func MX(ctx context.Context, b ServiceBackend, zone string, state request.Reques } // Internal name state1 := state.NewWithQuestion(mx.Mx, dns.TypeA) - addr, e1 := A(ctx, b, zone, state1, nil, opt) + addr, e1 := A(b, zone, state1, nil, opt) if e1 == nil { extra = append(extra, addr...) } @@ -309,8 +308,8 @@ func MX(ctx context.Context, b ServiceBackend, zone string, state request.Reques } // CNAME returns CNAME records from the backend or an error. -func CNAME(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) (records []dns.RR, err error) { - services, err := b.Services(ctx, state, true, opt) +func CNAME(b ServiceBackend, zone string, state request.Request, opt Options) (records []dns.RR, err error) { + services, err := b.Services(state, true, opt) if err != nil { return nil, err } @@ -325,31 +324,34 @@ func CNAME(ctx context.Context, b ServiceBackend, zone string, state request.Req } // TXT returns TXT records from Backend or an error. -func TXT(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) (records []dns.RR, err error) { - services, err := b.Services(ctx, state, false, opt) +func TXT(b ServiceBackend, zone string, state request.Request, opt Options) (records []dns.RR, err error) { + services, err := b.Services(state, false, opt) if err != nil { return nil, err } for _, serv := range services { + if serv.Text == "" { + continue + } records = append(records, serv.NewTXT(state.QName())) } return records, nil } // PTR returns the PTR records from the backend, only services that have a domain name as host are included. -func PTR(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) (records []dns.RR, err error) { - services, err := b.Reverse(ctx, state, true, opt) +func PTR(b ServiceBackend, zone string, state request.Request, opt Options) (records []dns.RR, err error) { + services, err := b.Reverse(state, true, opt) if err != nil { return nil, err } - dup := make(map[string]struct{}) + dup := make(map[string]bool) for _, serv := range services { if ip := net.ParseIP(serv.Host); ip == nil { if _, ok := dup[serv.Host]; !ok { - dup[serv.Host] = struct{}{} + dup[serv.Host] = true records = append(records, serv.NewPTR(state.QName(), serv.Host)) } } @@ -358,14 +360,14 @@ func PTR(ctx context.Context, b ServiceBackend, zone string, state request.Reque } // NS returns NS records from the backend -func NS(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) (records, extra []dns.RR, err error) { +func NS(b ServiceBackend, zone string, state request.Request, opt Options) (records, extra []dns.RR, err error) { // NS record for this zone live in a special place, ns.dns.. Fake our lookup. // only a tad bit fishy... old := state.QName() state.Clear() state.Req.Question[0].Name = "ns.dns." + zone - services, err := b.Services(ctx, state, false, opt) + services, err := b.Services(state, false, opt) if err != nil { return nil, nil, err } @@ -388,14 +390,8 @@ func NS(ctx context.Context, b ServiceBackend, zone string, state request.Reques } // SOA returns a SOA record from the backend. -func SOA(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) ([]dns.RR, error) { - minTTL := b.MinTTL(state) - ttl := uint32(300) - if minTTL < ttl { - ttl = minTTL - } - - header := dns.RR_Header{Name: zone, Rrtype: dns.TypeSOA, Ttl: ttl, Class: dns.ClassINET} +func SOA(b ServiceBackend, zone string, state request.Request, opt Options) ([]dns.RR, error) { + header := dns.RR_Header{Name: zone, Rrtype: dns.TypeSOA, Ttl: 300, Class: dns.ClassINET} Mbox := hostmaster + "." Ns := "ns.dns." @@ -411,18 +407,19 @@ func SOA(ctx context.Context, b ServiceBackend, zone string, state request.Reque Refresh: 7200, Retry: 1800, Expire: 86400, - Minttl: minTTL, + Minttl: b.MinTTL(state), } return []dns.RR{soa}, nil } // BackendError writes an error response to the client. -func BackendError(ctx context.Context, b ServiceBackend, zone string, rcode int, state request.Request, err error, opt Options) (int, error) { +func BackendError(b ServiceBackend, zone string, rcode int, state request.Request, err error, opt Options) (int, error) { m := new(dns.Msg) m.SetRcode(state.Req, rcode) - m.Authoritative = true - m.Ns, _ = SOA(ctx, b, zone, state, opt) + m.Authoritative, m.RecursionAvailable = true, true + m.Ns, _ = SOA(b, zone, state, opt) + state.SizeAndDo(m) state.W.WriteMsg(m) // Return success as the rcode to signal we have written to the client. return dns.RcodeSuccess, err @@ -439,10 +436,10 @@ func newAddress(s msg.Service, name string, ip net.IP, what uint16) dns.RR { return &dns.AAAA{Hdr: hdr, AAAA: ip} } -// checkForApex checks the special apex.dns directory for records that will be returned as A or AAAA. -func checkForApex(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) ([]msg.Service, error) { +// checkForApex checks the spcecial apex.dns directory for records that will be returned as A or AAAA. +func checkForApex(b ServiceBackend, zone string, state request.Request, opt Options) ([]msg.Service, error) { if state.Name() != zone { - return b.Services(ctx, state, false, opt) + return b.Services(state, false, opt) } // If the zone name itself is queried we fake the query to search for a special entry @@ -451,14 +448,14 @@ func checkForApex(ctx context.Context, b ServiceBackend, zone string, state requ state.Clear() state.Req.Question[0].Name = dnsutil.Join("apex.dns", zone) - services, err := b.Services(ctx, state, false, opt) + services, err := b.Services(state, false, opt) if err == nil { state.Req.Question[0].Name = old return services, err } state.Req.Question[0].Name = old - return b.Services(ctx, state, false, opt) + return b.Services(state, false, opt) } // item holds records. @@ -470,17 +467,17 @@ type item struct { // isDuplicate uses m to see if the combo (name, addr, port) already exists. If it does // not exist already IsDuplicate will also add the record to the map. -func isDuplicate(m map[item]struct{}, name, addr string, port uint16) bool { +func isDuplicate(m map[item]bool, name, addr string, port uint16) bool { if addr != "" { _, ok := m[item{name, 0, addr}] if !ok { - m[item{name, 0, addr}] = struct{}{} + m[item{name, 0, addr}] = true } return ok } _, ok := m[item{name, port, ""}] if !ok { - m[item{name, port, ""}] = struct{}{} + m[item{name, port, ""}] = true } return ok } diff --git a/vendor/github.com/coredns/coredns/plugin/cache/README.md b/vendor/github.com/coredns/coredns/plugin/cache/README.md index 8b2bdf075..ad2483ae9 100644 --- a/vendor/github.com/coredns/coredns/plugin/cache/README.md +++ b/vendor/github.com/coredns/coredns/plugin/cache/README.md @@ -19,7 +19,7 @@ cache [TTL] [ZONES...] ~~~ * **TTL** max TTL in seconds. If not specified, the maximum TTL will be used, which is 3600 for - NOERROR responses and 1800 for denial of existence ones. + noerror responses and 1800 for denial of existence ones. Setting a TTL of 300: `cache 300` would cache records up to 300 seconds. * **ZONES** zones it should cache for. If empty, the zones from the configuration block are used. @@ -88,14 +88,13 @@ Proxy to Google Public DNS and only cache responses for example.org (or below). ~~~ corefile . { - forward . 8.8.8.8:53 + proxy . 8.8.8.8:53 cache example.org } ~~~ Enable caching for all zones, keep a positive cache size of 5000 and a negative cache size of 2500: - -~~~ corefile + ~~~ corefile . { cache { success 5000 diff --git a/vendor/github.com/coredns/coredns/plugin/cache/cache.go b/vendor/github.com/coredns/coredns/plugin/cache/cache.go index bc82bb604..cd0fda505 100644 --- a/vendor/github.com/coredns/coredns/plugin/cache/cache.go +++ b/vendor/github.com/coredns/coredns/plugin/cache/cache.go @@ -118,7 +118,7 @@ type ResponseWriter struct { // newPrefetchResponseWriter returns a Cache ResponseWriter to be used in // prefetch requests. It ensures RemoteAddr() can be called even after the -// original connection has already been closed. +// original connetion has already been closed. func newPrefetchResponseWriter(server string, state request.Request, c *Cache) *ResponseWriter { // Resolve the address now, the connection might be already closed when the // actual prefetch request is made. diff --git a/vendor/github.com/coredns/coredns/plugin/done.go b/vendor/github.com/coredns/coredns/plugin/done.go deleted file mode 100644 index 3f53273da..000000000 --- a/vendor/github.com/coredns/coredns/plugin/done.go +++ /dev/null @@ -1,14 +0,0 @@ -package plugin - -import "context" - -// Done is a non-blocking function that returns true if the context has been canceled. -func Done(ctx context.Context) bool { - select { - case <-ctx.Done(): - return true - default: - return false - } - return false -} diff --git a/vendor/github.com/coredns/coredns/plugin/errors/README.md b/vendor/github.com/coredns/coredns/plugin/errors/README.md index 8b1449ea3..70ec3a2c3 100644 --- a/vendor/github.com/coredns/coredns/plugin/errors/README.md +++ b/vendor/github.com/coredns/coredns/plugin/errors/README.md @@ -34,7 +34,7 @@ Option `consolidate` allows collecting several error messages matching the regul Multiple `consolidate` options with different **DURATION** and **REGEXP** are allowed. In case if some error message corresponds to several defined regular expressions the message will be associated with the first appropriate **REGEXP**. -For better performance, it's recommended to use the `^` or `$` metacharacters in regular expression when filtering error messages by prefix or suffix, e.g. `^failed to .*`, or `.* timeout$`. +For better performance, it's recomended to use the `^` or `$` metacharacters in regular expression when filtering error messages by prefix or suffix, e.g. `^failed to .*`, or `.* timeout$`. ## Examples diff --git a/vendor/github.com/coredns/coredns/plugin/errors/errors.go b/vendor/github.com/coredns/coredns/plugin/errors/errors.go index 9565f11ab..aa4b384b8 100644 --- a/vendor/github.com/coredns/coredns/plugin/errors/errors.go +++ b/vendor/github.com/coredns/coredns/plugin/errors/errors.go @@ -35,19 +35,28 @@ func (p *pattern) setTimer(t *time.Timer) { // errorHandler handles DNS errors (and errors from other plugin). type errorHandler struct { patterns []*pattern + eLogger func(int, string, string, string) + cLogger func(uint32, string, time.Duration) stopFlag uint32 Next plugin.Handler } func newErrorHandler() *errorHandler { - return &errorHandler{} + return &errorHandler{eLogger: errorLogger, cLogger: consLogger} +} + +func errorLogger(code int, qName, qType, err string) { + log.Errorf("%d %s %s: %s", code, qName, qType, err) +} + +func consLogger(cnt uint32, pattern string, p time.Duration) { + log.Errorf("%d errors like '%s' occured in last %s", cnt, pattern, p) } func (h *errorHandler) logPattern(i int) { cnt := atomic.SwapUint32(&h.patterns[i].count, 0) if cnt > 0 { - log.Errorf("%d errors like '%s' occurred in last %s", - cnt, h.patterns[i].pattern.String(), h.patterns[i].period) + h.cLogger(cnt, h.patterns[i].pattern.String(), h.patterns[i].period) } } @@ -93,7 +102,7 @@ func (h *errorHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dn } } state := request.Request{W: w, Req: r} - log.Errorf("%d %s %s: %s", rcode, state.Name(), state.Type(), strErr) + h.eLogger(rcode, state.Name(), state.Type(), strErr) } return rcode, err diff --git a/vendor/github.com/coredns/coredns/plugin/etcd/msg/path.go b/vendor/github.com/coredns/coredns/plugin/etcd/msg/path.go index c90798035..dc7494782 100644 --- a/vendor/github.com/coredns/coredns/plugin/etcd/msg/path.go +++ b/vendor/github.com/coredns/coredns/plugin/etcd/msg/path.go @@ -30,7 +30,7 @@ func Domain(s string) string { } // PathWithWildcard ascts as Path, but if a name contains wildcards (* or any), the name will be -// chopped of before the (first) wildcard, and we do a higher level search and +// chopped of before the (first) wildcard, and we do a highler evel search and // later find the matching names. So service.*.skydns.local, will look for all // services under skydns.local and will later check for names that match // service.*.skydns.local. If a wildcard is found the returned bool is true. diff --git a/vendor/github.com/coredns/coredns/plugin/etcd/msg/service.go b/vendor/github.com/coredns/coredns/plugin/etcd/msg/service.go index 4e049e10a..987fb14c7 100644 --- a/vendor/github.com/coredns/coredns/plugin/etcd/msg/service.go +++ b/vendor/github.com/coredns/coredns/plugin/etcd/msg/service.go @@ -38,21 +38,15 @@ type Service struct { // NewSRV returns a new SRV record based on the Service. func (s *Service) NewSRV(name string, weight uint16) *dns.SRV { - host := dns.Fqdn(s.Host) - if s.TargetStrip > 0 { - host = targetStrip(host, s.TargetStrip) - } + host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip) return &dns.SRV{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeSRV, Class: dns.ClassINET, Ttl: s.TTL}, - Priority: uint16(s.Priority), Weight: weight, Port: uint16(s.Port), Target: host} + Priority: uint16(s.Priority), Weight: weight, Port: uint16(s.Port), Target: dns.Fqdn(host)} } // NewMX returns a new MX record based on the Service. func (s *Service) NewMX(name string) *dns.MX { - host := dns.Fqdn(s.Host) - if s.TargetStrip > 0 { - host = targetStrip(host, s.TargetStrip) - } + host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip) return &dns.MX{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: s.TTL}, Preference: uint16(s.Priority), Mx: host} @@ -85,10 +79,7 @@ func (s *Service) NewPTR(name string, target string) *dns.PTR { // NewNS returns a new NS record based on the Service. func (s *Service) NewNS(name string) *dns.NS { - host := dns.Fqdn(s.Host) - if s.TargetStrip > 0 { - host = targetStrip(host, s.TargetStrip) - } + host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip) return &dns.NS{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: s.TTL}, Ns: host} } @@ -164,6 +155,10 @@ func split255(s string) []string { // targetStrip strips "targetstrip" labels from the left side of the fully qualified name. func targetStrip(name string, targetStrip int) string { + if targetStrip == 0 { + return name + } + offset, end := 0, false for i := 0; i < targetStrip; i++ { offset, end = dns.NextLabel(name, offset) diff --git a/vendor/github.com/coredns/coredns/plugin/forward/README.md b/vendor/github.com/coredns/coredns/plugin/forward/README.md index 94e306ea8..c0a426aab 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/README.md +++ b/vendor/github.com/coredns/coredns/plugin/forward/README.md @@ -21,6 +21,10 @@ connect to a random upstream (which may or may not work). This plugin can only be used once per Server Block. +How does *forward* relate to *proxy*? This plugin is the "new" version of *proxy* and is faster +because it re-uses connections to the upstreams. It also does in-band health checks - using DNS +instead of HTTP. Since it is newer it has a little less (production) mileage on it. + ## Syntax In its most basic form, a simple forwarder uses this syntax: @@ -75,13 +79,8 @@ forward FROM TO... { The server certificate is verified using the specified CA file * `tls_servername` **NAME** allows you to set a server name in the TLS configuration; for instance 9.9.9.9 - needs this to be set to `dns.quad9.net`. Multiple upstreams are still allowed in this scenario, - but they have to use the same `tls_servername`. E.g. mixing 9.9.9.9 (QuadDNS) with 1.1.1.1 - (Cloudflare) will not work. + needs this to be set to `dns.quad9.net`. * `policy` specifies the policy to use for selecting upstream servers. The default is `random`. - * `random` is a policy that implements random upstream selection. - * `round_robin` is a policy that selects hosts based on round robin ordering. - * `sequential` is a policy that selects hosts based on sequential ordering. * `health_check`, use a different **DURATION** for health checking, the default duration is 0.5s. Also note the TLS config is "global" for the whole forwarding proxy if you need a different @@ -161,18 +160,6 @@ service with health checks. } ~~~ -Or with multiple upstreams from the same provider - -~~~ corefile -. { - forward . tls://1.1.1.1 tls://1.0.0.1 { - tls_servername cloudflare-dns.com - health_check 5s - } - cache 30 -} -~~~ - ## Bugs The TLS config is global for the whole forwarding proxy if you need a different `tls_servername` for diff --git a/vendor/github.com/coredns/coredns/plugin/forward/connect.go b/vendor/github.com/coredns/coredns/plugin/forward/connect.go index 8fde2224b..64edb395e 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/connect.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/connect.go @@ -69,6 +69,14 @@ func (t *Transport) Dial(proto string) (*dns.Conn, bool, error) { return conn, false, err } +func (p *Proxy) readTimeout() time.Duration { + return limitTimeout(&p.avgRtt, minTimeout, maxTimeout) +} + +func (p *Proxy) updateRtt(newRtt time.Duration) { + averageTimeout(&p.avgRtt, newRtt, cumulativeAvgWeight) +} + // Connect selects an upstream, sends the request and waits for a response. func (p *Proxy) Connect(ctx context.Context, state request.Request, opts options) (*dns.Msg, error) { start := time.Now() @@ -95,6 +103,7 @@ func (p *Proxy) Connect(ctx context.Context, state request.Request, opts options } conn.SetWriteDeadline(time.Now().Add(maxTimeout)) + reqTime := time.Now() if err := conn.WriteMsg(state.Req); err != nil { conn.Close() // not giving it back if err == io.EOF && cached { @@ -103,23 +112,19 @@ func (p *Proxy) Connect(ctx context.Context, state request.Request, opts options return nil, err } - var ret *dns.Msg - conn.SetReadDeadline(time.Now().Add(readTimeout)) - for { - ret, err = conn.ReadMsg() - if err != nil { - conn.Close() // not giving it back - if err == io.EOF && cached { - return nil, ErrCachedClosed - } - return ret, err - } - // drop out-of-order responses - if state.Req.Id == ret.Id { - break + conn.SetReadDeadline(time.Now().Add(p.readTimeout())) + ret, err := conn.ReadMsg() + if err != nil { + p.updateRtt(maxTimeout) + conn.Close() // not giving it back + if err == io.EOF && cached { + return nil, ErrCachedClosed } + return ret, err } + p.updateRtt(time.Since(reqTime)) + p.transport.Yield(conn) rc, ok := dns.RcodeToString[ret.Rcode] diff --git a/vendor/github.com/coredns/coredns/plugin/forward/dnstap.go b/vendor/github.com/coredns/coredns/plugin/forward/dnstap.go deleted file mode 100644 index 7866aa39b..000000000 --- a/vendor/github.com/coredns/coredns/plugin/forward/dnstap.go +++ /dev/null @@ -1,61 +0,0 @@ -package forward - -import ( - "context" - "time" - - "github.com/coredns/coredns/plugin/dnstap" - "github.com/coredns/coredns/plugin/dnstap/msg" - "github.com/coredns/coredns/request" - - tap "github.com/dnstap/golang-dnstap" - "github.com/miekg/dns" -) - -func toDnstap(ctx context.Context, host string, f *Forward, state request.Request, reply *dns.Msg, start time.Time) error { - tapper := dnstap.TapperFromContext(ctx) - if tapper == nil { - return nil - } - // Query - b := msg.New().Time(start).HostPort(host) - opts := f.opts - t := "" - switch { - case opts.forceTCP: // TCP flag has precedence over UDP flag - t = "tcp" - case opts.preferUDP: - t = "udp" - default: - t = state.Proto() - } - - if t == "tcp" { - b.SocketProto = tap.SocketProtocol_TCP - } else { - b.SocketProto = tap.SocketProtocol_UDP - } - - if tapper.Pack() { - b.Msg(state.Req) - } - m, err := b.ToOutsideQuery(tap.Message_FORWARDER_QUERY) - if err != nil { - return err - } - tapper.TapMessage(m) - - // Response - if reply != nil { - if tapper.Pack() { - b.Msg(reply) - } - m, err := b.Time(time.Now()).ToOutsideResponse(tap.Message_FORWARDER_RESPONSE) - if err != nil { - return err - } - tapper.TapMessage(m) - } - - return nil -} diff --git a/vendor/github.com/coredns/coredns/plugin/forward/forward.go b/vendor/github.com/coredns/coredns/plugin/forward/forward.go index da2e175fe..dfa1aaca1 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/forward.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/forward.go @@ -74,7 +74,7 @@ func (f *Forward) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg i := 0 list := f.List() deadline := time.Now().Add(defaultTimeout) - start := time.Now() + for time.Now().Before(deadline) { if i >= len(list) { // reached the end of list, reset to begin @@ -116,7 +116,7 @@ func (f *Forward) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg continue } // Retry with TCP if truncated and prefer_udp configured. - if ret != nil && ret.Truncated && !opts.forceTCP && f.opts.preferUDP { + if err == dns.ErrTruncated && !opts.forceTCP && f.opts.preferUDP { opts.forceTCP = true continue } @@ -126,8 +126,8 @@ func (f *Forward) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg if child != nil { child.Finish() } - taperr := toDnstap(ctx, proxy.addr, f, state, ret, start) + ret, err = truncated(state, ret, err) upstreamErr = err if err != nil { @@ -144,16 +144,15 @@ func (f *Forward) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg // Check if the reply is correct; if not return FormErr. if !state.Match(ret) { - debug.Hexdumpf(ret, "Wrong reply for id: %d, %s %d", ret.Id, state.QName(), state.QType()) + debug.Hexdumpf(ret, "Wrong reply for id: %d, %s/%d", state.QName(), state.QType()) - formerr := new(dns.Msg) - formerr.SetRcode(state.Req, dns.RcodeFormatError) + formerr := state.ErrorMessage(dns.RcodeFormatError) w.WriteMsg(formerr) - return 0, taperr + return 0, nil } w.WriteMsg(ret) - return 0, taperr + return 0, nil } if upstreamErr != nil { diff --git a/vendor/github.com/coredns/coredns/plugin/forward/lookup.go b/vendor/github.com/coredns/coredns/plugin/forward/lookup.go new file mode 100644 index 000000000..f6c9a0745 --- /dev/null +++ b/vendor/github.com/coredns/coredns/plugin/forward/lookup.go @@ -0,0 +1,89 @@ +// Package forward implements a forwarding proxy. It caches an upstream net.Conn for some time, so if the same +// client returns the upstream's Conn will be precached. Depending on how you benchmark this looks to be +// 50% faster than just opening a new connection for every client. It works with UDP and TCP and uses +// inband healthchecking. +package forward + +import ( + "context" + + "github.com/coredns/coredns/plugin/pkg/transport" + "github.com/coredns/coredns/request" + + "github.com/miekg/dns" +) + +// Forward forward the request in state as-is. Unlike Lookup that adds EDNS0 suffix to the message. +// Forward may be called with a nil f, an error is returned in that case. +func (f *Forward) Forward(state request.Request) (*dns.Msg, error) { + if f == nil { + return nil, ErrNoForward + } + + fails := 0 + var upstreamErr error + for _, proxy := range f.List() { + if proxy.Down(f.maxfails) { + fails++ + if fails < len(f.proxies) { + continue + } + // All upstream proxies are dead, assume healtcheck is complete broken and randomly + // select an upstream to connect to. + proxy = f.List()[0] + } + + ret, err := proxy.Connect(context.Background(), state, f.opts) + + ret, err = truncated(state, ret, err) + upstreamErr = err + + if err != nil { + if fails < len(f.proxies) { + continue + } + break + } + + // Check if the reply is correct; if not return FormErr. + if !state.Match(ret) { + return state.ErrorMessage(dns.RcodeFormatError), nil + } + + return ret, err + } + + if upstreamErr != nil { + return nil, upstreamErr + } + + return nil, ErrNoHealthy +} + +// Lookup will use name and type to forge a new message and will send that upstream. It will +// set any EDNS0 options correctly so that downstream will be able to process the reply. +// Lookup may be called with a nil f, an error is returned in that case. +func (f *Forward) Lookup(state request.Request, name string, typ uint16) (*dns.Msg, error) { + if f == nil { + return nil, ErrNoForward + } + + req := new(dns.Msg) + req.SetQuestion(name, typ) + state.SizeAndDo(req) + + state2 := request.Request{W: state.W, Req: req} + + return f.Forward(state2) +} + +// NewLookup returns a Forward that can be used for plugin that need an upstream to resolve external names. +// Note that the caller must run Close on the forward to stop the health checking goroutines. +func NewLookup(addr []string) *Forward { + f := New() + for i := range addr { + p := NewProxy(addr[i], transport.DNS) + f.SetProxy(p) + } + return f +} diff --git a/vendor/github.com/coredns/coredns/plugin/forward/persistent.go b/vendor/github.com/coredns/coredns/plugin/forward/persistent.go index d1348f94d..fabdc70b2 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/persistent.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/persistent.go @@ -31,7 +31,7 @@ type Transport struct { func newTransport(addr string) *Transport { t := &Transport{ - avgDialTime: int64(maxDialTimeout / 2), + avgDialTime: int64(defaultDialTimeout / 2), conns: make(map[string][]*persistConn), expire: defaultExpire, addr: addr, @@ -159,10 +159,8 @@ func (t *Transport) SetExpire(expire time.Duration) { t.expire = expire } func (t *Transport) SetTLSConfig(cfg *tls.Config) { t.tlsConfig = cfg } const ( - defaultExpire = 10 * time.Second - minDialTimeout = 1 * time.Second - maxDialTimeout = 30 * time.Second - - // Some resolves might take quite a while, usually (cached) responses are fast. Set to 2s to give us some time to retry a different upstream. - readTimeout = 2 * time.Second + defaultExpire = 10 * time.Second + minDialTimeout = 100 * time.Millisecond + maxDialTimeout = 30 * time.Second + defaultDialTimeout = 30 * time.Second ) diff --git a/vendor/github.com/coredns/coredns/plugin/forward/proxy.go b/vendor/github.com/coredns/coredns/plugin/forward/proxy.go index bf4d68dca..fbe79cd69 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/proxy.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/proxy.go @@ -11,7 +11,8 @@ import ( // Proxy defines an upstream host. type Proxy struct { - fails uint32 + avgRtt int64 + fails uint32 addr string @@ -31,6 +32,7 @@ func NewProxy(addr, trans string) *Proxy { fails: 0, probe: up.New(), transport: newTransport(addr), + avgRtt: int64(maxTimeout / 2), } p.health = NewHealthChecker(trans) runtime.SetFinalizer(p, (*Proxy).finalizer) diff --git a/vendor/github.com/coredns/coredns/plugin/forward/truncated.go b/vendor/github.com/coredns/coredns/plugin/forward/truncated.go new file mode 100644 index 000000000..fb821d335 --- /dev/null +++ b/vendor/github.com/coredns/coredns/plugin/forward/truncated.go @@ -0,0 +1,29 @@ +package forward + +import ( + "github.com/coredns/coredns/request" + + "github.com/miekg/dns" +) + +// truncated looks at the error and if truncated return a nil error +// and a possible reconstructed dns message if that was nil. +func truncated(state request.Request, ret *dns.Msg, err error) (*dns.Msg, error) { + // If you query for instance ANY isc.org; you get a truncated query back which miekg/dns fails to unpack + // because the RRs are not finished. The returned message can be useful or useless. Return the original + // query with some header bits set that they should retry with TCP. + if err != dns.ErrTruncated { + return ret, err + } + + // We may or may not have something sensible... if not reassemble something to send to the client. + m := ret + if ret == nil { + m = new(dns.Msg) + m.SetReply(state.Req) + m.Truncated = true + m.Authoritative = true + m.Rcode = dns.RcodeSuccess + } + return m, nil +} diff --git a/vendor/github.com/coredns/coredns/plugin/health/README.md b/vendor/github.com/coredns/coredns/plugin/health/README.md index 68afeac06..1907a98c1 100644 --- a/vendor/github.com/coredns/coredns/plugin/health/README.md +++ b/vendor/github.com/coredns/coredns/plugin/health/README.md @@ -6,8 +6,9 @@ ## Description -Enabled process wide health endpoint. When CoreDNS is up and running this returns a 200 OK http -status code. The health is exported, by default, on port 8080/health . +By enabling *health* any plugin that implements +[healt.Healther interface](https://godoc.org/github.com/coredns/coredns/plugin/health#Healther) +will be queried for it's health. The combined health is exported, by default, on port 8080/health . ## Syntax @@ -16,9 +17,12 @@ health [ADDRESS] ~~~ Optionally takes an address; the default is `:8080`. The health path is fixed to `/health`. The -health endpoint returns a 200 response code and the word "OK" when this server is healthy. +health endpoint returns a 200 response code and the word "OK" when this server is healthy. It returns +a 503. *health* periodically (1s) polls plugins that exports health information. If any of the +plugins signals that it is unhealthy, the server will go unhealthy too. Each plugin that supports +health checks has a section "Health" in their README. -An extra option can be set with this extended syntax: +More options can be set with this extended syntax: ~~~ health [ADDRESS] { @@ -29,8 +33,8 @@ health [ADDRESS] { * Where `lameduck` will make the process unhealthy then *wait* for **DURATION** before the process shuts down. -If you have multiple Server Blocks, *health* should only be enabled in one of them (as it is process -wide). If you really need multiple endpoints, you must run health endpoints on different ports: +If you have multiple Server Blocks and need to export health for each of the plugins, you must run +health endpoints on different ports: ~~~ corefile com { @@ -44,6 +48,21 @@ net { } ~~~ +Note that if you format this in one server block you will get an error on startup, that the second +server can't setup the health plugin (on the same port). + +~~~ txt +com net { + whoami + erratic + health :8080 +} +~~~ + +## Plugins + +Any plugin that implements the Healther interface will be used to report health. + ## Metrics If monitoring is enabled (via the *prometheus* directive) then the following metric is exported: @@ -77,7 +96,7 @@ Set a lameduck duration of 1 second: ## Bugs -When reloading, the health handler is stopped before the new server instance is started. If that -new server fails to start, then the initial server instance is still available and DNS queries still -served, but health handler stays down. Health will not reply HTTP request until a successful reload -or a complete restart of CoreDNS. +When reloading, the Health handler is stopped before the new server instance is started. +If that new server fails to start, then the initial server instance is still available and DNS queries still served, +but Health handler stays down. +Health will not reply HTTP request until a successful reload or a complete restart of CoreDNS. diff --git a/vendor/github.com/coredns/coredns/plugin/health/health.go b/vendor/github.com/coredns/coredns/plugin/health/health.go index 895704409..7f35b0709 100644 --- a/vendor/github.com/coredns/coredns/plugin/health/health.go +++ b/vendor/github.com/coredns/coredns/plugin/health/health.go @@ -5,6 +5,7 @@ import ( "io" "net" "net/http" + "sync" "time" clog "github.com/coredns/coredns/plugin/pkg/log" @@ -21,12 +22,18 @@ type health struct { nlSetup bool mux *http.ServeMux - stop chan bool + // A slice of Healthers that the health plugin will poll every second for their health status. + h []Healther + sync.RWMutex + ok bool // ok is the global boolean indicating an all healthy plugin stack + + stop chan bool + pollstop chan bool } // newHealth returns a new initialized health. func newHealth(addr string) *health { - return &health{Addr: addr, stop: make(chan bool)} + return &health{Addr: addr, stop: make(chan bool), pollstop: make(chan bool)} } func (h *health) OnStartup() error { @@ -44,10 +51,12 @@ func (h *health) OnStartup() error { h.nlSetup = true h.mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { - // We're always healthy. - w.WriteHeader(http.StatusOK) - io.WriteString(w, ok) - return + if h.Ok() { + w.WriteHeader(http.StatusOK) + io.WriteString(w, ok) + return + } + w.WriteHeader(http.StatusServiceUnavailable) }) go func() { http.Serve(h.ln, h.mux) }() @@ -63,6 +72,11 @@ func (h *health) OnFinalShutdown() error { return nil } + // Stop polling plugins + h.pollstop <- true + // NACK health + h.SetOk(false) + if h.lameduck > 0 { log.Infof("Going into lameduck mode for %s", h.lameduck) time.Sleep(h.lameduck) @@ -70,8 +84,8 @@ func (h *health) OnFinalShutdown() error { h.ln.Close() + h.stop <- true h.nlSetup = false - close(h.stop) return nil } diff --git a/vendor/github.com/coredns/coredns/plugin/health/healther.go b/vendor/github.com/coredns/coredns/plugin/health/healther.go new file mode 100644 index 000000000..8bb6c907c --- /dev/null +++ b/vendor/github.com/coredns/coredns/plugin/health/healther.go @@ -0,0 +1,36 @@ +package health + +// Healther interface needs to be implemented by each plugin willing to provide +// healthhceck information to the health plugin. Note this method should return +// quickly, i.e. just checking a boolean status, as it is called every second +// from the health plugin. +type Healther interface { + // Health returns a boolean indicating the health status of a plugin. + // False indicates unhealthy. + Health() bool +} + +// Ok returns the global health status of all plugin configured in this server. +func (h *health) Ok() bool { + h.RLock() + defer h.RUnlock() + return h.ok +} + +// SetOk sets the global health status of all plugin configured in this server. +func (h *health) SetOk(ok bool) { + h.Lock() + defer h.Unlock() + h.ok = ok +} + +// poll polls all healthers and sets the global state. +func (h *health) poll() { + for _, m := range h.h { + if !m.Health() { + h.SetOk(false) + return + } + } + h.SetOk(true) +} diff --git a/vendor/github.com/coredns/coredns/plugin/health/setup.go b/vendor/github.com/coredns/coredns/plugin/health/setup.go index 19aeba58d..5a1725125 100644 --- a/vendor/github.com/coredns/coredns/plugin/health/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/health/setup.go @@ -5,6 +5,7 @@ import ( "net" "time" + "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/metrics" @@ -27,6 +28,32 @@ func setup(c *caddy.Controller) error { h := newHealth(addr) h.lameduck = lame + c.OnStartup(func() error { + plugins := dnsserver.GetConfig(c).Handlers() + for _, p := range plugins { + if x, ok := p.(Healther); ok { + h.h = append(h.h, x) + } + } + return nil + }) + + c.OnStartup(func() error { + // Poll all middleware every second. + h.poll() + go func() { + for { + select { + case <-time.After(1 * time.Second): + h.poll() + case <-h.pollstop: + return + } + } + }() + return nil + }) + c.OnStartup(func() error { metrics.MustRegister(c, HealthDuration) return nil diff --git a/vendor/github.com/coredns/coredns/plugin/log/README.md b/vendor/github.com/coredns/coredns/plugin/log/README.md index bbd853bd3..48e5acb6c 100644 --- a/vendor/github.com/coredns/coredns/plugin/log/README.md +++ b/vendor/github.com/coredns/coredns/plugin/log/README.md @@ -23,18 +23,16 @@ log Or if you want/need slightly more control: ~~~ txt -log [NAMES...] [FORMAT] +log [NAME] [FORMAT] ~~~ -* `NAMES` is the name list to match in order to be logged -* `FORMAT` is the log format to use (default is Common Log Format), `{common}` is used as a shortcut - for the Common Log Format. You can also use `{combined}` for a format that adds the query opcode - `{>opcode}` to the Common Log Format. +* `NAME` is the name to match in order to be logged +* `FORMAT` is the log format to use (default is Common Log Format) You can further specify the classes of responses that get logged: ~~~ txt -log [NAMES...] [FORMAT] { +log [NAME] [FORMAT] { class CLASSES... } ~~~ @@ -44,12 +42,10 @@ log [NAMES...] [FORMAT] { The classes of responses have the following meaning: * `success`: successful response -* `denial`: either NXDOMAIN or nodata responses (Name exists, type does not). A nodata response - sets the return code to NOERROR. +* `denial`: either NXDOMAIN or NODATA (name exists, type does not) * `error`: SERVFAIL, NOTIMP, REFUSED, etc. Anything that indicates the remote server is not willing to resolve the request. -* `all`: the default - nothing is specified. Using of this class means that all messages will be - logged whatever we mix together with "all". +* `all`: the default - nothing is specified. Using of this class means that all messages will be logged whatever we mix together with "all". If no class is specified, it defaults to *all*. @@ -65,7 +61,6 @@ The following place holders are supported: * `{class}`: qclass of the request * `{proto}`: protocol used (tcp or udp) * `{remote}`: client's IP address, for IPv6 addresses these are enclosed in brackets: `[::1]` -* `{local}`: server's IP address, for IPv6 addresses these are enclosed in brackets: `[::1]` * `{size}`: request size in bytes * `{port}`: client's port * `{duration}`: response duration @@ -77,11 +72,6 @@ The following place holders are supported: * `{>do}`: is the EDNS0 DO (DNSSEC OK) bit set in the query * `{>id}`: query ID * `{>opcode}`: query OPCODE -* `{common}`: the default Common Log Format. -* `{combined}`: the Common Log Format with the query opcode. -* `{/LABEL}`: any metadata label is accepted as a place holder if it is enclosed between `{/` and - `}`, the place holder will be replaced by the corresponding metadata value or the default value - `-` if label is not defined. See the *metadata* plugin for more information. The default Common Log Format is: @@ -114,7 +104,7 @@ Custom log format, for all zones (`.`) } ~~~ -Only log denials (NXDOMAIN and nodata) for example.org (and below) +Only log denials for example.org (and below to a file) ~~~ corefile . { @@ -124,11 +114,11 @@ Only log denials (NXDOMAIN and nodata) for example.org (and below) } ~~~ -Log all queries which were not resolved successfully in the Combined Log Format. +Log all queries which were not resolved successfully ~~~ corefile . { - log . {combined} { + log . { class denial error } } diff --git a/vendor/github.com/coredns/coredns/plugin/log/log.go b/vendor/github.com/coredns/coredns/plugin/log/log.go index 49581dfc4..685c55191 100644 --- a/vendor/github.com/coredns/coredns/plugin/log/log.go +++ b/vendor/github.com/coredns/coredns/plugin/log/log.go @@ -6,8 +6,10 @@ import ( "time" "github.com/coredns/coredns/plugin" + "github.com/coredns/coredns/plugin/metrics/vars" "github.com/coredns/coredns/plugin/pkg/dnstest" clog "github.com/coredns/coredns/plugin/pkg/log" + "github.com/coredns/coredns/plugin/pkg/rcode" "github.com/coredns/coredns/plugin/pkg/replacer" "github.com/coredns/coredns/plugin/pkg/response" "github.com/coredns/coredns/request" @@ -17,10 +19,9 @@ import ( // Logger is a basic request logging plugin. type Logger struct { - Next plugin.Handler - Rules []Rule - - repl replacer.Replacer + Next plugin.Handler + Rules []Rule + ErrorFunc func(context.Context, dns.ResponseWriter, *dns.Msg, int) // failover error handler } // ServeDNS implements the plugin.Handler interface. @@ -34,15 +35,30 @@ func (l Logger) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) rrw := dnstest.NewRecorder(w) rc, err := plugin.NextOrFailure(l.Name(), l.Next, ctx, rrw, r) + if rc > 0 { + // There was an error up the chain, but no response has been written yet. + // The error must be handled here so the log entry will record the response size. + if l.ErrorFunc != nil { + l.ErrorFunc(ctx, rrw, r, rc) + } else { + answer := new(dns.Msg) + answer.SetRcode(r, rc) + state.SizeAndDo(answer) + + vars.Report(ctx, state, vars.Dropped, rcode.ToString(rc), answer.Len(), time.Now()) + + w.WriteMsg(answer) + } + rc = 0 + } + tpe, _ := response.Typify(rrw.Msg, time.Now().UTC()) class := response.Classify(tpe) // If we don't set up a class in config, the default "all" will be added // and we shouldn't have an empty rule.Class. - _, ok := rule.Class[response.All] - _, ok1 := rule.Class[class] - if ok || ok1 { - logstr := l.repl.Replace(ctx, state, rrw, rule.Format) - clog.Infof(logstr) + if rule.Class[response.All] || rule.Class[class] { + rep := replacer.New(r, rrw, CommonLogEmptyValue) + clog.Infof(rep.Replace(rule.Format)) } return rc, err @@ -57,13 +73,15 @@ func (l Logger) Name() string { return "log" } // Rule configures the logging plugin. type Rule struct { NameScope string - Class map[response.Class]struct{} + Class map[response.Class]bool Format string } const ( // CommonLogFormat is the common log format. - CommonLogFormat = `{remote}:{port} ` + replacer.EmptyValue + ` {>id} "{type} {class} {name} {proto} {size} {>do} {>bufsize}" {rcode} {>rflags} {rsize} {duration}` + CommonLogFormat = `{remote}:{port} ` + CommonLogEmptyValue + ` {>id} "{type} {class} {name} {proto} {size} {>do} {>bufsize}" {rcode} {>rflags} {rsize} {duration}` + // CommonLogEmptyValue is the common empty log value. + CommonLogEmptyValue = "-" // CombinedLogFormat is the combined log format. CombinedLogFormat = CommonLogFormat + ` "{>opcode}"` // DefaultLogFormat is the default log format. diff --git a/vendor/github.com/coredns/coredns/plugin/log/setup.go b/vendor/github.com/coredns/coredns/plugin/log/setup.go index b9ecb1f72..c3c1af4ca 100644 --- a/vendor/github.com/coredns/coredns/plugin/log/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/log/setup.go @@ -1,11 +1,8 @@ package log import ( - "strings" - "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/plugin" - "github.com/coredns/coredns/plugin/pkg/replacer" "github.com/coredns/coredns/plugin/pkg/response" "github.com/mholt/caddy" @@ -26,7 +23,7 @@ func setup(c *caddy.Controller) error { } dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { - return Logger{Next: next, Rules: rules, repl: replacer.New()} + return Logger{Next: next, Rules: rules, ErrorFunc: dnsserver.DefaultErrorFunc} }) return nil @@ -37,75 +34,62 @@ func logParse(c *caddy.Controller) ([]Rule, error) { for c.Next() { args := c.RemainingArgs() - length := len(rules) - switch len(args) { - case 0: + if len(args) == 0 { // Nothing specified; use defaults rules = append(rules, Rule{ NameScope: ".", Format: DefaultLogFormat, - Class: make(map[response.Class]struct{}), + Class: make(map[response.Class]bool), }) - case 1: + } else if len(args) == 1 { rules = append(rules, Rule{ NameScope: dns.Fqdn(args[0]), Format: DefaultLogFormat, - Class: make(map[response.Class]struct{}), + Class: make(map[response.Class]bool), }) - default: - // Name scopes, and maybe a format specified + } else { + // Name scope, and maybe a format specified format := DefaultLogFormat - if strings.Contains(args[len(args)-1], "{") { - switch args[len(args)-1] { - case "{common}": - format = CommonLogFormat - case "{combined}": - format = CombinedLogFormat - default: - format = args[len(args)-1] - } - - args = args[:len(args)-1] + switch args[1] { + case "{common}": + format = CommonLogFormat + case "{combined}": + format = CombinedLogFormat + default: + format = args[1] } - for _, str := range args { - rules = append(rules, Rule{ - NameScope: dns.Fqdn(str), - Format: format, - Class: make(map[response.Class]struct{}), - }) - } + rules = append(rules, Rule{ + NameScope: dns.Fqdn(args[0]), + Format: format, + Class: make(map[response.Class]bool), + }) } // Class refinements in an extra block. - classes := make(map[response.Class]struct{}) for c.NextBlock() { switch c.Val() { // class followed by combinations of all, denial, error and success. case "class": - classesArgs := c.RemainingArgs() - if len(classesArgs) == 0 { + classes := c.RemainingArgs() + if len(classes) == 0 { return nil, c.ArgErr() } - for _, c := range classesArgs { + for _, c := range classes { cls, err := response.ClassFromString(c) if err != nil { return nil, err } - classes[cls] = struct{}{} + rules[len(rules)-1].Class[cls] = true } default: return nil, c.ArgErr() } } - if len(classes) == 0 { - classes[response.All] = struct{}{} - } - - for i := len(rules) - 1; i >= length; i -= 1 { - rules[i].Class = classes + if len(rules[len(rules)-1].Class) == 0 { + rules[len(rules)-1].Class[response.All] = true } } diff --git a/vendor/github.com/coredns/coredns/plugin/loop/README.md b/vendor/github.com/coredns/coredns/plugin/loop/README.md index c893ed050..9bc51be49 100644 --- a/vendor/github.com/coredns/coredns/plugin/loop/README.md +++ b/vendor/github.com/coredns/coredns/plugin/loop/README.md @@ -7,10 +7,10 @@ ## Description The *loop* plugin will send a random probe query to ourselves and will then keep track of how many times -we see it. If we see it more than twice, we assume CoreDNS has seen a forwarding loop and we halt the process. +we see it. If we see it more than twice, we assume CoreDNS is looping and we halt the process. The plugin will try to send the query for up to 30 seconds. This is done to give CoreDNS enough time -to start up. Once a query has been successfully sent, *loop* disables itself to prevent a query of +to start up. Once a query has been successfully sent *loop* disables itself to prevent a query of death. The query sent is `..zone` with type set to HINFO. @@ -36,56 +36,50 @@ forwards to it self. After CoreDNS has started it stops the process while logging: ~~~ txt -plugin/loop: Loop (127.0.0.1:55953 -> :1053) detected for zone ".", see https://coredns.io/plugins/loop#troubleshooting. Query: "HINFO 4547991504243258144.3688648895315093531." +plugin/loop: Forwarding loop detected in "." zone. Exiting. See https://coredns.io/plugins/loop#troubleshooting. Probe query: "HINFO 5577006791947779410.8674665223082153551.". ~~~ ## Limitations -This plugin only attempts to find simple static forwarding loops at start up time. To detect a loop, -the following must be true: +This plugin only attempts to find simple static forwarding loops at start up time. To detect a loop, all of the following must be true -* the loop must be present at start up time. - -* the loop must occur for the `HINFO` query type. +* the loop must be present at start up time. +* the loop must occur for at least the `HINFO` query type. ## Troubleshooting -When CoreDNS logs contain the message `Loop ... detected ...`, this means that the `loop` detection -plugin has detected an infinite forwarding loop in one of the upstream DNS servers. This is a fatal -error because operating with an infinite loop will consume memory and CPU until eventual out of -memory death by the host. +When CoreDNS logs contain the message `Forwarding loop detected ...`, this means that +the `loop` detection plugin has detected an infinite forwarding loop in one of the upstream +DNS servers. This is a fatal error because operating with an infinite loop will consume +memory and CPU until eventual out of memory death by the host. A forwarding loop is usually caused by: * Most commonly, CoreDNS forwarding requests directly to itself. e.g. via a loopback address such as `127.0.0.1`, `::1` or `127.0.0.53` * Less commonly, CoreDNS forwarding to an upstream server that in turn, forwards requests back to CoreDNS. -To troubleshoot this problem, look in your Corefile for any `forward`s to the zone +To troubleshoot this problem, look in your Corefile for any `proxy` or `forward` to the zone in which the loop was detected. Make sure that they are not forwarding to a local address or -to another DNS server that is forwarding requests back to CoreDNS. If `forward` is -using a file (e.g. `/etc/resolv.conf`), make sure that file does not contain local addresses. +to another DNS server that is forwarding requests back to CoreDNS. If `proxy` or `forward` are + using a file (e.g. `/etc/resolv.conf`), make sure that file does not contain local addresses. ### Troubleshooting Loops In Kubernetes Clusters - When a CoreDNS Pod deployed in Kubernetes detects a loop, the CoreDNS Pod will start to "CrashLoopBackOff". This is because Kubernetes will try to restart the Pod every time CoreDNS detects the loop and exits. -A common cause of forwarding loops in Kubernetes clusters is an interaction with a local DNS cache -on the host node (e.g. `systemd-resolved`). For example, in certain configurations `systemd-resolved` will -put the loopback address `127.0.0.53` as a nameserver into `/etc/resolv.conf`. Kubernetes (via `kubelet`) by default -will pass this `/etc/resolv.conf` file to all Pods using the `default` dnsPolicy rendering them -unable to make DNS lookups (this includes CoreDNS Pods). CoreDNS uses this `/etc/resolv.conf` -as a list of upstreams to forward requests to. Since it contains a loopback address, CoreDNS ends up forwarding +A common cause of forwarding loops in Kubernetes clusters is an interaction with +`systemd-resolved` on the host node. `systemd-resolved` will, in certain configurations, +put `127.0.0.53` as an upstream into `/etc/resolv.conf`. Kubernetes (`kubelet`) by default +will pass this `/etc/resolv/conf` file to all Pods using the `default` dnsPolicy (this +includes CoreDNS Pods). CoreDNS then uses this `/etc/resolv.conf` as a list of upstreams +to proxy/forward requests to. Since it contains a local address, CoreDNS ends up forwarding requests to itself. There are many ways to work around this issue, some are listed here: -* Add the following to `kubelet`: `--resolv-conf `. Your "real" - `resolv.conf` is the one that contains the actual IPs of your upstream servers, and no local/loopback address. - This flag tells `kubelet` to pass an alternate `resolv.conf` to Pods. For systems using `systemd-resolved`, -`/run/systemd/resolve/resolv.conf` is typically the location of the "real" `resolv.conf`, -although this can be different depending on your distribution. -* Disable the local DNS cache on host nodes, and restore `/etc/resolv.conf` to the original. -* A quick and dirty fix is to edit your Corefile, replacing `forward . /etc/resolv.conf` with -the ip address of your upstream DNS, for example `forward . 8.8.8.8`. But this only fixes the issue for CoreDNS, -kubelet will continue to forward the invalid `resolv.conf` to all `default` dnsPolicy Pods, leaving them unable to resolve DNS. +* Add the following to `kubelet`: `--resolv-conf /run/systemd/resolve/resolv.conf`. This flag +tells `kubelet` to pass an alternate `resolv.conf` to Pods. For `systemd-resolved`, +`/run/systemd/resolve/resolv.conf` is typically the location of the "original" `/etc/resolv.conf`. +* Disable `systemd-resolved` on host nodes, and restore `/etc/resolv.conf` to the original. +* A quick and dirty fix is to edit your Corefile, replacing `proxy . /etc/resolv.conf` with +the ip address of your upstream DNS, for example `proxy . 8.8.8.8`. diff --git a/vendor/github.com/coredns/coredns/plugin/loop/loop.go b/vendor/github.com/coredns/coredns/plugin/loop/loop.go index 8d29798ad..45bdc4267 100644 --- a/vendor/github.com/coredns/coredns/plugin/loop/loop.go +++ b/vendor/github.com/coredns/coredns/plugin/loop/loop.go @@ -19,7 +19,6 @@ type Loop struct { zone string qname string - addr string sync.RWMutex i int @@ -50,7 +49,7 @@ func (l *Loop) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) ( } if l.seen() > 2 { - log.Fatalf(`Loop (%s -> %s) detected for zone %q, see https://coredns.io/plugins/loop#troubleshooting. Query: "HINFO %s"`, state.RemoteAddr(), l.address(), l.zone, l.qname) + log.Fatalf("Forwarding loop detected in \"%s\" zone. Exiting. See https://coredns.io/plugins/loop#troubleshooting. Probe query: \"HINFO %s\".", l.zone, l.qname) } return plugin.NextOrFailure(l.Name(), l.Next, ctx, w, r) @@ -95,15 +94,3 @@ func (l *Loop) disabled() bool { defer l.RUnlock() return l.off } - -func (l *Loop) setAddress(addr string) { - l.Lock() - defer l.Unlock() - l.addr = addr -} - -func (l *Loop) address() string { - l.RLock() - defer l.RUnlock() - return l.addr -} diff --git a/vendor/github.com/coredns/coredns/plugin/loop/setup.go b/vendor/github.com/coredns/coredns/plugin/loop/setup.go index db6821341..ba4681cb4 100644 --- a/vendor/github.com/coredns/coredns/plugin/loop/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/loop/setup.go @@ -41,7 +41,6 @@ func setup(c *caddy.Controller) error { addr := net.JoinHostPort(lh, conf.Port) for time.Now().Before(deadline) { - l.setAddress(addr) if _, err := l.exchange(addr); err != nil { l.reset() time.Sleep(1 * time.Second) diff --git a/vendor/github.com/coredns/coredns/plugin/metrics/README.md b/vendor/github.com/coredns/coredns/plugin/metrics/README.md index 5aec4e97b..b3fbc111f 100644 --- a/vendor/github.com/coredns/coredns/plugin/metrics/README.md +++ b/vendor/github.com/coredns/coredns/plugin/metrics/README.md @@ -19,7 +19,6 @@ The following metrics are exported: * `coredns_dns_request_type_count_total{server, zone, type}` - counter of queries per zone and type. * `coredns_dns_response_size_bytes{server, zone, proto}` - response size in bytes. * `coredns_dns_response_rcode_count_total{server, zone, rcode}` - response per zone and rcode. -* `coredns_plugin_enabled{server, zone, name}` - indicates whether a plugin is enabled on per server and zone basis. Each counter has a label `zone` which is the zonename used for the request/response. @@ -48,12 +47,12 @@ prometheus [ADDRESS] For each zone that you want to see metrics for. -It optionally takes a bind address to which the metrics are exported; the default -listens on `localhost:9153`. The metrics path is fixed to `/metrics`. +It optionally takes an address to which the metrics are exported; the default +is `localhost:9153`. The metrics path is fixed to `/metrics`. ## Examples -Use an alternative listening address: +Use an alternative address: ~~~ corefile . { @@ -76,4 +75,3 @@ When reloading, the Prometheus handler is stopped before the new server instance If that new server fails to start, then the initial server instance is still available and DNS queries still served, but Prometheus handler stays down. Prometheus will not reply HTTP request until a successful reload or a complete restart of CoreDNS. -Only the plugins that register as Handler are visible in `coredns_plugin_enabled{server, zone, name}`. As of today the plugins reload and bind will not be reported. diff --git a/vendor/github.com/coredns/coredns/plugin/metrics/context.go b/vendor/github.com/coredns/coredns/plugin/metrics/context.go index da6bdb12d..7ee25ef4a 100644 --- a/vendor/github.com/coredns/coredns/plugin/metrics/context.go +++ b/vendor/github.com/coredns/coredns/plugin/metrics/context.go @@ -3,7 +3,7 @@ package metrics import ( "context" - "github.com/coredns/coredns/core/dnsserver" + "github.com/coredns/coredns/plugin/metrics/vars" ) // WithServer returns the current server handling the request. It returns the @@ -15,10 +15,4 @@ import ( // Basic usage with a metric: // // .WithLabelValues(metrics.WithServer(ctx), labels..).Add(1) -func WithServer(ctx context.Context) string { - srv := ctx.Value(dnsserver.Key{}) - if srv == nil { - return "" - } - return srv.(*dnsserver.Server).Addr -} +func WithServer(ctx context.Context) string { return vars.WithServer(ctx) } diff --git a/vendor/github.com/coredns/coredns/plugin/metrics/handler.go b/vendor/github.com/coredns/coredns/plugin/metrics/handler.go index 0dbc053da..71132c274 100644 --- a/vendor/github.com/coredns/coredns/plugin/metrics/handler.go +++ b/vendor/github.com/coredns/coredns/plugin/metrics/handler.go @@ -26,7 +26,7 @@ func (m *Metrics) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg rw := dnstest.NewRecorder(w) status, err := plugin.NextOrFailure(m.Name(), m.Next, ctx, rw, r) - vars.Report(WithServer(ctx), state, zone, rcode.ToString(rw.Rcode), rw.Len, rw.Start) + vars.Report(ctx, state, zone, rcode.ToString(rw.Rcode), rw.Len, rw.Start) return status, err } diff --git a/vendor/github.com/coredns/coredns/plugin/metrics/metrics.go b/vendor/github.com/coredns/coredns/plugin/metrics/metrics.go index 89c948aa6..6b496cccc 100644 --- a/vendor/github.com/coredns/coredns/plugin/metrics/metrics.go +++ b/vendor/github.com/coredns/coredns/plugin/metrics/metrics.go @@ -5,6 +5,7 @@ import ( "context" "net" "net/http" + "os" "sync" "time" @@ -26,7 +27,7 @@ type Metrics struct { srv *http.Server zoneNames []string - zoneMap map[string]struct{} + zoneMap map[string]bool zoneMu sync.RWMutex } @@ -35,11 +36,11 @@ func New(addr string) *Metrics { met := &Metrics{ Addr: addr, Reg: prometheus.NewRegistry(), - zoneMap: make(map[string]struct{}), + zoneMap: make(map[string]bool), } // Add the default collectors met.MustRegister(prometheus.NewGoCollector()) - met.MustRegister(prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{})) + met.MustRegister(prometheus.NewProcessCollector(os.Getpid(), "")) // Add all of our collectors met.MustRegister(buildInfo) @@ -51,7 +52,6 @@ func New(addr string) *Metrics { met.MustRegister(vars.RequestType) met.MustRegister(vars.ResponseSize) met.MustRegister(vars.ResponseRcode) - met.MustRegister(vars.PluginEnabled) return met } @@ -70,7 +70,7 @@ func (m *Metrics) MustRegister(c prometheus.Collector) { // AddZone adds zone z to m. func (m *Metrics) AddZone(z string) { m.zoneMu.Lock() - m.zoneMap[z] = struct{}{} + m.zoneMap[z] = true m.zoneNames = keys(m.zoneMap) m.zoneMu.Unlock() } @@ -141,7 +141,7 @@ func (m *Metrics) OnFinalShutdown() error { return m.stopServer() } -func keys(m map[string]struct{}) []string { +func keys(m map[string]bool) []string { sx := []string{} for k := range m { sx = append(sx, k) diff --git a/vendor/github.com/coredns/coredns/plugin/metrics/setup.go b/vendor/github.com/coredns/coredns/plugin/metrics/setup.go index b50960211..ffc0466f3 100644 --- a/vendor/github.com/coredns/coredns/plugin/metrics/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/metrics/setup.go @@ -7,7 +7,6 @@ import ( "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/coremain" "github.com/coredns/coredns/plugin" - "github.com/coredns/coredns/plugin/metrics/vars" clog "github.com/coredns/coredns/plugin/pkg/log" "github.com/coredns/coredns/plugin/pkg/uniq" @@ -51,23 +50,6 @@ func setup(c *caddy.Controller) error { return nil }) - c.OnRestart(func() error { - vars.PluginEnabled.Reset() - return nil - }) - - c.OnStartup(func() error { - conf := dnsserver.GetConfig(c) - plugins := conf.Handlers() - for _, h := range conf.ListenHosts { - addrstr := conf.Transport + "://" + net.JoinHostPort(h, conf.Port) - for _, p := range plugins { - vars.PluginEnabled.WithLabelValues(addrstr, conf.Zone, p.Name()).Set(1) - } - } - return nil - - }) c.OnRestart(m.OnRestart) c.OnFinalShutdown(m.OnFinalShutdown) diff --git a/vendor/github.com/coredns/coredns/plugin/metrics/vars/report.go b/vendor/github.com/coredns/coredns/plugin/metrics/vars/report.go index 343d98a22..d0c64b864 100644 --- a/vendor/github.com/coredns/coredns/plugin/metrics/vars/report.go +++ b/vendor/github.com/coredns/coredns/plugin/metrics/vars/report.go @@ -1,17 +1,17 @@ package vars import ( + "context" "time" + "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/request" "github.com/miekg/dns" ) -// Report reports the metrics data associated with request. This function is exported because it is also -// called from core/dnsserver to report requests hitting the server that should not be handled and are thus -// not sent down the plugin chain. -func Report(server string, req request.Request, zone, rcode string, size int, start time.Time) { +// Report reports the metrics data associated with request. +func Report(ctx context.Context, req request.Request, zone, rcode string, size int, start time.Time) { // Proto and Family. net := req.Proto() fam := "1" @@ -19,6 +19,8 @@ func Report(server string, req request.Request, zone, rcode string, size int, st fam = "2" } + server := WithServer(ctx) + typ := req.QType() RequestCount.WithLabelValues(server, zone, net, fam).Inc() RequestDuration.WithLabelValues(server, zone).Observe(time.Since(start).Seconds()) @@ -39,25 +41,34 @@ func Report(server string, req request.Request, zone, rcode string, size int, st ResponseRcode.WithLabelValues(server, zone, rcode).Inc() } -var monitorType = map[uint16]struct{}{ - dns.TypeAAAA: struct{}{}, - dns.TypeA: struct{}{}, - dns.TypeCNAME: struct{}{}, - dns.TypeDNSKEY: struct{}{}, - dns.TypeDS: struct{}{}, - dns.TypeMX: struct{}{}, - dns.TypeNSEC3: struct{}{}, - dns.TypeNSEC: struct{}{}, - dns.TypeNS: struct{}{}, - dns.TypePTR: struct{}{}, - dns.TypeRRSIG: struct{}{}, - dns.TypeSOA: struct{}{}, - dns.TypeSRV: struct{}{}, - dns.TypeTXT: struct{}{}, +// WithServer returns the current server handling the request. +func WithServer(ctx context.Context) string { + srv := ctx.Value(plugin.ServerCtx{}) + if srv == nil { + return "" + } + return srv.(string) +} + +var monitorType = map[uint16]bool{ + dns.TypeAAAA: true, + dns.TypeA: true, + dns.TypeCNAME: true, + dns.TypeDNSKEY: true, + dns.TypeDS: true, + dns.TypeMX: true, + dns.TypeNSEC3: true, + dns.TypeNSEC: true, + dns.TypeNS: true, + dns.TypePTR: true, + dns.TypeRRSIG: true, + dns.TypeSOA: true, + dns.TypeSRV: true, + dns.TypeTXT: true, // Meta Qtypes - dns.TypeIXFR: struct{}{}, - dns.TypeAXFR: struct{}{}, - dns.TypeANY: struct{}{}, + dns.TypeIXFR: true, + dns.TypeAXFR: true, + dns.TypeANY: true, } const other = "other" diff --git a/vendor/github.com/coredns/coredns/plugin/metrics/vars/vars.go b/vendor/github.com/coredns/coredns/plugin/metrics/vars/vars.go index 3adee1d76..a25a0894c 100644 --- a/vendor/github.com/coredns/coredns/plugin/metrics/vars/vars.go +++ b/vendor/github.com/coredns/coredns/plugin/metrics/vars/vars.go @@ -65,12 +65,6 @@ var ( Name: "panic_count_total", Help: "A metrics that counts the number of panics.", }) - - PluginEnabled = prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Namespace: plugin.Namespace, - Name: "plugin_enabled", - Help: "A metric that indicates whether a plugin is enabled on per server and zone basis.", - }, []string{"server", "zone", "name"}) ) const ( diff --git a/vendor/github.com/coredns/coredns/plugin/normalize.go b/vendor/github.com/coredns/coredns/plugin/normalize.go index 6402bf8d9..dc295ce01 100644 --- a/vendor/github.com/coredns/coredns/plugin/normalize.go +++ b/vendor/github.com/coredns/coredns/plugin/normalize.go @@ -15,8 +15,8 @@ import ( // Zones respresents a lists of zone names. type Zones []string -// Matches checks if qname is a subdomain of any of the zones in z. The match -// will return the most specific zones that matches. The empty string +// Matches checks is qname is a subdomain of any of the zones in z. The match +// will return the most specific zones that matches other. The empty string // signals a not found condition. func (z Zones) Matches(qname string) string { zone := "" diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/edns/edns.go b/vendor/github.com/coredns/coredns/plugin/pkg/edns/edns.go index 68fb03865..3f0ea5e16 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/edns/edns.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/edns/edns.go @@ -3,37 +3,10 @@ package edns import ( "errors" - "sync" "github.com/miekg/dns" ) -var sup = &supported{m: make(map[uint16]struct{})} - -type supported struct { - m map[uint16]struct{} - sync.RWMutex -} - -// SetSupportedOption adds a new supported option the set of EDNS0 options that we support. Plugins typically call -// this in their setup code to signal support for a new option. -// By default we support: -// dns.EDNS0NSID, dns.EDNS0EXPIRE, dns.EDNS0COOKIE, dns.EDNS0TCPKEEPALIVE, dns.EDNS0PADDING. These -// values are not in this map and checked directly in the server. -func SetSupportedOption(option uint16) { - sup.Lock() - sup.m[option] = struct{}{} - sup.Unlock() -} - -// SupportedOption returns true if the option code is supported as an extra EDNS0 option. -func SupportedOption(option uint16) bool { - sup.RLock() - _, ok := sup.m[option] - sup.RUnlock() - return ok -} - // Version checks the EDNS version in the request. If error // is nil everything is OK and we can invoke the plugin. If non-nil, the // returned Msg is valid to be returned to the client (and should). For some @@ -55,7 +28,6 @@ func Version(req *dns.Msg) (*dns.Msg, error) { o.Hdr.Name = "." o.Hdr.Rrtype = dns.TypeOPT o.SetVersion(0) - m.Rcode = dns.RcodeBadVers o.SetExtendedRcode(dns.RcodeBadVers) m.Extra = []dns.RR{o} diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/log/log.go b/vendor/github.com/coredns/coredns/plugin/pkg/log/log.go index b4bd1cad8..2c8971e1d 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/log/log.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/log/log.go @@ -20,7 +20,7 @@ import ( var D bool // RFC3339Milli doesn't exist, invent it here. -func clock() string { return time.Now().Format("2006-01-02T15:04:05.000Z07:00") } +func clock() string { return time.Now().Format("2006-01-02T15:04:05.999Z07:00") } // logf calls log.Printf prefixed with level. func logf(level, format string, v ...interface{}) { diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/log/plugin.go b/vendor/github.com/coredns/coredns/plugin/pkg/log/plugin.go index 0cc5f881a..1df302609 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/log/plugin.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/log/plugin.go @@ -2,6 +2,7 @@ package log import ( "fmt" + golog "log" "os" ) @@ -12,14 +13,16 @@ type P struct { // NewWithPlugin returns a logger that includes "plugin/name: " in the log message. // I.e [INFO] plugin/: message. -func NewWithPlugin(name string) P { return P{"plugin/" + name + ": "} } +func NewWithPlugin(name string) P { return P{name} } func (p P) logf(level, format string, v ...interface{}) { - log(level, p.plugin, fmt.Sprintf(format, v...)) + s := level + pFormat(p.plugin) + fmt.Sprintf(format, v...) + golog.Print(s) } func (p P) log(level string, v ...interface{}) { - log(level+p.plugin, v...) + s := level + pFormat(p.plugin) + fmt.Sprint(v...) + golog.Print(s) } // Debug logs as log.Debug. @@ -61,3 +64,5 @@ func (p P) Fatal(v ...interface{}) { p.log(fatal, v...); os.Exit(1) } // Fatalf logs as log.Fatalf and calls os.Exit(1). func (p P) Fatalf(format string, v ...interface{}) { p.logf(fatal, format, v...); os.Exit(1) } + +func pFormat(s string) string { return "plugin/" + s + ": " } diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/replacer/replacer.go b/vendor/github.com/coredns/coredns/plugin/pkg/replacer/replacer.go index 3e2cbfcbd..695e9e392 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/replacer/replacer.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/replacer/replacer.go @@ -1,146 +1,107 @@ package replacer import ( - "context" "strconv" "strings" "time" - "github.com/coredns/coredns/plugin/metadata" "github.com/coredns/coredns/plugin/pkg/dnstest" "github.com/coredns/coredns/request" "github.com/miekg/dns" ) -// Replacer replaces labels for values in strings. -type Replacer struct { - valueFunc func(request.Request, *dnstest.Recorder, string) string - labels []string +// Replacer is a type which can replace placeholder +// substrings in a string with actual values from a +// dns.Msg and responseRecorder. Always use +// NewReplacer to get one of these. +type Replacer interface { + Replace(string) string + Set(key, value string) } -// labels are all supported labels that can be used in the default Replacer. -var labels = []string{ - "{type}", - "{name}", - "{class}", - "{proto}", - "{size}", - "{remote}", - "{port}", - "{local}", - // Header values. - headerReplacer + "id}", - headerReplacer + "opcode}", - headerReplacer + "do}", - headerReplacer + "bufsize}", - // Recorded replacements. - "{rcode}", - "{rsize}", - "{duration}", - headerReplacer + "rflags}", +type replacer struct { + replacements map[string]string + emptyValue string } -// value returns the current value of label. -func value(state request.Request, rr *dnstest.Recorder, label string) string { - switch label { - case "{type}": - return state.Type() - case "{name}": - return state.Name() - case "{class}": - return state.Class() - case "{proto}": - return state.Proto() - case "{size}": - return strconv.Itoa(state.Req.Len()) - case "{remote}": - return addrToRFC3986(state.IP()) - case "{port}": - return state.Port() - case "{local}": - return addrToRFC3986(state.LocalIP()) - // Header placeholders (case-insensitive). - case headerReplacer + "id}": - return strconv.Itoa(int(state.Req.Id)) - case headerReplacer + "opcode}": - return strconv.Itoa(state.Req.Opcode) - case headerReplacer + "do}": - return boolToString(state.Do()) - case headerReplacer + "bufsize}": - return strconv.Itoa(state.Size()) - // Recorded replacements. - case "{rcode}": - if rr == nil { - return EmptyValue - } +// New makes a new replacer based on r and rr. +// Do not create a new replacer until r and rr have all +// the needed values, because this function copies those +// values into the replacer. rr may be nil if it is not +// available. emptyValue should be the string that is used +// in place of empty string (can still be empty string). +func New(r *dns.Msg, rr *dnstest.Recorder, emptyValue string) Replacer { + req := request.Request{W: rr, Req: r} + rep := replacer{ + replacements: map[string]string{ + "{type}": req.Type(), + "{name}": req.Name(), + "{class}": req.Class(), + "{proto}": req.Proto(), + "{when}": "", // made a noop + "{size}": strconv.Itoa(req.Len()), + "{remote}": addrToRFC3986(req.IP()), + "{port}": req.Port(), + }, + emptyValue: emptyValue, + } + if rr != nil { rcode := dns.RcodeToString[rr.Rcode] if rcode == "" { rcode = strconv.Itoa(rr.Rcode) } - return rcode - case "{rsize}": - if rr == nil { - return EmptyValue - } - return strconv.Itoa(rr.Len) - case "{duration}": - if rr == nil { - return EmptyValue + rep.replacements["{rcode}"] = rcode + rep.replacements["{rsize}"] = strconv.Itoa(rr.Len) + rep.replacements["{duration}"] = strconv.FormatFloat(time.Since(rr.Start).Seconds(), 'f', -1, 64) + "s" + if rr.Msg != nil { + rep.replacements[headerReplacer+"rflags}"] = flagsToString(rr.Msg.MsgHdr) } - return strconv.FormatFloat(time.Since(rr.Start).Seconds(), 'f', -1, 64) + "s" - case headerReplacer + "rflags}": - if rr != nil && rr.Msg != nil { - return flagsToString(rr.Msg.MsgHdr) - } - return EmptyValue } - return EmptyValue -} -// New makes a new replacer. This only needs to be called once in the setup and then call Replace for each incoming message. -// A replacer is safe for concurrent use. -func New() Replacer { - return Replacer{ - valueFunc: value, - labels: labels, - } -} + // Header placeholders (case-insensitive) + rep.replacements[headerReplacer+"id}"] = strconv.Itoa(int(r.Id)) + rep.replacements[headerReplacer+"opcode}"] = strconv.Itoa(r.Opcode) + rep.replacements[headerReplacer+"do}"] = boolToString(req.Do()) + rep.replacements[headerReplacer+"bufsize}"] = strconv.Itoa(req.Size()) -// Replace performs a replacement of values on s and returns the string with the replaced values. -func (r Replacer) Replace(ctx context.Context, state request.Request, rr *dnstest.Recorder, s string) string { - for _, placeholder := range r.labels { - if strings.Contains(s, placeholder) { - s = strings.Replace(s, placeholder, r.valueFunc(state, rr, placeholder), -1) - } - } + return rep +} - // Metadata label replacements. Scan for {/ and search for next }, replace that metadata label with - // any meta data that is available. - b := strings.Builder{} - for strings.Contains(s, labelReplacer) { - idxStart := strings.Index(s, labelReplacer) - endOffset := idxStart + len(labelReplacer) +// Replace performs a replacement of values on s and returns +// the string with the replaced values. +func (r replacer) Replace(s string) string { + // Header replacements - these are case-insensitive, so we can't just use strings.Replace() + for strings.Contains(s, headerReplacer) { + idxStart := strings.Index(s, headerReplacer) + endOffset := idxStart + len(headerReplacer) idxEnd := strings.Index(s[endOffset:], "}") if idxEnd > -1 { - label := s[idxStart+2 : endOffset+idxEnd] - - fm := metadata.ValueFunc(ctx, label) - replacement := EmptyValue - if fm != nil { - replacement = fm() + placeholder := strings.ToLower(s[idxStart : endOffset+idxEnd+1]) + replacement := r.replacements[placeholder] + if replacement == "" { + replacement = r.emptyValue } - - b.WriteString(s[:idxStart]) - b.WriteString(replacement) - s = s[endOffset+idxEnd+1:] + s = s[:idxStart] + replacement + s[endOffset+idxEnd+1:] } else { break } } - b.WriteString(s) - return b.String() + // Regular replacements - these are easier because they're case-sensitive + for placeholder, replacement := range r.replacements { + if replacement == "" { + replacement = r.emptyValue + } + s = strings.Replace(s, placeholder, replacement, -1) + } + + return s +} + +// Set sets key to value in the replacements map. +func (r replacer) Set(key, value string) { + r.replacements["{"+key+"}"] = value } func boolToString(b bool) string { @@ -200,9 +161,4 @@ func addrToRFC3986(addr string) string { return addr } -const ( - headerReplacer = "{>" - labelReplacer = "{/" - // EmptyValue is the default empty value. - EmptyValue = "-" -) +const headerReplacer = "{>" diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/up/up.go b/vendor/github.com/coredns/coredns/plugin/pkg/up/up.go index 8f866311b..e2c9fe2cd 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/up/up.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/up/up.go @@ -9,12 +9,10 @@ import ( // Probe is used to run a single Func until it returns true (indicating a target is healthy). If an Func // is already in progress no new one will be added, i.e. there is always a maximum of 1 checks in flight. -// When failures start to happen we will back off every second failure up to maximum of 4 intervals. type Probe struct { sync.Mutex inprogress int interval time.Duration - max time.Duration } // Func is used to determine if a target is alive. If so this function must return nil. @@ -36,22 +34,17 @@ func (p *Probe) Do(f Func) { // Passed the lock. Now run f for as long it returns false. If a true is returned // we return from the goroutine and we can accept another Func to run. go func() { - i := 1 for { if err := f(); err == nil { break } time.Sleep(interval) - if i%2 == 0 && i < 4 { // 4 is 2 doubles, so no need to increase anymore - this is *also* checked in double() - p.double() - } p.Lock() if p.inprogress == stop { p.Unlock() return } p.Unlock() - i++ } p.Lock() @@ -60,15 +53,6 @@ func (p *Probe) Do(f Func) { }() } -func (p *Probe) double() { - p.Lock() - p.interval *= 2 - if p.interval > p.max { - p.interval = p.max - } - p.Unlock() -} - // Stop stops the probing. func (p *Probe) Stop() { p.Lock() @@ -77,10 +61,12 @@ func (p *Probe) Stop() { } // Start will initialize the probe manager, after which probes can be initiated with Do. -func (p *Probe) Start(interval time.Duration) { +func (p *Probe) Start(interval time.Duration) { p.SetInterval(interval) } + +// SetInterval sets the probing interval to be used by upcoming probes initiated with Do. +func (p *Probe) SetInterval(interval time.Duration) { p.Lock() p.interval = interval - p.max = interval * multiplier p.Unlock() } @@ -88,6 +74,4 @@ const ( idle = iota active stop - - multiplier = 4 ) diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/watch/watch.go b/vendor/github.com/coredns/coredns/plugin/pkg/watch/watch.go new file mode 100644 index 000000000..7e77bb7b3 --- /dev/null +++ b/vendor/github.com/coredns/coredns/plugin/pkg/watch/watch.go @@ -0,0 +1,23 @@ +package watch + +// Chan is used to inform the server of a change. Whenever +// a watched FQDN has a change in data, that FQDN should be +// sent down this channel. +type Chan chan string + +// Watchable is the interface watchable plugins should implement +type Watchable interface { + // Name returns the plugin name. + Name() string + + // SetWatchChan is called when the watch channel is created. + SetWatchChan(Chan) + + // Watch is called whenever a watch is created for a FQDN. Plugins + // should send the FQDN down the watch channel when its data may have + // changed. This is an exact match only. + Watch(qname string) error + + // StopWatching is called whenever all watches are canceled for a FQDN. + StopWatching(qname string) +} diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/watch/watcher.go b/vendor/github.com/coredns/coredns/plugin/pkg/watch/watcher.go new file mode 100644 index 000000000..86a952db2 --- /dev/null +++ b/vendor/github.com/coredns/coredns/plugin/pkg/watch/watcher.go @@ -0,0 +1,178 @@ +package watch + +import ( + "fmt" + "io" + "sync" + + "github.com/miekg/dns" + + "github.com/coredns/coredns/pb" + "github.com/coredns/coredns/plugin" + "github.com/coredns/coredns/plugin/pkg/log" + "github.com/coredns/coredns/request" +) + +// Watcher handles watch creation, cancellation, and processing. +type Watcher interface { + // Watch monitors a client stream and creates and cancels watches. + Watch(pb.DnsService_WatchServer) error + + // Stop cancels open watches and stops the watch processing go routine. + Stop() +} + +// Manager contains all the data needed to manage watches +type Manager struct { + changes Chan + stopper chan bool + counter int64 + watches map[string]watchlist + plugins []Watchable + mutex sync.Mutex +} + +type watchlist map[int64]pb.DnsService_WatchServer + +// NewWatcher creates a Watcher, which is used to manage watched names. +func NewWatcher(plugins []Watchable) *Manager { + w := &Manager{changes: make(Chan), stopper: make(chan bool), watches: make(map[string]watchlist), plugins: plugins} + + for _, p := range plugins { + p.SetWatchChan(w.changes) + } + + go w.process() + return w +} + +func (w *Manager) nextID() int64 { + w.mutex.Lock() + + w.counter++ + id := w.counter + + w.mutex.Unlock() + return id +} + +// Watch monitors a client stream and creates and cancels watches. +func (w *Manager) Watch(stream pb.DnsService_WatchServer) error { + for { + in, err := stream.Recv() + if err == io.EOF { + return nil + } + if err != nil { + return err + } + create := in.GetCreateRequest() + if create != nil { + msg := new(dns.Msg) + err := msg.Unpack(create.Query.Msg) + if err != nil { + log.Warningf("Could not decode watch request: %s\n", err) + stream.Send(&pb.WatchResponse{Err: "could not decode request"}) + continue + } + id := w.nextID() + if err := stream.Send(&pb.WatchResponse{WatchId: id, Created: true}); err != nil { + // if we fail to notify client of watch creation, don't create the watch + continue + } + + // Normalize qname + qname := (&request.Request{Req: msg}).Name() + + w.mutex.Lock() + if _, ok := w.watches[qname]; !ok { + w.watches[qname] = make(watchlist) + } + w.watches[qname][id] = stream + w.mutex.Unlock() + + for _, p := range w.plugins { + err := p.Watch(qname) + if err != nil { + log.Warningf("Failed to start watch for %s in plugin %s: %s\n", qname, p.Name(), err) + stream.Send(&pb.WatchResponse{Err: fmt.Sprintf("failed to start watch for %s in plugin %s", qname, p.Name())}) + } + } + continue + } + + cancel := in.GetCancelRequest() + if cancel != nil { + w.mutex.Lock() + for qname, wl := range w.watches { + ws, ok := wl[cancel.WatchId] + if !ok { + continue + } + + // only allow cancels from the client that started it + // TODO: test what happens if a stream tries to cancel a watchID that it doesn't own + if ws != stream { + continue + } + + delete(wl, cancel.WatchId) + + // if there are no more watches for this qname, we should tell the plugins + if len(wl) == 0 { + for _, p := range w.plugins { + p.StopWatching(qname) + } + delete(w.watches, qname) + } + + // let the client know we canceled the watch + stream.Send(&pb.WatchResponse{WatchId: cancel.WatchId, Canceled: true}) + } + w.mutex.Unlock() + continue + } + } +} + +func (w *Manager) process() { + for { + select { + case <-w.stopper: + return + case changed := <-w.changes: + w.mutex.Lock() + for qname, wl := range w.watches { + if plugin.Zones([]string{changed}).Matches(qname) == "" { + continue + } + for id, stream := range wl { + wr := pb.WatchResponse{WatchId: id, Qname: qname} + err := stream.Send(&wr) + if err != nil { + log.Warningf("Error sending change for %s to watch %d: %s. Removing watch.\n", qname, id, err) + delete(w.watches[qname], id) + } + } + } + w.mutex.Unlock() + } + } +} + +// Stop cancels open watches and stops the watch processing go routine. +func (w *Manager) Stop() { + w.stopper <- true + w.mutex.Lock() + for wn, wl := range w.watches { + for id, stream := range wl { + wr := pb.WatchResponse{WatchId: id, Canceled: true} + err := stream.Send(&wr) + if err != nil { + log.Warningf("Error notifying client of cancellation: %s\n", err) + } + } + delete(w.watches, wn) + } + w.mutex.Unlock() +} diff --git a/vendor/github.com/coredns/coredns/plugin/plugin.go b/vendor/github.com/coredns/coredns/plugin/plugin.go index 4a4448f62..65bf939f1 100644 --- a/vendor/github.com/coredns/coredns/plugin/plugin.go +++ b/vendor/github.com/coredns/coredns/plugin/plugin.go @@ -68,7 +68,7 @@ func (f HandlerFunc) Name() string { return "handlerfunc" } // Error returns err with 'plugin/name: ' prefixed to it. func Error(name string, err error) error { return fmt.Errorf("%s/%s: %s", "plugin", name, err) } -// NextOrFailure calls next.ServeDNS when next is not nil, otherwise it will return, a ServerFailure and a nil error. +// NextOrFailure calls next.ServeDNS when next is not nill, otherwise it will return, a ServerFailure and a nil error. func NextOrFailure(name string, next Handler, ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { // nolint: golint if next != nil { if span := ot.SpanFromContext(ctx); span != nil { @@ -106,3 +106,6 @@ var TimeBuckets = prometheus.ExponentialBuckets(0.00025, 2, 16) // from 0.25ms t // ErrOnce is returned when a plugin doesn't support multiple setups per server. var ErrOnce = errors.New("this plugin can only be used once per Server Block") + +// ServerCtx is the context key to pass server address context to the plugins handling the request. +type ServerCtx struct{} diff --git a/vendor/github.com/coredns/coredns/plugin/reload/README.md b/vendor/github.com/coredns/coredns/plugin/reload/README.md index 8d1f5db64..625e4b025 100644 --- a/vendor/github.com/coredns/coredns/plugin/reload/README.md +++ b/vendor/github.com/coredns/coredns/plugin/reload/README.md @@ -88,10 +88,3 @@ After the aborted attempt to reload we are left with the old processes running, closed in step 1; so the health endpoint is broken. The same can hopen in the prometheus metrics plugin. In general be careful with assigning new port and expecting reload to work fully. - -Also any `import` statement is not discovered by this plugin. This means if any of these imported files -changes the *reload* plugin is ignorant of that fact. - -## Also See - -See coredns-import(7) and corefile(5). diff --git a/vendor/github.com/coredns/coredns/plugin/reload/reload.go b/vendor/github.com/coredns/coredns/plugin/reload/reload.go index 3abc33835..d04bb037b 100644 --- a/vendor/github.com/coredns/coredns/plugin/reload/reload.go +++ b/vendor/github.com/coredns/coredns/plugin/reload/reload.go @@ -2,7 +2,6 @@ package reload import ( "crypto/md5" - "sync" "time" "github.com/mholt/caddy" @@ -16,34 +15,9 @@ const ( ) type reload struct { - dur time.Duration - u int - mtx sync.RWMutex - quit chan bool -} - -func (r *reload) setUsage(u int) { - r.mtx.Lock() - defer r.mtx.Unlock() - r.u = u -} - -func (r *reload) usage() int { - r.mtx.RLock() - defer r.mtx.RUnlock() - return r.u -} - -func (r *reload) setInterval(i time.Duration) { - r.mtx.Lock() - defer r.mtx.Unlock() - r.dur = i -} - -func (r *reload) interval() time.Duration { - r.mtx.RLock() - defer r.mtx.RUnlock() - return r.dur + interval time.Duration + usage int + quit chan bool } func hook(event caddy.EventName, info interface{}) error { @@ -54,7 +28,7 @@ func hook(event caddy.EventName, info interface{}) error { // if reload is removed from the Corefile, then the hook // is still registered but setup is never called again // so we need a flag to tell us not to reload - if r.usage() == unused { + if r.usage == unused { return nil } @@ -64,7 +38,7 @@ func hook(event caddy.EventName, info interface{}) error { log.Infof("Running configuration MD5 = %x\n", md5sum) go func() { - tick := time.NewTicker(r.interval()) + tick := time.NewTicker(r.interval) for { select { @@ -79,15 +53,15 @@ func hook(event caddy.EventName, info interface{}) error { md5sum = s // now lets consider that plugin will not be reload, unless appear in next config file // change status iof usage will be reset in setup if the plugin appears in config file - r.setUsage(maybeUsed) + r.usage = maybeUsed _, err := instance.Restart(corefile) if err != nil { - log.Errorf("Corefile changed but reload failed: %s", err) + log.Errorf("Corefile changed but reload failed: %s\n", err) continue } // we are done, if the plugin was not set used, then it is not. - if r.usage() == maybeUsed { - r.setUsage(unused) + if r.usage == maybeUsed { + r.usage = unused } return } diff --git a/vendor/github.com/coredns/coredns/plugin/reload/setup.go b/vendor/github.com/coredns/coredns/plugin/reload/setup.go index ed4d9f85d..c6b33e959 100644 --- a/vendor/github.com/coredns/coredns/plugin/reload/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/reload/setup.go @@ -25,10 +25,9 @@ func init() { // it is used to transmit data between Setup and start of the hook called 'onInstanceStartup' // channel for QUIT is never changed in purpose. // WARNING: this data may be unsync after an invalid attempt of reload Corefile. -var ( - r = reload{dur: defaultInterval, u: unused, quit: make(chan bool)} - once, shutOnce sync.Once -) +var r = reload{interval: defaultInterval, usage: unused, quit: make(chan bool)} +var once sync.Once +var shutOnce sync.Once func setup(c *caddy.Controller) error { c.Next() // 'reload' @@ -70,8 +69,8 @@ func setup(c *caddy.Controller) error { i = i + jitter // prepare info for next onInstanceStartup event - r.setInterval(i) - r.setUsage(used) + r.interval = i + r.usage = used once.Do(func() { caddy.RegisterEventHook("reload", hook) diff --git a/vendor/github.com/coredns/coredns/plugin/test/helpers.go b/vendor/github.com/coredns/coredns/plugin/test/helpers.go index eff5ed550..70159e99a 100644 --- a/vendor/github.com/coredns/coredns/plugin/test/helpers.go +++ b/vendor/github.com/coredns/coredns/plugin/test/helpers.go @@ -2,8 +2,8 @@ package test import ( "context" - "fmt" "sort" + "testing" "github.com/miekg/dns" ) @@ -113,25 +113,29 @@ func OPT(bufsize int, do bool) *dns.OPT { } // Header test if the header in resp matches the header as defined in tc. -func Header(tc Case, resp *dns.Msg) error { +func Header(t *testing.T, tc Case, resp *dns.Msg) bool { if resp.Rcode != tc.Rcode { - return fmt.Errorf("rcode is %q, expected %q", dns.RcodeToString[resp.Rcode], dns.RcodeToString[tc.Rcode]) + t.Errorf("Rcode is %q, expected %q", dns.RcodeToString[resp.Rcode], dns.RcodeToString[tc.Rcode]) + return false } if len(resp.Answer) != len(tc.Answer) { - return fmt.Errorf("answer for %q contained %d results, %d expected", tc.Qname, len(resp.Answer), len(tc.Answer)) + t.Errorf("Answer for %q contained %d results, %d expected", tc.Qname, len(resp.Answer), len(tc.Answer)) + return false } if len(resp.Ns) != len(tc.Ns) { - return fmt.Errorf("authority for %q contained %d results, %d expected", tc.Qname, len(resp.Ns), len(tc.Ns)) + t.Errorf("Authority for %q contained %d results, %d expected", tc.Qname, len(resp.Ns), len(tc.Ns)) + return false } if len(resp.Extra) != len(tc.Extra) { - return fmt.Errorf("additional for %q contained %d results, %d expected", tc.Qname, len(resp.Extra), len(tc.Extra)) + t.Errorf("Additional for %q contained %d results, %d expected", tc.Qname, len(resp.Extra), len(tc.Extra)) + return false } - return nil + return true } -// Section tests if the section in tc matches rr. -func Section(tc Case, sec sect, rr []dns.RR) error { +// Section tests if the the section in tc matches rr. +func Section(t *testing.T, tc Case, sec sect, rr []dns.RR) bool { section := []dns.RR{} switch sec { case 0: @@ -144,112 +148,134 @@ func Section(tc Case, sec sect, rr []dns.RR) error { for i, a := range rr { if a.Header().Name != section[i].Header().Name { - return fmt.Errorf("RR %d should have a Header Name of %q, but has %q", i, section[i].Header().Name, a.Header().Name) + t.Errorf("RR %d should have a Header Name of %q, but has %q", i, section[i].Header().Name, a.Header().Name) + return false } // 303 signals: don't care what the ttl is. if section[i].Header().Ttl != 303 && a.Header().Ttl != section[i].Header().Ttl { if _, ok := section[i].(*dns.OPT); !ok { // we check edns0 bufize on this one - return fmt.Errorf("RR %d should have a Header TTL of %d, but has %d", i, section[i].Header().Ttl, a.Header().Ttl) + t.Errorf("RR %d should have a Header TTL of %d, but has %d", i, section[i].Header().Ttl, a.Header().Ttl) + return false } } if a.Header().Rrtype != section[i].Header().Rrtype { - return fmt.Errorf("RR %d should have a header rr type of %d, but has %d", i, section[i].Header().Rrtype, a.Header().Rrtype) + t.Errorf("RR %d should have a header rr type of %d, but has %d", i, section[i].Header().Rrtype, a.Header().Rrtype) + return false } switch x := a.(type) { case *dns.SRV: if x.Priority != section[i].(*dns.SRV).Priority { - return fmt.Errorf("RR %d should have a Priority of %d, but has %d", i, section[i].(*dns.SRV).Priority, x.Priority) + t.Errorf("RR %d should have a Priority of %d, but has %d", i, section[i].(*dns.SRV).Priority, x.Priority) + return false } if x.Weight != section[i].(*dns.SRV).Weight { - return fmt.Errorf("RR %d should have a Weight of %d, but has %d", i, section[i].(*dns.SRV).Weight, x.Weight) + t.Errorf("RR %d should have a Weight of %d, but has %d", i, section[i].(*dns.SRV).Weight, x.Weight) + return false } if x.Port != section[i].(*dns.SRV).Port { - return fmt.Errorf("RR %d should have a Port of %d, but has %d", i, section[i].(*dns.SRV).Port, x.Port) + t.Errorf("RR %d should have a Port of %d, but has %d", i, section[i].(*dns.SRV).Port, x.Port) + return false } if x.Target != section[i].(*dns.SRV).Target { - return fmt.Errorf("RR %d should have a Target of %q, but has %q", i, section[i].(*dns.SRV).Target, x.Target) + t.Errorf("RR %d should have a Target of %q, but has %q", i, section[i].(*dns.SRV).Target, x.Target) + return false } case *dns.RRSIG: if x.TypeCovered != section[i].(*dns.RRSIG).TypeCovered { - return fmt.Errorf("RR %d should have a TypeCovered of %d, but has %d", i, section[i].(*dns.RRSIG).TypeCovered, x.TypeCovered) + t.Errorf("RR %d should have a TypeCovered of %d, but has %d", i, section[i].(*dns.RRSIG).TypeCovered, x.TypeCovered) + return false } if x.Labels != section[i].(*dns.RRSIG).Labels { - return fmt.Errorf("RR %d should have a Labels of %d, but has %d", i, section[i].(*dns.RRSIG).Labels, x.Labels) + t.Errorf("RR %d should have a Labels of %d, but has %d", i, section[i].(*dns.RRSIG).Labels, x.Labels) + return false } if x.SignerName != section[i].(*dns.RRSIG).SignerName { - return fmt.Errorf("RR %d should have a SignerName of %s, but has %s", i, section[i].(*dns.RRSIG).SignerName, x.SignerName) + t.Errorf("RR %d should have a SignerName of %s, but has %s", i, section[i].(*dns.RRSIG).SignerName, x.SignerName) + return false } case *dns.NSEC: if x.NextDomain != section[i].(*dns.NSEC).NextDomain { - return fmt.Errorf("RR %d should have a NextDomain of %s, but has %s", i, section[i].(*dns.NSEC).NextDomain, x.NextDomain) + t.Errorf("RR %d should have a NextDomain of %s, but has %s", i, section[i].(*dns.NSEC).NextDomain, x.NextDomain) + return false } // TypeBitMap case *dns.A: if x.A.String() != section[i].(*dns.A).A.String() { - return fmt.Errorf("RR %d should have a Address of %q, but has %q", i, section[i].(*dns.A).A.String(), x.A.String()) + t.Errorf("RR %d should have a Address of %q, but has %q", i, section[i].(*dns.A).A.String(), x.A.String()) + return false } case *dns.AAAA: if x.AAAA.String() != section[i].(*dns.AAAA).AAAA.String() { - return fmt.Errorf("RR %d should have a Address of %q, but has %q", i, section[i].(*dns.AAAA).AAAA.String(), x.AAAA.String()) + t.Errorf("RR %d should have a Address of %q, but has %q", i, section[i].(*dns.AAAA).AAAA.String(), x.AAAA.String()) + return false } case *dns.TXT: for j, txt := range x.Txt { if txt != section[i].(*dns.TXT).Txt[j] { - return fmt.Errorf("RR %d should have a Txt of %q, but has %q", i, section[i].(*dns.TXT).Txt[j], txt) + t.Errorf("RR %d should have a Txt of %q, but has %q", i, section[i].(*dns.TXT).Txt[j], txt) + return false } } case *dns.HINFO: if x.Cpu != section[i].(*dns.HINFO).Cpu { - return fmt.Errorf("RR %d should have a Cpu of %s, but has %s", i, section[i].(*dns.HINFO).Cpu, x.Cpu) + t.Errorf("RR %d should have a Cpu of %s, but has %s", i, section[i].(*dns.HINFO).Cpu, x.Cpu) } if x.Os != section[i].(*dns.HINFO).Os { - return fmt.Errorf("RR %d should have a Os of %s, but has %s", i, section[i].(*dns.HINFO).Os, x.Os) + t.Errorf("RR %d should have a Os of %s, but has %s", i, section[i].(*dns.HINFO).Os, x.Os) } case *dns.SOA: tt := section[i].(*dns.SOA) if x.Ns != tt.Ns { - return fmt.Errorf("SOA nameserver should be %q, but is %q", tt.Ns, x.Ns) + t.Errorf("SOA nameserver should be %q, but is %q", tt.Ns, x.Ns) + return false } case *dns.PTR: tt := section[i].(*dns.PTR) if x.Ptr != tt.Ptr { - return fmt.Errorf("PTR ptr should be %q, but is %q", tt.Ptr, x.Ptr) + t.Errorf("PTR ptr should be %q, but is %q", tt.Ptr, x.Ptr) + return false } case *dns.CNAME: tt := section[i].(*dns.CNAME) if x.Target != tt.Target { - return fmt.Errorf("CNAME target should be %q, but is %q", tt.Target, x.Target) + t.Errorf("CNAME target should be %q, but is %q", tt.Target, x.Target) + return false } case *dns.MX: tt := section[i].(*dns.MX) if x.Mx != tt.Mx { - return fmt.Errorf("MX Mx should be %q, but is %q", tt.Mx, x.Mx) + t.Errorf("MX Mx should be %q, but is %q", tt.Mx, x.Mx) + return false } if x.Preference != tt.Preference { - return fmt.Errorf("MX Preference should be %q, but is %q", tt.Preference, x.Preference) + t.Errorf("MX Preference should be %q, but is %q", tt.Preference, x.Preference) + return false } case *dns.NS: tt := section[i].(*dns.NS) if x.Ns != tt.Ns { - return fmt.Errorf("NS nameserver should be %q, but is %q", tt.Ns, x.Ns) + t.Errorf("NS nameserver should be %q, but is %q", tt.Ns, x.Ns) + return false } case *dns.OPT: tt := section[i].(*dns.OPT) if x.UDPSize() != tt.UDPSize() { - return fmt.Errorf("OPT UDPSize should be %d, but is %d", tt.UDPSize(), x.UDPSize()) + t.Errorf("OPT UDPSize should be %d, but is %d", tt.UDPSize(), x.UDPSize()) + return false } if x.Do() != tt.Do() { - return fmt.Errorf("OPT DO should be %t, but is %t", tt.Do(), x.Do()) + t.Errorf("OPT DO should be %t, but is %t", tt.Do(), x.Do()) + return false } } } - return nil + return true } // CNAMEOrder makes sure that CNAMES do not appear after their target records -func CNAMEOrder(res *dns.Msg) error { +func CNAMEOrder(t *testing.T, res *dns.Msg) { for i, c := range res.Answer { if c.Header().Rrtype != dns.TypeCNAME { continue @@ -258,29 +284,38 @@ func CNAMEOrder(res *dns.Msg) error { if a.Header().Name != c.(*dns.CNAME).Target { continue } - return fmt.Errorf("CNAME found after target record") + t.Errorf("CNAME found after target record\n") + t.Logf("%v\n", res) + } } - return nil } // SortAndCheck sorts resp and the checks the header and three sections against the testcase in tc. -func SortAndCheck(resp *dns.Msg, tc Case) error { +func SortAndCheck(t *testing.T, resp *dns.Msg, tc Case) { sort.Sort(RRSet(resp.Answer)) sort.Sort(RRSet(resp.Ns)) sort.Sort(RRSet(resp.Extra)) - if err := Header(tc, resp); err != nil { - return err + if !Header(t, tc, resp) { + t.Logf("%v\n", resp) + return } - if err := Section(tc, Answer, resp.Answer); err != nil { - return err + + if !Section(t, tc, Answer, resp.Answer) { + t.Logf("%v\n", resp) + return } - if err := Section(tc, Ns, resp.Ns); err != nil { - return err + if !Section(t, tc, Ns, resp.Ns) { + t.Logf("%v\n", resp) + return } - return Section(tc, Extra, resp.Extra) + if !Section(t, tc, Extra, resp.Extra) { + t.Logf("%v\n", resp) + return + } + return } // ErrorHandler returns a Handler that returns ServerFailure error when called. diff --git a/vendor/github.com/coredns/coredns/plugin/test/scrape.go b/vendor/github.com/coredns/coredns/plugin/test/scrape.go deleted file mode 100644 index 132a4f944..000000000 --- a/vendor/github.com/coredns/coredns/plugin/test/scrape.go +++ /dev/null @@ -1,266 +0,0 @@ -// Adapted by Miek Gieben for CoreDNS testing. -// -// License from prom2json -// Copyright 2014 Prometheus Team -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package test will scrape a target and you can inspect the variables. -// Basic usage: -// -// result := Scrape("http://localhost:9153/metrics") -// v := MetricValue("coredns_cache_capacity", result) -// -package test - -import ( - "fmt" - "io" - "mime" - "net/http" - "strconv" - - "github.com/matttproud/golang_protobuf_extensions/pbutil" - "github.com/prometheus/common/expfmt" - - dto "github.com/prometheus/client_model/go" -) - -type ( - // MetricFamily holds a prometheus metric. - MetricFamily struct { - Name string `json:"name"` - Help string `json:"help"` - Type string `json:"type"` - Metrics []interface{} `json:"metrics,omitempty"` // Either metric or summary. - } - - // metric is for all "single value" metrics. - metric struct { - Labels map[string]string `json:"labels,omitempty"` - Value string `json:"value"` - } - - summary struct { - Labels map[string]string `json:"labels,omitempty"` - Quantiles map[string]string `json:"quantiles,omitempty"` - Count string `json:"count"` - Sum string `json:"sum"` - } - - histogram struct { - Labels map[string]string `json:"labels,omitempty"` - Buckets map[string]string `json:"buckets,omitempty"` - Count string `json:"count"` - Sum string `json:"sum"` - } -) - -// Scrape returns the all the vars a []*metricFamily. -func Scrape(url string) []*MetricFamily { - mfChan := make(chan *dto.MetricFamily, 1024) - - go fetchMetricFamilies(url, mfChan) - - result := []*MetricFamily{} - for mf := range mfChan { - result = append(result, newMetricFamily(mf)) - } - return result -} - -// ScrapeMetricAsInt provide a sum of all metrics collected for the name and label provided. -// if the metric is not a numeric value, it will be counted a 0. -func ScrapeMetricAsInt(addr string, name string, label string, nometricvalue int) int { - - valueToInt := func(m metric) int { - v := m.Value - r, err := strconv.Atoi(v) - if err != nil { - return 0 - } - return r - } - - met := Scrape(fmt.Sprintf("http://%s/metrics", addr)) - found := false - tot := 0 - for _, mf := range met { - if mf.Name == name { - // Sum all metrics available - for _, m := range mf.Metrics { - if label == "" { - tot += valueToInt(m.(metric)) - found = true - continue - } - for _, v := range m.(metric).Labels { - if v == label { - tot += valueToInt(m.(metric)) - found = true - } - } - } - } - } - - if !found { - return nometricvalue - } - return tot -} - -// MetricValue returns the value associated with name as a string as well as the labels. -// It only returns the first metrics of the slice. -func MetricValue(name string, mfs []*MetricFamily) (string, map[string]string) { - for _, mf := range mfs { - if mf.Name == name { - // Only works with Gauge and Counter... - return mf.Metrics[0].(metric).Value, mf.Metrics[0].(metric).Labels - } - } - return "", nil -} - -// MetricValueLabel returns the value for name *and* label *value*. -func MetricValueLabel(name, label string, mfs []*MetricFamily) (string, map[string]string) { - // bit hacky is this really handy...? - for _, mf := range mfs { - if mf.Name == name { - for _, m := range mf.Metrics { - for _, v := range m.(metric).Labels { - if v == label { - return m.(metric).Value, m.(metric).Labels - } - } - - } - } - } - return "", nil -} - -func newMetricFamily(dtoMF *dto.MetricFamily) *MetricFamily { - mf := &MetricFamily{ - Name: dtoMF.GetName(), - Help: dtoMF.GetHelp(), - Type: dtoMF.GetType().String(), - Metrics: make([]interface{}, len(dtoMF.Metric)), - } - for i, m := range dtoMF.Metric { - if dtoMF.GetType() == dto.MetricType_SUMMARY { - mf.Metrics[i] = summary{ - Labels: makeLabels(m), - Quantiles: makeQuantiles(m), - Count: fmt.Sprint(m.GetSummary().GetSampleCount()), - Sum: fmt.Sprint(m.GetSummary().GetSampleSum()), - } - } else if dtoMF.GetType() == dto.MetricType_HISTOGRAM { - mf.Metrics[i] = histogram{ - Labels: makeLabels(m), - Buckets: makeBuckets(m), - Count: fmt.Sprint(m.GetHistogram().GetSampleCount()), - Sum: fmt.Sprint(m.GetSummary().GetSampleSum()), - } - } else { - mf.Metrics[i] = metric{ - Labels: makeLabels(m), - Value: fmt.Sprint(value(m)), - } - } - } - return mf -} - -func value(m *dto.Metric) float64 { - if m.Gauge != nil { - return m.GetGauge().GetValue() - } - if m.Counter != nil { - return m.GetCounter().GetValue() - } - if m.Untyped != nil { - return m.GetUntyped().GetValue() - } - return 0. -} - -func makeLabels(m *dto.Metric) map[string]string { - result := map[string]string{} - for _, lp := range m.Label { - result[lp.GetName()] = lp.GetValue() - } - return result -} - -func makeQuantiles(m *dto.Metric) map[string]string { - result := map[string]string{} - for _, q := range m.GetSummary().Quantile { - result[fmt.Sprint(q.GetQuantile())] = fmt.Sprint(q.GetValue()) - } - return result -} - -func makeBuckets(m *dto.Metric) map[string]string { - result := map[string]string{} - for _, b := range m.GetHistogram().Bucket { - result[fmt.Sprint(b.GetUpperBound())] = fmt.Sprint(b.GetCumulativeCount()) - } - return result -} - -func fetchMetricFamilies(url string, ch chan<- *dto.MetricFamily) { - defer close(ch) - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return - } - req.Header.Add("Accept", acceptHeader) - resp, err := http.DefaultClient.Do(req) - if err != nil { - return - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return - } - - mediatype, params, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) - if err == nil && mediatype == "application/vnd.google.protobuf" && - params["encoding"] == "delimited" && - params["proto"] == "io.prometheus.client.MetricFamily" { - for { - mf := &dto.MetricFamily{} - if _, err = pbutil.ReadDelimited(resp.Body, mf); err != nil { - if err == io.EOF { - break - } - return - } - ch <- mf - } - } else { - // We could do further content-type checks here, but the - // fallback for now will anyway be the text format - // version 0.0.4, so just go for it and see if it works. - var parser expfmt.TextParser - metricFamilies, err := parser.TextToMetricFamilies(resp.Body) - if err != nil { - return - } - for _, mf := range metricFamilies { - ch <- mf - } - } -} - -const acceptHeader = `application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.7,text/plain;version=0.0.4;q=0.3` diff --git a/vendor/github.com/coredns/coredns/request/edns0.go b/vendor/github.com/coredns/coredns/request/edns0.go deleted file mode 100644 index 89eb6b468..000000000 --- a/vendor/github.com/coredns/coredns/request/edns0.go +++ /dev/null @@ -1,31 +0,0 @@ -package request - -import ( - "github.com/coredns/coredns/plugin/pkg/edns" - - "github.com/miekg/dns" -) - -func supportedOptions(o []dns.EDNS0) []dns.EDNS0 { - var supported = make([]dns.EDNS0, 0, 3) - // For as long as possible try avoid looking up in the map, because that need an Rlock. - for _, opt := range o { - switch code := opt.Option(); code { - case dns.EDNS0NSID: - fallthrough - case dns.EDNS0EXPIRE: - fallthrough - case dns.EDNS0COOKIE: - fallthrough - case dns.EDNS0TCPKEEPALIVE: - fallthrough - case dns.EDNS0PADDING: - supported = append(supported, opt) - default: - if edns.SupportedOption(code) { - supported = append(supported, opt) - } - } - } - return supported -} diff --git a/vendor/github.com/coredns/coredns/request/request.go b/vendor/github.com/coredns/coredns/request/request.go index 0ec98b310..260d73f5e 100644 --- a/vendor/github.com/coredns/coredns/request/request.go +++ b/vendor/github.com/coredns/coredns/request/request.go @@ -2,6 +2,7 @@ package request import ( + "context" "net" "strings" @@ -18,9 +19,12 @@ type Request struct { // Optional lowercased zone of this query. Zone string + Context context.Context + // Cache size after first call to Size or Do. size int do *bool // nil: nothing, otherwise *do value + // TODO(miek): opt record itself as well? // Caches name string // lowercase qname. @@ -188,13 +192,15 @@ func (r *Request) Size() int { } // SizeAndDo adds an OPT record that the reflects the intent from request. -// The returned bool indicates if an record was found and normalised. +// The returned bool indicated if an record was found and normalised. func (r *Request) SizeAndDo(m *dns.Msg) bool { - o := r.Req.IsEdns0() + o := r.Req.IsEdns0() // TODO(miek): speed this up if o == nil { return false } + odo := o.Do() + if mo := m.IsEdns0(); mo != nil { mo.Hdr.Name = "." mo.Hdr.Rrtype = dns.TypeOPT @@ -202,24 +208,20 @@ func (r *Request) SizeAndDo(m *dns.Msg) bool { mo.SetUDPSize(o.UDPSize()) mo.Hdr.Ttl &= 0xff00 // clear flags - // Assume if the message m has options set, they are OK and represent what an upstream can do. - - if o.Do() { + if odo { mo.SetDo() } return true } - // Reuse the request's OPT record and tack it to m. o.Hdr.Name = "." o.Hdr.Rrtype = dns.TypeOPT o.SetVersion(0) o.Hdr.Ttl &= 0xff00 // clear flags - if len(o.Option) > 0 { - o.Option = supportedOptions(o.Option) + if odo { + o.SetDo() } - m.Extra = append(m.Extra, o) return true } @@ -238,23 +240,6 @@ func (r *Request) Scrub(reply *dns.Msg) *dns.Msg { reply.Compress = false rl := reply.Len() if size >= rl { - if r.Proto() != "udp" { - return reply - } - - // Last ditch attempt to avoid fragmentation, if the size is bigger than the v4/v6 UDP fragmentation - // limit and sent via UDP compress it (in the hope we go under that limit). Limits taken from NSD: - // - // .., 1480 (EDNS/IPv4), 1220 (EDNS/IPv6), or the advertized EDNS buffer size if that is - // smaller than the EDNS default. - // See: https://open.nlnetlabs.nl/pipermail/nsd-users/2011-November/001278.html - if rl > 1480 && r.Family() == 1 { - reply.Compress = true - } - if rl > 1220 && r.Family() == 2 { - reply.Compress = true - } - return reply } @@ -295,14 +280,15 @@ func (r *Request) Scrub(reply *dns.Msg) *dns.Msg { // pretty rare. Normally, the loop will exit when l > re, meaning that // in the previous iteration either: // rl < size: no need to do anything. - // rl > size: the final size is too large, and if m > 0, the preceding - // iteration the size was too small. Select that preceding size. + // rl > size: the final size is too large, and if m > 0, the preceeding + // iteration the size was too small. Select that preceeding size. if rl > size && m > 0 { reply.Extra = origExtra[:m-1] rl = reply.Len() } if rl <= size { + r.SizeAndDo(reply) return reply } @@ -330,14 +316,15 @@ func (r *Request) Scrub(reply *dns.Msg) *dns.Msg { // pretty rare. Normally, the loop will exit when l > ra, meaning that // in the previous iteration either: // rl < size: no need to do anything. - // rl > size: the final size is too large, and if m > 0, the preceding - // iteration the size was too small. Select that preceding size. + // rl > size: the final size is too large, and if m > 0, the preceeding + // iteration the size was too small. Select that preceeding size. if rl > size && m > 0 { reply.Answer = origAnswer[:m-1] // No need to recalc length, as we don't use it. We set truncated anyway. Doing // this extra m-1 step does make it fit in the client's buffer however. } + r.SizeAndDo(reply) reply.Truncated = true return reply } @@ -429,6 +416,14 @@ func (r *Request) QClass() uint16 { } +// ErrorMessage returns an error message suitable for sending +// back to the client. +func (r *Request) ErrorMessage(rcode int) *dns.Msg { + m := new(dns.Msg) + m.SetRcode(r.Req, rcode) + return m +} + // Clear clears all caching from Request s. func (r *Request) Clear() { r.name = "" diff --git a/vendor/github.com/coredns/coredns/request/writer.go b/vendor/github.com/coredns/coredns/request/writer.go index 6caba0c2e..ffbbe93e3 100644 --- a/vendor/github.com/coredns/coredns/request/writer.go +++ b/vendor/github.com/coredns/coredns/request/writer.go @@ -11,12 +11,10 @@ type ScrubWriter struct { // NewScrubWriter returns a new and initialized ScrubWriter. func NewScrubWriter(req *dns.Msg, w dns.ResponseWriter) *ScrubWriter { return &ScrubWriter{w, req} } -// WriteMsg overrides the default implementation of the underlying dns.ResponseWriter and calls +// WriteMsg overrides the default implementation of the underlaying dns.ResponseWriter and calls // scrub on the message m and will then write it to the client. func (s *ScrubWriter) WriteMsg(m *dns.Msg) error { state := Request{Req: s.req, W: s.ResponseWriter} - n := state.Scrub(m) - state.SizeAndDo(n) return s.ResponseWriter.WriteMsg(n) }