From 68028cb7815ec8cc4ad2be78635b3b5c7339e96d Mon Sep 17 00:00:00 2001 From: TangBin Date: Mon, 12 Nov 2018 20:09:01 +0800 Subject: [PATCH 1/3] fix region-dns build error --- Gopkg.lock | 35 +- Gopkg.toml | 10 +- pkg/util/qcloud/shell/instance.go | 3 +- .../thrift/lib/go/thrift/binary_protocol.go | 5 +- .../lib/go/thrift/buffered_transport.go | 5 +- .../apache/thrift/lib/go/thrift/client.go | 17 +- .../thrift/lib/go/thrift/client_go17.go | 13 - .../thrift/lib/go/thrift/client_pre_go17.go | 13 - .../thrift/lib/go/thrift/common_test_go17.go | 32 - .../lib/go/thrift/common_test_pre_go17.go | 32 - .../thrift/lib/go/thrift/compact_protocol.go | 7 +- .../lib/go/thrift/{go17.go => context.go} | 2 - .../thrift/lib/go/thrift/debug_protocol.go | 5 +- .../thrift/lib/go/thrift/framed_transport.go | 5 +- .../thrift/lib/go/thrift/http_client.go | 6 +- .../thrift/lib/go/thrift/http_transport.go | 12 + .../lib/go/thrift/http_transport_go17.go | 38 - .../lib/go/thrift/http_transport_pre_go17.go | 40 - .../lib/go/thrift/iostream_transport.go | 3 +- .../thrift/lib/go/thrift/json_protocol.go | 5 +- .../thrift/lib/go/thrift/memory_buffer.go | 3 +- .../lib/go/thrift/multiplexed_protocol.go | 31 + .../go/thrift/multiplexed_protocol_go17.go | 53 -- .../thrift/multiplexed_protocol_pre_go17.go | 54 -- .../apache/thrift/lib/go/thrift/pre_go17.go | 26 - .../apache/thrift/lib/go/thrift/processor.go | 34 - .../thrift/lib/go/thrift/processor_factory.go | 12 + .../thrift/lib/go/thrift/processor_go17.go | 34 - .../apache/thrift/lib/go/thrift/protocol.go | 3 +- .../apache/thrift/lib/go/thrift/serializer.go | 16 +- .../lib/go/thrift/simple_json_protocol.go | 5 +- .../thrift/lib/go/thrift/simple_server.go | 40 +- .../apache/thrift/lib/go/thrift/socket.go | 3 +- .../apache/thrift/lib/go/thrift/ssl_socket.go | 3 +- .../apache/thrift/lib/go/thrift/transport.go | 9 +- .../thrift/lib/go/thrift/zlib_transport.go | 5 +- .../coredns/coredns/core/dnsserver/address.go | 62 +- .../coredns/core/dnsserver/listen_go111.go | 32 - .../core/dnsserver/listen_go_not111.go | 11 - .../coredns/core/dnsserver/register.go | 24 +- .../coredns/coredns/core/dnsserver/server.go | 8 +- .../coredns/core/dnsserver/server_grpc.go | 5 +- .../coredns/core/dnsserver/server_https.go | 5 +- .../coredns/core/dnsserver/server_tls.go | 6 +- .../coredns/core/dnsserver/zdirectives.go | 1 - .../coredns/coredns/coremain/run.go | 4 +- .../coredns/coredns/coremain/version.go | 2 +- .../coredns/coredns/plugin/backend_lookup.go | 48 +- .../coredns/coredns/plugin/cache/README.md | 31 +- .../coredns/coredns/plugin/cache/cache.go | 87 ++- .../coredns/coredns/plugin/cache/handler.go | 5 + .../coredns/coredns/plugin/cache/setup.go | 30 +- .../coredns/coredns/plugin/dnstap/handler.go | 2 +- .../coredns/coredns/plugin/dnstap/setup.go | 4 +- .../coredns/coredns/plugin/errors/README.md | 34 +- .../coredns/coredns/plugin/errors/errors.go | 93 +-- .../coredns/coredns/plugin/errors/setup.go | 46 +- .../coredns/coredns/plugin/etcd/msg/path.go | 2 +- .../coredns/plugin/etcd/msg/service.go | 2 +- .../coredns/coredns/plugin/file/README.md | 7 +- .../coredns/coredns/plugin/file/dname.go | 2 +- .../coredns/coredns/plugin/file/file.go | 16 +- .../coredns/coredns/plugin/file/lookup.go | 4 +- .../coredns/coredns/plugin/file/reload.go | 14 +- .../coredns/coredns/plugin/file/secondary.go | 2 +- .../coredns/coredns/plugin/file/setup.go | 23 +- .../coredns/coredns/plugin/file/shutdown.go | 2 +- .../coredns/coredns/plugin/file/xfr.go | 2 +- .../coredns/coredns/plugin/file/zone.go | 13 +- .../coredns/coredns/plugin/forward/connect.go | 8 +- .../coredns/coredns/plugin/forward/forward.go | 11 +- .../coredns/coredns/plugin/forward/health.go | 11 +- .../coredns/coredns/plugin/forward/lookup.go | 5 +- .../coredns/coredns/plugin/forward/metrics.go | 4 + .../coredns/plugin/forward/persistent.go | 26 +- .../coredns/plugin/forward/protocol.go | 30 + .../coredns/coredns/plugin/forward/proxy.go | 11 +- .../coredns/coredns/plugin/forward/setup.go | 103 +-- .../coredns/plugin/forward/truncated.go | 2 +- .../coredns/coredns/plugin/health/README.md | 2 +- .../coredns/coredns/plugin/health/setup.go | 2 +- .../coredns/coredns/plugin/hosts/hosts.go | 2 + .../coredns/coredns/plugin/hosts/hostsfile.go | 30 +- .../coredns/coredns/plugin/hosts/setup.go | 6 +- .../coredns/coredns/plugin/log/README.md | 18 +- .../coredns/coredns/plugin/log/log.go | 7 +- .../coredns/coredns/plugin/log/setup.go | 12 + .../coredns/coredns/plugin/metrics/README.md | 2 +- .../coredns/coredns/plugin/metrics/metrics.go | 47 +- .../coredns/coredns/plugin/metrics/setup.go | 11 +- .../coredns/plugin/metrics/vars/report.go | 2 +- .../coredns/coredns/plugin/normalize.go | 30 +- .../coredns/coredns/plugin/pkg/cache/cache.go | 36 +- .../plugin/pkg/{parse => dnsutil}/host.go | 38 +- .../coredns/plugin/pkg/dnsutil/join.go | 8 +- .../coredns/coredns/plugin/pkg/dnsutil/ttl.go | 38 +- .../coredns/coredns/plugin/pkg/doh/doh.go | 2 +- .../coredns/coredns/plugin/pkg/log/log.go | 39 +- .../coredns/coredns/plugin/pkg/log/plugin.go | 7 - .../coredns/coredns/plugin/pkg/parse/parse.go | 7 +- .../coredns/plugin/pkg/parse/transport.go | 33 - .../coredns/plugin/pkg/replacer/replacer.go | 17 +- .../coredns/plugin/pkg/transport/transport.go | 21 - .../coredns/coredns/plugin/pkg/uniq/uniq.go | 21 +- .../coredns/coredns/plugin/pkg/up/up.go | 2 +- .../coredns/plugin/pkg/upstream/upstream.go | 4 +- .../coredns/plugin/pkg/watch/watcher.go | 2 +- .../coredns/coredns/plugin/proxy/README.md | 2 +- .../coredns/coredns/plugin/proxy/dns.go | 17 +- .../coredns/coredns/plugin/proxy/lookup.go | 2 +- .../coredns/coredns/plugin/proxy/metrics.go | 4 + .../coredns/coredns/plugin/proxy/proxy.go | 4 +- .../coredns/coredns/plugin/proxy/setup.go | 2 +- .../coredns/coredns/plugin/proxy/upstream.go | 4 +- .../coredns/coredns/plugin/reload/README.md | 2 +- .../coredns/coredns/plugin/trace/trace.go | 35 +- .../coredns/coredns/request/request.go | 62 +- .../coredns/coredns/request/writer.go | 20 - .../prometheus/client_golang/AUTHORS.md | 18 + .../client_golang/prometheus/collector.go | 73 +- .../client_golang/prometheus/counter.go | 191 ++--- .../client_golang/prometheus/desc.go | 47 +- .../client_golang/prometheus/doc.go | 92 +-- .../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 | 218 ++---- .../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 | 260 ++----- .../prometheus/promhttp/instrument_client.go | 97 --- .../promhttp/instrument_client_1_8.go | 144 ---- .../prometheus/promhttp/instrument_server.go | 447 ----------- .../client_golang/prometheus/registry.go | 707 +++++++----------- .../client_golang/prometheus/summary.go | 194 ++--- .../client_golang/prometheus/timer.go | 51 -- .../client_golang/prometheus/untyped.go | 102 ++- .../client_golang/prometheus/value.go | 94 ++- .../client_golang/prometheus/vec.go | 516 ++++++------- .../client_golang/prometheus/wrap.go | 179 ----- 147 files changed, 2075 insertions(+), 4999 deletions(-) delete mode 100644 vendor/github.com/apache/thrift/lib/go/thrift/client_go17.go delete mode 100644 vendor/github.com/apache/thrift/lib/go/thrift/client_pre_go17.go delete mode 100644 vendor/github.com/apache/thrift/lib/go/thrift/common_test_go17.go delete mode 100644 vendor/github.com/apache/thrift/lib/go/thrift/common_test_pre_go17.go rename vendor/github.com/apache/thrift/lib/go/thrift/{go17.go => context.go} (98%) delete mode 100644 vendor/github.com/apache/thrift/lib/go/thrift/http_transport_go17.go delete mode 100644 vendor/github.com/apache/thrift/lib/go/thrift/http_transport_pre_go17.go delete mode 100644 vendor/github.com/apache/thrift/lib/go/thrift/multiplexed_protocol_go17.go delete mode 100644 vendor/github.com/apache/thrift/lib/go/thrift/multiplexed_protocol_pre_go17.go delete mode 100644 vendor/github.com/apache/thrift/lib/go/thrift/pre_go17.go delete mode 100644 vendor/github.com/apache/thrift/lib/go/thrift/processor.go delete mode 100644 vendor/github.com/apache/thrift/lib/go/thrift/processor_go17.go delete mode 100644 vendor/github.com/coredns/coredns/core/dnsserver/listen_go111.go delete mode 100644 vendor/github.com/coredns/coredns/core/dnsserver/listen_go_not111.go create mode 100644 vendor/github.com/coredns/coredns/plugin/forward/protocol.go rename vendor/github.com/coredns/coredns/plugin/pkg/{parse => dnsutil}/host.go (60%) delete mode 100644 vendor/github.com/coredns/coredns/plugin/pkg/parse/transport.go delete mode 100644 vendor/github.com/coredns/coredns/plugin/pkg/transport/transport.go delete mode 100644 vendor/github.com/coredns/coredns/request/writer.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 414c3a0e55..454faee8ba 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -113,12 +113,11 @@ version = "v1.0.1" [[projects]] - digest = "1:e64acfe8cda1955db545ede8e863c54d69f1ac6cd058df4349be4040320840fd" + digest = "1:eee9386329f4fcdf8d6c0def0c9771b634bdd5ba460d888aa98c17d59b37a76c" name = "github.com/apache/thrift" packages = ["lib/go/thrift"] pruneopts = "UT" - revision = "327ebb6c2b6df8bf075da02ef45a2a034e9b79ba" - version = "0.11.0" + revision = "e59b73d3c2bf1c328ccb78e683c0462fa1a473c7" [[projects]] digest = "1:55bf2a4da68caa693d660683e53bab3e651a940cd46354b997ab68bec7920e23" @@ -190,7 +189,7 @@ version = "v0.2.1" [[projects]] - digest = "1:1c45e98df3726b24a0cc200f5ed182e5eefbe6a8cf2927f9d4335151aa190d15" + digest = "1:40098afbdd06a76dee4b6bcb85a22fed81ac9d2ebaf775c91e558c80f57aaff1" name = "github.com/coredns/coredns" packages = [ "core/dnsserver", @@ -232,7 +231,6 @@ "plugin/pkg/response", "plugin/pkg/tls", "plugin/pkg/trace", - "plugin/pkg/transport", "plugin/pkg/uniq", "plugin/pkg/up", "plugin/pkg/upstream", @@ -244,8 +242,8 @@ "request", ] pruneopts = "UT" - revision = "756749c5cad4cd40453b82f70592bc19bb9ccb15" - version = "v1.2.6" + revision = "2e322f6e8a54f18c6aef9c25a7c432c291a3d9f7" + version = "v1.2.0" [[projects]] digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" @@ -806,16 +804,15 @@ version = "v1.0.0" [[projects]] - digest = "1:26663fafdea73a38075b07e8e9d82fc0056379d2be8bb4e13899e8fda7c7dd23" + digest = "1:d14a5f4bfecf017cb780bdde1b6483e5deb87e12c332544d2c430eda58734bcb" name = "github.com/prometheus/client_golang" packages = [ "prometheus", - "prometheus/internal", "prometheus/promhttp", ] pruneopts = "UT" - revision = "abad2d1bd44235a26707c172eab6bca5bf2dbad3" - version = "v0.9.1" + revision = "c5b7fccd204277076155f10851dad72b76a49317" + version = "v0.8.0" [[projects]] branch = "master" @@ -1292,14 +1289,6 @@ revision = "1638f8970cefaa404ff3a62950f88b08292b2696" version = "v9.0.0" -[[projects]] - digest = "1:7719608fe0b52a4ece56c2dde37bedd95b938677d1ab0f84b8a7852e4c59f849" - name = "sigs.k8s.io/yaml" - packages = ["."] - pruneopts = "UT" - revision = "fd68e9863619f6ec2fdd8625fe1f02e7c877e480" - version = "v1.1.0" - [[projects]] digest = "1:356a33d2d627cbb232a1d71cc42051fbac0e5e6a1717679f110054baa02472e5" name = "k8s.io/kubernetes" @@ -1311,6 +1300,14 @@ revision = "17c77c7898218073f14c8d573582e8d2313dc740" version = "v1.12.2" +[[projects]] + digest = "1:7719608fe0b52a4ece56c2dde37bedd95b938677d1ab0f84b8a7852e4c59f849" + name = "sigs.k8s.io/yaml" + packages = ["."] + pruneopts = "UT" + revision = "fd68e9863619f6ec2fdd8625fe1f02e7c877e480" + version = "v1.1.0" + [[projects]] branch = "master" digest = "1:54554b3c72f4fcbd3c27c5137f9acd9d153d7e7150d77cd509af50c75fcb41b9" diff --git a/Gopkg.toml b/Gopkg.toml index 5fdb369c32..e8e23c55f2 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -63,7 +63,7 @@ [[constraint]] name = "github.com/coredns/coredns" - version = "1.2.6" + version = "=v1.2.0" [[constraint]] name = "github.com/fatih/color" @@ -181,6 +181,14 @@ branch = "master" name = "yunion.io/x/structarg" +[[constraint]] + name = "github.com/prometheus/client_golang" + version = "=v0.8.0" + +[[constraint]] + revision = "e59b73d3c2bf1c328ccb78e683c0462fa1a473c7" + name = "github.com/apache/thrift" + [prune] go-tests = true unused-packages = true diff --git a/pkg/util/qcloud/shell/instance.go b/pkg/util/qcloud/shell/instance.go index 1343410391..558761272f 100644 --- a/pkg/util/qcloud/shell/instance.go +++ b/pkg/util/qcloud/shell/instance.go @@ -136,11 +136,10 @@ func init() { } shellutils.R(&InstanceRebuildRootOptions{}, "instance-rebuild-root", "Reinstall virtual server system image", func(cli *qcloud.SRegion, args *InstanceRebuildRootOptions) error { - diskID, err := cli.ReplaceSystemDisk(args.ID, args.Image, args.Password, args.Keypair, args.Size) + err := cli.ReplaceSystemDisk(args.ID, args.Image, args.Password, args.Keypair, args.Size) if err != nil { return err } - fmt.Printf("New diskID is %s", diskID) return nil }) diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/binary_protocol.go b/vendor/github.com/apache/thrift/lib/go/thrift/binary_protocol.go index 690d341111..de0f6a7a5c 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/binary_protocol.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/binary_protocol.go @@ -21,6 +21,7 @@ package thrift import ( "bytes" + "context" "encoding/binary" "errors" "fmt" @@ -457,8 +458,8 @@ func (p *TBinaryProtocol) ReadBinary() ([]byte, error) { return buf, NewTProtocolException(err) } -func (p *TBinaryProtocol) Flush() (err error) { - return NewTProtocolException(p.trans.Flush()) +func (p *TBinaryProtocol) Flush(ctx context.Context) (err error) { + return NewTProtocolException(p.trans.Flush(ctx)) } func (p *TBinaryProtocol) Skip(fieldType TType) (err error) { diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/buffered_transport.go b/vendor/github.com/apache/thrift/lib/go/thrift/buffered_transport.go index b754f925d0..96702061b2 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/buffered_transport.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/buffered_transport.go @@ -21,6 +21,7 @@ package thrift import ( "bufio" + "context" ) type TBufferedTransportFactory struct { @@ -78,12 +79,12 @@ func (p *TBufferedTransport) Write(b []byte) (int, error) { return n, err } -func (p *TBufferedTransport) Flush() error { +func (p *TBufferedTransport) Flush(ctx context.Context) error { if err := p.ReadWriter.Flush(); err != nil { p.ReadWriter.Writer.Reset(p.tp) return err } - return p.tp.Flush() + return p.tp.Flush(ctx) } func (p *TBufferedTransport) RemainingBytes() (num_bytes uint64) { diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/client.go b/vendor/github.com/apache/thrift/lib/go/thrift/client.go index 8bdb53d8d9..28791ccd0c 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/client.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/client.go @@ -1,6 +1,13 @@ package thrift -import "fmt" +import ( + "context" + "fmt" +) + +type TClient interface { + Call(ctx context.Context, method string, args, result TStruct) error +} type TStandardClient struct { seqId int32 @@ -16,7 +23,7 @@ func NewTStandardClient(inputProtocol, outputProtocol TProtocol) *TStandardClien } } -func (p *TStandardClient) Send(oprot TProtocol, seqId int32, method string, args TStruct) error { +func (p *TStandardClient) Send(ctx context.Context, oprot TProtocol, seqId int32, method string, args TStruct) error { if err := oprot.WriteMessageBegin(method, CALL, seqId); err != nil { return err } @@ -26,7 +33,7 @@ func (p *TStandardClient) Send(oprot TProtocol, seqId int32, method string, args if err := oprot.WriteMessageEnd(); err != nil { return err } - return oprot.Flush() + return oprot.Flush(ctx) } func (p *TStandardClient) Recv(iprot TProtocol, seqId int32, method string, result TStruct) error { @@ -61,11 +68,11 @@ func (p *TStandardClient) Recv(iprot TProtocol, seqId int32, method string, resu return iprot.ReadMessageEnd() } -func (p *TStandardClient) call(method string, args, result TStruct) error { +func (p *TStandardClient) Call(ctx context.Context, method string, args, result TStruct) error { p.seqId++ seqId := p.seqId - if err := p.Send(p.oprot, seqId, method, args); err != nil { + if err := p.Send(ctx, p.oprot, seqId, method, args); err != nil { return err } diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/client_go17.go b/vendor/github.com/apache/thrift/lib/go/thrift/client_go17.go deleted file mode 100644 index 15c1c52ca6..0000000000 --- a/vendor/github.com/apache/thrift/lib/go/thrift/client_go17.go +++ /dev/null @@ -1,13 +0,0 @@ -// +build go1.7 - -package thrift - -import "context" - -type TClient interface { - Call(ctx context.Context, method string, args, result TStruct) error -} - -func (p *TStandardClient) Call(ctx context.Context, method string, args, result TStruct) error { - return p.call(method, args, result) -} diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/client_pre_go17.go b/vendor/github.com/apache/thrift/lib/go/thrift/client_pre_go17.go deleted file mode 100644 index d2e99ef2af..0000000000 --- a/vendor/github.com/apache/thrift/lib/go/thrift/client_pre_go17.go +++ /dev/null @@ -1,13 +0,0 @@ -// +build !go1.7 - -package thrift - -import "golang.org/x/net/context" - -type TClient interface { - Call(ctx context.Context, method string, args, result TStruct) error -} - -func (p *TStandardClient) Call(ctx context.Context, method string, args, result TStruct) error { - return p.call(method, args, result) -} diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/common_test_go17.go b/vendor/github.com/apache/thrift/lib/go/thrift/common_test_go17.go deleted file mode 100644 index 2c729a2269..0000000000 --- a/vendor/github.com/apache/thrift/lib/go/thrift/common_test_go17.go +++ /dev/null @@ -1,32 +0,0 @@ -// +build go1.7 - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 thrift - -import "context" - -type mockProcessor struct { - ProcessFunc func(in, out TProtocol) (bool, TException) -} - -func (m *mockProcessor) Process(ctx context.Context, in, out TProtocol) (bool, TException) { - return m.ProcessFunc(in, out) -} diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/common_test_pre_go17.go b/vendor/github.com/apache/thrift/lib/go/thrift/common_test_pre_go17.go deleted file mode 100644 index e6d0c4d9af..0000000000 --- a/vendor/github.com/apache/thrift/lib/go/thrift/common_test_pre_go17.go +++ /dev/null @@ -1,32 +0,0 @@ -// +build !go1.7 - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 thrift - -import "golang.org/x/net/context" - -type mockProcessor struct { - ProcessFunc func(in, out TProtocol) (bool, TException) -} - -func (m *mockProcessor) Process(ctx context.Context, in, out TProtocol) (bool, TException) { - return m.ProcessFunc(in, out) -} diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/compact_protocol.go b/vendor/github.com/apache/thrift/lib/go/thrift/compact_protocol.go index 0bc5fddeb3..66fbf5c335 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/compact_protocol.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/compact_protocol.go @@ -20,6 +20,7 @@ package thrift import ( + "context" "encoding/binary" "fmt" "io" @@ -599,8 +600,8 @@ func (p *TCompactProtocol) ReadBinary() (value []byte, err error) { return buf, NewTProtocolException(e) } -func (p *TCompactProtocol) Flush() (err error) { - return NewTProtocolException(p.trans.Flush()) +func (p *TCompactProtocol) Flush(ctx context.Context) (err error) { + return NewTProtocolException(p.trans.Flush(ctx)) } func (p *TCompactProtocol) Skip(fieldType TType) (err error) { @@ -806,7 +807,7 @@ func (p *TCompactProtocol) getTType(t tCompactType) (TType, error) { case COMPACT_STRUCT: return STRUCT, nil } - return STOP, TException(fmt.Errorf("don't know what type: %s", t&0x0f)) + return STOP, TException(fmt.Errorf("don't know what type: %v", t&0x0f)) } // Given a TType value, find the appropriate TCompactProtocol.Types constant. diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/go17.go b/vendor/github.com/apache/thrift/lib/go/thrift/context.go similarity index 98% rename from vendor/github.com/apache/thrift/lib/go/thrift/go17.go rename to vendor/github.com/apache/thrift/lib/go/thrift/context.go index e3b21c4b73..d15c1bcf89 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/go17.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/context.go @@ -1,5 +1,3 @@ -// +build go1.7 - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/debug_protocol.go b/vendor/github.com/apache/thrift/lib/go/thrift/debug_protocol.go index d37252cc61..57943e0f35 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/debug_protocol.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/debug_protocol.go @@ -20,6 +20,7 @@ package thrift import ( + "context" "log" ) @@ -258,8 +259,8 @@ func (tdp *TDebugProtocol) Skip(fieldType TType) (err error) { log.Printf("%sSkip(fieldType=%#v) (err=%#v)", tdp.LogPrefix, fieldType, err) return } -func (tdp *TDebugProtocol) Flush() (err error) { - err = tdp.Delegate.Flush() +func (tdp *TDebugProtocol) Flush(ctx context.Context) (err error) { + err = tdp.Delegate.Flush(ctx) log.Printf("%sFlush() (err=%#v)", tdp.LogPrefix, err) return } diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/framed_transport.go b/vendor/github.com/apache/thrift/lib/go/thrift/framed_transport.go index 60b1249915..81fa65aaae 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/framed_transport.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/framed_transport.go @@ -22,6 +22,7 @@ package thrift import ( "bufio" "bytes" + "context" "encoding/binary" "fmt" "io" @@ -135,7 +136,7 @@ func (p *TFramedTransport) WriteString(s string) (n int, err error) { return p.buf.WriteString(s) } -func (p *TFramedTransport) Flush() error { +func (p *TFramedTransport) Flush(ctx context.Context) error { size := p.buf.Len() buf := p.buffer[:4] binary.BigEndian.PutUint32(buf, uint32(size)) @@ -151,7 +152,7 @@ func (p *TFramedTransport) Flush() error { return NewTTransportExceptionFromError(err) } } - err = p.transport.Flush() + err = p.transport.Flush(ctx) return NewTTransportExceptionFromError(err) } diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/http_client.go b/vendor/github.com/apache/thrift/lib/go/thrift/http_client.go index 33f2aa4b59..5c82bf5387 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/http_client.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/http_client.go @@ -21,6 +21,7 @@ package thrift import ( "bytes" + "context" "io" "io/ioutil" "net/http" @@ -181,7 +182,7 @@ func (p *THttpClient) WriteString(s string) (n int, err error) { return p.requestBuffer.WriteString(s) } -func (p *THttpClient) Flush() error { +func (p *THttpClient) Flush(ctx context.Context) error { // Close any previous response body to avoid leaking connections. p.closeResponse() @@ -190,6 +191,9 @@ func (p *THttpClient) Flush() error { return NewTTransportExceptionFromError(err) } req.Header = p.header + if ctx != nil { + req = req.WithContext(ctx) + } response, err := p.client.Do(req) if err != nil { return NewTTransportExceptionFromError(err) diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/http_transport.go b/vendor/github.com/apache/thrift/lib/go/thrift/http_transport.go index 601855b926..66f0f388a0 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/http_transport.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/http_transport.go @@ -26,6 +26,18 @@ import ( "strings" ) +// NewThriftHandlerFunc is a function that create a ready to use Apache Thrift Handler function +func NewThriftHandlerFunc(processor TProcessor, + inPfactory, outPfactory TProtocolFactory) func(w http.ResponseWriter, r *http.Request) { + + return gz(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/x-thrift") + + transport := NewStreamTransport(r.Body, w) + processor.Process(r.Context(), inPfactory.GetProtocol(transport), outPfactory.GetProtocol(transport)) + }) +} + // gz transparently compresses the HTTP response if the client supports it. func gz(handler http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/http_transport_go17.go b/vendor/github.com/apache/thrift/lib/go/thrift/http_transport_go17.go deleted file mode 100644 index 1313ac225b..0000000000 --- a/vendor/github.com/apache/thrift/lib/go/thrift/http_transport_go17.go +++ /dev/null @@ -1,38 +0,0 @@ -// +build go1.7 - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 thrift - -import ( - "net/http" -) - -// NewThriftHandlerFunc is a function that create a ready to use Apache Thrift Handler function -func NewThriftHandlerFunc(processor TProcessor, - inPfactory, outPfactory TProtocolFactory) func(w http.ResponseWriter, r *http.Request) { - - return gz(func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("Content-Type", "application/x-thrift") - - transport := NewStreamTransport(r.Body, w) - processor.Process(r.Context(), inPfactory.GetProtocol(transport), outPfactory.GetProtocol(transport)) - }) -} diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/http_transport_pre_go17.go b/vendor/github.com/apache/thrift/lib/go/thrift/http_transport_pre_go17.go deleted file mode 100644 index 13aa1c11d1..0000000000 --- a/vendor/github.com/apache/thrift/lib/go/thrift/http_transport_pre_go17.go +++ /dev/null @@ -1,40 +0,0 @@ -// +build !go1.7 - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 thrift - -import ( - "net/http" - - "golang.org/x/net/context" -) - -// NewThriftHandlerFunc is a function that create a ready to use Apache Thrift Handler function -func NewThriftHandlerFunc(processor TProcessor, - inPfactory, outPfactory TProtocolFactory) func(w http.ResponseWriter, r *http.Request) { - - return gz(func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("Content-Type", "application/x-thrift") - - transport := NewStreamTransport(r.Body, w) - processor.Process(context.Background(), inPfactory.GetProtocol(transport), outPfactory.GetProtocol(transport)) - }) -} diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/iostream_transport.go b/vendor/github.com/apache/thrift/lib/go/thrift/iostream_transport.go index b18be81c46..fea93bceff 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/iostream_transport.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/iostream_transport.go @@ -21,6 +21,7 @@ package thrift import ( "bufio" + "context" "io" ) @@ -138,7 +139,7 @@ func (p *StreamTransport) Close() error { } // Flushes the underlying output stream if not null. -func (p *StreamTransport) Flush() error { +func (p *StreamTransport) Flush(ctx context.Context) error { if p.Writer == nil { return NewTTransportException(NOT_OPEN, "Cannot flush null outputStream") } diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/json_protocol.go b/vendor/github.com/apache/thrift/lib/go/thrift/json_protocol.go index 442fa9144d..7be685d43f 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/json_protocol.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/json_protocol.go @@ -20,6 +20,7 @@ package thrift import ( + "context" "encoding/base64" "fmt" ) @@ -438,10 +439,10 @@ func (p *TJSONProtocol) ReadBinary() ([]byte, error) { return v, p.ParsePostValue() } -func (p *TJSONProtocol) Flush() (err error) { +func (p *TJSONProtocol) Flush(ctx context.Context) (err error) { err = p.writer.Flush() if err == nil { - err = p.trans.Flush() + err = p.trans.Flush(ctx) } return NewTProtocolException(err) } diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/memory_buffer.go b/vendor/github.com/apache/thrift/lib/go/thrift/memory_buffer.go index 97a4edfa5d..5936d27303 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/memory_buffer.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/memory_buffer.go @@ -21,6 +21,7 @@ package thrift import ( "bytes" + "context" ) // Memory buffer-based implementation of the TTransport interface. @@ -70,7 +71,7 @@ func (p *TMemoryBuffer) Close() error { } // Flushing a memory buffer is a no-op -func (p *TMemoryBuffer) Flush() error { +func (p *TMemoryBuffer) Flush(ctx context.Context) error { return nil } diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/multiplexed_protocol.go b/vendor/github.com/apache/thrift/lib/go/thrift/multiplexed_protocol.go index b7f4f8a1cc..d028a30b33 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/multiplexed_protocol.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/multiplexed_protocol.go @@ -19,6 +19,12 @@ package thrift +import ( + "context" + "fmt" + "strings" +) + /* TMultiplexedProtocol is a protocol-independent concrete decorator that allows a Thrift client to communicate with a multiplexing Thrift server, @@ -122,6 +128,31 @@ func (t *TMultiplexedProcessor) RegisterProcessor(name string, processor TProces t.serviceProcessorMap[name] = processor } +func (t *TMultiplexedProcessor) Process(ctx context.Context, in, out TProtocol) (bool, TException) { + name, typeId, seqid, err := in.ReadMessageBegin() + if err != nil { + return false, err + } + if typeId != CALL && typeId != ONEWAY { + return false, fmt.Errorf("Unexpected message type %v", typeId) + } + //extract the service name + v := strings.SplitN(name, MULTIPLEXED_SEPARATOR, 2) + if len(v) != 2 { + if t.DefaultProcessor != nil { + smb := NewStoredMessageProtocol(in, name, typeId, seqid) + return t.DefaultProcessor.Process(ctx, smb, out) + } + return false, fmt.Errorf("Service name not found in message name: %s. Did you forget to use a TMultiplexProtocol in your client?", name) + } + actualProcessor, ok := t.serviceProcessorMap[v[0]] + if !ok { + return false, fmt.Errorf("Service name not found: %s. Did you forget to call registerProcessor()?", v[0]) + } + smb := NewStoredMessageProtocol(in, v[1], typeId, seqid) + return actualProcessor.Process(ctx, smb, out) +} + //Protocol that use stored message for ReadMessageBegin type storedMessageProtocol struct { TProtocol diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/multiplexed_protocol_go17.go b/vendor/github.com/apache/thrift/lib/go/thrift/multiplexed_protocol_go17.go deleted file mode 100644 index c71035e680..0000000000 --- a/vendor/github.com/apache/thrift/lib/go/thrift/multiplexed_protocol_go17.go +++ /dev/null @@ -1,53 +0,0 @@ -// +build go1.7 - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 thrift - -import ( - "context" - "fmt" - "strings" -) - -func (t *TMultiplexedProcessor) Process(ctx context.Context, in, out TProtocol) (bool, TException) { - name, typeId, seqid, err := in.ReadMessageBegin() - if err != nil { - return false, err - } - if typeId != CALL && typeId != ONEWAY { - return false, fmt.Errorf("Unexpected message type %v", typeId) - } - //extract the service name - v := strings.SplitN(name, MULTIPLEXED_SEPARATOR, 2) - if len(v) != 2 { - if t.DefaultProcessor != nil { - smb := NewStoredMessageProtocol(in, name, typeId, seqid) - return t.DefaultProcessor.Process(ctx, smb, out) - } - return false, fmt.Errorf("Service name not found in message name: %s. Did you forget to use a TMultiplexProtocol in your client?", name) - } - actualProcessor, ok := t.serviceProcessorMap[v[0]] - if !ok { - return false, fmt.Errorf("Service name not found: %s. Did you forget to call registerProcessor()?", v[0]) - } - smb := NewStoredMessageProtocol(in, v[1], typeId, seqid) - return actualProcessor.Process(ctx, smb, out) -} diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/multiplexed_protocol_pre_go17.go b/vendor/github.com/apache/thrift/lib/go/thrift/multiplexed_protocol_pre_go17.go deleted file mode 100644 index 5c27b38755..0000000000 --- a/vendor/github.com/apache/thrift/lib/go/thrift/multiplexed_protocol_pre_go17.go +++ /dev/null @@ -1,54 +0,0 @@ -// +build !go1.7 - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 thrift - -import ( - "fmt" - "strings" - - "golang.org/x/net/context" -) - -func (t *TMultiplexedProcessor) Process(ctx context.Context, in, out TProtocol) (bool, TException) { - name, typeId, seqid, err := in.ReadMessageBegin() - if err != nil { - return false, err - } - if typeId != CALL && typeId != ONEWAY { - return false, fmt.Errorf("Unexpected message type %v", typeId) - } - //extract the service name - v := strings.SplitN(name, MULTIPLEXED_SEPARATOR, 2) - if len(v) != 2 { - if t.DefaultProcessor != nil { - smb := NewStoredMessageProtocol(in, name, typeId, seqid) - return t.DefaultProcessor.Process(ctx, smb, out) - } - return false, fmt.Errorf("Service name not found in message name: %s. Did you forget to use a TMultiplexProtocol in your client?", name) - } - actualProcessor, ok := t.serviceProcessorMap[v[0]] - if !ok { - return false, fmt.Errorf("Service name not found: %s. Did you forget to call registerProcessor()?", v[0]) - } - smb := NewStoredMessageProtocol(in, v[1], typeId, seqid) - return actualProcessor.Process(ctx, smb, out) -} diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/pre_go17.go b/vendor/github.com/apache/thrift/lib/go/thrift/pre_go17.go deleted file mode 100644 index cb564b8dbd..0000000000 --- a/vendor/github.com/apache/thrift/lib/go/thrift/pre_go17.go +++ /dev/null @@ -1,26 +0,0 @@ -// +build !go1.7 - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 thrift - -import "golang.org/x/net/context" - -var defaultCtx = context.Background() diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/processor.go b/vendor/github.com/apache/thrift/lib/go/thrift/processor.go deleted file mode 100644 index 566aaaf718..0000000000 --- a/vendor/github.com/apache/thrift/lib/go/thrift/processor.go +++ /dev/null @@ -1,34 +0,0 @@ -// +build !go1.7 - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 thrift - -import "golang.org/x/net/context" - -// A processor is a generic object which operates upon an input stream and -// writes to some output stream. -type TProcessor interface { - Process(ctx context.Context, in, out TProtocol) (bool, TException) -} - -type TProcessorFunction interface { - Process(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException) -} diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/processor_factory.go b/vendor/github.com/apache/thrift/lib/go/thrift/processor_factory.go index 9d645df244..e4b132b307 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/processor_factory.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/processor_factory.go @@ -19,6 +19,18 @@ package thrift +import "context" + +// A processor is a generic object which operates upon an input stream and +// writes to some output stream. +type TProcessor interface { + Process(ctx context.Context, in, out TProtocol) (bool, TException) +} + +type TProcessorFunction interface { + Process(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException) +} + // The default processor factory just returns a singleton // instance. type TProcessorFactory interface { diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/processor_go17.go b/vendor/github.com/apache/thrift/lib/go/thrift/processor_go17.go deleted file mode 100644 index fb0b165dcc..0000000000 --- a/vendor/github.com/apache/thrift/lib/go/thrift/processor_go17.go +++ /dev/null @@ -1,34 +0,0 @@ -// +build go1.7 - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 thrift - -import "context" - -// A processor is a generic object which operates upon an input stream and -// writes to some output stream. -type TProcessor interface { - Process(ctx context.Context, in, out TProtocol) (bool, TException) -} - -type TProcessorFunction interface { - Process(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException) -} diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/protocol.go b/vendor/github.com/apache/thrift/lib/go/thrift/protocol.go index 25e6d24b90..615b7a4a8f 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/protocol.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/protocol.go @@ -20,6 +20,7 @@ package thrift import ( + "context" "errors" "fmt" ) @@ -74,7 +75,7 @@ type TProtocol interface { ReadBinary() (value []byte, err error) Skip(fieldType TType) (err error) - Flush() (err error) + Flush(ctx context.Context) (err error) Transport() TTransport } diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/serializer.go b/vendor/github.com/apache/thrift/lib/go/thrift/serializer.go index 7712229990..1ff4d37545 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/serializer.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/serializer.go @@ -19,6 +19,10 @@ package thrift +import ( + "context" +) + type TSerializer struct { Transport *TMemoryBuffer Protocol TProtocol @@ -38,35 +42,35 @@ func NewTSerializer() *TSerializer { protocol} } -func (t *TSerializer) WriteString(msg TStruct) (s string, err error) { +func (t *TSerializer) WriteString(ctx context.Context, msg TStruct) (s string, err error) { t.Transport.Reset() if err = msg.Write(t.Protocol); err != nil { return } - if err = t.Protocol.Flush(); err != nil { + if err = t.Protocol.Flush(ctx); err != nil { return } - if err = t.Transport.Flush(); err != nil { + if err = t.Transport.Flush(ctx); err != nil { return } return t.Transport.String(), nil } -func (t *TSerializer) Write(msg TStruct) (b []byte, err error) { +func (t *TSerializer) Write(ctx context.Context, msg TStruct) (b []byte, err error) { t.Transport.Reset() if err = msg.Write(t.Protocol); err != nil { return } - if err = t.Protocol.Flush(); err != nil { + if err = t.Protocol.Flush(ctx); err != nil { return } - if err = t.Transport.Flush(); err != nil { + if err = t.Transport.Flush(ctx); err != nil { return } diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/simple_json_protocol.go b/vendor/github.com/apache/thrift/lib/go/thrift/simple_json_protocol.go index 7353322318..2e8a71112a 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/simple_json_protocol.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/simple_json_protocol.go @@ -22,6 +22,7 @@ package thrift import ( "bufio" "bytes" + "context" "encoding/base64" "encoding/json" "fmt" @@ -552,7 +553,7 @@ func (p *TSimpleJSONProtocol) ReadBinary() ([]byte, error) { return v, p.ParsePostValue() } -func (p *TSimpleJSONProtocol) Flush() (err error) { +func (p *TSimpleJSONProtocol) Flush(ctx context.Context) (err error) { return NewTProtocolException(p.writer.Flush()) } @@ -1064,7 +1065,7 @@ func (p *TSimpleJSONProtocol) ParseListEnd() error { for _, char := range line { switch char { default: - e := fmt.Errorf("Expecting end of list \"]\", but found: \"", line, "\"") + e := fmt.Errorf("Expecting end of list \"]\", but found: \"%v\"", line) return NewTProtocolExceptionWithType(INVALID_DATA, e) case ' ', '\n', '\r', '\t', rune(JSON_RBRACKET[0]): break diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/simple_server.go b/vendor/github.com/apache/thrift/lib/go/thrift/simple_server.go index 37081bd835..6035802516 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/simple_server.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/simple_server.go @@ -125,26 +125,38 @@ func (p *TSimpleServer) Listen() error { return p.serverTransport.Listen() } +func (p *TSimpleServer) innerAccept() (int32, error) { + client, err := p.serverTransport.Accept() + p.mu.Lock() + defer p.mu.Unlock() + closed := atomic.LoadInt32(&p.closed) + if closed != 0 { + return closed, nil + } + if err != nil { + return 0, err + } + if client != nil { + p.wg.Add(1) + go func() { + defer p.wg.Done() + if err := p.processRequests(client); err != nil { + log.Println("error processing request:", err) + } + }() + } + return 0, nil +} + func (p *TSimpleServer) AcceptLoop() error { for { - client, err := p.serverTransport.Accept() - p.mu.Lock() - if atomic.LoadInt32(&p.closed) != 0 { - return nil - } + closed, err := p.innerAccept() if err != nil { return err } - if client != nil { - p.wg.Add(1) - go func() { - defer p.wg.Done() - if err := p.processRequests(client); err != nil { - log.Println("error processing request:", err) - } - }() + if closed != 0 { + return nil } - p.mu.Unlock() } } diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/socket.go b/vendor/github.com/apache/thrift/lib/go/thrift/socket.go index 383b1fe3e9..8854279651 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/socket.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/socket.go @@ -20,6 +20,7 @@ package thrift import ( + "context" "net" "time" ) @@ -148,7 +149,7 @@ func (p *TSocket) Write(buf []byte) (int, error) { return p.conn.Write(buf) } -func (p *TSocket) Flush() error { +func (p *TSocket) Flush(ctx context.Context) error { return nil } diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/ssl_socket.go b/vendor/github.com/apache/thrift/lib/go/thrift/ssl_socket.go index c3bd72cc46..ba63377263 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/ssl_socket.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/ssl_socket.go @@ -20,6 +20,7 @@ package thrift import ( + "context" "crypto/tls" "net" "time" @@ -158,7 +159,7 @@ func (p *TSSLSocket) Write(buf []byte) (int, error) { return p.conn.Write(buf) } -func (p *TSSLSocket) Flush() error { +func (p *TSSLSocket) Flush(ctx context.Context) error { return nil } diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/transport.go b/vendor/github.com/apache/thrift/lib/go/thrift/transport.go index 70a85a8489..ba2738a8df 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/transport.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/transport.go @@ -20,6 +20,7 @@ package thrift import ( + "context" "errors" "io" ) @@ -30,6 +31,10 @@ type Flusher interface { Flush() (err error) } +type ContextFlusher interface { + Flush(ctx context.Context) (err error) +} + type ReadSizeProvider interface { RemainingBytes() (num_bytes uint64) } @@ -37,7 +42,7 @@ type ReadSizeProvider interface { // Encapsulates the I/O layer type TTransport interface { io.ReadWriteCloser - Flusher + ContextFlusher ReadSizeProvider // Opens the transport for communication @@ -60,6 +65,6 @@ type TRichTransport interface { io.ByteReader io.ByteWriter stringWriter - Flusher + ContextFlusher ReadSizeProvider } diff --git a/vendor/github.com/apache/thrift/lib/go/thrift/zlib_transport.go b/vendor/github.com/apache/thrift/lib/go/thrift/zlib_transport.go index f2f0732221..f3d42673af 100644 --- a/vendor/github.com/apache/thrift/lib/go/thrift/zlib_transport.go +++ b/vendor/github.com/apache/thrift/lib/go/thrift/zlib_transport.go @@ -21,6 +21,7 @@ package thrift import ( "compress/zlib" + "context" "io" "log" ) @@ -91,11 +92,11 @@ func (z *TZlibTransport) Close() error { } // Flush flushes the writer and its underlying transport. -func (z *TZlibTransport) Flush() error { +func (z *TZlibTransport) Flush(ctx context.Context) error { if err := z.writer.Flush(); err != nil { return err } - return z.transport.Flush() + return z.transport.Flush(ctx) } // IsOpen returns true if the transport is open diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/address.go b/vendor/github.com/coredns/coredns/core/dnsserver/address.go index 5d3d2b393f..8f544e97a2 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/address.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/address.go @@ -6,8 +6,6 @@ import ( "strings" "github.com/coredns/coredns/plugin" - "github.com/coredns/coredns/plugin/pkg/parse" - "github.com/coredns/coredns/plugin/pkg/transport" "github.com/miekg/dns" ) @@ -29,13 +27,43 @@ func (z zoneAddr) String() string { return s } +// Transport returns the protocol of the string s +func Transport(s string) string { + switch { + case strings.HasPrefix(s, TransportTLS+"://"): + return TransportTLS + case strings.HasPrefix(s, TransportDNS+"://"): + return TransportDNS + case strings.HasPrefix(s, TransportGRPC+"://"): + return TransportGRPC + case strings.HasPrefix(s, TransportHTTPS+"://"): + return TransportHTTPS + } + return TransportDNS +} + // 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) { var err error - var trans string - trans, str = parse.Transport(str) + // Default to DNS if there isn't a transport protocol prefix. + trans := TransportDNS + + switch { + case strings.HasPrefix(str, TransportTLS+"://"): + trans = TransportTLS + str = str[len(TransportTLS+"://"):] + case strings.HasPrefix(str, TransportDNS+"://"): + trans = TransportDNS + str = str[len(TransportDNS+"://"):] + case strings.HasPrefix(str, TransportGRPC+"://"): + trans = TransportGRPC + str = str[len(TransportGRPC+"://"):] + case strings.HasPrefix(str, TransportHTTPS+"://"): + trans = TransportHTTPS + str = str[len(TransportHTTPS+"://"):] + } host, port, ipnet, err := plugin.SplitHostPort(str) if err != nil { @@ -43,15 +71,17 @@ func normalizeZone(str string) (zoneAddr, error) { } if port == "" { - switch trans { - case transport.DNS: + if trans == TransportDNS { port = Port - case transport.TLS: - port = transport.TLSPort - case transport.GRPC: - port = transport.GRPCPort - case transport.HTTPS: - port = transport.HTTPSPort + } + if trans == TransportTLS { + port = TLSPort + } + if trans == TransportGRPC { + port = GRPCPort + } + if trans == TransportHTTPS { + port = HTTPSPort } } @@ -73,6 +103,14 @@ func SplitProtocolHostPort(address string) (protocol string, ip string, port str } } +// Supported transports. +const ( + TransportDNS = "dns" + TransportTLS = "tls" + TransportGRPC = "grpc" + TransportHTTPS = "https" +) + type zoneOverlap struct { registeredAddr map[zoneAddr]zoneAddr // each zoneAddr is registered once by its key unboundOverlap map[zoneAddr]zoneAddr // the "no bind" equiv ZoneAdddr is registered by its original key diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/listen_go111.go b/vendor/github.com/coredns/coredns/core/dnsserver/listen_go111.go deleted file mode 100644 index d71c9292bd..0000000000 --- a/vendor/github.com/coredns/coredns/core/dnsserver/listen_go111.go +++ /dev/null @@ -1,32 +0,0 @@ -// +build go1.11 -// +build aix darwin dragonfly freebsd linux netbsd openbsd - -package dnsserver - -import ( - "context" - "net" - "syscall" - - "github.com/coredns/coredns/plugin/pkg/log" - "golang.org/x/sys/unix" -) - -func reuseportControl(network, address string, c syscall.RawConn) error { - c.Control(func(fd uintptr) { - if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil { - log.Warningf("Failed to set SO_REUSEPORT on socket: %s", err) - } - }) - return nil -} - -func listen(network, addr string) (net.Listener, error) { - lc := net.ListenConfig{Control: reuseportControl} - return lc.Listen(context.Background(), network, addr) -} - -func listenPacket(network, addr string) (net.PacketConn, error) { - lc := net.ListenConfig{Control: reuseportControl} - return lc.ListenPacket(context.Background(), network, addr) -} diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/listen_go_not111.go b/vendor/github.com/coredns/coredns/core/dnsserver/listen_go_not111.go deleted file mode 100644 index 11021d0999..0000000000 --- a/vendor/github.com/coredns/coredns/core/dnsserver/listen_go_not111.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build !go1.11 !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd - -package dnsserver - -import "net" - -func listen(network, addr string) (net.Listener, error) { return net.Listen(network, addr) } - -func listenPacket(network, addr string) (net.PacketConn, error) { - return net.ListenPacket(network, addr) -} diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/register.go b/vendor/github.com/coredns/coredns/core/dnsserver/register.go index 89066d1083..ced2519af1 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/register.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/register.go @@ -9,8 +9,6 @@ import ( "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/pkg/dnsutil" - "github.com/coredns/coredns/plugin/pkg/parse" - "github.com/coredns/coredns/plugin/pkg/transport" "github.com/mholt/caddy" "github.com/mholt/caddy/caddyfile" @@ -113,29 +111,29 @@ func (h *dnsContext) MakeServers() ([]caddy.Server, error) { var servers []caddy.Server for addr, group := range groups { // switch on addr - switch tr, _ := parse.Transport(addr); tr { - case transport.DNS: + switch Transport(addr) { + case TransportDNS: s, err := NewServer(addr, group) if err != nil { return nil, err } servers = append(servers, s) - case transport.TLS: + case TransportTLS: s, err := NewServerTLS(addr, group) if err != nil { return nil, err } servers = append(servers, s) - case transport.GRPC: + case TransportGRPC: s, err := NewServergRPC(addr, group) if err != nil { return nil, err } servers = append(servers, s) - case transport.HTTPS: + case TransportHTTPS: s, err := NewServerHTTPS(addr, group) if err != nil { return nil, err @@ -236,8 +234,16 @@ func groupConfigsByListenAddr(configs []*Config) (map[string][]*Config, error) { return groups, nil } -// DefaultPort is the default port. -const DefaultPort = transport.Port +const ( + // DefaultPort is the default port. + DefaultPort = "53" + // TLSPort is the default port for DNS-over-TLS. + TLSPort = "853" + // GRPCPort is the default port for DNS-over-gRPC. + GRPCPort = "443" + // HTTPSPort is the default port for DNS-over-HTTPS. + HTTPSPort = "443" +) // These "soft defaults" are configurable by // command line flags, etc. diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/server.go b/vendor/github.com/coredns/coredns/core/dnsserver/server.go index 5ca826b0ae..5242a6d2db 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/server.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/server.go @@ -15,7 +15,6 @@ import ( "github.com/coredns/coredns/plugin/pkg/log" "github.com/coredns/coredns/plugin/pkg/rcode" "github.com/coredns/coredns/plugin/pkg/trace" - "github.com/coredns/coredns/plugin/pkg/transport" "github.com/coredns/coredns/request" "github.com/miekg/dns" @@ -135,7 +134,7 @@ func (s *Server) ServePacket(p net.PacketConn) error { // Listen implements caddy.TCPServer interface. func (s *Server) Listen() (net.Listener, error) { - l, err := listen("tcp", s.Addr[len(transport.DNS+"://"):]) + l, err := net.Listen("tcp", s.Addr[len(TransportDNS+"://"):]) if err != nil { return nil, err } @@ -144,7 +143,7 @@ func (s *Server) Listen() (net.Listener, error) { // ListenPacket implements caddy.UDPServer interface. func (s *Server) ListenPacket() (net.PacketConn, error) { - p, err := listenPacket("udp", s.Addr[len(transport.DNS+"://"):]) + p, err := net.ListenPacket("udp", s.Addr[len(TransportDNS+"://"):]) if err != nil { return nil, err } @@ -238,9 +237,6 @@ func (s *Server) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) var dshandler *Config - // Wrap the response writer in a ScrubWriter so we automatically make the reply fit in the client's buffer. - w = request.NewScrubWriter(r, w) - for { l := len(q[off:]) for i := 0; i < l; i++ { 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 7de36a5fda..e5b87749d9 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/server_grpc.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/server_grpc.go @@ -8,7 +8,6 @@ import ( "net" "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" @@ -74,7 +73,7 @@ func (s *ServergRPC) ServePacket(p net.PacketConn) error { return nil } // Listen implements caddy.TCPServer interface. func (s *ServergRPC) Listen() (net.Listener, error) { - l, err := net.Listen("tcp", s.Addr[len(transport.GRPC+"://"):]) + l, err := net.Listen("tcp", s.Addr[len(TransportGRPC+"://"):]) if err != nil { return nil, err } @@ -91,7 +90,7 @@ func (s *ServergRPC) OnStartupComplete() { return } - out := startUpZones(transport.GRPC+"://", s.Addr, s.zones) + out := startUpZones(TransportGRPC+"://", s.Addr, s.zones) if out != "" { fmt.Print(out) } 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 1e184e0449..9b1eaaa7e2 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/server_https.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/server_https.go @@ -12,7 +12,6 @@ import ( "github.com/coredns/coredns/plugin/pkg/dnsutil" "github.com/coredns/coredns/plugin/pkg/doh" "github.com/coredns/coredns/plugin/pkg/response" - "github.com/coredns/coredns/plugin/pkg/transport" ) // ServerHTTPS represents an instance of a DNS-over-HTTPS server. @@ -61,7 +60,7 @@ func (s *ServerHTTPS) ServePacket(p net.PacketConn) error { return nil } // Listen implements caddy.TCPServer interface. func (s *ServerHTTPS) Listen() (net.Listener, error) { - l, err := net.Listen("tcp", s.Addr[len(transport.HTTPS+"://"):]) + l, err := net.Listen("tcp", s.Addr[len(TransportHTTPS+"://"):]) if err != nil { return nil, err } @@ -78,7 +77,7 @@ func (s *ServerHTTPS) OnStartupComplete() { return } - out := startUpZones(transport.HTTPS+"://", s.Addr, s.zones) + out := startUpZones(TransportHTTPS+"://", s.Addr, s.zones) if out != "" { fmt.Print(out) } diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/server_tls.go b/vendor/github.com/coredns/coredns/core/dnsserver/server_tls.go index a63ac848ae..0fd0c1fbe7 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/server_tls.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/server_tls.go @@ -6,8 +6,6 @@ import ( "fmt" "net" - "github.com/coredns/coredns/plugin/pkg/transport" - "github.com/miekg/dns" ) @@ -57,7 +55,7 @@ func (s *ServerTLS) ServePacket(p net.PacketConn) error { return nil } // Listen implements caddy.TCPServer interface. func (s *ServerTLS) Listen() (net.Listener, error) { - l, err := net.Listen("tcp", s.Addr[len(transport.TLS+"://"):]) + l, err := net.Listen("tcp", s.Addr[len(TransportTLS+"://"):]) if err != nil { return nil, err } @@ -74,7 +72,7 @@ func (s *ServerTLS) OnStartupComplete() { return } - out := startUpZones(transport.TLS+"://", s.Addr, s.zones) + out := startUpZones(TransportTLS+"://", s.Addr, s.zones) if out != "" { fmt.Print(out) } diff --git a/vendor/github.com/coredns/coredns/core/dnsserver/zdirectives.go b/vendor/github.com/coredns/coredns/core/dnsserver/zdirectives.go index 280c031444..e2eed21171 100644 --- a/vendor/github.com/coredns/coredns/core/dnsserver/zdirectives.go +++ b/vendor/github.com/coredns/coredns/core/dnsserver/zdirectives.go @@ -39,7 +39,6 @@ var Directives = []string{ "auto", "secondary", "etcd", - "loop", "forward", "proxy", "erratic", diff --git a/vendor/github.com/coredns/coredns/coremain/run.go b/vendor/github.com/coredns/coredns/coremain/run.go index 63ac6f354d..98eaa5ba86 100644 --- a/vendor/github.com/coredns/coredns/coremain/run.go +++ b/vendor/github.com/coredns/coredns/coremain/run.go @@ -42,7 +42,7 @@ func Run() { caddy.TrapSignals() // Reset flag.CommandLine to get rid of unwanted flags for instance from glog (used in kubernetes). - // And read the ones we want to keep. + // And readd the once we want to keep. flag.VisitAll(func(f *flag.Flag) { if _, ok := flagsBlacklist[f.Name]; ok { return @@ -62,7 +62,7 @@ func Run() { } log.SetOutput(os.Stdout) - log.SetFlags(0) // Set to 0 because we're doing our own time, with timezone + log.SetFlags(log.LstdFlags) if version { showVersion() diff --git a/vendor/github.com/coredns/coredns/coremain/version.go b/vendor/github.com/coredns/coredns/coremain/version.go index ae274821d9..7e73e5adb7 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.2.6" + CoreVersion = "1.2.0" coreName = "CoreDNS" serverType = "dns" ) diff --git a/vendor/github.com/coredns/coredns/plugin/backend_lookup.go b/vendor/github.com/coredns/coredns/plugin/backend_lookup.go index 77e08174f1..3946c9fee5 100644 --- a/vendor/github.com/coredns/coredns/plugin/backend_lookup.go +++ b/vendor/github.com/coredns/coredns/plugin/backend_lookup.go @@ -40,22 +40,24 @@ func A(b ServiceBackend, zone string, state request.Request, previousRecords []d if dnsutil.DuplicateCNAME(newRecord, previousRecords) { continue } - if dns.IsSubDomain(zone, dns.Fqdn(serv.Host)) { - state1 := state.NewWithQuestion(serv.Host, state.QType()) - state1.Zone = zone - 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. - if len(nextRecords) > 0 { - records = append(records, newRecord) - records = append(records, nextRecords...) - } + state1 := state.NewWithQuestion(serv.Host, state.QType()) + 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. + if len(nextRecords) > 0 { + records = append(records, newRecord) + records = append(records, nextRecords...) } continue } // This means we can not complete the CNAME, try to look else where. target := newRecord.Target + if dns.IsSubDomain(zone, target) { + // We should already have found it + continue + } // Lookup m1, e1 := b.Lookup(state, target, state.QType()) if e1 != nil { @@ -108,20 +110,19 @@ func AAAA(b ServiceBackend, zone string, state request.Request, previousRecords if dnsutil.DuplicateCNAME(newRecord, previousRecords) { continue } - if dns.IsSubDomain(zone, dns.Fqdn(serv.Host)) { - state1 := state.NewWithQuestion(serv.Host, state.QType()) - state1.Zone = zone - 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. - if len(nextRecords) > 0 { - records = append(records, newRecord) - records = append(records, nextRecords...) - } + state1 := state.NewWithQuestion(serv.Host, state.QType()) + 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. + if len(nextRecords) > 0 { + records = append(records, newRecord) + records = append(records, nextRecords...) } continue } + // This means we can not complete the CNAME, try to look else where. target := newRecord.Target m1, e1 := b.Lookup(state, target, state.QType()) @@ -172,11 +173,6 @@ func SRV(b ServiceBackend, zone string, state request.Request, opt Options) (rec w[serv.Priority] += weight } for _, serv := range services { - // Don't add the entry if the port is -1 (invalid). The kubernetes plugin uses port -1 when a service/endpoint - // does not have any declared ports. - if serv.Port == -1 { - continue - } w1 := 100.0 / float64(w[serv.Priority]) if serv.Weight == 0 { w1 *= 100 @@ -446,7 +442,7 @@ func checkForApex(b ServiceBackend, zone string, state request.Request, opt Opti // this is equivalent to the NS search code. old := state.QName() state.Clear() - state.Req.Question[0].Name = dnsutil.Join("apex.dns", zone) + state.Req.Question[0].Name = dnsutil.Join([]string{"apex.dns", zone}) services, err := b.Services(state, false, opt) if err == nil { diff --git a/vendor/github.com/coredns/coredns/plugin/cache/README.md b/vendor/github.com/coredns/coredns/plugin/cache/README.md index ad2483ae97..e48152682b 100644 --- a/vendor/github.com/coredns/coredns/plugin/cache/README.md +++ b/vendor/github.com/coredns/coredns/plugin/cache/README.md @@ -24,15 +24,16 @@ cache [TTL] [ZONES...] * **ZONES** zones it should cache for. If empty, the zones from the configuration block are used. Each element in the cache is cached according to its TTL (with **TTL** as the max). -A cache is divided into 256 shards, each holding up to 39 items by default - for a total size -of 256 * 39 = 9984 items. +For the negative cache, the SOA's MinTTL value is used. A TTL of zero is not allowed. +A cache is divided into 256 shards, each holding up to 512 items by default - for a total size +of 256 * 512 = 131,072 items. If you want more control: ~~~ txt cache [TTL] [ZONES...] { - success CAPACITY [TTL] [MINTTL] - denial CAPACITY [TTL] [MINTTL] + success CAPACITY [TTL] + denial CAPACITY [TTL] prefetch AMOUNT [[DURATION] [PERCENTAGE%]] } ~~~ @@ -40,10 +41,8 @@ cache [TTL] [ZONES...] { * **TTL** and **ZONES** as above. * `success`, override the settings for caching successful responses. **CAPACITY** indicates the maximum number of packets we cache before we start evicting (*randomly*). **TTL** overrides the cache maximum TTL. - **MINTTL** overrides the cache minimum TTL (default 5), which can be useful to limit queries to the backend. * `denial`, override the settings for caching denial of existence responses. **CAPACITY** indicates the maximum number of packets we cache before we start evicting (LRU). **TTL** overrides the cache maximum TTL. - **MINTTL** overrides the cache minimum TTL (default 5), which can be useful to limit queries to the backend. There is a third category (`error`) but those responses are never cached. * `prefetch` will prefetch popular items when they are about to be expunged from the cache. Popular means **AMOUNT** queries have been seen with no gaps of **DURATION** or more between them. @@ -53,13 +52,11 @@ cache [TTL] [ZONES...] { ## Capacity and Eviction -If **CAPACITY** _is not_ specified, the default cache size is 9984 per cache. The minimum allowed cache size is 1024. -If **CAPACITY** _is_ specified, the actual cache size used will be rounded down to the nearest number divisible by 256 (so all shards are equal in size). +When specifying **CAPACITY**, the minimum cache capacity is 131,072. Specifying a lower value will be +ignored. Specifying a **CAPACITY** of zero does not disable the cache. + +Eviction is done per shard - i.e. when a shard reaches capacity, items are evicted from that shard. Since shards don't fill up perfectly evenly, evictions will occur before the entire cache reaches full capacity. Each shard capacity is equal to the total cache size / number of shards (256). -Eviction is done per shard. In effect, when a shard reaches capacity, items are evicted from that shard. -Since shards don't fill up perfectly evenly, evictions will occur before the entire cache reaches full capacity. -Each shard capacity is equal to the total cache size / number of shards (256). Eviction is random, not TTL based. -Entries with 0 TTL will remain in the cache until randomly evicted when the shard reaches capacity. ## Metrics @@ -92,13 +89,3 @@ Proxy to Google Public DNS and only cache responses for example.org (or below). cache example.org } ~~~ - -Enable caching for all zones, keep a positive cache size of 5000 and a negative cache size of 2500: - ~~~ corefile - . { - cache { - success 5000 - denial 2500 - } - } - ~~~ diff --git a/vendor/github.com/coredns/coredns/plugin/cache/cache.go b/vendor/github.com/coredns/coredns/plugin/cache/cache.go index cd0fda5052..c462676588 100644 --- a/vendor/github.com/coredns/coredns/plugin/cache/cache.go +++ b/vendor/github.com/coredns/coredns/plugin/cache/cache.go @@ -2,6 +2,7 @@ package cache import ( + "encoding/binary" "hash/fnv" "net" "time" @@ -21,15 +22,13 @@ type Cache struct { Next plugin.Handler Zones []string - ncache *cache.Cache - ncap int - nttl time.Duration - minnttl time.Duration + ncache *cache.Cache + ncap int + nttl time.Duration - pcache *cache.Cache - pcap int - pttl time.Duration - minpttl time.Duration + pcache *cache.Cache + pcap int + pttl time.Duration // Prefetch. prefetch int @@ -48,11 +47,9 @@ func New() *Cache { pcap: defaultCap, pcache: cache.New(defaultCap), pttl: maxTTL, - minpttl: minTTL, ncap: defaultCap, ncache: cache.New(defaultCap), nttl: maxNTTL, - minnttl: minNTTL, prefetch: 0, duration: 1 * time.Minute, percentage: 10, @@ -60,27 +57,27 @@ func New() *Cache { } } -// key returns key under which we store the item, -1 will be returned if we don't store the message. +// Return key under which we store the item, -1 will be returned if we don't store the +// message. // Currently we do not cache Truncated, errors zone transfers or dynamic update messages. -// qname holds the already lowercased qname. -func key(qname string, m *dns.Msg, t response.Type, do bool) (bool, uint64) { +func key(m *dns.Msg, t response.Type, do bool) int { // We don't store truncated responses. if m.Truncated { - return false, 0 + return -1 } // Nor errors or Meta or Update if t == response.OtherError || t == response.Meta || t == response.Update { - return false, 0 + return -1 } - return true, hash(qname, m.Question[0].Qtype, do) + return int(hash(m.Question[0].Name, m.Question[0].Qtype, do)) } var one = []byte("1") var zero = []byte("0") -func hash(qname string, qtype uint16, do bool) uint64 { - h := fnv.New64() +func hash(qname string, qtype uint16, do bool) uint32 { + h := fnv.New32() if do { h.Write(one) @@ -88,21 +85,19 @@ func hash(qname string, qtype uint16, do bool) uint64 { h.Write(zero) } - h.Write([]byte{byte(qtype >> 8)}) - h.Write([]byte{byte(qtype)}) - h.Write([]byte(qname)) - return h.Sum64() -} + b := make([]byte, 2) + binary.BigEndian.PutUint16(b, qtype) + h.Write(b) -func computeTTL(msgTTL, minTTL, maxTTL time.Duration) time.Duration { - ttl := msgTTL - if ttl < minTTL { - ttl = minTTL + for i := range qname { + c := qname[i] + if c >= 'A' && c <= 'Z' { + c += 'a' - 'A' + } + h.Write([]byte{c}) } - if ttl > maxTTL { - ttl = maxTTL - } - return ttl + + return h.Sum32() } // ResponseWriter is a response writer that caches the reply message. @@ -157,17 +152,19 @@ func (w *ResponseWriter) WriteMsg(res *dns.Msg) error { } // key returns empty string for anything we don't want to cache. - hasKey, key := key(w.state.Name(), res, mt, do) + key := key(res, mt, do) - msgTTL := dnsutil.MinimalTTL(res, mt) - var duration time.Duration + duration := w.pttl if mt == response.NameError || mt == response.NoData { - duration = computeTTL(msgTTL, w.minnttl, w.nttl) - } else { - duration = computeTTL(msgTTL, w.minpttl, w.pttl) + duration = w.nttl } - if hasKey && duration > 0 { + msgTTL := dnsutil.MinimalTTL(res, mt) + if msgTTL < duration { + duration = msgTTL + } + + if key != -1 && duration > 0 { if w.state.Match(res) { w.set(res, key, mt, duration) cacheSize.WithLabelValues(w.server, Success).Set(float64(w.pcache.Len())) @@ -198,17 +195,19 @@ func (w *ResponseWriter) WriteMsg(res *dns.Msg) error { return w.ResponseWriter.WriteMsg(res) } -func (w *ResponseWriter) set(m *dns.Msg, key uint64, mt response.Type, duration time.Duration) { - // duration is expected > 0 - // and key is valid +func (w *ResponseWriter) set(m *dns.Msg, key int, mt response.Type, duration time.Duration) { + if key == -1 || duration == 0 { + return + } + switch mt { case response.NoError, response.Delegation: i := newItem(m, w.now(), duration) - w.pcache.Add(key, i) + w.pcache.Add(uint32(key), i) case response.NameError, response.NoData: i := newItem(m, w.now(), duration) - w.ncache.Add(key, i) + w.ncache.Add(uint32(key), i) case response.OtherError: // don't cache these @@ -229,9 +228,7 @@ func (w *ResponseWriter) Write(buf []byte) (int, error) { const ( maxTTL = dnsutil.MaximumDefaulTTL - minTTL = dnsutil.MinimalDefaultTTL maxNTTL = dnsutil.MaximumDefaulTTL / 2 - minNTTL = dnsutil.MinimalDefaultTTL defaultCap = 10000 // default capacity of the cache. diff --git a/vendor/github.com/coredns/coredns/plugin/cache/handler.go b/vendor/github.com/coredns/coredns/plugin/cache/handler.go index 2d608e8d35..11e1323f67 100644 --- a/vendor/github.com/coredns/coredns/plugin/cache/handler.go +++ b/vendor/github.com/coredns/coredns/plugin/cache/handler.go @@ -3,6 +3,7 @@ package cache import ( "context" "math" + "sync" "time" "github.com/coredns/coredns/plugin" @@ -30,6 +31,8 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) if i != nil && found { resp := i.toMsg(r, now) + state.SizeAndDo(resp) + resp, _ = state.Scrub(resp) w.WriteMsg(resp) if c.prefetch > 0 { @@ -125,3 +128,5 @@ var ( Help: "The number responses that are not cached, because the reply is malformed.", }, []string{"server"}) ) + +var once sync.Once diff --git a/vendor/github.com/coredns/coredns/plugin/cache/setup.go b/vendor/github.com/coredns/coredns/plugin/cache/setup.go index d6052b1627..17a588885a 100644 --- a/vendor/github.com/coredns/coredns/plugin/cache/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/cache/setup.go @@ -34,9 +34,11 @@ func setup(c *caddy.Controller) error { }) c.OnStartup(func() error { - metrics.MustRegister(c, - cacheSize, cacheHits, cacheMisses, - cachePrefetches, cacheDrops) + once.Do(func() { + metrics.MustRegister(c, + cacheSize, cacheHits, cacheMisses, + cachePrefetches, cacheDrops) + }) return nil }) @@ -99,17 +101,6 @@ func cacheParse(c *caddy.Controller) (*Cache, error) { return nil, fmt.Errorf("cache TTL can not be zero or negative: %d", pttl) } ca.pttl = time.Duration(pttl) * time.Second - if len(args) > 2 { - minpttl, err := strconv.Atoi(args[2]) - if err != nil { - return nil, err - } - // Reserve < 0 - if minpttl < 0 { - return nil, fmt.Errorf("cache min TTL can not be negative: %d", minpttl) - } - ca.minpttl = time.Duration(minpttl) * time.Second - } } case Denial: args := c.RemainingArgs() @@ -131,17 +122,6 @@ func cacheParse(c *caddy.Controller) (*Cache, error) { return nil, fmt.Errorf("cache TTL can not be zero or negative: %d", nttl) } ca.nttl = time.Duration(nttl) * time.Second - if len(args) > 2 { - minnttl, err := strconv.Atoi(args[2]) - if err != nil { - return nil, err - } - // Reserve < 0 - if minnttl < 0 { - return nil, fmt.Errorf("cache min TTL can not be negative: %d", minnttl) - } - ca.minnttl = time.Duration(minnttl) * time.Second - } } case "prefetch": args := c.RemainingArgs() diff --git a/vendor/github.com/coredns/coredns/plugin/dnstap/handler.go b/vendor/github.com/coredns/coredns/plugin/dnstap/handler.go index b09c704066..1c411e2aaa 100644 --- a/vendor/github.com/coredns/coredns/plugin/dnstap/handler.go +++ b/vendor/github.com/coredns/coredns/plugin/dnstap/handler.go @@ -51,7 +51,7 @@ func TapperFromContext(ctx context.Context) (t Tapper) { } // TapMessage implements Tapper. -func (h Dnstap) TapMessage(m *tap.Message) { +func (h *Dnstap) TapMessage(m *tap.Message) { t := tap.Dnstap_MESSAGE h.IO.Dnstap(tap.Dnstap{ Type: &t, diff --git a/vendor/github.com/coredns/coredns/plugin/dnstap/setup.go b/vendor/github.com/coredns/coredns/plugin/dnstap/setup.go index 70896722a4..c7050b35ca 100644 --- a/vendor/github.com/coredns/coredns/plugin/dnstap/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/dnstap/setup.go @@ -6,8 +6,8 @@ import ( "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/dnstap/dnstapio" + "github.com/coredns/coredns/plugin/pkg/dnsutil" clog "github.com/coredns/coredns/plugin/pkg/log" - "github.com/coredns/coredns/plugin/pkg/parse" "github.com/mholt/caddy" "github.com/mholt/caddy/caddyfile" @@ -44,7 +44,7 @@ func parseConfig(d *caddyfile.Dispenser) (c config, err error) { if strings.HasPrefix(c.target, "tcp://") { // remote IP endpoint - servers, err := parse.HostPortOrFile(c.target[6:]) + servers, err := dnsutil.ParseHostPortOrFile(c.target[6:]) if err != nil { return c, d.ArgErr() } diff --git a/vendor/github.com/coredns/coredns/plugin/errors/README.md b/vendor/github.com/coredns/coredns/plugin/errors/README.md index 70ec3a2c35..f15b271f67 100644 --- a/vendor/github.com/coredns/coredns/plugin/errors/README.md +++ b/vendor/github.com/coredns/coredns/plugin/errors/README.md @@ -6,36 +6,16 @@ ## Description -Any errors encountered during the query processing will be printed to standard output. The errors of particular type can be consolidated and printed once per some period of time. +Any errors encountered during the query processing will be printed to standard output. This plugin can only be used once per Server Block. ## Syntax -The basic syntax is: - ~~~ errors ~~~ -Extra knobs are available with an expanded syntax: - -~~~ -errors { - consolidate DURATION REGEXP -} -~~~ - -Option `consolidate` allows collecting several error messages matching the regular expression **REGEXP** during **DURATION**. After the **DURATION** since receiving the first such message, the consolidated message will be printed to standard output, e.g. - -~~~ -2 errors like '^read udp .* i/o timeout$' occurred in last 30s -~~~ - -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 recomended to use the `^` or `$` metacharacters in regular expression when filtering error messages by prefix or suffix, e.g. `^failed to .*`, or `.* timeout$`. - ## Examples Use the *whoami* to respond to queries and Log errors to standard output. @@ -46,15 +26,3 @@ Use the *whoami* to respond to queries and Log errors to standard output. errors } ~~~ - -Use the *forward* to resolve queries via 8.8.8.8 and print consolidated error messages for errors with suffix " i/o timeout" or with prefix "Failed to ". - -~~~ corefile -. { - forward . 8.8.8.8 - errors { - consolidate 5m ".* i/o timeout$" - consolidate 30s "^Failed to .+" - } -} -~~~ diff --git a/vendor/github.com/coredns/coredns/plugin/errors/errors.go b/vendor/github.com/coredns/coredns/plugin/errors/errors.go index aa4b384b89..88027b191a 100644 --- a/vendor/github.com/coredns/coredns/plugin/errors/errors.go +++ b/vendor/github.com/coredns/coredns/plugin/errors/errors.go @@ -1,12 +1,8 @@ -// Package errors implements an error handling plugin. +// Package errors implements an HTTP error handling plugin. package errors import ( "context" - "regexp" - "sync/atomic" - "time" - "unsafe" "github.com/coredns/coredns/plugin" clog "github.com/coredns/coredns/plugin/pkg/log" @@ -15,98 +11,19 @@ import ( "github.com/miekg/dns" ) -var log = clog.NewWithPlugin("errors") - -type pattern struct { - ptimer unsafe.Pointer - count uint32 - period time.Duration - pattern *regexp.Regexp -} - -func (p *pattern) timer() *time.Timer { - return (*time.Timer)(atomic.LoadPointer(&p.ptimer)) -} - -func (p *pattern) setTimer(t *time.Timer) { - atomic.StorePointer(&p.ptimer, unsafe.Pointer(t)) -} - // 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{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 { - h.cLogger(cnt, h.patterns[i].pattern.String(), h.patterns[i].period) - } -} - -func (h *errorHandler) inc(i int) bool { - if atomic.LoadUint32(&h.stopFlag) > 0 { - return false - } - if atomic.AddUint32(&h.patterns[i].count, 1) == 1 { - ind := i - t := time.AfterFunc(h.patterns[ind].period, func() { - h.logPattern(ind) - }) - h.patterns[ind].setTimer(t) - if atomic.LoadUint32(&h.stopFlag) > 0 && t.Stop() { - h.logPattern(ind) - } - } - return true -} - -func (h *errorHandler) stop() { - atomic.StoreUint32(&h.stopFlag, 1) - for i := range h.patterns { - t := h.patterns[i].timer() - if t != nil && t.Stop() { - h.logPattern(i) - } - } -} +type errorHandler struct{ Next plugin.Handler } // ServeDNS implements the plugin.Handler interface. -func (h *errorHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { +func (h errorHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { rcode, err := plugin.NextOrFailure(h.Name(), h.Next, ctx, w, r) if err != nil { - strErr := err.Error() - for i := range h.patterns { - if h.patterns[i].pattern.MatchString(strErr) { - if h.inc(i) { - return rcode, err - } - break - } - } state := request.Request{W: w, Req: r} - h.eLogger(rcode, state.Name(), state.Type(), strErr) + clog.Errorf("%d %s %s: %v", rcode, state.Name(), state.Type(), err) } return rcode, err } -// Name implements the plugin.Handler interface. -func (h *errorHandler) Name() string { return "errors" } +func (h errorHandler) Name() string { return "errors" } diff --git a/vendor/github.com/coredns/coredns/plugin/errors/setup.go b/vendor/github.com/coredns/coredns/plugin/errors/setup.go index 42a5a40f54..d90928f740 100644 --- a/vendor/github.com/coredns/coredns/plugin/errors/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/errors/setup.go @@ -1,8 +1,7 @@ package errors import ( - "regexp" - "time" + "fmt" "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/plugin" @@ -23,11 +22,6 @@ func setup(c *caddy.Controller) error { return plugin.Error("errors", err) } - c.OnShutdown(func() error { - handler.stop() - return nil - }) - dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { handler.Next = next return handler @@ -36,13 +30,13 @@ func setup(c *caddy.Controller) error { return nil } -func errorsParse(c *caddy.Controller) (*errorHandler, error) { - handler := newErrorHandler() +func errorsParse(c *caddy.Controller) (errorHandler, error) { + handler := errorHandler{} i := 0 for c.Next() { if i > 0 { - return nil, plugin.ErrOnce + return handler, plugin.ErrOnce } i++ @@ -51,39 +45,11 @@ func errorsParse(c *caddy.Controller) (*errorHandler, error) { case 0: case 1: if args[0] != "stdout" { - return nil, c.Errf("invalid log file: %s", args[0]) + return handler, fmt.Errorf("invalid log file: %s", args[0]) } default: - return nil, c.ArgErr() - } - - for c.NextBlock() { - if err := parseBlock(c, handler); err != nil { - return nil, err - } + return handler, c.ArgErr() } } return handler, nil } - -func parseBlock(c *caddy.Controller, h *errorHandler) error { - if c.Val() != "consolidate" { - return c.SyntaxErr("consolidate") - } - - args := c.RemainingArgs() - if len(args) != 2 { - return c.ArgErr() - } - p, err := time.ParseDuration(args[0]) - if err != nil { - return c.Err(err.Error()) - } - re, err := regexp.Compile(args[1]) - if err != nil { - return c.Err(err.Error()) - } - h.patterns = append(h.patterns, &pattern{period: p, pattern: re}) - - return nil -} 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 dc74947821..2abdec0fed 100644 --- a/vendor/github.com/coredns/coredns/plugin/etcd/msg/path.go +++ b/vendor/github.com/coredns/coredns/plugin/etcd/msg/path.go @@ -26,7 +26,7 @@ func Domain(s string) string { for i, j := 1, len(l)-1; i < j; i, j = i+1, j-1 { l[i], l[j] = l[j], l[i] } - return dnsutil.Join(l[1 : len(l)-1]...) + return dnsutil.Join(l[1 : len(l)-1]) } // PathWithWildcard ascts as Path, but if a name contains wildcards (* or any), the name will be 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 987fb14c7d..68ffc544bd 100644 --- a/vendor/github.com/coredns/coredns/plugin/etcd/msg/service.go +++ b/vendor/github.com/coredns/coredns/plugin/etcd/msg/service.go @@ -164,7 +164,7 @@ func targetStrip(name string, targetStrip int) string { offset, end = dns.NextLabel(name, offset) } if end { - // We overshot the name, use the original one. + // We overshot the name, use the orignal one. offset = 0 } name = name[offset:] diff --git a/vendor/github.com/coredns/coredns/plugin/file/README.md b/vendor/github.com/coredns/coredns/plugin/file/README.md index dbc83f553e..1643251c2a 100644 --- a/vendor/github.com/coredns/coredns/plugin/file/README.md +++ b/vendor/github.com/coredns/coredns/plugin/file/README.md @@ -27,7 +27,6 @@ If you want to round robin A and AAAA responses look at the *loadbalance* plugin ~~~ file DBFILE [ZONES... ] { transfer to ADDRESS... - reload DURATION no_reload upstream [ADDRESS...] } @@ -37,10 +36,8 @@ file DBFILE [ZONES... ] { the direction. **ADDRESS** must be denoted in CIDR notation (127.0.0.1/32 etc.) or just as plain addresses. The special wildcard `*` means: the entire internet (only valid for 'transfer to'). When an address is specified a notify message will be send whenever the zone is reloaded. -* `reload` interval to perform reload of zone if SOA version changes. Default is one minute. - Value of `0` means to not scan for changes and reload. eg. `30s` checks zonefile every 30 seconds - and reloads zone when serial changes. -* `no_reload` deprecated. Sets reload to 0. +* `no_reload` by default CoreDNS will try to reload a zone every minute and reloads if the + SOA's serial has changed. This option disables that behavior. * `upstream` defines upstream resolvers to be used resolve external names found (think CNAMEs) pointing to external names. This is only really useful when CoreDNS is configured as a proxy, for normal authoritative serving you don't need *or* want to use this. **ADDRESS** can be an IP diff --git a/vendor/github.com/coredns/coredns/plugin/file/dname.go b/vendor/github.com/coredns/coredns/plugin/file/dname.go index 58351a350b..f552bfdfd9 100644 --- a/vendor/github.com/coredns/coredns/plugin/file/dname.go +++ b/vendor/github.com/coredns/coredns/plugin/file/dname.go @@ -14,7 +14,7 @@ func substituteDNAME(qname, owner, target string) string { labels := dns.SplitDomainName(qname) labels = append(labels[0:len(labels)-dns.CountLabel(owner)], dns.SplitDomainName(target)...) - return dnsutil.Join(labels...) + return dnsutil.Join(labels) } return "" diff --git a/vendor/github.com/coredns/coredns/plugin/file/file.go b/vendor/github.com/coredns/coredns/plugin/file/file.go index ee4d64da0c..f2294fa533 100644 --- a/vendor/github.com/coredns/coredns/plugin/file/file.go +++ b/vendor/github.com/coredns/coredns/plugin/file/file.go @@ -98,6 +98,8 @@ func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i return dns.RcodeServerFailure, nil } + state.SizeAndDo(m) + m, _ = state.Scrub(m) w.WriteMsg(m) return dns.RcodeSuccess, nil } @@ -120,18 +122,16 @@ func (s *serialErr) Error() string { // If serial >= 0 it will reload the zone, if the SOA hasn't changed // it returns an error indicating nothing was read. func Parse(f io.Reader, origin, fileName string, serial int64) (*Zone, error) { - - zp := dns.NewZoneParser(f, dns.Fqdn(origin), fileName) - zp.SetIncludeAllowed(true) + tokens := dns.ParseZone(f, dns.Fqdn(origin), fileName) z := NewZone(origin, fileName) seenSOA := false - for rr, ok := zp.Next(); ok; rr, ok = zp.Next() { - if err := zp.Err(); err != nil { - return nil, err + for x := range tokens { + if x.Error != nil { + return nil, x.Error } if !seenSOA && serial >= 0 { - if s, ok := rr.(*dns.SOA); ok { + if s, ok := x.RR.(*dns.SOA); ok { if s.Serial == uint32(serial) { // same serial return nil, &serialErr{err: "no change in SOA serial", origin: origin, zone: fileName, serial: serial} } @@ -139,7 +139,7 @@ func Parse(f io.Reader, origin, fileName string, serial int64) (*Zone, error) { } } - if err := z.Insert(rr); err != nil { + if err := z.Insert(x.RR); err != nil { return nil, err } } diff --git a/vendor/github.com/coredns/coredns/plugin/file/lookup.go b/vendor/github.com/coredns/coredns/plugin/file/lookup.go index d15ff5b6a3..31248f3145 100644 --- a/vendor/github.com/coredns/coredns/plugin/file/lookup.go +++ b/vendor/github.com/coredns/coredns/plugin/file/lookup.go @@ -30,11 +30,11 @@ func (z *Zone) Lookup(state request.Request, qname string) ([]dns.RR, []dns.RR, qtype := state.QType() do := state.Do() - if 0 < z.ReloadInterval { + if !z.NoReload { z.reloadMu.RLock() } defer func() { - if 0 < z.ReloadInterval { + if !z.NoReload { z.reloadMu.RUnlock() } }() diff --git a/vendor/github.com/coredns/coredns/plugin/file/reload.go b/vendor/github.com/coredns/coredns/plugin/file/reload.go index 512ea68c95..7c0fe85448 100644 --- a/vendor/github.com/coredns/coredns/plugin/file/reload.go +++ b/vendor/github.com/coredns/coredns/plugin/file/reload.go @@ -5,14 +5,15 @@ import ( "time" ) -// TickTime is clock resolution. By default ticks every second. Handler checks if reloadInterval has been reached on every tick. -var TickTime = 1 * time.Second +// TickTime is the default time we use to reload zone. Exported to be tweaked in tests. +var TickTime = 1 * time.Minute // Reload reloads a zone when it is changed on disk. If z.NoRoload is true, no reloading will be done. func (z *Zone) Reload() error { - if z.ReloadInterval == 0 { + if z.NoReload { return nil } + tick := time.NewTicker(TickTime) go func() { @@ -21,13 +22,6 @@ func (z *Zone) Reload() error { select { case <-tick.C: - if z.LastReloaded.Add(z.ReloadInterval).After(time.Now()) { - //reload interval not reached yet - continue - } - //saving timestamp of last attempted reload - z.LastReloaded = time.Now() - zFile := z.File() reader, err := os.Open(zFile) if err != nil { diff --git a/vendor/github.com/coredns/coredns/plugin/file/secondary.go b/vendor/github.com/coredns/coredns/plugin/file/secondary.go index 049fe0cda2..39f79f2b13 100644 --- a/vendor/github.com/coredns/coredns/plugin/file/secondary.go +++ b/vendor/github.com/coredns/coredns/plugin/file/secondary.go @@ -103,7 +103,7 @@ func less(a, b uint32) bool { // Update updates the secondary zone according to its SOA. It will run for the life time of the server // and uses the SOA parameters. Every refresh it will check for a new SOA number. If that fails (for all -// server) it will retry every retry interval. If the zone failed to transfer before the expire, the zone +// server) it wil retry every retry interval. If the zone failed to transfer before the expire, the zone // will be marked expired. func (z *Zone) Update() error { // If we don't have a SOA, we don't have a zone, wait for it to appear. diff --git a/vendor/github.com/coredns/coredns/plugin/file/setup.go b/vendor/github.com/coredns/coredns/plugin/file/setup.go index 5c371babce..3f8d1969b8 100644 --- a/vendor/github.com/coredns/coredns/plugin/file/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/file/setup.go @@ -2,8 +2,7 @@ package file import ( "os" - "path/filepath" - "time" + "path" "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/plugin" @@ -54,6 +53,7 @@ func setup(c *caddy.Controller) error { func fileParse(c *caddy.Controller) (Zones, error) { z := make(map[string]*Zone) names := []string{} + origins := []string{} config := dnsserver.GetConfig(c) @@ -64,15 +64,15 @@ func fileParse(c *caddy.Controller) (Zones, error) { } fileName := c.Val() - origins := make([]string, len(c.ServerBlockKeys)) + origins = make([]string, len(c.ServerBlockKeys)) copy(origins, c.ServerBlockKeys) args := c.RemainingArgs() if len(args) > 0 { origins = args } - if !filepath.IsAbs(fileName) && config.Root != "" { - fileName = filepath.Join(config.Root, fileName) + if !path.IsAbs(fileName) && config.Root != "" { + fileName = path.Join(config.Root, fileName) } reader, err := os.Open(fileName) @@ -92,7 +92,7 @@ func fileParse(c *caddy.Controller) (Zones, error) { names = append(names, origins[i]) } - reload := 1 * time.Minute + noReload := false upstr := upstream.Upstream{} t := []string{} var e error @@ -105,15 +105,8 @@ func fileParse(c *caddy.Controller) (Zones, error) { return Zones{}, e } - case "reload": - d, err := time.ParseDuration(c.RemainingArgs()[0]) - if err != nil { - return Zones{}, plugin.Error("file", err) - } - reload = d - case "no_reload": - reload = 0 + noReload = true case "upstream": args := c.RemainingArgs() @@ -130,7 +123,7 @@ func fileParse(c *caddy.Controller) (Zones, error) { if t != nil { z[origin].TransferTo = append(z[origin].TransferTo, t...) } - z[origin].ReloadInterval = reload + z[origin].NoReload = noReload z[origin].Upstream = upstr } } diff --git a/vendor/github.com/coredns/coredns/plugin/file/shutdown.go b/vendor/github.com/coredns/coredns/plugin/file/shutdown.go index 9aa5989483..cecd76e9e2 100644 --- a/vendor/github.com/coredns/coredns/plugin/file/shutdown.go +++ b/vendor/github.com/coredns/coredns/plugin/file/shutdown.go @@ -2,7 +2,7 @@ package file // OnShutdown shuts down any running go-routines for this zone. func (z *Zone) OnShutdown() error { - if 0 < z.ReloadInterval { + if !z.NoReload { z.reloadShutdown <- true } return nil diff --git a/vendor/github.com/coredns/coredns/plugin/file/xfr.go b/vendor/github.com/coredns/coredns/plugin/file/xfr.go index 18b6bb1176..3ad380902a 100644 --- a/vendor/github.com/coredns/coredns/plugin/file/xfr.go +++ b/vendor/github.com/coredns/coredns/plugin/file/xfr.go @@ -55,7 +55,7 @@ func (x Xfr) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (in return dns.RcodeSuccess, nil } -// Name implements the plugin.Handler interface. +// Name implements the plugin.Hander interface. func (x Xfr) Name() string { return "xfr" } const transferLength = 1000 // Start a new envelop after message reaches this size in bytes. Intentionally small to test multi envelope parsing. diff --git a/vendor/github.com/coredns/coredns/plugin/file/zone.go b/vendor/github.com/coredns/coredns/plugin/file/zone.go index e94a00f16c..6e1ec6d698 100644 --- a/vendor/github.com/coredns/coredns/plugin/file/zone.go +++ b/vendor/github.com/coredns/coredns/plugin/file/zone.go @@ -3,10 +3,9 @@ package file import ( "fmt" "net" - "path/filepath" + "path" "strings" "sync" - "time" "github.com/coredns/coredns/plugin/file/tree" "github.com/coredns/coredns/plugin/pkg/upstream" @@ -28,8 +27,7 @@ type Zone struct { TransferFrom []string Expired *bool - ReloadInterval time.Duration - LastReloaded time.Time + NoReload bool reloadMu sync.RWMutex reloadShutdown chan bool Upstream upstream.Upstream // Upstream for looking up names during the resolution process @@ -48,11 +46,10 @@ func NewZone(name, file string) *Zone { z := &Zone{ origin: dns.Fqdn(name), origLen: dns.CountLabel(dns.Fqdn(name)), - file: filepath.Clean(file), + file: path.Clean(file), Tree: &tree.Tree{}, Expired: new(bool), reloadShutdown: make(chan bool), - LastReloaded: time.Now(), } *z.Expired = false @@ -164,7 +161,7 @@ func (z *Zone) TransferAllowed(state request.Request) bool { // All returns all records from the zone, the first record will be the SOA record, // otionally followed by all RRSIG(SOA)s. func (z *Zone) All() []dns.RR { - if z.ReloadInterval > 0 { + if !z.NoReload { z.reloadMu.RLock() defer z.reloadMu.RUnlock() } @@ -206,7 +203,7 @@ func (z *Zone) nameFromRight(qname string, i int) (string, bool) { } k := 0 - var shot bool + shot := false for j := 1; j <= i; j++ { k, shot = dns.PrevLabel(qname, j+z.origLen) if shot { diff --git a/vendor/github.com/coredns/coredns/plugin/forward/connect.go b/vendor/github.com/coredns/coredns/plugin/forward/connect.go index 64edb395e9..439caf9328 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/connect.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/connect.go @@ -1,6 +1,6 @@ // 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 +// 50% faster than just openening a new connection for every client. It works with UDP and TCP and uses // inband healthchecking. package forward @@ -35,16 +35,16 @@ func averageTimeout(currentAvg *int64, observedDuration time.Duration, weight in atomic.AddInt64(currentAvg, int64(observedDuration-dt)/weight) } -func (t *Transport) dialTimeout() time.Duration { +func (t *transport) dialTimeout() time.Duration { return limitTimeout(&t.avgDialTime, minDialTimeout, maxDialTimeout) } -func (t *Transport) updateDialTimeout(newDialTime time.Duration) { +func (t *transport) updateDialTimeout(newDialTime time.Duration) { averageTimeout(&t.avgDialTime, newDialTime, cumulativeAvgWeight) } // Dial dials the address configured in transport, potentially reusing a connection or creating a new one. -func (t *Transport) Dial(proto string) (*dns.Conn, bool, error) { +func (t *transport) Dial(proto string) (*dns.Conn, bool, error) { // If tls has been configured; use it. if t.tlsConfig != nil { proto = "tcp-tls" diff --git a/vendor/github.com/coredns/coredns/plugin/forward/forward.go b/vendor/github.com/coredns/coredns/plugin/forward/forward.go index dfa1aaca1c..6eb2e0084e 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/forward.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/forward.go @@ -1,6 +1,6 @@ // 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 +// 50% faster than just openening a new connection for every client. It works with UDP and TCP and uses // inband healthchecking. package forward @@ -12,15 +12,12 @@ import ( "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/debug" - clog "github.com/coredns/coredns/plugin/pkg/log" "github.com/coredns/coredns/request" "github.com/miekg/dns" ot "github.com/opentracing/opentracing-go" ) -var log = clog.NewWithPlugin("forward") - // Forward represents a plugin instance that can proxy requests to another (DNS) server. It has a list // of proxies each representing one upstream proxy. type Forward struct { @@ -151,7 +148,13 @@ func (f *Forward) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg return 0, nil } + // When using force_tcp the upstream can send a message that is too big for + // the udp buffer, hence we need to truncate the message to at least make it + // fit the udp buffer. + ret, _ = state.Scrub(ret) + w.WriteMsg(ret) + return 0, nil } diff --git a/vendor/github.com/coredns/coredns/plugin/forward/health.go b/vendor/github.com/coredns/coredns/plugin/forward/health.go index 4512d96c86..4d3278f6df 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/health.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/health.go @@ -5,8 +5,6 @@ import ( "sync/atomic" "time" - "github.com/coredns/coredns/plugin/pkg/transport" - "github.com/miekg/dns" ) @@ -19,10 +17,10 @@ type HealthChecker interface { // dnsHc is a health checker for a DNS endpoint (DNS, and DoT). type dnsHc struct{ c *dns.Client } -// NewHealthChecker returns a new HealthChecker based on transport. -func NewHealthChecker(trans string) HealthChecker { - switch trans { - case transport.DNS, transport.TLS: +// NewHealthChecker returns a new HealthChecker based on protocol. +func NewHealthChecker(protocol int) HealthChecker { + switch protocol { + case DNS, TLS: c := new(dns.Client) c.Net = "udp" c.ReadTimeout = 1 * time.Second @@ -31,7 +29,6 @@ func NewHealthChecker(trans string) HealthChecker { return &dnsHc{c: c} } - log.Warningf("No healthchecker for transport %q", trans) return nil } diff --git a/vendor/github.com/coredns/coredns/plugin/forward/lookup.go b/vendor/github.com/coredns/coredns/plugin/forward/lookup.go index f6c9a07450..94114647c3 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/lookup.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/lookup.go @@ -1,13 +1,12 @@ // 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 +// 50% faster than just openening 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" @@ -82,7 +81,7 @@ func (f *Forward) Lookup(state request.Request, name string, typ uint16) (*dns.M func NewLookup(addr []string) *Forward { f := New() for i := range addr { - p := NewProxy(addr[i], transport.DNS) + p := NewProxy(addr[i], DNS) f.SetProxy(p) } return f diff --git a/vendor/github.com/coredns/coredns/plugin/forward/metrics.go b/vendor/github.com/coredns/coredns/plugin/forward/metrics.go index 0fe470926f..063e2dc00a 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/metrics.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/metrics.go @@ -1,6 +1,8 @@ package forward import ( + "sync" + "github.com/coredns/coredns/plugin" "github.com/prometheus/client_golang/prometheus" @@ -46,3 +48,5 @@ var ( Help: "Gauge of open sockets per upstream.", }, []string{"to"}) ) + +var once sync.Once diff --git a/vendor/github.com/coredns/coredns/plugin/forward/persistent.go b/vendor/github.com/coredns/coredns/plugin/forward/persistent.go index fabdc70b26..4da1514fe4 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/persistent.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/persistent.go @@ -15,10 +15,10 @@ type persistConn struct { used time.Time } -// Transport hold the persistent cache. -type Transport struct { +// transport hold the persistent cache. +type transport struct { avgDialTime int64 // kind of average time of dial time - conns map[string][]*persistConn // Buckets for udp, tcp and tcp-tls. + conns map[string][]*persistConn // Buckets for udp, tcp and tcp-tls. expire time.Duration // After this duration a connection is expired. addr string tlsConfig *tls.Config @@ -29,8 +29,8 @@ type Transport struct { stop chan bool } -func newTransport(addr string) *Transport { - t := &Transport{ +func newTransport(addr string) *transport { + t := &transport{ avgDialTime: int64(defaultDialTimeout / 2), conns: make(map[string][]*persistConn), expire: defaultExpire, @@ -45,7 +45,7 @@ func newTransport(addr string) *Transport { // len returns the number of connection, used for metrics. Can only be safely // used inside connManager() because of data races. -func (t *Transport) len() int { +func (t *transport) len() int { l := 0 for _, conns := range t.conns { l += len(conns) @@ -54,7 +54,7 @@ func (t *Transport) len() int { } // connManagers manages the persistent connection cache for UDP and TCP. -func (t *Transport) connManager() { +func (t *transport) connManager() { ticker := time.NewTicker(t.expire) Wait: for { @@ -115,7 +115,7 @@ func closeConns(conns []*persistConn) { } // cleanup removes connections from cache. -func (t *Transport) cleanup(all bool) { +func (t *transport) cleanup(all bool) { staleTime := time.Now().Add(-t.expire) for proto, stack := range t.conns { if len(stack) == 0 { @@ -144,19 +144,19 @@ func (t *Transport) cleanup(all bool) { } // Yield return the connection to transport for reuse. -func (t *Transport) Yield(c *dns.Conn) { t.yield <- c } +func (t *transport) Yield(c *dns.Conn) { t.yield <- c } // Start starts the transport's connection manager. -func (t *Transport) Start() { go t.connManager() } +func (t *transport) Start() { go t.connManager() } // Stop stops the transport's connection manager. -func (t *Transport) Stop() { close(t.stop) } +func (t *transport) Stop() { close(t.stop) } // SetExpire sets the connection expire time in transport. -func (t *Transport) SetExpire(expire time.Duration) { t.expire = expire } +func (t *transport) SetExpire(expire time.Duration) { t.expire = expire } // SetTLSConfig sets the TLS config in transport. -func (t *Transport) SetTLSConfig(cfg *tls.Config) { t.tlsConfig = cfg } +func (t *transport) SetTLSConfig(cfg *tls.Config) { t.tlsConfig = cfg } const ( defaultExpire = 10 * time.Second diff --git a/vendor/github.com/coredns/coredns/plugin/forward/protocol.go b/vendor/github.com/coredns/coredns/plugin/forward/protocol.go new file mode 100644 index 0000000000..338b601164 --- /dev/null +++ b/vendor/github.com/coredns/coredns/plugin/forward/protocol.go @@ -0,0 +1,30 @@ +package forward + +// Copied from coredns/core/dnsserver/address.go + +import ( + "strings" +) + +// protocol returns the protocol of the string s. The second string returns s +// with the prefix chopped off. +func protocol(s string) (int, string) { + switch { + case strings.HasPrefix(s, _tls+"://"): + return TLS, s[len(_tls)+3:] + case strings.HasPrefix(s, _dns+"://"): + return DNS, s[len(_dns)+3:] + } + return DNS, s +} + +// Supported protocols. +const ( + DNS = iota + 1 + TLS +) + +const ( + _dns = "dns" + _tls = "tls" +) diff --git a/vendor/github.com/coredns/coredns/plugin/forward/proxy.go b/vendor/github.com/coredns/coredns/plugin/forward/proxy.go index fbe79cd691..ac74bf0f82 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/proxy.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/proxy.go @@ -18,7 +18,7 @@ type Proxy struct { // Connection caching expire time.Duration - transport *Transport + transport *transport // health checking probe *up.Probe @@ -26,7 +26,7 @@ type Proxy struct { } // NewProxy returns a new proxy. -func NewProxy(addr, trans string) *Proxy { +func NewProxy(addr string, protocol int) *Proxy { p := &Proxy{ addr: addr, fails: 0, @@ -34,7 +34,7 @@ func NewProxy(addr, trans string) *Proxy { transport: newTransport(addr), avgRtt: int64(maxTimeout / 2), } - p.health = NewHealthChecker(trans) + p.health = NewHealthChecker(protocol) runtime.SetFinalizer(p, (*Proxy).finalizer) return p } @@ -50,11 +50,6 @@ func (p *Proxy) SetExpire(expire time.Duration) { p.transport.SetExpire(expire) // Healthcheck kicks of a round of health checks for this proxy. func (p *Proxy) Healthcheck() { - if p.health == nil { - log.Warning("No healthchecker") - return - } - p.probe.Do(func() error { return p.health.Check(p) }) diff --git a/vendor/github.com/coredns/coredns/plugin/forward/setup.go b/vendor/github.com/coredns/coredns/plugin/forward/setup.go index 23d9acfed8..ee48fdaf6c 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/setup.go @@ -2,18 +2,17 @@ package forward import ( "fmt" + "net" "strconv" "time" "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/metrics" - "github.com/coredns/coredns/plugin/pkg/parse" + "github.com/coredns/coredns/plugin/pkg/dnsutil" pkgtls "github.com/coredns/coredns/plugin/pkg/tls" - "github.com/coredns/coredns/plugin/pkg/transport" "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyfile" ) func init() { @@ -38,7 +37,9 @@ func setup(c *caddy.Controller) error { }) c.OnStartup(func() error { - metrics.MustRegister(c, RequestCount, RcodeCount, RequestDuration, HealthcheckFailureCount, SocketGauge) + once.Do(func() { + metrics.MustRegister(c, RequestCount, RcodeCount, RequestDuration, HealthcheckFailureCount, SocketGauge) + }) return f.OnStartup() }) @@ -69,55 +70,69 @@ func (f *Forward) OnShutdown() error { func (f *Forward) Close() { f.OnShutdown() } func parseForward(c *caddy.Controller) (*Forward, error) { - var ( - f *Forward - err error - i int - ) + f := New() + + protocols := map[int]int{} + + i := 0 for c.Next() { if i > 0 { return nil, plugin.ErrOnce } i++ - f, err = ParseForwardStanza(&c.Dispenser) - if err != nil { - return nil, err + + if !c.Args(&f.from) { + return f, c.ArgErr() } - } - return f, nil -} + f.from = plugin.Host(f.from).Normalize() -// ParseForwardStanza parses one forward stanza -func ParseForwardStanza(c *caddyfile.Dispenser) (*Forward, error) { - f := New() + to := c.RemainingArgs() + if len(to) == 0 { + return f, c.ArgErr() + } - if !c.Args(&f.from) { - return f, c.ArgErr() - } - f.from = plugin.Host(f.from).Normalize() + // A bit fiddly, but first check if we've got protocols and if so add them back in when we create the proxies. + protocols = make(map[int]int) + for i := range to { + protocols[i], to[i] = protocol(to[i]) + } - to := c.RemainingArgs() - if len(to) == 0 { - return f, c.ArgErr() - } - - toHosts, err := parse.HostPortOrFile(to...) - if err != nil { - return f, err - } - - transports := make([]string, len(toHosts)) - for i, host := range toHosts { - trans, h := parse.Transport(host) - p := NewProxy(h, trans) - f.proxies = append(f.proxies, p) - transports[i] = trans - } - - for c.NextBlock() { - if err := parseBlock(c, f); err != nil { + // If parseHostPortOrFile expands a file with a lot of nameserver our accounting in protocols doesn't make + // any sense anymore... For now: lets don't care. + toHosts, err := dnsutil.ParseHostPortOrFile(to...) + if err != nil { return f, err } + + for i, h := range toHosts { + // Double check the port, if e.g. is 53 and the transport is TLS make it 853. + // This can be somewhat annoying because you *can't* have TLS on port 53 then. + switch protocols[i] { + case TLS: + h1, p, err := net.SplitHostPort(h) + if err != nil { + break + } + + // This is more of a bug in dnsutil.ParseHostPortOrFile that defaults to + // 53 because it doesn't know about the tls:// // and friends (that should be fixed). Hence + // Fix the port number here, back to what the user intended. + if p == "53" { + h = net.JoinHostPort(h1, "853") + } + } + + // We can't set tlsConfig here, because we haven't parsed it yet. + // We set it below at the end of parseBlock, use nil now. + p := NewProxy(h, protocols[i]) + f.proxies = append(f.proxies, p) + } + + for c.NextBlock() { + if err := parseBlock(c, f); err != nil { + return f, err + } + } } if f.tlsServerName != "" { @@ -125,7 +140,7 @@ func ParseForwardStanza(c *caddyfile.Dispenser) (*Forward, error) { } for i := range f.proxies { // Only set this for proxies that need it. - if transports[i] == transport.TLS { + if protocols[i] == TLS { f.proxies[i].SetTLSConfig(f.tlsConfig) } f.proxies[i].SetExpire(f.expire) @@ -133,7 +148,7 @@ func ParseForwardStanza(c *caddyfile.Dispenser) (*Forward, error) { return f, nil } -func parseBlock(c *caddyfile.Dispenser, f *Forward) error { +func parseBlock(c *caddy.Controller, f *Forward) error { switch c.Val() { case "except": ignore := c.RemainingArgs() diff --git a/vendor/github.com/coredns/coredns/plugin/forward/truncated.go b/vendor/github.com/coredns/coredns/plugin/forward/truncated.go index fb821d3351..f9bd464d1d 100644 --- a/vendor/github.com/coredns/coredns/plugin/forward/truncated.go +++ b/vendor/github.com/coredns/coredns/plugin/forward/truncated.go @@ -6,7 +6,7 @@ import ( "github.com/miekg/dns" ) -// truncated looks at the error and if truncated return a nil error +// truncated looks at the error and if truncated return a nil errror // 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 diff --git a/vendor/github.com/coredns/coredns/plugin/health/README.md b/vendor/github.com/coredns/coredns/plugin/health/README.md index 1907a98c1d..95d00889be 100644 --- a/vendor/github.com/coredns/coredns/plugin/health/README.md +++ b/vendor/github.com/coredns/coredns/plugin/health/README.md @@ -57,7 +57,7 @@ com net { erratic health :8080 } -~~~ +~~~~ ## Plugins diff --git a/vendor/github.com/coredns/coredns/plugin/health/setup.go b/vendor/github.com/coredns/coredns/plugin/health/setup.go index 5a17251257..0b90d829ac 100644 --- a/vendor/github.com/coredns/coredns/plugin/health/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/health/setup.go @@ -55,7 +55,7 @@ func setup(c *caddy.Controller) error { }) c.OnStartup(func() error { - metrics.MustRegister(c, HealthDuration) + once.Do(func() { metrics.MustRegister(c, HealthDuration) }) return nil }) diff --git a/vendor/github.com/coredns/coredns/plugin/hosts/hosts.go b/vendor/github.com/coredns/coredns/plugin/hosts/hosts.go index 4041b384cd..c9ce163c9c 100644 --- a/vendor/github.com/coredns/coredns/plugin/hosts/hosts.go +++ b/vendor/github.com/coredns/coredns/plugin/hosts/hosts.go @@ -66,6 +66,8 @@ func (h Hosts) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) ( m.Authoritative, m.RecursionAvailable = true, true m.Answer = answers + state.SizeAndDo(m) + m, _ = state.Scrub(m) w.WriteMsg(m) return dns.RcodeSuccess, nil } diff --git a/vendor/github.com/coredns/coredns/plugin/hosts/hostsfile.go b/vendor/github.com/coredns/coredns/plugin/hosts/hostsfile.go index 8b2ffa0a6a..4b912b17c0 100644 --- a/vendor/github.com/coredns/coredns/plugin/hosts/hostsfile.go +++ b/vendor/github.com/coredns/coredns/plugin/hosts/hostsfile.go @@ -54,27 +54,11 @@ func newHostsMap() *hostsMap { } } -// Len returns the total number of addresses in the hostmap, this includes -// V4/V6 and any reverse addresses. -func (h *hostsMap) Len() int { - l := 0 - for _, v4 := range h.byNameV4 { - l += len(v4) - } - for _, v6 := range h.byNameV6 { - l += len(v6) - } - for _, a := range h.byAddr { - l += len(a) - } - return l -} - // Hostsfile contains known host entries. type Hostsfile struct { sync.RWMutex - // list of zones we are authoritative for + // list of zones we are authoritive for Origins []string // hosts maps for lookups @@ -106,17 +90,13 @@ func (h *Hostsfile) readHosts() { return } - newMap := h.parse(file, h.inline) - log.Debugf("Parsed hosts file into %d entries", newMap.Len()) - h.Lock() + defer h.Unlock() + h.parseReader(file) - h.hmap = newMap // Update the data cache. h.mtime = stat.ModTime() h.size = stat.Size() - - h.Unlock() } func (h *Hostsfile) initInline(inline []string) { @@ -129,6 +109,10 @@ func (h *Hostsfile) initInline(inline []string) { *h.hmap = *h.inline } +func (h *Hostsfile) parseReader(r io.Reader) { + h.hmap = h.parse(r, h.inline) +} + // Parse reads the hostsfile and populates the byName and byAddr maps. func (h *Hostsfile) parse(r io.Reader, override *hostsMap) *hostsMap { hmap := newHostsMap() diff --git a/vendor/github.com/coredns/coredns/plugin/hosts/setup.go b/vendor/github.com/coredns/coredns/plugin/hosts/setup.go index 3dafb5be75..ed5cd5c7aa 100644 --- a/vendor/github.com/coredns/coredns/plugin/hosts/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/hosts/setup.go @@ -2,7 +2,7 @@ package hosts import ( "os" - "path/filepath" + "path" "strings" "time" @@ -83,8 +83,8 @@ func hostsParse(c *caddy.Controller) (Hosts, error) { h.path = args[0] args = args[1:] - if !filepath.IsAbs(h.path) && config.Root != "" { - h.path = filepath.Join(config.Root, h.path) + if !path.IsAbs(h.path) && config.Root != "" { + h.path = path.Join(config.Root, h.path) } s, err := os.Stat(h.path) if err != nil { diff --git a/vendor/github.com/coredns/coredns/plugin/log/README.md b/vendor/github.com/coredns/coredns/plugin/log/README.md index 48e5acb6c2..65cca19c85 100644 --- a/vendor/github.com/coredns/coredns/plugin/log/README.md +++ b/vendor/github.com/coredns/coredns/plugin/log/README.md @@ -7,10 +7,9 @@ ## Description By just using *log* you dump all queries (and parts for the reply) on standard output. Options exist -to tweak the output a little. The date/time prefix on log lines is RFC3339 formatted with -milliseconds. +to tweak the output a little. -Note that for busy servers logging will incur a performance hit. +Note that for busy servers this will incur a performance hit. ## Syntax @@ -60,14 +59,15 @@ The following place holders are supported: * `{name}`: qname of the request * `{class}`: qclass of the request * `{proto}`: protocol used (tcp or udp) +* `{when}`: time of the query * `{remote}`: client's IP address, for IPv6 addresses these are enclosed in brackets: `[::1]` * `{size}`: request size in bytes * `{port}`: client's port * `{duration}`: response duration * `{rcode}`: response RCODE -* `{rsize}`: raw (uncompressed), response size (a client may receive a smaller response) +* `{rsize}`: response size * `{>rflags}`: response flags, each set flag will be displayed, e.g. "aa, tc". This includes the qr - bit as well + bit as well. * `{>bufsize}`: the EDNS0 buffer size advertised in the query * `{>do}`: is the EDNS0 DO (DNSSEC OK) bit set in the query * `{>id}`: query ID @@ -76,15 +76,9 @@ The following place holders are supported: The default Common Log Format is: ~~~ txt -`{remote}:{port} - {>id} "{type} {class} {name} {proto} {size} {>do} {>bufsize}" {rcode} {>rflags} {rsize} {duration}` +`{remote}:{port} - [{when}] {>id} "{type} {class} {name} {proto} {size} {>do} {>bufsize}" {rcode} {>rflags} {rsize} {duration}` ~~~ -Each of these logs will be outputted with `log.Infof`, so a typical example looks like this: - -~~~ txt -2018-10-30T19:10:07.547Z [INFO] [::1]:50759 - 29008 "A IN example.org. udp 41 false 4096" NOERROR qr,rd,ra,ad 68 0.037990251s -~~~~ - ## Examples Log all requests to stdout diff --git a/vendor/github.com/coredns/coredns/plugin/log/log.go b/vendor/github.com/coredns/coredns/plugin/log/log.go index 685c55191c..f52a703445 100644 --- a/vendor/github.com/coredns/coredns/plugin/log/log.go +++ b/vendor/github.com/coredns/coredns/plugin/log/log.go @@ -3,12 +3,12 @@ package log import ( "context" + "log" "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" @@ -58,7 +58,7 @@ func (l Logger) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) // and we shouldn't have an empty rule.Class. if rule.Class[response.All] || rule.Class[class] { rep := replacer.New(r, rrw, CommonLogEmptyValue) - clog.Infof(rep.Replace(rule.Format)) + rule.Log.Println(rep.Replace(rule.Format)) } return rc, err @@ -75,11 +75,12 @@ type Rule struct { NameScope string Class map[response.Class]bool Format string + Log *log.Logger } const ( // CommonLogFormat is the common log format. - CommonLogFormat = `{remote}:{port} ` + CommonLogEmptyValue + ` {>id} "{type} {class} {name} {proto} {size} {>do} {>bufsize}" {rcode} {>rflags} {rsize} {duration}` + CommonLogFormat = `{remote}:{port} ` + CommonLogEmptyValue + ` [{when}] {>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. diff --git a/vendor/github.com/coredns/coredns/plugin/log/setup.go b/vendor/github.com/coredns/coredns/plugin/log/setup.go index c3c1af4ca5..1f9501d68f 100644 --- a/vendor/github.com/coredns/coredns/plugin/log/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/log/setup.go @@ -1,6 +1,9 @@ package log import ( + "log" + "os" + "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/pkg/response" @@ -22,6 +25,15 @@ func setup(c *caddy.Controller) error { return plugin.Error("log", err) } + // Open the log files for writing when the server starts + c.OnStartup(func() error { + for i := 0; i < len(rules); i++ { + rules[i].Log = log.New(os.Stdout, "", 0) + } + + return nil + }) + dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { return Logger{Next: next, Rules: rules, ErrorFunc: dnsserver.DefaultErrorFunc} }) diff --git a/vendor/github.com/coredns/coredns/plugin/metrics/README.md b/vendor/github.com/coredns/coredns/plugin/metrics/README.md index b3fbc111f3..c8c2983998 100644 --- a/vendor/github.com/coredns/coredns/plugin/metrics/README.md +++ b/vendor/github.com/coredns/coredns/plugin/metrics/README.md @@ -60,7 +60,7 @@ Use an alternative address: } ~~~ -Or via an environment variable (this is supported throughout the Corefile): `export PORT=9253`, and +Or via an enviroment variable (this is supported throughout the Corefile): `export PORT=9253`, and then: ~~~ corefile diff --git a/vendor/github.com/coredns/coredns/plugin/metrics/metrics.go b/vendor/github.com/coredns/coredns/plugin/metrics/metrics.go index 6b496cccc0..af4afc6ac7 100644 --- a/vendor/github.com/coredns/coredns/plugin/metrics/metrics.go +++ b/vendor/github.com/coredns/coredns/plugin/metrics/metrics.go @@ -2,12 +2,10 @@ package metrics import ( - "context" "net" "net/http" "os" "sync" - "time" "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/metrics/vars" @@ -24,7 +22,6 @@ type Metrics struct { ln net.Listener lnSetup bool mux *http.ServeMux - srv *http.Server zoneNames []string zoneMap map[string]bool @@ -57,15 +54,7 @@ func New(addr string) *Metrics { } // MustRegister wraps m.Reg.MustRegister. -func (m *Metrics) MustRegister(c prometheus.Collector) { - err := m.Reg.Register(c) - if err != nil { - // ignore any duplicate error, but fatal on any other kind of error - if _, ok := err.(prometheus.AlreadyRegisteredError); !ok { - log.Fatalf("Cannot register metrics collector: %s", err) - } - } -} +func (m *Metrics) MustRegister(c prometheus.Collector) { m.Reg.MustRegister(c) } // AddZone adds zone z to m. func (m *Metrics) AddZone(z string) { @@ -105,9 +94,9 @@ func (m *Metrics) OnStartup() error { m.mux = http.NewServeMux() m.mux.Handle("/metrics", promhttp.HandlerFor(m.Reg, promhttp.HandlerOpts{})) - m.srv = &http.Server{Handler: m.mux} + go func() { - m.srv.Serve(m.ln) + http.Serve(m.ln, m.mux) }() return nil } @@ -117,28 +106,24 @@ func (m *Metrics) OnRestart() error { if !m.lnSetup { return nil } - uniqAddr.Unset(m.Addr) - return m.stopServer() -} -func (m *Metrics) stopServer() error { - if !m.lnSetup { - return nil - } - ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout) - defer cancel() - if err := m.srv.Shutdown(ctx); err != nil { - log.Infof("Failed to stop prometheus http server: %s", err) - return err - } - m.lnSetup = false + uniqAddr.SetTodo(m.Addr) + m.ln.Close() + m.lnSetup = false return nil } // OnFinalShutdown tears down the metrics listener on shutdown and restart. func (m *Metrics) OnFinalShutdown() error { - return m.stopServer() + // We allow prometheus statements in multiple Server Blocks, but only the first + // will open the listener, for the rest they are all nil; guard against that. + if !m.lnSetup { + return nil + } + + m.lnSetup = false + return m.ln.Close() } func keys(m map[string]bool) []string { @@ -153,10 +138,6 @@ func keys(m map[string]bool) []string { // we listen on "localhost:0" and need to retrieve the actual address. var ListenAddr string -// shutdownTimeout is the maximum amount of time the metrics plugin will wait -// before erroring when it tries to close the metrics server -const shutdownTimeout time.Duration = time.Second * 5 - var buildInfo = prometheus.NewGaugeVec(prometheus.GaugeOpts{ Namespace: plugin.Namespace, Name: "build_info", diff --git a/vendor/github.com/coredns/coredns/plugin/metrics/setup.go b/vendor/github.com/coredns/coredns/plugin/metrics/setup.go index ffc0466f3c..c00f44a830 100644 --- a/vendor/github.com/coredns/coredns/plugin/metrics/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/metrics/setup.go @@ -31,13 +31,6 @@ func setup(c *caddy.Controller) error { return plugin.Error("prometheus", err) } - // register the metrics to its address (ensure only one active metrics per address) - obj := uniqAddr.Set(m.Addr, m.OnStartup, m) - //propagate the real active Registry to current metrics - if om, ok := obj.(*Metrics); ok { - m.Reg = om.Reg - } - dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { m.Next = next return m @@ -62,6 +55,10 @@ func setup(c *caddy.Controller) error { func prometheusParse(c *caddy.Controller) (*Metrics, error) { var met = New(defaultAddr) + defer func() { + uniqAddr.Set(met.Addr, met.OnStartup) + }() + i := 0 for c.Next() { if i > 0 { 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 d0c64b8647..8b619a5f94 100644 --- a/vendor/github.com/coredns/coredns/plugin/metrics/vars/report.go +++ b/vendor/github.com/coredns/coredns/plugin/metrics/vars/report.go @@ -10,7 +10,7 @@ import ( "github.com/miekg/dns" ) -// Report reports the metrics data associated with request. +// Report reports the metrics data associcated with request. func Report(ctx context.Context, req request.Request, zone, rcode string, size int, start time.Time) { // Proto and Family. net := req.Proto() diff --git a/vendor/github.com/coredns/coredns/plugin/normalize.go b/vendor/github.com/coredns/coredns/plugin/normalize.go index dc295ce01e..fbbe5c8263 100644 --- a/vendor/github.com/coredns/coredns/plugin/normalize.go +++ b/vendor/github.com/coredns/coredns/plugin/normalize.go @@ -6,7 +6,6 @@ import ( "strconv" "strings" - "github.com/coredns/coredns/plugin/pkg/parse" "github.com/miekg/dns" ) @@ -62,10 +61,22 @@ type ( // Normalize will return the host portion of host, stripping // of any port or transport. The host will also be fully qualified and lowercased. func (h Host) Normalize() string { - s := string(h) - _, s = parse.Transport(s) - // The error can be ignore here, because this function is called after the corefile has already been vetted. + s := string(h) + + switch { + case strings.HasPrefix(s, TransportTLS+"://"): + s = s[len(TransportTLS+"://"):] + case strings.HasPrefix(s, TransportDNS+"://"): + s = s[len(TransportDNS+"://"):] + case strings.HasPrefix(s, TransportGRPC+"://"): + s = s[len(TransportGRPC+"://"):] + case strings.HasPrefix(s, TransportHTTPS+"://"): + s = s[len(TransportHTTPS+"://"):] + } + + // The error can be ignore here, because this function is called after the corefile + // has already been vetted. host, _, _, _ := SplitHostPort(s) return Name(host).Normalize() } @@ -114,8 +125,7 @@ func SplitHostPort(s string) (host, port string, ipnet *net.IPNet, err error) { // Get the first lower octet boundary to see what encompassing zone we should be authoritative for. mod := (bits - ones) % sizeDigit nearest := (bits - ones) + mod - offset := 0 - var end bool + offset, end := 0, false for i := 0; i < nearest/sizeDigit; i++ { offset, end = dns.NextLabel(rev, offset) if end { @@ -127,3 +137,11 @@ func SplitHostPort(s string) (host, port string, ipnet *net.IPNet, err error) { } return host, port, n, nil } + +// Duplicated from core/dnsserver/address.go ! +const ( + TransportDNS = "dns" + TransportTLS = "tls" + TransportGRPC = "grpc" + TransportHTTPS = "https" +) diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/cache/cache.go b/vendor/github.com/coredns/coredns/plugin/pkg/cache/cache.go index 8a5ad783e0..25faf93ea6 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/cache/cache.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/cache/cache.go @@ -9,10 +9,10 @@ import ( ) // Hash returns the FNV hash of what. -func Hash(what []byte) uint64 { - h := fnv.New64() +func Hash(what []byte) uint32 { + h := fnv.New32() h.Write(what) - return h.Sum64() + return h.Sum32() } // Cache is cache. @@ -22,7 +22,7 @@ type Cache struct { // shard is a cache with random eviction. type shard struct { - items map[uint64]interface{} + items map[uint32]interface{} size int sync.RWMutex @@ -31,8 +31,8 @@ type shard struct { // New returns a new cache. func New(size int) *Cache { ssize := size / shardSize - if ssize < 4 { - ssize = 4 + if ssize < 512 { + ssize = 512 } c := &Cache{} @@ -45,19 +45,19 @@ func New(size int) *Cache { } // Add adds a new element to the cache. If the element already exists it is overwritten. -func (c *Cache) Add(key uint64, el interface{}) { +func (c *Cache) Add(key uint32, el interface{}) { shard := key & (shardSize - 1) c.shards[shard].Add(key, el) } // Get looks up element index under key. -func (c *Cache) Get(key uint64) (interface{}, bool) { +func (c *Cache) Get(key uint32) (interface{}, bool) { shard := key & (shardSize - 1) return c.shards[shard].Get(key) } // Remove removes the element indexed with key. -func (c *Cache) Remove(key uint64) { +func (c *Cache) Remove(key uint32) { shard := key & (shardSize - 1) c.shards[shard].Remove(key) } @@ -72,10 +72,10 @@ func (c *Cache) Len() int { } // newShard returns a new shard with size. -func newShard(size int) *shard { return &shard{items: make(map[uint64]interface{}), size: size} } +func newShard(size int) *shard { return &shard{items: make(map[uint32]interface{}), size: size} } // Add adds element indexed by key into the cache. Any existing element is overwritten -func (s *shard) Add(key uint64, el interface{}) { +func (s *shard) Add(key uint32, el interface{}) { l := s.Len() if l+1 > s.size { s.Evict() @@ -87,7 +87,7 @@ func (s *shard) Add(key uint64, el interface{}) { } // Remove removes the element indexed by key from the cache. -func (s *shard) Remove(key uint64) { +func (s *shard) Remove(key uint32) { s.Lock() delete(s.items, key) s.Unlock() @@ -95,28 +95,26 @@ func (s *shard) Remove(key uint64) { // Evict removes a random element from the cache. func (s *shard) Evict() { - hasKey := false - var key uint64 + key := -1 s.RLock() for k := range s.items { - key = k - hasKey = true + key = int(k) break } s.RUnlock() - if !hasKey { + if key == -1 { // empty cache return } // If this item is gone between the RUnlock and Lock race we don't care. - s.Remove(key) + s.Remove(uint32(key)) } // Get looks up the element indexed under key. -func (s *shard) Get(key uint64) (interface{}, bool) { +func (s *shard) Get(key uint32) (interface{}, bool) { s.RLock() el, found := s.items[key] s.RUnlock() diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/parse/host.go b/vendor/github.com/coredns/coredns/plugin/pkg/dnsutil/host.go similarity index 60% rename from vendor/github.com/coredns/coredns/plugin/pkg/parse/host.go rename to vendor/github.com/coredns/coredns/plugin/pkg/dnsutil/host.go index 87177125fb..aaab586e81 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/parse/host.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/dnsutil/host.go @@ -1,25 +1,19 @@ -package parse +package dnsutil import ( "fmt" "net" "os" - "github.com/coredns/coredns/plugin/pkg/transport" - "github.com/miekg/dns" ) -// HostPortOrFile parses the strings in s, each string can either be a -// address, [scheme://]address:port or a filename. The address part is checked -// and in case of filename a resolv.conf like file is (assumed) and parsed and -// the nameservers found are returned. -func HostPortOrFile(s ...string) ([]string, error) { +// ParseHostPortOrFile parses the strings in s, each string can either be a address, +// address:port or a filename. The address part is checked and the filename case a +// resolv.conf like file is parsed and the nameserver found are returned. +func ParseHostPortOrFile(s ...string) ([]string, error) { var servers []string - for _, h := range s { - - trans, host := Transport(h) - + for _, host := range s { addr, _, err := net.SplitHostPort(host) if err != nil { // Parse didn't work, it is not a addr:port combo @@ -32,23 +26,13 @@ func HostPortOrFile(s ...string) ([]string, error) { } return servers, fmt.Errorf("not an IP address or file: %q", host) } - var ss string - switch trans { - case transport.DNS: - ss = net.JoinHostPort(host, transport.Port) - case transport.TLS: - ss = transport.TLS + "://" + net.JoinHostPort(host, transport.TLSPort) - case transport.GRPC: - ss = transport.GRPC + "://" + net.JoinHostPort(host, transport.GRPCPort) - case transport.HTTPS: - ss = transport.HTTPS + "://" + net.JoinHostPort(host, transport.HTTPSPort) - } + ss := net.JoinHostPort(host, "53") servers = append(servers, ss) continue } if net.ParseIP(addr) == nil { - // Not an IP address. + // No an IP address. ss, err := tryFile(host) if err == nil { servers = append(servers, ss...) @@ -56,7 +40,7 @@ func HostPortOrFile(s ...string) ([]string, error) { } return servers, fmt.Errorf("not an IP address or file: %q", host) } - servers = append(servers, h) + servers = append(servers, host) } return servers, nil } @@ -77,9 +61,9 @@ func tryFile(s string) ([]string, error) { return servers, nil } -// HostPort will check if the host part is a valid IP address, if the +// ParseHostPort will check if the host part is a valid IP address, if the // IP address is valid, but no port is found, defaultPort is added. -func HostPort(s, defaultPort string) (string, error) { +func ParseHostPort(s, defaultPort string) (string, error) { addr, port, err := net.SplitHostPort(s) if port == "" { port = defaultPort diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/dnsutil/join.go b/vendor/github.com/coredns/coredns/plugin/pkg/dnsutil/join.go index b3a40db425..515bf3dad1 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/dnsutil/join.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/dnsutil/join.go @@ -8,10 +8,12 @@ import ( // Join joins labels to form a fully qualified domain name. If the last label is // the root label it is ignored. Not other syntax checks are performed. -func Join(labels ...string) string { +func Join(labels []string) string { ll := len(labels) if labels[ll-1] == "." { - return strings.Join(labels[:ll-1], ".") + "." + s := strings.Join(labels[:ll-1], ".") + return dns.Fqdn(s) } - return dns.Fqdn(strings.Join(labels, ".")) + s := strings.Join(labels, ".") + return dns.Fqdn(s) } diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/dnsutil/ttl.go b/vendor/github.com/coredns/coredns/plugin/pkg/dnsutil/ttl.go index e2b26526df..e969fa8a65 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/dnsutil/ttl.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/dnsutil/ttl.go @@ -14,21 +14,34 @@ func MinimalTTL(m *dns.Msg, mt response.Type) time.Duration { return MinimalDefaultTTL } - // No records or OPT is the only record, return a short ttl as a fail safe. - if len(m.Answer)+len(m.Ns) == 0 && - (len(m.Extra) == 0 || (len(m.Extra) == 1 && m.Extra[0].Header().Rrtype == dns.TypeOPT)) { + // No data to examine, return a short ttl as a fail safe. + if len(m.Answer)+len(m.Ns)+len(m.Extra) == 0 { return MinimalDefaultTTL } minTTL := MaximumDefaulTTL for _, r := range m.Answer { - if r.Header().Ttl < uint32(minTTL.Seconds()) { - minTTL = time.Duration(r.Header().Ttl) * time.Second + switch mt { + case response.NameError, response.NoData: + if r.Header().Rrtype == dns.TypeSOA { + minTTL = time.Duration(r.(*dns.SOA).Minttl) * time.Second + } + case response.NoError, response.Delegation: + if r.Header().Ttl < uint32(minTTL.Seconds()) { + minTTL = time.Duration(r.Header().Ttl) * time.Second + } } } for _, r := range m.Ns { - if r.Header().Ttl < uint32(minTTL.Seconds()) { - minTTL = time.Duration(r.Header().Ttl) * time.Second + switch mt { + case response.NameError, response.NoData: + if r.Header().Rrtype == dns.TypeSOA { + minTTL = time.Duration(r.(*dns.SOA).Minttl) * time.Second + } + case response.NoError, response.Delegation: + if r.Header().Ttl < uint32(minTTL.Seconds()) { + minTTL = time.Duration(r.Header().Ttl) * time.Second + } } } @@ -37,8 +50,15 @@ func MinimalTTL(m *dns.Msg, mt response.Type) time.Duration { // OPT records use TTL field for extended rcode and flags continue } - if r.Header().Ttl < uint32(minTTL.Seconds()) { - minTTL = time.Duration(r.Header().Ttl) * time.Second + switch mt { + case response.NameError, response.NoData: + if r.Header().Rrtype == dns.TypeSOA { + minTTL = time.Duration(r.(*dns.SOA).Minttl) * time.Second + } + case response.NoError, response.Delegation: + if r.Header().Ttl < uint32(minTTL.Seconds()) { + minTTL = time.Duration(r.Header().Ttl) * time.Second + } } } return minTTL diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/doh/doh.go b/vendor/github.com/coredns/coredns/plugin/pkg/doh/doh.go index 812b89b1e2..e0a398e9c7 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/doh/doh.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/doh/doh.go @@ -53,7 +53,7 @@ func NewRequest(method, url string, m *dns.Msg) (*http.Request, error) { } -// ResponseToMsg converts a http.Response to a dns message. +// ResponseToMsg converts a http.Repsonse to a dns message. func ResponseToMsg(resp *http.Response) (*dns.Msg, error) { defer resp.Body.Close() 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 2c8971e1d4..161fbc8869 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/log/log.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/log/log.go @@ -1,36 +1,29 @@ // Package log implements a small wrapper around the std lib log package. -// It implements log levels by prefixing the logs with the current time -// with in RFC3339Milli and [INFO], [DEBUG], [WARNING] or [ERROR]. +// It implements log levels by prefixing the logs with [INFO], [DEBUG], +// [WARNING] or [ERROR]. // Debug logging is available and enabled if the *debug* plugin is used. // // log.Info("this is some logging"), will log on the Info level. // -// log.Debug("this is debug output"), will log in the Debug level, etc. +// log.Debug("this is debug output"), will log in the Debug level. package log import ( "fmt" - "io/ioutil" golog "log" - "os" - "time" ) // D controls whether we should output debug logs. If true, we do. var D bool -// RFC3339Milli doesn't exist, invent it here. -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{}) { - golog.Print(clock(), level, fmt.Sprintf(format, v...)) + s := level + fmt.Sprintf(format, v...) + golog.Print(s) } // log calls log.Print prefixed with level. -func log(level string, v ...interface{}) { - golog.Print(clock(), level, fmt.Sprint(v...)) -} +func log(level string, v ...interface{}) { s := level + fmt.Sprint(v...); golog.Print(s) } // Debug is equivalent to log.Print(), but prefixed with "[DEBUG] ". It only outputs something // if D is true. @@ -68,21 +61,9 @@ func Error(v ...interface{}) { log(err, v...) } // Errorf is equivalent to log.Printf, but prefixed with "[ERROR] ". func Errorf(format string, v ...interface{}) { logf(err, format, v...) } -// Fatal is equivalent to log.Print, but prefixed with "[FATAL] ", and calling -// os.Exit(1). -func Fatal(v ...interface{}) { log(fatal, v...); os.Exit(1) } - -// Fatalf is equivalent to log.Printf, but prefixed with "[FATAL] ", and calling -// os.Exit(1) -func Fatalf(format string, v ...interface{}) { logf(fatal, format, v...); os.Exit(1) } - -// Discard sets the log output to /dev/null. -func Discard() { golog.SetOutput(ioutil.Discard) } - const ( - debug = " [DEBUG] " - err = " [ERROR] " - fatal = " [FATAL] " - info = " [INFO] " - warning = " [WARNING] " + debug = "[DEBUG] " + err = "[ERROR] " + warning = "[WARNING] " + info = "[INFO] " ) 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 1df3026095..354c19d3fb 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/log/plugin.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/log/plugin.go @@ -3,7 +3,6 @@ package log import ( "fmt" golog "log" - "os" ) // P is a logger that includes the plugin doing the logging. @@ -59,10 +58,4 @@ func (p P) Error(v ...interface{}) { p.log(err, v...) } // Errorf logs as log.Errorf. func (p P) Errorf(format string, v ...interface{}) { p.logf(err, format, v...) } -// Fatal logs as log.Fatal and calls os.Exit(1). -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/parse/parse.go b/vendor/github.com/coredns/coredns/plugin/pkg/parse/parse.go index 17fe75fb08..17d2641ef6 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/parse/parse.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/parse/parse.go @@ -4,8 +4,7 @@ package parse import ( "fmt" - "github.com/coredns/coredns/plugin/pkg/transport" - + "github.com/coredns/coredns/plugin/pkg/dnsutil" "github.com/mholt/caddy" ) @@ -20,7 +19,7 @@ func Transfer(c *caddy.Controller, secondary bool) (tos, froms []string, err err tos = c.RemainingArgs() for i := range tos { if tos[i] != "*" { - normalized, err := HostPort(tos[i], transport.Port) + normalized, err := dnsutil.ParseHostPort(tos[i], "53") if err != nil { return nil, nil, err } @@ -35,7 +34,7 @@ func Transfer(c *caddy.Controller, secondary bool) (tos, froms []string, err err froms = c.RemainingArgs() for i := range froms { if froms[i] != "*" { - normalized, err := HostPort(froms[i], transport.Port) + normalized, err := dnsutil.ParseHostPort(froms[i], "53") if err != nil { return nil, nil, err } diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/parse/transport.go b/vendor/github.com/coredns/coredns/plugin/pkg/parse/transport.go deleted file mode 100644 index d632120d77..0000000000 --- a/vendor/github.com/coredns/coredns/plugin/pkg/parse/transport.go +++ /dev/null @@ -1,33 +0,0 @@ -package parse - -import ( - "strings" - - "github.com/coredns/coredns/plugin/pkg/transport" -) - -// Transport returns the transport defined in s and a string where the -// transport prefix is removed (if there was any). If no transport is defined -// we default to TransportDNS -func Transport(s string) (trans string, addr string) { - switch { - case strings.HasPrefix(s, transport.TLS+"://"): - s = s[len(transport.TLS+"://"):] - return transport.TLS, s - - case strings.HasPrefix(s, transport.DNS+"://"): - s = s[len(transport.DNS+"://"):] - return transport.DNS, s - - case strings.HasPrefix(s, transport.GRPC+"://"): - s = s[len(transport.GRPC+"://"):] - return transport.GRPC, s - - case strings.HasPrefix(s, transport.HTTPS+"://"): - s = s[len(transport.HTTPS+"://"):] - - return transport.HTTPS, s - } - - return transport.DNS, 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 695e9e392a..b9e1fcaa67 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/replacer/replacer.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/replacer/replacer.go @@ -35,11 +35,13 @@ 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 + "{type}": req.Type(), + "{name}": req.Name(), + "{class}": req.Class(), + "{proto}": req.Proto(), + "{when}": func() string { + return time.Now().Format(timeFormat) + }(), "{size}": strconv.Itoa(req.Len()), "{remote}": addrToRFC3986(req.IP()), "{port}": req.Port(), @@ -161,4 +163,7 @@ func addrToRFC3986(addr string) string { return addr } -const headerReplacer = "{>" +const ( + timeFormat = "02/Jan/2006:15:04:05 -0700" + headerReplacer = "{>" +) diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/transport/transport.go b/vendor/github.com/coredns/coredns/plugin/pkg/transport/transport.go deleted file mode 100644 index 85b3bee5fc..0000000000 --- a/vendor/github.com/coredns/coredns/plugin/pkg/transport/transport.go +++ /dev/null @@ -1,21 +0,0 @@ -package transport - -// These transports are supported by CoreDNS. -const ( - DNS = "dns" - TLS = "tls" - GRPC = "grpc" - HTTPS = "https" -) - -// Port numbers for the various transports. -const ( - // Port is the default port for DNS - Port = "53" - // TLSPort is the default port for DNS-over-TLS. - TLSPort = "853" - // GRPCPort is the default port for DNS-over-gRPC. - GRPCPort = "443" - // HTTPSPort is the default port for DNS-over-HTTPS. - HTTPSPort = "443" -) diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/uniq/uniq.go b/vendor/github.com/coredns/coredns/plugin/pkg/uniq/uniq.go index f4b4f54a69..3e50d64b5f 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/uniq/uniq.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/uniq/uniq.go @@ -10,7 +10,6 @@ type U struct { type item struct { state int // either todo or done f func() error // function to be executed. - obj interface{} // any object to return when needed } // New returns a new initialized U. @@ -18,19 +17,21 @@ func New() U { return U{u: make(map[string]item)} } // Set sets function f in U under key. If the key already exists // it is not overwritten. -func (u U) Set(key string, f func() error, o interface{}) interface{} { - if item, ok := u.u[key]; ok { - return item.obj +func (u U) Set(key string, f func() error) { + if _, ok := u.u[key]; ok { + return } - u.u[key] = item{todo, f, o} - return o + u.u[key] = item{todo, f} } -// Unset removes the key. -func (u U) Unset(key string) { - if _, ok := u.u[key]; ok { - delete(u.u, key) +// SetTodo sets key to 'todo' again. +func (u U) SetTodo(key string) { + v, ok := u.u[key] + if !ok { + return } + v.state = todo + u.u[key] = v } // ForEach iterates for u executes f for each element that is 'todo' and sets it to 'done'. 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 e2c9fe2cd8..e4752ce4d4 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/up/up.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/up/up.go @@ -18,7 +18,7 @@ type Probe struct { // Func is used to determine if a target is alive. If so this function must return nil. type Func func() error -// New returns a pointer to an initialized Probe. +// New returns a pointer to an intialized Probe. func New() *Probe { return &Probe{} } // Do will probe target, if a probe is already in progress this is a noop. diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/upstream/upstream.go b/vendor/github.com/coredns/coredns/plugin/pkg/upstream/upstream.go index 239d3dd96a..466da8990b 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/upstream/upstream.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/upstream/upstream.go @@ -6,8 +6,8 @@ import ( "github.com/miekg/dns" "github.com/coredns/coredns/core/dnsserver" + "github.com/coredns/coredns/plugin/pkg/dnsutil" "github.com/coredns/coredns/plugin/pkg/nonwriter" - "github.com/coredns/coredns/plugin/pkg/parse" "github.com/coredns/coredns/plugin/proxy" "github.com/coredns/coredns/request" ) @@ -27,7 +27,7 @@ func New(dests []string) (Upstream, error) { return u, nil } u.self = false - ups, err := parse.HostPortOrFile(dests...) + ups, err := dnsutil.ParseHostPortOrFile(dests...) if err != nil { return u, err } diff --git a/vendor/github.com/coredns/coredns/plugin/pkg/watch/watcher.go b/vendor/github.com/coredns/coredns/plugin/pkg/watch/watcher.go index 86a952db21..59474a7bc1 100644 --- a/vendor/github.com/coredns/coredns/plugin/pkg/watch/watcher.go +++ b/vendor/github.com/coredns/coredns/plugin/pkg/watch/watcher.go @@ -169,7 +169,7 @@ func (w *Manager) Stop() { wr := pb.WatchResponse{WatchId: id, Canceled: true} err := stream.Send(&wr) if err != nil { - log.Warningf("Error notifying client of cancellation: %s\n", err) + log.Warningf("Error notifiying client of cancellation: %s\n", err) } } delete(w.watches, wn) diff --git a/vendor/github.com/coredns/coredns/plugin/proxy/README.md b/vendor/github.com/coredns/coredns/plugin/proxy/README.md index b108b52a9b..f9dc3493b5 100644 --- a/vendor/github.com/coredns/coredns/plugin/proxy/README.md +++ b/vendor/github.com/coredns/coredns/plugin/proxy/README.md @@ -99,7 +99,7 @@ If monitoring is enabled (via the *prometheus* directive) then the following met Where `proxy_proto` is the protocol used (`dns` or `grpc`) and `to` is **TO** specified in the config, `proto` is the protocol used by the incoming query ("tcp" or "udp"), family the transport family ("1" for IPv4, and "2" for IPv6). `Server` is the server responsible for the -request (and metric). See the documentation in the metrics plugin. +request (and metric). See the documention in the metrics plugin. ## Examples diff --git a/vendor/github.com/coredns/coredns/plugin/proxy/dns.go b/vendor/github.com/coredns/coredns/plugin/proxy/dns.go index a96762c1dd..d3153bdff7 100644 --- a/vendor/github.com/coredns/coredns/plugin/proxy/dns.go +++ b/vendor/github.com/coredns/coredns/plugin/proxy/dns.go @@ -64,6 +64,10 @@ func (d *dnsEx) Exchange(ctx context.Context, addr string, state request.Request return nil, err } reply.Id = state.Req.Id + // When using force_tcp the upstream can send a message that is too big for + // the udp buffer, hence we need to truncate the message to at least make it + // fit the udp buffer. + reply, _ = state.Scrub(reply) return reply, nil } @@ -89,12 +93,15 @@ func exchange(m *dns.Msg, co net.Conn) (*dns.Msg, error) { writeDeadline := time.Now().Add(defaultTimeout) dnsco.SetWriteDeadline(writeDeadline) - if err := dnsco.WriteMsg(m); err != nil { - log.Debugf("Failed to send message: %v", err) - return nil, err - } + dnsco.WriteMsg(m) readDeadline := time.Now().Add(defaultTimeout) co.SetReadDeadline(readDeadline) - return dnsco.ReadMsg() + r, err := dnsco.ReadMsg() + + dnsco.Close() + if r == nil { + return nil, err + } + return r, err } diff --git a/vendor/github.com/coredns/coredns/plugin/proxy/lookup.go b/vendor/github.com/coredns/coredns/plugin/proxy/lookup.go index 0437e8eb73..44bee4b0eb 100644 --- a/vendor/github.com/coredns/coredns/plugin/proxy/lookup.go +++ b/vendor/github.com/coredns/coredns/plugin/proxy/lookup.go @@ -71,7 +71,7 @@ func (p Proxy) lookup(state request.Request) (*dns.Msg, error) { } for { start := time.Now() - var reply *dns.Msg + reply := new(dns.Msg) var backendErr error // Since Select() should give us "up" hosts, keep retrying diff --git a/vendor/github.com/coredns/coredns/plugin/proxy/metrics.go b/vendor/github.com/coredns/coredns/plugin/proxy/metrics.go index 0bea5ae4be..e5d6139b48 100644 --- a/vendor/github.com/coredns/coredns/plugin/proxy/metrics.go +++ b/vendor/github.com/coredns/coredns/plugin/proxy/metrics.go @@ -1,6 +1,8 @@ package proxy import ( + "sync" + "github.com/coredns/coredns/plugin" "github.com/prometheus/client_golang/prometheus" @@ -34,3 +36,5 @@ func familyToString(f int) string { } return "" } + +var once sync.Once diff --git a/vendor/github.com/coredns/coredns/plugin/proxy/proxy.go b/vendor/github.com/coredns/coredns/plugin/proxy/proxy.go index 002007c98b..2a8e350963 100644 --- a/vendor/github.com/coredns/coredns/plugin/proxy/proxy.go +++ b/vendor/github.com/coredns/coredns/plugin/proxy/proxy.go @@ -70,7 +70,7 @@ func (p Proxy) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) ( for { start := time.Now() - var reply *dns.Msg + reply := new(dns.Msg) var backendErr error // Since Select() should give us "up" hosts, keep retrying @@ -123,7 +123,7 @@ func (p Proxy) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) ( // Note this keeps looping and trying until tryDuration is hit, at which point our client // might be long gone... if oe.Timeout() { - // Our upstream's upstream is probably messing up, continue with next selected + // Our upstream's upstream is problably messing up, continue with next selected // host - which my be the *same* one as we don't set any uh.Fails. continue } diff --git a/vendor/github.com/coredns/coredns/plugin/proxy/setup.go b/vendor/github.com/coredns/coredns/plugin/proxy/setup.go index 2f3d7dd629..279e02cac9 100644 --- a/vendor/github.com/coredns/coredns/plugin/proxy/setup.go +++ b/vendor/github.com/coredns/coredns/plugin/proxy/setup.go @@ -33,7 +33,7 @@ func setup(c *caddy.Controller) error { }) c.OnStartup(func() error { - metrics.MustRegister(c, RequestCount, RequestDuration) + once.Do(func() { metrics.MustRegister(c, RequestCount, RequestDuration) }) return nil }) diff --git a/vendor/github.com/coredns/coredns/plugin/proxy/upstream.go b/vendor/github.com/coredns/coredns/plugin/proxy/upstream.go index 0d62f45e20..72d9e04a76 100644 --- a/vendor/github.com/coredns/coredns/plugin/proxy/upstream.go +++ b/vendor/github.com/coredns/coredns/plugin/proxy/upstream.go @@ -7,8 +7,8 @@ import ( "time" "github.com/coredns/coredns/plugin" + "github.com/coredns/coredns/plugin/pkg/dnsutil" "github.com/coredns/coredns/plugin/pkg/healthcheck" - "github.com/coredns/coredns/plugin/pkg/parse" "github.com/coredns/coredns/plugin/pkg/tls" "github.com/mholt/caddy/caddyfile" "github.com/miekg/dns" @@ -60,7 +60,7 @@ func NewStaticUpstream(c *caddyfile.Dispenser) (Upstream, error) { } // process the host list, substituting in any nameservers in files - toHosts, err := parse.HostPortOrFile(to...) + toHosts, err := dnsutil.ParseHostPortOrFile(to...) if err != nil { return upstream, err } diff --git a/vendor/github.com/coredns/coredns/plugin/reload/README.md b/vendor/github.com/coredns/coredns/plugin/reload/README.md index 625e4b0255..eb06bb71ca 100644 --- a/vendor/github.com/coredns/coredns/plugin/reload/README.md +++ b/vendor/github.com/coredns/coredns/plugin/reload/README.md @@ -84,7 +84,7 @@ is already listening on that port. The process reloads and performs the followin 3. fail to start a new listener on 443 4. fail loading the new Corefile, abort and keep using the old process -After the aborted attempt to reload we are left with the old processes running, but the listener is +After the aborted attempt to reload we are left with the old proceses running, but the listener is 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. diff --git a/vendor/github.com/coredns/coredns/plugin/trace/trace.go b/vendor/github.com/coredns/coredns/plugin/trace/trace.go index 5421836a58..a0a2071ea4 100644 --- a/vendor/github.com/coredns/coredns/plugin/trace/trace.go +++ b/vendor/github.com/coredns/coredns/plugin/trace/trace.go @@ -10,11 +10,8 @@ import ( "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/metrics" - "github.com/coredns/coredns/plugin/pkg/dnstest" - "github.com/coredns/coredns/plugin/pkg/rcode" // Plugin the trace package. _ "github.com/coredns/coredns/plugin/pkg/trace" - "github.com/coredns/coredns/request" ddtrace "github.com/DataDog/dd-trace-go/opentracing" "github.com/miekg/dns" @@ -22,12 +19,6 @@ import ( zipkin "github.com/openzipkin/zipkin-go-opentracing" ) -const ( - tagName = "coredns.io/name" - tagType = "coredns.io/type" - tagRcode = "coredns.io/rcode" -) - type trace struct { Next plugin.Handler Endpoint string @@ -103,26 +94,10 @@ func (t *trace) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) trace = true } } - span := ot.SpanFromContext(ctx) - if !trace || span != nil { - return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r) + if span := ot.SpanFromContext(ctx); span == nil && trace { + span := t.Tracer().StartSpan("servedns:" + metrics.WithServer(ctx)) + defer span.Finish() + ctx = ot.ContextWithSpan(ctx, span) } - - req := request.Request{W: w, Req: r} - span = t.Tracer().StartSpan(spanName(ctx, req)) - defer span.Finish() - - rw := dnstest.NewRecorder(w) - ctx = ot.ContextWithSpan(ctx, span) - status, err := plugin.NextOrFailure(t.Name(), t.Next, ctx, rw, r) - - span.SetTag(tagName, req.Name()) - span.SetTag(tagType, req.Type()) - span.SetTag(tagRcode, rcode.ToString(rw.Rcode)) - - return status, err -} - -func spanName(ctx context.Context, req request.Request) string { - return "servedns:" + metrics.WithServer(ctx) + " " + req.Name() + return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r) } diff --git a/vendor/github.com/coredns/coredns/request/request.go b/vendor/github.com/coredns/coredns/request/request.go index 260d73f5eb..06f840f89e 100644 --- a/vendor/github.com/coredns/coredns/request/request.go +++ b/vendor/github.com/coredns/coredns/request/request.go @@ -226,6 +226,18 @@ func (r *Request) SizeAndDo(m *dns.Msg) bool { return true } +// Result is the result of Scrub. +type Result int + +const ( + // ScrubIgnored is returned when Scrub did nothing to the message. + ScrubIgnored Result = iota + // ScrubExtra is returned when the reply has been scrubbed by removing RRs from the additional section. + ScrubExtra + // ScrubAnswer is returned when the reply has been scrubbed by removing RRs from the answer section. + ScrubAnswer +) + // Scrub scrubs the reply message so that it will fit the client's buffer. It will first // check if the reply fits without compression and then *with* compression. // Scrub will then use binary search to find a save cut off point in the additional section. @@ -234,32 +246,30 @@ func (r *Request) SizeAndDo(m *dns.Msg) bool { // we set the TC bit on the reply; indicating the client should retry over TCP. // Note, the TC bit will be set regardless of protocol, even TCP message will // get the bit, the client should then retry with pigeons. -func (r *Request) Scrub(reply *dns.Msg) *dns.Msg { +func (r *Request) Scrub(reply *dns.Msg) (*dns.Msg, Result) { size := r.Size() reply.Compress = false rl := reply.Len() if size >= rl { - return reply + return reply, ScrubIgnored } reply.Compress = true rl = reply.Len() if size >= rl { - return reply + return reply, ScrubIgnored } // Account for the OPT record that gets added in SizeAndDo(), subtract that length. - re := len(reply.Extra) - if r.Req.IsEdns0() != nil { - size -= optLen - // re can never be 0 because we have an OPT RR. - re-- + sub := 0 + if r.Do() { + sub = optLen } - - l, m := 0, 0 origExtra := reply.Extra - for l <= re { + re := len(reply.Extra) - sub + l, m := 0, 0 + for l < re { m = (l + re) / 2 reply.Extra = origExtra[:m] rl = reply.Len() @@ -276,26 +286,21 @@ func (r *Request) Scrub(reply *dns.Msg) *dns.Msg { } } - // The binary search only breaks on an exact match, which will be - // 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 preceeding - // iteration the size was too small. Select that preceeding size. + // We may come out of this loop with one rotation too many, m makes it too large, but m-1 works. if rl > size && m > 0 { reply.Extra = origExtra[:m-1] rl = reply.Len() } - if rl <= size { + if rl < size { r.SizeAndDo(reply) - return reply + return reply, ScrubExtra } + origAnswer := reply.Answer ra := len(reply.Answer) l, m = 0, 0 - origAnswer := reply.Answer - for l <= ra { + for l < ra { m = (l + ra) / 2 reply.Answer = origAnswer[:m] rl = reply.Len() @@ -312,24 +317,21 @@ func (r *Request) Scrub(reply *dns.Msg) *dns.Msg { } } - // The binary search only breaks on an exact match, which will be - // 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 preceeding - // iteration the size was too small. Select that preceeding size. + // We may come out of this loop with one rotation too many, m makes it too large, but m-1 works. 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) + // It now fits, but Truncated. We can't call sizeAndDo() because that adds a new record (OPT) + // in the additional section. reply.Truncated = true - return reply + return reply, ScrubAnswer } -// Type returns the type of the question as a string. If the request is malformed the empty string is returned. +// Type returns the type of the question as a string. If the request is malformed +// the empty string is returned. func (r *Request) Type() string { if r.Req == nil { return "" diff --git a/vendor/github.com/coredns/coredns/request/writer.go b/vendor/github.com/coredns/coredns/request/writer.go deleted file mode 100644 index ffbbe93e3c..0000000000 --- a/vendor/github.com/coredns/coredns/request/writer.go +++ /dev/null @@ -1,20 +0,0 @@ -package request - -import "github.com/miekg/dns" - -// ScrubWriter will, when writing the message, call scrub to make it fit the client's buffer. -type ScrubWriter struct { - dns.ResponseWriter - req *dns.Msg // original request -} - -// 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 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) - return s.ResponseWriter.WriteMsg(n) -} 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 0000000000..c5275d5ab1 --- /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 c0d70b2faf..623d3d83fe 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 d463e36d3e..ee37949ada 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 7b8827ffbc..77f4b30e84 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 } @@ -104,12 +127,6 @@ 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. @@ -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 5d9525defc..b15a2d3b98 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. +// 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 +// 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 3d383a735c..e3b67df8ac 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 71d406bd92..8b70e5141d 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 ba3b9333ed..abc9d4ec40 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 f88da707bc..9719e8fac8 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. + + desc *Desc upperBounds []float64 - - // 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. + 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 - ) + his := &dto.Histogram{} + buckets := make([]*dto.Bucket, len(h.upperBounds)) - // 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.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 +func (m *HistogramVec) WithLabelValues(lvs ...string) Histogram { + return m.MetricVec.WithLabelValues(lvs...).(Histogram) } -// 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 -} - -// 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 9f0875bfc8..67ee5ac794 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()) +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) - out := make(chan int, 1) + // N.B. r.Form and r.MultipartForm are assumed to be included in r.URL. - 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) - } - } - 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) - } - out <- s - close(out) - }() - - 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 351c26e1ae..0000000000 --- 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 2744443ac2..0000000000 --- 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 55e6d86d59..d4063d98f4 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 5806cd09e3..0000000000 --- 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 55176d58ce..e31e62e78d 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 + "_" - } +// 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, + ) +} - 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, - ), - } +// 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) {}, - if opts.PidFn == nil { - pid := os.Getpid() - c.pidFn = func() (int, error) { return pid, nil } - } else { - c.pidFn = opts.PidFn + 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) + c.maxFDs.Set(float64(limits.OpenFiles)) + ch <- c.maxFDs } } - -func (c *processCollector) reportError(ch chan<- Metric, desc *Desc, err error) { - if !c.reportErrors { - return - } - if desc == nil { - desc = NewInvalidDesc(err) - } - 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 67b56d37cf..0000000000 --- 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 31a7069569..0000000000 --- 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 8bb9b8b68f..0000000000 --- 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 668eb6b3c9..b6dd5a266c 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. +// 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. // -// 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. -// -// 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 -// 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. -// -// 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. -func Handler() http.Handler { - return InstrumentMetricHandler( - prometheus.DefaultRegisterer, 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. -func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler { - var inFlightSem chan struct{} - if opts.MaxRequestsInFlight > 0 { - inFlightSem = make(chan struct{}, opts.MaxRequestsInFlight) +func getBuf() *bytes.Buffer { + buf := bufPool.Get() + if buf == nil { + return &bytes.Buffer{} } + return buf.(*bytes.Buffer) +} - 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 - } - } +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. +// +// 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 HandlerFor(prometheus.DefaultGatherer, HandlerOpts{}) +} + +// 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 { + 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 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()) + // TODO(beorn7): Consider streaming serving of metrics. }) - - 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) - } - } - - 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) - } - } - - return InstrumentHandlerCounter(cnt, InstrumentHandlerInFlight(gge, handler)) } // 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 86fd564470..0000000000 --- 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 a034d1ec0f..0000000000 --- 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 9db2438053..0000000000 --- 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 f98c81a869..32a3986b06 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) - }() - - // 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, - )) - case metric, ok := <-umc: - if !ok { - umc = nil - break - } - errs.Append(processMetric( - metric, metricFamiliesByName, - metricHashes, - nil, + // 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, )) + continue } - break + 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 + } + 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") } - // 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, - ) + } 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_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 { - return fmt.Errorf( - "collected metric %s %s should be a Summary", - desc.fqName, dtoMetric, - ) - } - case dto.MetricType_UNTYPED: - if dtoMetric.Untyped == nil { - return fmt.Errorf( - "collected metric %s %s should be Untyped", - desc.fqName, dtoMetric, - ) - } - case dto.MetricType_HISTOGRAM: - if dtoMetric.Histogram == nil { - return fmt.Errorf( - "collected metric %s %s should be a Histogram", - desc.fqName, dtoMetric, - ) - } - default: - panic("encountered MetricFamily with invalid type") + metricFamiliesByName[desc.fqName] = metricFamily } - } 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 := checkMetricConsistency(metricFamily, dtoMetric, metricHashes, dimHashes); err != nil { + errs = append(errs, err) + continue } - if err := checkSuffixCollisions(metricFamily, metricFamiliesByName); err != nil { - return err + 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 + } + if err := checkDescConsistency(metricFamily, dtoMetric, desc); err != nil { + errs = append(errs, err) + continue + } } - 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] +// 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) } - 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, - ) - } + for n, lp := range s[i].Label { + vi := lp.GetValue() + vj := s[j].Label[n].GetValue() + if vi != vj { + return vi < vj } } - 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", - ) + + // 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 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) } } - 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", - ) - } + sort.Strings(names) + result := make([]*dto.MetricFamily, 0, len(names)) + for _, name := range names { + result = append(result, metricFamiliesByName[name]) } - return nil + 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,59 +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. - sort.Sort(labelPairSorter(dtoMetric.Label)) + 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 } @@ -916,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 2980614dff..bce05bf9a0 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 b8fc5f18c8..0000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/timer.go +++ /dev/null @@ -1,51 +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. 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() { - if t.observer != nil { - t.observer.Observe(time.Since(t.begin).Seconds()) - } -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/untyped.go b/vendor/github.com/prometheus/client_golang/prometheus/untyped.go index 0f9ce63f40..5faf7e6e3e 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 eb248f1087..a944c37754 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 14ed9e856d..7f3eef9a46 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/vec.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/vec.go @@ -20,180 +20,33 @@ 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 +// 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 - curry []curriedLabelValue - - // hashAdd and hashAddByte can be replaced for testing collision handling. - hashAdd func(h uint64, s string) uint64 + 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) -} - -// 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) -} - -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}) - } - } - 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) { - h, err := m.hashLabelValues(lvs) - if err != nil { - return nil, err - } - - return m.metricMap.getOrCreateMetricWithLabelValues(h, lvs, m.curry), nil -} - -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 -} - -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) - } - return h, nil -} - -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) - } - return h, nil -} - // metricWithLabelValues provides the metric and its label values for // disambiguation on hash collision. type metricWithLabelValues struct { @@ -201,72 +54,166 @@ type metricWithLabelValues struct { 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) { +// Describe implements Collector. The length of the returned slice +// is always one. +func (m *MetricVec) Describe(ch chan<- *Desc) { ch <- m.desc } // Collect implements Collector. -func (m *metricMap) Collect(ch chan<- Metric) { +func (m *MetricVec) Collect(ch chan<- Metric) { m.mtx.RLock() defer m.mtx.RUnlock() - for _, metrics := range m.metrics { + for _, metrics := range m.children { for _, metric := range metrics { ch <- metric.metric } } } -// Reset deletes all metrics in this vector. -func (m *metricMap) Reset() { +// 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.getOrCreateMetricWithLabelValues(h, lvs), nil +} + +// 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.getOrCreateMetricWithLabels(h, labels), nil +} + +// 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 metric +} + +// 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 metric +} + +// 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 h := range m.metrics { - delete(m.metrics, h) + h, err := m.hashLabelValues(lvs) + if err != nil { + return false } + return m.deleteByHashWithLabelValues(h, lvs) +} + +// 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() + + 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 49159bf3eb..0000000000 --- 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 -} From 09d638456c08bbec2400b9fb5f80f14fb86d19d0 Mon Sep 17 00:00:00 2001 From: TangBin Date: Mon, 12 Nov 2018 20:31:54 +0800 Subject: [PATCH 2/3] fix depends --- Gopkg.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gopkg.toml b/Gopkg.toml index e8e23c55f2..81392f3eff 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -181,11 +181,11 @@ branch = "master" name = "yunion.io/x/structarg" -[[constraint]] +[[override]] name = "github.com/prometheus/client_golang" version = "=v0.8.0" -[[constraint]] +[[override]] revision = "e59b73d3c2bf1c328ccb78e683c0462fa1a473c7" name = "github.com/apache/thrift" From 6599ddc53b9a0ddb46c85ce08238c6e90c3e4b03 Mon Sep 17 00:00:00 2001 From: TangBin Date: Mon, 12 Nov 2018 20:43:03 +0800 Subject: [PATCH 3/3] rm transitive dependents --- Gopkg.toml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Gopkg.toml b/Gopkg.toml index 81392f3eff..64685308fd 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -63,7 +63,7 @@ [[constraint]] name = "github.com/coredns/coredns" - version = "=v1.2.0" + version = "v1.2.0" [[constraint]] name = "github.com/fatih/color" @@ -181,14 +181,6 @@ branch = "master" name = "yunion.io/x/structarg" -[[override]] - name = "github.com/prometheus/client_golang" - version = "=v0.8.0" - -[[override]] - revision = "e59b73d3c2bf1c328ccb78e683c0462fa1a473c7" - name = "github.com/apache/thrift" - [prune] go-tests = true unused-packages = true