|
1 | 1 | package cli_test
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "bufio" |
| 5 | + "context" |
| 6 | + "errors" |
4 | 7 | "fmt"
|
5 | 8 | "net/http"
|
6 | 9 | "net/http/httptest"
|
| 10 | + "strings" |
| 11 | + "sync" |
7 | 12 | "sync/atomic"
|
8 | 13 | "testing"
|
9 | 14 |
|
10 | 15 | "github.com/stretchr/testify/assert"
|
11 | 16 | "github.com/stretchr/testify/require"
|
12 | 17 |
|
| 18 | + "github.com/coder/coder/v2/cli/clitest" |
13 | 19 | "github.com/coder/coder/v2/pty/ptytest"
|
| 20 | + "github.com/coder/coder/v2/testutil" |
14 | 21 | )
|
15 | 22 |
|
16 | 23 | func Test_Headers(t *testing.T) {
|
@@ -52,3 +59,88 @@ func Test_Headers(t *testing.T) {
|
52 | 59 |
|
53 | 60 | assert.EqualValues(t, 1, atomic.LoadInt64(&called))
|
54 | 61 | }
|
| 62 | + |
| 63 | +func TestWorkspaceProxy_Server_PrometheusEnabled(t *testing.T) { |
| 64 | + t.Parallel() |
| 65 | + |
| 66 | + prometheusPort := randomPort(t) |
| 67 | + |
| 68 | + var wg sync.WaitGroup |
| 69 | + wg.Add(1) |
| 70 | + |
| 71 | + // Start fake coderd |
| 72 | + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 73 | + if r.URL.Path == "/api/v2/workspaceproxies/me/register" { |
| 74 | + // Give fake app_security_key (96 bytes) |
| 75 | + w.WriteHeader(http.StatusCreated) |
| 76 | + _, _ = w.Write([]byte(`{"app_security_key": "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789123456012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789123456"}`)) |
| 77 | + return |
| 78 | + } |
| 79 | + if r.URL.Path == "/api/v2/workspaceproxies/me/coordinate" { |
| 80 | + // Slow down proxy registration, so that test runner can check if Prometheus endpoint is exposed. |
| 81 | + wg.Wait() |
| 82 | + |
| 83 | + // Does not matter, we are not going to implement a real workspace proxy. |
| 84 | + w.WriteHeader(http.StatusNotImplemented) |
| 85 | + return |
| 86 | + } |
| 87 | + |
| 88 | + w.Header().Add("Content-Type", "application/json") |
| 89 | + _, _ = w.Write([]byte(`{}`)) // build info can be ignored |
| 90 | + })) |
| 91 | + defer srv.Close() |
| 92 | + defer wg.Done() |
| 93 | + |
| 94 | + // Configure CLI client |
| 95 | + inv, _ := newCLI(t, "wsproxy", "server", |
| 96 | + "--primary-access-url", srv.URL, |
| 97 | + "--proxy-session-token", "test-token", |
| 98 | + "--access-url", "http://foobar:3001", |
| 99 | + "--http-address", fmt.Sprintf("127.0.0.1:%d", randomPort(t)), |
| 100 | + "--prometheus-enable", |
| 101 | + "--prometheus-address", fmt.Sprintf("127.0.0.1:%d", prometheusPort), |
| 102 | + ) |
| 103 | + pty := ptytest.New(t).Attach(inv) |
| 104 | + |
| 105 | + ctx, cancel := context.WithTimeout(inv.Context(), testutil.WaitLong) |
| 106 | + defer cancel() |
| 107 | + |
| 108 | + // Start "wsproxy server" command |
| 109 | + clitest.StartWithAssert(t, inv, func(t *testing.T, err error) { |
| 110 | + assert.Error(t, err) |
| 111 | + assert.False(t, errors.Is(err, context.Canceled), "error was expected, but context was canceled") |
| 112 | + }) |
| 113 | + pty.ExpectMatchContext(ctx, "Started HTTP listener at") |
| 114 | + |
| 115 | + // Fetch metrics from Prometheus endpoint |
| 116 | + var res *http.Response |
| 117 | + require.Eventually(t, func() bool { |
| 118 | + req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("http://127.0.0.1:%d", prometheusPort), nil) |
| 119 | + assert.NoError(t, err) |
| 120 | + // nolint:bodyclose |
| 121 | + res, err = http.DefaultClient.Do(req) |
| 122 | + return err == nil |
| 123 | + }, testutil.WaitShort, testutil.IntervalFast) |
| 124 | + defer res.Body.Close() |
| 125 | + |
| 126 | + // Scan for metric patterns |
| 127 | + scanner := bufio.NewScanner(res.Body) |
| 128 | + hasGoStats := false |
| 129 | + hasPromHTTP := false |
| 130 | + for scanner.Scan() { |
| 131 | + if strings.HasPrefix(scanner.Text(), "go_goroutines") { |
| 132 | + hasGoStats = true |
| 133 | + continue |
| 134 | + } |
| 135 | + if strings.HasPrefix(scanner.Text(), "promhttp_metric_handler_requests_total") { |
| 136 | + hasPromHTTP = true |
| 137 | + continue |
| 138 | + } |
| 139 | + t.Logf("scanned %s", scanner.Text()) |
| 140 | + } |
| 141 | + require.NoError(t, scanner.Err()) |
| 142 | + |
| 143 | + // Verify patterns |
| 144 | + require.True(t, hasGoStats, "Go stats are missing") |
| 145 | + require.True(t, hasPromHTTP, "Prometheus HTTP metrics are missing") |
| 146 | +} |
0 commit comments