github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/syz-cluster/email-reporter/main.go (about)

     1  // Copyright 2025 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  // NOTE: This app assumes that only one copy of it is runnning at the same time.
     5  
     6  package main
     7  
     8  import (
     9  	"context"
    10  	"log"
    11  	"time"
    12  
    13  	"github.com/google/syzkaller/pkg/email/lore"
    14  	"github.com/google/syzkaller/syz-cluster/pkg/api"
    15  	"github.com/google/syzkaller/syz-cluster/pkg/app"
    16  	"github.com/google/syzkaller/syz-cluster/pkg/emailclient"
    17  	"golang.org/x/sync/errgroup"
    18  )
    19  
    20  // TODO: add extra sanity checks that would prevent flooding the mailing lists:
    21  // - this pod may crash and be restarted by K8S: this complicates accounting,
    22  // - the send operation might return an error, yet an email would be actually sent: back off on errors?
    23  
    24  const (
    25  	// How often to check whether there are new emails to be sent.
    26  	senderPollPeriod = 30 * time.Second
    27  	// How often to check whether there are new incoming emails.
    28  	fetcherPollPeriod = 2 * time.Minute
    29  )
    30  
    31  func main() {
    32  	ctx := context.Background()
    33  	cfg, err := app.Config()
    34  	if err != nil {
    35  		app.Fatalf("failed to load config: %v", err)
    36  	}
    37  	if cfg.EmailReporting == nil {
    38  		app.Fatalf("reporting is not configured: %v", err)
    39  	}
    40  	sender, err := emailclient.MakeSender(ctx, cfg.EmailReporting)
    41  	if err != nil {
    42  		app.Fatalf("failed to create a sender: %s", err)
    43  	}
    44  	reporterClient := app.DefaultReporterClient()
    45  	handler := &Handler{
    46  		reporter:    api.LKMLReporter,
    47  		apiClient:   reporterClient,
    48  		emailConfig: cfg.EmailReporting,
    49  		sender:      sender,
    50  	}
    51  	msgCh := make(chan *lore.Email, 16)
    52  	eg, loopCtx := errgroup.WithContext(ctx)
    53  	if cfg.EmailReporting.LoreArchiveURL != "" {
    54  		fetcher := NewLKMLEmailStream("/lore-repo/checkout", reporterClient, cfg.EmailReporting, msgCh)
    55  		eg.Go(func() error { return fetcher.Loop(loopCtx, fetcherPollPeriod) })
    56  	}
    57  	eg.Go(func() error {
    58  		for {
    59  			var newEmail *lore.Email
    60  			select {
    61  			case newEmail = <-msgCh:
    62  			case <-loopCtx.Done():
    63  				return nil
    64  			}
    65  			log.Printf("received email %q", newEmail.MessageID)
    66  			err := handler.IncomingEmail(loopCtx, newEmail.Email)
    67  			if err != nil {
    68  				// Note that we just print an error and go on instead of retrying.
    69  				// Some retrying may be reasonable, but it also comes with a risk of flooding
    70  				// the mailing lists.
    71  				app.Errorf("email %q: failed to process: %v", newEmail.MessageID, err)
    72  			}
    73  		}
    74  	})
    75  	eg.Go(func() error {
    76  		handler.PollReportsLoop(loopCtx, senderPollPeriod)
    77  		return nil
    78  	})
    79  	if err = eg.Wait(); err != nil {
    80  		app.Errorf("failed: %s", err)
    81  	}
    82  }