aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2025-04-24 17:04:38 +0200
committerAleksandr Nogikh <nogikh@google.com>2025-04-30 12:32:14 +0000
commit937aafd76e698830e784cf8af849eaae479a6ace (patch)
tree1cf7050e4136b688fbac482cef6b415d181605bf
parent7c6d304203f2c91fd2927dc473e0b56379eb0dd5 (diff)
syz-cluster: separate global env from global config
Environment variables are convenient for storing values like DB or GCS bucket names, but structured formats are more convenient for the actual service configuration. Separate global-config from global-config-env and add the functionality that queries and parses the config options.
-rw-r--r--syz-cluster/controller/deployment.yaml9
-rw-r--r--syz-cluster/controller/main.go6
-rw-r--r--syz-cluster/controller/processor.go42
-rw-r--r--syz-cluster/controller/processor_test.go12
-rw-r--r--syz-cluster/dashboard/deployment.yaml2
-rw-r--r--syz-cluster/overlays/gke/global-config-env.yaml10
-rw-r--r--syz-cluster/overlays/gke/global-config.yaml9
-rw-r--r--syz-cluster/overlays/gke/kustomization.yaml1
-rw-r--r--syz-cluster/overlays/minikube/global-config-env.yaml12
-rw-r--r--syz-cluster/overlays/minikube/global-config.yaml11
-rw-r--r--syz-cluster/overlays/minikube/kustomization.yaml1
-rw-r--r--syz-cluster/pkg/app/config.go61
-rw-r--r--syz-cluster/reporter-server/deployment.yaml2
-rw-r--r--syz-cluster/series-tracker/deployment.yaml11
-rw-r--r--syz-cluster/series-tracker/main.go19
15 files changed, 143 insertions, 65 deletions
diff --git a/syz-cluster/controller/deployment.yaml b/syz-cluster/controller/deployment.yaml
index 5f0df411e..5272725a0 100644
--- a/syz-cluster/controller/deployment.yaml
+++ b/syz-cluster/controller/deployment.yaml
@@ -21,7 +21,10 @@ spec:
image: ${IMAGE_PREFIX}controller:${IMAGE_TAG}
envFrom:
- configMapRef:
- name: global-config
+ name: global-config-env
+ volumeMounts:
+ - name: config-volume
+ mountPath: /config
ports:
- containerPort: 8080
resources:
@@ -31,3 +34,7 @@ spec:
limits:
cpu: 4
memory: 16G
+ volumes:
+ - name: config-volume
+ configMap:
+ name: global-config
diff --git a/syz-cluster/controller/main.go b/syz-cluster/controller/main.go
index 525c02da0..dc6404b38 100644
--- a/syz-cluster/controller/main.go
+++ b/syz-cluster/controller/main.go
@@ -20,7 +20,11 @@ func main() {
if err != nil {
app.Fatalf("failed to set up environment: %v", err)
}
- sp := NewSeriesProcessor(env)
+ cfg, err := app.Config()
+ if err != nil {
+ app.Fatalf("failed to fetch the config: %v", err)
+ }
+ sp := NewSeriesProcessor(env, cfg)
go func() {
err := sp.Loop(ctx)
app.Fatalf("processor loop failed: %v", err)
diff --git a/syz-cluster/controller/processor.go b/syz-cluster/controller/processor.go
index aa286a149..07851a9fa 100644
--- a/syz-cluster/controller/processor.go
+++ b/syz-cluster/controller/processor.go
@@ -8,8 +8,6 @@ import (
"context"
"fmt"
"log"
- "os"
- "strconv"
"sync"
"time"
@@ -22,36 +20,28 @@ import (
)
type SeriesProcessor struct {
- blobStorage blob.Storage
- seriesRepo *db.SeriesRepository
- sessionRepo *db.SessionRepository
- sessionTestRepo *db.SessionTestRepository
- workflows workflow.Service
- dbPollInterval time.Duration
- parallelWorkers int
+ blobStorage blob.Storage
+ seriesRepo *db.SeriesRepository
+ sessionRepo *db.SessionRepository
+ sessionTestRepo *db.SessionTestRepository
+ workflows workflow.Service
+ dbPollInterval time.Duration
+ parallelWorkflows int
}
-func NewSeriesProcessor(env *app.AppEnvironment) *SeriesProcessor {
+func NewSeriesProcessor(env *app.AppEnvironment, cfg *app.AppConfig) *SeriesProcessor {
workflows, err := workflow.NewArgoService()
if err != nil {
app.Fatalf("failed to initialize workflows: %v", err)
}
- parallelWorkers := 1
- if val := os.Getenv("PARALLEL_WORKERS"); val != "" {
- var err error
- parallelWorkers, err = strconv.Atoi(val)
- if err != nil || parallelWorkers < 1 {
- app.Fatalf("invalid PARALLEL_WORKERS value")
- }
- }
return &SeriesProcessor{
- blobStorage: env.BlobStorage,
- seriesRepo: db.NewSeriesRepository(env.Spanner),
- sessionRepo: db.NewSessionRepository(env.Spanner),
- sessionTestRepo: db.NewSessionTestRepository(env.Spanner),
- dbPollInterval: time.Minute,
- workflows: workflows,
- parallelWorkers: parallelWorkers,
+ blobStorage: env.BlobStorage,
+ seriesRepo: db.NewSeriesRepository(env.Spanner),
+ sessionRepo: db.NewSessionRepository(env.Spanner),
+ sessionTestRepo: db.NewSessionTestRepository(env.Spanner),
+ dbPollInterval: time.Minute,
+ workflows: workflows,
+ parallelWorkflows: cfg.ParallelWorkflows,
}
}
@@ -113,7 +103,7 @@ func (sp *SeriesProcessor) seriesRunner(ctx context.Context, ch <-chan *db.Sessi
var eg errgroup.Group
defer eg.Wait()
- eg.SetLimit(sp.parallelWorkers)
+ eg.SetLimit(sp.parallelWorkflows)
for {
var session *db.Session
select {
diff --git a/syz-cluster/controller/processor_test.go b/syz-cluster/controller/processor_test.go
index 9f5e04d9b..68c62ba48 100644
--- a/syz-cluster/controller/processor_test.go
+++ b/syz-cluster/controller/processor_test.go
@@ -191,11 +191,11 @@ func prepareProcessorTest(t *testing.T, workflows workflow.Service) (*SeriesProc
env, ctx := app.TestEnvironment(t)
client := controller.TestServer(t, env)
return &SeriesProcessor{
- seriesRepo: db.NewSeriesRepository(env.Spanner),
- sessionRepo: db.NewSessionRepository(env.Spanner),
- sessionTestRepo: db.NewSessionTestRepository(env.Spanner),
- workflows: workflows,
- dbPollInterval: time.Second / 10,
- parallelWorkers: 2,
+ seriesRepo: db.NewSeriesRepository(env.Spanner),
+ sessionRepo: db.NewSessionRepository(env.Spanner),
+ sessionTestRepo: db.NewSessionTestRepository(env.Spanner),
+ workflows: workflows,
+ dbPollInterval: time.Second / 10,
+ parallelWorkflows: 2,
}, client, ctx
}
diff --git a/syz-cluster/dashboard/deployment.yaml b/syz-cluster/dashboard/deployment.yaml
index 1437098f7..4dd683d6f 100644
--- a/syz-cluster/dashboard/deployment.yaml
+++ b/syz-cluster/dashboard/deployment.yaml
@@ -21,7 +21,7 @@ spec:
image: ${IMAGE_PREFIX}web-dashboard:${IMAGE_TAG}
envFrom:
- configMapRef:
- name: global-config
+ name: global-config-env
ports:
- containerPort: 8081
resources:
diff --git a/syz-cluster/overlays/gke/global-config-env.yaml b/syz-cluster/overlays/gke/global-config-env.yaml
new file mode 100644
index 000000000..ee4d160f1
--- /dev/null
+++ b/syz-cluster/overlays/gke/global-config-env.yaml
@@ -0,0 +1,10 @@
+# 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.
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: global-config-env
+data:
+ SPANNER_DATABASE_URI: "${SPANNER_DATABASE_URI}"
+ BLOB_STORAGE_GCS_BUCKET: "${BLOB_STORAGE_GCS_BUCKET}"
diff --git a/syz-cluster/overlays/gke/global-config.yaml b/syz-cluster/overlays/gke/global-config.yaml
index e38357c34..85cfe4c07 100644
--- a/syz-cluster/overlays/gke/global-config.yaml
+++ b/syz-cluster/overlays/gke/global-config.yaml
@@ -6,7 +6,8 @@ kind: ConfigMap
metadata:
name: global-config
data:
- SPANNER_DATABASE_URI: "${SPANNER_DATABASE_URI}"
- BLOB_STORAGE_GCS_BUCKET: "${BLOB_STORAGE_GCS_BUCKET}"
- PARALLEL_WORKERS: "1"
- LORE_ARCHIVES_TO_POLL: "netdev,linux-ext4" # To start with.
+ config.yaml: |
+ parallelWorkflows: 5
+ loreArchives:
+ - netdev
+ - linux-ext4
diff --git a/syz-cluster/overlays/gke/kustomization.yaml b/syz-cluster/overlays/gke/kustomization.yaml
index ed2846a29..7c5491b10 100644
--- a/syz-cluster/overlays/gke/kustomization.yaml
+++ b/syz-cluster/overlays/gke/kustomization.yaml
@@ -4,6 +4,7 @@
resources:
- ../common
- global-config.yaml
+ - global-config-env.yaml
- kernel-disk-pvc.yaml
- workflow-artifacts.yaml
diff --git a/syz-cluster/overlays/minikube/global-config-env.yaml b/syz-cluster/overlays/minikube/global-config-env.yaml
new file mode 100644
index 000000000..1abfea2cf
--- /dev/null
+++ b/syz-cluster/overlays/minikube/global-config-env.yaml
@@ -0,0 +1,12 @@
+# 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.
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: global-config-env
+data:
+ SPANNER_EMULATOR_HOST: "cloud-spanner-emulator:9010"
+ SPANNER_DATABASE_URI: "projects/my-project/instances/my-instance/databases/db"
+ STORAGE_EMULATOR_HOST: "http://fake-gcs-server.default.svc.cluster.local:4443"
+ BLOB_STORAGE_GCS_BUCKET: "blobs" # Initialized in fake-gcs.yaml
diff --git a/syz-cluster/overlays/minikube/global-config.yaml b/syz-cluster/overlays/minikube/global-config.yaml
index cf517a3f3..e0c126082 100644
--- a/syz-cluster/overlays/minikube/global-config.yaml
+++ b/syz-cluster/overlays/minikube/global-config.yaml
@@ -6,9 +6,8 @@ kind: ConfigMap
metadata:
name: global-config
data:
- SPANNER_EMULATOR_HOST: "cloud-spanner-emulator:9010"
- SPANNER_DATABASE_URI: "projects/my-project/instances/my-instance/databases/db"
- STORAGE_EMULATOR_HOST: "http://fake-gcs-server.default.svc.cluster.local:4443"
- BLOB_STORAGE_GCS_BUCKET: "blobs" # Initialized in fake-gcs.yaml
- PARALLEL_WORKERS: "1" # Process only one series at a time.
- LORE_ARCHIVES_TO_POLL: "linux-wireless" # Whatever, it's just for debugging.
+ config.yaml: |
+ parallelWorkflows: 1
+ # Whatever, it's just for debugging.
+ loreArchives:
+ - linux-wireless
diff --git a/syz-cluster/overlays/minikube/kustomization.yaml b/syz-cluster/overlays/minikube/kustomization.yaml
index 7fed2e666..3e683677d 100644
--- a/syz-cluster/overlays/minikube/kustomization.yaml
+++ b/syz-cluster/overlays/minikube/kustomization.yaml
@@ -6,6 +6,7 @@ resources:
- kernel-disk-pvc.yaml
- ../common
- global-config.yaml
+ - global-config-env.yaml
- fake-gcs.yaml
- network-policy-spanner.yaml
- workflow-artifacts.yaml
diff --git a/syz-cluster/pkg/app/config.go b/syz-cluster/pkg/app/config.go
new file mode 100644
index 000000000..b56d32dbd
--- /dev/null
+++ b/syz-cluster/pkg/app/config.go
@@ -0,0 +1,61 @@
+// 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 app
+
+import (
+ "fmt"
+ "os"
+ "sync"
+
+ "gopkg.in/yaml.v3"
+)
+
+type AppConfig struct {
+ // How many workflows are scheduled in parallel.
+ ParallelWorkflows int `yaml:"parallelWorkflows"`
+ // What Lore archives are to be polled for new patch series.
+ LoreArchives []string `yaml:"loreArchives"`
+}
+
+// The project configuration is expected to be mounted at /config/config.yaml.
+
+func Config() (*AppConfig, error) {
+ configLoadedOnce.Do(loadConfig)
+ return config, configErr
+}
+
+const configPath = `/config/config.yaml`
+
+var configLoadedOnce sync.Once
+var configErr error
+var config *AppConfig
+
+func loadConfig() {
+ data, err := os.ReadFile(configPath)
+ if err != nil {
+ configErr = fmt.Errorf("failed to read %q: %w", configPath, err)
+ return
+ }
+ obj := AppConfig{
+ ParallelWorkflows: 1,
+ }
+ err = yaml.Unmarshal(data, &obj)
+ if err != nil {
+ configErr = fmt.Errorf("failed to parse: %w", err)
+ return
+ }
+ err = obj.Validate()
+ if err != nil {
+ configErr = err
+ return
+ }
+ config = &obj
+}
+
+func (c AppConfig) Validate() error {
+ if c.ParallelWorkflows < 0 {
+ return fmt.Errorf("parallelWorkflows must be non-negative")
+ }
+ return nil
+}
diff --git a/syz-cluster/reporter-server/deployment.yaml b/syz-cluster/reporter-server/deployment.yaml
index 792bb5b13..310924f75 100644
--- a/syz-cluster/reporter-server/deployment.yaml
+++ b/syz-cluster/reporter-server/deployment.yaml
@@ -21,7 +21,7 @@ spec:
image: ${IMAGE_PREFIX}reporter-server:${IMAGE_TAG}
envFrom:
- configMapRef:
- name: global-config
+ name: global-config-env
ports:
- containerPort: 8080
resources:
diff --git a/syz-cluster/series-tracker/deployment.yaml b/syz-cluster/series-tracker/deployment.yaml
index 4e4cb7729..53d03a448 100644
--- a/syz-cluster/series-tracker/deployment.yaml
+++ b/syz-cluster/series-tracker/deployment.yaml
@@ -18,15 +18,11 @@ spec:
containers:
- name: series-tracker-image
image: ${IMAGE_PREFIX}series-tracker:${IMAGE_TAG}
- args:
- - "-archives"
- - "$(LORE_ARCHIVES_TO_POLL)"
- envFrom:
- - configMapRef:
- name: global-config
volumeMounts:
- name: series-tracker-repo-disk
mountPath: /git-repo
+ - name: config-volume
+ mountPath: /config
resources:
requests:
cpu: 4
@@ -38,3 +34,6 @@ spec:
- name: series-tracker-repo-disk
persistentVolumeClaim:
claimName: series-tracker-repo-disk-claim
+ - name: config-volume
+ configMap:
+ name: global-config
diff --git a/syz-cluster/series-tracker/main.go b/syz-cluster/series-tracker/main.go
index 63d3ef91e..2b6611798 100644
--- a/syz-cluster/series-tracker/main.go
+++ b/syz-cluster/series-tracker/main.go
@@ -15,7 +15,6 @@ import (
"regexp"
"slices"
"sort"
- "strings"
"time"
"github.com/google/syzkaller/pkg/email"
@@ -26,8 +25,6 @@ import (
)
var (
- flagArchives = flag.String("archives", "",
- "a comma-separated list of the archives to poll")
flagVerbose = flag.Bool("verbose", false, "enable verbose output")
)
@@ -58,18 +55,14 @@ func main() {
}
func archivesToPoll() []string {
- var ret []string
- for _, part := range strings.Split(*flagArchives, ",") {
- part = strings.TrimSpace(part)
- if part == "" {
- app.Fatalf("an empty name in the --archives argument")
- }
- ret = append(ret, part)
+ cfg, err := app.Config()
+ if err != nil {
+ app.Fatalf("failed to fetch the config: %v", err)
}
- if len(ret) == 0 {
- app.Fatalf("--archives must not be empty")
+ if len(cfg.LoreArchives) == 0 {
+ app.Fatalf("the list of archives to poll is empty")
}
- return ret
+ return cfg.LoreArchives
}
type SeriesFetcher struct {