diff --git a/Gopkg.lock b/Gopkg.lock
index d4323f62ad5209a52ad7924f0b9f092993a80417..e4cc221dfba1a068a91a5421b09bafe436b38a32 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -527,14 +527,16 @@
   version = "v1.0.0"
 
 [[projects]]
-  digest = "1:3d2ac72e9e669ccb31a9bb68fdd782c4766bae32c4269f527115927a4b44cd65"
+  digest = "1:984304bfc0e10d78028584caa88a6025f43011266ed7b3efad9a81a454966b7d"
   name = "github.com/prometheus/client_golang"
   packages = [
     "prometheus",
+    "prometheus/internal",
     "prometheus/promhttp",
   ]
   pruneopts = "N"
-  revision = "334af0119a8f8fb6af5bb950d535c482cac7f836"
+  revision = "505eaef017263e299324067d40ca2c48f6a2cf50"
+  version = "v0.9.2"
 
 [[projects]]
   branch = "master"
diff --git a/Gopkg.toml b/Gopkg.toml
index b6566416d8550c375a7ef1ac08c6930668e9e537..60ae0dcf0d75f1d053c5d6ed990cf9beea81b7bf 100644
--- a/Gopkg.toml
+++ b/Gopkg.toml
@@ -89,8 +89,7 @@ ignored = ["test", "appengine"]
 
 [[constraint]]
   name = "github.com/prometheus/client_golang"
-  # version = "0.8.0"
-  revision = "334af0119a8f8fb6af5bb950d535c482cac7f836"
+  version = "0.9.0"
 
 [[constraint]]
   name = "github.com/stretchr/testify"
diff --git a/commands/multi.go b/commands/multi.go
index 9149d280f769f8c2d2f2a70f42a9a2626f72c0ad..1f87c32d9d60683ed2f5b401b8076da1b51ad63b 100644
--- a/commands/multi.go
+++ b/commands/multi.go
@@ -458,7 +458,7 @@ func (mr *RunCommand) serveMetrics(mux *http.ServeMux) {
 	// Go-specific metrics about the process (GC stats, goroutines, etc.).
 	registry.MustRegister(prometheus.NewGoCollector())
 	// Go-unrelated process metrics (memory usage, file descriptors, etc.).
-	registry.MustRegister(prometheus.NewProcessCollector(os.Getpid(), ""))
+	registry.MustRegister(prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}))
 
 	// Register all executor provider collectors
 	for _, provider := range common.GetExecutorProviders() {
diff --git a/vendor/github.com/prometheus/client_golang/AUTHORS.md b/vendor/github.com/prometheus/client_golang/AUTHORS.md
deleted file mode 100644
index c5275d5ab1949a879479e570d5984499c8362d5b..0000000000000000000000000000000000000000
--- a/vendor/github.com/prometheus/client_golang/AUTHORS.md
+++ /dev/null
@@ -1,18 +0,0 @@
-The Prometheus project was started by Matt T. Proud (emeritus) and
-Julius Volz in 2012.
-
-Maintainers of this repository:
-
-* Björn Rabenstein <beorn@soundcloud.com>
-
-The following individuals have contributed code to this repository
-(listed in alphabetical order):
-
-* Bernerd Schaefer <bj.schaefer@gmail.com>
-* Björn Rabenstein <beorn@soundcloud.com>
-* Daniel Bornkessel <daniel@soundcloud.com>
-* Jeff Younker <jeff@drinktomi.com>
-* Julius Volz <julius.volz@gmail.com>
-* Matt T. Proud <matt.proud@gmail.com>
-* Tobias Schmidt <ts@soundcloud.com>
-
diff --git a/vendor/github.com/prometheus/client_golang/api/client.go b/vendor/github.com/prometheus/client_golang/api/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..09af749caa5476990842eb5b3e56bd682ce57737
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/api/client.go
@@ -0,0 +1,131 @@
+// Copyright 2015 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.7
+
+// Package api provides clients for the HTTP APIs.
+package api
+
+import (
+	"context"
+	"io/ioutil"
+	"net"
+	"net/http"
+	"net/url"
+	"path"
+	"strings"
+	"time"
+)
+
+// DefaultRoundTripper is used if no RoundTripper is set in Config.
+var DefaultRoundTripper http.RoundTripper = &http.Transport{
+	Proxy: http.ProxyFromEnvironment,
+	DialContext: (&net.Dialer{
+		Timeout:   30 * time.Second,
+		KeepAlive: 30 * time.Second,
+	}).DialContext,
+	TLSHandshakeTimeout: 10 * time.Second,
+}
+
+// Config defines configuration parameters for a new client.
+type Config struct {
+	// The address of the Prometheus to connect to.
+	Address string
+
+	// RoundTripper is used by the Client to drive HTTP requests. If not
+	// provided, DefaultRoundTripper will be used.
+	RoundTripper http.RoundTripper
+}
+
+func (cfg *Config) roundTripper() http.RoundTripper {
+	if cfg.RoundTripper == nil {
+		return DefaultRoundTripper
+	}
+	return cfg.RoundTripper
+}
+
+// Client is the interface for an API client.
+type Client interface {
+	URL(ep string, args map[string]string) *url.URL
+	Do(context.Context, *http.Request) (*http.Response, []byte, error)
+}
+
+// NewClient returns a new Client.
+//
+// It is safe to use the returned Client from multiple goroutines.
+func NewClient(cfg Config) (Client, error) {
+	u, err := url.Parse(cfg.Address)
+	if err != nil {
+		return nil, err
+	}
+	u.Path = strings.TrimRight(u.Path, "/")
+
+	return &httpClient{
+		endpoint: u,
+		client:   http.Client{Transport: cfg.roundTripper()},
+	}, nil
+}
+
+type httpClient struct {
+	endpoint *url.URL
+	client   http.Client
+}
+
+func (c *httpClient) URL(ep string, args map[string]string) *url.URL {
+	p := path.Join(c.endpoint.Path, ep)
+
+	for arg, val := range args {
+		arg = ":" + arg
+		p = strings.Replace(p, arg, val, -1)
+	}
+
+	u := *c.endpoint
+	u.Path = p
+
+	return &u
+}
+
+func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
+	if ctx != nil {
+		req = req.WithContext(ctx)
+	}
+	resp, err := c.client.Do(req)
+	defer func() {
+		if resp != nil {
+			resp.Body.Close()
+		}
+	}()
+
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var body []byte
+	done := make(chan struct{})
+	go func() {
+		body, err = ioutil.ReadAll(resp.Body)
+		close(done)
+	}()
+
+	select {
+	case <-ctx.Done():
+		err = resp.Body.Close()
+		<-done
+		if err == nil {
+			err = ctx.Err()
+		}
+	case <-done:
+	}
+
+	return resp, body, err
+}
diff --git a/vendor/github.com/prometheus/client_golang/api/client_test.go b/vendor/github.com/prometheus/client_golang/api/client_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..b1bcfc9b8e361489c13146e1bc707e739be592e2
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/api/client_test.go
@@ -0,0 +1,115 @@
+// Copyright 2015 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.7
+
+package api
+
+import (
+	"net/http"
+	"net/url"
+	"testing"
+)
+
+func TestConfig(t *testing.T) {
+	c := Config{}
+	if c.roundTripper() != DefaultRoundTripper {
+		t.Fatalf("expected default roundtripper for nil RoundTripper field")
+	}
+}
+
+func TestClientURL(t *testing.T) {
+	tests := []struct {
+		address  string
+		endpoint string
+		args     map[string]string
+		expected string
+	}{
+		{
+			address:  "http://localhost:9090",
+			endpoint: "/test",
+			expected: "http://localhost:9090/test",
+		},
+		{
+			address:  "http://localhost",
+			endpoint: "/test",
+			expected: "http://localhost/test",
+		},
+		{
+			address:  "http://localhost:9090",
+			endpoint: "test",
+			expected: "http://localhost:9090/test",
+		},
+		{
+			address:  "http://localhost:9090/prefix",
+			endpoint: "/test",
+			expected: "http://localhost:9090/prefix/test",
+		},
+		{
+			address:  "https://localhost:9090/",
+			endpoint: "/test/",
+			expected: "https://localhost:9090/test",
+		},
+		{
+			address:  "http://localhost:9090",
+			endpoint: "/test/:param",
+			args: map[string]string{
+				"param": "content",
+			},
+			expected: "http://localhost:9090/test/content",
+		},
+		{
+			address:  "http://localhost:9090",
+			endpoint: "/test/:param/more/:param",
+			args: map[string]string{
+				"param": "content",
+			},
+			expected: "http://localhost:9090/test/content/more/content",
+		},
+		{
+			address:  "http://localhost:9090",
+			endpoint: "/test/:param/more/:foo",
+			args: map[string]string{
+				"param": "content",
+				"foo":   "bar",
+			},
+			expected: "http://localhost:9090/test/content/more/bar",
+		},
+		{
+			address:  "http://localhost:9090",
+			endpoint: "/test/:param",
+			args: map[string]string{
+				"nonexistent": "content",
+			},
+			expected: "http://localhost:9090/test/:param",
+		},
+	}
+
+	for _, test := range tests {
+		ep, err := url.Parse(test.address)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		hclient := &httpClient{
+			endpoint: ep,
+			client:   http.Client{Transport: DefaultRoundTripper},
+		}
+
+		u := hclient.URL(test.endpoint, test.args)
+		if u.String() != test.expected {
+			t.Errorf("unexpected result: got %s, want %s", u, test.expected)
+			continue
+		}
+	}
+}
diff --git a/vendor/github.com/prometheus/client_golang/api/prometheus/api.go b/vendor/github.com/prometheus/client_golang/api/prometheus/api.go
deleted file mode 100644
index cc5cbc364d3111c55a8d7b91a5fc29d15ae8503a..0000000000000000000000000000000000000000
--- a/vendor/github.com/prometheus/client_golang/api/prometheus/api.go
+++ /dev/null
@@ -1,348 +0,0 @@
-// Copyright 2015 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 provides bindings to the Prometheus HTTP API:
-// http://prometheus.io/docs/querying/api/
-package prometheus
-
-import (
-	"encoding/json"
-	"fmt"
-	"io/ioutil"
-	"net"
-	"net/http"
-	"net/url"
-	"path"
-	"strconv"
-	"strings"
-	"time"
-
-	"github.com/prometheus/common/model"
-	"golang.org/x/net/context"
-	"golang.org/x/net/context/ctxhttp"
-)
-
-const (
-	statusAPIError = 422
-	apiPrefix      = "/api/v1"
-
-	epQuery       = "/query"
-	epQueryRange  = "/query_range"
-	epLabelValues = "/label/:name/values"
-	epSeries      = "/series"
-)
-
-// ErrorType models the different API error types.
-type ErrorType string
-
-// Possible values for ErrorType.
-const (
-	ErrBadData     ErrorType = "bad_data"
-	ErrTimeout               = "timeout"
-	ErrCanceled              = "canceled"
-	ErrExec                  = "execution"
-	ErrBadResponse           = "bad_response"
-)
-
-// Error is an error returned by the API.
-type Error struct {
-	Type ErrorType
-	Msg  string
-}
-
-func (e *Error) Error() string {
-	return fmt.Sprintf("%s: %s", e.Type, e.Msg)
-}
-
-// CancelableTransport is like net.Transport but provides
-// per-request cancelation functionality.
-type CancelableTransport interface {
-	http.RoundTripper
-	CancelRequest(req *http.Request)
-}
-
-// DefaultTransport is used if no Transport is set in Config.
-var DefaultTransport CancelableTransport = &http.Transport{
-	Proxy: http.ProxyFromEnvironment,
-	Dial: (&net.Dialer{
-		Timeout:   30 * time.Second,
-		KeepAlive: 30 * time.Second,
-	}).Dial,
-	TLSHandshakeTimeout: 10 * time.Second,
-}
-
-// Config defines configuration parameters for a new client.
-type Config struct {
-	// The address of the Prometheus to connect to.
-	Address string
-
-	// Transport is used by the Client to drive HTTP requests. If not
-	// provided, DefaultTransport will be used.
-	Transport CancelableTransport
-}
-
-func (cfg *Config) transport() CancelableTransport {
-	if cfg.Transport == nil {
-		return DefaultTransport
-	}
-	return cfg.Transport
-}
-
-// Client is the interface for an API client.
-type Client interface {
-	url(ep string, args map[string]string) *url.URL
-	do(context.Context, *http.Request) (*http.Response, []byte, error)
-}
-
-// New returns a new Client.
-//
-// It is safe to use the returned Client from multiple goroutines.
-func New(cfg Config) (Client, error) {
-	u, err := url.Parse(cfg.Address)
-	if err != nil {
-		return nil, err
-	}
-	u.Path = strings.TrimRight(u.Path, "/") + apiPrefix
-
-	return &httpClient{
-		endpoint:  u,
-		transport: cfg.transport(),
-	}, nil
-}
-
-type httpClient struct {
-	endpoint  *url.URL
-	transport CancelableTransport
-}
-
-func (c *httpClient) url(ep string, args map[string]string) *url.URL {
-	p := path.Join(c.endpoint.Path, ep)
-
-	for arg, val := range args {
-		arg = ":" + arg
-		p = strings.Replace(p, arg, val, -1)
-	}
-
-	u := *c.endpoint
-	u.Path = p
-
-	return &u
-}
-
-func (c *httpClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
-	resp, err := ctxhttp.Do(ctx, &http.Client{Transport: c.transport}, req)
-
-	defer func() {
-		if resp != nil {
-			resp.Body.Close()
-		}
-	}()
-
-	if err != nil {
-		return nil, nil, err
-	}
-
-	var body []byte
-	done := make(chan struct{})
-	go func() {
-		body, err = ioutil.ReadAll(resp.Body)
-		close(done)
-	}()
-
-	select {
-	case <-ctx.Done():
-		err = resp.Body.Close()
-		<-done
-		if err == nil {
-			err = ctx.Err()
-		}
-	case <-done:
-	}
-
-	return resp, body, err
-}
-
-// apiClient wraps a regular client and processes successful API responses.
-// Successful also includes responses that errored at the API level.
-type apiClient struct {
-	Client
-}
-
-type apiResponse struct {
-	Status    string          `json:"status"`
-	Data      json.RawMessage `json:"data"`
-	ErrorType ErrorType       `json:"errorType"`
-	Error     string          `json:"error"`
-}
-
-func (c apiClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
-	resp, body, err := c.Client.do(ctx, req)
-	if err != nil {
-		return resp, body, err
-	}
-
-	code := resp.StatusCode
-
-	if code/100 != 2 && code != statusAPIError {
-		return resp, body, &Error{
-			Type: ErrBadResponse,
-			Msg:  fmt.Sprintf("bad response code %d", resp.StatusCode),
-		}
-	}
-
-	var result apiResponse
-
-	if err = json.Unmarshal(body, &result); err != nil {
-		return resp, body, &Error{
-			Type: ErrBadResponse,
-			Msg:  err.Error(),
-		}
-	}
-
-	if (code == statusAPIError) != (result.Status == "error") {
-		err = &Error{
-			Type: ErrBadResponse,
-			Msg:  "inconsistent body for response code",
-		}
-	}
-
-	if code == statusAPIError && result.Status == "error" {
-		err = &Error{
-			Type: result.ErrorType,
-			Msg:  result.Error,
-		}
-	}
-
-	return resp, []byte(result.Data), err
-}
-
-// Range represents a sliced time range.
-type Range struct {
-	// The boundaries of the time range.
-	Start, End time.Time
-	// The maximum time between two slices within the boundaries.
-	Step time.Duration
-}
-
-// queryResult contains result data for a query.
-type queryResult struct {
-	Type   model.ValueType `json:"resultType"`
-	Result interface{}     `json:"result"`
-
-	// The decoded value.
-	v model.Value
-}
-
-func (qr *queryResult) UnmarshalJSON(b []byte) error {
-	v := struct {
-		Type   model.ValueType `json:"resultType"`
-		Result json.RawMessage `json:"result"`
-	}{}
-
-	err := json.Unmarshal(b, &v)
-	if err != nil {
-		return err
-	}
-
-	switch v.Type {
-	case model.ValScalar:
-		var sv model.Scalar
-		err = json.Unmarshal(v.Result, &sv)
-		qr.v = &sv
-
-	case model.ValVector:
-		var vv model.Vector
-		err = json.Unmarshal(v.Result, &vv)
-		qr.v = vv
-
-	case model.ValMatrix:
-		var mv model.Matrix
-		err = json.Unmarshal(v.Result, &mv)
-		qr.v = mv
-
-	default:
-		err = fmt.Errorf("unexpected value type %q", v.Type)
-	}
-	return err
-}
-
-// QueryAPI provides bindings the Prometheus's query API.
-type QueryAPI interface {
-	// Query performs a query for the given time.
-	Query(ctx context.Context, query string, ts time.Time) (model.Value, error)
-	// Query performs a query for the given range.
-	QueryRange(ctx context.Context, query string, r Range) (model.Value, error)
-}
-
-// NewQueryAPI returns a new QueryAPI for the client.
-//
-// It is safe to use the returned QueryAPI from multiple goroutines.
-func NewQueryAPI(c Client) QueryAPI {
-	return &httpQueryAPI{client: apiClient{c}}
-}
-
-type httpQueryAPI struct {
-	client Client
-}
-
-func (h *httpQueryAPI) Query(ctx context.Context, query string, ts time.Time) (model.Value, error) {
-	u := h.client.url(epQuery, nil)
-	q := u.Query()
-
-	q.Set("query", query)
-	q.Set("time", ts.Format(time.RFC3339Nano))
-
-	u.RawQuery = q.Encode()
-
-	req, _ := http.NewRequest("GET", u.String(), nil)
-
-	_, body, err := h.client.do(ctx, req)
-	if err != nil {
-		return nil, err
-	}
-
-	var qres queryResult
-	err = json.Unmarshal(body, &qres)
-
-	return model.Value(qres.v), err
-}
-
-func (h *httpQueryAPI) QueryRange(ctx context.Context, query string, r Range) (model.Value, error) {
-	u := h.client.url(epQueryRange, nil)
-	q := u.Query()
-
-	var (
-		start = r.Start.Format(time.RFC3339Nano)
-		end   = r.End.Format(time.RFC3339Nano)
-		step  = strconv.FormatFloat(r.Step.Seconds(), 'f', 3, 64)
-	)
-
-	q.Set("query", query)
-	q.Set("start", start)
-	q.Set("end", end)
-	q.Set("step", step)
-
-	u.RawQuery = q.Encode()
-
-	req, _ := http.NewRequest("GET", u.String(), nil)
-
-	_, body, err := h.client.do(ctx, req)
-	if err != nil {
-		return nil, err
-	}
-
-	var qres queryResult
-	err = json.Unmarshal(body, &qres)
-
-	return model.Value(qres.v), err
-}
diff --git a/vendor/github.com/prometheus/client_golang/api/prometheus/api_test.go b/vendor/github.com/prometheus/client_golang/api/prometheus/api_test.go
deleted file mode 100644
index ca084a04e16938b2eece84d2801d8e5a57fb0841..0000000000000000000000000000000000000000
--- a/vendor/github.com/prometheus/client_golang/api/prometheus/api_test.go
+++ /dev/null
@@ -1,453 +0,0 @@
-// Copyright 2015 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 (
-	"encoding/json"
-	"fmt"
-	"net/http"
-	"net/url"
-	"reflect"
-	"testing"
-	"time"
-
-	"github.com/prometheus/common/model"
-	"golang.org/x/net/context"
-)
-
-func TestConfig(t *testing.T) {
-	c := Config{}
-	if c.transport() != DefaultTransport {
-		t.Fatalf("expected default transport for nil Transport field")
-	}
-}
-
-func TestClientURL(t *testing.T) {
-	tests := []struct {
-		address  string
-		endpoint string
-		args     map[string]string
-		expected string
-	}{
-		{
-			address:  "http://localhost:9090",
-			endpoint: "/test",
-			expected: "http://localhost:9090/test",
-		},
-		{
-			address:  "http://localhost",
-			endpoint: "/test",
-			expected: "http://localhost/test",
-		},
-		{
-			address:  "http://localhost:9090",
-			endpoint: "test",
-			expected: "http://localhost:9090/test",
-		},
-		{
-			address:  "http://localhost:9090/prefix",
-			endpoint: "/test",
-			expected: "http://localhost:9090/prefix/test",
-		},
-		{
-			address:  "https://localhost:9090/",
-			endpoint: "/test/",
-			expected: "https://localhost:9090/test",
-		},
-		{
-			address:  "http://localhost:9090",
-			endpoint: "/test/:param",
-			args: map[string]string{
-				"param": "content",
-			},
-			expected: "http://localhost:9090/test/content",
-		},
-		{
-			address:  "http://localhost:9090",
-			endpoint: "/test/:param/more/:param",
-			args: map[string]string{
-				"param": "content",
-			},
-			expected: "http://localhost:9090/test/content/more/content",
-		},
-		{
-			address:  "http://localhost:9090",
-			endpoint: "/test/:param/more/:foo",
-			args: map[string]string{
-				"param": "content",
-				"foo":   "bar",
-			},
-			expected: "http://localhost:9090/test/content/more/bar",
-		},
-		{
-			address:  "http://localhost:9090",
-			endpoint: "/test/:param",
-			args: map[string]string{
-				"nonexistant": "content",
-			},
-			expected: "http://localhost:9090/test/:param",
-		},
-	}
-
-	for _, test := range tests {
-		ep, err := url.Parse(test.address)
-		if err != nil {
-			t.Fatal(err)
-		}
-
-		hclient := &httpClient{
-			endpoint:  ep,
-			transport: DefaultTransport,
-		}
-
-		u := hclient.url(test.endpoint, test.args)
-		if u.String() != test.expected {
-			t.Errorf("unexpected result: got %s, want %s", u, test.expected)
-			continue
-		}
-
-		// The apiClient must return exactly the same result as the httpClient.
-		aclient := &apiClient{hclient}
-
-		u = aclient.url(test.endpoint, test.args)
-		if u.String() != test.expected {
-			t.Errorf("unexpected result: got %s, want %s", u, test.expected)
-		}
-	}
-}
-
-type testClient struct {
-	*testing.T
-
-	ch  chan apiClientTest
-	req *http.Request
-}
-
-type apiClientTest struct {
-	code     int
-	response interface{}
-	expected string
-	err      *Error
-}
-
-func (c *testClient) url(ep string, args map[string]string) *url.URL {
-	return nil
-}
-
-func (c *testClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
-	if ctx == nil {
-		c.Fatalf("context was not passed down")
-	}
-	if req != c.req {
-		c.Fatalf("request was not passed down")
-	}
-
-	test := <-c.ch
-
-	var b []byte
-	var err error
-
-	switch v := test.response.(type) {
-	case string:
-		b = []byte(v)
-	default:
-		b, err = json.Marshal(v)
-		if err != nil {
-			c.Fatal(err)
-		}
-	}
-
-	resp := &http.Response{
-		StatusCode: test.code,
-	}
-
-	return resp, b, nil
-}
-
-func TestAPIClientDo(t *testing.T) {
-	tests := []apiClientTest{
-		{
-			response: &apiResponse{
-				Status:    "error",
-				Data:      json.RawMessage(`null`),
-				ErrorType: ErrBadData,
-				Error:     "failed",
-			},
-			err: &Error{
-				Type: ErrBadData,
-				Msg:  "failed",
-			},
-			code:     statusAPIError,
-			expected: `null`,
-		},
-		{
-			response: &apiResponse{
-				Status:    "error",
-				Data:      json.RawMessage(`"test"`),
-				ErrorType: ErrTimeout,
-				Error:     "timed out",
-			},
-			err: &Error{
-				Type: ErrTimeout,
-				Msg:  "timed out",
-			},
-			code:     statusAPIError,
-			expected: `test`,
-		},
-		{
-			response: "bad json",
-			err: &Error{
-				Type: ErrBadResponse,
-				Msg:  "bad response code 400",
-			},
-			code: http.StatusBadRequest,
-		},
-		{
-			response: "bad json",
-			err: &Error{
-				Type: ErrBadResponse,
-				Msg:  "invalid character 'b' looking for beginning of value",
-			},
-			code: statusAPIError,
-		},
-		{
-			response: &apiResponse{
-				Status: "success",
-				Data:   json.RawMessage(`"test"`),
-			},
-			err: &Error{
-				Type: ErrBadResponse,
-				Msg:  "inconsistent body for response code",
-			},
-			code: statusAPIError,
-		},
-		{
-			response: &apiResponse{
-				Status:    "success",
-				Data:      json.RawMessage(`"test"`),
-				ErrorType: ErrTimeout,
-				Error:     "timed out",
-			},
-			err: &Error{
-				Type: ErrBadResponse,
-				Msg:  "inconsistent body for response code",
-			},
-			code: statusAPIError,
-		},
-		{
-			response: &apiResponse{
-				Status:    "error",
-				Data:      json.RawMessage(`"test"`),
-				ErrorType: ErrTimeout,
-				Error:     "timed out",
-			},
-			err: &Error{
-				Type: ErrBadResponse,
-				Msg:  "inconsistent body for response code",
-			},
-			code: http.StatusOK,
-		},
-	}
-
-	tc := &testClient{
-		T:   t,
-		ch:  make(chan apiClientTest, 1),
-		req: &http.Request{},
-	}
-	client := &apiClient{tc}
-
-	for _, test := range tests {
-
-		tc.ch <- test
-
-		_, body, err := client.do(context.Background(), tc.req)
-
-		if test.err != nil {
-			if err == nil {
-				t.Errorf("expected error %q but got none", test.err)
-				continue
-			}
-			if test.err.Error() != err.Error() {
-				t.Errorf("unexpected error: want %q, got %q", test.err, err)
-			}
-			continue
-		}
-		if err != nil {
-			t.Errorf("unexpeceted error %s", err)
-			continue
-		}
-
-		want, got := test.expected, string(body)
-		if want != got {
-			t.Errorf("unexpected body: want %q, got %q", want, got)
-		}
-	}
-}
-
-type apiTestClient struct {
-	*testing.T
-	curTest apiTest
-}
-
-type apiTest struct {
-	do    func() (interface{}, error)
-	inErr error
-	inRes interface{}
-
-	reqPath   string
-	reqParam  url.Values
-	reqMethod string
-	res       interface{}
-	err       error
-}
-
-func (c *apiTestClient) url(ep string, args map[string]string) *url.URL {
-	u := &url.URL{
-		Host: "test:9090",
-		Path: apiPrefix + ep,
-	}
-	return u
-}
-
-func (c *apiTestClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
-
-	test := c.curTest
-
-	if req.URL.Path != test.reqPath {
-		c.Errorf("unexpected request path: want %s, got %s", test.reqPath, req.URL.Path)
-	}
-	if req.Method != test.reqMethod {
-		c.Errorf("unexpected request method: want %s, got %s", test.reqMethod, req.Method)
-	}
-
-	b, err := json.Marshal(test.inRes)
-	if err != nil {
-		c.Fatal(err)
-	}
-
-	resp := &http.Response{}
-	if test.inErr != nil {
-		resp.StatusCode = statusAPIError
-	} else {
-		resp.StatusCode = http.StatusOK
-	}
-
-	return resp, b, test.inErr
-}
-
-func TestAPIs(t *testing.T) {
-
-	testTime := time.Now()
-
-	client := &apiTestClient{T: t}
-
-	queryAPI := &httpQueryAPI{
-		client: client,
-	}
-
-	doQuery := func(q string, ts time.Time) func() (interface{}, error) {
-		return func() (interface{}, error) {
-			return queryAPI.Query(context.Background(), q, ts)
-		}
-	}
-
-	doQueryRange := func(q string, rng Range) func() (interface{}, error) {
-		return func() (interface{}, error) {
-			return queryAPI.QueryRange(context.Background(), q, rng)
-		}
-	}
-
-	queryTests := []apiTest{
-		{
-			do: doQuery("2", testTime),
-			inRes: &queryResult{
-				Type: model.ValScalar,
-				Result: &model.Scalar{
-					Value:     2,
-					Timestamp: model.TimeFromUnix(testTime.Unix()),
-				},
-			},
-
-			reqMethod: "GET",
-			reqPath:   "/api/v1/query",
-			reqParam: url.Values{
-				"query": []string{"2"},
-				"time":  []string{testTime.Format(time.RFC3339Nano)},
-			},
-			res: &model.Scalar{
-				Value:     2,
-				Timestamp: model.TimeFromUnix(testTime.Unix()),
-			},
-		},
-		{
-			do:    doQuery("2", testTime),
-			inErr: fmt.Errorf("some error"),
-
-			reqMethod: "GET",
-			reqPath:   "/api/v1/query",
-			reqParam: url.Values{
-				"query": []string{"2"},
-				"time":  []string{testTime.Format(time.RFC3339Nano)},
-			},
-			err: fmt.Errorf("some error"),
-		},
-
-		{
-			do: doQueryRange("2", Range{
-				Start: testTime.Add(-time.Minute),
-				End:   testTime,
-				Step:  time.Minute,
-			}),
-			inErr: fmt.Errorf("some error"),
-
-			reqMethod: "GET",
-			reqPath:   "/api/v1/query_range",
-			reqParam: url.Values{
-				"query": []string{"2"},
-				"start": []string{testTime.Add(-time.Minute).Format(time.RFC3339Nano)},
-				"end":   []string{testTime.Format(time.RFC3339Nano)},
-				"step":  []string{time.Minute.String()},
-			},
-			err: fmt.Errorf("some error"),
-		},
-	}
-
-	var tests []apiTest
-	tests = append(tests, queryTests...)
-
-	for _, test := range tests {
-		client.curTest = test
-
-		res, err := test.do()
-
-		if test.err != nil {
-			if err == nil {
-				t.Errorf("expected error %q but got none", test.err)
-				continue
-			}
-			if err.Error() != test.err.Error() {
-				t.Errorf("unexpected error: want %s, got %s", test.err, err)
-			}
-			continue
-		}
-		if err != nil {
-			t.Errorf("unexpected error: %s", err)
-			continue
-		}
-
-		if !reflect.DeepEqual(res, test.res) {
-			t.Errorf("unexpected result: want %v, got %v", test.res, res)
-		}
-	}
-}
diff --git a/vendor/github.com/prometheus/client_golang/api/prometheus/v1/api.go b/vendor/github.com/prometheus/client_golang/api/prometheus/v1/api.go
new file mode 100644
index 0000000000000000000000000000000000000000..642418890cad404b0074f1377c0843bde6c77254
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/api/prometheus/v1/api.go
@@ -0,0 +1,519 @@
+// 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.7
+
+// Package v1 provides bindings to the Prometheus HTTP API v1:
+// http://prometheus.io/docs/querying/api/
+package v1
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strconv"
+	"time"
+
+	"github.com/prometheus/client_golang/api"
+	"github.com/prometheus/common/model"
+)
+
+const (
+	statusAPIError = 422
+
+	apiPrefix = "/api/v1"
+
+	epAlertManagers   = apiPrefix + "/alertmanagers"
+	epQuery           = apiPrefix + "/query"
+	epQueryRange      = apiPrefix + "/query_range"
+	epLabelValues     = apiPrefix + "/label/:name/values"
+	epSeries          = apiPrefix + "/series"
+	epTargets         = apiPrefix + "/targets"
+	epSnapshot        = apiPrefix + "/admin/tsdb/snapshot"
+	epDeleteSeries    = apiPrefix + "/admin/tsdb/delete_series"
+	epCleanTombstones = apiPrefix + "/admin/tsdb/clean_tombstones"
+	epConfig          = apiPrefix + "/status/config"
+	epFlags           = apiPrefix + "/status/flags"
+)
+
+// ErrorType models the different API error types.
+type ErrorType string
+
+// HealthStatus models the health status of a scrape target.
+type HealthStatus string
+
+const (
+	// Possible values for ErrorType.
+	ErrBadData     ErrorType = "bad_data"
+	ErrTimeout     ErrorType = "timeout"
+	ErrCanceled    ErrorType = "canceled"
+	ErrExec        ErrorType = "execution"
+	ErrBadResponse ErrorType = "bad_response"
+	ErrServer      ErrorType = "server_error"
+	ErrClient      ErrorType = "client_error"
+
+	// Possible values for HealthStatus.
+	HealthGood    HealthStatus = "up"
+	HealthUnknown HealthStatus = "unknown"
+	HealthBad     HealthStatus = "down"
+)
+
+// Error is an error returned by the API.
+type Error struct {
+	Type   ErrorType
+	Msg    string
+	Detail string
+}
+
+func (e *Error) Error() string {
+	return fmt.Sprintf("%s: %s", e.Type, e.Msg)
+}
+
+// Range represents a sliced time range.
+type Range struct {
+	// The boundaries of the time range.
+	Start, End time.Time
+	// The maximum time between two slices within the boundaries.
+	Step time.Duration
+}
+
+// API provides bindings for Prometheus's v1 API.
+type API interface {
+	// AlertManagers returns an overview of the current state of the Prometheus alert manager discovery.
+	AlertManagers(ctx context.Context) (AlertManagersResult, error)
+	// CleanTombstones removes the deleted data from disk and cleans up the existing tombstones.
+	CleanTombstones(ctx context.Context) error
+	// Config returns the current Prometheus configuration.
+	Config(ctx context.Context) (ConfigResult, error)
+	// DeleteSeries deletes data for a selection of series in a time range.
+	DeleteSeries(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) error
+	// Flags returns the flag values that Prometheus was launched with.
+	Flags(ctx context.Context) (FlagsResult, error)
+	// LabelValues performs a query for the values of the given label.
+	LabelValues(ctx context.Context, label string) (model.LabelValues, error)
+	// Query performs a query for the given time.
+	Query(ctx context.Context, query string, ts time.Time) (model.Value, error)
+	// QueryRange performs a query for the given range.
+	QueryRange(ctx context.Context, query string, r Range) (model.Value, error)
+	// Series finds series by label matchers.
+	Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, error)
+	// Snapshot creates a snapshot of all current data into snapshots/<datetime>-<rand>
+	// under the TSDB's data directory and returns the directory as response.
+	Snapshot(ctx context.Context, skipHead bool) (SnapshotResult, error)
+	// Targets returns an overview of the current state of the Prometheus target discovery.
+	Targets(ctx context.Context) (TargetsResult, error)
+}
+
+// AlertManagersResult contains the result from querying the alertmanagers endpoint.
+type AlertManagersResult struct {
+	Active  []AlertManager `json:"activeAlertManagers"`
+	Dropped []AlertManager `json:"droppedAlertManagers"`
+}
+
+// AlertManager models a configured Alert Manager.
+type AlertManager struct {
+	URL string `json:"url"`
+}
+
+// ConfigResult contains the result from querying the config endpoint.
+type ConfigResult struct {
+	YAML string `json:"yaml"`
+}
+
+// FlagsResult contains the result from querying the flag endpoint.
+type FlagsResult map[string]string
+
+// SnapshotResult contains the result from querying the snapshot endpoint.
+type SnapshotResult struct {
+	Name string `json:"name"`
+}
+
+// TargetsResult contains the result from querying the targets endpoint.
+type TargetsResult struct {
+	Active  []ActiveTarget  `json:"activeTargets"`
+	Dropped []DroppedTarget `json:"droppedTargets"`
+}
+
+// ActiveTarget models an active Prometheus scrape target.
+type ActiveTarget struct {
+	DiscoveredLabels model.LabelSet `json:"discoveredLabels"`
+	Labels           model.LabelSet `json:"labels"`
+	ScrapeURL        string         `json:"scrapeUrl"`
+	LastError        string         `json:"lastError"`
+	LastScrape       time.Time      `json:"lastScrape"`
+	Health           HealthStatus   `json:"health"`
+}
+
+// DroppedTarget models a dropped Prometheus scrape target.
+type DroppedTarget struct {
+	DiscoveredLabels model.LabelSet `json:"discoveredLabels"`
+}
+
+// queryResult contains result data for a query.
+type queryResult struct {
+	Type   model.ValueType `json:"resultType"`
+	Result interface{}     `json:"result"`
+
+	// The decoded value.
+	v model.Value
+}
+
+func (qr *queryResult) UnmarshalJSON(b []byte) error {
+	v := struct {
+		Type   model.ValueType `json:"resultType"`
+		Result json.RawMessage `json:"result"`
+	}{}
+
+	err := json.Unmarshal(b, &v)
+	if err != nil {
+		return err
+	}
+
+	switch v.Type {
+	case model.ValScalar:
+		var sv model.Scalar
+		err = json.Unmarshal(v.Result, &sv)
+		qr.v = &sv
+
+	case model.ValVector:
+		var vv model.Vector
+		err = json.Unmarshal(v.Result, &vv)
+		qr.v = vv
+
+	case model.ValMatrix:
+		var mv model.Matrix
+		err = json.Unmarshal(v.Result, &mv)
+		qr.v = mv
+
+	default:
+		err = fmt.Errorf("unexpected value type %q", v.Type)
+	}
+	return err
+}
+
+// NewAPI returns a new API for the client.
+//
+// It is safe to use the returned API from multiple goroutines.
+func NewAPI(c api.Client) API {
+	return &httpAPI{client: apiClient{c}}
+}
+
+type httpAPI struct {
+	client api.Client
+}
+
+func (h *httpAPI) AlertManagers(ctx context.Context) (AlertManagersResult, error) {
+	u := h.client.URL(epAlertManagers, nil)
+
+	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
+	if err != nil {
+		return AlertManagersResult{}, err
+	}
+
+	_, body, err := h.client.Do(ctx, req)
+	if err != nil {
+		return AlertManagersResult{}, err
+	}
+
+	var res AlertManagersResult
+	err = json.Unmarshal(body, &res)
+	return res, err
+}
+
+func (h *httpAPI) CleanTombstones(ctx context.Context) error {
+	u := h.client.URL(epCleanTombstones, nil)
+
+	req, err := http.NewRequest(http.MethodPost, u.String(), nil)
+	if err != nil {
+		return err
+	}
+
+	_, _, err = h.client.Do(ctx, req)
+	return err
+}
+
+func (h *httpAPI) Config(ctx context.Context) (ConfigResult, error) {
+	u := h.client.URL(epConfig, nil)
+
+	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
+	if err != nil {
+		return ConfigResult{}, err
+	}
+
+	_, body, err := h.client.Do(ctx, req)
+	if err != nil {
+		return ConfigResult{}, err
+	}
+
+	var res ConfigResult
+	err = json.Unmarshal(body, &res)
+	return res, err
+}
+
+func (h *httpAPI) DeleteSeries(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) error {
+	u := h.client.URL(epDeleteSeries, nil)
+	q := u.Query()
+
+	for _, m := range matches {
+		q.Add("match[]", m)
+	}
+
+	q.Set("start", startTime.Format(time.RFC3339Nano))
+	q.Set("end", endTime.Format(time.RFC3339Nano))
+
+	u.RawQuery = q.Encode()
+
+	req, err := http.NewRequest(http.MethodPost, u.String(), nil)
+	if err != nil {
+		return err
+	}
+
+	_, _, err = h.client.Do(ctx, req)
+	return err
+}
+
+func (h *httpAPI) Flags(ctx context.Context) (FlagsResult, error) {
+	u := h.client.URL(epFlags, nil)
+
+	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
+	if err != nil {
+		return FlagsResult{}, err
+	}
+
+	_, body, err := h.client.Do(ctx, req)
+	if err != nil {
+		return FlagsResult{}, err
+	}
+
+	var res FlagsResult
+	err = json.Unmarshal(body, &res)
+	return res, err
+}
+
+func (h *httpAPI) LabelValues(ctx context.Context, label string) (model.LabelValues, error) {
+	u := h.client.URL(epLabelValues, map[string]string{"name": label})
+	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
+	if err != nil {
+		return nil, err
+	}
+	_, body, err := h.client.Do(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	var labelValues model.LabelValues
+	err = json.Unmarshal(body, &labelValues)
+	return labelValues, err
+}
+
+func (h *httpAPI) Query(ctx context.Context, query string, ts time.Time) (model.Value, error) {
+	u := h.client.URL(epQuery, nil)
+	q := u.Query()
+
+	q.Set("query", query)
+	if !ts.IsZero() {
+		q.Set("time", ts.Format(time.RFC3339Nano))
+	}
+
+	u.RawQuery = q.Encode()
+
+	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
+	if err != nil {
+		return nil, err
+	}
+
+	_, body, err := h.client.Do(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+
+	var qres queryResult
+	err = json.Unmarshal(body, &qres)
+
+	return model.Value(qres.v), err
+}
+
+func (h *httpAPI) QueryRange(ctx context.Context, query string, r Range) (model.Value, error) {
+	u := h.client.URL(epQueryRange, nil)
+	q := u.Query()
+
+	var (
+		start = r.Start.Format(time.RFC3339Nano)
+		end   = r.End.Format(time.RFC3339Nano)
+		step  = strconv.FormatFloat(r.Step.Seconds(), 'f', 3, 64)
+	)
+
+	q.Set("query", query)
+	q.Set("start", start)
+	q.Set("end", end)
+	q.Set("step", step)
+
+	u.RawQuery = q.Encode()
+
+	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
+	if err != nil {
+		return nil, err
+	}
+
+	_, body, err := h.client.Do(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+
+	var qres queryResult
+	err = json.Unmarshal(body, &qres)
+
+	return model.Value(qres.v), err
+}
+
+func (h *httpAPI) Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, error) {
+	u := h.client.URL(epSeries, nil)
+	q := u.Query()
+
+	for _, m := range matches {
+		q.Add("match[]", m)
+	}
+
+	q.Set("start", startTime.Format(time.RFC3339Nano))
+	q.Set("end", endTime.Format(time.RFC3339Nano))
+
+	u.RawQuery = q.Encode()
+
+	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
+	if err != nil {
+		return nil, err
+	}
+
+	_, body, err := h.client.Do(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+
+	var mset []model.LabelSet
+	err = json.Unmarshal(body, &mset)
+	return mset, err
+}
+
+func (h *httpAPI) Snapshot(ctx context.Context, skipHead bool) (SnapshotResult, error) {
+	u := h.client.URL(epSnapshot, nil)
+	q := u.Query()
+
+	q.Set("skip_head", strconv.FormatBool(skipHead))
+
+	u.RawQuery = q.Encode()
+
+	req, err := http.NewRequest(http.MethodPost, u.String(), nil)
+	if err != nil {
+		return SnapshotResult{}, err
+	}
+
+	_, body, err := h.client.Do(ctx, req)
+	if err != nil {
+		return SnapshotResult{}, err
+	}
+
+	var res SnapshotResult
+	err = json.Unmarshal(body, &res)
+	return res, err
+}
+
+func (h *httpAPI) Targets(ctx context.Context) (TargetsResult, error) {
+	u := h.client.URL(epTargets, nil)
+
+	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
+	if err != nil {
+		return TargetsResult{}, err
+	}
+
+	_, body, err := h.client.Do(ctx, req)
+	if err != nil {
+		return TargetsResult{}, err
+	}
+
+	var res TargetsResult
+	err = json.Unmarshal(body, &res)
+	return res, err
+}
+
+// apiClient wraps a regular client and processes successful API responses.
+// Successful also includes responses that errored at the API level.
+type apiClient struct {
+	api.Client
+}
+
+type apiResponse struct {
+	Status    string          `json:"status"`
+	Data      json.RawMessage `json:"data"`
+	ErrorType ErrorType       `json:"errorType"`
+	Error     string          `json:"error"`
+}
+
+func apiError(code int) bool {
+	// These are the codes that Prometheus sends when it returns an error.
+	return code == statusAPIError || code == http.StatusBadRequest
+}
+
+func errorTypeAndMsgFor(resp *http.Response) (ErrorType, string) {
+	switch resp.StatusCode / 100 {
+	case 4:
+		return ErrClient, fmt.Sprintf("client error: %d", resp.StatusCode)
+	case 5:
+		return ErrServer, fmt.Sprintf("server error: %d", resp.StatusCode)
+	}
+	return ErrBadResponse, fmt.Sprintf("bad response code %d", resp.StatusCode)
+}
+
+func (c apiClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
+	resp, body, err := c.Client.Do(ctx, req)
+	if err != nil {
+		return resp, body, err
+	}
+
+	code := resp.StatusCode
+
+	if code/100 != 2 && !apiError(code) {
+		errorType, errorMsg := errorTypeAndMsgFor(resp)
+		return resp, body, &Error{
+			Type:   errorType,
+			Msg:    errorMsg,
+			Detail: string(body),
+		}
+	}
+
+	var result apiResponse
+
+	if http.StatusNoContent != code {
+		if err = json.Unmarshal(body, &result); err != nil {
+			return resp, body, &Error{
+				Type: ErrBadResponse,
+				Msg:  err.Error(),
+			}
+		}
+	}
+
+	if apiError(code) != (result.Status == "error") {
+		err = &Error{
+			Type: ErrBadResponse,
+			Msg:  "inconsistent body for response code",
+		}
+	}
+
+	if apiError(code) && result.Status == "error" {
+		err = &Error{
+			Type: result.ErrorType,
+			Msg:  result.Error,
+		}
+	}
+
+	return resp, []byte(result.Data), err
+}
diff --git a/vendor/github.com/prometheus/client_golang/api/prometheus/v1/api_test.go b/vendor/github.com/prometheus/client_golang/api/prometheus/v1/api_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..8492a5c38fce86d34eea92e71ab612cdc4e4dbf9
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/api/prometheus/v1/api_test.go
@@ -0,0 +1,768 @@
+// 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.7
+
+package v1
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+	"net/url"
+	"reflect"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/prometheus/common/model"
+)
+
+type apiTest struct {
+	do           func() (interface{}, error)
+	inErr        error
+	inStatusCode int
+	inRes        interface{}
+
+	reqPath   string
+	reqParam  url.Values
+	reqMethod string
+	res       interface{}
+	err       error
+}
+
+type apiTestClient struct {
+	*testing.T
+	curTest apiTest
+}
+
+func (c *apiTestClient) URL(ep string, args map[string]string) *url.URL {
+	path := ep
+	for k, v := range args {
+		path = strings.Replace(path, ":"+k, v, -1)
+	}
+	u := &url.URL{
+		Host: "test:9090",
+		Path: path,
+	}
+	return u
+}
+
+func (c *apiTestClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
+
+	test := c.curTest
+
+	if req.URL.Path != test.reqPath {
+		c.Errorf("unexpected request path: want %s, got %s", test.reqPath, req.URL.Path)
+	}
+	if req.Method != test.reqMethod {
+		c.Errorf("unexpected request method: want %s, got %s", test.reqMethod, req.Method)
+	}
+
+	b, err := json.Marshal(test.inRes)
+	if err != nil {
+		c.Fatal(err)
+	}
+
+	resp := &http.Response{}
+	if test.inStatusCode != 0 {
+		resp.StatusCode = test.inStatusCode
+	} else if test.inErr != nil {
+		resp.StatusCode = statusAPIError
+	} else {
+		resp.StatusCode = http.StatusOK
+	}
+
+	return resp, b, test.inErr
+}
+
+func TestAPIs(t *testing.T) {
+
+	testTime := time.Now()
+
+	client := &apiTestClient{T: t}
+
+	promAPI := &httpAPI{
+		client: client,
+	}
+
+	doAlertManagers := func() func() (interface{}, error) {
+		return func() (interface{}, error) {
+			return promAPI.AlertManagers(context.Background())
+		}
+	}
+
+	doCleanTombstones := func() func() (interface{}, error) {
+		return func() (interface{}, error) {
+			return nil, promAPI.CleanTombstones(context.Background())
+		}
+	}
+
+	doConfig := func() func() (interface{}, error) {
+		return func() (interface{}, error) {
+			return promAPI.Config(context.Background())
+		}
+	}
+
+	doDeleteSeries := func(matcher string, startTime time.Time, endTime time.Time) func() (interface{}, error) {
+		return func() (interface{}, error) {
+			return nil, promAPI.DeleteSeries(context.Background(), []string{matcher}, startTime, endTime)
+		}
+	}
+
+	doFlags := func() func() (interface{}, error) {
+		return func() (interface{}, error) {
+			return promAPI.Flags(context.Background())
+		}
+	}
+
+	doLabelValues := func(label string) func() (interface{}, error) {
+		return func() (interface{}, error) {
+			return promAPI.LabelValues(context.Background(), label)
+		}
+	}
+
+	doQuery := func(q string, ts time.Time) func() (interface{}, error) {
+		return func() (interface{}, error) {
+			return promAPI.Query(context.Background(), q, ts)
+		}
+	}
+
+	doQueryRange := func(q string, rng Range) func() (interface{}, error) {
+		return func() (interface{}, error) {
+			return promAPI.QueryRange(context.Background(), q, rng)
+		}
+	}
+
+	doSeries := func(matcher string, startTime time.Time, endTime time.Time) func() (interface{}, error) {
+		return func() (interface{}, error) {
+			return promAPI.Series(context.Background(), []string{matcher}, startTime, endTime)
+		}
+	}
+
+	doSnapshot := func(skipHead bool) func() (interface{}, error) {
+		return func() (interface{}, error) {
+			return promAPI.Snapshot(context.Background(), skipHead)
+		}
+	}
+
+	doTargets := func() func() (interface{}, error) {
+		return func() (interface{}, error) {
+			return promAPI.Targets(context.Background())
+		}
+	}
+
+	queryTests := []apiTest{
+		{
+			do: doQuery("2", testTime),
+			inRes: &queryResult{
+				Type: model.ValScalar,
+				Result: &model.Scalar{
+					Value:     2,
+					Timestamp: model.TimeFromUnix(testTime.Unix()),
+				},
+			},
+
+			reqMethod: "GET",
+			reqPath:   "/api/v1/query",
+			reqParam: url.Values{
+				"query": []string{"2"},
+				"time":  []string{testTime.Format(time.RFC3339Nano)},
+			},
+			res: &model.Scalar{
+				Value:     2,
+				Timestamp: model.TimeFromUnix(testTime.Unix()),
+			},
+		},
+		{
+			do:    doQuery("2", testTime),
+			inErr: fmt.Errorf("some error"),
+
+			reqMethod: "GET",
+			reqPath:   "/api/v1/query",
+			reqParam: url.Values{
+				"query": []string{"2"},
+				"time":  []string{testTime.Format(time.RFC3339Nano)},
+			},
+			err: fmt.Errorf("some error"),
+		},
+		{
+			do:           doQuery("2", testTime),
+			inRes:        "some body",
+			inStatusCode: 500,
+			inErr: &Error{
+				Type:   ErrServer,
+				Msg:    "server error: 500",
+				Detail: "some body",
+			},
+
+			reqMethod: "GET",
+			reqPath:   "/api/v1/query",
+			reqParam: url.Values{
+				"query": []string{"2"},
+				"time":  []string{testTime.Format(time.RFC3339Nano)},
+			},
+			err: errors.New("server_error: server error: 500"),
+		},
+		{
+			do:           doQuery("2", testTime),
+			inRes:        "some body",
+			inStatusCode: 404,
+			inErr: &Error{
+				Type:   ErrClient,
+				Msg:    "client error: 404",
+				Detail: "some body",
+			},
+
+			reqMethod: "GET",
+			reqPath:   "/api/v1/query",
+			reqParam: url.Values{
+				"query": []string{"2"},
+				"time":  []string{testTime.Format(time.RFC3339Nano)},
+			},
+			err: errors.New("client_error: client error: 404"),
+		},
+
+		{
+			do: doQueryRange("2", Range{
+				Start: testTime.Add(-time.Minute),
+				End:   testTime,
+				Step:  time.Minute,
+			}),
+			inErr: fmt.Errorf("some error"),
+
+			reqMethod: "GET",
+			reqPath:   "/api/v1/query_range",
+			reqParam: url.Values{
+				"query": []string{"2"},
+				"start": []string{testTime.Add(-time.Minute).Format(time.RFC3339Nano)},
+				"end":   []string{testTime.Format(time.RFC3339Nano)},
+				"step":  []string{time.Minute.String()},
+			},
+			err: fmt.Errorf("some error"),
+		},
+
+		{
+			do:        doLabelValues("mylabel"),
+			inRes:     []string{"val1", "val2"},
+			reqMethod: "GET",
+			reqPath:   "/api/v1/label/mylabel/values",
+			res:       model.LabelValues{"val1", "val2"},
+		},
+
+		{
+			do:        doLabelValues("mylabel"),
+			inErr:     fmt.Errorf("some error"),
+			reqMethod: "GET",
+			reqPath:   "/api/v1/label/mylabel/values",
+			err:       fmt.Errorf("some error"),
+		},
+
+		{
+			do: doSeries("up", testTime.Add(-time.Minute), testTime),
+			inRes: []map[string]string{
+				{
+					"__name__": "up",
+					"job":      "prometheus",
+					"instance": "localhost:9090"},
+			},
+			reqMethod: "GET",
+			reqPath:   "/api/v1/series",
+			reqParam: url.Values{
+				"match": []string{"up"},
+				"start": []string{testTime.Add(-time.Minute).Format(time.RFC3339Nano)},
+				"end":   []string{testTime.Format(time.RFC3339Nano)},
+			},
+			res: []model.LabelSet{
+				model.LabelSet{
+					"__name__": "up",
+					"job":      "prometheus",
+					"instance": "localhost:9090",
+				},
+			},
+		},
+
+		{
+			do:        doSeries("up", testTime.Add(-time.Minute), testTime),
+			inErr:     fmt.Errorf("some error"),
+			reqMethod: "GET",
+			reqPath:   "/api/v1/series",
+			reqParam: url.Values{
+				"match": []string{"up"},
+				"start": []string{testTime.Add(-time.Minute).Format(time.RFC3339Nano)},
+				"end":   []string{testTime.Format(time.RFC3339Nano)},
+			},
+			err: fmt.Errorf("some error"),
+		},
+
+		{
+			do: doSnapshot(true),
+			inRes: map[string]string{
+				"name": "20171210T211224Z-2be650b6d019eb54",
+			},
+			reqMethod: "POST",
+			reqPath:   "/api/v1/admin/tsdb/snapshot",
+			reqParam: url.Values{
+				"skip_head": []string{"true"},
+			},
+			res: SnapshotResult{
+				Name: "20171210T211224Z-2be650b6d019eb54",
+			},
+		},
+
+		{
+			do:        doSnapshot(true),
+			inErr:     fmt.Errorf("some error"),
+			reqMethod: "POST",
+			reqPath:   "/api/v1/admin/tsdb/snapshot",
+			err:       fmt.Errorf("some error"),
+		},
+
+		{
+			do:        doCleanTombstones(),
+			reqMethod: "POST",
+			reqPath:   "/api/v1/admin/tsdb/clean_tombstones",
+		},
+
+		{
+			do:        doCleanTombstones(),
+			inErr:     fmt.Errorf("some error"),
+			reqMethod: "POST",
+			reqPath:   "/api/v1/admin/tsdb/clean_tombstones",
+			err:       fmt.Errorf("some error"),
+		},
+
+		{
+			do: doDeleteSeries("up", testTime.Add(-time.Minute), testTime),
+			inRes: []map[string]string{
+				{
+					"__name__": "up",
+					"job":      "prometheus",
+					"instance": "localhost:9090"},
+			},
+			reqMethod: "POST",
+			reqPath:   "/api/v1/admin/tsdb/delete_series",
+			reqParam: url.Values{
+				"match": []string{"up"},
+				"start": []string{testTime.Add(-time.Minute).Format(time.RFC3339Nano)},
+				"end":   []string{testTime.Format(time.RFC3339Nano)},
+			},
+		},
+
+		{
+			do:        doDeleteSeries("up", testTime.Add(-time.Minute), testTime),
+			inErr:     fmt.Errorf("some error"),
+			reqMethod: "POST",
+			reqPath:   "/api/v1/admin/tsdb/delete_series",
+			reqParam: url.Values{
+				"match": []string{"up"},
+				"start": []string{testTime.Add(-time.Minute).Format(time.RFC3339Nano)},
+				"end":   []string{testTime.Format(time.RFC3339Nano)},
+			},
+			err: fmt.Errorf("some error"),
+		},
+
+		{
+			do:        doConfig(),
+			reqMethod: "GET",
+			reqPath:   "/api/v1/status/config",
+			inRes: map[string]string{
+				"yaml": "<content of the loaded config file in YAML>",
+			},
+			res: ConfigResult{
+				YAML: "<content of the loaded config file in YAML>",
+			},
+		},
+
+		{
+			do:        doConfig(),
+			reqMethod: "GET",
+			reqPath:   "/api/v1/status/config",
+			inErr:     fmt.Errorf("some error"),
+			err:       fmt.Errorf("some error"),
+		},
+
+		{
+			do:        doFlags(),
+			reqMethod: "GET",
+			reqPath:   "/api/v1/status/flags",
+			inRes: map[string]string{
+				"alertmanager.notification-queue-capacity": "10000",
+				"alertmanager.timeout":                     "10s",
+				"log.level":                                "info",
+				"query.lookback-delta":                     "5m",
+				"query.max-concurrency":                    "20",
+			},
+			res: FlagsResult{
+				"alertmanager.notification-queue-capacity": "10000",
+				"alertmanager.timeout":                     "10s",
+				"log.level":                                "info",
+				"query.lookback-delta":                     "5m",
+				"query.max-concurrency":                    "20",
+			},
+		},
+
+		{
+			do:        doFlags(),
+			reqMethod: "GET",
+			reqPath:   "/api/v1/status/flags",
+			inErr:     fmt.Errorf("some error"),
+			err:       fmt.Errorf("some error"),
+		},
+
+		{
+			do:        doAlertManagers(),
+			reqMethod: "GET",
+			reqPath:   "/api/v1/alertmanagers",
+			inRes: map[string]interface{}{
+				"activeAlertManagers": []map[string]string{
+					{
+						"url": "http://127.0.0.1:9091/api/v1/alerts",
+					},
+				},
+				"droppedAlertManagers": []map[string]string{
+					{
+						"url": "http://127.0.0.1:9092/api/v1/alerts",
+					},
+				},
+			},
+			res: AlertManagersResult{
+				Active: []AlertManager{
+					{
+						URL: "http://127.0.0.1:9091/api/v1/alerts",
+					},
+				},
+				Dropped: []AlertManager{
+					{
+						URL: "http://127.0.0.1:9092/api/v1/alerts",
+					},
+				},
+			},
+		},
+
+		{
+			do:        doAlertManagers(),
+			reqMethod: "GET",
+			reqPath:   "/api/v1/alertmanagers",
+			inErr:     fmt.Errorf("some error"),
+			err:       fmt.Errorf("some error"),
+		},
+
+		{
+			do:        doTargets(),
+			reqMethod: "GET",
+			reqPath:   "/api/v1/targets",
+			inRes: map[string]interface{}{
+				"activeTargets": []map[string]interface{}{
+					{
+						"discoveredLabels": map[string]string{
+							"__address__":      "127.0.0.1:9090",
+							"__metrics_path__": "/metrics",
+							"__scheme__":       "http",
+							"job":              "prometheus",
+						},
+						"labels": map[string]string{
+							"instance": "127.0.0.1:9090",
+							"job":      "prometheus",
+						},
+						"scrapeUrl":  "http://127.0.0.1:9090",
+						"lastError":  "error while scraping target",
+						"lastScrape": testTime.UTC().Format(time.RFC3339Nano),
+						"health":     "up",
+					},
+				},
+				"droppedTargets": []map[string]interface{}{
+					{
+						"discoveredLabels": map[string]string{
+							"__address__":      "127.0.0.1:9100",
+							"__metrics_path__": "/metrics",
+							"__scheme__":       "http",
+							"job":              "node",
+						},
+					},
+				},
+			},
+			res: TargetsResult{
+				Active: []ActiveTarget{
+					{
+						DiscoveredLabels: model.LabelSet{
+							"__address__":      "127.0.0.1:9090",
+							"__metrics_path__": "/metrics",
+							"__scheme__":       "http",
+							"job":              "prometheus",
+						},
+						Labels: model.LabelSet{
+							"instance": "127.0.0.1:9090",
+							"job":      "prometheus",
+						},
+						ScrapeURL:  "http://127.0.0.1:9090",
+						LastError:  "error while scraping target",
+						LastScrape: testTime.UTC(),
+						Health:     HealthGood,
+					},
+				},
+				Dropped: []DroppedTarget{
+					{
+						DiscoveredLabels: model.LabelSet{
+							"__address__":      "127.0.0.1:9100",
+							"__metrics_path__": "/metrics",
+							"__scheme__":       "http",
+							"job":              "node",
+						},
+					},
+				},
+			},
+		},
+
+		{
+			do:        doTargets(),
+			reqMethod: "GET",
+			reqPath:   "/api/v1/targets",
+			inErr:     fmt.Errorf("some error"),
+			err:       fmt.Errorf("some error"),
+		},
+	}
+
+	var tests []apiTest
+	tests = append(tests, queryTests...)
+
+	for i, test := range tests {
+		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+			client.curTest = test
+
+			res, err := test.do()
+
+			if test.err != nil {
+				if err == nil {
+					t.Fatalf("expected error %q but got none", test.err)
+				}
+				if err.Error() != test.err.Error() {
+					t.Errorf("unexpected error: want %s, got %s", test.err, err)
+				}
+				if apiErr, ok := err.(*Error); ok {
+					if apiErr.Detail != test.inRes {
+						t.Errorf("%q should be %q", apiErr.Detail, test.inRes)
+					}
+				}
+				return
+			}
+			if err != nil {
+				t.Fatalf("unexpected error: %s", err)
+			}
+
+			if !reflect.DeepEqual(res, test.res) {
+				t.Errorf("unexpected result: want %v, got %v", test.res, res)
+			}
+		})
+	}
+}
+
+type testClient struct {
+	*testing.T
+
+	ch  chan apiClientTest
+	req *http.Request
+}
+
+type apiClientTest struct {
+	code         int
+	response     interface{}
+	expectedBody string
+	expectedErr  *Error
+}
+
+func (c *testClient) URL(ep string, args map[string]string) *url.URL {
+	return nil
+}
+
+func (c *testClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
+	if ctx == nil {
+		c.Fatalf("context was not passed down")
+	}
+	if req != c.req {
+		c.Fatalf("request was not passed down")
+	}
+
+	test := <-c.ch
+
+	var b []byte
+	var err error
+
+	switch v := test.response.(type) {
+	case string:
+		b = []byte(v)
+	default:
+		b, err = json.Marshal(v)
+		if err != nil {
+			c.Fatal(err)
+		}
+	}
+
+	resp := &http.Response{
+		StatusCode: test.code,
+	}
+
+	return resp, b, nil
+}
+
+func TestAPIClientDo(t *testing.T) {
+	tests := []apiClientTest{
+		{
+			code: statusAPIError,
+			response: &apiResponse{
+				Status:    "error",
+				Data:      json.RawMessage(`null`),
+				ErrorType: ErrBadData,
+				Error:     "failed",
+			},
+			expectedErr: &Error{
+				Type: ErrBadData,
+				Msg:  "failed",
+			},
+			expectedBody: `null`,
+		},
+		{
+			code: statusAPIError,
+			response: &apiResponse{
+				Status:    "error",
+				Data:      json.RawMessage(`"test"`),
+				ErrorType: ErrTimeout,
+				Error:     "timed out",
+			},
+			expectedErr: &Error{
+				Type: ErrTimeout,
+				Msg:  "timed out",
+			},
+			expectedBody: `test`,
+		},
+		{
+			code:     http.StatusInternalServerError,
+			response: "500 error details",
+			expectedErr: &Error{
+				Type:   ErrServer,
+				Msg:    "server error: 500",
+				Detail: "500 error details",
+			},
+		},
+		{
+			code:     http.StatusNotFound,
+			response: "404 error details",
+			expectedErr: &Error{
+				Type:   ErrClient,
+				Msg:    "client error: 404",
+				Detail: "404 error details",
+			},
+		},
+		{
+			code: http.StatusBadRequest,
+			response: &apiResponse{
+				Status:    "error",
+				Data:      json.RawMessage(`null`),
+				ErrorType: ErrBadData,
+				Error:     "end timestamp must not be before start time",
+			},
+			expectedErr: &Error{
+				Type: ErrBadData,
+				Msg:  "end timestamp must not be before start time",
+			},
+		},
+		{
+			code:     statusAPIError,
+			response: "bad json",
+			expectedErr: &Error{
+				Type: ErrBadResponse,
+				Msg:  "invalid character 'b' looking for beginning of value",
+			},
+		},
+		{
+			code: statusAPIError,
+			response: &apiResponse{
+				Status: "success",
+				Data:   json.RawMessage(`"test"`),
+			},
+			expectedErr: &Error{
+				Type: ErrBadResponse,
+				Msg:  "inconsistent body for response code",
+			},
+		},
+		{
+			code: statusAPIError,
+			response: &apiResponse{
+				Status:    "success",
+				Data:      json.RawMessage(`"test"`),
+				ErrorType: ErrTimeout,
+				Error:     "timed out",
+			},
+			expectedErr: &Error{
+				Type: ErrBadResponse,
+				Msg:  "inconsistent body for response code",
+			},
+		},
+		{
+			code: http.StatusOK,
+			response: &apiResponse{
+				Status:    "error",
+				Data:      json.RawMessage(`"test"`),
+				ErrorType: ErrTimeout,
+				Error:     "timed out",
+			},
+			expectedErr: &Error{
+				Type: ErrBadResponse,
+				Msg:  "inconsistent body for response code",
+			},
+		},
+	}
+
+	tc := &testClient{
+		T:   t,
+		ch:  make(chan apiClientTest, 1),
+		req: &http.Request{},
+	}
+	client := &apiClient{tc}
+
+	for i, test := range tests {
+		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+
+			tc.ch <- test
+
+			_, body, err := client.Do(context.Background(), tc.req)
+
+			if test.expectedErr != nil {
+				if err == nil {
+					t.Fatalf("expected error %q but got none", test.expectedErr)
+				}
+				if test.expectedErr.Error() != err.Error() {
+					t.Errorf("unexpected error: want %q, got %q", test.expectedErr, err)
+				}
+				if test.expectedErr.Detail != "" {
+					apiErr := err.(*Error)
+					if apiErr.Detail != test.expectedErr.Detail {
+						t.Errorf("unexpected error details: want %q, got %q", test.expectedErr.Detail, apiErr.Detail)
+					}
+				}
+				return
+			}
+			if err != nil {
+				t.Fatalf("unexpeceted error %s", err)
+			}
+
+			want, got := test.expectedBody, string(body)
+			if want != got {
+				t.Errorf("unexpected body: want %q, got %q", want, got)
+			}
+		})
+
+	}
+}
diff --git a/vendor/github.com/prometheus/client_golang/examples/random/main.go b/vendor/github.com/prometheus/client_golang/examples/random/main.go
index 5639571935428f166d129f49872ebcc0a09507e1..eef50d200d301b346917818013f403e160a86de3 100644
--- a/vendor/github.com/prometheus/client_golang/examples/random/main.go
+++ b/vendor/github.com/prometheus/client_golang/examples/random/main.go
@@ -18,19 +18,21 @@ package main
 
 import (
 	"flag"
+	"log"
 	"math"
 	"math/rand"
 	"net/http"
 	"time"
 
 	"github.com/prometheus/client_golang/prometheus"
+	"github.com/prometheus/client_golang/prometheus/promhttp"
 )
 
 var (
 	addr              = flag.String("listen-address", ":8080", "The address to listen on for HTTP requests.")
-	uniformDomain     = flag.Float64("uniform.domain", 200, "The domain for the uniform distribution.")
-	normDomain        = flag.Float64("normal.domain", 200, "The domain for the normal distribution.")
-	normMean          = flag.Float64("normal.mean", 10, "The mean for the normal distribution.")
+	uniformDomain     = flag.Float64("uniform.domain", 0.0002, "The domain for the uniform distribution.")
+	normDomain        = flag.Float64("normal.domain", 0.0002, "The domain for the normal distribution.")
+	normMean          = flag.Float64("normal.mean", 0.00001, "The mean for the normal distribution.")
 	oscillationPeriod = flag.Duration("oscillation-period", 10*time.Minute, "The duration of the rate oscillation period.")
 )
 
@@ -40,8 +42,9 @@ var (
 	// differentiated via a "service" label.
 	rpcDurations = prometheus.NewSummaryVec(
 		prometheus.SummaryOpts{
-			Name: "rpc_durations_microseconds",
-			Help: "RPC latency distributions.",
+			Name:       "rpc_durations_seconds",
+			Help:       "RPC latency distributions.",
+			Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
 		},
 		[]string{"service"},
 	)
@@ -50,7 +53,7 @@ var (
 	// normal distribution, with 20 buckets centered on the mean, each
 	// half-sigma wide.
 	rpcDurationsHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{
-		Name:    "rpc_durations_histogram_microseconds",
+		Name:    "rpc_durations_histogram_seconds",
 		Help:    "RPC latency distributions.",
 		Buckets: prometheus.LinearBuckets(*normMean-5**normDomain, .5**normDomain, 20),
 	})
@@ -91,13 +94,13 @@ func main() {
 
 	go func() {
 		for {
-			v := rand.ExpFloat64()
+			v := rand.ExpFloat64() / 1e6
 			rpcDurations.WithLabelValues("exponential").Observe(v)
 			time.Sleep(time.Duration(50*oscillationFactor()) * time.Millisecond)
 		}
 	}()
 
 	// Expose the registered metrics via HTTP.
-	http.Handle("/metrics", prometheus.Handler())
-	http.ListenAndServe(*addr, nil)
+	http.Handle("/metrics", promhttp.Handler())
+	log.Fatal(http.ListenAndServe(*addr, nil))
 }
diff --git a/vendor/github.com/prometheus/client_golang/examples/simple/main.go b/vendor/github.com/prometheus/client_golang/examples/simple/main.go
index 19620d2b3eb35304aa2328884f616ebf5902f6a0..1fc23249a294e95f86b6e32f92dca1efcc647562 100644
--- a/vendor/github.com/prometheus/client_golang/examples/simple/main.go
+++ b/vendor/github.com/prometheus/client_golang/examples/simple/main.go
@@ -16,15 +16,16 @@ package main
 
 import (
 	"flag"
+	"log"
 	"net/http"
 
-	"github.com/prometheus/client_golang/prometheus"
+	"github.com/prometheus/client_golang/prometheus/promhttp"
 )
 
 var addr = flag.String("listen-address", ":8080", "The address to listen on for HTTP requests.")
 
 func main() {
 	flag.Parse()
-	http.Handle("/metrics", prometheus.Handler())
-	http.ListenAndServe(*addr, nil)
+	http.Handle("/metrics", promhttp.Handler())
+	log.Fatal(http.ListenAndServe(*addr, nil))
 }
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/benchmark_test.go b/vendor/github.com/prometheus/client_golang/prometheus/benchmark_test.go
index a3d86698bfc750a0b5d7e79d4652f071d37f2709..4a05721dccd6f1e4c17ffcc1fa5902c21994855d 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/benchmark_test.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/benchmark_test.go
@@ -129,8 +129,9 @@ func BenchmarkGaugeNoLabels(b *testing.B) {
 func BenchmarkSummaryWithLabelValues(b *testing.B) {
 	m := NewSummaryVec(
 		SummaryOpts{
-			Name: "benchmark_summary",
-			Help: "A summary to benchmark it.",
+			Name:       "benchmark_summary",
+			Help:       "A summary to benchmark it.",
+			Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
 		},
 		[]string{"one", "two", "three"},
 	)
@@ -143,8 +144,9 @@ func BenchmarkSummaryWithLabelValues(b *testing.B) {
 
 func BenchmarkSummaryNoLabels(b *testing.B) {
 	m := NewSummary(SummaryOpts{
-		Name: "benchmark_summary",
-		Help: "A summary to benchmark it.",
+		Name:       "benchmark_summary",
+		Help:       "A summary to benchmark it.",
+		Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
 	},
 	)
 	b.ReportAllocs()
@@ -181,3 +183,17 @@ func BenchmarkHistogramNoLabels(b *testing.B) {
 		m.Observe(3.1415)
 	}
 }
+
+func BenchmarkParallelCounter(b *testing.B) {
+	c := NewCounter(CounterOpts{
+		Name: "benchmark_counter",
+		Help: "A Counter to benchmark it.",
+	})
+	b.ReportAllocs()
+	b.ResetTimer()
+	b.RunParallel(func(pb *testing.PB) {
+		for pb.Next() {
+			c.Inc()
+		}
+	})
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/collector.go b/vendor/github.com/prometheus/client_golang/prometheus/collector.go
index 623d3d83fefc849444c4efcb18c4db7a27f8affa..c0d70b2faf169973e3ea25b3c8da7ead42c29f19 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/collector.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/collector.go
@@ -29,27 +29,72 @@ 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.) 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.
+	// 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.
 	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. 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
+	// (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.
 	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/collector_test.go b/vendor/github.com/prometheus/client_golang/prometheus/collector_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..45eab3ea4a1d7ee2499a7edde3e5a3a9894b6f16
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/collector_test.go
@@ -0,0 +1,62 @@
+// 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 "testing"
+
+type collectorDescribedByCollect struct {
+	cnt Counter
+	gge Gauge
+}
+
+func (c collectorDescribedByCollect) Collect(ch chan<- Metric) {
+	ch <- c.cnt
+	ch <- c.gge
+}
+
+func (c collectorDescribedByCollect) Describe(ch chan<- *Desc) {
+	DescribeByCollect(c, ch)
+}
+
+func TestDescribeByCollect(t *testing.T) {
+
+	goodCollector := collectorDescribedByCollect{
+		cnt: NewCounter(CounterOpts{Name: "c1", Help: "help c1"}),
+		gge: NewGauge(GaugeOpts{Name: "g1", Help: "help g1"}),
+	}
+	collidingCollector := collectorDescribedByCollect{
+		cnt: NewCounter(CounterOpts{Name: "c2", Help: "help c2"}),
+		gge: NewGauge(GaugeOpts{Name: "g1", Help: "help g1"}),
+	}
+	inconsistentCollector := collectorDescribedByCollect{
+		cnt: NewCounter(CounterOpts{Name: "c3", Help: "help c3"}),
+		gge: NewGauge(GaugeOpts{Name: "c3", Help: "help inconsistent"}),
+	}
+
+	reg := NewPedanticRegistry()
+
+	if err := reg.Register(goodCollector); err != nil {
+		t.Error("registration failed:", err)
+	}
+	if err := reg.Register(collidingCollector); err == nil {
+		t.Error("registration unexpectedly succeeded")
+	}
+	if err := reg.Register(inconsistentCollector); err == nil {
+		t.Error("registration unexpectedly succeeded")
+	}
+
+	if _, err := reg.Gather(); err != nil {
+		t.Error("gathering failed:", err)
+	}
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/counter.go b/vendor/github.com/prometheus/client_golang/prometheus/counter.go
index ee37949ada1d6a2845c4894db8ecab463bb16105..d463e36d3e98d204d55648aeec64ce1c6fd0f7ba 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/counter.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/counter.go
@@ -15,6 +15,10 @@ 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
@@ -30,16 +34,8 @@ type Counter interface {
 	Metric
 	Collector
 
-	// 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 increments the counter by 1. Use Add to increment it by arbitrary
+	// non-negative values.
 	Inc()
 	// Add adds the given value to the counter. It panics if the value is <
 	// 0.
@@ -50,6 +46,14 @@ 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),
@@ -57,20 +61,58 @@ func NewCounter(opts CounterOpts) Counter {
 		nil,
 		opts.ConstLabels,
 	)
-	result := &counter{value: value{desc: desc, valType: CounterValue, labelPairs: desc.constLabelPairs}}
+	result := &counter{desc: desc, labelPairs: desc.constLabelPairs}
 	result.init(result) // Init self-collection.
 	return result
 }
 
 type counter struct {
-	value
+	// 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
 }
 
 func (c *counter) Add(v float64) {
 	if v < 0 {
 		panic(errors.New("counter cannot decrease in value"))
 	}
-	c.value.Add(v)
+	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)
 }
 
 // CounterVec is a Collector that bundles a set of Counters that all share the
@@ -78,16 +120,12 @@ func (c *counter) Add(v float64) {
 // 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. At least one label name must be
-// provided.
+// partitioned by the given label names.
 func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
 	desc := NewDesc(
 		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
@@ -96,34 +134,62 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
 		opts.ConstLabels,
 	)
 	return &CounterVec{
-		MetricVec: newMetricVec(desc, func(lvs ...string) Metric {
-			result := &counter{value: value{
-				desc:       desc,
-				valType:    CounterValue,
-				labelPairs: makeLabelPairs(desc, lvs),
-			}}
+		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)}
 			result.init(result) // Init self-collection.
 			return result
 		}),
 	}
 }
 
-// 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...)
+// 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...)
 	if metric != nil {
 		return metric.(Counter), err
 	}
 	return nil, err
 }
 
-// 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)
+// 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)
 	if metric != nil {
 		return metric.(Counter), err
 	}
@@ -131,18 +197,57 @@ func (m *CounterVec) GetMetricWith(labels Labels) (Counter, error) {
 }
 
 // WithLabelValues works as GetMetricWithLabelValues, but panics where
-// GetMetricWithLabelValues would have returned an error. By not returning an
-// error, WithLabelValues allows shortcuts like
+// GetMetricWithLabelValues would have returned an error. Not returning an
+// error allows shortcuts like
 //     myVec.WithLabelValues("404", "GET").Add(42)
-func (m *CounterVec) WithLabelValues(lvs ...string) Counter {
-	return m.MetricVec.WithLabelValues(lvs...).(Counter)
+func (v *CounterVec) WithLabelValues(lvs ...string) Counter {
+	c, err := v.GetMetricWithLabelValues(lvs...)
+	if err != nil {
+		panic(err)
+	}
+	return c
 }
 
 // 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 *CounterVec) With(labels Labels) Counter {
-	return m.MetricVec.With(labels).(Counter)
+// 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
 }
 
 // CounterFunc is a Counter whose value is determined at collect time by calling a
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/counter_test.go b/vendor/github.com/prometheus/client_golang/prometheus/counter_test.go
index 67391a23aa8c650cf469f714c0b90d84d9f0abb2..5062f51af022b568dfa5a1175ceffc2a243a832f 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/counter_test.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/counter_test.go
@@ -14,6 +14,7 @@
 package prometheus
 
 import (
+	"fmt"
 	"math"
 	"testing"
 
@@ -27,13 +28,27 @@ func TestCounterAdd(t *testing.T) {
 		ConstLabels: Labels{"a": "1", "b": "2"},
 	}).(*counter)
 	counter.Inc()
-	if expected, got := 1., math.Float64frombits(counter.valBits); expected != got {
+	if expected, got := 0.0, math.Float64frombits(counter.valBits); expected != got {
 		t.Errorf("Expected %f, got %f.", expected, got)
 	}
+	if expected, got := uint64(1), counter.valInt; expected != got {
+		t.Errorf("Expected %d, got %d.", expected, got)
+	}
 	counter.Add(42)
-	if expected, got := 43., math.Float64frombits(counter.valBits); expected != got {
+	if expected, got := 0.0, math.Float64frombits(counter.valBits); expected != got {
 		t.Errorf("Expected %f, got %f.", expected, got)
 	}
+	if expected, got := uint64(43), counter.valInt; expected != got {
+		t.Errorf("Expected %d, got %d.", expected, got)
+	}
+
+	counter.Add(24.42)
+	if expected, got := 24.42, math.Float64frombits(counter.valBits); expected != got {
+		t.Errorf("Expected %f, got %f.", expected, got)
+	}
+	if expected, got := uint64(43), counter.valInt; expected != got {
+		t.Errorf("Expected %d, got %d.", expected, got)
+	}
 
 	if expected, got := "counter cannot decrease in value", decreaseCounter(counter).Error(); expected != got {
 		t.Errorf("Expected error %q, got %q.", expected, got)
@@ -42,7 +57,7 @@ func TestCounterAdd(t *testing.T) {
 	m := &dto.Metric{}
 	counter.Write(m)
 
-	if expected, got := `label:<name:"a" value:"1" > label:<name:"b" value:"2" > counter:<value:43 > `, m.String(); expected != got {
+	if expected, got := `label:<name:"a" value:"1" > label:<name:"b" value:"2" > counter:<value:67.42 > `, m.String(); expected != got {
 		t.Errorf("expected %q, got %q", expected, got)
 	}
 }
@@ -56,3 +71,142 @@ func decreaseCounter(c *counter) (err error) {
 	c.Add(-1)
 	return nil
 }
+
+func TestCounterVecGetMetricWithInvalidLabelValues(t *testing.T) {
+	testCases := []struct {
+		desc   string
+		labels Labels
+	}{
+		{
+			desc:   "non utf8 label value",
+			labels: Labels{"a": "\xFF"},
+		},
+		{
+			desc:   "not enough label values",
+			labels: Labels{},
+		},
+		{
+			desc:   "too many label values",
+			labels: Labels{"a": "1", "b": "2"},
+		},
+	}
+
+	for _, test := range testCases {
+		counterVec := NewCounterVec(CounterOpts{
+			Name: "test",
+		}, []string{"a"})
+
+		labelValues := make([]string, len(test.labels))
+		for _, val := range test.labels {
+			labelValues = append(labelValues, val)
+		}
+
+		expectPanic(t, func() {
+			counterVec.WithLabelValues(labelValues...)
+		}, fmt.Sprintf("WithLabelValues: expected panic because: %s", test.desc))
+		expectPanic(t, func() {
+			counterVec.With(test.labels)
+		}, fmt.Sprintf("WithLabelValues: expected panic because: %s", test.desc))
+
+		if _, err := counterVec.GetMetricWithLabelValues(labelValues...); err == nil {
+			t.Errorf("GetMetricWithLabelValues: expected error because: %s", test.desc)
+		}
+		if _, err := counterVec.GetMetricWith(test.labels); err == nil {
+			t.Errorf("GetMetricWith: expected error because: %s", test.desc)
+		}
+	}
+}
+
+func expectPanic(t *testing.T, op func(), errorMsg string) {
+	defer func() {
+		if err := recover(); err == nil {
+			t.Error(errorMsg)
+		}
+	}()
+
+	op()
+}
+
+func TestCounterAddInf(t *testing.T) {
+	counter := NewCounter(CounterOpts{
+		Name: "test",
+		Help: "test help",
+	}).(*counter)
+
+	counter.Inc()
+	if expected, got := 0.0, math.Float64frombits(counter.valBits); expected != got {
+		t.Errorf("Expected %f, got %f.", expected, got)
+	}
+	if expected, got := uint64(1), counter.valInt; expected != got {
+		t.Errorf("Expected %d, got %d.", expected, got)
+	}
+
+	counter.Add(math.Inf(1))
+	if expected, got := math.Inf(1), math.Float64frombits(counter.valBits); expected != got {
+		t.Errorf("valBits expected %f, got %f.", expected, got)
+	}
+	if expected, got := uint64(1), counter.valInt; expected != got {
+		t.Errorf("valInts expected %d, got %d.", expected, got)
+	}
+
+	counter.Inc()
+	if expected, got := math.Inf(1), math.Float64frombits(counter.valBits); expected != got {
+		t.Errorf("Expected %f, got %f.", expected, got)
+	}
+	if expected, got := uint64(2), counter.valInt; expected != got {
+		t.Errorf("Expected %d, got %d.", expected, got)
+	}
+
+	m := &dto.Metric{}
+	counter.Write(m)
+
+	if expected, got := `counter:<value:inf > `, m.String(); expected != got {
+		t.Errorf("expected %q, got %q", expected, got)
+	}
+}
+
+func TestCounterAddLarge(t *testing.T) {
+	counter := NewCounter(CounterOpts{
+		Name: "test",
+		Help: "test help",
+	}).(*counter)
+
+	// large overflows the underlying type and should therefore be stored in valBits.
+	large := float64(math.MaxUint64 + 1)
+	counter.Add(large)
+	if expected, got := large, math.Float64frombits(counter.valBits); expected != got {
+		t.Errorf("valBits expected %f, got %f.", expected, got)
+	}
+	if expected, got := uint64(0), counter.valInt; expected != got {
+		t.Errorf("valInts expected %d, got %d.", expected, got)
+	}
+
+	m := &dto.Metric{}
+	counter.Write(m)
+
+	if expected, got := fmt.Sprintf("counter:<value:%0.16e > ", large), m.String(); expected != got {
+		t.Errorf("expected %q, got %q", expected, got)
+	}
+}
+
+func TestCounterAddSmall(t *testing.T) {
+	counter := NewCounter(CounterOpts{
+		Name: "test",
+		Help: "test help",
+	}).(*counter)
+	small := 0.000000000001
+	counter.Add(small)
+	if expected, got := small, math.Float64frombits(counter.valBits); expected != got {
+		t.Errorf("valBits expected %f, got %f.", expected, got)
+	}
+	if expected, got := uint64(0), counter.valInt; expected != got {
+		t.Errorf("valInts expected %d, got %d.", expected, got)
+	}
+
+	m := &dto.Metric{}
+	counter.Write(m)
+
+	if expected, got := fmt.Sprintf("counter:<value:%0.0e > ", small), m.String(); expected != got {
+		t.Errorf("expected %q, got %q", expected, got)
+	}
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/desc.go b/vendor/github.com/prometheus/client_golang/prometheus/desc.go
index 0593be50697d6d65890b853302e7d20ed3ccb4a9..1d034f871cb9cdd34e57144dfec3a3607e54d94d 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/desc.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/desc.go
@@ -16,33 +16,15 @@ 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
@@ -85,25 +67,20 @@ type Desc struct {
 
 // 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 and help must not be empty.
+// be nil if no such labels should be set. fqName 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 Opts documentation for the implications of
-// constant labels.
+// specified in the Desc. See the Collector example for a usage pattern.
 func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *Desc {
 	d := &Desc{
 		fqName:         fqName,
 		help:           help,
 		variableLabels: variableLabels,
 	}
-	if help == "" {
-		d.err = errors.New("empty help string")
-		return d
-	}
-	if !metricNameRE.MatchString(fqName) {
+	if !model.IsValidMetricName(model.LabelValue(fqName)) {
 		d.err = fmt.Errorf("%q is not a valid metric name", fqName)
 		return d
 	}
@@ -116,7 +93,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
 	// First add only the const label names and sort them...
 	for labelName := range constLabels {
 		if !checkLabelName(labelName) {
-			d.err = fmt.Errorf("%q is not a valid label name", labelName)
+			d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName)
 			return d
 		}
 		labelNames = append(labelNames, labelName)
@@ -127,12 +104,18 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
 	for _, labelName := range labelNames {
 		labelValues = append(labelValues, constLabels[labelName])
 	}
+	// Validate the const label values. They can't have a wrong cardinality, so
+	// use in len(labelValues) as expectedNumberOfValues.
+	if err := validateLabelValues(labelValues, len(labelValues)); err != nil {
+		d.err = err
+		return d
+	}
 	// Now add the variable label names, but prefix them with something that
 	// cannot be in a regular label name. That prevents matching the label
 	// dimension with a different mix between preset and variable labels.
 	for _, labelName := range variableLabels {
 		if !checkLabelName(labelName) {
-			d.err = fmt.Errorf("%q is not a valid label name", labelName)
+			d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName)
 			return d
 		}
 		labelNames = append(labelNames, "$"+labelName)
@@ -142,6 +125,7 @@ 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)
@@ -168,7 +152,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
 }
 
@@ -198,8 +182,3 @@ 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/desc_test.go b/vendor/github.com/prometheus/client_golang/prometheus/desc_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..5f854db0bdc9f8b14f431f61b4023e579b04115f
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/desc_test.go
@@ -0,0 +1,30 @@
+// 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 (
+	"testing"
+)
+
+func TestNewDescInvalidLabelValues(t *testing.T) {
+	desc := NewDesc(
+		"sample_label",
+		"sample label",
+		nil,
+		Labels{"a": "\xFF"},
+	)
+	if desc.err == nil {
+		t.Errorf("NewDesc: expected error because: %s", desc.err)
+	}
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/doc.go b/vendor/github.com/prometheus/client_golang/prometheus/doc.go
index b15a2d3b9864f8c8c2d24b313a287e984bd8b0d8..5d9525defc8dfd35b6bf990c17fe71b04faec6bf 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/doc.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/doc.go
@@ -11,13 +11,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// 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).
+// 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.
 //
 // All exported functions and methods are safe to be used concurrently unless
-//specified otherwise.
+// specified otherwise.
 //
 // A Basic Example
 //
@@ -26,6 +28,7 @@
 //    package main
 //
 //    import (
+//    	"log"
 //    	"net/http"
 //
 //    	"github.com/prometheus/client_golang/prometheus"
@@ -59,7 +62,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())
-//    	http.ListenAndServe(":8080", nil)
+//    	log.Fatal(http.ListenAndServe(":8080", nil))
 //    }
 //
 //
@@ -69,9 +72,12 @@
 // Metrics
 //
 // The number of exported identifiers in this package might appear a bit
-// overwhelming. Hovever, in addition to the basic plumbing shown in the example
+// overwhelming. However, 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.
+// 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.
 //
 // 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
@@ -95,8 +101,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
 //
@@ -114,8 +120,18 @@
 // 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.
+// 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.
 //
 // The Collector example illustrates the use case. You can also look at the
 // source code of the processCollector (mirroring process metrics), the
@@ -129,34 +145,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 DefaultRegistry variable. With NewRegistry, you
+// can be found in the global DefaultRegisterer variable. With NewRegistry, you
 // can create a custom registry, or you can even implement the Registerer or
-// Gatherer interfaces yourself. The methods Register and Unregister work in
-// the same way on a custom registry as the global functions Register and
-// Unregister on the default registry.
-//
-// There are a number of uses for custom registries: You can use registries
-// with special properties, see NewPedanticRegistry. You can avoid global state,
-// as it is imposed by the DefaultRegistry. You can use multiple registries at
-// the same time to expose different metrics in different ways. You can use
+// Gatherer interfaces yourself. The methods Register and Unregister work in the
+// same way on a custom registry as the global functions Register and Unregister
+// on the default registry.
+//
+// There are a number of uses for custom registries: You can use registries with
+// special properties, see NewPedanticRegistry. You can avoid global state, as
+// it is imposed by the DefaultRegisterer. 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 DefaultRegistry comes registered with a Collector for Go
+// Also note that the DefaultRegisterer 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.
@@ -166,16 +182,20 @@
 // 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. Sending metrics to
-// Graphite would be an example that will soon be implemented.
+// More ways of exposing metrics can easily be added by following the approaches
+// of the existing implementations.
 package prometheus
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/example_clustermanager_test.go b/vendor/github.com/prometheus/client_golang/prometheus/example_clustermanager_test.go
index 260c1b52ded4c41a43ce40d0fd59f7fc33284bdf..92b61ca8516bacb7c6e5d511e1a680435914ad67 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/example_clustermanager_test.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/example_clustermanager_test.go
@@ -13,22 +13,28 @@
 
 package prometheus_test
 
-import "github.com/prometheus/client_golang/prometheus"
+import (
+	"log"
+	"net/http"
+
+	"github.com/prometheus/client_golang/prometheus"
+	"github.com/prometheus/client_golang/prometheus/promhttp"
+)
 
 // ClusterManager is an example for a system that might have been built without
 // Prometheus in mind. It models a central manager of jobs running in a
-// cluster. To turn it into something that collects Prometheus metrics, we
-// simply add the two methods required for the Collector interface.
+// cluster. Thus, we implement a custom Collector called
+// ClusterManagerCollector, which collects information from a ClusterManager
+// using its provided methods and turns them into Prometheus Metrics for
+// collection.
 //
 // An additional challenge is that multiple instances of the ClusterManager are
 // run within the same binary, each in charge of a different zone. We need to
-// make use of ConstLabels to be able to register each ClusterManager instance
-// with Prometheus.
+// make use of wrapping Registerers to be able to register each
+// ClusterManagerCollector instance with Prometheus.
 type ClusterManager struct {
-	Zone         string
-	OOMCountDesc *prometheus.Desc
-	RAMUsageDesc *prometheus.Desc
-	// ... many more fields
+	Zone string
+	// Contains many more fields not listed in this example.
 }
 
 // ReallyExpensiveAssessmentOfTheSystemState is a mock for the data gathering a
@@ -50,10 +56,30 @@ func (c *ClusterManager) ReallyExpensiveAssessmentOfTheSystemState() (
 	return
 }
 
-// Describe simply sends the two Descs in the struct to the channel.
-func (c *ClusterManager) Describe(ch chan<- *prometheus.Desc) {
-	ch <- c.OOMCountDesc
-	ch <- c.RAMUsageDesc
+// ClusterManagerCollector implements the Collector interface.
+type ClusterManagerCollector struct {
+	ClusterManager *ClusterManager
+}
+
+// Descriptors used by the ClusterManagerCollector below.
+var (
+	oomCountDesc = prometheus.NewDesc(
+		"clustermanager_oom_crashes_total",
+		"Number of OOM crashes.",
+		[]string{"host"}, nil,
+	)
+	ramUsageDesc = prometheus.NewDesc(
+		"clustermanager_ram_usage_bytes",
+		"RAM usage as reported to the cluster manager.",
+		[]string{"host"}, nil,
+	)
+)
+
+// Describe is implemented with DescribeByCollect. That's possible because the
+// Collect method will always return the same two metrics with the same two
+// descriptors.
+func (cc ClusterManagerCollector) Describe(ch chan<- *prometheus.Desc) {
+	prometheus.DescribeByCollect(cc, ch)
 }
 
 // Collect first triggers the ReallyExpensiveAssessmentOfTheSystemState. Then it
@@ -61,11 +87,11 @@ func (c *ClusterManager) Describe(ch chan<- *prometheus.Desc) {
 //
 // Note that Collect could be called concurrently, so we depend on
 // ReallyExpensiveAssessmentOfTheSystemState to be concurrency-safe.
-func (c *ClusterManager) Collect(ch chan<- prometheus.Metric) {
-	oomCountByHost, ramUsageByHost := c.ReallyExpensiveAssessmentOfTheSystemState()
+func (cc ClusterManagerCollector) Collect(ch chan<- prometheus.Metric) {
+	oomCountByHost, ramUsageByHost := cc.ClusterManager.ReallyExpensiveAssessmentOfTheSystemState()
 	for host, oomCount := range oomCountByHost {
 		ch <- prometheus.MustNewConstMetric(
-			c.OOMCountDesc,
+			oomCountDesc,
 			prometheus.CounterValue,
 			float64(oomCount),
 			host,
@@ -73,7 +99,7 @@ func (c *ClusterManager) Collect(ch chan<- prometheus.Metric) {
 	}
 	for host, ramUsage := range ramUsageByHost {
 		ch <- prometheus.MustNewConstMetric(
-			c.RAMUsageDesc,
+			ramUsageDesc,
 			prometheus.GaugeValue,
 			ramUsage,
 			host,
@@ -81,38 +107,36 @@ func (c *ClusterManager) Collect(ch chan<- prometheus.Metric) {
 	}
 }
 
-// NewClusterManager creates the two Descs OOMCountDesc and RAMUsageDesc. Note
-// that the zone is set as a ConstLabel. (It's different in each instance of the
-// ClusterManager, but constant over the lifetime of an instance.) Then there is
-// a variable label "host", since we want to partition the collected metrics by
-// host. Since all Descs created in this way are consistent across instances,
-// with a guaranteed distinction by the "zone" label, we can register different
-// ClusterManager instances with the same registry.
-func NewClusterManager(zone string) *ClusterManager {
-	return &ClusterManager{
+// NewClusterManager first creates a Prometheus-ignorant ClusterManager
+// instance. Then, it creates a ClusterManagerCollector for the just created
+// ClusterManager. Finally, it registers the ClusterManagerCollector with a
+// wrapping Registerer that adds the zone as a label. In this way, the metrics
+// collected by different ClusterManagerCollectors do not collide.
+func NewClusterManager(zone string, reg prometheus.Registerer) *ClusterManager {
+	c := &ClusterManager{
 		Zone: zone,
-		OOMCountDesc: prometheus.NewDesc(
-			"clustermanager_oom_crashes_total",
-			"Number of OOM crashes.",
-			[]string{"host"},
-			prometheus.Labels{"zone": zone},
-		),
-		RAMUsageDesc: prometheus.NewDesc(
-			"clustermanager_ram_usage_bytes",
-			"RAM usage as reported to the cluster manager.",
-			[]string{"host"},
-			prometheus.Labels{"zone": zone},
-		),
 	}
+	cc := ClusterManagerCollector{ClusterManager: c}
+	prometheus.WrapRegistererWith(prometheus.Labels{"zone": zone}, reg).MustRegister(cc)
+	return c
 }
 
 func ExampleCollector() {
-	workerDB := NewClusterManager("db")
-	workerCA := NewClusterManager("ca")
-
 	// Since we are dealing with custom Collector implementations, it might
 	// be a good idea to try it out with a pedantic registry.
 	reg := prometheus.NewPedanticRegistry()
-	reg.MustRegister(workerDB)
-	reg.MustRegister(workerCA)
+
+	// Construct cluster managers. In real code, we would assign them to
+	// variables to then do something with them.
+	NewClusterManager("db", reg)
+	NewClusterManager("ca", reg)
+
+	// Add the standard process and Go metrics to the custom registry.
+	reg.MustRegister(
+		prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}),
+		prometheus.NewGoCollector(),
+	)
+
+	http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))
+	log.Fatal(http.ListenAndServe(":8080", nil))
 }
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/example_timer_complex_test.go b/vendor/github.com/prometheus/client_golang/prometheus/example_timer_complex_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..c5e7de5e5e5db2c1b27f2ccae6e4777028922599
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/example_timer_complex_test.go
@@ -0,0 +1,71 @@
+// Copyright 2014 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_test
+
+import (
+	"net/http"
+
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+var (
+	// apiRequestDuration tracks the duration separate for each HTTP status
+	// class (1xx, 2xx, ...). This creates a fair amount of time series on
+	// the Prometheus server. Usually, you would track the duration of
+	// serving HTTP request without partitioning by outcome. Do something
+	// like this only if needed. Also note how only status classes are
+	// tracked, not every single status code. The latter would create an
+	// even larger amount of time series. Request counters partitioned by
+	// status code are usually OK as each counter only creates one time
+	// series. Histograms are way more expensive, so partition with care and
+	// only where you really need separate latency tracking. Partitioning by
+	// status class is only an example. In concrete cases, other partitions
+	// might make more sense.
+	apiRequestDuration = prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Name:    "api_request_duration_seconds",
+			Help:    "Histogram for the request duration of the public API, partitioned by status class.",
+			Buckets: prometheus.ExponentialBuckets(0.1, 1.5, 5),
+		},
+		[]string{"status_class"},
+	)
+)
+
+func handler(w http.ResponseWriter, r *http.Request) {
+	status := http.StatusOK
+	// The ObserverFunc gets called by the deferred ObserveDuration and
+	// decides which Histogram's Observe method is called.
+	timer := prometheus.NewTimer(prometheus.ObserverFunc(func(v float64) {
+		switch {
+		case status >= 500: // Server error.
+			apiRequestDuration.WithLabelValues("5xx").Observe(v)
+		case status >= 400: // Client error.
+			apiRequestDuration.WithLabelValues("4xx").Observe(v)
+		case status >= 300: // Redirection.
+			apiRequestDuration.WithLabelValues("3xx").Observe(v)
+		case status >= 200: // Success.
+			apiRequestDuration.WithLabelValues("2xx").Observe(v)
+		default: // Informational.
+			apiRequestDuration.WithLabelValues("1xx").Observe(v)
+		}
+	}))
+	defer timer.ObserveDuration()
+
+	// Handle the request. Set status accordingly.
+	// ...
+}
+
+func ExampleTimer_complex() {
+	http.HandleFunc("/api", handler)
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/example_timer_gauge_test.go b/vendor/github.com/prometheus/client_golang/prometheus/example_timer_gauge_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..7184a0d1d9e3ca20f902cf20f235624a7eb90090
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/example_timer_gauge_test.go
@@ -0,0 +1,48 @@
+// Copyright 2014 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_test
+
+import (
+	"os"
+
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+var (
+	// If a function is called rarely (i.e. not more often than scrapes
+	// happen) or ideally only once (like in a batch job), it can make sense
+	// to use a Gauge for timing the function call. For timing a batch job
+	// and pushing the result to a Pushgateway, see also the comprehensive
+	// example in the push package.
+	funcDuration = prometheus.NewGauge(prometheus.GaugeOpts{
+		Name: "example_function_duration_seconds",
+		Help: "Duration of the last call of an example function.",
+	})
+)
+
+func run() error {
+	// The Set method of the Gauge is used to observe the duration.
+	timer := prometheus.NewTimer(prometheus.ObserverFunc(funcDuration.Set))
+	defer timer.ObserveDuration()
+
+	// Do something. Return errors as encountered. The use of 'defer' above
+	// makes sure the function is still timed properly.
+	return nil
+}
+
+func ExampleTimer_gauge() {
+	if err := run(); err != nil {
+		os.Exit(1)
+	}
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/example_timer_test.go b/vendor/github.com/prometheus/client_golang/prometheus/example_timer_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..bd86bb4720155989cba9e0b331dfdf797981304d
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/example_timer_test.go
@@ -0,0 +1,40 @@
+// Copyright 2014 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_test
+
+import (
+	"math/rand"
+	"time"
+
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+var (
+	requestDuration = prometheus.NewHistogram(prometheus.HistogramOpts{
+		Name:    "example_request_duration_seconds",
+		Help:    "Histogram for the runtime of a simple example function.",
+		Buckets: prometheus.LinearBuckets(0.01, 0.01, 10),
+	})
+)
+
+func ExampleTimer() {
+	// timer times this example function. It uses a Histogram, but a Summary
+	// would also work, as both implement Observer. Check out
+	// https://prometheus.io/docs/practices/histograms/ for differences.
+	timer := prometheus.NewTimer(requestDuration)
+	defer timer.ObserveDuration()
+
+	// Do something here that takes time.
+	time.Sleep(time.Duration(rand.NormFloat64()*10000+50000) * time.Microsecond)
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/examples_test.go b/vendor/github.com/prometheus/client_golang/prometheus/examples_test.go
index 468c9ef815eda2ce3297912724c8a2cb89c49c54..a3824793f2037eec5579bc4e1fc6a050d82a96f8 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/examples_test.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/examples_test.go
@@ -19,13 +19,13 @@ import (
 	"math"
 	"net/http"
 	"runtime"
-	"sort"
 	"strings"
+	"time"
 
-	dto "github.com/prometheus/client_model/go"
+	"github.com/golang/protobuf/proto"
 	"github.com/prometheus/common/expfmt"
 
-	"github.com/golang/protobuf/proto"
+	dto "github.com/prometheus/client_model/go"
 
 	"github.com/prometheus/client_golang/prometheus"
 )
@@ -89,37 +89,6 @@ func ExampleGaugeFunc() {
 	// GaugeFunc 'goroutines_count' registered.
 }
 
-func ExampleCounter() {
-	pushCounter := prometheus.NewCounter(prometheus.CounterOpts{
-		Name: "repository_pushes", // Note: No help string...
-	})
-	err := prometheus.Register(pushCounter) // ... so this will return an error.
-	if err != nil {
-		fmt.Println("Push counter couldn't be registered, no counting will happen:", err)
-		return
-	}
-
-	// Try it once more, this time with a help string.
-	pushCounter = prometheus.NewCounter(prometheus.CounterOpts{
-		Name: "repository_pushes",
-		Help: "Number of pushes to external repository.",
-	})
-	err = prometheus.Register(pushCounter)
-	if err != nil {
-		fmt.Println("Push counter couldn't be registered AGAIN, no counting will happen:", err)
-		return
-	}
-
-	pushComplete := make(chan struct{})
-	// TODO: Start a goroutine that performs repository pushes and reports
-	// each completion via the channel.
-	for range pushComplete {
-		pushCounter.Inc()
-	}
-	// Output:
-	// Push counter couldn't be registered, no counting will happen: descriptor Desc{fqName: "repository_pushes", help: "", constLabels: {}, variableLabels: []} is invalid: empty help string
-}
-
 func ExampleCounterVec() {
 	httpReqs := prometheus.NewCounterVec(
 		prometheus.CounterOpts{
@@ -167,19 +136,6 @@ func ExampleInstrumentHandler() {
 	))
 }
 
-func ExampleLabelPairSorter() {
-	labelPairs := []*dto.LabelPair{
-		{Name: proto.String("status"), Value: proto.String("404")},
-		{Name: proto.String("method"), Value: proto.String("get")},
-	}
-
-	sort.Sort(prometheus.LabelPairSorter(labelPairs))
-
-	fmt.Println(labelPairs)
-	// Output:
-	// [name:"method" value:"get"  name:"status" value:"404" ]
-}
-
 func ExampleRegister() {
 	// Imagine you have a worker pool and want to count the tasks completed.
 	taskCounter := prometheus.NewCounter(prometheus.CounterOpts{
@@ -326,7 +282,7 @@ func ExampleRegister() {
 	// taskCounter unregistered.
 	// taskCounterVec not registered: a previously registered descriptor with the same fully-qualified name as Desc{fqName: "worker_pool_completed_tasks_total", help: "Total number of tasks completed.", constLabels: {}, variableLabels: [worker_id]} has different label names or a different help string
 	// taskCounterVec registered.
-	// Worker initialization failed: inconsistent label cardinality
+	// Worker initialization failed: inconsistent label cardinality: expected 1 label values but got 2 in []string{"42", "spurious arg"}
 	// notMyCounter is nil.
 	// taskCounterForWorker42 registered.
 	// taskCounterForWorker2001 registered.
@@ -334,8 +290,9 @@ func ExampleRegister() {
 
 func ExampleSummary() {
 	temps := prometheus.NewSummary(prometheus.SummaryOpts{
-		Name: "pond_temperature_celsius",
-		Help: "The temperature of the frog pond.", // Sorry, we can't measure how badly it smells.
+		Name:       "pond_temperature_celsius",
+		Help:       "The temperature of the frog pond.",
+		Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
 	})
 
 	// Simulate some observations.
@@ -372,8 +329,9 @@ func ExampleSummary() {
 func ExampleSummaryVec() {
 	temps := prometheus.NewSummaryVec(
 		prometheus.SummaryOpts{
-			Name: "pond_temperature_celsius",
-			Help: "The temperature of the frog pond.", // Sorry, we can't measure how badly it smells.
+			Name:       "pond_temperature_celsius",
+			Help:       "The temperature of the frog pond.",
+			Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
 		},
 		[]string{"species"},
 	)
@@ -710,7 +668,7 @@ humidity_percent{location="inside"} 33.2
 # HELP temperature_kelvin Temperature in Kelvin.
 # Duplicate metric:
 temperature_kelvin{location="outside"} 265.3
- # Wrong labels:
+ # Missing location label (note that this is undesirable but valid):
 temperature_kelvin 4.5
 `
 
@@ -738,15 +696,47 @@ temperature_kelvin 4.5
 	// temperature_kelvin{location="outside"} 273.14
 	// temperature_kelvin{location="somewhere else"} 4.5
 	// ----------
-	// 2 error(s) occurred:
-	// * collected metric temperature_kelvin label:<name:"location" value:"outside" > gauge:<value:265.3 >  was collected before with the same name and label values
-	// * collected metric temperature_kelvin gauge:<value:4.5 >  has label dimensions inconsistent with previously collected metrics in the same metric family
+	// collected metric "temperature_kelvin" { label:<name:"location" value:"outside" > gauge:<value:265.3 > } was collected before with the same name and label values
 	// # HELP humidity_percent Humidity in %.
 	// # TYPE humidity_percent gauge
 	// humidity_percent{location="inside"} 33.2
 	// humidity_percent{location="outside"} 45.4
 	// # HELP temperature_kelvin Temperature in Kelvin.
 	// # TYPE temperature_kelvin gauge
+	// temperature_kelvin 4.5
 	// temperature_kelvin{location="inside"} 298.44
 	// temperature_kelvin{location="outside"} 273.14
 }
+
+func ExampleNewMetricWithTimestamp() {
+	desc := prometheus.NewDesc(
+		"temperature_kelvin",
+		"Current temperature in Kelvin.",
+		nil, nil,
+	)
+
+	// Create a constant gauge from values we got from an external
+	// temperature reporting system. Those values are reported with a slight
+	// delay, so we want to add the timestamp of the actual measurement.
+	temperatureReportedByExternalSystem := 298.15
+	timeReportedByExternalSystem := time.Date(2009, time.November, 10, 23, 0, 0, 12345678, time.UTC)
+	s := prometheus.NewMetricWithTimestamp(
+		timeReportedByExternalSystem,
+		prometheus.MustNewConstMetric(
+			desc, prometheus.GaugeValue, temperatureReportedByExternalSystem,
+		),
+	)
+
+	// Just for demonstration, let's check the state of the gauge by
+	// (ab)using its Write method (which is usually only used by Prometheus
+	// internally).
+	metric := &dto.Metric{}
+	s.Write(metric)
+	fmt.Println(proto.MarshalTextString(metric))
+
+	// Output:
+	// gauge: <
+	//   value: 298.15
+	// >
+	// timestamp_ms: 1257894000012
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector_test.go b/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector_test.go
index 910dac325841aeb3cac2a3347e70620d48e2a0fc..6bcd9b692d5cb5de4d9d12488f19c5957355da84 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector_test.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector_test.go
@@ -78,7 +78,7 @@ func ExampleNewExpvarCollector() {
 		close(metricChan)
 	}()
 	for m := range metricChan {
-		if strings.Index(m.Desc().String(), "expvar_memstats") == -1 {
+		if !strings.Contains(m.Desc().String(), "expvar_memstats") {
 			metric.Reset()
 			m.Write(&metric)
 			metricStrings = append(metricStrings, metric.String())
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/fnv.go b/vendor/github.com/prometheus/client_golang/prometheus/fnv.go
index e3b67df8ac0681fd73151e7e44c3ea8bed3bfb63..3d383a735c3850a08aa33041e7b00b5ecfbef27f 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/fnv.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/fnv.go
@@ -1,3 +1,16 @@
+// 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 8b70e5141d4099d2c3d768c4cede39aaa29b684a..71d406bd92ab26c9a6a9e4434f70ce7de4ff7861 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/gauge.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/gauge.go
@@ -13,6 +13,14 @@
 
 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.
 //
@@ -27,29 +35,95 @@ type Gauge interface {
 
 	// Set sets the Gauge to an arbitrary value.
 	Set(float64)
-	// Inc increments the Gauge by 1.
+	// Inc increments the Gauge by 1. Use Add to increment it by arbitrary
+	// values.
 	Inc()
-	// Dec decrements the Gauge by 1.
+	// Dec decrements the Gauge by 1. Use Sub to decrement it by arbitrary
+	// values.
 	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 {
-	return newValue(NewDesc(
+	desc := NewDesc(
 		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
 		opts.Help,
 		nil,
 		opts.ConstLabels,
-	), GaugeValue, 0)
+	)
+	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)
 }
 
 // GaugeVec is a Collector that bundles a set of Gauges that all share the same
@@ -58,12 +132,11 @@ func NewGauge(opts GaugeOpts) Gauge {
 // (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. At least one label name must be
-// provided.
+// partitioned by the given label names.
 func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
 	desc := NewDesc(
 		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
@@ -72,28 +145,62 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
 		opts.ConstLabels,
 	)
 	return &GaugeVec{
-		MetricVec: newMetricVec(desc, func(lvs ...string) Metric {
-			return newValue(desc, GaugeValue, 0, lvs...)
+		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
 		}),
 	}
 }
 
-// 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...)
+// 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...)
 	if metric != nil {
 		return metric.(Gauge), err
 	}
 	return nil, err
 }
 
-// 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)
+// 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)
 	if metric != nil {
 		return metric.(Gauge), err
 	}
@@ -101,18 +208,57 @@ func (m *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) {
 }
 
 // WithLabelValues works as GetMetricWithLabelValues, but panics where
-// GetMetricWithLabelValues would have returned an error. By not returning an
-// error, WithLabelValues allows shortcuts like
+// GetMetricWithLabelValues would have returned an error. Not returning an
+// error allows shortcuts like
 //     myVec.WithLabelValues("404", "GET").Add(42)
-func (m *GaugeVec) WithLabelValues(lvs ...string) Gauge {
-	return m.MetricVec.WithLabelValues(lvs...).(Gauge)
+func (v *GaugeVec) WithLabelValues(lvs ...string) Gauge {
+	g, err := v.GetMetricWithLabelValues(lvs...)
+	if err != nil {
+		panic(err)
+	}
+	return g
 }
 
 // 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 *GaugeVec) With(labels Labels) Gauge {
-	return m.MetricVec.With(labels).(Gauge)
+// 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
 }
 
 // GaugeFunc is a Gauge whose value is determined at collect time by calling a
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/gauge_test.go b/vendor/github.com/prometheus/client_golang/prometheus/gauge_test.go
index 48cab463671b334da3445beadd94b81368cbe9af..a2e3c141655fe66f86ff470d01fa7fa3b7ee6f53 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/gauge_test.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/gauge_test.go
@@ -19,6 +19,7 @@ import (
 	"sync"
 	"testing"
 	"testing/quick"
+	"time"
 
 	dto "github.com/prometheus/client_model/go"
 )
@@ -82,7 +83,7 @@ func TestGaugeConcurrency(t *testing.T) {
 		}
 		start.Done()
 
-		if expected, got := <-result, math.Float64frombits(gge.(*value).valBits); math.Abs(expected-got) > 0.000001 {
+		if expected, got := <-result, math.Float64frombits(gge.(*gauge).valBits); math.Abs(expected-got) > 0.000001 {
 			t.Fatalf("expected approx. %f, got %f", expected, got)
 			return false
 		}
@@ -146,7 +147,7 @@ func TestGaugeVecConcurrency(t *testing.T) {
 		start.Done()
 
 		for i := range sStreams {
-			if expected, got := <-results[i], math.Float64frombits(gge.WithLabelValues(string('A'+i)).(*value).valBits); math.Abs(expected-got) > 0.000001 {
+			if expected, got := <-results[i], math.Float64frombits(gge.WithLabelValues(string('A'+i)).(*gauge).valBits); math.Abs(expected-got) > 0.000001 {
 				t.Fatalf("expected approx. %f, got %f", expected, got)
 				return false
 			}
@@ -180,3 +181,22 @@ func TestGaugeFunc(t *testing.T) {
 		t.Errorf("expected %q, got %q", expected, got)
 	}
 }
+
+func TestGaugeSetCurrentTime(t *testing.T) {
+	g := NewGauge(GaugeOpts{
+		Name: "test_name",
+		Help: "test help",
+	})
+	g.SetToCurrentTime()
+	unixTime := float64(time.Now().Unix())
+
+	m := &dto.Metric{}
+	g.Write(m)
+
+	delta := unixTime - m.GetGauge().GetValue()
+	// This is just a smoke test to make sure SetToCurrentTime is not
+	// totally off. Tests with current time involved are hard...
+	if math.Abs(delta) > 5 {
+		t.Errorf("Gauge set to current time deviates from current time by more than 5s, delta is %f seconds", delta)
+	}
+}
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 abc9d4ec403c42766e3514e3a7f0e506379c744f..ba3b9333edde0d95b7c573fb81d44edab5bb5fd9 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
@@ -1,3 +1,16 @@
+// 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 (
@@ -8,26 +21,39 @@ import (
 )
 
 type goCollector struct {
-	goroutines Gauge
-	gcDesc     *Desc
+	goroutinesDesc *Desc
+	threadsDesc    *Desc
+	gcDesc         *Desc
+	goInfoDesc     *Desc
 
 	// metrics to describe and collect
 	metrics memStatsMetrics
 }
 
-// NewGoCollector returns a collector which exports metrics about the current
-// go process.
+// 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).
 func NewGoCollector() Collector {
 	return &goCollector{
-		goroutines: NewGauge(GaugeOpts{
-			Namespace: "go",
-			Name:      "goroutines",
-			Help:      "Number of goroutines that currently exist.",
-		}),
+		goroutinesDesc: NewDesc(
+			"go_goroutines",
+			"Number of goroutines that currently exist.",
+			nil, nil),
+		threadsDesc: NewDesc(
+			"go_threads",
+			"Number of OS threads created.",
+			nil, nil),
 		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(
@@ -48,7 +74,7 @@ func NewGoCollector() Collector {
 			}, {
 				desc: NewDesc(
 					memstatNamespace("sys_bytes"),
-					"Number of bytes obtained by system. Sum of all system allocations.",
+					"Number of bytes obtained from system.",
 					nil, nil,
 				),
 				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.Sys) },
@@ -111,12 +137,12 @@ func NewGoCollector() Collector {
 				valType: GaugeValue,
 			}, {
 				desc: NewDesc(
-					memstatNamespace("heap_released_bytes_total"),
-					"Total number of heap bytes released to OS.",
+					memstatNamespace("heap_released_bytes"),
+					"Number of heap bytes released to OS.",
 					nil, nil,
 				),
 				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.HeapReleased) },
-				valType: CounterValue,
+				valType: GaugeValue,
 			}, {
 				desc: NewDesc(
 					memstatNamespace("heap_objects"),
@@ -213,6 +239,14 @@ 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,
 			},
 		},
 	}
@@ -224,9 +258,10 @@ func memstatNamespace(s string) string {
 
 // Describe returns all descriptions of the collector.
 func (c *goCollector) Describe(ch chan<- *Desc) {
-	ch <- c.goroutines.Desc()
+	ch <- c.goroutinesDesc
+	ch <- c.threadsDesc
 	ch <- c.gcDesc
-
+	ch <- c.goInfoDesc
 	for _, i := range c.metrics {
 		ch <- i.desc
 	}
@@ -234,8 +269,9 @@ 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) {
-	c.goroutines.Set(float64(runtime.NumGoroutine()))
-	ch <- c.goroutines
+	ch <- MustNewConstMetric(c.goroutinesDesc, GaugeValue, float64(runtime.NumGoroutine()))
+	n, _ := runtime.ThreadCreateProfile(nil)
+	ch <- MustNewConstMetric(c.threadsDesc, GaugeValue, float64(n))
 
 	var stats debug.GCStats
 	stats.PauseQuantiles = make([]time.Duration, 5)
@@ -246,7 +282,9 @@ 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), float64(stats.PauseTotal.Seconds()), quantiles)
+	ch <- MustNewConstSummary(c.gcDesc, uint64(stats.NumGC), stats.PauseTotal.Seconds(), quantiles)
+
+	ch <- MustNewConstMetric(c.goInfoDesc, GaugeValue, 1)
 
 	ms := &runtime.MemStats{}
 	runtime.ReadMemStats(ms)
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/go_collector_test.go b/vendor/github.com/prometheus/client_golang/prometheus/go_collector_test.go
index 9a8858cbd2b8a3de0c89a8a183730ae5a0b66232..f93dcdcfcea48d6087e8ccf3e363dc8af0892240 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/go_collector_test.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/go_collector_test.go
@@ -1,3 +1,16 @@
+// 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 (
@@ -29,33 +42,37 @@ func TestGoCollector(t *testing.T) {
 
 	for {
 		select {
-		case metric := <-ch:
-			switch m := metric.(type) {
-			// Attention, this also catches Counter...
-			case Gauge:
-				pb := &dto.Metric{}
-				m.Write(pb)
-				if pb.GetGauge() == nil {
-					continue
-				}
-
-				if old == -1 {
-					old = int(pb.GetGauge().GetValue())
-					close(waitc)
-					continue
-				}
+		case m := <-ch:
+			// m can be Gauge or Counter,
+			// currently just test the go_goroutines Gauge
+			// and ignore others.
+			if m.Desc().fqName != "go_goroutines" {
+				continue
+			}
+			pb := &dto.Metric{}
+			m.Write(pb)
+			if pb.GetGauge() == nil {
+				continue
+			}
 
-				if diff := int(pb.GetGauge().GetValue()) - old; diff != 1 {
-					// TODO: This is flaky in highly concurrent situations.
-					t.Errorf("want 1 new goroutine, got %d", diff)
-				}
+			if old == -1 {
+				old = int(pb.GetGauge().GetValue())
+				close(waitc)
+				continue
+			}
 
-				// GoCollector performs two sends per call.
-				// On line 27 we need to receive the second send
-				// to shut down cleanly.
-				<-ch
-				return
+			if diff := int(pb.GetGauge().GetValue()) - old; diff != 1 {
+				// TODO: This is flaky in highly concurrent situations.
+				t.Errorf("want 1 new goroutine, got %d", diff)
 			}
+
+			// GoCollector performs three sends per call.
+			// On line 27 we need to receive three more sends
+			// to shut down cleanly.
+			<-ch
+			<-ch
+			<-ch
+			return
 		case <-time.After(1 * time.Second):
 			t.Fatalf("expected collect timed out")
 		}
@@ -85,37 +102,33 @@ func TestGCCollector(t *testing.T) {
 	for {
 		select {
 		case metric := <-ch:
-			switch m := metric.(type) {
-			case *constSummary, *value:
-				pb := &dto.Metric{}
-				m.Write(pb)
-				if pb.GetSummary() == nil {
-					continue
-				}
-
-				if len(pb.GetSummary().Quantile) != 5 {
-					t.Errorf("expected 4 buckets, got %d", len(pb.GetSummary().Quantile))
-				}
-				for idx, want := range []float64{0.0, 0.25, 0.5, 0.75, 1.0} {
-					if *pb.GetSummary().Quantile[idx].Quantile != want {
-						t.Errorf("bucket #%d is off, got %f, want %f", idx, *pb.GetSummary().Quantile[idx].Quantile, want)
-					}
-				}
-				if first {
-					first = false
-					oldGC = *pb.GetSummary().SampleCount
-					oldPause = *pb.GetSummary().SampleSum
-					close(waitc)
-					continue
-				}
-				if diff := *pb.GetSummary().SampleCount - oldGC; diff != 1 {
-					t.Errorf("want 1 new garbage collection run, got %d", diff)
-				}
-				if diff := *pb.GetSummary().SampleSum - oldPause; diff <= 0 {
-					t.Errorf("want moar pause, got %f", diff)
+			pb := &dto.Metric{}
+			metric.Write(pb)
+			if pb.GetSummary() == nil {
+				continue
+			}
+			if len(pb.GetSummary().Quantile) != 5 {
+				t.Errorf("expected 4 buckets, got %d", len(pb.GetSummary().Quantile))
+			}
+			for idx, want := range []float64{0.0, 0.25, 0.5, 0.75, 1.0} {
+				if *pb.GetSummary().Quantile[idx].Quantile != want {
+					t.Errorf("bucket #%d is off, got %f, want %f", idx, *pb.GetSummary().Quantile[idx].Quantile, want)
 				}
-				return
 			}
+			if first {
+				first = false
+				oldGC = *pb.GetSummary().SampleCount
+				oldPause = *pb.GetSummary().SampleSum
+				close(waitc)
+				continue
+			}
+			if diff := *pb.GetSummary().SampleCount - oldGC; diff != 1 {
+				t.Errorf("want 1 new garbage collection run, got %d", diff)
+			}
+			if diff := *pb.GetSummary().SampleSum - oldPause; diff <= 0 {
+				t.Errorf("want moar pause, got %f", diff)
+			}
+			return
 		case <-time.After(1 * time.Second):
 			t.Fatalf("expected collect timed out")
 		}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/graphite/bridge.go b/vendor/github.com/prometheus/client_golang/prometheus/graphite/bridge.go
new file mode 100644
index 0000000000000000000000000000000000000000..466e2295dff4cc49bdc901c1507408900d8dd97a
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/graphite/bridge.go
@@ -0,0 +1,282 @@
+// 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 graphite provides a bridge to push Prometheus metrics to a Graphite
+// server.
+package graphite
+
+import (
+	"bufio"
+	"errors"
+	"fmt"
+	"io"
+	"net"
+	"sort"
+	"time"
+
+	"github.com/prometheus/common/expfmt"
+	"github.com/prometheus/common/model"
+	"golang.org/x/net/context"
+
+	dto "github.com/prometheus/client_model/go"
+
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+const (
+	defaultInterval       = 15 * time.Second
+	millisecondsPerSecond = 1000
+)
+
+// HandlerErrorHandling defines how a Handler serving metrics will handle
+// errors.
+type HandlerErrorHandling int
+
+// These constants cause handlers serving metrics to behave as described if
+// errors are encountered.
+const (
+	// Ignore errors and try to push as many metrics to Graphite as possible.
+	ContinueOnError HandlerErrorHandling = iota
+
+	// Abort the push to Graphite upon the first error encountered.
+	AbortOnError
+)
+
+// Config defines the Graphite bridge config.
+type Config struct {
+	// The url to push data to. Required.
+	URL string
+
+	// The prefix for the pushed Graphite metrics. Defaults to empty string.
+	Prefix string
+
+	// The interval to use for pushing data to Graphite. Defaults to 15 seconds.
+	Interval time.Duration
+
+	// The timeout for pushing metrics to Graphite. Defaults to 15 seconds.
+	Timeout time.Duration
+
+	// The Gatherer to use for metrics. Defaults to prometheus.DefaultGatherer.
+	Gatherer prometheus.Gatherer
+
+	// The logger that messages are written to. Defaults to no logging.
+	Logger Logger
+
+	// ErrorHandling defines how errors are handled. Note that errors are
+	// logged regardless of the configured ErrorHandling provided Logger
+	// is not nil.
+	ErrorHandling HandlerErrorHandling
+}
+
+// Bridge pushes metrics to the configured Graphite server.
+type Bridge struct {
+	url      string
+	prefix   string
+	interval time.Duration
+	timeout  time.Duration
+
+	errorHandling HandlerErrorHandling
+	logger        Logger
+
+	g prometheus.Gatherer
+}
+
+// Logger is the minimal interface Bridge needs for logging. Note that
+// log.Logger from the standard library implements this interface, and it is
+// easy to implement by custom loggers, if they don't do so already anyway.
+type Logger interface {
+	Println(v ...interface{})
+}
+
+// NewBridge returns a pointer to a new Bridge struct.
+func NewBridge(c *Config) (*Bridge, error) {
+	b := &Bridge{}
+
+	if c.URL == "" {
+		return nil, errors.New("missing URL")
+	}
+	b.url = c.URL
+
+	if c.Gatherer == nil {
+		b.g = prometheus.DefaultGatherer
+	} else {
+		b.g = c.Gatherer
+	}
+
+	if c.Logger != nil {
+		b.logger = c.Logger
+	}
+
+	if c.Prefix != "" {
+		b.prefix = c.Prefix
+	}
+
+	var z time.Duration
+	if c.Interval == z {
+		b.interval = defaultInterval
+	} else {
+		b.interval = c.Interval
+	}
+
+	if c.Timeout == z {
+		b.timeout = defaultInterval
+	} else {
+		b.timeout = c.Timeout
+	}
+
+	b.errorHandling = c.ErrorHandling
+
+	return b, nil
+}
+
+// Run starts the event loop that pushes Prometheus metrics to Graphite at the
+// configured interval.
+func (b *Bridge) Run(ctx context.Context) {
+	ticker := time.NewTicker(b.interval)
+	defer ticker.Stop()
+	for {
+		select {
+		case <-ticker.C:
+			if err := b.Push(); err != nil && b.logger != nil {
+				b.logger.Println("error pushing to Graphite:", err)
+			}
+		case <-ctx.Done():
+			return
+		}
+	}
+}
+
+// Push pushes Prometheus metrics to the configured Graphite server.
+func (b *Bridge) Push() error {
+	mfs, err := b.g.Gather()
+	if err != nil || len(mfs) == 0 {
+		switch b.errorHandling {
+		case AbortOnError:
+			return err
+		case ContinueOnError:
+			if b.logger != nil {
+				b.logger.Println("continue on error:", err)
+			}
+		default:
+			panic("unrecognized error handling value")
+		}
+	}
+
+	conn, err := net.DialTimeout("tcp", b.url, b.timeout)
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	return writeMetrics(conn, mfs, b.prefix, model.Now())
+}
+
+func writeMetrics(w io.Writer, mfs []*dto.MetricFamily, prefix string, now model.Time) error {
+	vec, err := expfmt.ExtractSamples(&expfmt.DecodeOptions{
+		Timestamp: now,
+	}, mfs...)
+	if err != nil {
+		return err
+	}
+
+	buf := bufio.NewWriter(w)
+	for _, s := range vec {
+		for _, c := range prefix {
+			if _, err := buf.WriteRune(c); err != nil {
+				return err
+			}
+		}
+		if err := buf.WriteByte('.'); err != nil {
+			return err
+		}
+		if err := writeMetric(buf, s.Metric); err != nil {
+			return err
+		}
+		if _, err := fmt.Fprintf(buf, " %g %d\n", s.Value, int64(s.Timestamp)/millisecondsPerSecond); err != nil {
+			return err
+		}
+		if err := buf.Flush(); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func writeMetric(buf *bufio.Writer, m model.Metric) error {
+	metricName, hasName := m[model.MetricNameLabel]
+	numLabels := len(m) - 1
+	if !hasName {
+		numLabels = len(m)
+	}
+
+	labelStrings := make([]string, 0, numLabels)
+	for label, value := range m {
+		if label != model.MetricNameLabel {
+			labelStrings = append(labelStrings, fmt.Sprintf("%s %s", string(label), string(value)))
+		}
+	}
+
+	var err error
+	switch numLabels {
+	case 0:
+		if hasName {
+			return writeSanitized(buf, string(metricName))
+		}
+	default:
+		sort.Strings(labelStrings)
+		if err = writeSanitized(buf, string(metricName)); err != nil {
+			return err
+		}
+		for _, s := range labelStrings {
+			if err = buf.WriteByte('.'); err != nil {
+				return err
+			}
+			if err = writeSanitized(buf, s); err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+func writeSanitized(buf *bufio.Writer, s string) error {
+	prevUnderscore := false
+
+	for _, c := range s {
+		c = replaceInvalidRune(c)
+		if c == '_' {
+			if prevUnderscore {
+				continue
+			}
+			prevUnderscore = true
+		} else {
+			prevUnderscore = false
+		}
+		if _, err := buf.WriteRune(c); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func replaceInvalidRune(c rune) rune {
+	if c == ' ' {
+		return '.'
+	}
+	if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == ':' || c == '-' || (c >= '0' && c <= '9')) {
+		return '_'
+	}
+	return c
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/graphite/bridge_test.go b/vendor/github.com/prometheus/client_golang/prometheus/graphite/bridge_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..471edfe405d8558088b7f504f3e5479a3195d005
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/graphite/bridge_test.go
@@ -0,0 +1,338 @@
+// 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 graphite
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"io"
+	"log"
+	"net"
+	"os"
+	"regexp"
+	"testing"
+	"time"
+
+	"github.com/prometheus/common/model"
+	"golang.org/x/net/context"
+
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+func TestSanitize(t *testing.T) {
+	testCases := []struct {
+		in, out string
+	}{
+		{in: "hello", out: "hello"},
+		{in: "hE/l1o", out: "hE_l1o"},
+		{in: "he,*ll(.o", out: "he_ll_o"},
+		{in: "hello_there%^&", out: "hello_there_"},
+		{in: "hell-.o", out: "hell-_o"},
+	}
+
+	var buf bytes.Buffer
+	w := bufio.NewWriter(&buf)
+
+	for i, tc := range testCases {
+		if err := writeSanitized(w, tc.in); err != nil {
+			t.Fatalf("write failed: %v", err)
+		}
+		if err := w.Flush(); err != nil {
+			t.Fatalf("flush failed: %v", err)
+		}
+
+		if want, got := tc.out, buf.String(); want != got {
+			t.Fatalf("test case index %d: got sanitized string %s, want %s", i, got, want)
+		}
+
+		buf.Reset()
+	}
+}
+
+func TestWriteSummary(t *testing.T) {
+	sumVec := prometheus.NewSummaryVec(
+		prometheus.SummaryOpts{
+			Name:        "name",
+			Help:        "docstring",
+			ConstLabels: prometheus.Labels{"constname": "constvalue"},
+			Objectives:  map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
+		},
+		[]string{"labelname"},
+	)
+
+	sumVec.WithLabelValues("val1").Observe(float64(10))
+	sumVec.WithLabelValues("val1").Observe(float64(20))
+	sumVec.WithLabelValues("val1").Observe(float64(30))
+	sumVec.WithLabelValues("val2").Observe(float64(20))
+	sumVec.WithLabelValues("val2").Observe(float64(30))
+	sumVec.WithLabelValues("val2").Observe(float64(40))
+
+	reg := prometheus.NewRegistry()
+	reg.MustRegister(sumVec)
+
+	mfs, err := reg.Gather()
+	if err != nil {
+		t.Fatalf("error: %v", err)
+	}
+
+	testCases := []struct {
+		prefix string
+	}{
+		{prefix: "prefix"},
+		{prefix: "pre/fix"},
+		{prefix: "pre.fix"},
+	}
+
+	const want = `%s.name.constname.constvalue.labelname.val1.quantile.0_5 20 1477043
+%s.name.constname.constvalue.labelname.val1.quantile.0_9 30 1477043
+%s.name.constname.constvalue.labelname.val1.quantile.0_99 30 1477043
+%s.name_sum.constname.constvalue.labelname.val1 60 1477043
+%s.name_count.constname.constvalue.labelname.val1 3 1477043
+%s.name.constname.constvalue.labelname.val2.quantile.0_5 30 1477043
+%s.name.constname.constvalue.labelname.val2.quantile.0_9 40 1477043
+%s.name.constname.constvalue.labelname.val2.quantile.0_99 40 1477043
+%s.name_sum.constname.constvalue.labelname.val2 90 1477043
+%s.name_count.constname.constvalue.labelname.val2 3 1477043
+`
+	for i, tc := range testCases {
+
+		now := model.Time(1477043083)
+		var buf bytes.Buffer
+		err = writeMetrics(&buf, mfs, tc.prefix, now)
+		if err != nil {
+			t.Fatalf("error: %v", err)
+		}
+
+		wantWithPrefix := fmt.Sprintf(want,
+			tc.prefix, tc.prefix, tc.prefix, tc.prefix, tc.prefix,
+			tc.prefix, tc.prefix, tc.prefix, tc.prefix, tc.prefix,
+		)
+		if got := buf.String(); wantWithPrefix != got {
+			t.Fatalf("test case index %d: wanted \n%s\n, got \n%s\n", i, wantWithPrefix, got)
+		}
+	}
+}
+
+func TestWriteHistogram(t *testing.T) {
+	histVec := prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Name:        "name",
+			Help:        "docstring",
+			ConstLabels: prometheus.Labels{"constname": "constvalue"},
+			Buckets:     []float64{0.01, 0.02, 0.05, 0.1},
+		},
+		[]string{"labelname"},
+	)
+
+	histVec.WithLabelValues("val1").Observe(float64(10))
+	histVec.WithLabelValues("val1").Observe(float64(20))
+	histVec.WithLabelValues("val1").Observe(float64(30))
+	histVec.WithLabelValues("val2").Observe(float64(20))
+	histVec.WithLabelValues("val2").Observe(float64(30))
+	histVec.WithLabelValues("val2").Observe(float64(40))
+
+	reg := prometheus.NewRegistry()
+	reg.MustRegister(histVec)
+
+	mfs, err := reg.Gather()
+	if err != nil {
+		t.Fatalf("error: %v", err)
+	}
+
+	now := model.Time(1477043083)
+	var buf bytes.Buffer
+	err = writeMetrics(&buf, mfs, "prefix", now)
+	if err != nil {
+		t.Fatalf("error: %v", err)
+	}
+
+	want := `prefix.name_bucket.constname.constvalue.labelname.val1.le.0_01 0 1477043
+prefix.name_bucket.constname.constvalue.labelname.val1.le.0_02 0 1477043
+prefix.name_bucket.constname.constvalue.labelname.val1.le.0_05 0 1477043
+prefix.name_bucket.constname.constvalue.labelname.val1.le.0_1 0 1477043
+prefix.name_sum.constname.constvalue.labelname.val1 60 1477043
+prefix.name_count.constname.constvalue.labelname.val1 3 1477043
+prefix.name_bucket.constname.constvalue.labelname.val1.le._Inf 3 1477043
+prefix.name_bucket.constname.constvalue.labelname.val2.le.0_01 0 1477043
+prefix.name_bucket.constname.constvalue.labelname.val2.le.0_02 0 1477043
+prefix.name_bucket.constname.constvalue.labelname.val2.le.0_05 0 1477043
+prefix.name_bucket.constname.constvalue.labelname.val2.le.0_1 0 1477043
+prefix.name_sum.constname.constvalue.labelname.val2 90 1477043
+prefix.name_count.constname.constvalue.labelname.val2 3 1477043
+prefix.name_bucket.constname.constvalue.labelname.val2.le._Inf 3 1477043
+`
+	if got := buf.String(); want != got {
+		t.Fatalf("wanted \n%s\n, got \n%s\n", want, got)
+	}
+}
+
+func TestToReader(t *testing.T) {
+	cntVec := prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Name:        "name",
+			Help:        "docstring",
+			ConstLabels: prometheus.Labels{"constname": "constvalue"},
+		},
+		[]string{"labelname"},
+	)
+	cntVec.WithLabelValues("val1").Inc()
+	cntVec.WithLabelValues("val2").Inc()
+
+	reg := prometheus.NewRegistry()
+	reg.MustRegister(cntVec)
+
+	want := `prefix.name.constname.constvalue.labelname.val1 1 1477043
+prefix.name.constname.constvalue.labelname.val2 1 1477043
+`
+	mfs, err := reg.Gather()
+	if err != nil {
+		t.Fatalf("error: %v", err)
+	}
+
+	now := model.Time(1477043083)
+	var buf bytes.Buffer
+	err = writeMetrics(&buf, mfs, "prefix", now)
+	if err != nil {
+		t.Fatalf("error: %v", err)
+	}
+
+	if got := buf.String(); want != got {
+		t.Fatalf("wanted \n%s\n, got \n%s\n", want, got)
+	}
+}
+
+func TestPush(t *testing.T) {
+	reg := prometheus.NewRegistry()
+	cntVec := prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Name:        "name",
+			Help:        "docstring",
+			ConstLabels: prometheus.Labels{"constname": "constvalue"},
+		},
+		[]string{"labelname"},
+	)
+	cntVec.WithLabelValues("val1").Inc()
+	cntVec.WithLabelValues("val2").Inc()
+	reg.MustRegister(cntVec)
+
+	host := "localhost"
+	port := ":56789"
+	b, err := NewBridge(&Config{
+		URL:      host + port,
+		Gatherer: reg,
+		Prefix:   "prefix",
+	})
+	if err != nil {
+		t.Fatalf("error creating bridge: %v", err)
+	}
+
+	nmg, err := newMockGraphite(port)
+	if err != nil {
+		t.Fatalf("error creating mock graphite: %v", err)
+	}
+	defer nmg.Close()
+
+	err = b.Push()
+	if err != nil {
+		t.Fatalf("error pushing: %v", err)
+	}
+
+	wants := []string{
+		"prefix.name.constname.constvalue.labelname.val1 1",
+		"prefix.name.constname.constvalue.labelname.val2 1",
+	}
+
+	select {
+	case got := <-nmg.readc:
+		for _, want := range wants {
+			matched, err := regexp.MatchString(want, got)
+			if err != nil {
+				t.Fatalf("error pushing: %v", err)
+			}
+			if !matched {
+				t.Fatalf("missing metric:\nno match for %s received by server:\n%s", want, got)
+			}
+		}
+		return
+	case err := <-nmg.errc:
+		t.Fatalf("error reading push: %v", err)
+	case <-time.After(50 * time.Millisecond):
+		t.Fatalf("no result from graphite server")
+	}
+}
+
+func newMockGraphite(port string) (*mockGraphite, error) {
+	readc := make(chan string)
+	errc := make(chan error)
+	ln, err := net.Listen("tcp", port)
+	if err != nil {
+		return nil, err
+	}
+
+	go func() {
+		conn, err := ln.Accept()
+		if err != nil {
+			errc <- err
+		}
+		var b bytes.Buffer
+		io.Copy(&b, conn)
+		readc <- b.String()
+	}()
+
+	return &mockGraphite{
+		readc:    readc,
+		errc:     errc,
+		Listener: ln,
+	}, nil
+}
+
+type mockGraphite struct {
+	readc chan string
+	errc  chan error
+
+	net.Listener
+}
+
+func ExampleBridge() {
+	b, err := NewBridge(&Config{
+		URL:           "graphite.example.org:3099",
+		Gatherer:      prometheus.DefaultGatherer,
+		Prefix:        "prefix",
+		Interval:      15 * time.Second,
+		Timeout:       10 * time.Second,
+		ErrorHandling: AbortOnError,
+		Logger:        log.New(os.Stdout, "graphite bridge: ", log.Lshortfile),
+	})
+	if err != nil {
+		panic(err)
+	}
+
+	go func() {
+		// Start something in a goroutine that uses metrics.
+	}()
+
+	// Push initial metrics to Graphite. Fail fast if the push fails.
+	if err := b.Push(); err != nil {
+		panic(err)
+	}
+
+	// Create a Context to control stopping the Run() loop that pushes
+	// metrics to Graphite.
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+
+	// Start pushing metrics to Graphite in the Run() loop.
+	b.Run(ctx)
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go
index 9719e8fac8ba3ac6cbccc1635830a58bc680afb4..f88da707bc840cc3c0612cf8a58e709a55ec8a4e 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go
@@ -16,7 +16,9 @@ package prometheus
 import (
 	"fmt"
 	"math"
+	"runtime"
 	"sort"
+	"sync"
 	"sync/atomic"
 
 	"github.com/golang/protobuf/proto"
@@ -108,8 +110,9 @@ func ExponentialBuckets(start, factor float64, count int) []float64 {
 }
 
 // HistogramOpts bundles the options for creating a Histogram metric. 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.
+// 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.
 type HistogramOpts struct {
 	// Namespace, Subsystem, and Name are components of the fully-qualified
 	// name of the Histogram (created by joining these components with
@@ -120,29 +123,22 @@ type HistogramOpts struct {
 	Subsystem string
 	Name      string
 
-	// Help provides information about this Histogram. Mandatory!
+	// Help provides information about this Histogram.
 	//
 	// Metrics with the same fully-qualified name must have the same Help
 	// string.
 	Help string
 
-	// 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 used to attach fixed labels to this metric. Metrics
+	// with the same fully-qualified name must have the same label names in
+	// their 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
-	// 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 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
 	ConstLabels Labels
 
 	// Buckets defines the buckets into which observations are counted. Each
@@ -169,7 +165,7 @@ func NewHistogram(opts HistogramOpts) Histogram {
 
 func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram {
 	if len(desc.variableLabels) != len(labelValues) {
-		panic(errInconsistentCardinality)
+		panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, labelValues))
 	}
 
 	for _, n := range desc.variableLabels {
@@ -191,6 +187,7 @@ 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 {
@@ -207,28 +204,53 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
 			}
 		}
 	}
-	// Finally we know the final length of h.upperBounds and can make counts.
-	h.counts = make([]uint64, len(h.upperBounds))
+	// 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))
 
 	h.init(h) // Init self-collection.
 	return h
 }
 
-type histogram struct {
+type histogramCounts 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
+}
 
-	selfCollector
-	// Note that there is no mutex required.
+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
 
-	desc *Desc
+	selfCollector
+	desc     *Desc
+	writeMtx sync.Mutex // Only used in the Write method.
 
 	upperBounds []float64
-	counts      []uint64
+
+	// 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.
 
 	labelPairs []*dto.LabelPair
 }
@@ -248,36 +270,113 @@ 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)
-	if i < len(h.counts) {
-		atomic.AddUint64(&h.counts[i], 1)
+
+	// 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)
 	}
-	atomic.AddUint64(&h.count, 1)
 	for {
-		oldBits := atomic.LoadUint64(&h.sumBits)
+		oldBits := atomic.LoadUint64(&hotCounts.sumBits)
 		newBits := math.Float64bits(math.Float64frombits(oldBits) + v)
-		if atomic.CompareAndSwapUint64(&h.sumBits, oldBits, newBits) {
+		if atomic.CompareAndSwapUint64(&hotCounts.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 {
-	his := &dto.Histogram{}
-	buckets := make([]*dto.Bucket, len(h.upperBounds))
+	var (
+		his                   = &dto.Histogram{}
+		buckets               = make([]*dto.Bucket, len(h.upperBounds))
+		hotCounts, coldCounts *histogramCounts
+		count                 uint64
+	)
+
+	// For simplicity, we mutex the rest of this method. It is not in the
+	// hot path, i.e.  Observe is called much more often than Write. The
+	// complication of making Write lock-free isn't worth it.
+	h.writeMtx.Lock()
+	defer h.writeMtx.Unlock()
 
-	his.SampleSum = proto.Float64(math.Float64frombits(atomic.LoadUint64(&h.sumBits)))
-	his.SampleCount = proto.Uint64(atomic.LoadUint64(&h.count))
-	var count uint64
+	// 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
 	for i, upperBound := range h.upperBounds {
-		count += atomic.LoadUint64(&h.counts[i])
+		cumCount += atomic.LoadUint64(&coldCounts.buckets[i])
 		buckets[i] = &dto.Bucket{
-			CumulativeCount: proto.Uint64(count),
+			CumulativeCount: proto.Uint64(cumCount),
 			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
 }
 
@@ -287,12 +386,11 @@ 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. At least one label name must be
-// provided.
+// partitioned by the given label names.
 func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
 	desc := NewDesc(
 		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
@@ -301,47 +399,116 @@ 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 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...)
+// 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...)
 	if metric != nil {
-		return metric.(Histogram), err
+		return metric.(Observer), err
 	}
 	return nil, err
 }
 
-// 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)
+// 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)
 	if metric != nil {
-		return metric.(Histogram), err
+		return metric.(Observer), 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
+// GetMetricWithLabelValues would have returned an error. Not returning an
+// error allows shortcuts like
 //     myVec.WithLabelValues("404", "GET").Observe(42.21)
-func (m *HistogramVec) WithLabelValues(lvs ...string) Histogram {
-	return m.MetricVec.WithLabelValues(lvs...).(Histogram)
+func (v *HistogramVec) WithLabelValues(lvs ...string) Observer {
+	h, err := v.GetMetricWithLabelValues(lvs...)
+	if err != nil {
+		panic(err)
+	}
+	return h
+}
+
+// With works as GetMetricWith but panics where GetMetricWithLabels would have
+// returned an error. Not returning an error allows shortcuts like
+//     myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21)
+func (v *HistogramVec) With(labels Labels) Observer {
+	h, err := v.GetMetricWith(labels)
+	if err != nil {
+		panic(err)
+	}
+	return h
+}
+
+// CurryWith returns a vector curried with the provided labels, i.e. the
+// returned vector has those labels pre-set for all labeled operations performed
+// on it. The cardinality of the curried vector is reduced accordingly. The
+// order of the remaining labels stays the same (just with the curried labels
+// taken out of the sequence – which is relevant for the
+// (GetMetric)WithLabelValues methods). It is possible to curry a curried
+// vector, but only with labels not yet used for currying before.
+//
+// The metrics contained in the HistogramVec are shared between the curried and
+// uncurried vectors. They are just accessed differently. Curried and uncurried
+// vectors behave identically in terms of collection. Only one must be
+// registered with a given registry (usually the uncurried version). The Reset
+// method deletes all metrics, even if called on a curried vector.
+func (v *HistogramVec) CurryWith(labels Labels) (ObserverVec, error) {
+	vec, err := v.curryWith(labels)
+	if vec != nil {
+		return &HistogramVec{vec}, err
+	}
+	return nil, err
 }
 
-// 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)
+// 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
 }
 
 type constHistogram struct {
@@ -393,7 +560,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.
+// consistent with the variable labels in Desc or if Desc is invalid.
 func NewConstHistogram(
 	desc *Desc,
 	count uint64,
@@ -401,8 +568,11 @@ func NewConstHistogram(
 	buckets map[float64]uint64,
 	labelValues ...string,
 ) (Metric, error) {
-	if len(desc.variableLabels) != len(labelValues) {
-		return nil, errInconsistentCardinality
+	if desc.err != nil {
+		return nil, desc.err
+	}
+	if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
+		return nil, err
 	}
 	return &constHistogram{
 		desc:       desc,
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/histogram_test.go b/vendor/github.com/prometheus/client_golang/prometheus/histogram_test.go
index a73b1af1e5450c7a6f8cd4b4951010736e28efb2..9546b8776f8d09023a58fe4bdd93cc9a36c46f70 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/histogram_test.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/histogram_test.go
@@ -17,6 +17,7 @@ import (
 	"math"
 	"math/rand"
 	"reflect"
+	"runtime"
 	"sort"
 	"sync"
 	"testing"
@@ -286,7 +287,7 @@ func TestHistogramVecConcurrency(t *testing.T) {
 		for i := 0; i < vecLength; i++ {
 			m := &dto.Metric{}
 			s := his.WithLabelValues(string('A' + i))
-			s.Write(m)
+			s.(Histogram).Write(m)
 
 			if got, want := len(m.Histogram.Bucket), len(testBuckets)-1; got != want {
 				t.Errorf("got %d buckets in protobuf, want %d", got, want)
@@ -343,6 +344,50 @@ func TestBuckets(t *testing.T) {
 	got = ExponentialBuckets(100, 1.2, 3)
 	want = []float64{100, 120, 144}
 	if !reflect.DeepEqual(got, want) {
-		t.Errorf("linear buckets: got %v, want %v", got, want)
+		t.Errorf("exponential buckets: got %v, want %v", got, want)
+	}
+}
+
+func TestHistogramAtomicObserve(t *testing.T) {
+	var (
+		quit = make(chan struct{})
+		his  = NewHistogram(HistogramOpts{
+			Buckets: []float64{0.5, 10, 20},
+		})
+	)
+
+	defer func() { close(quit) }()
+
+	observe := func() {
+		for {
+			select {
+			case <-quit:
+				return
+			default:
+				his.Observe(1)
+			}
+		}
+	}
+
+	go observe()
+	go observe()
+	go observe()
+
+	for i := 0; i < 100; i++ {
+		m := &dto.Metric{}
+		if err := his.Write(m); err != nil {
+			t.Fatal("unexpected error writing histogram:", err)
+		}
+		h := m.GetHistogram()
+		if h.GetSampleCount() != uint64(h.GetSampleSum()) ||
+			h.GetSampleCount() != h.GetBucket()[1].GetCumulativeCount() ||
+			h.GetSampleCount() != h.GetBucket()[2].GetCumulativeCount() {
+			t.Fatalf(
+				"inconsistent counts in histogram: count=%d sum=%f buckets=[%d, %d]",
+				h.GetSampleCount(), h.GetSampleSum(),
+				h.GetBucket()[1].GetCumulativeCount(), h.GetBucket()[2].GetCumulativeCount(),
+			)
+		}
+		runtime.Gosched()
 	}
 }
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/http.go b/vendor/github.com/prometheus/client_golang/prometheus/http.go
index b2b00196236ac9ef2f4824bae98ee4a8be4e79e8..9f0875bfc811d4199e968377a814d53194ce3d32 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/http.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/http.go
@@ -15,9 +15,7 @@ package prometheus
 
 import (
 	"bufio"
-	"bytes"
 	"compress/gzip"
-	"fmt"
 	"io"
 	"net"
 	"net/http"
@@ -41,19 +39,10 @@ const (
 	acceptEncodingHeader  = "Accept-Encoding"
 )
 
-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)
+var gzipPool = sync.Pool{
+	New: func() interface{} {
+		return gzip.NewWriter(nil)
+	},
 }
 
 // Handler returns an HTTP handler for the DefaultGatherer. It is
@@ -61,68 +50,50 @@ func giveBuf(buf *bytes.Buffer) {
 // name).
 //
 // Deprecated: Please note the issues described in the doc comment of
-// InstrumentHandler. You might want to consider using promhttp.Handler instead
-// (which is non instrumented).
+// InstrumentHandler. You might want to consider using promhttp.Handler instead.
 func Handler() http.Handler {
 	return InstrumentHandler("prometheus", UninstrumentedHandler())
 }
 
 // UninstrumentedHandler returns an HTTP handler for the DefaultGatherer.
 //
-// Deprecated: Use promhttp.Handler instead. See there for further documentation.
+// Deprecated: Use promhttp.HandlerFor(DefaultGatherer, promhttp.HandlerOpts{})
+// instead. See there for further documentation.
 func UninstrumentedHandler() http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+	return http.HandlerFunc(func(rsp http.ResponseWriter, req *http.Request) {
 		mfs, err := DefaultGatherer.Gather()
 		if err != nil {
-			http.Error(w, "An error has occurred during metrics collection:\n\n"+err.Error(), http.StatusInternalServerError)
+			httpError(rsp, err)
 			return
 		}
 
 		contentType := expfmt.Negotiate(req.Header)
-		buf := getBuf()
-		defer giveBuf(buf)
-		writer, encoding := decorateWriter(req, buf)
-		enc := expfmt.NewEncoder(writer, contentType)
-		var lastErr error
+		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)
+
 		for _, mf := range mfs {
 			if err := enc.Encode(mf); err != nil {
-				lastErr = err
-				http.Error(w, "An error has occurred during metrics encoding:\n\n"+err.Error(), http.StatusInternalServerError)
+				httpError(rsp, err)
 				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 {
@@ -139,16 +110,6 @@ 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
@@ -158,23 +119,16 @@ func nowSeries(t ...time.Time) nower {
 // 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:
-//
-// - 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.
+// 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.
 func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFunc {
 	return InstrumentHandlerFunc(handlerName, handler.ServeHTTP)
 }
@@ -184,12 +138,13 @@ func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFun
 // issues).
 //
 // Deprecated: InstrumentHandlerFunc is deprecated for the same reasons as
-// InstrumentHandler is.
+// InstrumentHandler is. Use the tooling provided in package promhttp instead.
 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,
 	)
@@ -222,7 +177,7 @@ func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWri
 // SummaryOpts.
 //
 // Deprecated: InstrumentHandlerWithOpts is deprecated for the same reasons as
-// InstrumentHandler is.
+// InstrumentHandler is. Use the tooling provided in package promhttp instead.
 func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.HandlerFunc {
 	return InstrumentHandlerFuncWithOpts(opts, handler.ServeHTTP)
 }
@@ -233,7 +188,7 @@ func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.Hand
 // SummaryOpts are used.
 //
 // Deprecated: InstrumentHandlerFuncWithOpts is deprecated for the same reasons
-// as InstrumentHandler is.
+// as InstrumentHandler is. Use the tooling provided in package promhttp instead.
 func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
 	reqCnt := NewCounterVec(
 		CounterOpts{
@@ -245,23 +200,46 @@ 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)
-
-	regReqCnt := MustRegisterOrGet(reqCnt).(*CounterVec)
-	regReqDur := MustRegisterOrGet(reqDur).(Summary)
-	regReqSz := MustRegisterOrGet(reqSz).(Summary)
-	regResSz := MustRegisterOrGet(resSz).(Summary)
+	if err := Register(resSz); err != nil {
+		if are, ok := err.(AlreadyRegisteredError); ok {
+			resSz = are.ExistingCollector.(Summary)
+		} else {
+			panic(err)
+		}
+	}
 
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		now := time.Now()
@@ -285,15 +263,15 @@ func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.Respo
 
 		method := sanitizeMethod(r.Method)
 		code := sanitizeCode(delegate.status)
-		regReqCnt.WithLabelValues(method, code).Inc()
-		regReqDur.Observe(elapsed)
-		regResSz.Observe(float64(delegate.written))
-		regReqSz.Observe(float64(<-out))
+		reqCnt.WithLabelValues(method, code).Inc()
+		reqDur.Observe(elapsed)
+		resSz.Observe(float64(delegate.written))
+		reqSz.Observe(float64(<-out))
 	})
 }
 
 func computeApproximateRequestSize(r *http.Request) <-chan int {
-	// Get URL length in current go routine for avoiding a race condition.
+	// 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 {
@@ -328,10 +306,9 @@ func computeApproximateRequestSize(r *http.Request) <-chan int {
 type responseWriterDelegator struct {
 	http.ResponseWriter
 
-	handler, method string
-	status          int
-	written         int64
-	wroteHeader     bool
+	status      int
+	written     int64
+	wroteHeader bool
 }
 
 func (r *responseWriterDelegator) WriteHeader(code int) {
@@ -497,3 +474,31 @@ 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/http_test.go b/vendor/github.com/prometheus/client_golang/prometheus/http_test.go
index ffe0418cf8798314fd1cbfe7fc4e23ab6ab4218c..dd89ccf1a67abf8c89a2f2d4bd32ff7e45748fb0 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/http_test.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/http_test.go
@@ -29,6 +29,16 @@ func (b respBody) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	w.Write([]byte(b))
 }
 
+func nowSeries(t ...time.Time) nower {
+	return nowFunc(func() time.Time {
+		defer func() {
+			t = t[1:]
+		}()
+
+		return t[0]
+	})
+}
+
 func TestInstrumentHandler(t *testing.T) {
 	defer func(n nower) {
 		now = n.(nower)
@@ -37,16 +47,17 @@ func TestInstrumentHandler(t *testing.T) {
 	instant := time.Now()
 	end := instant.Add(30 * time.Second)
 	now = nowSeries(instant, end)
-	respBody := respBody("Howdy there!")
+	body := respBody("Howdy there!")
 
-	hndlr := InstrumentHandler("test-handler", respBody)
+	hndlr := InstrumentHandler("test-handler", body)
 
 	opts := SummaryOpts{
 		Subsystem:   "http",
 		ConstLabels: Labels{"handler": "test-handler"},
+		Objectives:  map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
 	}
 
-	reqCnt := MustRegisterOrGet(NewCounterVec(
+	reqCnt := NewCounterVec(
 		CounterOpts{
 			Namespace:   opts.Namespace,
 			Subsystem:   opts.Subsystem,
@@ -55,19 +66,51 @@ func TestInstrumentHandler(t *testing.T) {
 			ConstLabels: opts.ConstLabels,
 		},
 		instLabels,
-	)).(*CounterVec)
+	)
+	err := Register(reqCnt)
+	if err == nil {
+		t.Fatal("expected reqCnt to be registered already")
+	}
+	if are, ok := err.(AlreadyRegisteredError); ok {
+		reqCnt = are.ExistingCollector.(*CounterVec)
+	} else {
+		t.Fatal("unexpected registration error:", err)
+	}
 
 	opts.Name = "request_duration_microseconds"
 	opts.Help = "The HTTP request latencies in microseconds."
-	reqDur := MustRegisterOrGet(NewSummary(opts)).(Summary)
+	reqDur := NewSummary(opts)
+	err = Register(reqDur)
+	if err == nil {
+		t.Fatal("expected reqDur to be registered already")
+	}
+	if are, ok := err.(AlreadyRegisteredError); ok {
+		reqDur = are.ExistingCollector.(Summary)
+	} else {
+		t.Fatal("unexpected registration error:", err)
+	}
 
 	opts.Name = "request_size_bytes"
 	opts.Help = "The HTTP request sizes in bytes."
-	MustRegisterOrGet(NewSummary(opts))
+	reqSz := NewSummary(opts)
+	err = Register(reqSz)
+	if err == nil {
+		t.Fatal("expected reqSz to be registered already")
+	}
+	if _, ok := err.(AlreadyRegisteredError); !ok {
+		t.Fatal("unexpected registration error:", err)
+	}
 
 	opts.Name = "response_size_bytes"
 	opts.Help = "The HTTP response sizes in bytes."
-	MustRegisterOrGet(NewSummary(opts))
+	resSz := NewSummary(opts)
+	err = Register(resSz)
+	if err == nil {
+		t.Fatal("expected resSz to be registered already")
+	}
+	if _, ok := err.(AlreadyRegisteredError); !ok {
+		t.Fatal("unexpected registration error:", err)
+	}
 
 	reqCnt.Reset()
 
@@ -81,8 +124,8 @@ func TestInstrumentHandler(t *testing.T) {
 	if resp.Code != http.StatusTeapot {
 		t.Fatalf("expected status %d, got %d", http.StatusTeapot, resp.Code)
 	}
-	if string(resp.Body.Bytes()) != "Howdy there!" {
-		t.Fatalf("expected body %s, got %s", "Howdy there!", string(resp.Body.Bytes()))
+	if resp.Body.String() != "Howdy there!" {
+		t.Fatalf("expected body %s, got %s", "Howdy there!", resp.Body.String())
 	}
 
 	out := &dto.Metric{}
@@ -95,7 +138,7 @@ func TestInstrumentHandler(t *testing.T) {
 	}
 
 	out.Reset()
-	if want, got := 1, len(reqCnt.children); want != got {
+	if want, got := 1, len(reqCnt.metricMap.metrics); want != got {
 		t.Errorf("want %d children in reqCnt, got %d", want, got)
 	}
 	cnt, err := reqCnt.GetMetricWithLabelValues("get", "418")
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/internal/metric.go b/vendor/github.com/prometheus/client_golang/prometheus/internal/metric.go
new file mode 100644
index 0000000000000000000000000000000000000000..351c26e1aedb4ad9c0f78200757d8c1e946fc00b
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/internal/metric.go
@@ -0,0 +1,85 @@
+// 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
new file mode 100644
index 0000000000000000000000000000000000000000..2744443ac228b3d8203887f2cb3bd84b0f3ac07d
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/labels.go
@@ -0,0 +1,87 @@
+// 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 d4063d98f4172b800ca5a45a03b52a5f710e94f4..55e6d86d596f74a619efba25ff047b6d9123f7b1 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/metric.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/metric.go
@@ -15,6 +15,9 @@ package prometheus
 
 import (
 	"strings"
+	"time"
+
+	"github.com/golang/protobuf/proto"
 
 	dto "github.com/prometheus/client_model/go"
 )
@@ -43,9 +46,8 @@ 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. (Implementers may find
-	// LabelPairSorter useful for that.) Callers of Write should still make
-	// sure of sorting if they depend on it.
+	// recommended to sort labels lexicographically. 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
@@ -57,8 +59,9 @@ 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 and Help to a non-empty string. All other fields
-// are optional and can safely be left at their zero value.
+// 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.
 type Opts struct {
 	// Namespace, Subsystem, and Name are components of the fully-qualified
 	// name of the Metric (created by joining these components with
@@ -69,7 +72,7 @@ type Opts struct {
 	Subsystem string
 	Name      string
 
-	// Help provides information about this metric. Mandatory!
+	// Help provides information about this metric.
 	//
 	// Metrics with the same fully-qualified name must have the same Help
 	// string.
@@ -79,20 +82,12 @@ type Opts struct {
 	// with the same fully-qualified name must have the same label names in
 	// their 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 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 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
 	ConstLabels Labels
 }
 
@@ -118,37 +113,22 @@ func BuildFQName(namespace, subsystem, name string) string {
 	return name
 }
 
-// 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
+// labelPairSorter implements sort.Interface. It is used to sort a slice of
+// dto.LabelPair pointers.
+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
@@ -164,3 +144,31 @@ 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
new file mode 100644
index 0000000000000000000000000000000000000000..5806cd09e306cdd9c73df71839fc1273a5e2fc3f
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/observer.go
@@ -0,0 +1,52 @@
+// 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 e31e62e78d0d12903facc726498c44bf1d48ff68..55176d58ce6a213fe4008f6b2f8630709bd031cc 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
@@ -13,89 +13,139 @@
 
 package prometheus
 
-import "github.com/prometheus/procfs"
+import (
+	"errors"
+	"os"
+
+	"github.com/prometheus/procfs"
+)
 
 type processCollector struct {
-	pid             int
 	collectFn       func(chan<- Metric)
 	pidFn           func() (int, error)
-	cpuTotal        Counter
-	openFDs, maxFDs Gauge
-	vsize, rss      Gauge
-	startTime       Gauge
+	reportErrors    bool
+	cpuTotal        *Desc
+	openFDs, maxFDs *Desc
+	vsize, maxVsize *Desc
+	rss             *Desc
+	startTime       *Desc
 }
 
-// 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 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,
-	)
+// 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
 }
 
-// NewProcessCollectorPIDFn returns a collector which exports the current state
-// of process metrics including cpu, memory and file descriptor usage as well
-// as the process start time under the given namespace. The given pidFn is
-// called on each collect and is used to determine the process to export
-// metrics for.
-func NewProcessCollectorPIDFn(
-	pidFn func() (int, error),
-	namespace string,
-) Collector {
-	c := processCollector{
-		pidFn:     pidFn,
-		collectFn: func(chan<- Metric) {},
-
-		cpuTotal: NewCounter(CounterOpts{
-			Namespace: namespace,
-			Name:      "process_cpu_seconds_total",
-			Help:      "Total user and system CPU time spent in seconds.",
-		}),
-		openFDs: NewGauge(GaugeOpts{
-			Namespace: namespace,
-			Name:      "process_open_fds",
-			Help:      "Number of open file descriptors.",
-		}),
-		maxFDs: NewGauge(GaugeOpts{
-			Namespace: namespace,
-			Name:      "process_max_fds",
-			Help:      "Maximum number of open file descriptors.",
-		}),
-		vsize: NewGauge(GaugeOpts{
-			Namespace: namespace,
-			Name:      "process_virtual_memory_bytes",
-			Help:      "Virtual memory size in bytes.",
-		}),
-		rss: NewGauge(GaugeOpts{
-			Namespace: namespace,
-			Name:      "process_resident_memory_bytes",
-			Help:      "Resident memory size in bytes.",
-		}),
-		startTime: NewGauge(GaugeOpts{
-			Namespace: namespace,
-			Name:      "process_start_time_seconds",
-			Help:      "Start time of the process since unix epoch in seconds.",
-		}),
+// NewProcessCollector returns a collector which exports the current state of
+// process metrics including CPU, memory and file descriptor usage as well as
+// the process start time. The detailed behavior is defined by the provided
+// ProcessCollectorOpts. The zero value of ProcessCollectorOpts creates a
+// collector for the current process with an empty namespace string and no error
+// reporting.
+//
+// Currently, the collector depends on a Linux-style proc filesystem and
+// therefore only exports metrics for Linux.
+//
+// Note: An older version of this function had the following signature:
+//
+//     NewProcessCollector(pid int, namespace string) Collector
+//
+// Most commonly, it was called as
+//
+//     NewProcessCollector(os.Getpid(), "")
+//
+// The following call of the current version is equivalent to the above:
+//
+//     NewProcessCollector(ProcessCollectorOpts{})
+func NewProcessCollector(opts ProcessCollectorOpts) Collector {
+	ns := ""
+	if len(opts.Namespace) > 0 {
+		ns = opts.Namespace + "_"
+	}
+
+	c := &processCollector{
+		reportErrors: opts.ReportErrors,
+		cpuTotal: NewDesc(
+			ns+"process_cpu_seconds_total",
+			"Total user and system CPU time spent in seconds.",
+			nil, nil,
+		),
+		openFDs: NewDesc(
+			ns+"process_open_fds",
+			"Number of open file descriptors.",
+			nil, nil,
+		),
+		maxFDs: NewDesc(
+			ns+"process_max_fds",
+			"Maximum number of open file descriptors.",
+			nil, nil,
+		),
+		vsize: NewDesc(
+			ns+"process_virtual_memory_bytes",
+			"Virtual memory size in bytes.",
+			nil, nil,
+		),
+		maxVsize: NewDesc(
+			ns+"process_virtual_memory_max_bytes",
+			"Maximum amount of virtual memory available in bytes.",
+			nil, nil,
+		),
+		rss: NewDesc(
+			ns+"process_resident_memory_bytes",
+			"Resident memory size in bytes.",
+			nil, nil,
+		),
+		startTime: NewDesc(
+			ns+"process_start_time_seconds",
+			"Start time of the process since unix epoch in seconds.",
+			nil, nil,
+		),
+	}
+
+	if opts.PidFn == nil {
+		pid := os.Getpid()
+		c.pidFn = func() (int, error) { return pid, nil }
+	} else {
+		c.pidFn = opts.PidFn
 	}
 
 	// 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.Desc()
-	ch <- c.openFDs.Desc()
-	ch <- c.maxFDs.Desc()
-	ch <- c.vsize.Desc()
-	ch <- c.rss.Desc()
-	ch <- c.startTime.Desc()
+	ch <- c.cpuTotal
+	ch <- c.openFDs
+	ch <- c.maxFDs
+	ch <- c.vsize
+	ch <- c.maxVsize
+	ch <- c.rss
+	ch <- c.startTime
 }
 
 // Collect returns the current state of all metrics of the collector.
@@ -103,40 +153,52 @@ 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 {
-		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
-
+		ch <- MustNewConstMetric(c.cpuTotal, CounterValue, stat.CPUTime())
+		ch <- MustNewConstMetric(c.vsize, GaugeValue, float64(stat.VirtualMemory()))
+		ch <- MustNewConstMetric(c.rss, GaugeValue, float64(stat.ResidentMemory()))
 		if startTime, err := stat.StartTime(); err == nil {
-			c.startTime.Set(startTime)
-			ch <- c.startTime
+			ch <- MustNewConstMetric(c.startTime, GaugeValue, startTime)
+		} else {
+			c.reportError(ch, c.startTime, err)
 		}
+	} else {
+		c.reportError(ch, nil, err)
 	}
 
 	if fds, err := p.FileDescriptorsLen(); err == nil {
-		c.openFDs.Set(float64(fds))
-		ch <- c.openFDs
+		ch <- MustNewConstMetric(c.openFDs, GaugeValue, float64(fds))
+	} else {
+		c.reportError(ch, c.openFDs, err)
 	}
 
 	if limits, err := p.NewLimits(); err == nil {
-		c.maxFDs.Set(float64(limits.OpenFiles))
-		ch <- c.maxFDs
+		ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(limits.OpenFiles))
+		ch <- MustNewConstMetric(c.maxVsize, GaugeValue, float64(limits.AddressSpace))
+	} else {
+		c.reportError(ch, nil, err)
+	}
+}
+
+func (c *processCollector) reportError(ch chan<- Metric, desc *Desc, err error) {
+	if !c.reportErrors {
+		return
+	}
+	if desc == nil {
+		desc = NewInvalidDesc(err)
 	}
+	ch <- NewInvalidMetric(desc, err)
 }
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_test.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_test.go
index d3362dae72e5dd4615137418d4f2b997f637145d..8651d4f1337c695e30bfeb0b856aea8c3791a058 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_test.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_test.go
@@ -1,13 +1,31 @@
+// 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.
+
+// +build linux
+
 package prometheus
 
 import (
 	"bytes"
+	"errors"
 	"os"
 	"regexp"
 	"testing"
 
 	"github.com/prometheus/common/expfmt"
 	"github.com/prometheus/procfs"
+
+	dto "github.com/prometheus/client_model/go"
 )
 
 func TestProcessCollector(t *testing.T) {
@@ -16,12 +34,14 @@ func TestProcessCollector(t *testing.T) {
 	}
 
 	registry := NewRegistry()
-	if err := registry.Register(NewProcessCollector(os.Getpid(), "")); err != nil {
+	if err := registry.Register(NewProcessCollector(ProcessCollectorOpts{})); err != nil {
 		t.Fatal(err)
 	}
-	if err := registry.Register(NewProcessCollectorPIDFn(
-		func() (int, error) { return os.Getpid(), nil }, "foobar"),
-	); err != nil {
+	if err := registry.Register(NewProcessCollector(ProcessCollectorOpts{
+		PidFn:        func() (int, error) { return os.Getpid(), nil },
+		Namespace:    "foobar",
+		ReportErrors: true, // No errors expected, just to see if none are reported.
+	})); err != nil {
 		t.Fatal(err)
 	}
 
@@ -38,21 +58,46 @@ func TestProcessCollector(t *testing.T) {
 	}
 
 	for _, re := range []*regexp.Regexp{
-		regexp.MustCompile("process_cpu_seconds_total [0-9]"),
-		regexp.MustCompile("process_max_fds [1-9]"),
-		regexp.MustCompile("process_open_fds [1-9]"),
-		regexp.MustCompile("process_virtual_memory_bytes [1-9]"),
-		regexp.MustCompile("process_resident_memory_bytes [1-9]"),
-		regexp.MustCompile("process_start_time_seconds [0-9.]{10,}"),
-		regexp.MustCompile("foobar_process_cpu_seconds_total [0-9]"),
-		regexp.MustCompile("foobar_process_max_fds [1-9]"),
-		regexp.MustCompile("foobar_process_open_fds [1-9]"),
-		regexp.MustCompile("foobar_process_virtual_memory_bytes [1-9]"),
-		regexp.MustCompile("foobar_process_resident_memory_bytes [1-9]"),
-		regexp.MustCompile("foobar_process_start_time_seconds [0-9.]{10,}"),
+		regexp.MustCompile("\nprocess_cpu_seconds_total [0-9]"),
+		regexp.MustCompile("\nprocess_max_fds [1-9]"),
+		regexp.MustCompile("\nprocess_open_fds [1-9]"),
+		regexp.MustCompile("\nprocess_virtual_memory_max_bytes (-1|[1-9])"),
+		regexp.MustCompile("\nprocess_virtual_memory_bytes [1-9]"),
+		regexp.MustCompile("\nprocess_resident_memory_bytes [1-9]"),
+		regexp.MustCompile("\nprocess_start_time_seconds [0-9.]{10,}"),
+		regexp.MustCompile("\nfoobar_process_cpu_seconds_total [0-9]"),
+		regexp.MustCompile("\nfoobar_process_max_fds [1-9]"),
+		regexp.MustCompile("\nfoobar_process_open_fds [1-9]"),
+		regexp.MustCompile("\nfoobar_process_virtual_memory_max_bytes (-1|[1-9])"),
+		regexp.MustCompile("\nfoobar_process_virtual_memory_bytes [1-9]"),
+		regexp.MustCompile("\nfoobar_process_resident_memory_bytes [1-9]"),
+		regexp.MustCompile("\nfoobar_process_start_time_seconds [0-9.]{10,}"),
 	} {
 		if !re.Match(buf.Bytes()) {
 			t.Errorf("want body to match %s\n%s", re, buf.String())
 		}
 	}
+
+	brokenProcessCollector := NewProcessCollector(ProcessCollectorOpts{
+		PidFn:        func() (int, error) { return 0, errors.New("boo") },
+		ReportErrors: true,
+	})
+
+	ch := make(chan Metric)
+	go func() {
+		brokenProcessCollector.Collect(ch)
+		close(ch)
+	}()
+	n := 0
+	for m := range ch {
+		n++
+		pb := &dto.Metric{}
+		err := m.Write(pb)
+		if err == nil {
+			t.Error("metric collected from broken process collector is unexpectedly valid")
+		}
+	}
+	if n != 1 {
+		t.Errorf("%d metrics collected, want 1", n)
+	}
 }
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promauto/auto.go b/vendor/github.com/prometheus/client_golang/prometheus/promauto/auto.go
new file mode 100644
index 0000000000000000000000000000000000000000..a00ba1eb83d7bc7a487d95a56a6b5b0749aa6727
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/promauto/auto.go
@@ -0,0 +1,223 @@
+// 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 promauto provides constructors for the usual Prometheus metrics that
+// return them already registered with the global registry
+// (prometheus.DefaultRegisterer). This allows very compact code, avoiding any
+// references to the registry altogether, but all the constructors in this
+// package will panic if the registration fails.
+//
+// The following example is a complete program to create a histogram of normally
+// distributed random numbers from the math/rand package:
+//
+//      package main
+//
+//      import (
+//              "math/rand"
+//              "net/http"
+//
+//              "github.com/prometheus/client_golang/prometheus"
+//              "github.com/prometheus/client_golang/prometheus/promauto"
+//              "github.com/prometheus/client_golang/prometheus/promhttp"
+//      )
+//
+//      var histogram = promauto.NewHistogram(prometheus.HistogramOpts{
+//              Name:    "random_numbers",
+//              Help:    "A histogram of normally distributed random numbers.",
+//              Buckets: prometheus.LinearBuckets(-3, .1, 61),
+//      })
+//
+//      func Random() {
+//              for {
+//                      histogram.Observe(rand.NormFloat64())
+//              }
+//      }
+//
+//      func main() {
+//              go Random()
+//              http.Handle("/metrics", promhttp.Handler())
+//              http.ListenAndServe(":1971", nil)
+//      }
+//
+// Prometheus's version of a minimal hello-world program:
+//
+//      package main
+//
+//      import (
+//      	"fmt"
+//      	"net/http"
+//
+//      	"github.com/prometheus/client_golang/prometheus"
+//      	"github.com/prometheus/client_golang/prometheus/promauto"
+//      	"github.com/prometheus/client_golang/prometheus/promhttp"
+//      )
+//
+//      func main() {
+//      	http.Handle("/", promhttp.InstrumentHandlerCounter(
+//      		promauto.NewCounterVec(
+//      			prometheus.CounterOpts{
+//      				Name: "hello_requests_total",
+//      				Help: "Total number of hello-world requests by HTTP code.",
+//      			},
+//      			[]string{"code"},
+//      		),
+//      		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+//      			fmt.Fprint(w, "Hello, world!")
+//      		}),
+//      	))
+//      	http.Handle("/metrics", promhttp.Handler())
+//      	http.ListenAndServe(":1971", nil)
+//      }
+//
+// This appears very handy. So why are these constructors locked away in a
+// separate package? There are two caveats:
+//
+// First, in more complex programs, global state is often quite problematic.
+// That's the reason why the metrics constructors in the prometheus package do
+// not interact with the global prometheus.DefaultRegisterer on their own. You
+// are free to use the Register or MustRegister functions to register them with
+// the global prometheus.DefaultRegisterer, but you could as well choose a local
+// Registerer (usually created with prometheus.NewRegistry, but there are other
+// scenarios, e.g. testing).
+//
+// The second issue is that registration may fail, e.g. if a metric inconsistent
+// with the newly to be registered one is already registered. But how to signal
+// and handle a panic in the automatic registration with the default registry?
+// The only way is panicking. While panicking on invalid input provided by the
+// programmer is certainly fine, things are a bit more subtle in this case: You
+// might just add another package to the program, and that package (in its init
+// function) happens to register a metric with the same name as your code. Now,
+// all of a sudden, either your code or the code of the newly imported package
+// panics, depending on initialization order, without any opportunity to handle
+// the case gracefully. Even worse is a scenario where registration happens
+// later during the runtime (e.g. upon loading some kind of plugin), where the
+// panic could be triggered long after the code has been deployed to
+// production. A possibility to panic should be explicitly called out by the
+// Must… idiom, cf. prometheus.MustRegister. But adding a separate set of
+// constructors in the prometheus package called MustRegisterNewCounterVec or
+// similar would be quite unwieldy. Adding an extra MustRegister method to each
+// metric, returning the registered metric, would result in nice code for those
+// using the method, but would pollute every single metric interface for
+// everybody avoiding the global registry.
+//
+// To address both issues, the problematic auto-registering and possibly
+// panicking constructors are all in this package with a clear warning
+// ahead. And whoever cares about avoiding global state and possibly panicking
+// function calls can simply ignore the existence of the promauto package
+// altogether.
+//
+// A final note: There is a similar case in the net/http package of the standard
+// library. It has DefaultServeMux as a global instance of ServeMux, and the
+// Handle function acts on it, panicking if a handler for the same pattern has
+// already been registered. However, one might argue that the whole HTTP routing
+// is usually set up closely together in the same package or file, while
+// Prometheus metrics tend to be spread widely over the codebase, increasing the
+// chance of surprising registration failures. Furthermore, the use of global
+// state in net/http has been criticized widely, and some avoid it altogether.
+package promauto
+
+import "github.com/prometheus/client_golang/prometheus"
+
+// NewCounter works like the function of the same name in the prometheus package
+// but it automatically registers the Counter with the
+// prometheus.DefaultRegisterer. If the registration fails, NewCounter panics.
+func NewCounter(opts prometheus.CounterOpts) prometheus.Counter {
+	c := prometheus.NewCounter(opts)
+	prometheus.MustRegister(c)
+	return c
+}
+
+// NewCounterVec works like the function of the same name in the prometheus
+// package but it automatically registers the CounterVec with the
+// prometheus.DefaultRegisterer. If the registration fails, NewCounterVec
+// panics.
+func NewCounterVec(opts prometheus.CounterOpts, labelNames []string) *prometheus.CounterVec {
+	c := prometheus.NewCounterVec(opts, labelNames)
+	prometheus.MustRegister(c)
+	return c
+}
+
+// NewCounterFunc works like the function of the same name in the prometheus
+// package but it automatically registers the CounterFunc with the
+// prometheus.DefaultRegisterer. If the registration fails, NewCounterFunc
+// panics.
+func NewCounterFunc(opts prometheus.CounterOpts, function func() float64) prometheus.CounterFunc {
+	g := prometheus.NewCounterFunc(opts, function)
+	prometheus.MustRegister(g)
+	return g
+}
+
+// NewGauge works like the function of the same name in the prometheus package
+// but it automatically registers the Gauge with the
+// prometheus.DefaultRegisterer. If the registration fails, NewGauge panics.
+func NewGauge(opts prometheus.GaugeOpts) prometheus.Gauge {
+	g := prometheus.NewGauge(opts)
+	prometheus.MustRegister(g)
+	return g
+}
+
+// NewGaugeVec works like the function of the same name in the prometheus
+// package but it automatically registers the GaugeVec with the
+// prometheus.DefaultRegisterer. If the registration fails, NewGaugeVec panics.
+func NewGaugeVec(opts prometheus.GaugeOpts, labelNames []string) *prometheus.GaugeVec {
+	g := prometheus.NewGaugeVec(opts, labelNames)
+	prometheus.MustRegister(g)
+	return g
+}
+
+// NewGaugeFunc works like the function of the same name in the prometheus
+// package but it automatically registers the GaugeFunc with the
+// prometheus.DefaultRegisterer. If the registration fails, NewGaugeFunc panics.
+func NewGaugeFunc(opts prometheus.GaugeOpts, function func() float64) prometheus.GaugeFunc {
+	g := prometheus.NewGaugeFunc(opts, function)
+	prometheus.MustRegister(g)
+	return g
+}
+
+// NewSummary works like the function of the same name in the prometheus package
+// but it automatically registers the Summary with the
+// prometheus.DefaultRegisterer. If the registration fails, NewSummary panics.
+func NewSummary(opts prometheus.SummaryOpts) prometheus.Summary {
+	s := prometheus.NewSummary(opts)
+	prometheus.MustRegister(s)
+	return s
+}
+
+// NewSummaryVec works like the function of the same name in the prometheus
+// package but it automatically registers the SummaryVec with the
+// prometheus.DefaultRegisterer. If the registration fails, NewSummaryVec
+// panics.
+func NewSummaryVec(opts prometheus.SummaryOpts, labelNames []string) *prometheus.SummaryVec {
+	s := prometheus.NewSummaryVec(opts, labelNames)
+	prometheus.MustRegister(s)
+	return s
+}
+
+// NewHistogram works like the function of the same name in the prometheus
+// package but it automatically registers the Histogram with the
+// prometheus.DefaultRegisterer. If the registration fails, NewHistogram panics.
+func NewHistogram(opts prometheus.HistogramOpts) prometheus.Histogram {
+	h := prometheus.NewHistogram(opts)
+	prometheus.MustRegister(h)
+	return h
+}
+
+// NewHistogramVec works like the function of the same name in the prometheus
+// package but it automatically registers the HistogramVec with the
+// prometheus.DefaultRegisterer. If the registration fails, NewHistogramVec
+// panics.
+func NewHistogramVec(opts prometheus.HistogramOpts, labelNames []string) *prometheus.HistogramVec {
+	h := prometheus.NewHistogramVec(opts, labelNames)
+	prometheus.MustRegister(h)
+	return h
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go
new file mode 100644
index 0000000000000000000000000000000000000000..67b56d37cfd726f0fb7b3c75013d4e4e5e411584
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go
@@ -0,0 +1,199 @@
+// 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
new file mode 100644
index 0000000000000000000000000000000000000000..31a70695695c59b58609554743f13ff8a8e89cdd
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_1_8.go
@@ -0,0 +1,181 @@
+// 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
new file mode 100644
index 0000000000000000000000000000000000000000..8bb9b8b68f8b4a721d75b2d6e3e4f22af9088489
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_pre_1_8.go
@@ -0,0 +1,44 @@
+// 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 b6dd5a266c653d3559e7baf35013f548e28f80e5..668eb6b3c93aebb9f1fe45f86a8b2195bb9d69da 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go
@@ -11,31 +11,34 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Copyright (c) 2013, The Prometheus Authors
-// All rights reserved.
+// Package promhttp provides tooling around HTTP servers and clients.
 //
-// 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.
+// First, the package allows the creation of http.Handler instances to expose
+// Prometheus metrics via HTTP. promhttp.Handler acts on the
+// prometheus.DefaultGatherer. With HandlerFor, you can create a handler for a
+// custom registry or anything that implements the Gatherer interface. It also
+// allows the creation of handlers that act differently on errors or allow to
+// log errors.
+//
+// Second, the package provides tooling to instrument instances of http.Handler
+// via middleware. Middleware wrappers follow the naming scheme
+// InstrumentHandlerX, where X describes the intended use of the middleware.
+// See each function's doc comment for specific details.
 //
-// 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.
+// 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.
 package promhttp
 
 import (
-	"bytes"
 	"compress/gzip"
 	"fmt"
 	"io"
 	"net/http"
 	"strings"
 	"sync"
+	"time"
 
 	"github.com/prometheus/common/expfmt"
 
@@ -49,36 +52,56 @@ const (
 	acceptEncodingHeader  = "Accept-Encoding"
 )
 
-var bufPool sync.Pool
-
-func getBuf() *bytes.Buffer {
-	buf := bufPool.Get()
-	if buf == nil {
-		return &bytes.Buffer{}
-	}
-	return buf.(*bytes.Buffer)
+var gzipPool = sync.Pool{
+	New: func() interface{} {
+		return gzip.NewWriter(nil)
+	},
 }
 
-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.
+// 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.
 //
-// If you want to create a Handler for the DefaultGatherer with different
-// HandlerOpts, create it with HandlerFor with prometheus.DefaultGatherer and
-// your desired HandlerOpts.
+// 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 HandlerFor(prometheus.DefaultGatherer, HandlerOpts{})
+	return InstrumentMetricHandler(
+		prometheus.DefaultRegisterer, HandlerFor(prometheus.DefaultGatherer, HandlerOpts{}),
+	)
 }
 
-// HandlerFor returns an http.Handler for the provided Gatherer. The behavior
-// of the Handler is defined by the provided 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 {
-	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+	var inFlightSem chan struct{}
+	if opts.MaxRequestsInFlight > 0 {
+		inFlightSem = make(chan struct{}, opts.MaxRequestsInFlight)
+	}
+
+	h := http.HandlerFunc(func(rsp http.ResponseWriter, req *http.Request) {
+		if inFlightSem != nil {
+			select {
+			case inFlightSem <- struct{}{}: // All good, carry on.
+				defer func() { <-inFlightSem }()
+			default:
+				http.Error(rsp, fmt.Sprintf(
+					"Limit of concurrent requests reached (%d), try again later.", opts.MaxRequestsInFlight,
+				), http.StatusServiceUnavailable)
+				return
+			}
+		}
 		mfs, err := reg.Gather()
 		if err != nil {
 			if opts.ErrorLog != nil {
@@ -89,26 +112,40 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
 				panic(err)
 			case ContinueOnError:
 				if len(mfs) == 0 {
-					http.Error(w, "No metrics gathered, last error:\n\n"+err.Error(), http.StatusInternalServerError)
+					// Still report the error if no metrics have been gathered.
+					httpError(rsp, err)
 					return
 				}
 			case HTTPErrorOnError:
-				http.Error(w, "An error has occurred during metrics gathering:\n\n"+err.Error(), http.StatusInternalServerError)
+				httpError(rsp, err)
 				return
 			}
 		}
 
 		contentType := expfmt.Negotiate(req.Header)
-		buf := getBuf()
-		defer giveBuf(buf)
-		writer, encoding := decorateWriter(req, buf, opts.DisableCompression)
-		enc := expfmt.NewEncoder(writer, contentType)
+		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)
+
 		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 metric family:", err)
+					opts.ErrorLog.Println("error encoding and sending metric family:", err)
 				}
 				switch opts.ErrorHandling {
 				case PanicOnError:
@@ -116,27 +153,75 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
 				case ContinueOnError:
 					// Handled later.
 				case HTTPErrorOnError:
-					http.Error(w, "An error has occurred during metrics encoding:\n\n"+err.Error(), http.StatusInternalServerError)
+					httpError(rsp, err)
 					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
+
+		if lastErr != nil {
+			httpError(rsp, lastErr)
 		}
-		header := w.Header()
-		header.Set(contentTypeHeader, string(contentType))
-		header.Set(contentLengthHeader, fmt.Sprint(buf.Len()))
-		if encoding != "" {
-			header.Set(contentEncodingHeader, encoding)
+	})
+
+	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)
 		}
-		w.Write(buf.Bytes())
-		// TODO(beorn7): Consider streaming serving of metrics.
+	}
+
+	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
@@ -180,22 +265,47 @@ 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
 }
 
-// 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, ",")
+// 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)
+		part = strings.TrimSpace(part)
 		if part == "gzip" || strings.HasPrefix(part, "gzip;") {
-			return gzip.NewWriter(writer), "gzip"
+			return true
 		}
 	}
-	return writer, ""
+	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/promhttp/http_test.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http_test.go
index d4a7d4a7b57f9110726dfb3427c98f4111168ac8..6e23e6c6dc2e2c6753e67a62d4398a1e6488d1e4 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http_test.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http_test.go
@@ -11,12 +11,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Copyright (c) 2013, The Prometheus Authors
-// All rights reserved.
-//
-// Use of this source code is governed by a BSD-style license that can be found
-// in the LICENSE file.
-
 package promhttp
 
 import (
@@ -25,7 +19,9 @@ import (
 	"log"
 	"net/http"
 	"net/http/httptest"
+	"strings"
 	"testing"
+	"time"
 
 	"github.com/prometheus/client_golang/prometheus"
 )
@@ -43,6 +39,23 @@ func (e errorCollector) Collect(ch chan<- prometheus.Metric) {
 	)
 }
 
+type blockingCollector struct {
+	CollectStarted, Block chan struct{}
+}
+
+func (b blockingCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- prometheus.NewDesc("dummy_desc", "not helpful", nil, nil)
+}
+
+func (b blockingCollector) Collect(ch chan<- prometheus.Metric) {
+	select {
+	case b.CollectStarted <- struct{}{}:
+	default:
+	}
+	// Collects nothing, just waits for a channel receive.
+	<-b.Block
+}
+
 func TestHandlerErrorHandling(t *testing.T) {
 
 	// Create a registry that collects a MetricFamily with two elements,
@@ -90,7 +103,7 @@ func TestHandlerErrorHandling(t *testing.T) {
 	})
 	wantMsg := `error gathering metrics: error collecting metric Desc{fqName: "invalid_metric", help: "not helpful", constLabels: {}, variableLabels: []}: collect error
 `
-	wantErrorBody := `An error has occurred during metrics gathering:
+	wantErrorBody := `An error has occurred while serving metrics:
 
 error collecting metric Desc{fqName: "invalid_metric", help: "not helpful", constLabels: {}, variableLabels: []}: collect error
 `
@@ -108,7 +121,7 @@ the_count 0
 		t.Errorf("got HTTP status code %d, want %d", got, want)
 	}
 	if got := logBuf.String(); got != wantMsg {
-		t.Errorf("got log message:\n%s\nwant log mesage:\n%s\n", got, wantMsg)
+		t.Errorf("got log message:\n%s\nwant log message:\n%s\n", got, wantMsg)
 	}
 	if got := writer.Body.String(); got != wantErrorBody {
 		t.Errorf("got body:\n%s\nwant body:\n%s\n", got, wantErrorBody)
@@ -135,3 +148,103 @@ the_count 0
 	}()
 	panicHandler.ServeHTTP(writer, request)
 }
+
+func TestInstrumentMetricHandler(t *testing.T) {
+	reg := prometheus.NewRegistry()
+	handler := InstrumentMetricHandler(reg, HandlerFor(reg, HandlerOpts{}))
+	// Do it again to test idempotency.
+	InstrumentMetricHandler(reg, HandlerFor(reg, HandlerOpts{}))
+	writer := httptest.NewRecorder()
+	request, _ := http.NewRequest("GET", "/", nil)
+	request.Header.Add("Accept", "test/plain")
+
+	handler.ServeHTTP(writer, request)
+	if got, want := writer.Code, http.StatusOK; got != want {
+		t.Errorf("got HTTP status code %d, want %d", got, want)
+	}
+
+	want := "promhttp_metric_handler_requests_in_flight 1\n"
+	if got := writer.Body.String(); !strings.Contains(got, want) {
+		t.Errorf("got body %q, does not contain %q", got, want)
+	}
+	want = "promhttp_metric_handler_requests_total{code=\"200\"} 0\n"
+	if got := writer.Body.String(); !strings.Contains(got, want) {
+		t.Errorf("got body %q, does not contain %q", got, want)
+	}
+
+	writer.Body.Reset()
+	handler.ServeHTTP(writer, request)
+	if got, want := writer.Code, http.StatusOK; got != want {
+		t.Errorf("got HTTP status code %d, want %d", got, want)
+	}
+
+	want = "promhttp_metric_handler_requests_in_flight 1\n"
+	if got := writer.Body.String(); !strings.Contains(got, want) {
+		t.Errorf("got body %q, does not contain %q", got, want)
+	}
+	want = "promhttp_metric_handler_requests_total{code=\"200\"} 1\n"
+	if got := writer.Body.String(); !strings.Contains(got, want) {
+		t.Errorf("got body %q, does not contain %q", got, want)
+	}
+}
+
+func TestHandlerMaxRequestsInFlight(t *testing.T) {
+	reg := prometheus.NewRegistry()
+	handler := HandlerFor(reg, HandlerOpts{MaxRequestsInFlight: 1})
+	w1 := httptest.NewRecorder()
+	w2 := httptest.NewRecorder()
+	w3 := httptest.NewRecorder()
+	request, _ := http.NewRequest("GET", "/", nil)
+	request.Header.Add("Accept", "test/plain")
+
+	c := blockingCollector{Block: make(chan struct{}), CollectStarted: make(chan struct{}, 1)}
+	reg.MustRegister(c)
+
+	rq1Done := make(chan struct{})
+	go func() {
+		handler.ServeHTTP(w1, request)
+		close(rq1Done)
+	}()
+	<-c.CollectStarted
+
+	handler.ServeHTTP(w2, request)
+
+	if got, want := w2.Code, http.StatusServiceUnavailable; got != want {
+		t.Errorf("got HTTP status code %d, want %d", got, want)
+	}
+	if got, want := w2.Body.String(), "Limit of concurrent requests reached (1), try again later.\n"; got != want {
+		t.Errorf("got body %q, want %q", got, want)
+	}
+
+	close(c.Block)
+	<-rq1Done
+
+	handler.ServeHTTP(w3, request)
+
+	if got, want := w3.Code, http.StatusOK; got != want {
+		t.Errorf("got HTTP status code %d, want %d", got, want)
+	}
+}
+
+func TestHandlerTimeout(t *testing.T) {
+	reg := prometheus.NewRegistry()
+	handler := HandlerFor(reg, HandlerOpts{Timeout: time.Millisecond})
+	w := httptest.NewRecorder()
+
+	request, _ := http.NewRequest("GET", "/", nil)
+	request.Header.Add("Accept", "test/plain")
+
+	c := blockingCollector{Block: make(chan struct{}), CollectStarted: make(chan struct{}, 1)}
+	reg.MustRegister(c)
+
+	handler.ServeHTTP(w, request)
+
+	if got, want := w.Code, http.StatusServiceUnavailable; got != want {
+		t.Errorf("got HTTP status code %d, want %d", got, want)
+	}
+	if got, want := w.Body.String(), "Exceeded configured timeout of 1ms.\n"; got != want {
+		t.Errorf("got body %q, want %q", got, want)
+	}
+
+	close(c.Block) // To not leak a goroutine.
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..86fd564470f8161f9696beab0bb51d00c69d1c1b
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go
@@ -0,0 +1,97 @@
+// 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
new file mode 100644
index 0000000000000000000000000000000000000000..a034d1ec0f189a54e6269a24d04c39cfc7ce63d2
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client_1_8.go
@@ -0,0 +1,144 @@
+// 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_client_1_8_test.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client_1_8_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..7e3f5229fedc84226e66bd7c65f5b87909eeeefb
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client_1_8_test.go
@@ -0,0 +1,195 @@
+// 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 (
+	"log"
+	"net/http"
+	"testing"
+	"time"
+
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+func TestClientMiddlewareAPI(t *testing.T) {
+	client := http.DefaultClient
+	client.Timeout = 1 * time.Second
+
+	reg := prometheus.NewRegistry()
+
+	inFlightGauge := prometheus.NewGauge(prometheus.GaugeOpts{
+		Name: "client_in_flight_requests",
+		Help: "A gauge of in-flight requests for the wrapped client.",
+	})
+
+	counter := prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Name: "client_api_requests_total",
+			Help: "A counter for requests from the wrapped client.",
+		},
+		[]string{"code", "method"},
+	)
+
+	dnsLatencyVec := prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Name:    "dns_duration_seconds",
+			Help:    "Trace dns latency histogram.",
+			Buckets: []float64{.005, .01, .025, .05},
+		},
+		[]string{"event"},
+	)
+
+	tlsLatencyVec := prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Name:    "tls_duration_seconds",
+			Help:    "Trace tls latency histogram.",
+			Buckets: []float64{.05, .1, .25, .5},
+		},
+		[]string{"event"},
+	)
+
+	histVec := prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Name:    "request_duration_seconds",
+			Help:    "A histogram of request latencies.",
+			Buckets: prometheus.DefBuckets,
+		},
+		[]string{"method"},
+	)
+
+	reg.MustRegister(counter, tlsLatencyVec, dnsLatencyVec, histVec, inFlightGauge)
+
+	trace := &InstrumentTrace{
+		DNSStart: func(t float64) {
+			dnsLatencyVec.WithLabelValues("dns_start")
+		},
+		DNSDone: func(t float64) {
+			dnsLatencyVec.WithLabelValues("dns_done")
+		},
+		TLSHandshakeStart: func(t float64) {
+			tlsLatencyVec.WithLabelValues("tls_handshake_start")
+		},
+		TLSHandshakeDone: func(t float64) {
+			tlsLatencyVec.WithLabelValues("tls_handshake_done")
+		},
+	}
+
+	client.Transport = InstrumentRoundTripperInFlight(inFlightGauge,
+		InstrumentRoundTripperCounter(counter,
+			InstrumentRoundTripperTrace(trace,
+				InstrumentRoundTripperDuration(histVec, http.DefaultTransport),
+			),
+		),
+	)
+
+	resp, err := client.Get("http://google.com")
+	if err != nil {
+		t.Fatalf("%v", err)
+	}
+	defer resp.Body.Close()
+}
+
+func ExampleInstrumentRoundTripperDuration() {
+	client := http.DefaultClient
+	client.Timeout = 1 * time.Second
+
+	inFlightGauge := prometheus.NewGauge(prometheus.GaugeOpts{
+		Name: "client_in_flight_requests",
+		Help: "A gauge of in-flight requests for the wrapped client.",
+	})
+
+	counter := prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Name: "client_api_requests_total",
+			Help: "A counter for requests from the wrapped client.",
+		},
+		[]string{"code", "method"},
+	)
+
+	// dnsLatencyVec uses custom buckets based on expected dns durations.
+	// It has an instance label "event", which is set in the
+	// DNSStart and DNSDonehook functions defined in the
+	// InstrumentTrace struct below.
+	dnsLatencyVec := prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Name:    "dns_duration_seconds",
+			Help:    "Trace dns latency histogram.",
+			Buckets: []float64{.005, .01, .025, .05},
+		},
+		[]string{"event"},
+	)
+
+	// tlsLatencyVec uses custom buckets based on expected tls durations.
+	// It has an instance label "event", which is set in the
+	// TLSHandshakeStart and TLSHandshakeDone hook functions defined in the
+	// InstrumentTrace struct below.
+	tlsLatencyVec := prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Name:    "tls_duration_seconds",
+			Help:    "Trace tls latency histogram.",
+			Buckets: []float64{.05, .1, .25, .5},
+		},
+		[]string{"event"},
+	)
+
+	// histVec has no labels, making it a zero-dimensional ObserverVec.
+	histVec := prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Name:    "request_duration_seconds",
+			Help:    "A histogram of request latencies.",
+			Buckets: prometheus.DefBuckets,
+		},
+		[]string{},
+	)
+
+	// Register all of the metrics in the standard registry.
+	prometheus.MustRegister(counter, tlsLatencyVec, dnsLatencyVec, histVec, inFlightGauge)
+
+	// Define functions for the available httptrace.ClientTrace hook
+	// functions that we want to instrument.
+	trace := &InstrumentTrace{
+		DNSStart: func(t float64) {
+			dnsLatencyVec.WithLabelValues("dns_start")
+		},
+		DNSDone: func(t float64) {
+			dnsLatencyVec.WithLabelValues("dns_done")
+		},
+		TLSHandshakeStart: func(t float64) {
+			tlsLatencyVec.WithLabelValues("tls_handshake_start")
+		},
+		TLSHandshakeDone: func(t float64) {
+			tlsLatencyVec.WithLabelValues("tls_handshake_done")
+		},
+	}
+
+	// Wrap the default RoundTripper with middleware.
+	roundTripper := InstrumentRoundTripperInFlight(inFlightGauge,
+		InstrumentRoundTripperCounter(counter,
+			InstrumentRoundTripperTrace(trace,
+				InstrumentRoundTripperDuration(histVec, http.DefaultTransport),
+			),
+		),
+	)
+
+	// Set the RoundTripper on our client.
+	client.Transport = roundTripper
+
+	resp, err := client.Get("http://google.com")
+	if err != nil {
+		log.Printf("error: %v", err)
+	}
+	defer resp.Body.Close()
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..9db24380533ad27f27d4f191a8e644f5ec4e1cde
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go
@@ -0,0 +1,447 @@
+// 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/promhttp/instrument_server_test.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..716c6f45e006df0ac298f3fac24977e4b30d7260
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server_test.go
@@ -0,0 +1,401 @@
+// 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 (
+	"io"
+	"log"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+func TestLabelCheck(t *testing.T) {
+	scenarios := map[string]struct {
+		varLabels     []string
+		constLabels   []string
+		curriedLabels []string
+		ok            bool
+	}{
+		"empty": {
+			varLabels:     []string{},
+			constLabels:   []string{},
+			curriedLabels: []string{},
+			ok:            true,
+		},
+		"code as single var label": {
+			varLabels:     []string{"code"},
+			constLabels:   []string{},
+			curriedLabels: []string{},
+			ok:            true,
+		},
+		"method as single var label": {
+			varLabels:     []string{"method"},
+			constLabels:   []string{},
+			curriedLabels: []string{},
+			ok:            true,
+		},
+		"cade and method as var labels": {
+			varLabels:     []string{"method", "code"},
+			constLabels:   []string{},
+			curriedLabels: []string{},
+			ok:            true,
+		},
+		"valid case with all labels used": {
+			varLabels:     []string{"code", "method"},
+			constLabels:   []string{"foo", "bar"},
+			curriedLabels: []string{"dings", "bums"},
+			ok:            true,
+		},
+		"unsupported var label": {
+			varLabels:     []string{"foo"},
+			constLabels:   []string{},
+			curriedLabels: []string{},
+			ok:            false,
+		},
+		"mixed var labels": {
+			varLabels:     []string{"method", "foo", "code"},
+			constLabels:   []string{},
+			curriedLabels: []string{},
+			ok:            false,
+		},
+		"unsupported var label but curried": {
+			varLabels:     []string{},
+			constLabels:   []string{},
+			curriedLabels: []string{"foo"},
+			ok:            true,
+		},
+		"mixed var labels but unsupported curried": {
+			varLabels:     []string{"code", "method"},
+			constLabels:   []string{},
+			curriedLabels: []string{"foo"},
+			ok:            true,
+		},
+		"supported label as const and curry": {
+			varLabels:     []string{},
+			constLabels:   []string{"code"},
+			curriedLabels: []string{"method"},
+			ok:            true,
+		},
+		"supported label as const and curry with unsupported as var": {
+			varLabels:     []string{"foo"},
+			constLabels:   []string{"code"},
+			curriedLabels: []string{"method"},
+			ok:            false,
+		},
+	}
+
+	for name, sc := range scenarios {
+		t.Run(name, func(t *testing.T) {
+			constLabels := prometheus.Labels{}
+			for _, l := range sc.constLabels {
+				constLabels[l] = "dummy"
+			}
+			c := prometheus.NewCounterVec(
+				prometheus.CounterOpts{
+					Name:        "c",
+					Help:        "c help",
+					ConstLabels: constLabels,
+				},
+				append(sc.varLabels, sc.curriedLabels...),
+			)
+			o := prometheus.ObserverVec(prometheus.NewHistogramVec(
+				prometheus.HistogramOpts{
+					Name:        "c",
+					Help:        "c help",
+					ConstLabels: constLabels,
+				},
+				append(sc.varLabels, sc.curriedLabels...),
+			))
+			for _, l := range sc.curriedLabels {
+				c = c.MustCurryWith(prometheus.Labels{l: "dummy"})
+				o = o.MustCurryWith(prometheus.Labels{l: "dummy"})
+			}
+
+			func() {
+				defer func() {
+					if err := recover(); err != nil {
+						if sc.ok {
+							t.Error("unexpected panic:", err)
+						}
+					} else if !sc.ok {
+						t.Error("expected panic")
+					}
+				}()
+				InstrumentHandlerCounter(c, nil)
+			}()
+			func() {
+				defer func() {
+					if err := recover(); err != nil {
+						if sc.ok {
+							t.Error("unexpected panic:", err)
+						}
+					} else if !sc.ok {
+						t.Error("expected panic")
+					}
+				}()
+				InstrumentHandlerDuration(o, nil)
+			}()
+			if sc.ok {
+				// Test if wantCode and wantMethod were detected correctly.
+				var wantCode, wantMethod bool
+				for _, l := range sc.varLabels {
+					if l == "code" {
+						wantCode = true
+					}
+					if l == "method" {
+						wantMethod = true
+					}
+				}
+				gotCode, gotMethod := checkLabels(c)
+				if gotCode != wantCode {
+					t.Errorf("wanted code=%t for counter, got code=%t", wantCode, gotCode)
+				}
+				if gotMethod != wantMethod {
+					t.Errorf("wanted method=%t for counter, got method=%t", wantMethod, gotMethod)
+				}
+				gotCode, gotMethod = checkLabels(o)
+				if gotCode != wantCode {
+					t.Errorf("wanted code=%t for observer, got code=%t", wantCode, gotCode)
+				}
+				if gotMethod != wantMethod {
+					t.Errorf("wanted method=%t for observer, got method=%t", wantMethod, gotMethod)
+				}
+			}
+		})
+	}
+}
+
+func TestMiddlewareAPI(t *testing.T) {
+	reg := prometheus.NewRegistry()
+
+	inFlightGauge := prometheus.NewGauge(prometheus.GaugeOpts{
+		Name: "in_flight_requests",
+		Help: "A gauge of requests currently being served by the wrapped handler.",
+	})
+
+	counter := prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Name: "api_requests_total",
+			Help: "A counter for requests to the wrapped handler.",
+		},
+		[]string{"code", "method"},
+	)
+
+	histVec := prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Name:        "response_duration_seconds",
+			Help:        "A histogram of request latencies.",
+			Buckets:     prometheus.DefBuckets,
+			ConstLabels: prometheus.Labels{"handler": "api"},
+		},
+		[]string{"method"},
+	)
+
+	writeHeaderVec := prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Name:        "write_header_duration_seconds",
+			Help:        "A histogram of time to first write latencies.",
+			Buckets:     prometheus.DefBuckets,
+			ConstLabels: prometheus.Labels{"handler": "api"},
+		},
+		[]string{},
+	)
+
+	responseSize := prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Name:    "push_request_size_bytes",
+			Help:    "A histogram of request sizes for requests.",
+			Buckets: []float64{200, 500, 900, 1500},
+		},
+		[]string{},
+	)
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Write([]byte("OK"))
+	})
+
+	reg.MustRegister(inFlightGauge, counter, histVec, responseSize, writeHeaderVec)
+
+	chain := InstrumentHandlerInFlight(inFlightGauge,
+		InstrumentHandlerCounter(counter,
+			InstrumentHandlerDuration(histVec,
+				InstrumentHandlerTimeToWriteHeader(writeHeaderVec,
+					InstrumentHandlerResponseSize(responseSize, handler),
+				),
+			),
+		),
+	)
+
+	r, _ := http.NewRequest("GET", "www.example.com", nil)
+	w := httptest.NewRecorder()
+	chain.ServeHTTP(w, r)
+}
+
+func TestInstrumentTimeToFirstWrite(t *testing.T) {
+	var i int
+	dobs := &responseWriterDelegator{
+		ResponseWriter: httptest.NewRecorder(),
+		observeWriteHeader: func(status int) {
+			i = status
+		},
+	}
+	d := newDelegator(dobs, nil)
+
+	d.WriteHeader(http.StatusOK)
+
+	if i != http.StatusOK {
+		t.Fatalf("failed to execute observeWriteHeader")
+	}
+}
+
+// testResponseWriter is an http.ResponseWriter that also implements
+// http.CloseNotifier, http.Flusher, and io.ReaderFrom.
+type testResponseWriter struct {
+	closeNotifyCalled, flushCalled, readFromCalled bool
+}
+
+func (t *testResponseWriter) Header() http.Header       { return nil }
+func (t *testResponseWriter) Write([]byte) (int, error) { return 0, nil }
+func (t *testResponseWriter) WriteHeader(int)           {}
+func (t *testResponseWriter) CloseNotify() <-chan bool {
+	t.closeNotifyCalled = true
+	return nil
+}
+func (t *testResponseWriter) Flush() { t.flushCalled = true }
+func (t *testResponseWriter) ReadFrom(io.Reader) (int64, error) {
+	t.readFromCalled = true
+	return 0, nil
+}
+
+// testFlusher is an http.ResponseWriter that also implements http.Flusher.
+type testFlusher struct {
+	flushCalled bool
+}
+
+func (t *testFlusher) Header() http.Header       { return nil }
+func (t *testFlusher) Write([]byte) (int, error) { return 0, nil }
+func (t *testFlusher) WriteHeader(int)           {}
+func (t *testFlusher) Flush()                    { t.flushCalled = true }
+
+func TestInterfaceUpgrade(t *testing.T) {
+	w := &testResponseWriter{}
+	d := newDelegator(w, nil)
+	d.(http.CloseNotifier).CloseNotify()
+	if !w.closeNotifyCalled {
+		t.Error("CloseNotify not called")
+	}
+	d.(http.Flusher).Flush()
+	if !w.flushCalled {
+		t.Error("Flush not called")
+	}
+	d.(io.ReaderFrom).ReadFrom(nil)
+	if !w.readFromCalled {
+		t.Error("ReadFrom not called")
+	}
+	if _, ok := d.(http.Hijacker); ok {
+		t.Error("delegator unexpectedly implements http.Hijacker")
+	}
+
+	f := &testFlusher{}
+	d = newDelegator(f, nil)
+	if _, ok := d.(http.CloseNotifier); ok {
+		t.Error("delegator unexpectedly implements http.CloseNotifier")
+	}
+	d.(http.Flusher).Flush()
+	if !w.flushCalled {
+		t.Error("Flush not called")
+	}
+	if _, ok := d.(io.ReaderFrom); ok {
+		t.Error("delegator unexpectedly implements io.ReaderFrom")
+	}
+	if _, ok := d.(http.Hijacker); ok {
+		t.Error("delegator unexpectedly implements http.Hijacker")
+	}
+}
+
+func ExampleInstrumentHandlerDuration() {
+	inFlightGauge := prometheus.NewGauge(prometheus.GaugeOpts{
+		Name: "in_flight_requests",
+		Help: "A gauge of requests currently being served by the wrapped handler.",
+	})
+
+	counter := prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Name: "api_requests_total",
+			Help: "A counter for requests to the wrapped handler.",
+		},
+		[]string{"code", "method"},
+	)
+
+	// duration is partitioned by the HTTP method and handler. It uses custom
+	// buckets based on the expected request duration.
+	duration := prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Name:    "request_duration_seconds",
+			Help:    "A histogram of latencies for requests.",
+			Buckets: []float64{.25, .5, 1, 2.5, 5, 10},
+		},
+		[]string{"handler", "method"},
+	)
+
+	// responseSize has no labels, making it a zero-dimensional
+	// ObserverVec.
+	responseSize := prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Name:    "response_size_bytes",
+			Help:    "A histogram of response sizes for requests.",
+			Buckets: []float64{200, 500, 900, 1500},
+		},
+		[]string{},
+	)
+
+	// Create the handlers that will be wrapped by the middleware.
+	pushHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Write([]byte("Push"))
+	})
+	pullHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Write([]byte("Pull"))
+	})
+
+	// Register all of the metrics in the standard registry.
+	prometheus.MustRegister(inFlightGauge, counter, duration, responseSize)
+
+	// Instrument the handlers with all the metrics, injecting the "handler"
+	// label by currying.
+	pushChain := InstrumentHandlerInFlight(inFlightGauge,
+		InstrumentHandlerDuration(duration.MustCurryWith(prometheus.Labels{"handler": "push"}),
+			InstrumentHandlerCounter(counter,
+				InstrumentHandlerResponseSize(responseSize, pushHandler),
+			),
+		),
+	)
+	pullChain := InstrumentHandlerInFlight(inFlightGauge,
+		InstrumentHandlerDuration(duration.MustCurryWith(prometheus.Labels{"handler": "pull"}),
+			InstrumentHandlerCounter(counter,
+				InstrumentHandlerResponseSize(responseSize, pullHandler),
+			),
+		),
+	)
+
+	http.Handle("/metrics", Handler())
+	http.Handle("/push", pushChain)
+	http.Handle("/pull", pullChain)
+
+	if err := http.ListenAndServe(":3000", nil); err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/push/deprecated.go b/vendor/github.com/prometheus/client_golang/prometheus/push/deprecated.go
new file mode 100644
index 0000000000000000000000000000000000000000..3d62b5725d3e762c6bfe76643119c72ad780f477
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/push/deprecated.go
@@ -0,0 +1,172 @@
+// 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 push
+
+// This file contains only deprecated code. Remove after v0.9 is released.
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"os"
+	"strings"
+
+	"github.com/prometheus/common/expfmt"
+	"github.com/prometheus/common/model"
+
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+// FromGatherer triggers a metric collection by the provided Gatherer (which is
+// usually implemented by a prometheus.Registry) and pushes all gathered metrics
+// to the Pushgateway specified by url, using the provided job name and the
+// (optional) further grouping labels (the grouping map may be nil). See the
+// Pushgateway documentation for detailed implications of the job and other
+// grouping labels. Neither the job name nor any grouping label value may
+// contain a "/". The metrics pushed must not contain a job label of their own
+// nor any of the grouping labels.
+//
+// You can use just host:port or ip:port as url, in which case 'http://' is
+// added automatically. You can also include the schema in the URL. However, do
+// not include the '/metrics/jobs/...' part.
+//
+// Note that all previously pushed metrics with the same job and other grouping
+// labels will be replaced with the metrics pushed by this call. (It uses HTTP
+// method 'PUT' to push to the Pushgateway.)
+//
+// Deprecated: Please use a Pusher created with New instead.
+func FromGatherer(job string, grouping map[string]string, url string, g prometheus.Gatherer) error {
+	return push(job, grouping, url, g, "PUT")
+}
+
+// AddFromGatherer works like FromGatherer, but only previously pushed metrics
+// with the same name (and the same job and other grouping labels) will be
+// replaced. (It uses HTTP method 'POST' to push to the Pushgateway.)
+//
+// Deprecated: Please use a Pusher created with New instead.
+func AddFromGatherer(job string, grouping map[string]string, url string, g prometheus.Gatherer) error {
+	return push(job, grouping, url, g, "POST")
+}
+
+func push(job string, grouping map[string]string, pushURL string, g prometheus.Gatherer, method string) error {
+	if !strings.Contains(pushURL, "://") {
+		pushURL = "http://" + pushURL
+	}
+	if strings.HasSuffix(pushURL, "/") {
+		pushURL = pushURL[:len(pushURL)-1]
+	}
+
+	if strings.Contains(job, "/") {
+		return fmt.Errorf("job contains '/': %s", job)
+	}
+	urlComponents := []string{url.QueryEscape(job)}
+	for ln, lv := range grouping {
+		if !model.LabelName(ln).IsValid() {
+			return fmt.Errorf("grouping label has invalid name: %s", ln)
+		}
+		if strings.Contains(lv, "/") {
+			return fmt.Errorf("value of grouping label %s contains '/': %s", ln, lv)
+		}
+		urlComponents = append(urlComponents, ln, lv)
+	}
+	pushURL = fmt.Sprintf("%s/metrics/job/%s", pushURL, strings.Join(urlComponents, "/"))
+
+	mfs, err := g.Gather()
+	if err != nil {
+		return err
+	}
+	buf := &bytes.Buffer{}
+	enc := expfmt.NewEncoder(buf, expfmt.FmtProtoDelim)
+	// Check for pre-existing grouping labels:
+	for _, mf := range mfs {
+		for _, m := range mf.GetMetric() {
+			for _, l := range m.GetLabel() {
+				if l.GetName() == "job" {
+					return fmt.Errorf("pushed metric %s (%s) already contains a job label", mf.GetName(), m)
+				}
+				if _, ok := grouping[l.GetName()]; ok {
+					return fmt.Errorf(
+						"pushed metric %s (%s) already contains grouping label %s",
+						mf.GetName(), m, l.GetName(),
+					)
+				}
+			}
+		}
+		enc.Encode(mf)
+	}
+	req, err := http.NewRequest(method, pushURL, buf)
+	if err != nil {
+		return err
+	}
+	req.Header.Set(contentTypeHeader, string(expfmt.FmtProtoDelim))
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+	if resp.StatusCode != 202 {
+		body, _ := ioutil.ReadAll(resp.Body) // Ignore any further error as this is for an error message only.
+		return fmt.Errorf("unexpected status code %d while pushing to %s: %s", resp.StatusCode, pushURL, body)
+	}
+	return nil
+}
+
+// Collectors works like FromGatherer, but it does not use a Gatherer. Instead,
+// it collects from the provided collectors directly. It is a convenient way to
+// push only a few metrics.
+//
+// Deprecated: Please use a Pusher created with New instead.
+func Collectors(job string, grouping map[string]string, url string, collectors ...prometheus.Collector) error {
+	return pushCollectors(job, grouping, url, "PUT", collectors...)
+}
+
+// AddCollectors works like AddFromGatherer, but it does not use a Gatherer.
+// Instead, it collects from the provided collectors directly. It is a
+// convenient way to push only a few metrics.
+//
+// Deprecated: Please use a Pusher created with New instead.
+func AddCollectors(job string, grouping map[string]string, url string, collectors ...prometheus.Collector) error {
+	return pushCollectors(job, grouping, url, "POST", collectors...)
+}
+
+func pushCollectors(job string, grouping map[string]string, url, method string, collectors ...prometheus.Collector) error {
+	r := prometheus.NewRegistry()
+	for _, collector := range collectors {
+		if err := r.Register(collector); err != nil {
+			return err
+		}
+	}
+	return push(job, grouping, url, r, method)
+}
+
+// HostnameGroupingKey returns a label map with the only entry
+// {instance="<hostname>"}. This can be conveniently used as the grouping
+// parameter if metrics should be pushed with the hostname as label. The
+// returned map is created upon each call so that the caller is free to add more
+// labels to the map.
+//
+// Deprecated: Usually, metrics pushed to the Pushgateway should not be
+// host-centric. (You would use https://github.com/prometheus/node_exporter in
+// that case.) If you have the need to add the hostname to the grouping key, you
+// are probably doing something wrong. See
+// https://prometheus.io/docs/practices/pushing/ for details.
+func HostnameGroupingKey() map[string]string {
+	hostname, err := os.Hostname()
+	if err != nil {
+		return map[string]string{"instance": "unknown"}
+	}
+	return map[string]string{"instance": hostname}
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/push/example_add_from_gatherer_test.go b/vendor/github.com/prometheus/client_golang/prometheus/push/example_add_from_gatherer_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..dd22b526a23cb58999ef7d7076b1dbd7cf58cfd3
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/push/example_add_from_gatherer_test.go
@@ -0,0 +1,80 @@
+// 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 push_test
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/prometheus/client_golang/prometheus"
+	"github.com/prometheus/client_golang/prometheus/push"
+)
+
+var (
+	completionTime = prometheus.NewGauge(prometheus.GaugeOpts{
+		Name: "db_backup_last_completion_timestamp_seconds",
+		Help: "The timestamp of the last completion of a DB backup, successful or not.",
+	})
+	successTime = prometheus.NewGauge(prometheus.GaugeOpts{
+		Name: "db_backup_last_success_timestamp_seconds",
+		Help: "The timestamp of the last successful completion of a DB backup.",
+	})
+	duration = prometheus.NewGauge(prometheus.GaugeOpts{
+		Name: "db_backup_duration_seconds",
+		Help: "The duration of the last DB backup in seconds.",
+	})
+	records = prometheus.NewGauge(prometheus.GaugeOpts{
+		Name: "db_backup_records_processed",
+		Help: "The number of records processed in the last DB backup.",
+	})
+)
+
+func performBackup() (int, error) {
+	// Perform the backup and return the number of backed up records and any
+	// applicable error.
+	// ...
+	return 42, nil
+}
+
+func ExamplePusher_Add() {
+	// We use a registry here to benefit from the consistency checks that
+	// happen during registration.
+	registry := prometheus.NewRegistry()
+	registry.MustRegister(completionTime, duration, records)
+	// Note that successTime is not registered.
+
+	pusher := push.New("http://pushgateway:9091", "db_backup").Gatherer(registry)
+
+	start := time.Now()
+	n, err := performBackup()
+	records.Set(float64(n))
+	// Note that time.Since only uses a monotonic clock in Go1.9+.
+	duration.Set(time.Since(start).Seconds())
+	completionTime.SetToCurrentTime()
+	if err != nil {
+		fmt.Println("DB backup failed:", err)
+	} else {
+		// Add successTime to pusher only in case of success.
+		// We could as well register it with the registry.
+		// This example, however, demonstrates that you can
+		// mix Gatherers and Collectors when handling a Pusher.
+		pusher.Collector(successTime)
+		successTime.SetToCurrentTime()
+	}
+	// Add is used here rather than Push to not delete a previously pushed
+	// success timestamp in case of a failure of this backup.
+	if err := pusher.Add(); err != nil {
+		fmt.Println("Could not push to Pushgateway:", err)
+	}
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/push/examples_test.go b/vendor/github.com/prometheus/client_golang/prometheus/push/examples_test.go
index 8006bda7434e39ebbfdbaf84374fb5717d2662a9..fa5549a9ead5c6fc6bba0a7fa996c8c9bafc6863 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/push/examples_test.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/push/examples_test.go
@@ -15,42 +15,21 @@ package push_test
 
 import (
 	"fmt"
-	"time"
 
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/push"
 )
 
-func ExampleCollectors() {
+func ExamplePusher_Push() {
 	completionTime := prometheus.NewGauge(prometheus.GaugeOpts{
 		Name: "db_backup_last_completion_timestamp_seconds",
 		Help: "The timestamp of the last successful completion of a DB backup.",
 	})
-	completionTime.Set(float64(time.Now().Unix()))
-	if err := push.Collectors(
-		"db_backup", push.HostnameGroupingKey(),
-		"http://pushgateway:9091",
-		completionTime,
-	); err != nil {
-		fmt.Println("Could not push completion time to Pushgateway:", err)
-	}
-}
-
-func ExampleFromGatherer() {
-	registry := prometheus.NewRegistry()
-
-	completionTime := prometheus.NewGauge(prometheus.GaugeOpts{
-		Name: "db_backup_last_completion_timestamp_seconds",
-		Help: "The timestamp of the last successful completion of a DB backup.",
-	})
-	registry.MustRegister(completionTime)
-
-	completionTime.Set(float64(time.Now().Unix()))
-	if err := push.FromGatherer(
-		"db_backup", push.HostnameGroupingKey(),
-		"http://pushgateway:9091",
-		registry,
-	); err != nil {
+	completionTime.SetToCurrentTime()
+	if err := push.New("http://pushgateway:9091", "db_backup").
+		Collector(completionTime).
+		Grouping("db", "customers").
+		Push(); err != nil {
 		fmt.Println("Could not push completion time to Pushgateway:", err)
 	}
 }
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/push/push.go b/vendor/github.com/prometheus/client_golang/prometheus/push/push.go
index ae40402f8c69f42d1ee33887601f2cbf27116861..3721ff1988c8a8553a4a5bad9a57d7a94aaba22d 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/push/push.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/push/push.go
@@ -11,20 +11,27 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Copyright (c) 2013, The Prometheus Authors
-// All rights reserved.
+// Package push provides functions to push metrics to a Pushgateway. It uses a
+// builder approach. Create a Pusher with New and then add the various options
+// by using its methods, finally calling Add or Push, like this:
 //
-// Use of this source code is governed by a BSD-style license that can be found
-// in the LICENSE file.
-
-// Package push provides functions to push metrics to a Pushgateway. The metrics
-// to push are either collected from a provided registry, or from explicitly
-// listed collectors.
+//    // Easy case:
+//    push.New("http://example.org/metrics", "my_job").Gatherer(myRegistry).Push()
+//
+//    // Complex case:
+//    push.New("http://example.org/metrics", "my_job").
+//        Collector(myCollector1).
+//        Collector(myCollector2).
+//        Grouping("zone", "xy").
+//        Client(&myHTTPClient).
+//        BasicAuth("top", "secret").
+//        Add()
+//
+// See the examples section for more detailed examples.
 //
-// See the documentation of the Pushgateway to understand the meaning of the
-// grouping parameters and the differences between push.Registry and
-// push.Collectors on the one hand and push.AddRegistry and push.AddCollectors
-// on the other hand: https://github.com/prometheus/pushgateway
+// See the documentation of the Pushgateway to understand the meaning of
+// the grouping key and the differences between Push and Add:
+// https://github.com/prometheus/pushgateway
 package push
 
 import (
@@ -33,7 +40,6 @@ import (
 	"io/ioutil"
 	"net/http"
 	"net/url"
-	"os"
 	"strings"
 
 	"github.com/prometheus/common/expfmt"
@@ -44,57 +50,149 @@ import (
 
 const contentTypeHeader = "Content-Type"
 
-// FromGatherer triggers a metric collection by the provided Gatherer (which is
-// usually implemented by a prometheus.Registry) and pushes all gathered metrics
-// to the Pushgateway specified by url, using the provided job name and the
-// (optional) further grouping labels (the grouping map may be nil). See the
-// Pushgateway documentation for detailed implications of the job and other
-// grouping labels. Neither the job name nor any grouping label value may
-// contain a "/". The metrics pushed must not contain a job label of their own
-// nor any of the grouping labels.
+// Pusher manages a push to the Pushgateway. Use New to create one, configure it
+// with its methods, and finally use the Add or Push method to push.
+type Pusher struct {
+	error error
+
+	url, job string
+	grouping map[string]string
+
+	gatherers  prometheus.Gatherers
+	registerer prometheus.Registerer
+
+	client             *http.Client
+	useBasicAuth       bool
+	username, password string
+}
+
+// New creates a new Pusher to push to the provided URL with the provided job
+// name. You can use just host:port or ip:port as url, in which case “http://”
+// is added automatically. Alternatively, include the schema in the
+// URL. However, do not include the “/metrics/jobs/…” part.
 //
-// You can use just host:port or ip:port as url, in which case 'http://' is
-// added automatically. You can also include the schema in the URL. However, do
-// not include the '/metrics/jobs/...' part.
+// Note that until https://github.com/prometheus/pushgateway/issues/97 is
+// resolved, a “/” character in the job name is prohibited.
+func New(url, job string) *Pusher {
+	var (
+		reg = prometheus.NewRegistry()
+		err error
+	)
+	if !strings.Contains(url, "://") {
+		url = "http://" + url
+	}
+	if strings.HasSuffix(url, "/") {
+		url = url[:len(url)-1]
+	}
+	if strings.Contains(job, "/") {
+		err = fmt.Errorf("job contains '/': %s", job)
+	}
+
+	return &Pusher{
+		error:      err,
+		url:        url,
+		job:        job,
+		grouping:   map[string]string{},
+		gatherers:  prometheus.Gatherers{reg},
+		registerer: reg,
+		client:     &http.Client{},
+	}
+}
+
+// Push collects/gathers all metrics from all Collectors and Gatherers added to
+// this Pusher. Then, it pushes them to the Pushgateway configured while
+// creating this Pusher, using the configured job name and any added grouping
+// labels as grouping key. All previously pushed metrics with the same job and
+// other grouping labels will be replaced with the metrics pushed by this
+// call. (It uses HTTP method “PUT” to push to the Pushgateway.)
 //
-// Note that all previously pushed metrics with the same job and other grouping
-// labels will be replaced with the metrics pushed by this call. (It uses HTTP
-// method 'PUT' to push to the Pushgateway.)
-func FromGatherer(job string, grouping map[string]string, url string, g prometheus.Gatherer) error {
-	return push(job, grouping, url, g, "PUT")
+// Push returns the first error encountered by any method call (including this
+// one) in the lifetime of the Pusher.
+func (p *Pusher) Push() error {
+	return p.push("PUT")
 }
 
-// AddFromGatherer works like FromGatherer, but only previously pushed metrics
-// with the same name (and the same job and other grouping labels) will be
-// replaced. (It uses HTTP method 'POST' to push to the Pushgateway.)
-func AddFromGatherer(job string, grouping map[string]string, url string, g prometheus.Gatherer) error {
-	return push(job, grouping, url, g, "POST")
+// Add works like push, but only previously pushed metrics with the same name
+// (and the same job and other grouping labels) will be replaced. (It uses HTTP
+// method “POST” to push to the Pushgateway.)
+func (p *Pusher) Add() error {
+	return p.push("POST")
 }
 
-func push(job string, grouping map[string]string, pushURL string, g prometheus.Gatherer, method string) error {
-	if !strings.Contains(pushURL, "://") {
-		pushURL = "http://" + pushURL
-	}
-	if strings.HasSuffix(pushURL, "/") {
-		pushURL = pushURL[:len(pushURL)-1]
-	}
+// Gatherer adds a Gatherer to the Pusher, from which metrics will be gathered
+// to push them to the Pushgateway. The gathered metrics must not contain a job
+// label of their own.
+//
+// For convenience, this method returns a pointer to the Pusher itself.
+func (p *Pusher) Gatherer(g prometheus.Gatherer) *Pusher {
+	p.gatherers = append(p.gatherers, g)
+	return p
+}
 
-	if strings.Contains(job, "/") {
-		return fmt.Errorf("job contains '/': %s", job)
+// Collector adds a Collector to the Pusher, from which metrics will be
+// collected to push them to the Pushgateway. The collected metrics must not
+// contain a job label of their own.
+//
+// For convenience, this method returns a pointer to the Pusher itself.
+func (p *Pusher) Collector(c prometheus.Collector) *Pusher {
+	if p.error == nil {
+		p.error = p.registerer.Register(c)
 	}
-	urlComponents := []string{url.QueryEscape(job)}
-	for ln, lv := range grouping {
-		if !model.LabelNameRE.MatchString(ln) {
-			return fmt.Errorf("grouping label has invalid name: %s", ln)
+	return p
+}
+
+// Grouping adds a label pair to the grouping key of the Pusher, replacing any
+// previously added label pair with the same label name. Note that setting any
+// labels in the grouping key that are already contained in the metrics to push
+// will lead to an error.
+//
+// For convenience, this method returns a pointer to the Pusher itself.
+//
+// Note that until https://github.com/prometheus/pushgateway/issues/97 is
+// resolved, this method does not allow a “/” character in the label value.
+func (p *Pusher) Grouping(name, value string) *Pusher {
+	if p.error == nil {
+		if !model.LabelName(name).IsValid() {
+			p.error = fmt.Errorf("grouping label has invalid name: %s", name)
+			return p
 		}
-		if strings.Contains(lv, "/") {
-			return fmt.Errorf("value of grouping label %s contains '/': %s", ln, lv)
+		if strings.Contains(value, "/") {
+			p.error = fmt.Errorf("value of grouping label %s contains '/': %s", name, value)
+			return p
 		}
+		p.grouping[name] = value
+	}
+	return p
+}
+
+// Client sets a custom HTTP client for the Pusher. For convenience, this method
+// returns a pointer to the Pusher itself.
+func (p *Pusher) Client(c *http.Client) *Pusher {
+	p.client = c
+	return p
+}
+
+// BasicAuth configures the Pusher to use HTTP Basic Authentication with the
+// provided username and password. For convenience, this method returns a
+// pointer to the Pusher itself.
+func (p *Pusher) BasicAuth(username, password string) *Pusher {
+	p.useBasicAuth = true
+	p.username = username
+	p.password = password
+	return p
+}
+
+func (p *Pusher) push(method string) error {
+	if p.error != nil {
+		return p.error
+	}
+	urlComponents := []string{url.QueryEscape(p.job)}
+	for ln, lv := range p.grouping {
 		urlComponents = append(urlComponents, ln, lv)
 	}
-	pushURL = fmt.Sprintf("%s/metrics/job/%s", pushURL, strings.Join(urlComponents, "/"))
+	pushURL := fmt.Sprintf("%s/metrics/job/%s", p.url, strings.Join(urlComponents, "/"))
 
-	mfs, err := g.Gather()
+	mfs, err := p.gatherers.Gather()
 	if err != nil {
 		return err
 	}
@@ -107,7 +205,7 @@ func push(job string, grouping map[string]string, pushURL string, g prometheus.G
 				if l.GetName() == "job" {
 					return fmt.Errorf("pushed metric %s (%s) already contains a job label", mf.GetName(), m)
 				}
-				if _, ok := grouping[l.GetName()]; ok {
+				if _, ok := p.grouping[l.GetName()]; ok {
 					return fmt.Errorf(
 						"pushed metric %s (%s) already contains grouping label %s",
 						mf.GetName(), m, l.GetName(),
@@ -121,8 +219,11 @@ func push(job string, grouping map[string]string, pushURL string, g prometheus.G
 	if err != nil {
 		return err
 	}
+	if p.useBasicAuth {
+		req.SetBasicAuth(p.username, p.password)
+	}
 	req.Header.Set(contentTypeHeader, string(expfmt.FmtProtoDelim))
-	resp, err := http.DefaultClient.Do(req)
+	resp, err := p.client.Do(req)
 	if err != nil {
 		return err
 	}
@@ -133,40 +234,3 @@ func push(job string, grouping map[string]string, pushURL string, g prometheus.G
 	}
 	return nil
 }
-
-// Collectors works like FromGatherer, but it does not use a Gatherer. Instead,
-// it collects from the provided collectors directly. It is a convenient way to
-// push only a few metrics.
-func Collectors(job string, grouping map[string]string, url string, collectors ...prometheus.Collector) error {
-	return pushCollectors(job, grouping, url, "PUT", collectors...)
-}
-
-// AddCollectors works like AddFromGatherer, but it does not use a Gatherer.
-// Instead, it collects from the provided collectors directly. It is a
-// convenient way to push only a few metrics.
-func AddCollectors(job string, grouping map[string]string, url string, collectors ...prometheus.Collector) error {
-	return pushCollectors(job, grouping, url, "POST", collectors...)
-}
-
-func pushCollectors(job string, grouping map[string]string, url, method string, collectors ...prometheus.Collector) error {
-	r := prometheus.NewRegistry()
-	for _, collector := range collectors {
-		if err := r.Register(collector); err != nil {
-			return err
-		}
-	}
-	return push(job, grouping, url, r, method)
-}
-
-// HostnameGroupingKey returns a label map with the only entry
-// {instance="<hostname>"}. This can be conveniently used as the grouping
-// parameter if metrics should be pushed with the hostname as label. The
-// returned map is created upon each call so that the caller is free to add more
-// labels to the map.
-func HostnameGroupingKey() map[string]string {
-	hostname, err := os.Hostname()
-	if err != nil {
-		return map[string]string{"instance": "unknown"}
-	}
-	return map[string]string{"instance": hostname}
-}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/push/push_test.go b/vendor/github.com/prometheus/client_golang/prometheus/push/push_test.go
index 28ed9b74b69a06d15924f78189e3c1d76cd862f5..34ec334bb4fa0d35192b0f5c42936978a34bc5d2 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/push/push_test.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/push/push_test.go
@@ -11,12 +11,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Copyright (c) 2013, The Prometheus Authors
-// All rights reserved.
-//
-// Use of this source code is governed by a BSD-style license that can be found
-// in the LICENSE file.
-
 package push
 
 import (
@@ -24,7 +18,6 @@ import (
 	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
-	"os"
 	"testing"
 
 	"github.com/prometheus/common/expfmt"
@@ -40,11 +33,6 @@ func TestPush(t *testing.T) {
 		lastPath   string
 	)
 
-	host, err := os.Hostname()
-	if err != nil {
-		t.Error(err)
-	}
-
 	// Fake a Pushgateway that always responds with 202.
 	pgwOK := httptest.NewServer(
 		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -98,26 +86,32 @@ func TestPush(t *testing.T) {
 	}
 	wantBody := buf.Bytes()
 
-	// PushCollectors, all good.
-	if err := Collectors("testjob", HostnameGroupingKey(), pgwOK.URL, metric1, metric2); err != nil {
+	// Push some Collectors, all good.
+	if err := New(pgwOK.URL, "testjob").
+		Collector(metric1).
+		Collector(metric2).
+		Push(); err != nil {
 		t.Fatal(err)
 	}
 	if lastMethod != "PUT" {
-		t.Error("want method PUT for PushCollectors, got", lastMethod)
+		t.Error("want method PUT for Push, got", lastMethod)
 	}
 	if bytes.Compare(lastBody, wantBody) != 0 {
 		t.Errorf("got body %v, want %v", lastBody, wantBody)
 	}
-	if lastPath != "/metrics/job/testjob/instance/"+host {
+	if lastPath != "/metrics/job/testjob" {
 		t.Error("unexpected path:", lastPath)
 	}
 
-	// PushAddCollectors, with nil grouping, all good.
-	if err := AddCollectors("testjob", nil, pgwOK.URL, metric1, metric2); err != nil {
+	// Add some Collectors, with nil grouping, all good.
+	if err := New(pgwOK.URL, "testjob").
+		Collector(metric1).
+		Collector(metric2).
+		Add(); err != nil {
 		t.Fatal(err)
 	}
 	if lastMethod != "POST" {
-		t.Error("want method POST for PushAddCollectors, got", lastMethod)
+		t.Error("want method POST for Add, got", lastMethod)
 	}
 	if bytes.Compare(lastBody, wantBody) != 0 {
 		t.Errorf("got body %v, want %v", lastBody, wantBody)
@@ -126,8 +120,11 @@ func TestPush(t *testing.T) {
 		t.Error("unexpected path:", lastPath)
 	}
 
-	// PushCollectors with a broken PGW.
-	if err := Collectors("testjob", nil, pgwErr.URL, metric1, metric2); err == nil {
+	// Push some Collectors with a broken PGW.
+	if err := New(pgwErr.URL, "testjob").
+		Collector(metric1).
+		Collector(metric2).
+		Push(); err == nil {
 		t.Error("push to broken Pushgateway succeeded")
 	} else {
 		if got, want := err.Error(), "unexpected status code 500 while pushing to "+pgwErr.URL+"/metrics/job/testjob: fake error\n"; got != want {
@@ -135,22 +132,39 @@ func TestPush(t *testing.T) {
 		}
 	}
 
-	// PushCollectors with invalid grouping or job.
-	if err := Collectors("testjob", map[string]string{"foo": "bums"}, pgwErr.URL, metric1, metric2); err == nil {
+	// Push some Collectors with invalid grouping or job.
+	if err := New(pgwOK.URL, "testjob").
+		Grouping("foo", "bums").
+		Collector(metric1).
+		Collector(metric2).
+		Push(); err == nil {
 		t.Error("push with grouping contained in metrics succeeded")
 	}
-	if err := Collectors("test/job", nil, pgwErr.URL, metric1, metric2); err == nil {
+	if err := New(pgwOK.URL, "test/job").
+		Collector(metric1).
+		Collector(metric2).
+		Push(); err == nil {
 		t.Error("push with invalid job value succeeded")
 	}
-	if err := Collectors("testjob", map[string]string{"foo/bar": "bums"}, pgwErr.URL, metric1, metric2); err == nil {
+	if err := New(pgwOK.URL, "testjob").
+		Grouping("foobar", "bu/ms").
+		Collector(metric1).
+		Collector(metric2).
+		Push(); err == nil {
 		t.Error("push with invalid grouping succeeded")
 	}
-	if err := Collectors("testjob", map[string]string{"foo-bar": "bums"}, pgwErr.URL, metric1, metric2); err == nil {
+	if err := New(pgwOK.URL, "testjob").
+		Grouping("foo-bar", "bums").
+		Collector(metric1).
+		Collector(metric2).
+		Push(); err == nil {
 		t.Error("push with invalid grouping succeeded")
 	}
 
 	// Push registry, all good.
-	if err := FromGatherer("testjob", HostnameGroupingKey(), pgwOK.URL, reg); err != nil {
+	if err := New(pgwOK.URL, "testjob").
+		Gatherer(reg).
+		Push(); err != nil {
 		t.Fatal(err)
 	}
 	if lastMethod != "PUT" {
@@ -160,12 +174,16 @@ func TestPush(t *testing.T) {
 		t.Errorf("got body %v, want %v", lastBody, wantBody)
 	}
 
-	// PushAdd registry, all good.
-	if err := AddFromGatherer("testjob", map[string]string{"a": "x", "b": "y"}, pgwOK.URL, reg); err != nil {
+	// Add registry, all good.
+	if err := New(pgwOK.URL, "testjob").
+		Grouping("a", "x").
+		Grouping("b", "y").
+		Gatherer(reg).
+		Add(); err != nil {
 		t.Fatal(err)
 	}
 	if lastMethod != "POST" {
-		t.Error("want method POSTT for PushAdd, got", lastMethod)
+		t.Error("want method POST for Add, got", lastMethod)
 	}
 	if bytes.Compare(lastBody, wantBody) != 0 {
 		t.Errorf("got body %v, want %v", lastBody, wantBody)
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/registry.go b/vendor/github.com/prometheus/client_golang/prometheus/registry.go
index 405d6ddf42dc9a53f8827a25d225ea9c79ab025b..b5e70b93fa13869ea93dbdf44094fe63095edb25 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/registry.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/registry.go
@@ -15,15 +15,22 @@ 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 (
@@ -35,13 +42,14 @@ 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 (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.
+// 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.
 var (
 	defaultRegistry              = NewRegistry()
 	DefaultRegisterer Registerer = defaultRegistry
@@ -49,7 +57,7 @@ var (
 )
 
 func init() {
-	MustRegister(NewProcessCollector(os.Getpid(), ""))
+	MustRegister(NewProcessCollector(ProcessCollectorOpts{}))
 	MustRegister(NewGoCollector())
 }
 
@@ -65,7 +73,8 @@ 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.
+// actually been registered with the registry. Unchecked Collectors (those whose
+// Describe methed does not yield any descriptors) are excluded from the check.
 //
 // 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
@@ -80,7 +89,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 then the Registry type
+// Registerer as type for registration purposes (rather than the Registry type
 // directly). In that way, they are free to use custom Registerer implementation
 // (e.g. for testing purposes).
 type Registerer interface {
@@ -95,8 +104,13 @@ type Registerer interface {
 	// returned error is an instance of AlreadyRegisteredError, which
 	// contains the previously registered Collector.
 	//
-	// It is in general not safe to register the same Collector multiple
-	// times concurrently.
+	// 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.)
 	Register(Collector) error
 	// MustRegister works like Register but registers any number of
 	// Collectors and panics upon the first registration that causes an
@@ -105,7 +119,9 @@ 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.
+	// returns whether a Collector was unregistered. Note that an unchecked
+	// Collector cannot be unregistered (as its Describe method does not
+	// yield any descriptor).
 	//
 	// Note that even after unregistering, it will not be possible to
 	// register a new Collector that is inconsistent with the unregistered
@@ -123,15 +139,23 @@ 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 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.
+	// 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.
 	Gather() ([]*dto.MetricFamily, error)
 }
 
@@ -152,38 +176,6 @@ 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.
 //
@@ -201,25 +193,6 @@ 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
@@ -252,6 +225,13 @@ 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
@@ -276,6 +256,7 @@ 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
 }
 
@@ -293,8 +274,13 @@ func (r *Registry) Register(c Collector) error {
 		close(descChan)
 	}()
 	r.mtx.Lock()
-	defer r.mtx.Unlock()
-	// Coduct various tests...
+	defer func() {
+		// Drain channel in case of premature return to not leak a goroutine.
+		for range descChan {
+		}
+		r.mtx.Unlock()
+	}()
+	// Conduct various tests...
 	for desc := range descChan {
 
 		// Is the descriptor valid at all?
@@ -333,9 +319,10 @@ func (r *Registry) Register(c Collector) error {
 			}
 		}
 	}
-	// Did anything happen at all?
+	// A Collector yielding no Desc at all is considered unchecked.
 	if len(newDescIDs) == 0 {
-		return errors.New("collector has no descriptors")
+		r.uncheckedCollectors = append(r.uncheckedCollectors, c)
+		return nil
 	}
 	if existing, exists := r.collectorsByID[collectorID]; exists {
 		return AlreadyRegisteredError{
@@ -409,31 +396,25 @@ func (r *Registry) MustRegister(cs ...Collector) {
 // Gather implements Gatherer.
 func (r *Registry) Gather() ([]*dto.MetricFamily, error) {
 	var (
-		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
+		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
 	)
 
 	r.mtx.RLock()
+	goroutineBudget := len(r.collectorsByID) + len(r.uncheckedCollectors)
 	metricFamiliesByName := make(map[string]*dto.MetricFamily, len(r.dimHashesByName))
-
-	// 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)
-	}()
+	checkedCollectors := make(chan Collector, len(r.collectorsByID))
+	uncheckedCollectors := make(chan Collector, len(r.uncheckedCollectors))
 	for _, collector := range r.collectorsByID {
-		go func(collector Collector) {
-			defer wg.Done()
-			collector.Collect(metricChan)
-		}(collector)
+		checkedCollectors <- collector
+	}
+	for _, collector := range r.uncheckedCollectors {
+		uncheckedCollectors <- collector
 	}
-
 	// In case pedantic checks are enabled, we have to copy the map before
 	// giving up the RLock.
 	if r.pedanticChecksEnabled {
@@ -442,127 +423,258 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) {
 			registeredDescIDs[id] = struct{}{}
 		}
 	}
-
 	r.mtx.RUnlock()
 
-	// Drain metricChan in case of premature return.
+	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.
 	defer func() {
-		for range metricChan {
+		if checkedMetricChan != nil {
+			for range checkedMetricChan {
+			}
+		}
+		if uncheckedMetricChan != nil {
+			for range uncheckedMetricChan {
+			}
 		}
 	}()
 
-	// 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,
+	// 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,
 			))
-			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
+		case metric, ok := <-umc:
+			if !ok {
+				umc = nil
+				break
 			}
-			// 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
-				}
-			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,
+			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,
 					))
-					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,
+				case metric, ok := <-umc:
+					if !ok {
+						umc = nil
+						break
+					}
+					errs.Append(processMetric(
+						metric, metricFamiliesByName,
+						metricHashes,
+						nil,
 					))
-					continue
 				}
-			default:
-				panic("encountered MetricFamily with invalid type")
+				break
 			}
-		} 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
+			// 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,
+				)
 			}
-			metricFamiliesByName[desc.fqName] = metricFamily
-		}
-		if err := checkMetricConsistency(metricFamily, dtoMetric, metricHashes, dimHashes); err != nil {
-			errs = append(errs, err)
-			continue
-		}
-		if r.pedanticChecksEnabled {
-			// Is the desc registered at all?
-			if _, exist := registeredDescIDs[desc.id]; !exist {
-				errs = append(errs, fmt.Errorf(
-					"collected metric %s %s with unregistered descriptor %s",
-					metricFamily.GetName(), dtoMetric, desc,
-				))
-				continue
+		case dto.MetricType_GAUGE:
+			if dtoMetric.Gauge == nil {
+				return fmt.Errorf(
+					"collected metric %s %s should be a Gauge",
+					desc.fqName, dtoMetric,
+				)
 			}
-			if err := checkDescConsistency(metricFamily, dtoMetric, desc); err != nil {
-				errs = append(errs, err)
-				continue
+		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")
+		}
+	} else { // New name.
+		metricFamily = &dto.MetricFamily{}
+		metricFamily.Name = proto.String(desc.fqName)
+		metricFamily.Help = proto.String(desc.help)
+		// TODO(beorn7): Simplify switch once Desc has type.
+		switch {
+		case dtoMetric.Gauge != nil:
+			metricFamily.Type = dto.MetricType_GAUGE.Enum()
+		case dtoMetric.Counter != nil:
+			metricFamily.Type = dto.MetricType_COUNTER.Enum()
+		case dtoMetric.Summary != nil:
+			metricFamily.Type = dto.MetricType_SUMMARY.Enum()
+		case dtoMetric.Untyped != nil:
+			metricFamily.Type = dto.MetricType_UNTYPED.Enum()
+		case dtoMetric.Histogram != nil:
+			metricFamily.Type = dto.MetricType_HISTOGRAM.Enum()
+		default:
+			return fmt.Errorf("empty metric collected: %s", dtoMetric)
+		}
+		if err := checkSuffixCollisions(metricFamily, metricFamiliesByName); err != nil {
+			return err
 		}
-		metricFamily.Metric = append(metricFamily.Metric, dtoMetric)
+		metricFamiliesByName[desc.fqName] = metricFamily
 	}
-	return normalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap()
+	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
 }
 
 // Gatherers is a slice of Gatherer instances that implements the Gatherer
@@ -588,7 +700,6 @@ 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.
 	)
 
@@ -625,10 +736,14 @@ 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, dimHashes); err != nil {
+				if err := checkMetricConsistency(existingMF, m, metricHashes); err != nil {
 					errs = append(errs, err)
 					continue
 				}
@@ -636,88 +751,80 @@ func (gs Gatherers) Gather() ([]*dto.MetricFamily, error) {
 			}
 		}
 	}
-	return normalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap()
-}
-
-// 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()
+	return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap()
 }
 
-// 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))
+// checkSuffixCollisions checks for collisions with the “magic” suffixes the
+// Prometheus text format and the internal metric representation of the
+// Prometheus server add while flattening Summaries and Histograms.
+func checkSuffixCollisions(mf *dto.MetricFamily, mfs map[string]*dto.MetricFamily) error {
+	var (
+		newName              = mf.GetName()
+		newType              = mf.GetType()
+		newNameWithoutSuffix = ""
+	)
+	switch {
+	case strings.HasSuffix(newName, "_count"):
+		newNameWithoutSuffix = newName[:len(newName)-6]
+	case strings.HasSuffix(newName, "_sum"):
+		newNameWithoutSuffix = newName[:len(newName)-4]
+	case strings.HasSuffix(newName, "_bucket"):
+		newNameWithoutSuffix = newName[:len(newName)-7]
+	}
+	if newNameWithoutSuffix != "" {
+		if existingMF, ok := mfs[newNameWithoutSuffix]; ok {
+			switch existingMF.GetType() {
+			case dto.MetricType_SUMMARY:
+				if !strings.HasSuffix(newName, "_bucket") {
+					return fmt.Errorf(
+						"collected metric named %q collides with previously collected summary named %q",
+						newName, newNameWithoutSuffix,
+					)
+				}
+			case dto.MetricType_HISTOGRAM:
+				return fmt.Errorf(
+					"collected metric named %q collides with previously collected histogram named %q",
+					newName, newNameWithoutSuffix,
+				)
+			}
+		}
 	}
-	names := make([]string, 0, len(metricFamiliesByName))
-	for name, mf := range metricFamiliesByName {
-		if len(mf.Metric) > 0 {
-			names = append(names, name)
+	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",
+			)
 		}
 	}
-	sort.Strings(names)
-	result := make([]*dto.MetricFamily, 0, len(names))
-	for _, name := range names {
-		result = append(result, metricFamiliesByName[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",
+			)
+		}
 	}
-	return result
+	return nil
 }
 
 // checkMetricConsistency checks if the provided Metric is consistent with the
-// 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.
+// 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.
 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 ||
@@ -725,41 +832,65 @@ func checkMetricConsistency(
 		metricFamily.GetType() == dto.MetricType_HISTOGRAM && dtoMetric.Histogram == nil ||
 		metricFamily.GetType() == dto.MetricType_UNTYPED && dtoMetric.Untyped == nil {
 		return fmt.Errorf(
-			"collected metric %s %s is not a %s",
-			metricFamily.GetName(), dtoMetric, metricFamily.GetType(),
+			"collected metric %q { %s} is not a %s",
+			name, dtoMetric, metricFamily.GetType(),
 		)
 	}
 
-	// Is the metric unique (i.e. no other metric with the same name and the same label values)?
+	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)?
 	h := hashNew()
-	h = hashAdd(h, metricFamily.GetName())
+	h = hashAdd(h, name)
 	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))
+	if !sort.IsSorted(labelPairSorter(dtoMetric.Label)) {
+		// We cannot sort dtoMetric.Label in place as it is immutable by contract.
+		copiedLabels := make([]*dto.LabelPair, len(dtoMetric.Label))
+		copy(copiedLabels, dtoMetric.Label)
+		sort.Sort(labelPairSorter(copiedLabels))
+		dtoMetric.Label = copiedLabels
+	}
 	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 %s %s was collected before with the same name and label values",
-			metricFamily.GetName(), dtoMetric,
+			"collected metric %q { %s} was collected before with the same name and label values",
+			name, 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
 }
@@ -778,8 +909,8 @@ func checkDescConsistency(
 	}
 
 	// Is the desc consistent with the content of the metric?
-	lpsFromDesc := make([]*dto.LabelPair, 0, len(dtoMetric.Label))
-	lpsFromDesc = append(lpsFromDesc, desc.constLabelPairs...)
+	lpsFromDesc := make([]*dto.LabelPair, len(desc.constLabelPairs), len(dtoMetric.Label))
+	copy(lpsFromDesc, desc.constLabelPairs)
 	for _, l := range desc.variableLabels {
 		lpsFromDesc = append(lpsFromDesc, &dto.LabelPair{
 			Name: proto.String(l),
@@ -791,7 +922,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/registry_test.go b/vendor/github.com/prometheus/client_golang/prometheus/registry_test.go
index 9dacb6256dd7df996b2e90e8d4ed63fca3cb7a1f..b9d9f13b0bd738eeef03348d313ae24a4c6646e7 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/registry_test.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/registry_test.go
@@ -21,9 +21,15 @@ package prometheus_test
 
 import (
 	"bytes"
+	"fmt"
+	"io/ioutil"
+	"math/rand"
 	"net/http"
 	"net/http/httptest"
+	"os"
+	"sync"
 	"testing"
+	"time"
 
 	dto "github.com/prometheus/client_model/go"
 
@@ -34,7 +40,22 @@ import (
 	"github.com/prometheus/client_golang/prometheus/promhttp"
 )
 
+// uncheckedCollector wraps a Collector but its Describe method yields no Desc.
+type uncheckedCollector struct {
+	c prometheus.Collector
+}
+
+func (u uncheckedCollector) Describe(_ chan<- *prometheus.Desc) {}
+func (u uncheckedCollector) Collect(c chan<- prometheus.Metric) {
+	u.c.Collect(c)
+}
+
 func testHandler(t testing.TB) {
+	// TODO(beorn7): This test is a bit too "end-to-end". It tests quite a
+	// few moving parts that are not strongly coupled. They could/should be
+	// tested separately. However, the changes planned for v0.10 will
+	// require a major rework of this test anyway, at which time I will
+	// structure it in a better way.
 
 	metricVec := prometheus.NewCounterVec(
 		prometheus.CounterOpts{
@@ -209,6 +230,117 @@ metric: <
 	expectedMetricFamilyMergedWithExternalAsProtoCompactText := []byte(`name:"name" help:"docstring" type:COUNTER metric:<label:<name:"constname" value:"constvalue" > label:<name:"labelname" value:"different_val" > counter:<value:42 > > metric:<label:<name:"constname" value:"constvalue" > label:<name:"labelname" value:"val1" > counter:<value:1 > > metric:<label:<name:"constname" value:"constvalue" > label:<name:"labelname" value:"val2" > counter:<value:1 > > 
 `)
 
+	externalMetricFamilyWithInvalidLabelValue := &dto.MetricFamily{
+		Name: proto.String("name"),
+		Help: proto.String("docstring"),
+		Type: dto.MetricType_COUNTER.Enum(),
+		Metric: []*dto.Metric{
+			{
+				Label: []*dto.LabelPair{
+					{
+						Name:  proto.String("constname"),
+						Value: proto.String("\xFF"),
+					},
+					{
+						Name:  proto.String("labelname"),
+						Value: proto.String("different_val"),
+					},
+				},
+				Counter: &dto.Counter{
+					Value: proto.Float64(42),
+				},
+			},
+		},
+	}
+
+	expectedMetricFamilyInvalidLabelValueAsText := []byte(`An error has occurred while serving metrics:
+
+collected metric "name" { label:<name:"constname" value:"\377" > label:<name:"labelname" value:"different_val" > counter:<value:42 > } has a label named "constname" whose value is not utf8: "\xff"
+`)
+
+	summary := prometheus.NewSummary(prometheus.SummaryOpts{
+		Name: "complex",
+		Help: "A metric to check collisions with _sum and _count.",
+	})
+	summaryAsText := []byte(`# HELP complex A metric to check collisions with _sum and _count.
+# TYPE complex summary
+complex{quantile="0.5"} NaN
+complex{quantile="0.9"} NaN
+complex{quantile="0.99"} NaN
+complex_sum 0
+complex_count 0
+`)
+	histogram := prometheus.NewHistogram(prometheus.HistogramOpts{
+		Name: "complex",
+		Help: "A metric to check collisions with _sun, _count, and _bucket.",
+	})
+	externalMetricFamilyWithBucketSuffix := &dto.MetricFamily{
+		Name: proto.String("complex_bucket"),
+		Help: proto.String("externaldocstring"),
+		Type: dto.MetricType_COUNTER.Enum(),
+		Metric: []*dto.Metric{
+			{
+				Counter: &dto.Counter{
+					Value: proto.Float64(1),
+				},
+			},
+		},
+	}
+	externalMetricFamilyWithBucketSuffixAsText := []byte(`# HELP complex_bucket externaldocstring
+# TYPE complex_bucket counter
+complex_bucket 1
+`)
+	externalMetricFamilyWithCountSuffix := &dto.MetricFamily{
+		Name: proto.String("complex_count"),
+		Help: proto.String("externaldocstring"),
+		Type: dto.MetricType_COUNTER.Enum(),
+		Metric: []*dto.Metric{
+			{
+				Counter: &dto.Counter{
+					Value: proto.Float64(1),
+				},
+			},
+		},
+	}
+	bucketCollisionMsg := []byte(`An error has occurred while serving metrics:
+
+collected metric named "complex_bucket" collides with previously collected histogram named "complex"
+`)
+	summaryCountCollisionMsg := []byte(`An error has occurred while serving metrics:
+
+collected metric named "complex_count" collides with previously collected summary named "complex"
+`)
+	histogramCountCollisionMsg := []byte(`An error has occurred while serving metrics:
+
+collected metric named "complex_count" collides with previously collected histogram named "complex"
+`)
+	externalMetricFamilyWithDuplicateLabel := &dto.MetricFamily{
+		Name: proto.String("broken_metric"),
+		Help: proto.String("The registry should detect the duplicate label."),
+		Type: dto.MetricType_COUNTER.Enum(),
+		Metric: []*dto.Metric{
+			{
+				Label: []*dto.LabelPair{
+					{
+						Name:  proto.String("foo"),
+						Value: proto.String("bar"),
+					},
+					{
+						Name:  proto.String("foo"),
+						Value: proto.String("baz"),
+					},
+				},
+				Counter: &dto.Counter{
+					Value: proto.Float64(2.7),
+				},
+			},
+		},
+	}
+	duplicateLabelMsg := []byte(`An error has occurred while serving metrics:
+
+collected metric "broken_metric" { label:<name:"foo" value:"bar" > label:<name:"foo" value:"baz" > counter:<value:2.7 > } has two or more labels with the same name: foo
+`)
+
 	type output struct {
 		headers map[string]string
 		body    []byte
@@ -226,7 +358,7 @@ metric: <
 			},
 			out: output{
 				headers: map[string]string{
-					"Content-Type": `text/plain; version=0.0.4`,
+					"Content-Type": `text/plain; version=0.0.4; charset=utf-8`,
 				},
 				body: []byte{},
 			},
@@ -237,7 +369,7 @@ metric: <
 			},
 			out: output{
 				headers: map[string]string{
-					"Content-Type": `text/plain; version=0.0.4`,
+					"Content-Type": `text/plain; version=0.0.4; charset=utf-8`,
 				},
 				body: []byte{},
 			},
@@ -248,7 +380,7 @@ metric: <
 			},
 			out: output{
 				headers: map[string]string{
-					"Content-Type": `text/plain; version=0.0.4`,
+					"Content-Type": `text/plain; version=0.0.4; charset=utf-8`,
 				},
 				body: []byte{},
 			},
@@ -270,7 +402,7 @@ metric: <
 			},
 			out: output{
 				headers: map[string]string{
-					"Content-Type": `text/plain; version=0.0.4`,
+					"Content-Type": `text/plain; version=0.0.4; charset=utf-8`,
 				},
 				body: expectedMetricFamilyAsText,
 			},
@@ -294,7 +426,7 @@ metric: <
 			},
 			out: output{
 				headers: map[string]string{
-					"Content-Type": `text/plain; version=0.0.4`,
+					"Content-Type": `text/plain; version=0.0.4; charset=utf-8`,
 				},
 				body: externalMetricFamilyAsText,
 			},
@@ -337,7 +469,7 @@ metric: <
 			},
 			out: output{
 				headers: map[string]string{
-					"Content-Type": `text/plain; version=0.0.4`,
+					"Content-Type": `text/plain; version=0.0.4; charset=utf-8`,
 				},
 				body: []byte{},
 			},
@@ -348,7 +480,7 @@ metric: <
 			},
 			out: output{
 				headers: map[string]string{
-					"Content-Type": `text/plain; version=0.0.4`,
+					"Content-Type": `text/plain; version=0.0.4; charset=utf-8`,
 				},
 				body: expectedMetricFamilyAsText,
 			},
@@ -360,7 +492,7 @@ metric: <
 			},
 			out: output{
 				headers: map[string]string{
-					"Content-Type": `text/plain; version=0.0.4`,
+					"Content-Type": `text/plain; version=0.0.4; charset=utf-8`,
 				},
 				body: bytes.Join(
 					[][]byte{
@@ -452,6 +584,114 @@ metric: <
 				externalMetricFamilyWithSameName,
 			},
 		},
+		{ // 16
+			headers: map[string]string{
+				"Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=compact-text",
+			},
+			out: output{
+				headers: map[string]string{
+					"Content-Type": `text/plain; charset=utf-8`,
+				},
+				body: expectedMetricFamilyInvalidLabelValueAsText,
+			},
+			collector: metricVec,
+			externalMF: []*dto.MetricFamily{
+				externalMetricFamily,
+				externalMetricFamilyWithInvalidLabelValue,
+			},
+		},
+		{ // 17
+			headers: map[string]string{
+				"Accept": "text/plain",
+			},
+			out: output{
+				headers: map[string]string{
+					"Content-Type": `text/plain; version=0.0.4; charset=utf-8`,
+				},
+				body: expectedMetricFamilyAsText,
+			},
+			collector: uncheckedCollector{metricVec},
+		},
+		{ // 18
+			headers: map[string]string{
+				"Accept": "text/plain",
+			},
+			out: output{
+				headers: map[string]string{
+					"Content-Type": `text/plain; charset=utf-8`,
+				},
+				body: histogramCountCollisionMsg,
+			},
+			collector: histogram,
+			externalMF: []*dto.MetricFamily{
+				externalMetricFamilyWithCountSuffix,
+			},
+		},
+		{ // 19
+			headers: map[string]string{
+				"Accept": "text/plain",
+			},
+			out: output{
+				headers: map[string]string{
+					"Content-Type": `text/plain; charset=utf-8`,
+				},
+				body: bucketCollisionMsg,
+			},
+			collector: histogram,
+			externalMF: []*dto.MetricFamily{
+				externalMetricFamilyWithBucketSuffix,
+			},
+		},
+		{ // 20
+			headers: map[string]string{
+				"Accept": "text/plain",
+			},
+			out: output{
+				headers: map[string]string{
+					"Content-Type": `text/plain; charset=utf-8`,
+				},
+				body: summaryCountCollisionMsg,
+			},
+			collector: summary,
+			externalMF: []*dto.MetricFamily{
+				externalMetricFamilyWithCountSuffix,
+			},
+		},
+		{ // 21
+			headers: map[string]string{
+				"Accept": "text/plain",
+			},
+			out: output{
+				headers: map[string]string{
+					"Content-Type": `text/plain; version=0.0.4; charset=utf-8`,
+				},
+				body: bytes.Join(
+					[][]byte{
+						summaryAsText,
+						externalMetricFamilyWithBucketSuffixAsText,
+					},
+					[]byte{},
+				),
+			},
+			collector: summary,
+			externalMF: []*dto.MetricFamily{
+				externalMetricFamilyWithBucketSuffix,
+			},
+		},
+		{ // 22
+			headers: map[string]string{
+				"Accept": "text/plain",
+			},
+			out: output{
+				headers: map[string]string{
+					"Content-Type": `text/plain; charset=utf-8`,
+				},
+				body: duplicateLabelMsg,
+			},
+			externalMF: []*dto.MetricFamily{
+				externalMetricFamilyWithDuplicateLabel,
+			},
+		},
 	}
 	for i, scenario := range scenarios {
 		registry := prometheus.NewPedanticRegistry()
@@ -466,7 +706,7 @@ metric: <
 		}
 
 		if scenario.collector != nil {
-			registry.Register(scenario.collector)
+			registry.MustRegister(scenario.collector)
 		}
 		writer := httptest.NewRecorder()
 		handler := prometheus.InstrumentHandler("prometheus", promhttp.HandlerFor(gatherer, promhttp.HandlerOpts{}))
@@ -477,7 +717,7 @@ metric: <
 		handler(writer, request)
 
 		for key, value := range scenario.out.headers {
-			if writer.HeaderMap.Get(key) != value {
+			if writer.Header().Get(key) != value {
 				t.Errorf(
 					"%d. expected %q for header %q, got %q",
 					i, value, key, writer.Header().Get(key),
@@ -504,14 +744,8 @@ func BenchmarkHandler(b *testing.B) {
 	}
 }
 
-func TestRegisterWithOrGet(t *testing.T) {
-	// Replace the default registerer just to be sure. This is bad, but this
-	// whole test will go away once RegisterOrGet is removed.
-	oldRegisterer := prometheus.DefaultRegisterer
-	defer func() {
-		prometheus.DefaultRegisterer = oldRegisterer
-	}()
-	prometheus.DefaultRegisterer = prometheus.NewRegistry()
+func TestAlreadyRegistered(t *testing.T) {
+	reg := prometheus.NewRegistry()
 	original := prometheus.NewCounterVec(
 		prometheus.CounterOpts{
 			Name: "test",
@@ -526,20 +760,221 @@ func TestRegisterWithOrGet(t *testing.T) {
 		},
 		[]string{"foo", "bar"},
 	)
-	if err := prometheus.Register(original); err != nil {
+	var err error
+	if err = reg.Register(original); err != nil {
 		t.Fatal(err)
 	}
-	if err := prometheus.Register(equalButNotSame); err == nil {
-		t.Fatal("expected error when registringe equal collector")
+	if err = reg.Register(equalButNotSame); err == nil {
+		t.Fatal("expected error when registering equal collector")
+	}
+	if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
+		if are.ExistingCollector != original {
+			t.Error("expected original collector but got something else")
+		}
+		if are.ExistingCollector == equalButNotSame {
+			t.Error("expected original callector but got new one")
+		}
+	} else {
+		t.Error("unexpected error:", err)
+	}
+}
+
+// TestHistogramVecRegisterGatherConcurrency is an end-to-end test that
+// concurrently calls Observe on random elements of a HistogramVec while the
+// same HistogramVec is registered concurrently and the Gather method of the
+// registry is called concurrently.
+func TestHistogramVecRegisterGatherConcurrency(t *testing.T) {
+	labelNames := make([]string, 16) // Need at least 13 to expose #512.
+	for i := range labelNames {
+		labelNames[i] = fmt.Sprint("label_", i)
+	}
+
+	var (
+		reg = prometheus.NewPedanticRegistry()
+		hv  = prometheus.NewHistogramVec(
+			prometheus.HistogramOpts{
+				Name:        "test_histogram",
+				Help:        "This helps testing.",
+				ConstLabels: prometheus.Labels{"foo": "bar"},
+			},
+			labelNames,
+		)
+		labelValues = []string{"a", "b", "c", "alpha", "beta", "gamma", "aleph", "beth", "gimel"}
+		quit        = make(chan struct{})
+		wg          sync.WaitGroup
+	)
+
+	observe := func() {
+		defer wg.Done()
+		for {
+			select {
+			case <-quit:
+				return
+			default:
+				obs := rand.NormFloat64()*.1 + .2
+				values := make([]string, 0, len(labelNames))
+				for range labelNames {
+					values = append(values, labelValues[rand.Intn(len(labelValues))])
+				}
+				hv.WithLabelValues(values...).Observe(obs)
+			}
+		}
 	}
-	existing, err := prometheus.RegisterOrGet(equalButNotSame)
+
+	register := func() {
+		defer wg.Done()
+		for {
+			select {
+			case <-quit:
+				return
+			default:
+				if err := reg.Register(hv); err != nil {
+					if _, ok := err.(prometheus.AlreadyRegisteredError); !ok {
+						t.Error("Registering failed:", err)
+					}
+				}
+				time.Sleep(7 * time.Millisecond)
+			}
+		}
+	}
+
+	gather := func() {
+		defer wg.Done()
+		for {
+			select {
+			case <-quit:
+				return
+			default:
+				if g, err := reg.Gather(); err != nil {
+					t.Error("Gathering failed:", err)
+				} else {
+					if len(g) == 0 {
+						continue
+					}
+					if len(g) != 1 {
+						t.Error("Gathered unexpected number of metric families:", len(g))
+					}
+					if len(g[0].Metric[0].Label) != len(labelNames)+1 {
+						t.Error("Gathered unexpected number of label pairs:", len(g[0].Metric[0].Label))
+					}
+				}
+				time.Sleep(4 * time.Millisecond)
+			}
+		}
+	}
+
+	wg.Add(10)
+	go observe()
+	go observe()
+	go register()
+	go observe()
+	go gather()
+	go observe()
+	go register()
+	go observe()
+	go gather()
+	go observe()
+
+	time.Sleep(time.Second)
+	close(quit)
+	wg.Wait()
+}
+
+func TestWriteToTextfile(t *testing.T) {
+	expectedOut := `# HELP test_counter test counter
+# TYPE test_counter counter
+test_counter{name="qux"} 1
+# HELP test_gauge test gauge
+# TYPE test_gauge gauge
+test_gauge{name="baz"} 1.1
+# HELP test_hist test histogram
+# TYPE test_hist histogram
+test_hist_bucket{name="bar",le="0.005"} 0
+test_hist_bucket{name="bar",le="0.01"} 0
+test_hist_bucket{name="bar",le="0.025"} 0
+test_hist_bucket{name="bar",le="0.05"} 0
+test_hist_bucket{name="bar",le="0.1"} 0
+test_hist_bucket{name="bar",le="0.25"} 0
+test_hist_bucket{name="bar",le="0.5"} 0
+test_hist_bucket{name="bar",le="1"} 1
+test_hist_bucket{name="bar",le="2.5"} 1
+test_hist_bucket{name="bar",le="5"} 2
+test_hist_bucket{name="bar",le="10"} 2
+test_hist_bucket{name="bar",le="+Inf"} 2
+test_hist_sum{name="bar"} 3.64
+test_hist_count{name="bar"} 2
+# HELP test_summary test summary
+# TYPE test_summary summary
+test_summary{name="foo",quantile="0.5"} 10
+test_summary{name="foo",quantile="0.9"} 20
+test_summary{name="foo",quantile="0.99"} 20
+test_summary_sum{name="foo"} 30
+test_summary_count{name="foo"} 2
+`
+
+	registry := prometheus.NewRegistry()
+
+	summary := prometheus.NewSummaryVec(
+		prometheus.SummaryOpts{
+			Name: "test_summary",
+			Help: "test summary",
+		},
+		[]string{"name"},
+	)
+
+	histogram := prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Name: "test_hist",
+			Help: "test histogram",
+		},
+		[]string{"name"},
+	)
+
+	gauge := prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Name: "test_gauge",
+			Help: "test gauge",
+		},
+		[]string{"name"},
+	)
+
+	counter := prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Name: "test_counter",
+			Help: "test counter",
+		},
+		[]string{"name"},
+	)
+
+	registry.MustRegister(summary)
+	registry.MustRegister(histogram)
+	registry.MustRegister(gauge)
+	registry.MustRegister(counter)
+
+	summary.With(prometheus.Labels{"name": "foo"}).Observe(10)
+	summary.With(prometheus.Labels{"name": "foo"}).Observe(20)
+	histogram.With(prometheus.Labels{"name": "bar"}).Observe(0.93)
+	histogram.With(prometheus.Labels{"name": "bar"}).Observe(2.71)
+	gauge.With(prometheus.Labels{"name": "baz"}).Set(1.1)
+	counter.With(prometheus.Labels{"name": "qux"}).Inc()
+
+	tmpfile, err := ioutil.TempFile("", "prom_registry_test")
 	if err != nil {
 		t.Fatal(err)
 	}
-	if existing != original {
-		t.Error("expected original collector but got something else")
+	defer os.Remove(tmpfile.Name())
+
+	if err := prometheus.WriteToTextfile(tmpfile.Name(), registry); err != nil {
+		t.Fatal(err)
+	}
+
+	fileBytes, err := ioutil.ReadFile(tmpfile.Name())
+	if err != nil {
+		t.Fatal(err)
 	}
-	if existing == equalButNotSame {
-		t.Error("expected original callector but got new one")
+	fileContents := string(fileBytes)
+
+	if fileContents != expectedOut {
+		t.Error("file contents didn't match unexpected")
 	}
 }
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/summary.go b/vendor/github.com/prometheus/client_golang/prometheus/summary.go
index bce05bf9a027621ecb52680c4b04bbdcd81d2262..2980614dff4bd0a9c16d9d0036e413aae98249aa 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/summary.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/summary.go
@@ -36,7 +36,10 @@ 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.
+// 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.
 //
 // 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
@@ -54,6 +57,9 @@ 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}
 
@@ -75,8 +81,10 @@ const (
 )
 
 // SummaryOpts bundles the options for creating a Summary metric. 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.
+// 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.
 type SummaryOpts struct {
 	// Namespace, Subsystem, and Name are components of the fully-qualified
 	// name of the Summary (created by joining these components with
@@ -87,35 +95,39 @@ type SummaryOpts struct {
 	Subsystem string
 	Name      string
 
-	// Help provides information about this Summary. Mandatory!
+	// Help provides information about this Summary.
 	//
 	// Metrics with the same fully-qualified name must have the same Help
 	// string.
 	Help string
 
-	// 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.
+	// 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.
 	//
-	// 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.
+	// 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.
 	//
-	// 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 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
 	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.
+	// 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.
 	Objectives map[float64]float64
 
 	// MaxAge defines the duration for which an observation stays relevant
@@ -169,7 +181,7 @@ func NewSummary(opts SummaryOpts) Summary {
 
 func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
 	if len(desc.variableLabels) != len(labelValues) {
-		panic(errInconsistentCardinality)
+		panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, labelValues))
 	}
 
 	for _, n := range desc.variableLabels {
@@ -183,7 +195,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
 		}
 	}
 
-	if len(opts.Objectives) == 0 {
+	if opts.Objectives == nil {
 		opts.Objectives = DefObjectives
 	}
 
@@ -390,13 +402,21 @@ 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. At least one label name must be
-// provided.
+// 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.
 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,
@@ -404,47 +424,116 @@ 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 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...)
+// 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...)
 	if metric != nil {
-		return metric.(Summary), err
+		return metric.(Observer), err
 	}
 	return nil, err
 }
 
-// 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)
+// 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)
 	if metric != nil {
-		return metric.(Summary), err
+		return metric.(Observer), 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
+// GetMetricWithLabelValues would have returned an error. Not returning an
+// error allows shortcuts like
 //     myVec.WithLabelValues("404", "GET").Observe(42.21)
-func (m *SummaryVec) WithLabelValues(lvs ...string) Summary {
-	return m.MetricVec.WithLabelValues(lvs...).(Summary)
+func (v *SummaryVec) WithLabelValues(lvs ...string) Observer {
+	s, err := v.GetMetricWithLabelValues(lvs...)
+	if err != nil {
+		panic(err)
+	}
+	return s
 }
 
 // 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 *SummaryVec) With(labels Labels) Summary {
-	return m.MetricVec.With(labels).(Summary)
+// 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
 }
 
 type constSummary struct {
@@ -497,7 +586,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.
+// consistent with the variable labels in Desc or if Desc is invalid.
 func NewConstSummary(
 	desc *Desc,
 	count uint64,
@@ -505,8 +594,11 @@ func NewConstSummary(
 	quantiles map[float64]float64,
 	labelValues ...string,
 ) (Metric, error) {
-	if len(desc.variableLabels) != len(labelValues) {
-		return nil, errInconsistentCardinality
+	if desc.err != nil {
+		return nil, desc.err
+	}
+	if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
+		return nil, err
 	}
 	return &constSummary{
 		desc:       desc,
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/summary_test.go b/vendor/github.com/prometheus/client_golang/prometheus/summary_test.go
index b14c997bc361f52d3f688bd84f28daa954c3284d..8b1a62eeee0f30d8a6a0b094639270dc1bf065f2 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/summary_test.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/summary_test.go
@@ -25,6 +25,70 @@ import (
 	dto "github.com/prometheus/client_model/go"
 )
 
+func TestSummaryWithDefaultObjectives(t *testing.T) {
+	reg := NewRegistry()
+	summaryWithDefaultObjectives := NewSummary(SummaryOpts{
+		Name: "default_objectives",
+		Help: "Test help.",
+	})
+	if err := reg.Register(summaryWithDefaultObjectives); err != nil {
+		t.Error(err)
+	}
+
+	m := &dto.Metric{}
+	if err := summaryWithDefaultObjectives.Write(m); err != nil {
+		t.Error(err)
+	}
+	if len(m.GetSummary().Quantile) != len(DefObjectives) {
+		t.Error("expected default objectives in summary")
+	}
+}
+
+func TestSummaryWithoutObjectives(t *testing.T) {
+	reg := NewRegistry()
+	summaryWithEmptyObjectives := NewSummary(SummaryOpts{
+		Name:       "empty_objectives",
+		Help:       "Test help.",
+		Objectives: map[float64]float64{},
+	})
+	if err := reg.Register(summaryWithEmptyObjectives); err != nil {
+		t.Error(err)
+	}
+
+	m := &dto.Metric{}
+	if err := summaryWithEmptyObjectives.Write(m); err != nil {
+		t.Error(err)
+	}
+	if len(m.GetSummary().Quantile) != 0 {
+		t.Error("expected no objectives in summary")
+	}
+}
+
+func TestSummaryWithQuantileLabel(t *testing.T) {
+	defer func() {
+		if r := recover(); r == nil {
+			t.Error("Attempt to create Summary with 'quantile' label did not panic.")
+		}
+	}()
+	_ = NewSummary(SummaryOpts{
+		Name:        "test_summary",
+		Help:        "less",
+		ConstLabels: Labels{"quantile": "test"},
+	})
+}
+
+func TestSummaryVecWithQuantileLabel(t *testing.T) {
+	defer func() {
+		if r := recover(); r == nil {
+			t.Error("Attempt to create SummaryVec with 'quantile' label did not panic.")
+		}
+	}()
+	_ = NewSummaryVec(SummaryOpts{
+		Name: "test_summary",
+		Help: "less",
+	}, []string{"quantile"})
+}
+
 func benchmarkSummaryObserve(w int, b *testing.B) {
 	b.StopTimer()
 
@@ -136,8 +200,9 @@ func TestSummaryConcurrency(t *testing.T) {
 		end.Add(concLevel)
 
 		sum := NewSummary(SummaryOpts{
-			Name: "test_summary",
-			Help: "helpless",
+			Name:       "test_summary",
+			Help:       "helpless",
+			Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
 		})
 
 		allVars := make([]float64, total)
@@ -223,8 +288,9 @@ func TestSummaryVecConcurrency(t *testing.T) {
 
 		sum := NewSummaryVec(
 			SummaryOpts{
-				Name: "test_summary",
-				Help: "helpless",
+				Name:       "test_summary",
+				Help:       "helpless",
+				Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
 			},
 			[]string{"label"},
 		)
@@ -260,7 +326,7 @@ func TestSummaryVecConcurrency(t *testing.T) {
 		for i := 0; i < vecLength; i++ {
 			m := &dto.Metric{}
 			s := sum.WithLabelValues(string('A' + i))
-			s.Write(m)
+			s.(Summary).Write(m)
 			if got, want := int(*m.Summary.SampleCount), len(allVars[i]); got != want {
 				t.Errorf("got sample count %d for label %c, want %d", got, 'A'+i, want)
 			}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/testutil/testutil.go b/vendor/github.com/prometheus/client_golang/prometheus/testutil/testutil.go
new file mode 100644
index 0000000000000000000000000000000000000000..e344cc853c2144070c0973a1a24afe9f4dc95cc9
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/testutil/testutil.go
@@ -0,0 +1,189 @@
+// 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 testutil provides helpers to test code using the prometheus package
+// of client_golang.
+//
+// While writing unit tests to verify correct instrumentation of your code, it's
+// a common mistake to mostly test the instrumentation library instead of your
+// own code. Rather than verifying that a prometheus.Counter's value has changed
+// as expected or that it shows up in the exposition after registration, it is
+// in general more robust and more faithful to the concept of unit tests to use
+// mock implementations of the prometheus.Counter and prometheus.Registerer
+// interfaces that simply assert that the Add or Register methods have been
+// called with the expected arguments. However, this might be overkill in simple
+// scenarios. The ToFloat64 function is provided for simple inspection of a
+// single-value metric, but it has to be used with caution.
+//
+// End-to-end tests to verify all or larger parts of the metrics exposition can
+// be implemented with the CollectAndCompare or GatherAndCompare functions. The
+// most appropriate use is not so much testing instrumentation of your code, but
+// testing custom prometheus.Collector implementations and in particular whole
+// exporters, i.e. programs that retrieve telemetry data from a 3rd party source
+// and convert it into Prometheus metrics.
+package testutil
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+
+	"github.com/prometheus/common/expfmt"
+
+	dto "github.com/prometheus/client_model/go"
+
+	"github.com/prometheus/client_golang/prometheus"
+	"github.com/prometheus/client_golang/prometheus/internal"
+)
+
+// ToFloat64 collects all Metrics from the provided Collector. It expects that
+// this results in exactly one Metric being collected, which must be a Gauge,
+// Counter, or Untyped. In all other cases, ToFloat64 panics. ToFloat64 returns
+// the value of the collected Metric.
+//
+// The Collector provided is typically a simple instance of Gauge or Counter, or
+// – less commonly – a GaugeVec or CounterVec with exactly one element. But any
+// Collector fulfilling the prerequisites described above will do.
+//
+// Use this function with caution. It is computationally very expensive and thus
+// not suited at all to read values from Metrics in regular code. This is really
+// only for testing purposes, and even for testing, other approaches are often
+// more appropriate (see this package's documentation).
+//
+// A clear anti-pattern would be to use a metric type from the prometheus
+// package to track values that are also needed for something else than the
+// exposition of Prometheus metrics. For example, you would like to track the
+// number of items in a queue because your code should reject queuing further
+// items if a certain limit is reached. It is tempting to track the number of
+// items in a prometheus.Gauge, as it is then easily available as a metric for
+// exposition, too. However, then you would need to call ToFloat64 in your
+// regular code, potentially quite often. The recommended way is to track the
+// number of items conventionally (in the way you would have done it without
+// considering Prometheus metrics) and then expose the number with a
+// prometheus.GaugeFunc.
+func ToFloat64(c prometheus.Collector) float64 {
+	var (
+		m      prometheus.Metric
+		mCount int
+		mChan  = make(chan prometheus.Metric)
+		done   = make(chan struct{})
+	)
+
+	go func() {
+		for m = range mChan {
+			mCount++
+		}
+		close(done)
+	}()
+
+	c.Collect(mChan)
+	close(mChan)
+	<-done
+
+	if mCount != 1 {
+		panic(fmt.Errorf("collected %d metrics instead of exactly 1", mCount))
+	}
+
+	pb := &dto.Metric{}
+	m.Write(pb)
+	if pb.Gauge != nil {
+		return pb.Gauge.GetValue()
+	}
+	if pb.Counter != nil {
+		return pb.Counter.GetValue()
+	}
+	if pb.Untyped != nil {
+		return pb.Untyped.GetValue()
+	}
+	panic(fmt.Errorf("collected a non-gauge/counter/untyped metric: %s", pb))
+}
+
+// CollectAndCompare registers the provided Collector with a newly created
+// pedantic Registry. It then does the same as GatherAndCompare, gathering the
+// metrics from the pedantic Registry.
+func CollectAndCompare(c prometheus.Collector, expected io.Reader, metricNames ...string) error {
+	reg := prometheus.NewPedanticRegistry()
+	if err := reg.Register(c); err != nil {
+		return fmt.Errorf("registering collector failed: %s", err)
+	}
+	return GatherAndCompare(reg, expected, metricNames...)
+}
+
+// GatherAndCompare gathers all metrics from the provided Gatherer and compares
+// it to an expected output read from the provided Reader in the Prometheus text
+// exposition format. If any metricNames are provided, only metrics with those
+// names are compared.
+func GatherAndCompare(g prometheus.Gatherer, expected io.Reader, metricNames ...string) error {
+	got, err := g.Gather()
+	if err != nil {
+		return fmt.Errorf("gathering metrics failed: %s", err)
+	}
+	if metricNames != nil {
+		got = filterMetrics(got, metricNames)
+	}
+	var tp expfmt.TextParser
+	wantRaw, err := tp.TextToMetricFamilies(expected)
+	if err != nil {
+		return fmt.Errorf("parsing expected metrics failed: %s", err)
+	}
+	want := internal.NormalizeMetricFamilies(wantRaw)
+
+	return compare(got, want)
+}
+
+// compare encodes both provided slices of metric families into the text format,
+// compares their string message, and returns an error if they do not match.
+// The error contains the encoded text of both the desired and the actual
+// result.
+func compare(got, want []*dto.MetricFamily) error {
+	var gotBuf, wantBuf bytes.Buffer
+	enc := expfmt.NewEncoder(&gotBuf, expfmt.FmtText)
+	for _, mf := range got {
+		if err := enc.Encode(mf); err != nil {
+			return fmt.Errorf("encoding gathered metrics failed: %s", err)
+		}
+	}
+	enc = expfmt.NewEncoder(&wantBuf, expfmt.FmtText)
+	for _, mf := range want {
+		if err := enc.Encode(mf); err != nil {
+			return fmt.Errorf("encoding expected metrics failed: %s", err)
+		}
+	}
+
+	if wantBuf.String() != gotBuf.String() {
+		return fmt.Errorf(`
+metric output does not match expectation; want:
+
+%s
+
+got:
+
+%s
+`, wantBuf.String(), gotBuf.String())
+
+	}
+	return nil
+}
+
+func filterMetrics(metrics []*dto.MetricFamily, names []string) []*dto.MetricFamily {
+	var filtered []*dto.MetricFamily
+	for _, m := range metrics {
+		for _, name := range names {
+			if m.GetName() == name {
+				filtered = append(filtered, m)
+				break
+			}
+		}
+	}
+	return filtered
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/testutil/testutil_test.go b/vendor/github.com/prometheus/client_golang/prometheus/testutil/testutil_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..f9600779e8015bf78ec3944fa05b7a49b24f1b05
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/testutil/testutil_test.go
@@ -0,0 +1,311 @@
+// 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 testutil
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+type untypedCollector struct{}
+
+func (u untypedCollector) Describe(c chan<- *prometheus.Desc) {
+	c <- prometheus.NewDesc("name", "help", nil, nil)
+}
+
+func (u untypedCollector) Collect(c chan<- prometheus.Metric) {
+	c <- prometheus.MustNewConstMetric(
+		prometheus.NewDesc("name", "help", nil, nil),
+		prometheus.UntypedValue,
+		2001,
+	)
+}
+
+func TestToFloat64(t *testing.T) {
+	gaugeWithAValueSet := prometheus.NewGauge(prometheus.GaugeOpts{})
+	gaugeWithAValueSet.Set(3.14)
+
+	counterVecWithOneElement := prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"foo"})
+	counterVecWithOneElement.WithLabelValues("bar").Inc()
+
+	counterVecWithTwoElements := prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"foo"})
+	counterVecWithTwoElements.WithLabelValues("bar").Add(42)
+	counterVecWithTwoElements.WithLabelValues("baz").Inc()
+
+	histogramVecWithOneElement := prometheus.NewHistogramVec(prometheus.HistogramOpts{}, []string{"foo"})
+	histogramVecWithOneElement.WithLabelValues("bar").Observe(2.7)
+
+	scenarios := map[string]struct {
+		collector prometheus.Collector
+		panics    bool
+		want      float64
+	}{
+		"simple counter": {
+			collector: prometheus.NewCounter(prometheus.CounterOpts{}),
+			panics:    false,
+			want:      0,
+		},
+		"simple gauge": {
+			collector: prometheus.NewGauge(prometheus.GaugeOpts{}),
+			panics:    false,
+			want:      0,
+		},
+		"simple untyped": {
+			collector: untypedCollector{},
+			panics:    false,
+			want:      2001,
+		},
+		"simple histogram": {
+			collector: prometheus.NewHistogram(prometheus.HistogramOpts{}),
+			panics:    true,
+		},
+		"simple summary": {
+			collector: prometheus.NewSummary(prometheus.SummaryOpts{}),
+			panics:    true,
+		},
+		"simple gauge with an actual value set": {
+			collector: gaugeWithAValueSet,
+			panics:    false,
+			want:      3.14,
+		},
+		"counter vec with zero elements": {
+			collector: prometheus.NewCounterVec(prometheus.CounterOpts{}, nil),
+			panics:    true,
+		},
+		"counter vec with one element": {
+			collector: counterVecWithOneElement,
+			panics:    false,
+			want:      1,
+		},
+		"counter vec with two elements": {
+			collector: counterVecWithTwoElements,
+			panics:    true,
+		},
+		"histogram vec with one element": {
+			collector: histogramVecWithOneElement,
+			panics:    true,
+		},
+	}
+
+	for n, s := range scenarios {
+		t.Run(n, func(t *testing.T) {
+			defer func() {
+				r := recover()
+				if r == nil && s.panics {
+					t.Error("expected panic")
+				} else if r != nil && !s.panics {
+					t.Error("unexpected panic: ", r)
+				}
+				// Any other combination is the expected outcome.
+			}()
+			if got := ToFloat64(s.collector); got != s.want {
+				t.Errorf("want %f, got %f", s.want, got)
+			}
+		})
+	}
+}
+
+func TestCollectAndCompare(t *testing.T) {
+	const metadata = `
+		# HELP some_total A value that represents a counter.
+		# TYPE some_total counter
+	`
+
+	c := prometheus.NewCounter(prometheus.CounterOpts{
+		Name: "some_total",
+		Help: "A value that represents a counter.",
+		ConstLabels: prometheus.Labels{
+			"label1": "value1",
+		},
+	})
+	c.Inc()
+
+	expected := `
+
+		some_total{ label1 = "value1" } 1
+	`
+
+	if err := CollectAndCompare(c, strings.NewReader(metadata+expected), "some_total"); err != nil {
+		t.Errorf("unexpected collecting result:\n%s", err)
+	}
+}
+
+func TestCollectAndCompareNoLabel(t *testing.T) {
+	const metadata = `
+		# HELP some_total A value that represents a counter.
+		# TYPE some_total counter
+	`
+
+	c := prometheus.NewCounter(prometheus.CounterOpts{
+		Name: "some_total",
+		Help: "A value that represents a counter.",
+	})
+	c.Inc()
+
+	expected := `
+
+		some_total 1
+	`
+
+	if err := CollectAndCompare(c, strings.NewReader(metadata+expected), "some_total"); err != nil {
+		t.Errorf("unexpected collecting result:\n%s", err)
+	}
+}
+
+func TestCollectAndCompareHistogram(t *testing.T) {
+	inputs := []struct {
+		name        string
+		c           prometheus.Collector
+		metadata    string
+		expect      string
+		labels      []string
+		observation float64
+	}{
+		{
+			name: "Testing Histogram Collector",
+			c: prometheus.NewHistogram(prometheus.HistogramOpts{
+				Name:    "some_histogram",
+				Help:    "An example of a histogram",
+				Buckets: []float64{1, 2, 3},
+			}),
+			metadata: `
+				# HELP some_histogram An example of a histogram
+				# TYPE some_histogram histogram
+			`,
+			expect: `
+				some_histogram{le="1"} 0
+				some_histogram{le="2"} 0
+				some_histogram{le="3"} 1
+        			some_histogram_bucket{le="+Inf"} 1
+        			some_histogram_sum 2.5
+        			some_histogram_count 1
+
+			`,
+			observation: 2.5,
+		},
+		{
+			name: "Testing HistogramVec Collector",
+			c: prometheus.NewHistogramVec(prometheus.HistogramOpts{
+				Name:    "some_histogram",
+				Help:    "An example of a histogram",
+				Buckets: []float64{1, 2, 3},
+			}, []string{"test"}),
+
+			metadata: `
+				# HELP some_histogram An example of a histogram
+				# TYPE some_histogram histogram
+			`,
+			expect: `
+            			some_histogram_bucket{test="test",le="1"} 0
+            			some_histogram_bucket{test="test",le="2"} 0
+            			some_histogram_bucket{test="test",le="3"} 1
+            			some_histogram_bucket{test="test",le="+Inf"} 1
+            			some_histogram_sum{test="test"} 2.5
+           		 	some_histogram_count{test="test"} 1
+		
+			`,
+			observation: 2.5,
+		},
+	}
+
+	for _, input := range inputs {
+		switch collector := input.c.(type) {
+		case prometheus.Histogram:
+			collector.Observe(input.observation)
+		case *prometheus.HistogramVec:
+			collector.WithLabelValues("test").Observe(input.observation)
+		default:
+			t.Fatalf("unsuported collector tested")
+
+		}
+
+		t.Run(input.name, func(t *testing.T) {
+			if err := CollectAndCompare(input.c, strings.NewReader(input.metadata+input.expect)); err != nil {
+				t.Errorf("unexpected collecting result:\n%s", err)
+			}
+		})
+
+	}
+}
+
+func TestNoMetricFilter(t *testing.T) {
+	const metadata = `
+		# HELP some_total A value that represents a counter.
+		# TYPE some_total counter
+	`
+
+	c := prometheus.NewCounter(prometheus.CounterOpts{
+		Name: "some_total",
+		Help: "A value that represents a counter.",
+		ConstLabels: prometheus.Labels{
+			"label1": "value1",
+		},
+	})
+	c.Inc()
+
+	expected := `
+		some_total{label1="value1"} 1
+	`
+
+	if err := CollectAndCompare(c, strings.NewReader(metadata+expected)); err != nil {
+		t.Errorf("unexpected collecting result:\n%s", err)
+	}
+}
+
+func TestMetricNotFound(t *testing.T) {
+	const metadata = `
+		# HELP some_other_metric A value that represents a counter.
+		# TYPE some_other_metric counter
+	`
+
+	c := prometheus.NewCounter(prometheus.CounterOpts{
+		Name: "some_total",
+		Help: "A value that represents a counter.",
+		ConstLabels: prometheus.Labels{
+			"label1": "value1",
+		},
+	})
+	c.Inc()
+
+	expected := `
+		some_other_metric{label1="value1"} 1
+	`
+
+	expectedError := `
+metric output does not match expectation; want:
+
+# HELP some_other_metric A value that represents a counter.
+# TYPE some_other_metric counter
+some_other_metric{label1="value1"} 1
+
+
+got:
+
+# HELP some_total A value that represents a counter.
+# TYPE some_total counter
+some_total{label1="value1"} 1
+
+`
+
+	err := CollectAndCompare(c, strings.NewReader(metadata+expected))
+	if err == nil {
+		t.Error("Expected error, got no error.")
+	}
+
+	if err.Error() != expectedError {
+		t.Errorf("Expected\n%#+v\nGot:\n%#+v\n", expectedError, err.Error())
+	}
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/timer.go b/vendor/github.com/prometheus/client_golang/prometheus/timer.go
new file mode 100644
index 0000000000000000000000000000000000000000..8d5f10523375a542132139ddfe1fdd576b35d88a
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/timer.go
@@ -0,0 +1,54 @@
+// Copyright 2016 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import "time"
+
+// Timer is a helper type to time functions. Use NewTimer to create new
+// instances.
+type Timer struct {
+	begin    time.Time
+	observer Observer
+}
+
+// NewTimer creates a new Timer. The provided Observer is used to observe a
+// duration in seconds. Timer is usually used to time a function call in the
+// following way:
+//    func TimeMe() {
+//        timer := NewTimer(myHistogram)
+//        defer timer.ObserveDuration()
+//        // Do actual work.
+//    }
+func NewTimer(o Observer) *Timer {
+	return &Timer{
+		begin:    time.Now(),
+		observer: o,
+	}
+}
+
+// ObserveDuration records the duration passed since the Timer was created with
+// NewTimer. It calls the Observe method of the Observer provided during
+// construction with the duration in seconds as an argument. The observed
+// duration is also returned. ObserveDuration is usually called with a defer
+// statement.
+//
+// Note that this method is only guaranteed to never observe negative durations
+// if used with Go1.9+.
+func (t *Timer) ObserveDuration() time.Duration {
+	d := time.Since(t.begin)
+	if t.observer != nil {
+		t.observer.Observe(d.Seconds())
+	}
+	return d
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/timer_test.go b/vendor/github.com/prometheus/client_golang/prometheus/timer_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..294902068666f8fc248a4b7c447180b8bf62bf98
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/timer_test.go
@@ -0,0 +1,152 @@
+// 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 (
+	"testing"
+
+	dto "github.com/prometheus/client_model/go"
+)
+
+func TestTimerObserve(t *testing.T) {
+	var (
+		his   = NewHistogram(HistogramOpts{Name: "test_histogram"})
+		sum   = NewSummary(SummaryOpts{Name: "test_summary"})
+		gauge = NewGauge(GaugeOpts{Name: "test_gauge"})
+	)
+
+	func() {
+		hisTimer := NewTimer(his)
+		sumTimer := NewTimer(sum)
+		gaugeTimer := NewTimer(ObserverFunc(gauge.Set))
+		defer hisTimer.ObserveDuration()
+		defer sumTimer.ObserveDuration()
+		defer gaugeTimer.ObserveDuration()
+	}()
+
+	m := &dto.Metric{}
+	his.Write(m)
+	if want, got := uint64(1), m.GetHistogram().GetSampleCount(); want != got {
+		t.Errorf("want %d observations for histogram, got %d", want, got)
+	}
+	m.Reset()
+	sum.Write(m)
+	if want, got := uint64(1), m.GetSummary().GetSampleCount(); want != got {
+		t.Errorf("want %d observations for summary, got %d", want, got)
+	}
+	m.Reset()
+	gauge.Write(m)
+	if got := m.GetGauge().GetValue(); got <= 0 {
+		t.Errorf("want value > 0 for gauge, got %f", got)
+	}
+}
+
+func TestTimerEmpty(t *testing.T) {
+	emptyTimer := NewTimer(nil)
+	emptyTimer.ObserveDuration()
+	// Do nothing, just demonstrate it works without panic.
+}
+
+func TestTimerConditionalTiming(t *testing.T) {
+	var (
+		his = NewHistogram(HistogramOpts{
+			Name: "test_histogram",
+		})
+		timeMe = true
+		m      = &dto.Metric{}
+	)
+
+	timedFunc := func() {
+		timer := NewTimer(ObserverFunc(func(v float64) {
+			if timeMe {
+				his.Observe(v)
+			}
+		}))
+		defer timer.ObserveDuration()
+	}
+
+	timedFunc() // This will time.
+	his.Write(m)
+	if want, got := uint64(1), m.GetHistogram().GetSampleCount(); want != got {
+		t.Errorf("want %d observations for histogram, got %d", want, got)
+	}
+
+	timeMe = false
+	timedFunc() // This will not time again.
+	m.Reset()
+	his.Write(m)
+	if want, got := uint64(1), m.GetHistogram().GetSampleCount(); want != got {
+		t.Errorf("want %d observations for histogram, got %d", want, got)
+	}
+}
+
+func TestTimerByOutcome(t *testing.T) {
+	var (
+		his = NewHistogramVec(
+			HistogramOpts{Name: "test_histogram"},
+			[]string{"outcome"},
+		)
+		outcome = "foo"
+		m       = &dto.Metric{}
+	)
+
+	timedFunc := func() {
+		timer := NewTimer(ObserverFunc(func(v float64) {
+			his.WithLabelValues(outcome).Observe(v)
+		}))
+		defer timer.ObserveDuration()
+
+		if outcome == "foo" {
+			outcome = "bar"
+			return
+		}
+		outcome = "foo"
+	}
+
+	timedFunc()
+	his.WithLabelValues("foo").(Histogram).Write(m)
+	if want, got := uint64(0), m.GetHistogram().GetSampleCount(); want != got {
+		t.Errorf("want %d observations for 'foo' histogram, got %d", want, got)
+	}
+	m.Reset()
+	his.WithLabelValues("bar").(Histogram).Write(m)
+	if want, got := uint64(1), m.GetHistogram().GetSampleCount(); want != got {
+		t.Errorf("want %d observations for 'bar' histogram, got %d", want, got)
+	}
+
+	timedFunc()
+	m.Reset()
+	his.WithLabelValues("foo").(Histogram).Write(m)
+	if want, got := uint64(1), m.GetHistogram().GetSampleCount(); want != got {
+		t.Errorf("want %d observations for 'foo' histogram, got %d", want, got)
+	}
+	m.Reset()
+	his.WithLabelValues("bar").(Histogram).Write(m)
+	if want, got := uint64(1), m.GetHistogram().GetSampleCount(); want != got {
+		t.Errorf("want %d observations for 'bar' histogram, got %d", want, got)
+	}
+
+	timedFunc()
+	m.Reset()
+	his.WithLabelValues("foo").(Histogram).Write(m)
+	if want, got := uint64(1), m.GetHistogram().GetSampleCount(); want != got {
+		t.Errorf("want %d observations for 'foo' histogram, got %d", want, got)
+	}
+	m.Reset()
+	his.WithLabelValues("bar").(Histogram).Write(m)
+	if want, got := uint64(2), m.GetHistogram().GetSampleCount(); want != got {
+		t.Errorf("want %d observations for 'bar' histogram, got %d", want, got)
+	}
+
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/untyped.go b/vendor/github.com/prometheus/client_golang/prometheus/untyped.go
index 5faf7e6e3eba2960e858bb2640f3b276b0b5b837..0f9ce63f4093be007f8ed0ed19e5228a0f1dc2ce 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/untyped.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/untyped.go
@@ -13,108 +13,12 @@
 
 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
 
-// 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.
+// UntypedFunc works like GaugeFunc but the collected metric is of type
+// "Untyped". UntypedFunc is useful to mirror an external metric of unknown
+// type.
 //
 // 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 a944c37754fefc1b79eced52b667f55e417c73ae..eb248f10874350f3c3fdcced6a18d3c5a0fc62e3 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/value.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/value.go
@@ -14,15 +14,12 @@
 package prometheus
 
 import (
-	"errors"
 	"fmt"
-	"math"
 	"sort"
-	"sync/atomic"
-
-	dto "github.com/prometheus/client_model/go"
 
 	"github.com/golang/protobuf/proto"
+
+	dto "github.com/prometheus/client_model/go"
 )
 
 // ValueType is an enumeration of metric types that represent a simple value.
@@ -36,77 +33,6 @@ 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
@@ -151,10 +77,14 @@ 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.
+// labelValues is not consistent with the variable labels in Desc or if Desc is
+// invalid.
 func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) (Metric, error) {
-	if len(desc.variableLabels) != len(labelValues) {
-		return nil, errInconsistentCardinality
+	if desc.err != nil {
+		return nil, desc.err
+	}
+	if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
+		return nil, err
 	}
 	return &constMetric{
 		desc:       desc,
@@ -226,9 +156,7 @@ func makeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair {
 			Value: proto.String(labelValues[i]),
 		})
 	}
-	for _, lp := range desc.constLabelPairs {
-		labelPairs = append(labelPairs, lp)
-	}
-	sort.Sort(LabelPairSorter(labelPairs))
+	labelPairs = append(labelPairs, desc.constLabelPairs...)
+	sort.Sort(labelPairSorter(labelPairs))
 	return labelPairs
 }
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/value_test.go b/vendor/github.com/prometheus/client_golang/prometheus/value_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..51867b5e47b932ab91327c64bd22fe155228178a
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/value_test.go
@@ -0,0 +1,56 @@
+// 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"
+	"testing"
+)
+
+func TestNewConstMetricInvalidLabelValues(t *testing.T) {
+	testCases := []struct {
+		desc   string
+		labels Labels
+	}{
+		{
+			desc:   "non utf8 label value",
+			labels: Labels{"a": "\xFF"},
+		},
+		{
+			desc:   "not enough label values",
+			labels: Labels{},
+		},
+		{
+			desc:   "too many label values",
+			labels: Labels{"a": "1", "b": "2"},
+		},
+	}
+
+	for _, test := range testCases {
+		metricDesc := NewDesc(
+			"sample_value",
+			"sample value",
+			[]string{"a"},
+			Labels{},
+		)
+
+		expectPanic(t, func() {
+			MustNewConstMetric(metricDesc, CounterValue, 0.3, "\xFF")
+		}, fmt.Sprintf("WithLabelValues: expected panic because: %s", test.desc))
+
+		if _, err := NewConstMetric(metricDesc, CounterValue, 0.3, "\xFF"); err == nil {
+			t.Errorf("NewConstMetric: expected error because: %s", test.desc)
+		}
+	}
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/vec.go b/vendor/github.com/prometheus/client_golang/prometheus/vec.go
index 7f3eef9a46707813913a49bc466528ccd87a6c2f..14ed9e856d1c286fbc98f6919830bc7df82e7d7a 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/vec.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/vec.go
@@ -20,200 +20,253 @@ 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 usually not used directly but as a
-// building block for implementations of vectors of a given metric
-// type. GaugeVec, CounterVec, SummaryVec, and UntypedVec are examples already
-// provided in this package.
-type MetricVec struct {
-	mtx      sync.RWMutex // Protects the children.
-	children map[uint64][]metricWithLabelValues
-	desc     *Desc
-
-	newMetric   func(labelValues ...string) Metric
-	hashAdd     func(h uint64, s string) uint64 // replace hash function for testing collision handling
+// metricVec is a Collector to bundle metrics of the same name that differ in
+// their label values. metricVec is not used directly (and therefore
+// unexported). It is used as a building block for implementations of vectors of
+// a given metric type, like GaugeVec, CounterVec, SummaryVec, and HistogramVec.
+// It also handles label currying. It uses basicMetricVec internally.
+type metricVec struct {
+	*metricMap
+
+	curry []curriedLabelValue
+
+	// hashAdd and hashAddByte can be replaced for testing collision handling.
+	hashAdd     func(h uint64, s string) uint64
 	hashAddByte func(h uint64, b byte) uint64
 }
 
-// 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,
+// 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,
+		},
 		hashAdd:     hashAdd,
 		hashAddByte: hashAddByte,
 	}
 }
 
-// metricWithLabelValues provides the metric and its label values for
-// disambiguation on hash collision.
-type metricWithLabelValues struct {
-	values []string
-	metric 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 {
+	h, err := m.hashLabelValues(lvs)
+	if err != nil {
+		return false
+	}
 
-// Describe implements Collector. The length of the returned slice
-// is always one.
-func (m *MetricVec) Describe(ch chan<- *Desc) {
-	ch <- m.desc
+	return m.metricMap.deleteByHashWithLabelValues(h, lvs, m.curry)
 }
 
-// Collect implements Collector.
-func (m *MetricVec) Collect(ch chan<- Metric) {
-	m.mtx.RLock()
-	defer m.mtx.RUnlock()
+// 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
+	}
 
-	for _, metrics := range m.children {
-		for _, metric := range metrics {
-			ch <- metric.metric
+	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
 }
 
-// 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) {
+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
+	return m.metricMap.getOrCreateMetricWithLabelValues(h, lvs, m.curry), 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) {
+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
+	return m.metricMap.getOrCreateMetricWithLabels(h, labels, m.curry), 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)
+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 metric
+	return h, nil
 }
 
-// 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)
+func (m *metricVec) hashLabels(labels Labels) (uint64, error) {
+	if err := validateValuesInLabels(labels, len(m.desc.variableLabels)-len(m.curry)); err != nil {
+		return 0, err
 	}
-	return metric
+
+	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
 }
 
-// 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()
+// metricWithLabelValues provides the metric and its label values for
+// disambiguation on hash collision.
+type metricWithLabelValues struct {
+	values []string
+	metric Metric
+}
 
-	h, err := m.hashLabelValues(lvs)
-	if err != nil {
-		return false
+// curriedLabelValue sets the curried value for a label at the given index.
+type curriedLabelValue struct {
+	index int
+	value string
+}
+
+// metricMap is a helper for metricVec and shared between differently curried
+// metricVecs.
+type metricMap struct {
+	mtx       sync.RWMutex // Protects metrics.
+	metrics   map[uint64][]metricWithLabelValues
+	desc      *Desc
+	newMetric func(labelValues ...string) Metric
+}
+
+// Describe implements Collector. It will send exactly one Desc to the provided
+// channel.
+func (m *metricMap) Describe(ch chan<- *Desc) {
+	ch <- m.desc
+}
+
+// Collect implements Collector.
+func (m *metricMap) Collect(ch chan<- Metric) {
+	m.mtx.RLock()
+	defer m.mtx.RUnlock()
+
+	for _, metrics := range m.metrics {
+		for _, metric := range metrics {
+			ch <- metric.metric
+		}
 	}
-	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 {
+// Reset deletes all metrics in this vector.
+func (m *metricMap) Reset() {
 	m.mtx.Lock()
 	defer m.mtx.Unlock()
 
-	h, err := m.hashLabels(labels)
-	if err != nil {
-		return false
+	for h := range m.metrics {
+		delete(m.metrics, h)
 	}
-
-	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 *MetricVec) deleteByHashWithLabelValues(h uint64, lvs []string) bool {
-	metrics, ok := m.children[h]
+func (m *metricMap) deleteByHashWithLabelValues(
+	h uint64, lvs []string, curry []curriedLabelValue,
+) bool {
+	m.mtx.Lock()
+	defer m.mtx.Unlock()
+
+	metrics, ok := m.metrics[h]
 	if !ok {
 		return false
 	}
 
-	i := m.findMetricWithLabelValues(metrics, lvs)
+	i := findMetricWithLabelValues(metrics, lvs, curry)
 	if i >= len(metrics) {
 		return false
 	}
 
 	if len(metrics) > 1 {
-		m.children[h] = append(metrics[:i], metrics[i+1:]...)
+		m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
 	} else {
-		delete(m.children, h)
+		delete(m.metrics, h)
 	}
 	return true
 }
@@ -221,69 +274,38 @@ func (m *MetricVec) deleteByHashWithLabelValues(h uint64, lvs []string) bool {
 // 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 *MetricVec) deleteByHashWithLabels(h uint64, labels Labels) bool {
-	metrics, ok := m.children[h]
+func (m *metricMap) deleteByHashWithLabels(
+	h uint64, labels Labels, curry []curriedLabelValue,
+) bool {
+	m.mtx.Lock()
+	defer m.mtx.Unlock()
+
+	metrics, ok := m.metrics[h]
 	if !ok {
 		return false
 	}
-	i := m.findMetricWithLabels(metrics, labels)
+	i := findMetricWithLabels(m.desc, metrics, labels, curry)
 	if i >= len(metrics) {
 		return false
 	}
 
 	if len(metrics) > 1 {
-		m.children[h] = append(metrics[:i], metrics[i+1:]...)
+		m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
 	} else {
-		delete(m.children, h)
+		delete(m.metrics, 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 *MetricVec) getOrCreateMetricWithLabelValues(hash uint64, lvs []string) Metric {
+func (m *metricMap) getOrCreateMetricWithLabelValues(
+	hash uint64, lvs []string, curry []curriedLabelValue,
+) Metric {
 	m.mtx.RLock()
-	metric, ok := m.getMetricWithLabelValues(hash, lvs)
+	metric, ok := m.getMetricWithHashAndLabelValues(hash, lvs, curry)
 	m.mtx.RUnlock()
 	if ok {
 		return metric
@@ -291,13 +313,11 @@ func (m *MetricVec) getOrCreateMetricWithLabelValues(hash uint64, lvs []string)
 
 	m.mtx.Lock()
 	defer m.mtx.Unlock()
-	metric, ok = m.getMetricWithLabelValues(hash, lvs)
+	metric, ok = m.getMetricWithHashAndLabelValues(hash, lvs, curry)
 	if !ok {
-		// 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})
+		inlinedLVs := inlineLabelValues(lvs, curry)
+		metric = m.newMetric(inlinedLVs...)
+		m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: inlinedLVs, metric: metric})
 	}
 	return metric
 }
@@ -306,9 +326,11 @@ func (m *MetricVec) getOrCreateMetricWithLabelValues(hash uint64, lvs []string)
 // or creates it and returns the new one.
 //
 // This function holds the mutex.
-func (m *MetricVec) getOrCreateMetricWithLabels(hash uint64, labels Labels) Metric {
+func (m *metricMap) getOrCreateMetricWithLabels(
+	hash uint64, labels Labels, curry []curriedLabelValue,
+) Metric {
 	m.mtx.RLock()
-	metric, ok := m.getMetricWithLabels(hash, labels)
+	metric, ok := m.getMetricWithHashAndLabels(hash, labels, curry)
 	m.mtx.RUnlock()
 	if ok {
 		return metric
@@ -316,33 +338,37 @@ func (m *MetricVec) getOrCreateMetricWithLabels(hash uint64, labels Labels) Metr
 
 	m.mtx.Lock()
 	defer m.mtx.Unlock()
-	metric, ok = m.getMetricWithLabels(hash, labels)
+	metric, ok = m.getMetricWithHashAndLabels(hash, labels, curry)
 	if !ok {
-		lvs := m.extractLabelValues(labels)
+		lvs := extractLabelValues(m.desc, labels, curry)
 		metric = m.newMetric(lvs...)
-		m.children[hash] = append(m.children[hash], metricWithLabelValues{values: lvs, metric: metric})
+		m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: lvs, metric: metric})
 	}
 	return metric
 }
 
-// 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]
+// 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]
 	if ok {
-		if i := m.findMetricWithLabelValues(metrics, lvs); i < len(metrics) {
+		if i := findMetricWithLabelValues(metrics, lvs, curry); i < len(metrics) {
 			return metrics[i].metric, true
 		}
 	}
 	return nil, false
 }
 
-// getMetricWithLabels gets a metric while handling possible collisions in
+// getMetricWithHashAndLabels gets a metric while handling possible collisions in
 // the hash space. Must be called while holding read mutex.
-func (m *MetricVec) getMetricWithLabels(h uint64, labels Labels) (Metric, bool) {
-	metrics, ok := m.children[h]
+func (m *metricMap) getMetricWithHashAndLabels(
+	h uint64, labels Labels, curry []curriedLabelValue,
+) (Metric, bool) {
+	metrics, ok := m.metrics[h]
 	if ok {
-		if i := m.findMetricWithLabels(metrics, labels); i < len(metrics) {
+		if i := findMetricWithLabels(m.desc, metrics, labels, curry); i < len(metrics) {
 			return metrics[i].metric, true
 		}
 	}
@@ -351,9 +377,11 @@ func (m *MetricVec) getMetricWithLabels(h uint64, labels Labels) (Metric, bool)
 
 // findMetricWithLabelValues returns the index of the matching metric or
 // len(metrics) if not found.
-func (m *MetricVec) findMetricWithLabelValues(metrics []metricWithLabelValues, lvs []string) int {
+func findMetricWithLabelValues(
+	metrics []metricWithLabelValues, lvs []string, curry []curriedLabelValue,
+) int {
 	for i, metric := range metrics {
-		if m.matchLabelValues(metric.values, lvs) {
+		if matchLabelValues(metric.values, lvs, curry) {
 			return i
 		}
 	}
@@ -362,32 +390,51 @@ func (m *MetricVec) findMetricWithLabelValues(metrics []metricWithLabelValues, l
 
 // findMetricWithLabels returns the index of the matching metric or len(metrics)
 // if not found.
-func (m *MetricVec) findMetricWithLabels(metrics []metricWithLabelValues, labels Labels) int {
+func findMetricWithLabels(
+	desc *Desc, metrics []metricWithLabelValues, labels Labels, curry []curriedLabelValue,
+) int {
 	for i, metric := range metrics {
-		if m.matchLabels(metric.values, labels) {
+		if matchLabels(desc, metric.values, labels, curry) {
 			return i
 		}
 	}
 	return len(metrics)
 }
 
-func (m *MetricVec) matchLabelValues(values []string, lvs []string) bool {
-	if len(values) != len(lvs) {
+func matchLabelValues(values []string, lvs []string, curry []curriedLabelValue) bool {
+	if len(values) != len(lvs)+len(curry) {
 		return false
 	}
+	var iLVs, iCurry int
 	for i, v := range values {
-		if v != lvs[i] {
+		if iCurry < len(curry) && curry[iCurry].index == i {
+			if v != curry[iCurry].value {
+				return false
+			}
+			iCurry++
+			continue
+		}
+		if v != lvs[iLVs] {
 			return false
 		}
+		iLVs++
 	}
 	return true
 }
 
-func (m *MetricVec) matchLabels(values []string, labels Labels) bool {
-	if len(labels) != len(values) {
+func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool {
+	if len(values) != len(labels)+len(curry) {
 		return false
 	}
-	for i, k := range m.desc.variableLabels {
+	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
+		}
 		if values[i] != labels[k] {
 			return false
 		}
@@ -395,10 +442,31 @@ func (m *MetricVec) matchLabels(values []string, labels Labels) bool {
 	return true
 }
 
-func (m *MetricVec) extractLabelValues(labels Labels) []string {
-	labelValues := make([]string, len(labels))
-	for i, k := range m.desc.variableLabels {
+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
+		}
 		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/vec_test.go b/vendor/github.com/prometheus/client_golang/prometheus/vec_test.go
index e3c5aeba097cedc52a490b8841d6e6b21107a057..bd18a9f4e3d40e09a517e905fa3ac16567f6c1b4 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/vec_test.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/vec_test.go
@@ -21,8 +21,8 @@ import (
 )
 
 func TestDelete(t *testing.T) {
-	vec := NewUntypedVec(
-		UntypedOpts{
+	vec := NewGaugeVec(
+		GaugeOpts{
 			Name: "test",
 			Help: "helpless",
 		},
@@ -32,8 +32,8 @@ func TestDelete(t *testing.T) {
 }
 
 func TestDeleteWithCollisions(t *testing.T) {
-	vec := NewUntypedVec(
-		UntypedOpts{
+	vec := NewGaugeVec(
+		GaugeOpts{
 			Name: "test",
 			Help: "helpless",
 		},
@@ -44,12 +44,12 @@ func TestDeleteWithCollisions(t *testing.T) {
 	testDelete(t, vec)
 }
 
-func testDelete(t *testing.T, vec *UntypedVec) {
+func testDelete(t *testing.T, vec *GaugeVec) {
 	if got, want := vec.Delete(Labels{"l1": "v1", "l2": "v2"}), false; got != want {
 		t.Errorf("got %v, want %v", got, want)
 	}
 
-	vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42)
+	vec.With(Labels{"l1": "v1", "l2": "v2"}).(Gauge).Set(42)
 	if got, want := vec.Delete(Labels{"l1": "v1", "l2": "v2"}), true; got != want {
 		t.Errorf("got %v, want %v", got, want)
 	}
@@ -57,7 +57,7 @@ func testDelete(t *testing.T, vec *UntypedVec) {
 		t.Errorf("got %v, want %v", got, want)
 	}
 
-	vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42)
+	vec.With(Labels{"l1": "v1", "l2": "v2"}).(Gauge).Set(42)
 	if got, want := vec.Delete(Labels{"l2": "v2", "l1": "v1"}), true; got != want {
 		t.Errorf("got %v, want %v", got, want)
 	}
@@ -65,7 +65,7 @@ func testDelete(t *testing.T, vec *UntypedVec) {
 		t.Errorf("got %v, want %v", got, want)
 	}
 
-	vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42)
+	vec.With(Labels{"l1": "v1", "l2": "v2"}).(Gauge).Set(42)
 	if got, want := vec.Delete(Labels{"l2": "v1", "l1": "v2"}), false; got != want {
 		t.Errorf("got %v, want %v", got, want)
 	}
@@ -75,8 +75,8 @@ func testDelete(t *testing.T, vec *UntypedVec) {
 }
 
 func TestDeleteLabelValues(t *testing.T) {
-	vec := NewUntypedVec(
-		UntypedOpts{
+	vec := NewGaugeVec(
+		GaugeOpts{
 			Name: "test",
 			Help: "helpless",
 		},
@@ -86,8 +86,8 @@ func TestDeleteLabelValues(t *testing.T) {
 }
 
 func TestDeleteLabelValuesWithCollisions(t *testing.T) {
-	vec := NewUntypedVec(
-		UntypedOpts{
+	vec := NewGaugeVec(
+		GaugeOpts{
 			Name: "test",
 			Help: "helpless",
 		},
@@ -98,13 +98,13 @@ func TestDeleteLabelValuesWithCollisions(t *testing.T) {
 	testDeleteLabelValues(t, vec)
 }
 
-func testDeleteLabelValues(t *testing.T, vec *UntypedVec) {
+func testDeleteLabelValues(t *testing.T, vec *GaugeVec) {
 	if got, want := vec.DeleteLabelValues("v1", "v2"), false; got != want {
 		t.Errorf("got %v, want %v", got, want)
 	}
 
-	vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42)
-	vec.With(Labels{"l1": "v1", "l2": "v3"}).(Untyped).Set(42) // Add junk data for collision.
+	vec.With(Labels{"l1": "v1", "l2": "v2"}).(Gauge).Set(42)
+	vec.With(Labels{"l1": "v1", "l2": "v3"}).(Gauge).Set(42) // Add junk data for collision.
 	if got, want := vec.DeleteLabelValues("v1", "v2"), true; got != want {
 		t.Errorf("got %v, want %v", got, want)
 	}
@@ -115,7 +115,7 @@ func testDeleteLabelValues(t *testing.T, vec *UntypedVec) {
 		t.Errorf("got %v, want %v", got, want)
 	}
 
-	vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42)
+	vec.With(Labels{"l1": "v1", "l2": "v2"}).(Gauge).Set(42)
 	// Delete out of order.
 	if got, want := vec.DeleteLabelValues("v2", "v1"), false; got != want {
 		t.Errorf("got %v, want %v", got, want)
@@ -126,8 +126,8 @@ func testDeleteLabelValues(t *testing.T, vec *UntypedVec) {
 }
 
 func TestMetricVec(t *testing.T) {
-	vec := NewUntypedVec(
-		UntypedOpts{
+	vec := NewGaugeVec(
+		GaugeOpts{
 			Name: "test",
 			Help: "helpless",
 		},
@@ -137,8 +137,8 @@ func TestMetricVec(t *testing.T) {
 }
 
 func TestMetricVecWithCollisions(t *testing.T) {
-	vec := NewUntypedVec(
-		UntypedOpts{
+	vec := NewGaugeVec(
+		GaugeOpts{
 			Name: "test",
 			Help: "helpless",
 		},
@@ -149,7 +149,7 @@ func TestMetricVecWithCollisions(t *testing.T) {
 	testMetricVec(t, vec)
 }
 
-func testMetricVec(t *testing.T, vec *UntypedVec) {
+func testMetricVec(t *testing.T, vec *GaugeVec) {
 	vec.Reset() // Actually test Reset now!
 
 	var pair [2]string
@@ -162,11 +162,11 @@ func testMetricVec(t *testing.T, vec *UntypedVec) {
 		vec.WithLabelValues(pair[0], pair[1]).Inc()
 
 		expected[[2]string{"v1", "v2"}]++
-		vec.WithLabelValues("v1", "v2").(Untyped).Inc()
+		vec.WithLabelValues("v1", "v2").(Gauge).Inc()
 	}
 
 	var total int
-	for _, metrics := range vec.children {
+	for _, metrics := range vec.metricMap.metrics {
 		for _, metric := range metrics {
 			total++
 			copy(pair[:], metric.values)
@@ -175,7 +175,7 @@ func testMetricVec(t *testing.T, vec *UntypedVec) {
 			if err := metric.metric.Write(&metricOut); err != nil {
 				t.Fatal(err)
 			}
-			actual := *metricOut.Untyped.Value
+			actual := *metricOut.Gauge.Value
 
 			var actualPair [2]string
 			for i, label := range metricOut.Label {
@@ -201,7 +201,7 @@ func testMetricVec(t *testing.T, vec *UntypedVec) {
 
 	vec.Reset()
 
-	if len(vec.children) > 0 {
+	if len(vec.metricMap.metrics) > 0 {
 		t.Fatalf("reset failed")
 	}
 }
@@ -239,6 +239,229 @@ func TestCounterVecEndToEndWithCollision(t *testing.T) {
 	}
 }
 
+func TestCurryVec(t *testing.T) {
+	vec := NewCounterVec(
+		CounterOpts{
+			Name: "test",
+			Help: "helpless",
+		},
+		[]string{"one", "two", "three"},
+	)
+	testCurryVec(t, vec)
+}
+
+func TestCurryVecWithCollisions(t *testing.T) {
+	vec := NewCounterVec(
+		CounterOpts{
+			Name: "test",
+			Help: "helpless",
+		},
+		[]string{"one", "two", "three"},
+	)
+	vec.hashAdd = func(h uint64, s string) uint64 { return 1 }
+	vec.hashAddByte = func(h uint64, b byte) uint64 { return 1 }
+	testCurryVec(t, vec)
+}
+
+func testCurryVec(t *testing.T, vec *CounterVec) {
+
+	assertMetrics := func(t *testing.T) {
+		n := 0
+		for _, m := range vec.metricMap.metrics {
+			n += len(m)
+		}
+		if n != 2 {
+			t.Error("expected two metrics, got", n)
+		}
+		m := &dto.Metric{}
+		c1, err := vec.GetMetricWithLabelValues("1", "2", "3")
+		if err != nil {
+			t.Fatal("unexpected error getting metric:", err)
+		}
+		c1.Write(m)
+		if want, got := 1., m.GetCounter().GetValue(); want != got {
+			t.Errorf("want %f as counter value, got %f", want, got)
+		}
+		m.Reset()
+		c2, err := vec.GetMetricWithLabelValues("11", "22", "33")
+		if err != nil {
+			t.Fatal("unexpected error getting metric:", err)
+		}
+		c2.Write(m)
+		if want, got := 1., m.GetCounter().GetValue(); want != got {
+			t.Errorf("want %f as counter value, got %f", want, got)
+		}
+	}
+
+	assertNoMetric := func(t *testing.T) {
+		if n := len(vec.metricMap.metrics); n != 0 {
+			t.Error("expected no metrics, got", n)
+		}
+	}
+
+	t.Run("zero labels", func(t *testing.T) {
+		c1 := vec.MustCurryWith(nil)
+		c2 := vec.MustCurryWith(nil)
+		c1.WithLabelValues("1", "2", "3").Inc()
+		c2.With(Labels{"one": "11", "two": "22", "three": "33"}).Inc()
+		assertMetrics(t)
+		if !c1.Delete(Labels{"one": "1", "two": "2", "three": "3"}) {
+			t.Error("deletion failed")
+		}
+		if !c2.DeleteLabelValues("11", "22", "33") {
+			t.Error("deletion failed")
+		}
+		assertNoMetric(t)
+	})
+	t.Run("first label", func(t *testing.T) {
+		c1 := vec.MustCurryWith(Labels{"one": "1"})
+		c2 := vec.MustCurryWith(Labels{"one": "11"})
+		c1.WithLabelValues("2", "3").Inc()
+		c2.With(Labels{"two": "22", "three": "33"}).Inc()
+		assertMetrics(t)
+		if c1.Delete(Labels{"two": "22", "three": "33"}) {
+			t.Error("deletion unexpectedly succeeded")
+		}
+		if c2.DeleteLabelValues("2", "3") {
+			t.Error("deletion unexpectedly succeeded")
+		}
+		if !c1.Delete(Labels{"two": "2", "three": "3"}) {
+			t.Error("deletion failed")
+		}
+		if !c2.DeleteLabelValues("22", "33") {
+			t.Error("deletion failed")
+		}
+		assertNoMetric(t)
+	})
+	t.Run("middle label", func(t *testing.T) {
+		c1 := vec.MustCurryWith(Labels{"two": "2"})
+		c2 := vec.MustCurryWith(Labels{"two": "22"})
+		c1.WithLabelValues("1", "3").Inc()
+		c2.With(Labels{"one": "11", "three": "33"}).Inc()
+		assertMetrics(t)
+		if c1.Delete(Labels{"one": "11", "three": "33"}) {
+			t.Error("deletion unexpectedly succeeded")
+		}
+		if c2.DeleteLabelValues("1", "3") {
+			t.Error("deletion unexpectedly succeeded")
+		}
+		if !c1.Delete(Labels{"one": "1", "three": "3"}) {
+			t.Error("deletion failed")
+		}
+		if !c2.DeleteLabelValues("11", "33") {
+			t.Error("deletion failed")
+		}
+		assertNoMetric(t)
+	})
+	t.Run("last label", func(t *testing.T) {
+		c1 := vec.MustCurryWith(Labels{"three": "3"})
+		c2 := vec.MustCurryWith(Labels{"three": "33"})
+		c1.WithLabelValues("1", "2").Inc()
+		c2.With(Labels{"one": "11", "two": "22"}).Inc()
+		assertMetrics(t)
+		if c1.Delete(Labels{"two": "22", "one": "11"}) {
+			t.Error("deletion unexpectedly succeeded")
+		}
+		if c2.DeleteLabelValues("1", "2") {
+			t.Error("deletion unexpectedly succeeded")
+		}
+		if !c1.Delete(Labels{"two": "2", "one": "1"}) {
+			t.Error("deletion failed")
+		}
+		if !c2.DeleteLabelValues("11", "22") {
+			t.Error("deletion failed")
+		}
+		assertNoMetric(t)
+	})
+	t.Run("two labels", func(t *testing.T) {
+		c1 := vec.MustCurryWith(Labels{"three": "3", "one": "1"})
+		c2 := vec.MustCurryWith(Labels{"three": "33", "one": "11"})
+		c1.WithLabelValues("2").Inc()
+		c2.With(Labels{"two": "22"}).Inc()
+		assertMetrics(t)
+		if c1.Delete(Labels{"two": "22"}) {
+			t.Error("deletion unexpectedly succeeded")
+		}
+		if c2.DeleteLabelValues("2") {
+			t.Error("deletion unexpectedly succeeded")
+		}
+		if !c1.Delete(Labels{"two": "2"}) {
+			t.Error("deletion failed")
+		}
+		if !c2.DeleteLabelValues("22") {
+			t.Error("deletion failed")
+		}
+		assertNoMetric(t)
+	})
+	t.Run("all labels", func(t *testing.T) {
+		c1 := vec.MustCurryWith(Labels{"three": "3", "two": "2", "one": "1"})
+		c2 := vec.MustCurryWith(Labels{"three": "33", "one": "11", "two": "22"})
+		c1.WithLabelValues().Inc()
+		c2.With(nil).Inc()
+		assertMetrics(t)
+		if !c1.Delete(Labels{}) {
+			t.Error("deletion failed")
+		}
+		if !c2.DeleteLabelValues() {
+			t.Error("deletion failed")
+		}
+		assertNoMetric(t)
+	})
+	t.Run("double curry", func(t *testing.T) {
+		c1 := vec.MustCurryWith(Labels{"three": "3"}).MustCurryWith(Labels{"one": "1"})
+		c2 := vec.MustCurryWith(Labels{"three": "33"}).MustCurryWith(Labels{"one": "11"})
+		c1.WithLabelValues("2").Inc()
+		c2.With(Labels{"two": "22"}).Inc()
+		assertMetrics(t)
+		if c1.Delete(Labels{"two": "22"}) {
+			t.Error("deletion unexpectedly succeeded")
+		}
+		if c2.DeleteLabelValues("2") {
+			t.Error("deletion unexpectedly succeeded")
+		}
+		if !c1.Delete(Labels{"two": "2"}) {
+			t.Error("deletion failed")
+		}
+		if !c2.DeleteLabelValues("22") {
+			t.Error("deletion failed")
+		}
+		assertNoMetric(t)
+	})
+	t.Run("use already curried label", func(t *testing.T) {
+		c1 := vec.MustCurryWith(Labels{"three": "3"})
+		if _, err := c1.GetMetricWithLabelValues("1", "2", "3"); err == nil {
+			t.Error("expected error when using already curried label")
+		}
+		if _, err := c1.GetMetricWith(Labels{"one": "1", "two": "2", "three": "3"}); err == nil {
+			t.Error("expected error when using already curried label")
+		}
+		assertNoMetric(t)
+		c1.WithLabelValues("1", "2").Inc()
+		if c1.Delete(Labels{"one": "1", "two": "2", "three": "3"}) {
+			t.Error("deletion unexpectedly succeeded")
+		}
+		if !c1.Delete(Labels{"one": "1", "two": "2"}) {
+			t.Error("deletion failed")
+		}
+		assertNoMetric(t)
+	})
+	t.Run("curry already curried label", func(t *testing.T) {
+		if _, err := vec.MustCurryWith(Labels{"three": "3"}).CurryWith(Labels{"three": "33"}); err == nil {
+			t.Error("currying unexpectedly succeeded")
+		} else if err.Error() != `label name "three" is already curried` {
+			t.Error("currying returned unexpected error:", err)
+		}
+
+	})
+	t.Run("unknown label", func(t *testing.T) {
+		if _, err := vec.CurryWith(Labels{"foo": "bar"}); err == nil {
+			t.Error("currying unexpectedly succeeded")
+		} else if err.Error() != "1 unknown label(s) found during currying" {
+			t.Error("currying returned unexpected error:", err)
+		}
+	})
+}
+
 func BenchmarkMetricVecWithLabelValuesBasic(b *testing.B) {
 	benchmarkMetricVecWithLabelValues(b, map[string][]string{
 		"l1": {"onevalue"},
@@ -290,8 +513,8 @@ func benchmarkMetricVecWithLabelValues(b *testing.B, labels map[string][]string)
 	}
 
 	values := make([]string, len(labels)) // Value cache for permutations.
-	vec := NewUntypedVec(
-		UntypedOpts{
+	vec := NewGaugeVec(
+		GaugeOpts{
 			Name: "test",
 			Help: "helpless",
 		},
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/wrap.go b/vendor/github.com/prometheus/client_golang/prometheus/wrap.go
new file mode 100644
index 0000000000000000000000000000000000000000..49159bf3eb05f60d75cce7ca7f8e9ba3e19b8aa4
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/wrap.go
@@ -0,0 +1,179 @@
+// Copyright 2018 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+	"fmt"
+	"sort"
+
+	"github.com/golang/protobuf/proto"
+
+	dto "github.com/prometheus/client_model/go"
+)
+
+// WrapRegistererWith returns a Registerer wrapping the provided
+// Registerer. Collectors registered with the returned Registerer will be
+// registered with the wrapped Registerer in a modified way. The modified
+// Collector adds the provided Labels to all Metrics it collects (as
+// ConstLabels). The Metrics collected by the unmodified Collector must not
+// duplicate any of those labels.
+//
+// WrapRegistererWith provides a way to add fixed labels to a subset of
+// Collectors. It should not be used to add fixed labels to all metrics exposed.
+//
+// The Collector example demonstrates a use of WrapRegistererWith.
+func WrapRegistererWith(labels Labels, reg Registerer) Registerer {
+	return &wrappingRegisterer{
+		wrappedRegisterer: reg,
+		labels:            labels,
+	}
+}
+
+// WrapRegistererWithPrefix returns a Registerer wrapping the provided
+// Registerer. Collectors registered with the returned Registerer will be
+// registered with the wrapped Registerer in a modified way. The modified
+// Collector adds the provided prefix to the name of all Metrics it collects.
+//
+// WrapRegistererWithPrefix is useful to have one place to prefix all metrics of
+// a sub-system. To make this work, register metrics of the sub-system with the
+// wrapping Registerer returned by WrapRegistererWithPrefix. It is rarely useful
+// to use the same prefix for all metrics exposed. In particular, do not prefix
+// metric names that are standardized across applications, as that would break
+// horizontal monitoring, for example the metrics provided by the Go collector
+// (see NewGoCollector) and the process collector (see NewProcessCollector). (In
+// fact, those metrics are already prefixed with “go_” or “process_”,
+// respectively.)
+func WrapRegistererWithPrefix(prefix string, reg Registerer) Registerer {
+	return &wrappingRegisterer{
+		wrappedRegisterer: reg,
+		prefix:            prefix,
+	}
+}
+
+type wrappingRegisterer struct {
+	wrappedRegisterer Registerer
+	prefix            string
+	labels            Labels
+}
+
+func (r *wrappingRegisterer) Register(c Collector) error {
+	return r.wrappedRegisterer.Register(&wrappingCollector{
+		wrappedCollector: c,
+		prefix:           r.prefix,
+		labels:           r.labels,
+	})
+}
+
+func (r *wrappingRegisterer) MustRegister(cs ...Collector) {
+	for _, c := range cs {
+		if err := r.Register(c); err != nil {
+			panic(err)
+		}
+	}
+}
+
+func (r *wrappingRegisterer) Unregister(c Collector) bool {
+	return r.wrappedRegisterer.Unregister(&wrappingCollector{
+		wrappedCollector: c,
+		prefix:           r.prefix,
+		labels:           r.labels,
+	})
+}
+
+type wrappingCollector struct {
+	wrappedCollector Collector
+	prefix           string
+	labels           Labels
+}
+
+func (c *wrappingCollector) Collect(ch chan<- Metric) {
+	wrappedCh := make(chan Metric)
+	go func() {
+		c.wrappedCollector.Collect(wrappedCh)
+		close(wrappedCh)
+	}()
+	for m := range wrappedCh {
+		ch <- &wrappingMetric{
+			wrappedMetric: m,
+			prefix:        c.prefix,
+			labels:        c.labels,
+		}
+	}
+}
+
+func (c *wrappingCollector) Describe(ch chan<- *Desc) {
+	wrappedCh := make(chan *Desc)
+	go func() {
+		c.wrappedCollector.Describe(wrappedCh)
+		close(wrappedCh)
+	}()
+	for desc := range wrappedCh {
+		ch <- wrapDesc(desc, c.prefix, c.labels)
+	}
+}
+
+type wrappingMetric struct {
+	wrappedMetric Metric
+	prefix        string
+	labels        Labels
+}
+
+func (m *wrappingMetric) Desc() *Desc {
+	return wrapDesc(m.wrappedMetric.Desc(), m.prefix, m.labels)
+}
+
+func (m *wrappingMetric) Write(out *dto.Metric) error {
+	if err := m.wrappedMetric.Write(out); err != nil {
+		return err
+	}
+	if len(m.labels) == 0 {
+		// No wrapping labels.
+		return nil
+	}
+	for ln, lv := range m.labels {
+		out.Label = append(out.Label, &dto.LabelPair{
+			Name:  proto.String(ln),
+			Value: proto.String(lv),
+		})
+	}
+	sort.Sort(labelPairSorter(out.Label))
+	return nil
+}
+
+func wrapDesc(desc *Desc, prefix string, labels Labels) *Desc {
+	constLabels := Labels{}
+	for _, lp := range desc.constLabelPairs {
+		constLabels[*lp.Name] = *lp.Value
+	}
+	for ln, lv := range labels {
+		if _, alreadyUsed := constLabels[ln]; alreadyUsed {
+			return &Desc{
+				fqName:          desc.fqName,
+				help:            desc.help,
+				variableLabels:  desc.variableLabels,
+				constLabelPairs: desc.constLabelPairs,
+				err:             fmt.Errorf("attempted wrapping with already existing label name %q", ln),
+			}
+		}
+		constLabels[ln] = lv
+	}
+	// NewDesc will do remaining validations.
+	newDesc := NewDesc(prefix+desc.fqName, desc.help, desc.variableLabels, constLabels)
+	// Propagate errors if there was any. This will override any errer
+	// created by NewDesc above, i.e. earlier errors get precedence.
+	if desc.err != nil {
+		newDesc.err = desc.err
+	}
+	return newDesc
+}
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/wrap_test.go b/vendor/github.com/prometheus/client_golang/prometheus/wrap_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..28e24704ed9b6385d83730e7934a893833234c72
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/wrap_test.go
@@ -0,0 +1,322 @@
+// 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"
+	"reflect"
+	"strings"
+	"testing"
+
+	"github.com/golang/protobuf/proto"
+
+	dto "github.com/prometheus/client_model/go"
+)
+
+// uncheckedCollector wraps a Collector but its Describe method yields no Desc.
+type uncheckedCollector struct {
+	c Collector
+}
+
+func (u uncheckedCollector) Describe(_ chan<- *Desc) {}
+func (u uncheckedCollector) Collect(c chan<- Metric) {
+	u.c.Collect(c)
+}
+
+func toMetricFamilies(cs ...Collector) []*dto.MetricFamily {
+	reg := NewRegistry()
+	reg.MustRegister(cs...)
+	out, err := reg.Gather()
+	if err != nil {
+		panic(err)
+	}
+	return out
+}
+
+func TestWrap(t *testing.T) {
+
+	simpleCnt := NewCounter(CounterOpts{
+		Name: "simpleCnt",
+		Help: "helpSimpleCnt",
+	})
+	simpleCnt.Inc()
+
+	simpleGge := NewGauge(GaugeOpts{
+		Name: "simpleGge",
+		Help: "helpSimpleGge",
+	})
+	simpleGge.Set(3.14)
+
+	preCnt := NewCounter(CounterOpts{
+		Name: "pre_simpleCnt",
+		Help: "helpSimpleCnt",
+	})
+	preCnt.Inc()
+
+	barLabeledCnt := NewCounter(CounterOpts{
+		Name:        "simpleCnt",
+		Help:        "helpSimpleCnt",
+		ConstLabels: Labels{"foo": "bar"},
+	})
+	barLabeledCnt.Inc()
+
+	bazLabeledCnt := NewCounter(CounterOpts{
+		Name:        "simpleCnt",
+		Help:        "helpSimpleCnt",
+		ConstLabels: Labels{"foo": "baz"},
+	})
+	bazLabeledCnt.Inc()
+
+	labeledPreCnt := NewCounter(CounterOpts{
+		Name:        "pre_simpleCnt",
+		Help:        "helpSimpleCnt",
+		ConstLabels: Labels{"foo": "bar"},
+	})
+	labeledPreCnt.Inc()
+
+	twiceLabeledPreCnt := NewCounter(CounterOpts{
+		Name:        "pre_simpleCnt",
+		Help:        "helpSimpleCnt",
+		ConstLabels: Labels{"foo": "bar", "dings": "bums"},
+	})
+	twiceLabeledPreCnt.Inc()
+
+	barLabeledUncheckedCollector := uncheckedCollector{barLabeledCnt}
+
+	scenarios := map[string]struct {
+		prefix      string // First wrap with this prefix.
+		labels      Labels // Then wrap the result with these labels.
+		labels2     Labels // If any, wrap the prefix-wrapped one again.
+		preRegister []Collector
+		toRegister  []struct { // If there are any labels2, register every other with that one.
+			collector         Collector
+			registrationFails bool
+		}
+		gatherFails bool
+		output      []Collector
+	}{
+		"wrap nothing": {
+			prefix: "pre_",
+			labels: Labels{"foo": "bar"},
+		},
+		"wrap with nothing": {
+			preRegister: []Collector{simpleGge},
+			toRegister: []struct {
+				collector         Collector
+				registrationFails bool
+			}{{simpleCnt, false}},
+			output: []Collector{simpleGge, simpleCnt},
+		},
+		"wrap counter with prefix": {
+			prefix:      "pre_",
+			preRegister: []Collector{simpleGge},
+			toRegister: []struct {
+				collector         Collector
+				registrationFails bool
+			}{{simpleCnt, false}},
+			output: []Collector{simpleGge, preCnt},
+		},
+		"wrap counter with label pair": {
+			labels:      Labels{"foo": "bar"},
+			preRegister: []Collector{simpleGge},
+			toRegister: []struct {
+				collector         Collector
+				registrationFails bool
+			}{{simpleCnt, false}},
+			output: []Collector{simpleGge, barLabeledCnt},
+		},
+		"wrap counter with label pair and prefix": {
+			prefix:      "pre_",
+			labels:      Labels{"foo": "bar"},
+			preRegister: []Collector{simpleGge},
+			toRegister: []struct {
+				collector         Collector
+				registrationFails bool
+			}{{simpleCnt, false}},
+			output: []Collector{simpleGge, labeledPreCnt},
+		},
+		"wrap counter with invalid prefix": {
+			prefix:      "1+1",
+			preRegister: []Collector{simpleGge},
+			toRegister: []struct {
+				collector         Collector
+				registrationFails bool
+			}{{simpleCnt, true}},
+			output: []Collector{simpleGge},
+		},
+		"wrap counter with invalid label": {
+			preRegister: []Collector{simpleGge},
+			labels:      Labels{"42": "bar"},
+			toRegister: []struct {
+				collector         Collector
+				registrationFails bool
+			}{{simpleCnt, true}},
+			output: []Collector{simpleGge},
+		},
+		"counter registered twice but wrapped with different label values": {
+			labels:  Labels{"foo": "bar"},
+			labels2: Labels{"foo": "baz"},
+			toRegister: []struct {
+				collector         Collector
+				registrationFails bool
+			}{{simpleCnt, false}, {simpleCnt, false}},
+			output: []Collector{barLabeledCnt, bazLabeledCnt},
+		},
+		"counter registered twice but wrapped with different inconsistent label values": {
+			labels:  Labels{"foo": "bar"},
+			labels2: Labels{"bar": "baz"},
+			toRegister: []struct {
+				collector         Collector
+				registrationFails bool
+			}{{simpleCnt, false}, {simpleCnt, true}},
+			output: []Collector{barLabeledCnt},
+		},
+		"wrap counter with prefix and two labels": {
+			prefix:      "pre_",
+			labels:      Labels{"foo": "bar", "dings": "bums"},
+			preRegister: []Collector{simpleGge},
+			toRegister: []struct {
+				collector         Collector
+				registrationFails bool
+			}{{simpleCnt, false}},
+			output: []Collector{simpleGge, twiceLabeledPreCnt},
+		},
+		"wrap labeled counter with prefix and another label": {
+			prefix:      "pre_",
+			labels:      Labels{"dings": "bums"},
+			preRegister: []Collector{simpleGge},
+			toRegister: []struct {
+				collector         Collector
+				registrationFails bool
+			}{{barLabeledCnt, false}},
+			output: []Collector{simpleGge, twiceLabeledPreCnt},
+		},
+		"wrap labeled counter with prefix and inconsistent label": {
+			prefix:      "pre_",
+			labels:      Labels{"foo": "bums"},
+			preRegister: []Collector{simpleGge},
+			toRegister: []struct {
+				collector         Collector
+				registrationFails bool
+			}{{barLabeledCnt, true}},
+			output: []Collector{simpleGge},
+		},
+		"wrap labeled counter with prefix and the same label again": {
+			prefix:      "pre_",
+			labels:      Labels{"foo": "bar"},
+			preRegister: []Collector{simpleGge},
+			toRegister: []struct {
+				collector         Collector
+				registrationFails bool
+			}{{barLabeledCnt, true}},
+			output: []Collector{simpleGge},
+		},
+		"wrap labeled unchecked collector with prefix and another label": {
+			prefix:      "pre_",
+			labels:      Labels{"dings": "bums"},
+			preRegister: []Collector{simpleGge},
+			toRegister: []struct {
+				collector         Collector
+				registrationFails bool
+			}{{barLabeledUncheckedCollector, false}},
+			output: []Collector{simpleGge, twiceLabeledPreCnt},
+		},
+		"wrap labeled unchecked collector with prefix and inconsistent label": {
+			prefix:      "pre_",
+			labels:      Labels{"foo": "bums"},
+			preRegister: []Collector{simpleGge},
+			toRegister: []struct {
+				collector         Collector
+				registrationFails bool
+			}{{barLabeledUncheckedCollector, false}},
+			gatherFails: true,
+			output:      []Collector{simpleGge},
+		},
+		"wrap labeled unchecked collector with prefix and the same label again": {
+			prefix:      "pre_",
+			labels:      Labels{"foo": "bar"},
+			preRegister: []Collector{simpleGge},
+			toRegister: []struct {
+				collector         Collector
+				registrationFails bool
+			}{{barLabeledUncheckedCollector, false}},
+			gatherFails: true,
+			output:      []Collector{simpleGge},
+		},
+		"wrap labeled unchecked collector with prefix and another label resulting in collision with pre-registered counter": {
+			prefix:      "pre_",
+			labels:      Labels{"dings": "bums"},
+			preRegister: []Collector{twiceLabeledPreCnt},
+			toRegister: []struct {
+				collector         Collector
+				registrationFails bool
+			}{{barLabeledUncheckedCollector, false}},
+			gatherFails: true,
+			output:      []Collector{twiceLabeledPreCnt},
+		},
+	}
+
+	for n, s := range scenarios {
+		t.Run(n, func(t *testing.T) {
+			reg := NewPedanticRegistry()
+			for _, c := range s.preRegister {
+				if err := reg.Register(c); err != nil {
+					t.Fatal("error registering with unwrapped registry:", err)
+				}
+			}
+			preReg := WrapRegistererWithPrefix(s.prefix, reg)
+			lReg := WrapRegistererWith(s.labels, preReg)
+			l2Reg := WrapRegistererWith(s.labels2, preReg)
+			for i, tr := range s.toRegister {
+				var err error
+				if i%2 != 0 && len(s.labels2) != 0 {
+					err = l2Reg.Register(tr.collector)
+				} else {
+					err = lReg.Register(tr.collector)
+				}
+				if tr.registrationFails && err == nil {
+					t.Fatalf("registration with wrapping registry unexpectedly succeded for collector #%d", i)
+				}
+				if !tr.registrationFails && err != nil {
+					t.Fatalf("registration with wrapping registry failed for collector #%d: %s", i, err)
+				}
+			}
+			wantMF := toMetricFamilies(s.output...)
+			gotMF, err := reg.Gather()
+			if s.gatherFails && err == nil {
+				t.Fatal("gathering unexpectedly succeded")
+			}
+			if !s.gatherFails && err != nil {
+				t.Fatal("gathering failed:", err)
+			}
+			if !reflect.DeepEqual(gotMF, wantMF) {
+				var want, got []string
+
+				for i, mf := range wantMF {
+					want = append(want, fmt.Sprintf("%3d: %s", i, proto.MarshalTextString(mf)))
+				}
+				for i, mf := range gotMF {
+					got = append(got, fmt.Sprintf("%3d: %s", i, proto.MarshalTextString(mf)))
+				}
+
+				t.Fatalf(
+					"unexpected output of gathering:\n\nWANT:\n%s\n\nGOT:\n%s\n",
+					strings.Join(want, "\n"),
+					strings.Join(got, "\n"),
+				)
+			}
+		})
+	}
+
+}