aboutsummaryrefslogtreecommitdiffstats
path: root/syz-cluster/pkg/controller
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2025-04-10 15:09:06 +0200
committerAleksandr Nogikh <nogikh@google.com>2025-04-11 10:07:50 +0000
commit4d9e57eb150fee8d24ff32fb4a8a414c77d246e6 (patch)
tree9214e0b17c3aa833b9f812b52c5ecf7b28b4d741 /syz-cluster/pkg/controller
parentdfb5be349af98db984a0944f49896f454e4bc8a6 (diff)
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.
Diffstat (limited to 'syz-cluster/pkg/controller')
-rw-r--r--syz-cluster/pkg/controller/api.go31
-rw-r--r--syz-cluster/pkg/controller/api_test.go19
2 files changed, 50 insertions, 0 deletions
diff --git a/syz-cluster/pkg/controller/api.go b/syz-cluster/pkg/controller/api.go
index bcab7813f..6dbdc4889 100644
--- a/syz-cluster/pkg/controller/api.go
+++ b/syz-cluster/pkg/controller/api.go
@@ -43,6 +43,7 @@ func (c APIServer) Mux() *http.ServeMux {
mux.HandleFunc("/sessions/upload", c.uploadSession)
mux.HandleFunc("/sessions/{session_id}/series", c.getSessionSeries)
mux.HandleFunc("/sessions/{session_id}/skip", c.skipSession)
+ mux.HandleFunc("/tests/upload_artifacts", c.uploadTestArtifact)
mux.HandleFunc("/tests/upload", c.uploadTest)
mux.HandleFunc("/trees", c.getTrees)
return mux
@@ -116,6 +117,36 @@ func (c APIServer) uploadTest(w http.ResponseWriter, r *http.Request) {
api.ReplyJSON[interface{}](w, nil)
}
+func (c APIServer) uploadTestArtifact(w http.ResponseWriter, r *http.Request) {
+ const maxMemory = 16 * 1000 * 1000 // 16 MB.
+ if err := r.ParseMultipartForm(maxMemory); err != nil {
+ http.Error(w, "could not parse the multipart form", http.StatusBadRequest)
+ return
+ }
+ defer r.MultipartForm.RemoveAll()
+
+ file, _, err := r.FormFile("content")
+ if err != nil {
+ if err == http.ErrMissingFile {
+ http.Error(w, "the 'content' file must be present", http.StatusBadRequest)
+ return
+ }
+ http.Error(w, fmt.Sprintf("failed to query the file: %s", err), http.StatusInternalServerError)
+ return
+ }
+ defer file.Close()
+
+ err = c.testService.SaveArtifacts(r.Context(),
+ r.FormValue("session"),
+ r.FormValue("test"),
+ file)
+ if err != nil {
+ http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
+ return
+ }
+ api.ReplyJSON[interface{}](w, nil)
+}
+
func (c APIServer) uploadFinding(w http.ResponseWriter, r *http.Request) {
req := api.ParseJSON[api.NewFinding](w, r)
if req == nil {
diff --git a/syz-cluster/pkg/controller/api_test.go b/syz-cluster/pkg/controller/api_test.go
index 2b14fbe5d..fa8491208 100644
--- a/syz-cluster/pkg/controller/api_test.go
+++ b/syz-cluster/pkg/controller/api_test.go
@@ -4,6 +4,7 @@
package controller
import (
+ "bytes"
"testing"
"time"
@@ -80,6 +81,24 @@ func TestAPISaveFinding(t *testing.T) {
})
}
+func TestAPIUploadTestArtifacts(t *testing.T) {
+ env, ctx := app.TestEnvironment(t)
+ client := TestServer(t, env)
+
+ _, sessionID := UploadTestSeries(t, ctx, client, testSeries)
+ buildResp := UploadTestBuild(t, ctx, client, testBuild)
+ err := client.UploadTestResult(ctx, &api.TestResult{
+ SessionID: sessionID,
+ BaseBuildID: buildResp.ID,
+ TestName: "test",
+ Result: api.TestRunning,
+ Log: []byte("some log"),
+ })
+ assert.NoError(t, err)
+ err = client.UploadTestArtifacts(ctx, sessionID, "test", bytes.NewReader([]byte("artifacts content")))
+ assert.NoError(t, err)
+}
+
var testSeries = &api.Series{
ExtID: "ext-id",
AuthorEmail: "some@email.com",