mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
feat(coderd): add simple healthcheck formatting option (#9864)
This commit is contained in:
+28
-2
@@ -2,6 +2,7 @@ package coderd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -37,7 +38,7 @@ func (api *API) debugDeploymentHealth(rw http.ResponseWriter, r *http.Request) {
|
|||||||
// Get cached report if it exists.
|
// Get cached report if it exists.
|
||||||
if report := api.healthCheckCache.Load(); report != nil {
|
if report := api.healthCheckCache.Load(); report != nil {
|
||||||
if time.Since(report.Time) < api.HealthcheckRefresh {
|
if time.Since(report.Time) < api.HealthcheckRefresh {
|
||||||
httpapi.WriteIndent(ctx, rw, http.StatusOK, report)
|
formatHealthcheck(ctx, rw, r, report)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,11 +60,36 @@ func (api *API) debugDeploymentHealth(rw http.ResponseWriter, r *http.Request) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
case res := <-resChan:
|
case res := <-resChan:
|
||||||
httpapi.WriteIndent(ctx, rw, http.StatusOK, res.Val)
|
formatHealthcheck(ctx, rw, r, res.Val)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formatHealthcheck(ctx context.Context, rw http.ResponseWriter, r *http.Request, hc *healthcheck.Report) {
|
||||||
|
format := r.URL.Query().Get("format")
|
||||||
|
switch format {
|
||||||
|
case "text":
|
||||||
|
rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||||
|
rw.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
_, _ = fmt.Fprintln(rw, "time:", hc.Time.Format(time.RFC3339))
|
||||||
|
_, _ = fmt.Fprintln(rw, "healthy:", hc.Healthy)
|
||||||
|
_, _ = fmt.Fprintln(rw, "derp:", hc.DERP.Healthy)
|
||||||
|
_, _ = fmt.Fprintln(rw, "access_url:", hc.AccessURL.Healthy)
|
||||||
|
_, _ = fmt.Fprintln(rw, "websocket:", hc.Websocket.Healthy)
|
||||||
|
_, _ = fmt.Fprintln(rw, "database:", hc.Database.Healthy)
|
||||||
|
|
||||||
|
case "", "json":
|
||||||
|
httpapi.WriteIndent(ctx, rw, http.StatusOK, hc)
|
||||||
|
|
||||||
|
default:
|
||||||
|
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||||
|
Message: fmt.Sprintf("Invalid format option %q.", format),
|
||||||
|
Detail: "Allowed values are: \"json\", \"simple\".",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// For some reason the swagger docs need to be attached to a function.
|
// For some reason the swagger docs need to be attached to a function.
|
||||||
//
|
//
|
||||||
// @Summary Debug Info Websocket Test
|
// @Summary Debug Info Websocket Test
|
||||||
|
|||||||
+37
-1
@@ -12,6 +12,7 @@ import (
|
|||||||
|
|
||||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||||
"github.com/coder/coder/v2/coderd/healthcheck"
|
"github.com/coder/coder/v2/coderd/healthcheck"
|
||||||
|
"github.com/coder/coder/v2/coderd/healthcheck/derphealth"
|
||||||
"github.com/coder/coder/v2/testutil"
|
"github.com/coder/coder/v2/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ func TestDebugHealth(t *testing.T) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
sessionToken = client.SessionToken()
|
sessionToken = client.SessionToken()
|
||||||
res, err := client.Request(ctx, "GET", "/debug/health", nil)
|
res, err := client.Request(ctx, "GET", "/api/v2/debug/health", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
_, _ = io.ReadAll(res.Body)
|
_, _ = io.ReadAll(res.Body)
|
||||||
@@ -106,6 +107,41 @@ func TestDebugHealth(t *testing.T) {
|
|||||||
require.Equal(t, http.StatusOK, res.StatusCode)
|
require.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
require.Equal(t, 1, calls)
|
require.Equal(t, 1, calls)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Text", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var (
|
||||||
|
ctx, cancel = context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||||
|
sessionToken string
|
||||||
|
client = coderdtest.New(t, &coderdtest.Options{
|
||||||
|
HealthcheckFunc: func(_ context.Context, apiKey string) *healthcheck.Report {
|
||||||
|
assert.Equal(t, sessionToken, apiKey)
|
||||||
|
return &healthcheck.Report{
|
||||||
|
Time: time.Now(),
|
||||||
|
Healthy: true,
|
||||||
|
DERP: derphealth.Report{Healthy: true},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
_ = coderdtest.CreateFirstUser(t, client)
|
||||||
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
sessionToken = client.SessionToken()
|
||||||
|
res, err := client.Request(ctx, "GET", "/api/v2/debug/health?format=text", nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer res.Body.Close()
|
||||||
|
resB, _ := io.ReadAll(res.Body)
|
||||||
|
require.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
|
|
||||||
|
resStr := string(resB)
|
||||||
|
assert.Contains(t, resStr, "healthy: true")
|
||||||
|
assert.Contains(t, resStr, "derp: true")
|
||||||
|
assert.Contains(t, resStr, "access_url: false")
|
||||||
|
assert.Contains(t, resStr, "websocket: false")
|
||||||
|
assert.Contains(t, resStr, "database: false")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDebugWebsocket(t *testing.T) {
|
func TestDebugWebsocket(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user