diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2023-10-09 16:30:20 +0200 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2023-10-12 11:18:22 +0000 |
| commit | d753e779cd5f71b91eaca2e18c383c6598e77c9f (patch) | |
| tree | 65aee07af8b18ce7d5736e8420a966bd7abf3c45 | |
| parent | 3cefb1441cc82b6846ee4b8e43c1661d417c88e9 (diff) | |
dashboard: access config through context
We used to have a single global `config` variable and access it
throughout the whole dashboard application.
However, this approach has been more and more complicated test writing
-- sometimes we want the config to be only slightly different, so that
it's not worth it adding new namespaces, sometimes we have to test how
dashboard handles config changes over time.
This has already led to a number of hacky contextWithXXX methods that
mocked various parts of the global variable. The rest of the code had to
sometimes still use `config` directly and sometimes invoke getXXX(c)
methods. This is very inconsistent and prone to errors.
With more and more situations where we need to patch the config
appearing (see #4118), let's refactor the application to always access
config via the getConfig(c) method. This allows us to uniformly patch
the config and be sure that the non-patched copy is not accessible from
anywhere else.
29 files changed, 336 insertions, 293 deletions
diff --git a/dashboard/app/access.go b/dashboard/app/access.go index 97ed8af01..10a4ac3e9 100644 --- a/dashboard/app/access.go +++ b/dashboard/app/access.go @@ -63,7 +63,7 @@ func accessLevel(c context.Context, r *http.Request) AccessLevel { if u == nil || // Devappserver does not pass AuthDomain. u.AuthDomain != "gmail.com" && !isBrokenAuthDomainInTest || - !strings.HasSuffix(u.Email, config.AuthDomain) { + !strings.HasSuffix(u.Email, getConfig(c).AuthDomain) { return AccessPublic } return AccessUser @@ -127,7 +127,7 @@ func checkCrashTextAccess(c context.Context, r *http.Request, field string, id i if err := db.Get(c, keys[0].Parent(), bug); err != nil { return nil, nil, fmt.Errorf("failed to get bug: %w", err) } - bugLevel := bug.sanitizeAccess(accessLevel(c, r)) + bugLevel := bug.sanitizeAccess(c, accessLevel(c, r)) return bug, crash, checkAccessLevel(c, r, bugLevel) } @@ -151,11 +151,12 @@ func checkJobTextAccess(c context.Context, r *http.Request, field string, id int if err := db.Get(c, keys[0].Parent(), bug); err != nil { return fmt.Errorf("failed to get bug: %w", err) } - bugLevel := bug.sanitizeAccess(accessLevel(c, r)) + bugLevel := bug.sanitizeAccess(c, accessLevel(c, r)) return checkAccessLevel(c, r, bugLevel) } -func (bug *Bug) sanitizeAccess(currentLevel AccessLevel) AccessLevel { +func (bug *Bug) sanitizeAccess(c context.Context, currentLevel AccessLevel) AccessLevel { + config := getConfig(c) for ri := len(bug.Reporting) - 1; ri >= 0; ri-- { bugReporting := &bug.Reporting[ri] if ri == 0 || !bugReporting.Reported.IsZero() { diff --git a/dashboard/app/access_test.go b/dashboard/app/access_test.go index 7bd412acf..1247a8e36 100644 --- a/dashboard/app/access_test.go +++ b/dashboard/app/access_test.go @@ -12,11 +12,13 @@ import ( "testing" "github.com/google/syzkaller/dashboard/dashapi" + "golang.org/x/net/context" "google.golang.org/appengine/v2/user" ) // TestAccessConfig checks that access level were properly assigned throughout the config. func TestAccessConfig(t *testing.T) { + config := getConfig(context.Background()) tests := []struct { what string want AccessLevel @@ -162,7 +164,7 @@ func TestAccess(t *testing.T) { c.expectOK(err) crash, _, err := findCrashForBug(c.ctx, bug) c.expectOK(err) - bugID := bug.keyHash() + bugID := bug.keyHash(c.ctx) entities = append(entities, []entity{ { level: level, @@ -242,7 +244,7 @@ func TestAccess(t *testing.T) { build, err := loadBuild(c.ctx, ns, buildID) c.expectOK(err) entities = append(entities, entity{ - level: config.Namespaces[ns].AccessLevel, + level: c.config().Namespaces[ns].AccessLevel, ref: build.ID, url: fmt.Sprintf("/text?tag=KernelConfig&id=%v", build.KernelConfig), }) @@ -268,10 +270,10 @@ func TestAccess(t *testing.T) { // duplicate/similar cross-references. for _, ns := range []string{"access-admin", "access-user", "access-public"} { clientName, clientKey := "", "" - for k, v := range config.Namespaces[ns].Clients { + for k, v := range c.config().Namespaces[ns].Clients { clientName, clientKey = k, v } - nsLevel := config.Namespaces[ns].AccessLevel + nsLevel := c.config().Namespaces[ns].AccessLevel namespaceAccessPrefix := accessLevelPrefix(nsLevel) client := c.makeClient(clientName, clientKey, true) build := testBuild(1) @@ -280,7 +282,7 @@ func TestAccess(t *testing.T) { noteBuildAccessLevel(ns, build.ID) for reportingIdx := 0; reportingIdx < 2; reportingIdx++ { - accessLevel := config.Namespaces[ns].Reporting[reportingIdx].AccessLevel + accessLevel := c.config().Namespaces[ns].Reporting[reportingIdx].AccessLevel accessPrefix := accessLevelPrefix(accessLevel) crashInvalid := testCrashWithRepro(build, reportingIdx*10+0) @@ -292,8 +294,8 @@ func TestAccess(t *testing.T) { } client.updateBug(repInvalid.ID, dashapi.BugStatusInvalid, "") // Invalid bugs become visible up to the last reporting. - finalLevel := config.Namespaces[ns]. - Reporting[len(config.Namespaces[ns].Reporting)-1].AccessLevel + finalLevel := c.config().Namespaces[ns]. + Reporting[len(c.config().Namespaces[ns].Reporting)-1].AccessLevel noteBugAccessLevel(repInvalid.ID, finalLevel, nsLevel) crashFixed := testCrashWithRepro(build, reportingIdx*10+0) diff --git a/dashboard/app/admin.go b/dashboard/app/admin.go index 56c3d8bd5..767b4a627 100644 --- a/dashboard/app/admin.go +++ b/dashboard/app/admin.go @@ -213,7 +213,7 @@ func updateBugReporting(c context.Context, w http.ResponseWriter, r *http.Reques return err } log.Warningf(c, "fetched %v bugs for namespce %v", len(bugs), ns) - cfg := config.Namespaces[ns] + cfg := getConfig(c).Namespaces[ns] var update []*db.Key for i, bug := range bugs { if len(bug.Reporting) >= len(cfg.Reporting) { @@ -222,7 +222,7 @@ func updateBugReporting(c context.Context, w http.ResponseWriter, r *http.Reques update = append(update, keys[i]) } return updateBatch(c, update, func(_ *db.Key, bug *Bug) { - err := bug.updateReportings(cfg, timeNow(c)) + err := bug.updateReportings(c, cfg, timeNow(c)) if err != nil { panic(err) } @@ -364,8 +364,8 @@ func updateHeadReproLevel(c context.Context, w http.ResponseWriter, r *http.Requ } } if actual != bug.HeadReproLevel { - fmt.Fprintf(w, "%v: HeadReproLevel mismatch, actual=%d db=%d\n", bugLink(bug.keyHash()), actual, bug.HeadReproLevel) - newLevels[bug.keyHash()] = actual + fmt.Fprintf(w, "%v: HeadReproLevel mismatch, actual=%d db=%d\n", bugLink(bug.keyHash(c)), actual, bug.HeadReproLevel) + newLevels[bug.keyHash(c)] = actual keys = append(keys, key) } return nil @@ -373,7 +373,7 @@ func updateHeadReproLevel(c context.Context, w http.ResponseWriter, r *http.Requ return err } return updateBatch(c, keys, func(_ *db.Key, bug *Bug) { - newLevel, ok := newLevels[bug.keyHash()] + newLevel, ok := newLevels[bug.keyHash(c)] if !ok { panic("fetched unknown bug") } diff --git a/dashboard/app/api.go b/dashboard/app/api.go index 7ce56c390..a9b220f2d 100644 --- a/dashboard/app/api.go +++ b/dashboard/app/api.go @@ -132,7 +132,7 @@ func handleAPI(c context.Context, r *http.Request) (reply interface{}, err error return nil, err } // Somewhat confusingly the "key" parameter is the password. - ns, err := checkClient(config, client, r.PostFormValue("key"), subj) + ns, err := checkClient(getConfig(c), client, r.PostFormValue("key"), subj) if err != nil { if client != "" { log.Errorf(c, "%v", err) @@ -220,7 +220,7 @@ loop: } func reportEmail(c context.Context, ns string) string { - for _, reporting := range config.Namespaces[ns].Reporting { + for _, reporting := range getConfig(c).Namespaces[ns].Reporting { if _, ok := reporting.Config.(*EmailConfig); ok { return ownEmail(c) } @@ -232,7 +232,7 @@ func apiCommitPoll(c context.Context, ns string, r *http.Request, payload []byte resp := &dashapi.CommitPollResp{ ReportEmail: reportEmail(c, ns), } - for _, repo := range getKernelRepos(c, ns) { + for _, repo := range getConfig(c).Namespaces[ns].Repos { if repo.NoPoll { continue } @@ -652,7 +652,7 @@ func managerList(c context.Context, ns string) ([]string, error) { if err != nil { return nil, fmt.Errorf("failed to query builds: %w", err) } - configManagers := config.Namespaces[ns].Managers + configManagers := getConfig(c).Namespaces[ns].Managers var managers []string for _, build := range builds { if configManagers[build.Manager].Decommissioned { @@ -681,9 +681,9 @@ func apiReportBuildError(c context.Context, ns string, r *http.Request, payload if err := updateManager(c, ns, req.Build.Manager, func(mgr *Manager, stats *ManagerStats) error { log.Infof(c, "failed build on %v: kernel=%v", req.Build.Manager, req.Build.KernelCommit) if req.Build.KernelCommit != "" { - mgr.FailedBuildBug = bug.keyHash() + mgr.FailedBuildBug = bug.keyHash(c) } else { - mgr.FailedSyzBuildBug = bug.keyHash() + mgr.FailedSyzBuildBug = bug.keyHash(c) } return nil }); err != nil { @@ -706,7 +706,7 @@ func apiReportCrash(c context.Context, ns string, r *http.Request, payload []byt if err != nil { return nil, err } - if !config.Namespaces[ns].TransformCrash(build, req) { + if !getConfig(c).Namespaces[ns].TransformCrash(build, req) { return new(dashapi.ReportCrashResp), nil } bug, err := reportCrash(c, build, req) @@ -856,7 +856,7 @@ func (crash *Crash) UpdateReportingPriority(c context.Context, build *Build, bug prio += 1e8 // prefer reporting crash that matches bug title } managerPrio := 0 - if _, mgrConfig := activeManager(crash.Manager, bug.Namespace); mgrConfig != nil { + if _, mgrConfig := activeManager(c, crash.Manager, bug.Namespace); mgrConfig != nil { managerPrio = mgrConfig.Priority } prio += int64((managerPrio - MinManagerPriority) * 1e5) @@ -1221,7 +1221,7 @@ func loadBugReport(c context.Context, bug *Bug) (*dashapi.BugReport, error) { } // Create report for the last reporting so that it's stable and ExtID does not change over time. bugReporting := &bug.Reporting[len(bug.Reporting)-1] - reporting := config.Namespaces[bug.Namespace].ReportingByName(bugReporting.Name) + reporting := getConfig(c).Namespaces[bug.Namespace].ReportingByName(bugReporting.Name) if reporting == nil { return nil, fmt.Errorf("reporting %v is missing in config", bugReporting.Name) } @@ -1382,7 +1382,7 @@ func createBugForCrash(c context.Context, ns string, req *dashapi.Crash) (*Bug, tx := func(c context.Context) error { for seq := int64(0); ; seq++ { bug = new(Bug) - bugHash := bugKeyHash(ns, req.Title, seq) + bugHash := bugKeyHash(c, ns, req.Title, seq) bugKey := db.NewKey(c, "Bug", bugHash, 0, nil) if err := db.Get(c, bugKey, bug); err != nil { if err != db.ErrNoSuchEntity { @@ -1403,7 +1403,7 @@ func createBugForCrash(c context.Context, ns string, req *dashapi.Crash) (*Bug, LastTime: now, SubsystemsTime: now, } - err = bug.updateReportings(config.Namespaces[ns], now) + err = bug.updateReportings(c, getConfig(c).Namespaces[ns], now) if err != nil { return err } @@ -1465,7 +1465,7 @@ func needReproForBug(c context.Context, bug *Bug) bool { bug.Title == suppressedReportTitle { return false } - if !config.Namespaces[bug.Namespace].NeedRepro(bug) { + if !getConfig(c).Namespaces[bug.Namespace].NeedRepro(bug) { return false } bestReproLevel := ReproLevelC @@ -1604,7 +1604,7 @@ func checkClient(conf *GlobalConfig, name0, secretPassword, oauthSubject string) func handleRefreshSubsystems(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) const updateBugsCount = 25 - for ns := range config.Namespaces { + for ns := range getConfig(c).Namespaces { err := reassignBugSubsystems(c, ns, updateBugsCount) if err != nil { log.Errorf(c, "failed to update subsystems for %s: %v", ns, err) diff --git a/dashboard/app/asset_storage.go b/dashboard/app/asset_storage.go index 3ce096799..64d23f1fa 100644 --- a/dashboard/app/asset_storage.go +++ b/dashboard/app/asset_storage.go @@ -136,7 +136,7 @@ func neededCrashURLs(c context.Context) ([]string, error) { func handleDeprecateAssets(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) - for ns := range config.Namespaces { + for ns := range getConfig(c).Namespaces { err := deprecateNamespaceAssets(c, ns) if err != nil { log.Errorf(c, "deprecateNamespaceAssets failed for ns=%v: %v", ns, err) diff --git a/dashboard/app/asset_storage_test.go b/dashboard/app/asset_storage_test.go index c42b0d651..80d71105d 100644 --- a/dashboard/app/asset_storage_test.go +++ b/dashboard/app/asset_storage_test.go @@ -66,7 +66,7 @@ func TestBuildAssetLifetime(t *testing.T) { crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log) kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig) c.expectEQ(sender, fromAddr(c.ctx)) - to := config.Namespaces["test2"].Reporting[0].Config.(*EmailConfig).Email + to := c.config().Namespaces["test2"].Reporting[0].Config.(*EmailConfig).Email c.expectEQ(msg.To, []string{to}) c.expectEQ(msg.Subject, crash.Title) c.expectEQ(len(msg.Attachments), 0) @@ -358,7 +358,7 @@ func TestCrashAssetLifetime(t *testing.T) { crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log) kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig) c.expectEQ(sender, fromAddr(c.ctx)) - to := config.Namespaces["test2"].Reporting[0].Config.(*EmailConfig).Email + to := c.config().Namespaces["test2"].Reporting[0].Config.(*EmailConfig).Email c.expectEQ(msg.To, []string{to}) c.expectEQ(msg.Subject, crash.Title) c.expectEQ(len(msg.Attachments), 0) diff --git a/dashboard/app/bisect_test.go b/dashboard/app/bisect_test.go index 504e7d396..5e10d5a7d 100644 --- a/dashboard/app/bisect_test.go +++ b/dashboard/app/bisect_test.go @@ -170,9 +170,9 @@ For information about bisection process see: https://goo.gl/tpsmEJ#bisection `, extBugID, bisectLogLink, bisectCrashReportLink, bisectCrashLogLink, kernelConfigLink, reproSyzLink, reproCLink)) syzRepro := []byte(fmt.Sprintf("# https://testapp.appspot.com/bug?id=%v\n%s#%s\n%s", - dbBug.keyHash(), syzReproPrefix, crash2.ReproOpts, crash2.ReproSyz)) + dbBug.keyHash(c.ctx), syzReproPrefix, crash2.ReproOpts, crash2.ReproSyz)) cRepro := []byte(fmt.Sprintf("// https://testapp.appspot.com/bug?id=%v\n%s", - dbBug.keyHash(), crash2.ReproC)) + dbBug.keyHash(c.ctx), crash2.ReproC)) c.checkURLContents(bisectLogLink, []byte("bisect log 2")) c.checkURLContents(bisectCrashReportLink, []byte("bisect crash report")) c.checkURLContents(bisectCrashLogLink, []byte("bisect crash log")) @@ -428,7 +428,7 @@ For information about bisection process see: https://goo.gl/tpsmEJ#bisection `, extBugID, bisectLogLink, bisectCrashReportLink, bisectCrashLogLink, kernelConfigLink, reproSyzLink, reproCLink)) syzRepro := []byte(fmt.Sprintf("# https://testapp.appspot.com/bug?id=%v\n%s#%s\n%s", - dbBug.keyHash(), syzReproPrefix, crash4.ReproOpts, crash4.ReproSyz)) + dbBug.keyHash(c.ctx), syzReproPrefix, crash4.ReproOpts, crash4.ReproSyz)) c.checkURLContents(bisectLogLink, []byte("bisectfix log 4")) c.checkURLContents(bisectCrashReportLink, []byte("bisectfix crash report 4")) c.checkURLContents(bisectCrashLogLink, []byte("bisectfix crash log 4")) @@ -1231,7 +1231,7 @@ func addBisectFixJob(c *Ctx, build *dashapi.Build) (*dashapi.JobPollResp, *dasha c.expectTrue(strings.Contains(msg.Body, "syzbot suspects this issue was fixed by commit:")) // Ensure we do not automatically close the bug. - c.expectTrue(!config.Namespaces["test2"].FixBisectionAutoClose) + c.expectTrue(!c.config().Namespaces["test2"].FixBisectionAutoClose) _, extBugID, err := email.RemoveAddrContext(msg.Sender) c.expectOK(err) dbBug, _, _ := c.loadBug(extBugID) diff --git a/dashboard/app/cache.go b/dashboard/app/cache.go index 236a7f1c3..b9ed0d54f 100644 --- a/dashboard/app/cache.go +++ b/dashboard/app/cache.go @@ -57,7 +57,7 @@ func cacheUpdate(w http.ResponseWriter, r *http.Request) { log.Errorf(c, "failed load backports: %v", err) return } - for ns := range config.Namespaces { + for ns := range getConfig(c).Namespaces { bugs, _, err := loadNamespaceBugs(c, ns) if err != nil { log.Errorf(c, "failed load ns=%v bugs: %v", ns, err) @@ -79,7 +79,7 @@ func buildAndStoreCached(c context.Context, bugs []*Bug, backports []*rawBackpor Subsystems: make(map[string]CachedBugStats), } for _, bug := range bugs { - if bug.Status == BugStatusOpen && accessLevel < bug.sanitizeAccess(accessLevel) { + if bug.Status == BugStatusOpen && accessLevel < bug.sanitizeAccess(c, accessLevel) { continue } v.Total.Record(bug) @@ -96,7 +96,7 @@ func buildAndStoreCached(c context.Context, bugs []*Bug, backports []*rawBackpor for _, backport := range backports { outgoing := stringInList(backport.FromNs, ns) for _, bug := range backport.Bugs { - if accessLevel < bug.sanitizeAccess(accessLevel) { + if accessLevel < bug.sanitizeAccess(c, accessLevel) { continue } if bug.Namespace == ns || outgoing { diff --git a/dashboard/app/config.go b/dashboard/app/config.go index 9715c632a..235e4e853 100644 --- a/dashboard/app/config.go +++ b/dashboard/app/config.go @@ -336,25 +336,40 @@ func (cfg *Config) ReportingByName(name string) *Reporting { return nil } -// config is installed either by tests or from mainConfig in main function -// (a separate file should install mainConfig in an init function). +// configDontUse holds the configuration object that is installed either by tests +// or from mainConfig in main function (a separate file should install mainConfig +// in an init function). +// Please access it via the getConfig(context.Context) method. var ( - config *GlobalConfig - mainConfig *GlobalConfig + configDontUse *GlobalConfig + mainConfig *GlobalConfig ) func installConfig(cfg *GlobalConfig) { checkConfig(cfg) - if config != nil { + if configDontUse != nil { panic("another config is already installed") } - config = cfg + configDontUse = cfg initEmailReporting() initHTTPHandlers() initAPIHandlers() initKcidb() } +var contextConfigKey = "Updated config (to be used during tests). Use only in tests!" + +func contextWithConfig(c context.Context, cfg *GlobalConfig) context.Context { + return context.WithValue(c, &contextConfigKey, cfg) +} + +func getConfig(c context.Context) *GlobalConfig { + if val, ok := c.Value(&contextConfigKey).(*GlobalConfig); ok { + return val + } + return configDontUse // The base config was not overwriten. +} + func checkConfig(cfg *GlobalConfig) { if cfg == nil { panic("installing nil config") @@ -690,34 +705,6 @@ func (cfg *Config) lastActiveReporting() int { return last } -var kernelReposKey = "Custom list of kernel repositories" - -func contextWithRepos(c context.Context, list []KernelRepo) context.Context { - return context.WithValue(c, &kernelReposKey, list) -} - -func getKernelRepos(c context.Context, ns string) []KernelRepo { - if val, ok := c.Value(&kernelReposKey).([]KernelRepo); ok { - return val - } - return config.Namespaces[ns].Repos -} - -var decommKey = "Custom decommissioned status" - -func contextWithDecommission(c context.Context, ns string, value bool) context.Context { - mm, _ := c.Value(&decommKey).(map[string]bool) - if mm == nil { - mm = map[string]bool{} - } - mm[ns] = value - return context.WithValue(c, &decommKey, mm) -} - func isDecommissioned(c context.Context, ns string) bool { - mm, _ := c.Value(&decommKey).(map[string]bool) - if val, set := mm[ns]; set { - return val - } - return config.Namespaces[ns].Decommissioned + return getConfig(c).Namespaces[ns].Decommissioned } diff --git a/dashboard/app/email_test.go b/dashboard/app/email_test.go index 5c587199e..b79bdcb16 100644 --- a/dashboard/app/email_test.go +++ b/dashboard/app/email_test.go @@ -13,6 +13,7 @@ import ( "github.com/google/syzkaller/pkg/email" "github.com/google/syzkaller/sys/targets" "github.com/stretchr/testify/assert" + "golang.org/x/net/context" ) // nolint: funlen @@ -42,7 +43,7 @@ func TestEmailReport(t *testing.T) { crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log) kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig) c.expectEQ(sender, fromAddr(c.ctx)) - to := config.Namespaces["test2"].Reporting[0].Config.(*EmailConfig).Email + to := c.config().Namespaces["test2"].Reporting[0].Config.(*EmailConfig).Email c.expectEQ(msg.To, []string{to}) c.expectEQ(msg.Subject, crash.Title) c.expectEQ(len(msg.Attachments), 0) @@ -146,7 +147,7 @@ For more options, visit https://groups.google.com/d/optout. crash.ReproOpts = []byte("repro opts") crash.ReproSyz = []byte("getpid()") syzRepro := []byte(fmt.Sprintf("# https://testapp.appspot.com/bug?id=%v\n%s#%s\n%s", - dbBug0.keyHash(), syzReproPrefix, crash.ReproOpts, crash.ReproSyz)) + dbBug0.keyHash(c.ctx), syzReproPrefix, crash.ReproOpts, crash.ReproSyz)) c.client2.ReportCrash(crash) { @@ -165,7 +166,7 @@ For more options, visit https://groups.google.com/d/optout. "bugs@syzkaller.com", // This is from incomingEmail. "default@sender.com", // This is from incomingEmail. "foo@bar.com", - config.Namespaces["test2"].Reporting[0].Config.(*EmailConfig).Email, + c.config().Namespaces["test2"].Reporting[0].Config.(*EmailConfig).Email, } c.expectEQ(msg.To, to) c.expectEQ(msg.Subject, "Re: "+crash.Title) @@ -295,7 +296,7 @@ Content-Type: text/plain crash.Maintainers = []string{"\"qux\" <qux@qux.com>"} c.client2.ReportCrash(crash) cRepro := []byte(fmt.Sprintf("// https://testapp.appspot.com/bug?id=%v\n%s", - dbBug0.keyHash(), crash.ReproC)) + dbBug0.keyHash(c.ctx), crash.ReproC)) { msg := c.pollEmailBug() @@ -943,7 +944,7 @@ func TestSubjectTitleParser(t *testing.T) { }, } - p := subjectTitleParser{} + p := makeSubjectTitleParser(context.Background()) for _, test := range tests { title, seq, err := p.parseTitle(test.inSubject) if test.outTitle == "" { @@ -990,7 +991,7 @@ func TestBugFromSubjectInference(t *testing.T) { origSender := upstreamCrash(client, build, crashTitle) upstreamCrash(client, build, "unrelated crash 2") - mailingList := "<" + config.Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email + ">" + mailingList := "<" + c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email + ">" // First try to ping some non-existing bug. subject := "Re: unknown-bug" @@ -1154,7 +1155,7 @@ func TestEmailSetInvalidSubsystems(t *testing.T) { defer c.Close() client := c.makeClient(clientPublicEmail, keyPublicEmail, true) - mailingList := config.Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email + mailingList := c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email build := testBuild(1) client.UploadBuild(build) @@ -1186,7 +1187,7 @@ func TestEmailSetSubsystems(t *testing.T) { defer c.Close() client := c.makeClient(clientPublicEmail, keyPublicEmail, true) - mailingList := config.Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email + mailingList := c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email build := testBuild(1) client.UploadBuild(build) @@ -1218,7 +1219,7 @@ func TestEmailBugLabels(t *testing.T) { defer c.Close() client := c.makeClient(clientPublicEmail, keyPublicEmail, true) - mailingList := config.Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email + mailingList := c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email build := testBuild(1) client.UploadBuild(build) @@ -1264,7 +1265,7 @@ func TestInvalidEmailBugLabels(t *testing.T) { defer c.Close() client := c.makeClient(clientPublicEmail, keyPublicEmail, true) - mailingList := config.Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email + mailingList := c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email build := testBuild(1) client.UploadBuild(build) diff --git a/dashboard/app/entities.go b/dashboard/app/entities.go index f4cc68e79..8310cb281 100644 --- a/dashboard/app/entities.go +++ b/dashboard/app/entities.go @@ -775,7 +775,7 @@ func loadAllManagers(c context.Context, ns string) ([]*Manager, []*db.Key, error var result []*Manager var resultKeys []*db.Key for i, mgr := range managers { - if config.Namespaces[mgr.Namespace].Managers[mgr.Name].Decommissioned { + if getConfig(c).Namespaces[mgr.Namespace].Managers[mgr.Name].Decommissioned { continue } result = append(result, mgr) @@ -849,28 +849,28 @@ func canonicalBug(c context.Context, bug *Bug) (*Bug, error) { bugKey := db.NewKey(c, "Bug", bug.DupOf, 0, nil) if err := db.Get(c, bugKey, canon); err != nil { return nil, fmt.Errorf("failed to get dup bug %q for %q: %w", - bug.DupOf, bug.keyHash(), err) + bug.DupOf, bug.keyHash(c), err) } bug = canon } } func (bug *Bug) key(c context.Context) *db.Key { - return db.NewKey(c, "Bug", bug.keyHash(), 0, nil) + return db.NewKey(c, "Bug", bug.keyHash(c), 0, nil) } -func (bug *Bug) keyHash() string { - return bugKeyHash(bug.Namespace, bug.Title, bug.Seq) +func (bug *Bug) keyHash(c context.Context) string { + return bugKeyHash(c, bug.Namespace, bug.Title, bug.Seq) } -func bugKeyHash(ns, title string, seq int64) string { - return hash.String([]byte(fmt.Sprintf("%v-%v-%v-%v", config.Namespaces[ns].Key, ns, title, seq))) +func bugKeyHash(c context.Context, ns, title string, seq int64) string { + return hash.String([]byte(fmt.Sprintf("%v-%v-%v-%v", getConfig(c).Namespaces[ns].Key, ns, title, seq))) } func loadSimilarBugs(c context.Context, bug *Bug) ([]*Bug, error) { - domain := config.Namespaces[bug.Namespace].SimilarityDomain + domain := getConfig(c).Namespaces[bug.Namespace].SimilarityDomain dedup := make(map[string]bool) - dedup[bug.keyHash()] = true + dedup[bug.keyHash(c)] = true ret := []*Bug{} for _, title := range bug.AltTitles { @@ -882,11 +882,11 @@ func loadSimilarBugs(c context.Context, bug *Bug) ([]*Bug, error) { return nil, err } for _, bug := range similar { - if config.Namespaces[bug.Namespace].SimilarityDomain != domain || - dedup[bug.keyHash()] { + if getConfig(c).Namespaces[bug.Namespace].SimilarityDomain != domain || + dedup[bug.keyHash(c)] { continue } - dedup[bug.keyHash()] = true + dedup[bug.keyHash(c)] = true ret = append(ret, bug) } } @@ -1001,7 +1001,7 @@ func kernelRepoInfo(c context.Context, build *Build) KernelRepo { func kernelRepoInfoRaw(c context.Context, ns, url, branch string) KernelRepo { var info KernelRepo - for _, repo := range getKernelRepos(c, ns) { + for _, repo := range getConfig(c).Namespaces[ns].Repos { if repo.URL == url && repo.Branch == branch { info = repo break diff --git a/dashboard/app/graphs.go b/dashboard/app/graphs.go index 74d982eb0..89f549d92 100644 --- a/dashboard/app/graphs.go +++ b/dashboard/app/graphs.go @@ -166,7 +166,7 @@ func loadGraphBugs(c context.Context, ns string) ([]*Bug, error) { } n := 0 fixes := make(map[string]bool) - lastReporting := config.Namespaces[ns].lastActiveReporting() + lastReporting := getConfig(c).Namespaces[ns].lastActiveReporting() for _, bug := range bugs { if bug.Reporting[lastReporting].Reported.IsZero() { if bug.Status == BugStatusOpen { @@ -293,7 +293,7 @@ func createBugLifetimes(c context.Context, bugs []*Bug, causeBisects map[string] } else { ui.NotFixed = 400 - float32(i%7) } - if job := causeBisects[bug.keyHash()]; job != nil { + if job := causeBisects[bug.keyHash(c)]; job != nil { days := float32(job.Commits[0].Date.Sub(ui.Reported)) / float32(24*time.Hour) if days < -365 { ui.Introduced1y = -365 - float32(i%7) @@ -523,7 +523,7 @@ func handleGraphCrashes(c context.Context, w http.ResponseWriter, r *http.Reques accessLevel := accessLevel(c, r) nbugs := 0 for _, bug := range bugs { - if accessLevel < bug.sanitizeAccess(accessLevel) { + if accessLevel < bug.sanitizeAccess(c, accessLevel) { continue } bugs[nbugs] = bug @@ -564,7 +564,7 @@ func createCrashesTable(c context.Context, ns string, days int, bugs []*Bug) *ui titleRegexp := regexp.QuoteMeta(bug.Title) table.Rows = append(table.Rows, &uiCrashSummary{ Title: bug.Title, - Link: bugLink(bug.keyHash()), + Link: bugLink(bug.keyHash(c)), GraphLink: "?show-graph=1&Months=1®exp=" + url.QueryEscape(titleRegexp), Count: count, }) diff --git a/dashboard/app/handler.go b/dashboard/app/handler.go index 6c059c0f4..c8929290d 100644 --- a/dashboard/app/handler.go +++ b/dashboard/app/handler.go @@ -105,7 +105,7 @@ func (ce *ErrClient) HTTPStatus() int { func handleAuth(fn contextHandler) contextHandler { return func(c context.Context, w http.ResponseWriter, r *http.Request) error { - if err := checkAccessLevel(c, r, config.AccessLevel); err != nil { + if err := checkAccessLevel(c, r, getConfig(c).AccessLevel); err != nil { return err } return fn(c, w, r) @@ -148,8 +148,8 @@ func commonHeaderRaw(c context.Context, r *http.Request) *uiHeader { h := &uiHeader{ Admin: accessLevel(c, r) == AccessAdmin, URLPath: r.URL.Path, - AnalyticsTrackingID: config.AnalyticsTrackingID, - ContactEmail: config.ContactEmail, + AnalyticsTrackingID: getConfig(c).AnalyticsTrackingID, + ContactEmail: getConfig(c).ContactEmail, } if user.Current(c) == nil { h.LoginLink, _ = user.LoginURL(c, r.URL.String()) @@ -172,7 +172,7 @@ func commonHeader(c context.Context, r *http.Request, w http.ResponseWriter, ns const adminPage = "admin" isAdminPage := r.URL.Path == "/"+adminPage found := false - for ns1, cfg := range config.Namespaces { + for ns1, cfg := range getConfig(c).Namespaces { if accessLevel < cfg.AccessLevel { if ns1 == ns { return nil, ErrAccess @@ -195,8 +195,8 @@ func commonHeader(c context.Context, r *http.Request, w http.ResponseWriter, ns }) cookie := decodeCookie(r) if !found { - ns = config.DefaultNamespace - if cfg := config.Namespaces[cookie.Namespace]; cfg != nil && cfg.AccessLevel <= accessLevel { + ns = getConfig(c).DefaultNamespace + if cfg := getConfig(c).Namespaces[cookie.Namespace]; cfg != nil && cfg.AccessLevel <= accessLevel { ns = cookie.Namespace } if accessLevel == AccessAdmin { diff --git a/dashboard/app/jobs.go b/dashboard/app/jobs.go index 187b5aae3..4a46f7387 100644 --- a/dashboard/app/jobs.go +++ b/dashboard/app/jobs.go @@ -44,7 +44,7 @@ type testReqArgs struct { func handleTestRequest(c context.Context, args *testReqArgs) error { log.Infof(c, "test request: bug=%s user=%q extID=%q patch=%v, repo=%q branch=%q", args.bug.Title, args.user, args.extID, len(args.patch), args.repo, args.branch) - for _, blocked := range config.EmailBlocklist { + for _, blocked := range getConfig(c).EmailBlocklist { if args.user == blocked { return &TestRequestDeniedError{ fmt.Sprintf("test request from blocked user: %v", args.user), @@ -104,7 +104,7 @@ func addTestJob(c context.Context, args *testJobArgs) (*Job, *db.Key, error) { if reason := checkTestJob(args); reason != "" { return nil, nil, &BadTestRequestError{reason} } - manager, mgrConfig := activeManager(args.crash.Manager, args.bug.Namespace) + manager, mgrConfig := activeManager(c, args.crash.Manager, args.bug.Namespace) if mgrConfig != nil && mgrConfig.RestrictedTestingRepo != "" && args.repo != mgrConfig.RestrictedTestingRepo { return nil, nil, &BadTestRequestError{mgrConfig.RestrictedTestingReason} @@ -413,7 +413,7 @@ func jobFromBugSample(c context.Context, managers map[string]dashapi.ManagerJobs continue } managersList = append(managersList, name) - managersList = append(managersList, decommissionedInto(name)...) + managersList = append(managersList, decommissionedInto(c, name)...) } managersList = unique(managersList) @@ -444,7 +444,7 @@ func jobFromBugSample(c context.Context, managers map[string]dashapi.ManagerJobs } r := rand.New(rand.NewSource(timeNow(c).UnixNano())) // Bugs often happen on multiple instances, so let's filter out duplicates. - allBugs, allBugKeys = uniqueBugs(allBugs, allBugKeys) + allBugs, allBugKeys = uniqueBugs(c, allBugs, allBugKeys) r.Shuffle(len(allBugs), func(i, j int) { allBugs[i], allBugs[j] = allBugs[j], allBugs[i] allBugKeys[i], allBugKeys[j] = allBugKeys[j], allBugKeys[i] @@ -480,7 +480,7 @@ func createTreeBisectionJobs(c context.Context, bugs []*Bug, bugKeys []*db.Key, } any := false for _, mgr := range bug.HappenedOn { - newMgr, _ := activeManager(mgr, bug.Namespace) + newMgr, _ := activeManager(c, mgr, bug.Namespace) any = any || managers[newMgr].BisectFix } if !any { @@ -505,7 +505,7 @@ func createTreeTestJobs(c context.Context, bugs []*Bug, bugKeys []*db.Key, takeBugs := 5 prio, next := []int{}, []int{} for i, bug := range bugs { - if !config.Namespaces[bug.Namespace].FindBugOriginTrees { + if !getConfig(c).Namespaces[bug.Namespace].FindBugOriginTrees { continue } if timeNow(c).Before(bug.TreeTests.NextPoll) { @@ -538,12 +538,12 @@ func createPatchRetestingJobs(c context.Context, bugs []*Bug, bugKeys []*db.Key, managers map[string]dashapi.ManagerJobs) (*Job, *db.Key, error) { takeBugs := 5 for i, bug := range bugs { - if !config.Namespaces[bug.Namespace].RetestRepros { + if !getConfig(c).Namespaces[bug.Namespace].RetestRepros { // Repro retesting is disabled for the namespace. continue } - if config.Obsoleting.ReproRetestPeriod == 0 || - timeNow(c).Sub(bug.LastTime) < config.Obsoleting.ReproRetestStart { + if getConfig(c).Obsoleting.ReproRetestPeriod == 0 || + timeNow(c).Sub(bug.LastTime) < getConfig(c).Obsoleting.ReproRetestStart { // Don't retest reproducers if crashes are still happening. continue } @@ -561,9 +561,9 @@ func createPatchRetestingJobs(c context.Context, bugs []*Bug, bugKeys []*db.Key, return nil, nil, nil } -func decommissionedInto(jobMgr string) []string { +func decommissionedInto(c context.Context, jobMgr string) []string { var ret []string - for _, nsConfig := range config.Namespaces { + for _, nsConfig := range getConfig(c).Namespaces { for name, mgr := range nsConfig.Managers { if mgr.DelegatedTo == jobMgr { ret = append(ret, name) @@ -591,7 +591,7 @@ func handleRetestForBug(c context.Context, bug *Bug, bugKey *db.Key, if crash.ReproSyz == 0 && crash.ReproC == 0 { continue } - if now.Sub(crash.LastReproRetest) < config.Obsoleting.ReproRetestPeriod { + if now.Sub(crash.LastReproRetest) < getConfig(c).Obsoleting.ReproRetestPeriod { continue } if crash.ReproIsRevoked { @@ -599,7 +599,7 @@ func handleRetestForBug(c context.Context, bug *Bug, bugKey *db.Key, continue } // We could have decommissioned the original manager since then. - manager, _ := activeManager(crash.Manager, bug.Namespace) + manager, _ := activeManager(c, crash.Manager, bug.Namespace) if manager == "" || !managers[manager].TestPatches { continue } @@ -747,7 +747,7 @@ func bisectCrashForBug(c context.Context, bug *Bug, bugKey *db.Key, managers map continue } if jobType == JobBisectFix && - config.Namespaces[bug.Namespace].Managers[crash.Manager].FixBisectionDisabled { + getConfig(c).Namespaces[bug.Namespace].Managers[crash.Manager].FixBisectionDisabled { continue } return crash, crashKeys[ci], nil @@ -1171,7 +1171,7 @@ func updateBugBisection(c context.Context, job *Job, jobKey *db.Key, req *dashap if job.Type == JobBisectCause && infraError { bug.BisectCause = BisectNot } - _, bugReporting, _, _, _ := currentReporting(bug) + _, bugReporting, _, _, _ := currentReporting(c, bug) // The bug is either already closed or not yet reported in the current reporting, // either way we don't need to report it. If it wasn't reported, it will be reported // with the bisection results. @@ -1210,7 +1210,7 @@ func pollCompletedJobs(c context.Context, typ string) ([]*dashapi.BugReport, err // In some cases (e.g. repro retesting), it's ok not to have a reporting. continue } - reporting := config.Namespaces[job.Namespace].ReportingByName(job.Reporting) + reporting := getConfig(c).Namespaces[job.Namespace].ReportingByName(job.Reporting) if reporting.Config.Type() != typ { continue } @@ -1317,7 +1317,7 @@ func createBugReportForJob(c context.Context, job *Job, jobKey *db.Key, config i } rep.Maintainers = append(rep.Maintainers, emails...) } - if mgr := bug.managerConfig(); mgr != nil { + if mgr := bug.managerConfig(c); mgr != nil { rep.CC = append(rep.CC, mgr.CC.Always...) if job.Type == JobBisectCause || job.Type == JobBisectFix { rep.Maintainers = append(rep.Maintainers, mgr.CC.Maintainers...) @@ -1377,7 +1377,7 @@ func jobReported(c context.Context, jobID string) error { // Auto-mark the bug as fixed by the result of fix bisection, // if the setting is enabled for the namespace. if job.Type == JobBisectFix && - config.Namespaces[job.Namespace].FixBisectionAutoClose && + getConfig(c).Namespaces[job.Namespace].FixBisectionAutoClose && !job.IsCrossTree() && len(job.Commits) == 1 { bug := new(Bug) @@ -1486,8 +1486,8 @@ func loadPendingJob(c context.Context, managers map[string]dashapi.ManagerJobs) // activeManager determines the manager currently responsible for all bugs found by // the specified manager. -func activeManager(manager, ns string) (string, *ConfigManager) { - nsConfig := config.Namespaces[ns] +func activeManager(c context.Context, manager, ns string) (string, *ConfigManager) { + nsConfig := getConfig(c).Namespaces[ns] if mgr, ok := nsConfig.Managers[manager]; ok { if mgr.Decommissioned { newMgr := nsConfig.Managers[mgr.DelegatedTo] @@ -1599,13 +1599,13 @@ func makeJobInfo(c context.Context, job *Job, jobKey *db.Key, bug *Bug, build *B return info } -func uniqueBugs(inBugs []*Bug, inKeys []*db.Key) ([]*Bug, []*db.Key) { +func uniqueBugs(c context.Context, inBugs []*Bug, inKeys []*db.Key) ([]*Bug, []*db.Key) { var bugs []*Bug var keys []*db.Key dups := map[string]bool{} for i, bug := range inBugs { - hash := bug.keyHash() + hash := bug.keyHash(c) if dups[hash] { continue } diff --git a/dashboard/app/jobs_test.go b/dashboard/app/jobs_test.go index 3327d46db..d2e81de75 100644 --- a/dashboard/app/jobs_test.go +++ b/dashboard/app/jobs_test.go @@ -41,7 +41,7 @@ func TestJob(t *testing.T) { sender = c.pollEmailBug().Sender _, extBugID, err := email.RemoveAddrContext(sender) c.expectOK(err) - mailingList := config.Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email + mailingList := c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email c.incomingEmail(sender, "bla-bla-bla", EmailOptFrom("maintainer@kernel.org"), EmailOptCC([]string{mailingList, "kernel@mailing.list"})) @@ -293,7 +293,7 @@ func TestBootErrorPatch(t *testing.T) { sender := c.pollEmailBug().Sender c.incomingEmail(sender, "#syz upstream\n") sender = c.pollEmailBug().Sender - mailingList := config.Namespaces["test2"].Reporting[1].Config.(*EmailConfig).Email + mailingList := c.config().Namespaces["test2"].Reporting[1].Config.(*EmailConfig).Email c.incomingEmail(sender, "#syz test: git://git.git/git.git kernel-branch\n"+sampleGitPatch, EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList})) @@ -318,7 +318,7 @@ func TestTestErrorPatch(t *testing.T) { sender := c.pollEmailBug().Sender c.incomingEmail(sender, "#syz upstream\n") sender = c.pollEmailBug().Sender - mailingList := config.Namespaces["test2"].Reporting[1].Config.(*EmailConfig).Email + mailingList := c.config().Namespaces["test2"].Reporting[1].Config.(*EmailConfig).Email c.incomingEmail(sender, "#syz test: git://git.git/git.git kernel-branch\n"+sampleGitPatch, EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList})) @@ -431,7 +431,7 @@ func TestReproRetestJob(t *testing.T) { c.expectEQ(bug.ReproLevel, ReproLevelC) // Let's say that the C repro testing has failed. - c.advanceTime(config.Obsoleting.ReproRetestStart + time.Hour) + c.advanceTime(c.config().Obsoleting.ReproRetestStart + time.Hour) for i := 0; i < 2; i++ { resp := client.pollSpecificJobs(build.Manager, dashapi.ManagerJobs{TestPatches: true}) c.expectEQ(resp.Type, dashapi.JobTestPatch) @@ -461,7 +461,7 @@ func TestReproRetestJob(t *testing.T) { bug, _, _ = c.loadBug(extBugID) c.expectEQ(bug.HeadReproLevel, ReproLevelSyz) // Let's also deprecate the syz repro. - c.advanceTime(config.Obsoleting.ReproRetestPeriod + time.Hour) + c.advanceTime(c.config().Obsoleting.ReproRetestPeriod + time.Hour) resp := client.pollSpecificJobs(build.Manager, dashapi.ManagerJobs{TestPatches: true}) c.expectEQ(resp.Type, dashapi.JobTestPatch) @@ -510,10 +510,7 @@ func TestDelegatedManagerReproRetest(t *testing.T) { c.expectOK(err) // Deprecate the oldManager. - mgrConfig := config.Namespaces["test-mgr-decommission"].Managers[oldManager] - mgrConfig.Decommissioned = true - mgrConfig.DelegatedTo = newManager - config.Namespaces["test-mgr-decommission"].Managers[oldManager] = mgrConfig + c.decommissionManager("test-mgr-decommission", oldManager, newManager) // Upload a build for the new manager. c.advanceTime(time.Minute) @@ -531,7 +528,7 @@ func TestDelegatedManagerReproRetest(t *testing.T) { c.pollEmailBug() // Let's say that the C repro testing has failed. - c.advanceTime(config.Obsoleting.ReproRetestPeriod + time.Hour) + c.advanceTime(c.config().Obsoleting.ReproRetestPeriod + time.Hour) resp := client.pollSpecificJobs(build.Manager, dashapi.ManagerJobs{TestPatches: true}) c.expectEQ(resp.Type, dashapi.JobTestPatch) @@ -1252,7 +1249,7 @@ func TestEmailTestCommandNoArgs(t *testing.T) { client.ReportCrash(crash) sender := c.pollEmailBug().Sender - mailingList := config.Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email + mailingList := c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email c.incomingEmail(sender, "#syz test\n"+sampleGitPatch, EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList})) diff --git a/dashboard/app/kcidb.go b/dashboard/app/kcidb.go index b228e771c..19b704882 100644 --- a/dashboard/app/kcidb.go +++ b/dashboard/app/kcidb.go @@ -21,7 +21,7 @@ func initKcidb() { func handleKcidbPoll(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) - for ns, cfg := range config.Namespaces { + for ns, cfg := range getConfig(c).Namespaces { if cfg.Kcidb == nil { continue } @@ -60,7 +60,7 @@ func handleKcidbNamespce(c context.Context, ns string, cfg *KcidbConfig) error { func publishKcidbBug(c context.Context, client *kcidb.Client, bug *Bug, bugKey *db.Key) (bool, error) { if bug.KcidbStatus != 0 || - bug.sanitizeAccess(AccessPublic) > AccessPublic || + bug.sanitizeAccess(c, AccessPublic) > AccessPublic || bug.Reporting[len(bug.Reporting)-1].Reported.IsZero() || bug.Status != BugStatusOpen && timeSince(c, bug.LastTime) > 7*24*time.Hour { return false, nil diff --git a/dashboard/app/label.go b/dashboard/app/label.go index 01d360755..f25ff4c5e 100644 --- a/dashboard/app/label.go +++ b/dashboard/app/label.go @@ -52,7 +52,7 @@ func makeLabelSet(c context.Context, ns string) *labelSet { } originLabels := []string{} - for _, repo := range getKernelRepos(c, ns) { + for _, repo := range getConfig(c).Namespaces[ns].Repos { if repo.LabelIntroduced != "" { originLabels = append(originLabels, repo.LabelIntroduced) } diff --git a/dashboard/app/main.go b/dashboard/app/main.go index 2a4baa69b..748c76f2c 100644 --- a/dashboard/app/main.go +++ b/dashboard/app/main.go @@ -54,7 +54,7 @@ func initHTTPHandlers() { http.Handle("/x/bisect.txt", handlerWrapper(handleTextX(textLog))) http.Handle("/x/error.txt", handlerWrapper(handleTextX(textError))) http.Handle("/x/minfo.txt", handlerWrapper(handleTextX(textMachineInfo))) - for ns := range config.Namespaces { + for ns := range getConfig(context.Background()).Namespaces { http.Handle("/"+ns, handlerWrapper(handleMain)) http.Handle("/"+ns+"/fixed", handlerWrapper(handleFixed)) http.Handle("/"+ns+"/invalid", handlerWrapper(handleInvalid)) @@ -500,7 +500,7 @@ func handleMain(c context.Context, w http.ResponseWriter, r *http.Request) error return err } for _, group := range groups { - if config.Namespaces[hdr.Namespace].DisplayDiscussions { + if getConfig(c).Namespaces[hdr.Namespace].DisplayDiscussions { group.DispDiscuss = true } else { group.DispLastAct = true @@ -553,7 +553,7 @@ func handleSubsystemPage(c context.Context, w http.ResponseWriter, r *http.Reque var subsystem *subsystem.Subsystem if pos := strings.Index(r.URL.Path, "/s/"); pos != -1 { name := r.URL.Path[pos+3:] - if newName := config.Namespaces[hdr.Namespace].Subsystems.Redirect[name]; newName != "" { + if newName := getConfig(c).Namespaces[hdr.Namespace].Subsystems.Redirect[name]; newName != "" { http.Redirect(w, r, r.URL.Path[:pos+3]+newName, http.StatusMovedPermanently) return nil } @@ -575,7 +575,7 @@ func handleSubsystemPage(c context.Context, w http.ResponseWriter, r *http.Reque return err } for _, group := range groups { - group.DispDiscuss = config.Namespaces[hdr.Namespace].DisplayDiscussions + group.DispDiscuss = getConfig(c).Namespaces[hdr.Namespace].DisplayDiscussions } cached, err := CacheGet(c, r, hdr.Namespace) if err != nil { @@ -622,7 +622,7 @@ func handleBackports(c context.Context, w http.ResponseWriter, r *http.Request) } incoming := false for _, bug := range backport.Bugs { - if accessLevel < bug.sanitizeAccess(accessLevel) { + if accessLevel < bug.sanitizeAccess(c, accessLevel) { continue } if !outgoing && bug.Namespace != hdr.Namespace { @@ -680,7 +680,7 @@ func handleBackports(c context.Context, w http.ResponseWriter, r *http.Request) Header: hdr, Groups: groups, DisplayNamespace: func(ns string) string { - return config.Namespaces[ns].DisplayTitle + return getConfig(c).Namespaces[ns].DisplayTitle }, }) } @@ -743,7 +743,7 @@ func loadAllBackports(c context.Context) ([]*rawBackport, error) { if backport == nil { backport = &rawBackport{ From: from, - FromNs: namespacesForRepo(from.URL, from.Branch), + FromNs: namespacesForRepo(c, from.URL, from.Branch), To: to, Commit: commit} ret = append(ret, backport) @@ -754,9 +754,9 @@ func loadAllBackports(c context.Context) ([]*rawBackport, error) { return ret, nil } -func namespacesForRepo(url, branch string) []string { +func namespacesForRepo(c context.Context, url, branch string) []string { var ret []string - for ns, cfg := range config.Namespaces { + for ns, cfg := range getConfig(c).Namespaces { has := false for _, repo := range cfg.Repos { if repo.NoPoll { @@ -943,7 +943,7 @@ func handleBug(c context.Context, w http.ResponseWriter, r *http.Request) error return fmt.Errorf("%w: %w", ErrClientNotFound, err) } accessLevel := accessLevel(c, r) - if err := checkAccessLevel(c, r, bug.sanitizeAccess(accessLevel)); err != nil { + if err := checkAccessLevel(c, r, bug.sanitizeAccess(c, accessLevel)); err != nil { return err } if r.FormValue("debug_subsystems") != "" && accessLevel == AccessAdmin { @@ -967,7 +967,7 @@ func handleBug(c context.Context, w http.ResponseWriter, r *http.Request) error if err := db.Get(c, db.NewKey(c, "Bug", bug.DupOf, 0, nil), dup); err != nil { return err } - if accessLevel >= dup.sanitizeAccess(accessLevel) { + if accessLevel >= dup.sanitizeAccess(c, accessLevel) { sections = append(sections, &uiCollapsible{ Title: "Duplicate of", Show: true, @@ -1030,7 +1030,7 @@ func handleBug(c context.Context, w http.ResponseWriter, r *http.Request) error if len(similar.Bugs) > 0 { sections = append(sections, &uiCollapsible{ Title: fmt.Sprintf("Similar bugs (%d)", len(similar.Bugs)), - Show: config.Namespaces[hdr.Namespace].AccessLevel != AccessPublic, + Show: getConfig(c).Namespaces[hdr.Namespace].AccessLevel != AccessPublic, Type: sectionBugList, Value: similar, }) @@ -1415,7 +1415,7 @@ func handleTextImpl(c context.Context, w http.ResponseWriter, r *http.Request, t } return err } - if err := checkAccessLevel(c, r, config.Namespaces[ns].AccessLevel); err != nil { + if err := checkAccessLevel(c, r, getConfig(c).Namespaces[ns].AccessLevel); err != nil { return err } w.Header().Set("Content-Type", "text/plain; charset=utf-8") @@ -1435,7 +1435,7 @@ func augmentRepro(c context.Context, w http.ResponseWriter, tag string, bug *Bug if tag == textReproC { prefix = "//" } - fmt.Fprintf(w, "%v %v/bug?id=%v\n", prefix, appURL(c), bug.keyHash()) + fmt.Fprintf(w, "%v %v/bug?id=%v\n", prefix, appURL(c), bug.keyHash(c)) } } if tag == textReproSyz { @@ -1519,7 +1519,7 @@ func fetchNamespaceBugs(c context.Context, accessLevel AccessLevel, ns string, bugMap := make(map[string]*uiBug) var dups []*Bug for _, bug := range bugs { - if accessLevel < bug.sanitizeAccess(accessLevel) { + if accessLevel < bug.sanitizeAccess(c, accessLevel) { continue } if bug.Status == BugStatusDup { @@ -1534,7 +1534,7 @@ func fetchNamespaceBugs(c context.Context, accessLevel AccessLevel, ns string, // Don't show "fix pending" bugs on the main page. continue } - bugMap[bug.keyHash()] = uiBug + bugMap[bug.keyHash(c)] = uiBug id := uiBug.ReportingIndex groups[id] = append(groups[id], uiBug) } @@ -1545,7 +1545,7 @@ func fetchNamespaceBugs(c context.Context, accessLevel AccessLevel, ns string, } mergeUIBug(c, bug, dup) } - cfg := config.Namespaces[ns] + cfg := getConfig(c).Namespaces[ns] var uiGroups []*uiBugGroup for index, bugs := range groups { sort.Slice(bugs, func(i, j int) bool { @@ -1658,7 +1658,7 @@ func fetchTerminalBugs(c context.Context, accessLevel AccessLevel, Namespace: ns, } for _, bug := range bugs { - if accessLevel < bug.sanitizeAccess(accessLevel) { + if accessLevel < bug.sanitizeAccess(c, accessLevel) { continue } if !typ.Filter.MatchBug(bug) { @@ -1686,7 +1686,7 @@ func applyBugFilter(query *db.Query, filter *userBugFilter) *db.Query { func loadDupsForBug(c context.Context, r *http.Request, bug *Bug, state *ReportingState, managers []string) ( *uiBugGroup, error) { - bugHash := bug.keyHash() + bugHash := bug.keyHash(c) var dups []*Bug _, err := db.NewQuery("Bug"). Filter("Status=", BugStatusDup). @@ -1698,7 +1698,7 @@ func loadDupsForBug(c context.Context, r *http.Request, bug *Bug, state *Reporti var results []*uiBug accessLevel := accessLevel(c, r) for _, dup := range dups { - if accessLevel < dup.sanitizeAccess(accessLevel) { + if accessLevel < dup.sanitizeAccess(c, accessLevel) { continue } results = append(results, createUIBug(c, dup, state, managers)) @@ -1722,7 +1722,7 @@ func loadSimilarBugsUI(c context.Context, r *http.Request, bug *Bug, state *Repo } var results []*uiBug for _, similar := range similarBugs { - if accessLevel < similar.sanitizeAccess(accessLevel) { + if accessLevel < similar.sanitizeAccess(c, accessLevel) { continue } if managers[similar.Namespace] == nil { @@ -1820,13 +1820,13 @@ func createUIBug(c context.Context, bug *Bug, state *ReportingState, managers [] ReproLevel: bug.ReproLevel, ReportingIndex: reportingIdx, Status: status, - Link: bugExtLink(bug), + Link: bugExtLink(c, bug), ExternalLink: link, CreditEmail: creditEmail, NumManagers: len(managers), LastActivity: bug.LastActivity, Discussions: bug.discussionSummary(), - ID: bug.keyHash(), + ID: bug.keyHash(c), } for _, entry := range bug.Labels { uiBug.Labels = append(uiBug.Labels, makeBugLabelUI(c, bug, entry)) @@ -1834,7 +1834,7 @@ func createUIBug(c context.Context, bug *Bug, state *ReportingState, managers [] updateBugBadness(c, uiBug) if len(bug.Commits) != 0 { for i, com := range bug.Commits { - cfg := config.Namespaces[bug.Namespace] + cfg := getConfig(c).Namespaces[bug.Namespace] info := bug.getCommitInfo(i) uiBug.Commits = append(uiBug.Commits, &uiCommit{ Hash: info.Hash, @@ -2087,8 +2087,8 @@ func loadManagers(c context.Context, accessLevel AccessLevel, ns string, filter coverURL := "" if asset, ok := coverAssets[mgr.Name]; ok { coverURL = asset.DownloadURL - } else if config.CoverPath != "" { - coverURL = config.CoverPath + mgr.Name + ".html" + } else if getConfig(c).CoverPath != "" { + coverURL = getConfig(c).CoverPath + mgr.Name + ".html" } ui := &uiManager{ Now: timeNow(c), @@ -2128,7 +2128,7 @@ func loadManagerList(c context.Context, accessLevel AccessLevel, ns string, var filtered []*Manager var filteredKeys []*db.Key for i, mgr := range managers { - cfg := config.Namespaces[mgr.Namespace] + cfg := getConfig(c).Namespaces[mgr.Namespace] if accessLevel < cfg.AccessLevel { continue } @@ -2388,10 +2388,10 @@ func (b *bugJobs) uiBestFixCandidate(c context.Context) (*uiJob, error) { // bugExtLink should be preferred to bugLink since it provides a URL that's more consistent with // links from email addresses. -func bugExtLink(bug *Bug) string { - _, bugReporting, _, _, _ := currentReporting(bug) +func bugExtLink(c context.Context, bug *Bug) string { + _, bugReporting, _, _, _ := currentReporting(c, bug) if bugReporting == nil || bugReporting.ID == "" { - return bugLink(bug.keyHash()) + return bugLink(bug.keyHash(c)) } return "/bug?extid=" + bugReporting.ID } diff --git a/dashboard/app/main_test.go b/dashboard/app/main_test.go index 410dd43d4..84cf888dc 100644 --- a/dashboard/app/main_test.go +++ b/dashboard/app/main_test.go @@ -256,7 +256,7 @@ func TestMultiLabelFilter(t *testing.T) { defer c.Close() client := c.makeClient(clientPublicEmail, keyPublicEmail, true) - mailingList := config.Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email + mailingList := c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email build1 := testBuild(1) build1.Manager = "manager-name-123" diff --git a/dashboard/app/notifications_test.go b/dashboard/app/notifications_test.go index 0aceb9915..c9093c5e2 100644 --- a/dashboard/app/notifications_test.go +++ b/dashboard/app/notifications_test.go @@ -12,6 +12,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/syzkaller/dashboard/dashapi" "github.com/google/syzkaller/pkg/email" + "golang.org/x/net/context" ) func TestEmailNotifUpstreamEmbargo(t *testing.T) { @@ -214,9 +215,10 @@ func TestBugObsoleting(t *testing.T) { period: 80 * day, }, } + c := context.Background() for i, test := range tests { test.bug.Namespace = "test1" - got := test.bug.obsoletePeriod() + got := test.bug.obsoletePeriod(c) if got != test.period { t.Errorf("test #%v: got: %.2f, want %.2f", i, float64(got/time.Hour)/24, float64(test.period/time.Hour)/24) diff --git a/dashboard/app/reporting.go b/dashboard/app/reporting.go index 27b1fed0b..7c9607249 100644 --- a/dashboard/app/reporting.go +++ b/dashboard/app/reporting.go @@ -96,7 +96,7 @@ func handleReportBug(c context.Context, typ string, state *ReportingState, bug * func needReport(c context.Context, typ string, state *ReportingState, bug *Bug) ( reporting *Reporting, bugReporting *BugReporting, reportingIdx int, status, link string, err error) { - reporting, bugReporting, reportingIdx, status, err = currentReporting(bug) + reporting, bugReporting, reportingIdx, status, err = currentReporting(c, bug) if err != nil || reporting == nil { return } @@ -114,7 +114,7 @@ func needReport(c context.Context, typ string, state *ReportingState, bug *Bug) return } ent := state.getEntry(timeNow(c), bug.Namespace, reporting.Name) - cfg := config.Namespaces[bug.Namespace] + cfg := getConfig(c).Namespaces[bug.Namespace] if timeSince(c, bug.FirstTime) < cfg.ReportingDelay { status = fmt.Sprintf("%v: initial reporting delay", reporting.DisplayTitle) reporting, bugReporting = nil, nil @@ -187,7 +187,7 @@ func reportingPollNotifications(c context.Context, typ string) []*dashapi.BugNot } func handleReportNotif(c context.Context, typ string, bug *Bug) (*dashapi.BugNotification, error) { - reporting, bugReporting, _, _, err := currentReporting(bug) + reporting, bugReporting, _, _, err := currentReporting(c, bug) if err != nil || reporting == nil { return nil, nil } @@ -239,7 +239,7 @@ var notificationGenerators = []func(context.Context, *Bug, *Reporting, if len(bug.Commits) == 0 && bug.canBeObsoleted(c) && timeSince(c, bug.LastActivity) > notifyResendPeriod && - timeSince(c, bug.LastTime) > bug.obsoletePeriod() { + timeSince(c, bug.LastTime) > bug.obsoletePeriod(c) { log.Infof(c, "%v: obsoleting: %v", bug.Namespace, bug.Title) why := bugObsoletionReason(bug) return createNotification(c, dashapi.BugNotifObsoleted, false, string(why), bug, reporting, bugReporting) @@ -293,7 +293,7 @@ func createLabelNotification(c context.Context, label BugLabel, bug *Bug, report var err error notif.TreeJobs, err = treeTestJobs(c, bug) if err != nil { - log.Errorf(c, "failed to extract jobs for %s: %v", bug.keyHash(), err) + log.Errorf(c, "failed to extract jobs for %s: %v", bug.keyHash(c), err) return nil, fmt.Errorf("failed to fetch jobs: %w", err) } } @@ -331,7 +331,7 @@ func (bug *Bug) canBeObsoleted(c context.Context) bool { return true } if obsoleteWhatWontBeFixBisected { - cfg := config.Namespaces[bug.Namespace] + cfg := getConfig(c).Namespaces[bug.Namespace] for _, mgr := range bug.HappenedOn { if !cfg.Managers[mgr].FixBisectionDisabled { return false @@ -342,8 +342,9 @@ func (bug *Bug) canBeObsoleted(c context.Context) bool { return false } -func (bug *Bug) obsoletePeriod() time.Duration { +func (bug *Bug) obsoletePeriod(c context.Context) time.Duration { period := never + config := getConfig(c) if config.Obsoleting.MinPeriod == 0 { return period } @@ -371,7 +372,7 @@ func (bug *Bug) obsoletePeriod() time.Duration { bug.Reporting[len(bug.Reporting)-1].Reported.IsZero() { min, max = config.Obsoleting.NonFinalMinPeriod, config.Obsoleting.NonFinalMaxPeriod } - if mgr := bug.managerConfig(); mgr != nil && mgr.ObsoletingMinPeriod != 0 { + if mgr := bug.managerConfig(c); mgr != nil && mgr.ObsoletingMinPeriod != 0 { min, max = mgr.ObsoletingMinPeriod, mgr.ObsoletingMaxPeriod } if period < min { @@ -383,11 +384,11 @@ func (bug *Bug) obsoletePeriod() time.Duration { return period } -func (bug *Bug) managerConfig() *ConfigManager { +func (bug *Bug) managerConfig(c context.Context) *ConfigManager { if len(bug.HappenedOn) != 1 { return nil } - mgr := config.Namespaces[bug.Namespace].Managers[bug.HappenedOn[0]] + mgr := getConfig(c).Namespaces[bug.Namespace].Managers[bug.HappenedOn[0]] return &mgr } @@ -424,7 +425,7 @@ func createNotification(c context.Context, typ dashapi.BugNotif, public bool, te if (public || reporting.moderation) && bugReporting.CC != "" { notif.CC = append(notif.CC, strings.Split(bugReporting.CC, "|")...) } - if mgr := bug.managerConfig(); mgr != nil { + if mgr := bug.managerConfig(c); mgr != nil { notif.CC = append(notif.CC, mgr.CC.Always...) if public { notif.Maintainers = append(notif.Maintainers, mgr.CC.Maintainers...) @@ -433,7 +434,7 @@ func createNotification(c context.Context, typ dashapi.BugNotif, public bool, te return notif, nil } -func currentReporting(bug *Bug) (*Reporting, *BugReporting, int, string, error) { +func currentReporting(c context.Context, bug *Bug) (*Reporting, *BugReporting, int, string, error) { if bug.NumCrashes == 0 { // This is possible during the short window when we already created a bug, // but did not attach the first crash to it yet. We need to avoid reporting this bug yet @@ -446,7 +447,7 @@ func currentReporting(bug *Bug) (*Reporting, *BugReporting, int, string, error) if !bugReporting.Closed.IsZero() { continue } - reporting := config.Namespaces[bug.Namespace].ReportingByName(bugReporting.Name) + reporting := getConfig(c).Namespaces[bug.Namespace].ReportingByName(bugReporting.Name) if reporting == nil { return nil, nil, 0, "", fmt.Errorf("%v: missing in config", bugReporting.Name) } @@ -593,7 +594,7 @@ func crashBugReport(c context.Context, bug *Bug, crash *Crash, crashKey *db.Key, if build.Type == BuildFailed { rep.Maintainers = append(rep.Maintainers, kernelRepo.CC.BuildMaintainers...) } - if mgr := bug.managerConfig(); mgr != nil { + if mgr := bug.managerConfig(c); mgr != nil { rep.CC = append(rep.CC, mgr.CC.Always...) rep.Maintainers = append(rep.Maintainers, mgr.CC.Maintainers...) if build.Type == BuildFailed { @@ -923,7 +924,7 @@ func checkDupBug(c context.Context, cmd *dashapi.BugUpdate, bug *Bug, bugKey, du if !dupReporting.Closed.IsZero() && dupCanon.Status == BugStatusOpen { return nil, false, "Dup bug is already upstreamed.", nil } - if dupCanon.keyHash() == bugKey.StringID() { + if dupCanon.keyHash(c) == bugKey.StringID() { return nil, false, "Setting this dup would lead to a bug cycle, cycles are not allowed.", nil } return dup, true, "", nil @@ -945,7 +946,7 @@ func allowCrossReportingDup(c context.Context, bug, dup *Bug, // provided that these two reportings have the same access level and type. // The rest of the combinations can lead to surprising states and // information hiding, so we don't allow them. - cfg := config.Namespaces[bug.Namespace] + cfg := getConfig(c).Namespaces[bug.Namespace] bugConfig := &cfg.Reporting[bugIdx] dupConfig := &cfg.Reporting[dupIdx] lastIdx := len(cfg.Reporting) - 1 @@ -1109,7 +1110,7 @@ func incomingCommandCmd(c context.Context, now time.Time, cmd *dashapi.BugUpdate case dashapi.BugStatusDup: bug.Status = BugStatusDup bug.Closed = now - bug.DupOf = dup.keyHash() + bug.DupOf = dup.keyHash(c) case dashapi.BugStatusUpdate: // Just update Link, Commits, etc below. case dashapi.BugStatusUnCC: @@ -1188,7 +1189,7 @@ func findDupByTitle(c context.Context, ns, title string) (*Bug, *db.Key, error) if err != nil { return nil, nil, err } - bugHash := bugKeyHash(ns, title, seq) + bugHash := bugKeyHash(c, ns, title, seq) bugKey := db.NewKey(c, "Bug", bugHash, 0, nil) bug := new(Bug) if err := db.Get(c, bugKey, bug); err != nil { @@ -1229,7 +1230,7 @@ func lastReportedReporting(bug *Bug) *BugReporting { // The updateReporting method is supposed to be called both to fully initialize a new // Bug object and also to adjust it to the updated namespace configuration. -func (bug *Bug) updateReportings(cfg *Config, now time.Time) error { +func (bug *Bug) updateReportings(c context.Context, cfg *Config, now time.Time) error { oldReportings := map[string]BugReporting{} oldPositions := map[string]int{} for i, rep := range bug.Reporting { @@ -1251,7 +1252,7 @@ func (bug *Bug) updateReportings(cfg *Config, now time.Time) error { } else { bug.Reporting = append(bug.Reporting, BugReporting{ Name: rep.Name, - ID: bugReportingHash(bug.keyHash(), rep.Name), + ID: bugReportingHash(bug.keyHash(c), rep.Name), }) } } @@ -1351,7 +1352,7 @@ func (state *ReportingState) getEntry(now time.Time, namespace, name string) *Re func loadFullBugInfo(c context.Context, bug *Bug, bugKey *db.Key, bugReporting *BugReporting) (*dashapi.FullBugInfo, error) { - reporting := config.Namespaces[bug.Namespace].ReportingByName(bugReporting.Name) + reporting := getConfig(c).Namespaces[bug.Namespace].ReportingByName(bugReporting.Name) if reporting == nil { return nil, fmt.Errorf("failed to find the reporting object") } @@ -1383,7 +1384,7 @@ func loadFullBugInfo(c context.Context, bug *Bug, bugKey *db.Key, return nil, err } for _, similarBug := range similar { - _, bugReporting, _, _, _ := currentReporting(similarBug) + _, bugReporting, _, _, _ := currentReporting(c, similarBug) if bugReporting == nil { continue } diff --git a/dashboard/app/reporting_email.go b/dashboard/app/reporting_email.go index 17c8efcec..324f70575 100644 --- a/dashboard/app/reporting_email.go +++ b/dashboard/app/reporting_email.go @@ -38,7 +38,7 @@ func initEmailReporting() { http.HandleFunc("/_ah/bounce", handleEmailBounce) mailingLists = make(map[string]bool) - for _, cfg := range config.Namespaces { + for _, cfg := range getConfig(context.Background()).Namespaces { for _, reporting := range cfg.Reporting { if cfg, ok := reporting.Config.(*EmailConfig); ok { mailingLists[email.CanonicalEmail(cfg.Email)] = true @@ -272,7 +272,7 @@ func emailSendBugNotif(c context.Context, notif *dashapi.BugNotification) error func buildBadCommitMessage(c context.Context, notif *dashapi.BugNotification) (string, error) { var sb strings.Builder days := int(notifyAboutBadCommitPeriod / time.Hour / 24) - nsConfig := config.Namespaces[notif.Namespace] + nsConfig := getConfig(c).Namespaces[notif.Namespace] fmt.Fprintf(&sb, `This bug is marked as fixed by commit: %v @@ -456,14 +456,14 @@ func handleIncomingMail(w http.ResponseWriter, r *http.Request) { return } source := dashapi.NoDiscussion - for _, item := range config.DiscussionEmails { + for _, item := range getConfig(c).DiscussionEmails { if item.ReceiveAddress != myEmail { continue } source = item.Source break } - msg, err := email.Parse(r.Body, ownEmails(c), ownMailingLists(), []string{ + msg, err := email.Parse(r.Body, ownEmails(c), ownMailingLists(c), []string{ appURL(c), }) if err != nil { @@ -632,7 +632,7 @@ func handleBugCommand(c context.Context, bugInfo *bugInfoResult, msg *email.Emai return "no dup title" } var err error - cmd.DupOf, err = subjectParser.parseFullTitle(command.Args) + cmd.DupOf, err = getSubjectParser(c).parseFullTitle(command.Args) if err != nil { return "failed to parse the dup title" } @@ -708,7 +708,7 @@ func handleTestCommand(c context.Context, info *bugInfoResult, if len(args) == 2 { repo, branch = args[0], args[1] } - if info.bug.sanitizeAccess(AccessPublic) != AccessPublic { + if info.bug.sanitizeAccess(c, AccessPublic) != AccessPublic { log.Warningf(c, "%v: bug is not AccessPublic, patch testing request is denied", info.bug.Title) return "" } @@ -954,12 +954,12 @@ func identifyEmail(c context.Context, msg *email.Email) (*bugInfoResult, *bugLis log.Errorf(c, "no bug list with the %v ID found", bugID) return nil, nil, nil } - reminderConfig := config.Namespaces[subsystem.Namespace].Subsystems.Reminder + reminderConfig := getConfig(c).Namespaces[subsystem.Namespace].Subsystems.Reminder if reminderConfig == nil { log.Errorf(c, "reminder configuration is empty") return nil, nil, nil } - emailConfig, ok := bugListReportingConfig(subsystem.Namespace, stage).(*EmailConfig) + emailConfig, ok := bugListReportingConfig(c, subsystem.Namespace, stage).(*EmailConfig) if !ok { log.Errorf(c, "bug list's reporting config is not EmailConfig (id=%v)", bugID) return nil, nil, nil @@ -1047,7 +1047,7 @@ func loadBugInfo(c context.Context, msg *email.Email) *bugInfoResult { } return nil } - reporting := config.Namespaces[bug.Namespace].ReportingByName(bugReporting.Name) + reporting := getConfig(c).Namespaces[bug.Namespace].ReportingByName(bugReporting.Name) if reporting == nil { log.Errorf(c, "can't find reporting for this bug: namespace=%q reporting=%q", bug.Namespace, bugReporting.Name) @@ -1061,9 +1061,9 @@ func loadBugInfo(c context.Context, msg *email.Email) *bugInfoResult { return &bugInfoResult{bug, bugKey, bugReporting, reporting} } -func ownMailingLists() []string { +func ownMailingLists(c context.Context) []string { configs := []ReportingType{} - for _, ns := range config.Namespaces { + for _, ns := range getConfig(c).Namespaces { for _, rep := range ns.Reporting { configs = append(configs, rep.Config) } @@ -1090,12 +1090,25 @@ func ownMailingLists() []string { } var ( - subjectParser subjectTitleParser - errAmbiguousTitle = errors.New("ambiguous bug title") + // Use getSubjectParser(c) instead. + defaultSubjectParser *subjectTitleParser + subjectParserInit sync.Once + errAmbiguousTitle = errors.New("ambiguous bug title") ) +func getSubjectParser(c context.Context) *subjectTitleParser { + if getConfig(c) != getConfig(context.Background()) { + // For the non-default config, do not cache the parser. + return makeSubjectTitleParser(c) + } + subjectParserInit.Do(func() { + defaultSubjectParser = makeSubjectTitleParser(c) + }) + return defaultSubjectParser +} + func matchBugFromList(c context.Context, sender, subject string) (*bugInfoResult, error) { - title, seq, err := subjectParser.parseTitle(subject) + title, seq, err := getSubjectParser(c).parseTitle(subject) if err != nil { return nil, err } @@ -1117,11 +1130,11 @@ func matchBugFromList(c context.Context, sender, subject string) (*bugInfoResult log.Infof(c, "bug's seq is %v, wanted %d", bug.Seq, seq) continue } - if bug.sanitizeAccess(AccessPublic) != AccessPublic { + if bug.sanitizeAccess(c, AccessPublic) != AccessPublic { log.Infof(c, "access denied") continue } - reporting, bugReporting, _, _, err := currentReporting(bug) + reporting, bugReporting, _, _, err := currentReporting(c, bug) if err != nil || reporting == nil { log.Infof(c, "could not query reporting: %s", err) continue @@ -1154,7 +1167,25 @@ func matchBugFromList(c context.Context, sender, subject string) (*bugInfoResult type subjectTitleParser struct { pattern *regexp.Regexp - ready sync.Once +} + +func makeSubjectTitleParser(c context.Context) *subjectTitleParser { + stripPrefixes := []string{`R[eE]:`} + for _, ns := range getConfig(c).Namespaces { + for _, rep := range ns.Reporting { + emailConfig, ok := rep.Config.(*EmailConfig) + if !ok { + continue + } + if ok && emailConfig.SubjectPrefix != "" { + stripPrefixes = append(stripPrefixes, + regexp.QuoteMeta(emailConfig.SubjectPrefix)) + } + } + } + rePrefixes := `^(?:(?:` + strings.Join(stripPrefixes, "|") + `)\s*)*` + pattern := regexp.MustCompile(rePrefixes + `(?:\[[^\]]+\]\s*)*\s*(.*)$`) + return &subjectTitleParser{pattern} } func (p *subjectTitleParser) parseTitle(subject string) (string, int64, error) { @@ -1166,7 +1197,6 @@ func (p *subjectTitleParser) parseTitle(subject string) (string, int64, error) { } func (p *subjectTitleParser) parseFullTitle(subject string) (string, error) { - p.prepareRegexps() subject = strings.TrimSpace(subject) parts := p.pattern.FindStringSubmatch(subject) if parts == nil || parts[len(parts)-1] == "" { @@ -1175,26 +1205,6 @@ func (p *subjectTitleParser) parseFullTitle(subject string) (string, error) { return parts[len(parts)-1], nil } -func (p *subjectTitleParser) prepareRegexps() { - p.ready.Do(func() { - stripPrefixes := []string{`R[eE]:`} - for _, ns := range config.Namespaces { - for _, rep := range ns.Reporting { - emailConfig, ok := rep.Config.(*EmailConfig) - if !ok { - continue - } - if ok && emailConfig.SubjectPrefix != "" { - stripPrefixes = append(stripPrefixes, - regexp.QuoteMeta(emailConfig.SubjectPrefix)) - } - } - } - rePrefixes := `^(?:(?:` + strings.Join(stripPrefixes, "|") + `)\s*)*` - p.pattern = regexp.MustCompile(rePrefixes + `(?:\[[^\]]+\]\s*)*\s*(.*)$`) - }) -} - func checkMailingListInCC(c context.Context, msg *email.Email, mailingList string) bool { if msg.MailingList == mailingList { return true @@ -1266,8 +1276,8 @@ func replySubject(subject string) string { } func ownEmail(c context.Context) string { - if config.OwnEmailAddress != "" { - return config.OwnEmailAddress + if getConfig(c).OwnEmailAddress != "" { + return getConfig(c).OwnEmailAddress } return fmt.Sprintf("syzbot@%v.appspotmail.com", appengine.AppID(c)) } @@ -1278,6 +1288,7 @@ func fromAddr(c context.Context) string { func ownEmails(c context.Context) []string { emails := []string{ownEmail(c)} + config := getConfig(c) if config.ExtraOwnEmailAddresses != nil { emails = append(emails, config.ExtraOwnEmailAddresses...) } else if config.OwnEmailAddress == "" { @@ -1310,8 +1321,9 @@ func externalLink(c context.Context, tag string, id int64) string { } func appURL(c context.Context) string { - if config.AppURL != "" { - return config.AppURL + appURL := getConfig(c).AppURL + if appURL != "" { + return appURL } return fmt.Sprintf("https://%v.appspot.com", appengine.AppID(c)) } diff --git a/dashboard/app/reporting_lists.go b/dashboard/app/reporting_lists.go index 7e3ff0540..2cd6e94a3 100644 --- a/dashboard/app/reporting_lists.go +++ b/dashboard/app/reporting_lists.go @@ -32,7 +32,7 @@ func reportingPollBugLists(c context.Context, typ string) []*dashapi.BugListRepo return nil } ret := []*dashapi.BugListReport{} - for ns, nsConfig := range config.Namespaces { + for ns, nsConfig := range getConfig(c).Namespaces { rConfig := nsConfig.Subsystems.Reminder if rConfig == nil { continue @@ -80,7 +80,7 @@ func handleSubsystemReports(w http.ResponseWriter, r *http.Request) { log.Errorf(c, "failed to load subsystems: %v", err) return } - for ns, nsConfig := range config.Namespaces { + for ns, nsConfig := range getConfig(c).Namespaces { rConfig := nsConfig.Subsystems.Reminder if rConfig == nil { continue @@ -173,7 +173,7 @@ func reportingBugListCommand(c context.Context, cmd *dashapi.BugListUpdate) (str return fmt.Errorf("failed to query state: %w", err) } stateEnt := state.getEntry(timeNow(c), subsystem.Namespace, - config.Namespaces[subsystem.Namespace].Subsystems.Reminder.SourceReporting) + getConfig(c).Namespaces[subsystem.Namespace].Subsystems.Reminder.SourceReporting) stateEnt.Sent++ if err := saveReportingState(c, state); err != nil { return fmt.Errorf("failed to save state: %w", err) @@ -249,7 +249,7 @@ func querySubsystemReport(c context.Context, subsystem *Subsystem, reporting *Re } withRepro, noRepro := []*Bug{}, []*Bug{} for _, bug := range rawOpenBugs { - currReporting, _, _, _, _ := currentReporting(bug) + currReporting, _, _, _, _ := currentReporting(c, bug) if reporting.Name != currReporting.Name { // The big is not at the expected reporting stage. continue @@ -370,7 +370,7 @@ func queryMatchingBugs(c context.Context, ns, name string, accessLevel AccessLev fixed = append(fixed, bug) continue } - currReporting, _, _, _, err := currentReporting(bug) + currReporting, _, _, _, err := currentReporting(c, bug) if err != nil { continue } @@ -420,7 +420,7 @@ func reportingBugListReport(c context.Context, subsystemReport *SubsystemReport, if !stage.Closed.IsZero() { continue } - repConfig := bugListReportingConfig(ns, &stage) + repConfig := bugListReportingConfig(c, ns, &stage) if repConfig == nil { // It might happen if e.g. Moderation was set to nil. // Just skip the stage then. @@ -443,7 +443,7 @@ func reportingBugListReport(c context.Context, subsystemReport *SubsystemReport, Moderation: stage.Moderation, TotalStats: subsystemReport.TotalStats.toDashapi(), PeriodStats: subsystemReport.PeriodStats.toDashapi(), - PeriodDays: config.Namespaces[ns].Subsystems.Reminder.PeriodDays, + PeriodDays: getConfig(c).Namespaces[ns].Subsystems.Reminder.PeriodDays, } bugKeys, err := subsystemReport.getBugKeys() if err != nil { @@ -456,7 +456,7 @@ func reportingBugListReport(c context.Context, subsystemReport *SubsystemReport, } for _, bug := range bugs { bugReporting := bugReportingByName(bug, - config.Namespaces[ns].Subsystems.Reminder.SourceReporting) + getConfig(c).Namespaces[ns].Subsystems.Reminder.SourceReporting) ret.Bugs = append(ret.Bugs, dashapi.BugListItem{ Title: bug.displayTitle(), Link: fmt.Sprintf("%v/bug?extid=%v", appURL(c), bugReporting.ID), @@ -469,8 +469,8 @@ func reportingBugListReport(c context.Context, subsystemReport *SubsystemReport, return nil, nil } -func bugListReportingConfig(ns string, stage *SubsystemReportStage) ReportingType { - cfg := config.Namespaces[ns].Subsystems.Reminder +func bugListReportingConfig(c context.Context, ns string, stage *SubsystemReportStage) ReportingType { + cfg := getConfig(c).Namespaces[ns].Subsystems.Reminder if stage.Moderation { return cfg.ModerationConfig } diff --git a/dashboard/app/reporting_test.go b/dashboard/app/reporting_test.go index 7ef314df4..0cb198d15 100644 --- a/dashboard/app/reporting_test.go +++ b/dashboard/app/reporting_test.go @@ -16,6 +16,7 @@ import ( "github.com/google/syzkaller/pkg/email" "github.com/google/syzkaller/sys/targets" "github.com/stretchr/testify/assert" + "golang.org/x/net/context" ) func TestReportBug(t *testing.T) { @@ -790,12 +791,12 @@ func TestUpdateBugReporting(t *testing.T) { defer c.Close() setIDs := func(bug *Bug, arr []BugReporting) { for i := range arr { - arr[i].ID = bugReportingHash(bug.keyHash(), arr[i].Name) + arr[i].ID = bugReportingHash(bug.keyHash(c.ctx), arr[i].Name) } } now := timeNow(c.ctx) // We test against the test2 namespace. - cfg := config.Namespaces["test2"] + cfg := c.config().Namespaces["test2"] tests := []struct { Before []BugReporting After []BugReporting @@ -919,7 +920,7 @@ func TestUpdateBugReporting(t *testing.T) { } setIDs(bug, bug.Reporting) setIDs(bug, test.After) - hasError := bug.updateReportings(cfg, now) != nil + hasError := bug.updateReportings(c.ctx, cfg, now) != nil if hasError != test.Error { t.Errorf("Before: %#v, Expected error: %v, Got error: %v", test.Before, test.Error, hasError) } @@ -1109,6 +1110,8 @@ func TestReportDecommissionedBugs(t *testing.T) { func TestObsoletePeriod(t *testing.T) { base := time.Now() + c := context.Background() + config := getConfig(c) tests := []struct { name string bug *Bug @@ -1166,7 +1169,7 @@ func TestObsoletePeriod(t *testing.T) { for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { - ret := test.bug.obsoletePeriod() + ret := test.bug.obsoletePeriod(c) assert.Equal(t, test.period, ret) }) } diff --git a/dashboard/app/subsystem.go b/dashboard/app/subsystem.go index d056924c9..26b6b29ce 100644 --- a/dashboard/app/subsystem.go +++ b/dashboard/app/subsystem.go @@ -135,7 +135,7 @@ func logSubsystemChange(c context.Context, bug *Bug, new []*subsystem.Subsystem) sort.Strings(newNames) if !reflect.DeepEqual(oldNames, newNames) { log.Infof(c, "bug %s: subsystems set from %v to %v", - bug.keyHash(), oldNames, newNames) + bug.keyHash(c), oldNames, newNames) } } @@ -190,35 +190,12 @@ func subsystemMaintainers(c context.Context, ns, subsystemName string) []string return item.Emails() } -var subsystemsListKey = "custom list of kernel subsystems" - -type customSubsystemList struct { - ns string - list []*subsystem.Subsystem - revision int -} - -func contextWithSubsystems(c context.Context, custom *customSubsystemList) context.Context { - return context.WithValue(c, &subsystemsListKey, custom) -} - func getSubsystemService(c context.Context, ns string) *subsystem.Service { - // This is needed to emulate changes to the subsystem list over time during testing. - if val, ok := c.Value(&subsystemsListKey).(*customSubsystemList); ok && val.ns == ns { - if len(val.list) == 0 { - return nil - } else { - return subsystem.MustMakeService(val.list) - } - } - return config.Namespaces[ns].Subsystems.Service + return getConfig(c).Namespaces[ns].Subsystems.Service } func getSubsystemRevision(c context.Context, ns string) int { - if val, ok := c.Value(&subsystemsListKey).(*customSubsystemList); ok && val.ns == ns { - return val.revision - } - return config.Namespaces[ns].Subsystems.Revision + return getConfig(c).Namespaces[ns].Subsystems.Revision } func subsystemListURL(c context.Context, ns string) string { diff --git a/dashboard/app/subsystem_test.go b/dashboard/app/subsystem_test.go index bc2bdb505..3462a471f 100644 --- a/dashboard/app/subsystem_test.go +++ b/dashboard/app/subsystem_test.go @@ -1005,7 +1005,7 @@ func TestRemindersPriority(t *testing.T) { defer c.Close() client := c.makeClient(clientSubsystemRemind, keySubsystemRemind, true) - mailingList := config.Namespaces["subsystem-reminders"].Reporting[1].Config.(*EmailConfig).Email + mailingList := c.config().Namespaces["subsystem-reminders"].Reporting[1].Config.(*EmailConfig).Email build := testBuild(1) client.UploadBuild(build) diff --git a/dashboard/app/tree.go b/dashboard/app/tree.go index 07acde1fa..c1afc5b6c 100644 --- a/dashboard/app/tree.go +++ b/dashboard/app/tree.go @@ -585,7 +585,7 @@ func (ctx *bugTreeContext) loadCrashInfo() error { if ctx.crash != nil { var err error ns := ctx.bug.Namespace - repoGraph, err := makeRepoGraph(getKernelRepos(ctx.c, ns)) + repoGraph, err := makeRepoGraph(getConfig(ctx.c).Namespaces[ns].Repos) if err != nil { return err } @@ -602,7 +602,7 @@ func (ctx *bugTreeContext) isCrashRelevant(crash *Crash) (bool, *Build, error) { // Let's wait for the repro. return false, nil, nil } - newManager, _ := activeManager(crash.Manager, ctx.bug.Namespace) + newManager, _ := activeManager(ctx.cGlobal, crash.Manager, ctx.bug.Namespace) if newManager != crash.Manager { // The manager was deprecated since the crash. // Let's just ignore such bugs for now. @@ -719,7 +719,7 @@ func treeTestJobs(c context.Context, bug *Bug) ([]*dashapi.JobInfo, error) { // b) Whether the lookup was expensive (it can help optimize crossTreeBisection calls). func crossTreeBisection(c context.Context, bug *Bug, managers map[string]dashapi.ManagerJobs) (*Job, *db.Key, bool, error) { - repoGraph, err := makeRepoGraph(getKernelRepos(c, bug.Namespace)) + repoGraph, err := makeRepoGraph(getConfig(c).Namespaces[bug.Namespace].Repos) if err != nil { return nil, nil, false, err } @@ -754,7 +754,7 @@ func crossTreeBisection(c context.Context, bug *Bug, if err != nil { return err } - manager, _ := activeManager(crashJob.Manager, crashJob.Namespace) + manager, _ := activeManager(c, crashJob.Manager, crashJob.Namespace) if !managers[manager].BisectFix { return nil } diff --git a/dashboard/app/tree_test.go b/dashboard/app/tree_test.go index f9109202f..5b8fc6134 100644 --- a/dashboard/app/tree_test.go +++ b/dashboard/app/tree_test.go @@ -967,12 +967,12 @@ func (ctx *treeTestCtx) now() time.Time { } func (ctx *treeTestCtx) updateRepos(repos []KernelRepo) { - checkKernelRepos("tree-tests", config.Namespaces["tree-tests"], repos) + checkKernelRepos("tree-tests", ctx.ctx.config().Namespaces["tree-tests"], repos) ctx.perAlias = map[string]KernelRepo{} for _, repo := range repos { ctx.perAlias[repo.Alias] = repo } - ctx.ctx.setKernelRepos(repos) + ctx.ctx.setKernelRepos("tree-tests", repos) } func (ctx *treeTestCtx) uploadBuild(repo, branch string) *dashapi.Build { diff --git a/dashboard/app/util_test.go b/dashboard/app/util_test.go index b0ac1c6fe..668c3b962 100644 --- a/dashboard/app/util_test.go +++ b/dashboard/app/util_test.go @@ -86,6 +86,10 @@ func NewCtx(t *testing.T) *Ctx { return c } +func (c *Ctx) config() *GlobalConfig { + return getConfig(c.ctx) +} + func (c *Ctx) expectOK(err error) { if err != nil { c.t.Helper() @@ -211,17 +215,28 @@ func (c *Ctx) advanceTime(d time.Duration) { func (c *Ctx) setSubsystems(ns string, list []*subsystem.Subsystem, rev int) { c.transformContext = func(c context.Context) context.Context { - return contextWithSubsystems(c, &customSubsystemList{ - ns: ns, - list: list, - revision: rev, + newConfig := replaceNamespaceConfig(c, ns, func(cfg *Config) *Config { + ret := *cfg + ret.Subsystems.Revision = rev + if list == nil { + ret.Subsystems.Service = nil + } else { + ret.Subsystems.Service = subsystem.MustMakeService(list) + } + return &ret }) + return contextWithConfig(c, newConfig) } } -func (c *Ctx) setKernelRepos(list []KernelRepo) { +func (c *Ctx) setKernelRepos(ns string, list []KernelRepo) { c.transformContext = func(c context.Context) context.Context { - return contextWithRepos(c, list) + newConfig := replaceNamespaceConfig(c, ns, func(cfg *Config) *Config { + ret := *cfg + ret.Repos = list + return &ret + }) + return contextWithConfig(c, newConfig) } } @@ -231,9 +246,25 @@ func (c *Ctx) setNoObsoletions() { } } +func (c *Ctx) decommissionManager(ns, oldManager, newManager string) { + c.transformContext = func(c context.Context) context.Context { + newConfig := replaceManagerConfig(c, ns, oldManager, func(cfg ConfigManager) ConfigManager { + cfg.Decommissioned = true + cfg.DelegatedTo = newManager + return cfg + }) + return contextWithConfig(c, newConfig) + } +} + func (c *Ctx) decommission(ns string) { c.transformContext = func(c context.Context) context.Context { - return contextWithDecommission(c, ns, true) + newConfig := replaceNamespaceConfig(c, ns, func(cfg *Config) *Config { + ret := *cfg + ret.Decommissioned = true + return &ret + }) + return contextWithConfig(c, newConfig) } } @@ -669,3 +700,32 @@ func getRequestID(c context.Context) int { } return val } + +// Create a shallow copy of GlobalConfig with a replaced namespace config. +func replaceNamespaceConfig(c context.Context, ns string, f func(*Config) *Config) *GlobalConfig { + ret := *getConfig(c) + newNsMap := map[string]*Config{} + for name, nsCfg := range ret.Namespaces { + if name == ns { + nsCfg = f(nsCfg) + } + newNsMap[name] = nsCfg + } + ret.Namespaces = newNsMap + return &ret +} + +func replaceManagerConfig(c context.Context, ns, mgr string, f func(ConfigManager) ConfigManager) *GlobalConfig { + return replaceNamespaceConfig(c, ns, func(cfg *Config) *Config { + ret := *cfg + newMgrMap := map[string]ConfigManager{} + for name, mgrCfg := range ret.Managers { + if name == mgr { + mgrCfg = f(mgrCfg) + } + newMgrMap[name] = mgrCfg + } + ret.Managers = newMgrMap + return &ret + }) +} |
