From 4d9e57eb150fee8d24ff32fb4a8a414c77d246e6 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Thu, 10 Apr 2025 15:09:06 +0200 Subject: syz-cluster: provide API for uploading artifacts archive The archive would be a useful source of debugging information. Provide an HTTP endpoint that accepts a multipart form request with the archived data. Provide an *api.Client method to encapsulate the encoding of the data. Add a test. --- syz-cluster/pkg/api/client.go | 11 +++++++++++ syz-cluster/pkg/api/http.go | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+) (limited to 'syz-cluster/pkg/api') diff --git a/syz-cluster/pkg/api/client.go b/syz-cluster/pkg/api/client.go index d0509cfe0..787748e51 100644 --- a/syz-cluster/pkg/api/client.go +++ b/syz-cluster/pkg/api/client.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "net/http" + "net/url" "strings" "time" ) @@ -75,6 +76,16 @@ func (client Client) UploadTestResult(ctx context.Context, req *TestResult) erro return err } +func (client Client) UploadTestArtifacts(ctx context.Context, sessionID, testName string, + tarGzContent io.Reader) error { + v := url.Values{} + v.Add("session", sessionID) + v.Add("test", testName) + url := client.baseURL + "/tests/upload_artifacts?" + v.Encode() + _, err := postMultiPartFile[any](ctx, url, tarGzContent) + return err +} + func (client Client) UploadFinding(ctx context.Context, req *NewFinding) error { _, err := postJSON[NewFinding, any](ctx, client.baseURL+"/findings/upload", req) return err diff --git a/syz-cluster/pkg/api/http.go b/syz-cluster/pkg/api/http.go index 420b787a7..4523b5374 100644 --- a/syz-cluster/pkg/api/http.go +++ b/syz-cluster/pkg/api/http.go @@ -7,7 +7,9 @@ import ( "bytes" "context" "encoding/json" + "fmt" "io" + "mime/multipart" "net/http" ) @@ -32,6 +34,28 @@ func postJSON[Req any, Resp any](ctx context.Context, url string, req *Req) (*Re return finishRequest[Resp](httpReq) } +func postMultiPartFile[Resp any](ctx context.Context, url string, reader io.Reader) (*Resp, error) { + // TODO: this will work well only up to some size of the file. We need a pipe and a goroutine. + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + part, err := writer.CreateFormFile("content", "content") + if err != nil { + return nil, fmt.Errorf("failed to create a form file part: %w", err) + } + if _, err := io.Copy(part, reader); err != nil { + return nil, err + } + if err := writer.Close(); err != nil { + return nil, err + } + httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body) + if err != nil { + return nil, err + } + httpReq.Header.Set("Content-Type", writer.FormDataContentType()) + return finishRequest[Resp](httpReq) +} + func ReplyJSON[T any](w http.ResponseWriter, resp T) { w.Header().Set("Content-Type", "application/json") err := json.NewEncoder(w).Encode(resp) -- cgit mrf-deployment