1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
|
package observability_test
import (
"context"
"fmt"
"net"
"net/http"
"net/http/httptest"
"testing"
"github.com/golang/mock/gomock"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/module/modshared"
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/module/observability"
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_modserver"
"go.uber.org/zap/zaptest"
)
func TestMetricServer(t *testing.T) {
ctrl := gomock.NewController(t)
listener, err := net.Listen("tcp", "localhost:0")
require.NoError(t, err)
defer listener.Close()
logger := zaptest.NewLogger(t)
mockApi := mock_modserver.NewMockApi(ctrl)
probeRegistry := observability.NewProbeRegistry()
metricSrv := observability.MetricServer{
Api: mockApi,
Log: logger,
Name: "test-server",
Listener: listener,
PrometheusUrlPath: "/metrics",
LivenessProbeUrlPath: "/liveness",
ReadinessProbeUrlPath: "/readiness",
Gatherer: prometheus.DefaultGatherer,
Registerer: prometheus.DefaultRegisterer,
ProbeRegistry: probeRegistry,
}
handler := metricSrv.ConstructHandler()
httpGet := func(t *testing.T, path string) *httptest.ResponseRecorder {
request, err := http.NewRequest("GET", path, nil) // nolint:noctx
require.NoError(t, err)
recorder := httptest.NewRecorder()
handler.ServeHTTP(recorder, request)
return recorder
}
// tests
t.Run("/metrics", func(t *testing.T) {
httpResponse := httpGet(t, "/metrics").Result()
require.Equal(t, http.StatusOK, httpResponse.StatusCode)
httpResponse.Body.Close()
})
t.Run("/liveness", func(t *testing.T) {
// succeeds when there are no probes
rec := httpGet(t, "/liveness")
httpResponse := rec.Result()
require.Equal(t, http.StatusOK, httpResponse.StatusCode)
require.Empty(t, rec.Body)
httpResponse.Body.Close()
// fails when a probe fails
probeErr := fmt.Errorf("failed liveness on purpose")
expectedErr := fmt.Errorf("test-liveness: %w", probeErr)
probeRegistry.RegisterLivenessProbe("test-liveness", func(ctx context.Context) error {
return probeErr
})
mockApi.EXPECT().
HandleProcessingError(gomock.Any(), gomock.Any(), modshared.NoAgentId, "LivenessProbe failed", expectedErr)
rec = httpGet(t, "/liveness")
httpResponse = rec.Result()
require.Equal(t, http.StatusInternalServerError, httpResponse.StatusCode)
require.Equal(t, "test-liveness: failed liveness on purpose\n", rec.Body.String())
httpResponse.Body.Close()
})
t.Run("/readiness", func(t *testing.T) {
markReady := probeRegistry.RegisterReadinessToggle("test-readiness-toggle")
// fails when toggle has not been called
expectedErr := fmt.Errorf("test-readiness-toggle: %w", fmt.Errorf("not ready yet"))
mockApi.EXPECT().HandleProcessingError(gomock.Any(), gomock.Any(), modshared.NoAgentId, "ReadinessProbe failed", expectedErr)
rec := httpGet(t, "/readiness")
httpResponse := rec.Result()
require.Equal(t, http.StatusInternalServerError, httpResponse.StatusCode)
require.Equal(t, "test-readiness-toggle: not ready yet\n", rec.Body.String())
httpResponse.Body.Close()
// succeeds when toggle has been called
markReady()
rec = httpGet(t, "/readiness")
httpResponse = rec.Result()
require.Equal(t, http.StatusOK, httpResponse.StatusCode)
require.Empty(t, rec.Body)
httpResponse.Body.Close()
// fails when a probe fails
probeErr := fmt.Errorf("failed readiness on purpose")
expectedErr = fmt.Errorf("test-readiness: %w", probeErr)
probeRegistry.RegisterReadinessProbe("test-readiness", func(ctx context.Context) error {
return probeErr
})
mockApi.EXPECT().
HandleProcessingError(gomock.Any(), gomock.Any(), modshared.NoAgentId, "ReadinessProbe failed", expectedErr)
rec = httpGet(t, "/readiness")
httpResponse = rec.Result()
require.Equal(t, http.StatusInternalServerError, httpResponse.StatusCode)
require.Equal(t, "test-readiness: failed readiness on purpose\n", rec.Body.String())
httpResponse.Body.Close()
})
}
|