nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: hetznercloud/hcloud-cloud-controller-manager
Pull request #558 (2023-11-09 14:46:13)

chore(metrics): add timeouts for metrics server
Setup proper timeouts for metrics server


Patch:
diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go
index 707bdf13..c8b8a1d2 100644
--- a/internal/metrics/metrics.go
+++ b/internal/metrics/metrics.go
@@ -18,12 +18,19 @@ package metrics
 
 import (
 	"net/http"
+	"time"
 
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
 	"k8s.io/klog/v2"
 )
 
+const (
+	readTimeout    = 5 * time.Second
+	requestTimeout = 10 * time.Second
+	writeTimeout   = 20 * time.Second
+)
+
 var (
 	OperationCalled = prometheus.NewCounterVec(prometheus.CounterOpts{
 		Name: "cloud_controller_manager_operations_total",
@@ -37,9 +44,7 @@ func GetRegistry() *prometheus.Registry {
 	return registry
 }
 
-func Serve(address string) {
-	klog.Info("Starting metrics server at ", address)
-
+func GetHandler() http.Handler {
 	registry.MustRegister(OperationCalled)
 
 	gatherers := prometheus.Gatherers{
@@ -47,9 +52,23 @@ func Serve(address string) {
 		registry,
 	}
 
-	http.Handle("/metrics", promhttp.HandlerFor(gatherers, promhttp.HandlerOpts{}))
-	// TODO: Setup proper timeouts for metrics server and remove nolint:gosec
-	if err := http.ListenAndServe(address, nil); err != nil { //nolint:gosec
+	return promhttp.HandlerFor(gatherers, promhttp.HandlerOpts{})
+}
+
+func Serve(address string) {
+	mux := http.NewServeMux()
+	mux.Handle("/metrics", GetHandler())
+
+	server := &http.Server{
+		Addr:         address,
+		Handler:      http.TimeoutHandler(mux, requestTimeout, "timeout"),
+		ReadTimeout:  readTimeout,
+		WriteTimeout: writeTimeout,
+	}
+
+	klog.Info("Starting metrics server at ", server.Addr)
+
+	if err := server.ListenAndServe(); err != nil {
 		klog.ErrorS(err, "create metrics service")
 	}
 }


Test patch:
diff --git a/internal/metrics/metrics_test.go b/internal/metrics/metrics_test.go
new file mode 100644
index 00000000..73b50df1
--- /dev/null
+++ b/internal/metrics/metrics_test.go
@@ -0,0 +1,42 @@
+package metrics_test
+
+import (
+	"context"
+	"io"
+	"net/http"
+	"net/http/httptest"
+	"strings"
+	"testing"
+
+	"github.com/hetznercloud/hcloud-cloud-controller-manager/internal/metrics"
+)
+
+func TestMetrics(t *testing.T) {
+	t.Parallel()
+
+	client := &http.Client{}
+	ts := httptest.NewServer(metrics.GetHandler())
+	ctx := context.Background()
+
+	metrics.OperationCalled.WithLabelValues("test").Inc()
+
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	resp, err := client.Do(req)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer resp.Body.Close()
+
+	if m := `cloud_controller_manager_operations_total{op="test"} 1`; !strings.Contains(string(body), m) {
+		t.Fatalf("no metric %s found", m)
+	}
+}