diff --git a/runsc/cmd/metric_server.go b/runsc/cmd/metric_server.go
index bae315b5d..5624b6180 100644
--- a/runsc/cmd/metric_server.go
+++ b/runsc/cmd/metric_server.go
@@ -578,6 +578,16 @@ var httpOK = httpResult{code: http.StatusOK}
// serveIndex serves the index page.
func (m *MetricServer) serveIndex(w http.ResponseWriter, req *http.Request) httpResult {
if req.URL.Path != "/" {
+ if strings.HasPrefix(req.URL.Path, "/metrics?") {
+ // Prometheus's scrape_config.metrics_path takes in a query path and automatically encodes
+ // all special characters in it to %-form, including the "?" character.
+ // This can prevent use of query parameters, and we end up here instead.
+ // To address this, rewrite the URL to undo this transformation.
+ // This means requesting "/metrics%3Ffoo=bar" is rewritten to "/metrics?foo=bar".
+ req.URL.RawQuery = strings.TrimPrefix(req.URL.Path, "/metrics?")
+ req.URL.Path = "/metrics"
+ return m.serveMetrics(w, req)
+ }
return httpResult{http.StatusNotFound, errors.New("path not found")}
}
fmt.Fprintf(w, "
runsc metrics")
diff --git a/runsc/container/metric_server_test.go b/runsc/container/metric_server_test.go
index 7b392040e..1b954014b 100644
--- a/runsc/container/metric_server_test.go
+++ b/runsc/container/metric_server_test.go
@@ -647,6 +647,30 @@ func TestContainerMetricsFilter(t *testing.T) {
if err != nil {
t.Errorf("Cannot get sandbox metadata from unfiltered data: %v", err)
}
+
+ // Fifth pass: Use alternate URL encoding to mimic Prometheus's URL-encoding
+ // behavior.
+ alternatePathData, err := te.client.GetMetrics(te.testCtx, map[string]string{
+ // Encoded version of "/metrics?runsc-sandbox-metrics-filter=^$", this should match nothing.
+ "": "/metrics%3Frunsc-sandbox-metrics-filter=%5E%24",
+ })
+ if err != nil {
+ t.Fatalf("Cannot get metrics: %v", err)
+ }
+ _, err = alternatePathData.GetSandboxMetadataMetric(metricclient.WantMetric{
+ Metric: "testmetric_meta_sandbox_metadata",
+ Sandbox: args.ID,
+ })
+ if err != nil {
+ t.Errorf("Cannot get sandbox metadata from data obtained from alternate path: %v\n\nData:\n\n%v\n\n", err, alternatePathData)
+ }
+ _, _, err = alternatePathData.GetPrometheusContainerInteger(metricclient.WantMetric{
+ Metric: "testmetric_fs_opens",
+ Sandbox: args.ID,
+ })
+ if err == nil {
+ t.Errorf("Was unexpectedly able to get testmetric_fs_opens from data obtained from alternate path which was supposed to filter it out:\n\n%v\n\n", alternatePathData)
+ }
}
// TestContainerCapabilityFilter verifies the ability to filter capabilities in /metrics requests.
diff --git a/test/metricclient/metricclient.go b/test/metricclient/metricclient.go
index f08e354b8..7a3cd450a 100644
--- a/test/metricclient/metricclient.go
+++ b/test/metricclient/metricclient.go
@@ -274,8 +274,16 @@ func (c *MetricClient) ShutdownServer(ctx context.Context) error {
type MetricData string
// GetMetrics returns the raw Prometheus-formatted metric data from the metric server.
+// `urlParams` may contain a special parameter with the empty string as the key.
+// If this is set, that string is used to override the request path from its default
+// value of `/metrics`.
func (c *MetricClient) GetMetrics(ctx context.Context, urlParams map[string]string) (MetricData, error) {
- resp, closeReq, err := c.req(ctx, 10*time.Second, http.MethodGet, "/metrics", urlParams)
+ path := "/metrics"
+ if overridePath, found := urlParams[""]; found {
+ path = overridePath
+ delete(urlParams, "")
+ }
+ resp, closeReq, err := c.req(ctx, 10*time.Second, http.MethodGet, path, urlParams)
if err != nil {
return "", fmt.Errorf("cannot get /metrics: %v", err)
}