From c6aaae86e8a8c8851fff28f6c68f2ec03d045f95 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 12 Aug 2024 12:11:11 +0100 Subject: [PATCH 1/2] chore(log): add unit test to validate that retries happen --- log/coder_internal_test.go | 49 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/log/coder_internal_test.go b/log/coder_internal_test.go index 22b6f249..4895150e 100644 --- a/log/coder_internal_test.go +++ b/log/coder_internal_test.go @@ -170,6 +170,55 @@ func TestCoder(t *testing.T) { require.ErrorIs(t, err, context.DeadlineExceeded) <-handlerDone }) + + // In this test, we validate that a 401 error on the initial connect + // results in a retry. When envbuilder initially attempts to connect + // using the Coder agent token, the workspace build may not yet have + // completed. + t.Run("V2Retry", func(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + token := uuid.NewString() + done := make(chan struct{}) + handlerSend := make(chan int) + handler := func(w http.ResponseWriter, r *http.Request) { + t.Logf("test handler: %s", r.URL.Path) + if r.URL.Path == "/api/v2/buildinfo" { + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"version": "v2.9.0"}`)) + return + } + code := <-handlerSend + t.Logf("test handler response: %d", code) + w.WriteHeader(code) + } + srv := httptest.NewServer(http.HandlerFunc(handler)) + defer srv.Close() + + u, err := url.Parse(srv.URL) + require.NoError(t, err) + var connectError error + go func() { + defer close(handlerSend) + defer close(done) + _, _, connectError = Coder(ctx, u, token) + }() + + // Initial: unauthorized + handlerSend <- http.StatusUnauthorized + // 2nd try: still unauthorized + handlerSend <- http.StatusUnauthorized + // 3rd try: authorized + handlerSend <- http.StatusOK + + cancel() + + <-done + require.ErrorContains(t, connectError, "failed to WebSocket dial") + require.ErrorIs(t, connectError, context.Canceled) + }) } type fakeLogDest struct { From 7d72b567bdd82a458eb73d044c51d5edb557b51b Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 12 Aug 2024 14:58:01 +0100 Subject: [PATCH 2/2] fix(log): bump rpcConnectTimeout to 30s --- log/coder.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/log/coder.go b/log/coder.go index 38e9373e..d8b4fe0d 100644 --- a/log/coder.go +++ b/log/coder.go @@ -19,7 +19,10 @@ import ( ) var ( - rpcConnectTimeout = 10 * time.Second + // We set a relatively high connection timeout for the initial connection. + // There is an unfortunate race between the envbuilder container starting and the + // associated provisioner job completing. + rpcConnectTimeout = 30 * time.Second logSendGracePeriod = 10 * time.Second minAgentAPIV2 = "v2.9" )