diff --git a/agent/agent_endpoint.go b/agent/agent_endpoint.go
index 0296c3e1cc..1ce49faec0 100644
--- a/agent/agent_endpoint.go
+++ b/agent/agent_endpoint.go
@@ -54,6 +54,21 @@ func (s *HTTPServer) AgentSelf(resp http.ResponseWriter, req *http.Request) (int
}, nil
}
+func (s *HTTPServer) AgentMetrics(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
+ // Fetch the ACL token, if any, and enforce agent policy.
+ var token string
+ s.parseToken(req, &token)
+ acl, err := s.agent.resolveToken(token)
+ if err != nil {
+ return nil, err
+ }
+ if acl != nil && !acl.AgentRead(s.agent.config.NodeName) {
+ return nil, errPermissionDenied
+ }
+
+ return s.agent.MemSink.DisplayMetrics(resp, req)
+}
+
func (s *HTTPServer) AgentReload(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
if req.Method != "PUT" {
resp.WriteHeader(http.StatusMethodNotAllowed)
diff --git a/agent/agent_endpoint_test.go b/agent/agent_endpoint_test.go
index 1d9fe5ff7e..0b2dd2f0eb 100644
--- a/agent/agent_endpoint_test.go
+++ b/agent/agent_endpoint_test.go
@@ -239,6 +239,34 @@ func TestAgent_Self_ACLDeny(t *testing.T) {
})
}
+func TestAgent_Metrics_ACLDeny(t *testing.T) {
+ t.Parallel()
+ a := NewTestAgent(t.Name(), TestACLConfig())
+ defer a.Shutdown()
+
+ t.Run("no token", func(t *testing.T) {
+ req, _ := http.NewRequest("GET", "/v1/agent/metrics", nil)
+ if _, err := a.srv.AgentSelf(nil, req); !isPermissionDenied(err) {
+ t.Fatalf("err: %v", err)
+ }
+ })
+
+ t.Run("agent master token", func(t *testing.T) {
+ req, _ := http.NewRequest("GET", "/v1/agent/metrics?token=towel", nil)
+ if _, err := a.srv.AgentSelf(nil, req); err != nil {
+ t.Fatalf("err: %v", err)
+ }
+ })
+
+ t.Run("read-only token", func(t *testing.T) {
+ ro := makeReadOnlyAgentACL(t, a.srv)
+ req, _ := http.NewRequest("GET", fmt.Sprintf("/v1/agent/metrics?token=%s", ro), nil)
+ if _, err := a.srv.AgentSelf(nil, req); err != nil {
+ t.Fatalf("err: %v", err)
+ }
+ })
+}
+
func TestAgent_Reload(t *testing.T) {
t.Parallel()
cfg := TestConfig()
diff --git a/agent/http.go b/agent/http.go
index 968f2100d3..8806ee2d2c 100644
--- a/agent/http.go
+++ b/agent/http.go
@@ -98,7 +98,7 @@ func (s *HTTPServer) handler(enableDebug bool) http.Handler {
handleFuncMetrics("/v1/agent/maintenance", s.wrap(s.AgentNodeMaintenance))
handleFuncMetrics("/v1/agent/reload", s.wrap(s.AgentReload))
handleFuncMetrics("/v1/agent/monitor", s.wrap(s.AgentMonitor))
- handleFuncMetrics("/v1/agent/metrics", s.wrap(s.requireAgentRead(s.agent.MemSink.DisplayMetrics)))
+ handleFuncMetrics("/v1/agent/metrics", s.wrap(s.AgentMetrics))
handleFuncMetrics("/v1/agent/services", s.wrap(s.AgentServices))
handleFuncMetrics("/v1/agent/checks", s.wrap(s.AgentChecks))
handleFuncMetrics("/v1/agent/members", s.wrap(s.AgentMembers))
@@ -264,26 +264,6 @@ func (s *HTTPServer) wrap(handler func(resp http.ResponseWriter, req *http.Reque
}
}
-type handlerFunc func(resp http.ResponseWriter, req *http.Request) (interface{}, error)
-
-// requireAgentRead wraps the given function, requiring a token with agent read permissions
-func (s *HTTPServer) requireAgentRead(handler handlerFunc) handlerFunc {
- return func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
- // Fetch the ACL token, if any, and enforce agent policy.
- var token string
- s.parseToken(req, &token)
- acl, err := s.agent.resolveToken(token)
- if err != nil {
- return nil, err
- }
- if acl != nil && !acl.AgentRead(s.agent.config.NodeName) {
- return nil, errPermissionDenied
- }
-
- return handler(resp, req)
- }
-}
-
// marshalJSON marshals the object into JSON, respecting the user's pretty-ness
// configuration.
func (s *HTTPServer) marshalJSON(req *http.Request, obj interface{}) ([]byte, error) {
diff --git a/website/source/docs/agent/options.html.md b/website/source/docs/agent/options.html.md
index fde27bdbf0..89dffbc89e 100644
--- a/website/source/docs/agent/options.html.md
+++ b/website/source/docs/agent/options.html.md
@@ -1345,3 +1345,4 @@ items which are reloaded include:
* Watches
* HTTP Client Address
* Node Metadata
+* Metric Prefix Filter
diff --git a/website/source/docs/agent/telemetry.html.md b/website/source/docs/agent/telemetry.html.md
index b63f686868..02bb3767ca 100644
--- a/website/source/docs/agent/telemetry.html.md
+++ b/website/source/docs/agent/telemetry.html.md
@@ -22,7 +22,8 @@ getting a better view of what Consul is doing.
Additionally, if the [`telemetry` configuration options](/docs/agent/options.html#telemetry)
are provided, the telemetry information will be streamed to a
[statsite](http://github.com/armon/statsite) or [statsd](http://github.com/etsy/statsd) server where
-it can be aggregated and flushed to Graphite or any other metrics store.
+it can be aggregated and flushed to Graphite or any other metrics store. This
+information can also be viewed with the [metrics endpoint](/api/agent.html#view-metrics)
Below is sample output of a telemetry dump: