-
Notifications
You must be signed in to change notification settings - Fork 1.5k
feat(server): add /healthz endpoint for container health checks #3060
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -116,6 +116,89 @@ func TestServe(t *testing.T) { | |
| } | ||
| } | ||
|
|
||
| func TestHealthz(t *testing.T) { | ||
| ctx, cancel := context.WithCancel(context.Background()) | ||
| defer cancel() | ||
|
|
||
| addr, port := "127.0.0.1", 5004 | ||
| cfg := server.ServerConfig{ | ||
| Version: "0.0.0", | ||
| Address: addr, | ||
| Port: port, | ||
| AllowedHosts: []string{"*"}, | ||
| } | ||
|
|
||
| otelShutdown, err := telemetry.SetupOTel(ctx, "0.0.0", "", false, "toolbox") | ||
| if err != nil { | ||
| t.Fatalf("unexpected error: %s", err) | ||
| } | ||
| defer func() { | ||
| err := otelShutdown(ctx) | ||
| if err != nil { | ||
| t.Fatalf("unexpected error: %s", err) | ||
| } | ||
| }() | ||
|
|
||
| testLogger, err := log.NewStdLogger(os.Stdout, os.Stderr, "info") | ||
| if err != nil { | ||
| t.Fatalf("unexpected error: %s", err) | ||
| } | ||
| ctx = util.WithLogger(ctx, testLogger) | ||
|
|
||
| instrumentation, err := telemetry.CreateTelemetryInstrumentation(cfg.Version) | ||
| if err != nil { | ||
| t.Fatalf("unexpected error: %s", err) | ||
| } | ||
| ctx = util.WithInstrumentation(ctx, instrumentation) | ||
|
|
||
| s, err := server.NewServer(ctx, cfg) | ||
| if err != nil { | ||
| t.Fatalf("unable to initialize server: %v", err) | ||
| } | ||
|
|
||
| err = s.Listen(ctx) | ||
| if err != nil { | ||
| t.Fatalf("unable to start server: %v", err) | ||
| } | ||
|
|
||
| errCh := make(chan error) | ||
| go func() { | ||
| defer close(errCh) | ||
| err = s.Serve(ctx) | ||
| if err != nil { | ||
| errCh <- err | ||
| } | ||
| }() | ||
|
Comment on lines
+165
to
+170
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a race condition in this test. The goroutine captures and modifies the go func() {
defer close(errCh)
if err := s.Serve(ctx); err != nil {
errCh <- err
}
}() |
||
|
|
||
| url := fmt.Sprintf("http://%s:%d/healthz", addr, port) | ||
| resp, err := http.Get(url) | ||
| if err != nil { | ||
| t.Fatalf("error when sending a request: %s", err) | ||
| } | ||
| defer resp.Body.Close() | ||
|
|
||
| if resp.StatusCode != http.StatusOK { | ||
| t.Fatalf("expected status 200, got %d", resp.StatusCode) | ||
| } | ||
|
|
||
| if ct := resp.Header.Get("Content-Type"); ct != "application/json" { | ||
| t.Fatalf("expected Content-Type application/json, got %q", ct) | ||
| } | ||
|
|
||
| raw, err := io.ReadAll(resp.Body) | ||
| if err != nil { | ||
| t.Fatalf("error reading from request body: %s", err) | ||
| } | ||
|
|
||
| var body map[string]string | ||
| if err := json.Unmarshal(raw, &body); err != nil { | ||
| t.Fatalf("expected JSON body, got %q: %s", string(raw), err) | ||
| } | ||
| if body["status"] != "ok" { | ||
| t.Fatalf(`expected {"status":"ok"}, got %q`, string(raw)) | ||
| } | ||
| } | ||
|
|
||
| func TestUpdateServer(t *testing.T) { | ||
| ctx, err := testutils.ContextWithNewLogger() | ||
| if err != nil { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
/healthzendpoint is registered on the main router, which is subject to thehostCheckmiddleware applied at line 417. In many containerized environments (like Kubernetes), health probes hit the endpoint using the Pod's IP address orlocalhost. IfAllowedHostsis configured with specific domains, these probes will fail with a403 Forbiddenerror, potentially causing deployment failures or restart loops. Consider registering health check endpoints on a router group that bypasses host header validation.