Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions internal/api/serverless/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ func (c *Client) ListGpuTypes(ctx context.Context) ([]GpuType, error) {
return nil, fmt.Errorf("list GPU types: %w", err)
}

c.logResponse(ctx, "/v1/gpu-types", resp.StatusCode(), resp.Body)
c.logResponse(ctx, resp.HTTPResponse, resp.Body)

switch resp.StatusCode() {
case http.StatusOK:
Expand Down Expand Up @@ -230,7 +230,7 @@ func (c *Client) CreateDeployment(ctx context.Context, body DeploymentCreate) (*
return nil, fmt.Errorf("create deployment: %w", err)
}

c.logResponse(ctx, "/v1/deployments", resp.StatusCode(), resp.Body)
c.logResponse(ctx, resp.HTTPResponse, resp.Body)

switch resp.StatusCode() {
case http.StatusCreated:
Expand Down Expand Up @@ -264,7 +264,7 @@ func (c *Client) ListDeployments(ctx context.Context, params *ListDeploymentsPar
return Page[Deployment]{}, fmt.Errorf("list deployments: %w", err)
}

c.logResponse(ctx, "/v1/deployments", resp.StatusCode(), resp.Body)
c.logResponse(ctx, resp.HTTPResponse, resp.Body)

switch resp.StatusCode() {
case http.StatusOK:
Expand Down Expand Up @@ -296,7 +296,7 @@ func (c *Client) GetDeployment(ctx context.Context, deploymentID string) (*Deplo
return nil, fmt.Errorf("get deployment: %w", err)
}

c.logResponse(ctx, "/v1/deployments/"+deploymentID, resp.StatusCode(), resp.Body)
c.logResponse(ctx, resp.HTTPResponse, resp.Body)

switch resp.StatusCode() {
case http.StatusOK:
Expand Down Expand Up @@ -327,7 +327,7 @@ func (c *Client) UpdateDeployment(ctx context.Context, deploymentID string, body
return nil, fmt.Errorf("update deployment: %w", err)
}

c.logResponse(ctx, "/v1/deployments/"+deploymentID, resp.StatusCode(), resp.Body)
c.logResponse(ctx, resp.HTTPResponse, resp.Body)

switch resp.StatusCode() {
case http.StatusOK:
Expand Down Expand Up @@ -363,7 +363,7 @@ func (c *Client) ListEndpoints(ctx context.Context, deploymentID string, params
return Page[Endpoint]{}, fmt.Errorf("list endpoints: %w", err)
}

c.logResponse(ctx, "/v1/deployments/"+deploymentID+"/endpoints", resp.StatusCode(), resp.Body)
c.logResponse(ctx, resp.HTTPResponse, resp.Body)

switch resp.StatusCode() {
case http.StatusOK:
Expand Down Expand Up @@ -393,7 +393,7 @@ func (c *Client) ListVersions(ctx context.Context, deploymentID string, params *
return Page[Version]{}, fmt.Errorf("list versions: %w", err)
}

c.logResponse(ctx, "/v1/deployments/"+deploymentID+"/versions", resp.StatusCode(), resp.Body)
c.logResponse(ctx, resp.HTTPResponse, resp.Body)

switch resp.StatusCode() {
case http.StatusOK:
Expand Down Expand Up @@ -423,7 +423,7 @@ func (c *Client) GetVersion(ctx context.Context, deploymentID string, versionNum
return nil, fmt.Errorf("get version: %w", err)
}

c.logResponse(ctx, fmt.Sprintf("/v1/deployments/%s/versions/%d", deploymentID, versionNumber), resp.StatusCode(), resp.Body)
c.logResponse(ctx, resp.HTTPResponse, resp.Body)

switch resp.StatusCode() {
case http.StatusOK:
Expand Down Expand Up @@ -453,7 +453,7 @@ func (c *Client) ListBuilds(ctx context.Context, deploymentID string, params *Li
return Page[Build]{}, fmt.Errorf("list builds: %w", err)
}

c.logResponse(ctx, "/v1/deployments/"+deploymentID+"/builds", resp.StatusCode(), resp.Body)
c.logResponse(ctx, resp.HTTPResponse, resp.Body)

switch resp.StatusCode() {
case http.StatusOK:
Expand Down Expand Up @@ -483,7 +483,7 @@ func (c *Client) GetBuild(ctx context.Context, deploymentID string, buildID uuid
return nil, fmt.Errorf("get build: %w", err)
}

c.logResponse(ctx, "/v1/deployments/"+deploymentID+"/builds/"+buildID.String(), resp.StatusCode(), resp.Body)
c.logResponse(ctx, resp.HTTPResponse, resp.Body)

switch resp.StatusCode() {
case http.StatusOK:
Expand Down Expand Up @@ -513,7 +513,7 @@ func (c *Client) ListWorkers(ctx context.Context, deploymentID string, params *L
return Page[Worker]{}, fmt.Errorf("list workers: %w", err)
}

c.logResponse(ctx, "/v1/deployments/"+deploymentID+"/workers", resp.StatusCode(), resp.Body)
c.logResponse(ctx, resp.HTTPResponse, resp.Body)

switch resp.StatusCode() {
case http.StatusOK:
Expand Down
6 changes: 3 additions & 3 deletions internal/api/serverless/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func (c *Client) ListDeploymentEnvironmentVariables(ctx context.Context, deploym
return Page[EnvironmentVariable]{}, fmt.Errorf("list environment variables: %w", err)
}

c.logResponse(ctx, "/v1/deployments/"+deploymentID+"/environment-variables", resp.StatusCode(), resp.Body)
c.logResponse(ctx, resp.HTTPResponse, resp.Body)

switch resp.StatusCode() {
case http.StatusOK:
Expand Down Expand Up @@ -62,7 +62,7 @@ func (c *Client) UpdateDeploymentEnvironmentVariable(ctx context.Context, deploy
return nil, fmt.Errorf("update environment variable: %w", err)
}

c.logResponse(ctx, "/v1/deployments/"+deploymentID+"/environment-variables/"+key, resp.StatusCode(), resp.Body)
c.logResponse(ctx, resp.HTTPResponse, resp.Body)

switch resp.StatusCode() {
case http.StatusOK:
Expand Down Expand Up @@ -97,7 +97,7 @@ func (c *Client) DeleteDeploymentEnvironmentVariable(ctx context.Context, deploy
return fmt.Errorf("delete environment variable: %w", err)
}

c.logResponse(ctx, "/v1/deployments/"+deploymentID+"/environment-variables/"+key, resp.StatusCode(), resp.Body)
c.logResponse(ctx, resp.HTTPResponse, resp.Body)

switch resp.StatusCode() {
case http.StatusNoContent:
Expand Down
24 changes: 19 additions & 5 deletions internal/api/serverless/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ import (

const redactedValue = "[redacted]"

// logResponse writes a debug line for a control-plane response. Success bodies
// have JSON "value" fields redacted so plaintext env vars are not persisted in
// debug logs. Error bodies (problem details) are logged as returned. A nil body
// logs path and status only (used for secrets endpoints whose metadata is opaque).
func (c *Client) logResponse(ctx context.Context, path string, status int, body []byte) {
// logResponse writes a debug line for a control-plane response. Path and
// status come from the generated client's HTTPResponse so they track OpenAPI
// regen (no duplicated path literals). Success bodies have JSON "value"
// fields redacted so plaintext env vars are not persisted in debug logs.
// Error bodies (problem details) are logged as returned. A nil body logs path
// and status only (used for secrets endpoints whose metadata is opaque).
func (c *Client) logResponse(ctx context.Context, httpResp *http.Response, body []byte) {
if c.logger == nil || !c.logger.Enabled(ctx, slog.LevelDebug) {
return
}
path, status := responsePathStatus(httpResp)
if body == nil {
c.logger.Debug("serverless response", //nolint:errcheck,gosec
"path", path,
Expand All @@ -32,6 +35,17 @@ func (c *Client) logResponse(ctx context.Context, path string, status int, body
)
}

func responsePathStatus(httpResp *http.Response) (path string, status int) {
if httpResp == nil {
return "", 0
}
status = httpResp.StatusCode
if httpResp.Request != nil && httpResp.Request.URL != nil {
path = httpResp.Request.URL.Path
}
return path, status
}

func debugLogBody(status int, body []byte) string {
if status >= http.StatusOK && status < http.StatusBadRequest {
return string(redactJSONValues(body))
Expand Down
37 changes: 36 additions & 1 deletion internal/api/serverless/log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ func TestLogResponse_NilBodyOmitsBody(t *testing.T) {
c := &Client{
logger: slog.New(slog.NewJSONHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug})),
}
c.logResponse(context.Background(), "/v1/secrets", http.StatusOK, nil)
httpResp := testHTTPResponse(t, "/v1/secrets", http.StatusOK)
defer func() { _ = httpResp.Body.Close() }()
c.logResponse(context.Background(), httpResp, nil)
out := buf.String()
if !strings.Contains(out, `"/v1/secrets"`) {
t.Fatalf("path missing: %s", out)
Expand All @@ -79,6 +81,39 @@ func TestLogResponse_NilBodyOmitsBody(t *testing.T) {
}
}

func TestResponsePathStatus_FromRequestURL(t *testing.T) {
httpResp := testHTTPResponse(t, "/v1/apps/my-app/builds", http.StatusOK)
defer func() { _ = httpResp.Body.Close() }()
path, status := responsePathStatus(httpResp)
if path != "/v1/apps/my-app/builds" {
t.Fatalf("path = %q", path)
}
if status != http.StatusOK {
t.Fatalf("status = %d", status)
}
}

func TestResponsePathStatus_NilResponse(t *testing.T) {
path, status := responsePathStatus(nil)
if path != "" || status != 0 {
t.Fatalf("got path=%q status=%d", path, status)
}
}

func testHTTPResponse(t *testing.T, path string, status int) *http.Response {
t.Helper()
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "https://api.example"+path, nil)
if err != nil {
t.Fatal(err)
}
resp := &http.Response{
StatusCode: status,
Body: http.NoBody,
Request: req,
}
return resp
}

func TestDebugLogBody_CreatedRedacts(t *testing.T) {
in := []byte(`{"environmentVariables":[{"key":"K","value":"hidden"}]}`)
got := debugLogBody(http.StatusCreated, in)
Expand Down
14 changes: 7 additions & 7 deletions internal/api/serverless/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (c *Client) ListSecrets(ctx context.Context, params *ListSecretsParams) (Pa
return Page[Secret]{}, fmt.Errorf("list secrets: %w", err)
}

c.logResponse(ctx, "/v1/secrets", resp.StatusCode(), nil)
c.logResponse(ctx, resp.HTTPResponse, nil)

switch resp.StatusCode() {
case http.StatusOK:
Expand Down Expand Up @@ -80,7 +80,7 @@ func (c *Client) CreateSecret(ctx context.Context, body SecretCreate) (*Secret,
return nil, fmt.Errorf("create secret: %w", err)
}

c.logResponse(ctx, "/v1/secrets", resp.StatusCode(), nil)
c.logResponse(ctx, resp.HTTPResponse, nil)

switch resp.StatusCode() {
case http.StatusCreated:
Expand Down Expand Up @@ -114,7 +114,7 @@ func (c *Client) UpdateSecret(ctx context.Context, name string, body SecretUpdat
return nil, fmt.Errorf("update secret: %w", err)
}

c.logResponse(ctx, "/v1/secrets/"+name, resp.StatusCode(), nil)
c.logResponse(ctx, resp.HTTPResponse, nil)

switch resp.StatusCode() {
case http.StatusOK:
Expand Down Expand Up @@ -149,7 +149,7 @@ func (c *Client) DeleteSecret(ctx context.Context, name string) error {
return fmt.Errorf("delete secret: %w", err)
}

c.logResponse(ctx, "/v1/secrets/"+name, resp.StatusCode(), nil)
c.logResponse(ctx, resp.HTTPResponse, nil)

switch resp.StatusCode() {
case http.StatusNoContent:
Expand Down Expand Up @@ -180,7 +180,7 @@ func (c *Client) ListDeploymentSecrets(ctx context.Context, deploymentID string,
return Page[SecretAttachment]{}, fmt.Errorf("list deployment secrets: %w", err)
}

c.logResponse(ctx, "/v1/deployments/"+deploymentID+"/secrets", resp.StatusCode(), nil)
c.logResponse(ctx, resp.HTTPResponse, nil)

switch resp.StatusCode() {
case http.StatusOK:
Expand Down Expand Up @@ -215,7 +215,7 @@ func (c *Client) AttachDeploymentSecret(ctx context.Context, deploymentID string
return fmt.Errorf("attach secret: %w", err)
}

c.logResponse(ctx, "/v1/deployments/"+deploymentID+"/secrets", resp.StatusCode(), nil)
c.logResponse(ctx, resp.HTTPResponse, nil)

switch resp.StatusCode() {
case http.StatusNoContent:
Expand Down Expand Up @@ -249,7 +249,7 @@ func (c *Client) DetachDeploymentSecret(ctx context.Context, deploymentID, secre
return fmt.Errorf("detach secret: %w", err)
}

c.logResponse(ctx, "/v1/deployments/"+deploymentID+"/secrets/"+secretName, resp.StatusCode(), nil)
c.logResponse(ctx, resp.HTTPResponse, nil)

switch resp.StatusCode() {
case http.StatusNoContent:
Expand Down