From 5ea5b40d3fd030c867d68b5966a3c19187682019 Mon Sep 17 00:00:00 2001 From: Ying Li Date: Thu, 29 Oct 2015 21:04:35 -0700 Subject: [PATCH] Add prometheus stats to server http handlers Signed-off-by: Ying Li --- server/server.go | 70 +++++++++++++++++++++++++++++++------------ server/server_test.go | 13 ++++++++ 2 files changed, 64 insertions(+), 19 deletions(-) diff --git a/server/server.go b/server/server.go index c6a17d8797..66935da786 100644 --- a/server/server.go +++ b/server/server.go @@ -9,13 +9,13 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/distribution/health" "github.com/docker/distribution/registry/auth" + "github.com/docker/notary/server/handlers" "github.com/docker/notary/tuf/data" "github.com/docker/notary/tuf/signed" - "github.com/gorilla/mux" - "golang.org/x/net/context" - - "github.com/docker/notary/server/handlers" "github.com/docker/notary/utils" + "github.com/gorilla/mux" + "github.com/prometheus/client_golang/prometheus" + "golang.org/x/net/context" ) func init() { @@ -26,6 +26,14 @@ func init() { ) } +func prometheusOpts(operation string) prometheus.SummaryOpts { + return prometheus.SummaryOpts{ + Namespace: "notary_server", + Subsystem: "http", + ConstLabels: prometheus.Labels{"operation": operation}, + } +} + // Run sets up and starts a TLS server that can be cancelled using the // given configuration. The context it is passed is the context it should // use directly for the TLS server, and generate children off for requests @@ -57,24 +65,10 @@ func Run(ctx context.Context, addr string, tlsConfig *tls.Config, trust signed.C return err } } - hand := utils.RootHandlerFactory(ac, ctx, trust) - r := mux.NewRouter() - r.Methods("GET").Path("/v2/").Handler(hand(handlers.MainHandler)) - r.Methods("POST").Path("/v2/{imageName:.*}/_trust/tuf/").Handler(hand(handlers.AtomicUpdateHandler, "push", "pull")) - r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/{tufRole:(root|targets|snapshot)}.json").Handler(hand(handlers.GetHandler, "pull")) - r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/timestamp.json").Handler(hand(handlers.GetTimestampHandler, "pull")) - r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/timestamp.key").Handler(hand(handlers.GetTimestampKeyHandler, "push", "pull")) - r.Methods("DELETE").Path("/v2/{imageName:.*}/_trust/tuf/").Handler(hand(handlers.DeleteHandler, "push", "pull")) - r.Methods("GET").Path("/_notary_server/health").Handler(hand( - func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - health.StatusHandler(w, r) - return nil - })) - r.Methods("GET", "POST", "PUT", "HEAD", "DELETE").Path("/{other:.*}").Handler(hand(utils.NotFoundHandler)) svr := http.Server{ Addr: addr, - Handler: r, + Handler: RootHandler(ac, ctx, trust), } logrus.Info("Starting on ", addr) @@ -83,3 +77,41 @@ func Run(ctx context.Context, addr string, tlsConfig *tls.Config, trust signed.C return err } + +// RootHandler returns the handler that routes all the paths from / for the +// server. +func RootHandler(ac auth.AccessController, ctx context.Context, trust signed.CryptoService) http.Handler { + hand := utils.RootHandlerFactory(ac, ctx, trust) + + r := mux.NewRouter() + r.Methods("GET").Path("/v2/").Handler(hand(handlers.MainHandler)) + r.Methods("POST").Path("/v2/{imageName:.*}/_trust/tuf/").Handler( + prometheus.InstrumentHandlerWithOpts( + prometheusOpts("UpdateTuf"), + hand(handlers.AtomicUpdateHandler, "push", "pull"))) + r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/{tufRole:(root|targets|snapshot)}.json").Handler( + prometheus.InstrumentHandlerWithOpts( + prometheusOpts("GetRole"), + hand(handlers.GetHandler, "pull"))) + r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/timestamp.json").Handler( + prometheus.InstrumentHandlerWithOpts( + prometheusOpts("GetTimestamp"), + hand(handlers.GetTimestampHandler, "pull"))) + r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/timestamp.key").Handler( + prometheus.InstrumentHandlerWithOpts( + prometheusOpts("GetTimestampKey"), + hand(handlers.GetTimestampKeyHandler, "push", "pull"))) + r.Methods("DELETE").Path("/v2/{imageName:.*}/_trust/tuf/").Handler( + prometheus.InstrumentHandlerWithOpts( + prometheusOpts("DeleteTuf"), + hand(handlers.DeleteHandler, "push", "pull"))) + r.Methods("GET").Path("/_notary_server/health").Handler(hand( + func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { + health.StatusHandler(w, r) + return nil + })) + r.Methods("GET").Path("/_notary_server/metrics").Handler(prometheus.Handler()) + r.Methods("GET", "POST", "PUT", "HEAD", "DELETE").Path("/{other:.*}").Handler(hand(utils.NotFoundHandler)) + + return r +} diff --git a/server/server_test.go b/server/server_test.go index a90cbcf8eb..986eaa1069 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -2,11 +2,14 @@ package server import ( "net" + "net/http" + "net/http/httptest" "strings" "testing" _ "github.com/docker/distribution/registry/auth/silly" "github.com/docker/notary/tuf/signed" + "github.com/stretchr/testify/assert" "golang.org/x/net/context" ) @@ -43,3 +46,13 @@ func TestRunReservedPort(t *testing.T) { t.Fatalf("Received unexpected err: %s", err.Error()) } } + +func TestMetricsEndpoint(t *testing.T) { + handler := RootHandler(nil, context.Background(), signed.NewEd25519()) + ts := httptest.NewServer(handler) + defer ts.Close() + + res, err := http.Get(ts.URL + "/_notary_server/metrics") + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, res.StatusCode) +}