File: metric_server_test.go

package info (click to toggle)
gitlab-agent 16.1.3-2
  • links: PTS, VCS
  • area: contrib
  • in suites: forky, sid, trixie
  • size: 6,324 kB
  • sloc: makefile: 175; sh: 52; ruby: 3
file content (116 lines) | stat: -rw-r--r-- 4,207 bytes parent folder | download
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()
	})
}