aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2025-02-13 13:59:19 +0100
committerAleksandr Nogikh <nogikh@google.com>2025-02-14 13:40:12 +0000
commiteaf86f3f4dc8a7190abf09fe840e20bcf83709d8 (patch)
treec28d030a8923833ffc39005b1a57946cd48fea62
parent59c86b9e1c7a0f91fbb1b680676f33b4cc7bf137 (diff)
syz-cluster/controller: move the API server to pkg/controller
This will facilitate its reuse in tests.
-rw-r--r--syz-cluster/controller/main.go3
-rw-r--r--syz-cluster/controller/processor_test.go12
-rw-r--r--syz-cluster/pkg/controller/api.go (renamed from syz-cluster/controller/api.go)42
-rw-r--r--syz-cluster/pkg/controller/api_test.go (renamed from syz-cluster/controller/api_test.go)75
-rw-r--r--syz-cluster/pkg/controller/testutil.go43
-rw-r--r--syz-cluster/pkg/service/finding.go2
6 files changed, 93 insertions, 84 deletions
diff --git a/syz-cluster/controller/main.go b/syz-cluster/controller/main.go
index 9667c2239..525c02da0 100644
--- a/syz-cluster/controller/main.go
+++ b/syz-cluster/controller/main.go
@@ -11,6 +11,7 @@ import (
"net/http"
"github.com/google/syzkaller/syz-cluster/pkg/app"
+ "github.com/google/syzkaller/syz-cluster/pkg/controller"
)
func main() {
@@ -24,7 +25,7 @@ func main() {
err := sp.Loop(ctx)
app.Fatalf("processor loop failed: %v", err)
}()
- api := NewControllerAPI(env)
+ api := controller.NewAPIServer(env)
log.Printf("listening on port 8080")
app.Fatalf("listen failed: %v", http.ListenAndServe(":8080", api.Mux()))
}
diff --git a/syz-cluster/controller/processor_test.go b/syz-cluster/controller/processor_test.go
index f56108d3f..ff85ebec9 100644
--- a/syz-cluster/controller/processor_test.go
+++ b/syz-cluster/controller/processor_test.go
@@ -6,13 +6,13 @@ package main
import (
"context"
"fmt"
- "net/http/httptest"
"sync"
"testing"
"time"
"github.com/google/syzkaller/syz-cluster/pkg/api"
"github.com/google/syzkaller/syz-cluster/pkg/app"
+ "github.com/google/syzkaller/syz-cluster/pkg/controller"
"github.com/google/syzkaller/syz-cluster/pkg/db"
"github.com/google/syzkaller/syz-cluster/pkg/workflow"
"github.com/stretchr/testify/assert"
@@ -42,7 +42,7 @@ func TestProcessor(t *testing.T) {
})
}
for _, series := range allSeries[0:5] {
- uploadSeries(t, ctx, client, series)
+ controller.UploadTestSeries(t, ctx, client, series)
}
// Let some workflows finish.
@@ -66,7 +66,7 @@ func TestProcessor(t *testing.T) {
// Add some more series.
for _, series := range allSeries[5:10] {
- uploadSeries(t, ctx, client, series)
+ controller.UploadTestSeries(t, ctx, client, series)
}
// Finish all of them.
@@ -143,14 +143,12 @@ func newMockedWorkflows() *mockedWorkflows {
func prepareProcessorTest(t *testing.T, workflows workflow.Service) (*SeriesProcessor,
*api.Client, context.Context) {
env, ctx := app.TestEnvironment(t)
- apiServer := NewControllerAPI(env)
- server := httptest.NewServer(apiServer.Mux())
- t.Cleanup(server.Close)
+ client := controller.TestServer(t, env)
return &SeriesProcessor{
seriesRepo: db.NewSeriesRepository(env.Spanner),
sessionRepo: db.NewSessionRepository(env.Spanner),
workflows: workflows,
dbPollInterval: time.Second / 10,
parallelWorkers: 2,
- }, api.NewClient(server.URL), ctx
+ }, client, ctx
}
diff --git a/syz-cluster/controller/api.go b/syz-cluster/pkg/controller/api.go
index cddaaf2e2..270803c3b 100644
--- a/syz-cluster/controller/api.go
+++ b/syz-cluster/pkg/controller/api.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
// nolint: dupl // The methods look similar, but extracting the common parts will only make the code worse.
-package main
+package controller
import (
"encoding/json"
@@ -16,7 +16,7 @@ import (
"github.com/google/syzkaller/syz-cluster/pkg/service"
)
-type ControllerAPI struct {
+type APIServer struct {
seriesService *service.SeriesService
sessionService *service.SessionService
buildService *service.BuildService
@@ -24,8 +24,8 @@ type ControllerAPI struct {
findingService *service.FindingService
}
-func NewControllerAPI(env *app.AppEnvironment) *ControllerAPI {
- return &ControllerAPI{
+func NewAPIServer(env *app.AppEnvironment) *APIServer {
+ return &APIServer{
seriesService: service.NewSeriesService(env),
sessionService: service.NewSessionService(env),
buildService: service.NewBuildService(env),
@@ -34,7 +34,7 @@ func NewControllerAPI(env *app.AppEnvironment) *ControllerAPI {
}
}
-func (c ControllerAPI) Mux() *http.ServeMux {
+func (c APIServer) Mux() *http.ServeMux {
mux := http.NewServeMux()
mux.HandleFunc("/sessions/{session_id}/series", c.getSessionSeries)
mux.HandleFunc("/sessions/{session_id}/skip", c.skipSession)
@@ -48,7 +48,7 @@ func (c ControllerAPI) Mux() *http.ServeMux {
return mux
}
-func (c ControllerAPI) getSessionSeries(w http.ResponseWriter, r *http.Request) {
+func (c APIServer) getSessionSeries(w http.ResponseWriter, r *http.Request) {
resp, err := c.seriesService.GetSessionSeries(r.Context(), r.PathValue("session_id"))
if err == service.ErrSeriesNotFound || err == service.ErrSessionNotFound {
http.Error(w, fmt.Sprint(err), http.StatusNotFound)
@@ -60,8 +60,8 @@ func (c ControllerAPI) getSessionSeries(w http.ResponseWriter, r *http.Request)
reply(w, resp)
}
-func (c ControllerAPI) skipSession(w http.ResponseWriter, r *http.Request) {
- req := parseBody[api.SkipRequest](w, r)
+func (c APIServer) skipSession(w http.ResponseWriter, r *http.Request) {
+ req := api.ParseJSON[api.SkipRequest](w, r)
if req == nil {
return
}
@@ -76,7 +76,7 @@ func (c ControllerAPI) skipSession(w http.ResponseWriter, r *http.Request) {
reply[interface{}](w, nil)
}
-func (c ControllerAPI) getSeries(w http.ResponseWriter, r *http.Request) {
+func (c APIServer) getSeries(w http.ResponseWriter, r *http.Request) {
resp, err := c.seriesService.GetSeries(r.Context(), r.PathValue("series_id"))
if errors.Is(err, service.ErrSeriesNotFound) {
http.Error(w, fmt.Sprint(err), http.StatusNotFound)
@@ -88,8 +88,8 @@ func (c ControllerAPI) getSeries(w http.ResponseWriter, r *http.Request) {
reply(w, resp)
}
-func (c ControllerAPI) uploadBuild(w http.ResponseWriter, r *http.Request) {
- req := parseBody[api.UploadBuildReq](w, r)
+func (c APIServer) uploadBuild(w http.ResponseWriter, r *http.Request) {
+ req := api.ParseJSON[api.UploadBuildReq](w, r)
if req == nil {
return
}
@@ -102,8 +102,8 @@ func (c ControllerAPI) uploadBuild(w http.ResponseWriter, r *http.Request) {
reply(w, resp)
}
-func (c ControllerAPI) uploadTest(w http.ResponseWriter, r *http.Request) {
- req := parseBody[api.TestResult](w, r)
+func (c APIServer) uploadTest(w http.ResponseWriter, r *http.Request) {
+ req := api.ParseJSON[api.TestResult](w, r)
if req == nil {
return
}
@@ -116,8 +116,8 @@ func (c ControllerAPI) uploadTest(w http.ResponseWriter, r *http.Request) {
reply[interface{}](w, nil)
}
-func (c ControllerAPI) uploadFinding(w http.ResponseWriter, r *http.Request) {
- req := parseBody[api.Finding](w, r)
+func (c APIServer) uploadFinding(w http.ResponseWriter, r *http.Request) {
+ req := api.ParseJSON[api.Finding](w, r)
if req == nil {
return
}
@@ -130,8 +130,8 @@ func (c ControllerAPI) uploadFinding(w http.ResponseWriter, r *http.Request) {
reply[interface{}](w, nil)
}
-func (c ControllerAPI) getLastBuild(w http.ResponseWriter, r *http.Request) {
- req := parseBody[api.LastBuildReq](w, r)
+func (c APIServer) getLastBuild(w http.ResponseWriter, r *http.Request) {
+ req := api.ParseJSON[api.LastBuildReq](w, r)
if req == nil {
return
}
@@ -143,8 +143,8 @@ func (c ControllerAPI) getLastBuild(w http.ResponseWriter, r *http.Request) {
reply[*api.Build](w, resp)
}
-func (c ControllerAPI) uploadSeries(w http.ResponseWriter, r *http.Request) {
- req := parseBody[api.Series](w, r)
+func (c APIServer) uploadSeries(w http.ResponseWriter, r *http.Request) {
+ req := api.ParseJSON[api.Series](w, r)
if req == nil {
return
}
@@ -156,8 +156,8 @@ func (c ControllerAPI) uploadSeries(w http.ResponseWriter, r *http.Request) {
reply[*api.UploadSeriesResp](w, resp)
}
-func (c ControllerAPI) uploadSession(w http.ResponseWriter, r *http.Request) {
- req := parseBody[api.NewSession](w, r)
+func (c APIServer) uploadSession(w http.ResponseWriter, r *http.Request) {
+ req := api.ParseJSON[api.NewSession](w, r)
if req == nil {
return
}
diff --git a/syz-cluster/controller/api_test.go b/syz-cluster/pkg/controller/api_test.go
index 24c113b12..2e3483414 100644
--- a/syz-cluster/controller/api_test.go
+++ b/syz-cluster/pkg/controller/api_test.go
@@ -1,11 +1,9 @@
// Copyright 2024 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
-package main
+package controller
import (
- "context"
- "net/http/httptest"
"testing"
"time"
@@ -16,12 +14,8 @@ import (
func TestAPIGetSeries(t *testing.T) {
env, ctx := app.TestEnvironment(t)
- apiServer := NewControllerAPI(env)
- server := httptest.NewServer(apiServer.Mux())
- defer server.Close()
-
- client := api.NewClient(server.URL)
- seriesID, sessionID := uploadSeries(t, ctx, client, testSeries)
+ client := TestServer(t, env)
+ seriesID, sessionID := UploadTestSeries(t, ctx, client, testSeries)
ret, err := client.GetSessionSeries(ctx, sessionID)
assert.NoError(t, err)
@@ -36,31 +30,23 @@ func TestAPIGetSeries(t *testing.T) {
func TestAPISuccessfulBuild(t *testing.T) {
env, ctx := app.TestEnvironment(t)
- apiServer := NewControllerAPI(env)
- server := httptest.NewServer(apiServer.Mux())
- defer server.Close()
-
- client := api.NewClient(server.URL)
- buildInfo, _ := uploadTestBuild(t, client)
+ client := TestServer(t, env)
+ UploadTestBuild(t, ctx, client, testBuild)
info, err := client.LastSuccessfulBuild(ctx, &api.LastBuildReq{
- Arch: buildInfo.Arch,
- TreeName: buildInfo.TreeName,
- ConfigName: buildInfo.ConfigName,
+ Arch: testBuild.Arch,
+ TreeName: testBuild.TreeName,
+ ConfigName: testBuild.ConfigName,
})
assert.NoError(t, err)
- assert.Equal(t, buildInfo, info)
+ assert.Equal(t, testBuild, info)
}
func TestAPISaveFinding(t *testing.T) {
env, ctx := app.TestEnvironment(t)
- apiServer := NewControllerAPI(env)
- server := httptest.NewServer(apiServer.Mux())
- defer server.Close()
+ client := TestServer(t, env)
- client := api.NewClient(server.URL)
-
- _, sessionID := uploadSeries(t, ctx, client, testSeries)
- _, buildResp := uploadTestBuild(t, client)
+ _, sessionID := UploadTestSeries(t, ctx, client, testSeries)
+ buildResp := UploadTestBuild(t, ctx, client, testBuild)
err := client.UploadTestResult(ctx, &api.TestResult{
SessionID: sessionID,
BaseBuildID: buildResp.ID,
@@ -93,34 +79,6 @@ func TestAPISaveFinding(t *testing.T) {
})
}
-func uploadTestBuild(t *testing.T, client *api.Client) (*api.Build, *api.UploadBuildResp) {
- buildInfo := &api.Build{
- Arch: "amd64",
- TreeName: "mainline",
- ConfigName: "config",
- CommitHash: "abcd",
- CommitDate: time.Date(2020, time.January, 1, 3, 0, 0, 0, time.UTC),
- BuildSuccess: true,
- }
- ret, err := client.UploadBuild(context.Background(), &api.UploadBuildReq{
- Build: *buildInfo,
- })
- assert.NoError(t, err)
- assert.NotEmpty(t, ret.ID)
- return buildInfo, ret
-}
-
-// Returns a (session ID, series ID) tuple.
-func uploadSeries(t *testing.T, ctx context.Context, client *api.Client, series *api.Series) (string, string) {
- retSeries, err := client.UploadSeries(ctx, series)
- assert.NoError(t, err)
- retSession, err := client.UploadSession(ctx, &api.NewSession{
- ExtID: series.ExtID,
- })
- assert.NoError(t, err)
- return retSeries.ID, retSession.ID
-}
-
var testSeries = &api.Series{
ExtID: "ext-id",
AuthorEmail: "some@email.com",
@@ -139,3 +97,12 @@ var testSeries = &api.Series{
},
},
}
+
+var testBuild = &api.Build{
+ Arch: "amd64",
+ TreeName: "mainline",
+ ConfigName: "config",
+ CommitHash: "abcd",
+ CommitDate: time.Date(2020, time.January, 1, 3, 0, 0, 0, time.UTC),
+ BuildSuccess: true,
+}
diff --git a/syz-cluster/pkg/controller/testutil.go b/syz-cluster/pkg/controller/testutil.go
new file mode 100644
index 000000000..0c33df139
--- /dev/null
+++ b/syz-cluster/pkg/controller/testutil.go
@@ -0,0 +1,43 @@
+// Copyright 2025 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package controller
+
+import (
+ "context"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/google/syzkaller/syz-cluster/pkg/api"
+ "github.com/google/syzkaller/syz-cluster/pkg/app"
+ "github.com/stretchr/testify/assert"
+)
+
+// UploadTestSeries returns a (session ID, series ID) tuple.
+func UploadTestSeries(t *testing.T, ctx context.Context,
+ client *api.Client, series *api.Series) (string, string) {
+ retSeries, err := client.UploadSeries(ctx, series)
+ assert.NoError(t, err)
+ retSession, err := client.UploadSession(ctx, &api.NewSession{
+ ExtID: series.ExtID,
+ })
+ assert.NoError(t, err)
+ return retSeries.ID, retSession.ID
+}
+
+func UploadTestBuild(t *testing.T, ctx context.Context, client *api.Client,
+ build *api.Build) *api.UploadBuildResp {
+ ret, err := client.UploadBuild(ctx, &api.UploadBuildReq{
+ Build: *build,
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, ret.ID)
+ return ret
+}
+
+func TestServer(t *testing.T, env *app.AppEnvironment) *api.Client {
+ apiServer := NewAPIServer(env)
+ server := httptest.NewServer(apiServer.Mux())
+ t.Cleanup(server.Close)
+ return api.NewClient(server.URL)
+}
diff --git a/syz-cluster/pkg/service/finding.go b/syz-cluster/pkg/service/finding.go
index aed8dfac7..21eb74c66 100644
--- a/syz-cluster/pkg/service/finding.go
+++ b/syz-cluster/pkg/service/finding.go
@@ -26,7 +26,7 @@ func NewFindingService(env *app.AppEnvironment) *FindingService {
}
}
-func (s *FindingService) Save(ctx context.Context, req *api.NewFinding) error {
+func (s *FindingService) Save(ctx context.Context, req *api.Finding) error {
var reportURI, logURI string
var err error
if len(req.Log) > 0 {