[bugs-central] Poll the various issue frameworks for Skia's clients
Bug: skia:10783
Change-Id: I980f8217a2372e846944ea729fa79e1bad174615
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/329077
Reviewed-by: Joe Gregorio <jcgregorio@google.com>
diff --git a/bugs-central/go/poller/poller.go b/bugs-central/go/poller/poller.go
new file mode 100644
index 0000000..e025ece
--- /dev/null
+++ b/bugs-central/go/poller/poller.go
@@ -0,0 +1,203 @@
+package poller
+
+// Initializes and polls the various issue frameworks.
+
+import (
+ "context"
+ "os/user"
+ "path/filepath"
+ "time"
+
+ "cloud.google.com/go/storage"
+ "golang.org/x/oauth2"
+ "google.golang.org/api/option"
+
+ "go.skia.org/infra/bugs-central/go/bugs"
+ "go.skia.org/infra/bugs-central/go/bugs/github"
+ "go.skia.org/infra/bugs-central/go/bugs/issuetracker"
+ "go.skia.org/infra/bugs-central/go/bugs/monorail"
+ "go.skia.org/infra/bugs-central/go/db"
+ "go.skia.org/infra/bugs-central/go/types"
+ "go.skia.org/infra/go/baseapp"
+ "go.skia.org/infra/go/cleanup"
+ github_lib "go.skia.org/infra/go/github"
+ "go.skia.org/infra/go/httputils"
+ "go.skia.org/infra/go/skerr"
+ "go.skia.org/infra/go/sklog"
+)
+
+const (
+ // All recognized clients.
+ AndroidClient types.RecognizedClient = "Android"
+ ChromiumClient types.RecognizedClient = "Chromium"
+ FlutterNativeClient types.RecognizedClient = "Flutter-native"
+ FlutterOnWebClient types.RecognizedClient = "Flutter-on-web"
+ SkiaClient types.RecognizedClient = "Skia"
+)
+
+// IssuesPoller will be used to poll the different issue frameworks.
+type IssuesPoller struct {
+ storageClient *storage.Client
+ pathToGithubToken string
+ pathToServiceAccountFile string
+
+ dbClient *db.FirestoreDB
+}
+
+// New returns an instance of IssuesPoller.
+func New(ctx context.Context, ts oauth2.TokenSource, pathToServiceAccountFile string, dbClient *db.FirestoreDB) (*IssuesPoller, error) {
+ httpClient := httputils.DefaultClientConfig().WithTokenSource(ts).With2xxOnly().Client()
+ storageClient, err := storage.NewClient(ctx, option.WithHTTPClient(httpClient))
+ if err != nil {
+ return nil, skerr.Wrapf(err, "failed to init storage client")
+ }
+
+ pathToGithubToken := filepath.Join(github_lib.GITHUB_TOKEN_SERVER_PATH, github_lib.GITHUB_TOKEN_FILENAME)
+ if *baseapp.Local {
+ usr, err := user.Current()
+ if err != nil {
+ return nil, err
+ }
+ pathToGithubToken = filepath.Join(usr.HomeDir, github_lib.GITHUB_TOKEN_FILENAME)
+ }
+
+ return &IssuesPoller{
+ storageClient: storageClient,
+ pathToGithubToken: pathToGithubToken,
+ pathToServiceAccountFile: pathToServiceAccountFile,
+ dbClient: dbClient,
+ }, nil
+}
+
+// Start polls the different issue frameworks and populates DB and an in-memory object with that data.
+// It hardcodes information about Skia's various clients. It may be possible to extract some/all of these into
+// flags or YAML config files in the future.
+func (p *IssuesPoller) Start(ctx context.Context, pollInterval time.Duration) error {
+ // Instantiate the in-memory open issues object that will be passed to the different frameworks to
+ // populate.
+ openIssues := bugs.InitOpenIssues()
+
+ // Instantiate the bug frameworks with the different client configurations and then poll them.
+ bugFrameworks := []bugs.BugFramework{}
+
+ //////////////////// Android - IssueTracker ////////////////////
+ androidQueryConfig := &issuetracker.IssueTrackerQueryConfig{
+ Query: "componentid:1346 status:open",
+ Client: AndroidClient,
+ UntriagedPriorities: []string{"P4"},
+ UntriagedAliases: []string{"skia-android-triage@google.com"},
+ }
+ androidIssueTracker, err := issuetracker.New(p.storageClient, openIssues, androidQueryConfig)
+ if err != nil {
+ return skerr.Wrapf(err, "failed to init issuetracker for android")
+ }
+ bugFrameworks = append(bugFrameworks, androidIssueTracker)
+
+ //////////////////// Flutter_on_web - Github ////////////////////
+ flutterOnWebQueryConfig := &github.GithubQueryConfig{
+ Labels: []string{"e: web_canvaskit"},
+ Open: true,
+ PriorityRequired: true,
+ Client: FlutterOnWebClient,
+ }
+ flutterOnWebGithub, err := github.New(ctx, "flutter", "flutter", p.pathToGithubToken, openIssues, flutterOnWebQueryConfig)
+ if err != nil {
+ return skerr.Wrapf(err, "failed to init github for flutter-on-web")
+ }
+ bugFrameworks = append(bugFrameworks, flutterOnWebGithub)
+
+ //////////////////// Flutter_native - Github ////////////////////
+ flutterNativeQueryConfig := &github.GithubQueryConfig{
+ Labels: []string{"dependency: skia"},
+ ExcludeLabels: []string{"e: web_canvaskit"}, // These issues are already covered by flutter-on-web
+ Open: true,
+ PriorityRequired: false,
+ Client: FlutterNativeClient,
+ }
+ flutterNativeGithub, err := github.New(ctx, "flutter", "flutter", p.pathToGithubToken, openIssues, flutterNativeQueryConfig)
+ if err != nil {
+ return skerr.Wrapf(err, "failed to init github for flutter-on-web")
+ }
+ bugFrameworks = append(bugFrameworks, flutterNativeGithub)
+
+ //////////////////// Chromium:Internals>Skia - Monorail ////////////////////
+ crQueryConfig1 := &monorail.MonorailQueryConfig{
+ Instance: "chromium",
+ Query: "is:open component=Internals>Skia",
+ Client: ChromiumClient,
+ UntriagedStatuses: []string{"Untriaged", "Unconfirmed"},
+ }
+ crMonorail1, err := monorail.New(ctx, p.pathToServiceAccountFile, openIssues, crQueryConfig1)
+ if err != nil {
+ return skerr.Wrapf(err, "failed to init monorail for chromium")
+ }
+ bugFrameworks = append(bugFrameworks, crMonorail1)
+
+ //////////////////// Chromium:Internals>Skia>Compositing - Monorail ////////////////////
+ crQueryConfig2 := &monorail.MonorailQueryConfig{
+ Instance: "chromium",
+ Query: "is:open component=Internals>Skia>Compositing",
+ Client: ChromiumClient,
+ UntriagedStatuses: []string{"Untriaged", "Unconfirmed"},
+ }
+ crMonorail2, err := monorail.New(ctx, p.pathToServiceAccountFile, openIssues, crQueryConfig2)
+ if err != nil {
+ return skerr.Wrapf(err, "failed to init monorail for chromium")
+ }
+ bugFrameworks = append(bugFrameworks, crMonorail2)
+
+ //////////////////// Chromium:Internals>Skia>PDF - Monorail ////////////////////
+ crQueryConfig3 := &monorail.MonorailQueryConfig{
+ Instance: "chromium",
+ Query: "is:open component=Internals>Skia>PDF",
+ Client: ChromiumClient,
+ UntriagedStatuses: []string{"Untriaged", "Unconfirmed"},
+ }
+ crMonorail3, err := monorail.New(ctx, p.pathToServiceAccountFile, openIssues, crQueryConfig3)
+ if err != nil {
+ return skerr.Wrapf(err, "failed to init monorail for chromium")
+ }
+ bugFrameworks = append(bugFrameworks, crMonorail3)
+
+ //////////////////// Skia - Monorail ////////////////////
+ skQueryConfig := &monorail.MonorailQueryConfig{
+ Instance: "skia",
+ Query: "is:open",
+ Client: SkiaClient,
+ UntriagedStatuses: []string{"New"},
+ }
+ skMonorail, err := monorail.New(ctx, p.pathToServiceAccountFile, openIssues, skQueryConfig)
+ if err != nil {
+ return skerr.Wrapf(err, "failed to init monorail for skia")
+ }
+ bugFrameworks = append(bugFrameworks, skMonorail)
+
+ cleanup.Repeat(pollInterval, func(ctx context.Context) {
+ if !*baseapp.Local {
+ // Ignore the passed-in context; this allows us to continue running even if the
+ // context is canceled due to transient errors.
+ ctx = context.Background()
+ }
+
+ // Create a runID timestamp to associate all found issues with this poll iteration.
+ runId := p.dbClient.GenerateRunId(time.Now())
+
+ // Search all bug frameworks.
+ for _, b := range bugFrameworks {
+ if err := b.SearchClientAndPersist(ctx, p.dbClient, runId); err != nil {
+ sklog.Errorf("Error when searching and saving issues: %s", err)
+ return
+ }
+ }
+
+ // We are done with this iteration. Add the runId timestamp to the DB.
+ if err := p.dbClient.StoreRunId(context.Background(), runId); err != nil {
+ sklog.Errorf("Could not store runId in DB: %s", err)
+ return
+ }
+
+ openIssues.PrettyPrintOpenIssues()
+ }, nil)
+
+ return nil
+}