github.com/GoogleCloudPlatform/testgrid@v0.0.174/cmd/summarizer/main.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"flag"
    23  	"runtime"
    24  	"strings"
    25  	"time"
    26  
    27  	gpubsub "cloud.google.com/go/pubsub"
    28  	"github.com/GoogleCloudPlatform/testgrid/pkg/pubsub"
    29  	"github.com/GoogleCloudPlatform/testgrid/pkg/summarizer"
    30  	"github.com/GoogleCloudPlatform/testgrid/util"
    31  	"github.com/GoogleCloudPlatform/testgrid/util/gcs"
    32  	"github.com/GoogleCloudPlatform/testgrid/util/metrics/prometheus"
    33  	"github.com/sirupsen/logrus"
    34  	"google.golang.org/api/option"
    35  )
    36  
    37  type options struct {
    38  	config            gcs.Path // gcs://path/to/config/proto
    39  	persistQueue      gcs.Path
    40  	creds             string
    41  	confirm           bool
    42  	dashboards        util.Strings
    43  	concurrency       int
    44  	wait              time.Duration
    45  	summaryPathPrefix string
    46  	pubsub            string
    47  	tabPathPrefix     string
    48  
    49  	features summarizer.FeatureFlags
    50  
    51  	debug    bool
    52  	trace    bool
    53  	jsonLogs bool
    54  }
    55  
    56  func (o *options) validate() error {
    57  	if o.config.String() == "" {
    58  		return errors.New("empty --config")
    59  	}
    60  	if o.concurrency == 0 {
    61  		o.concurrency = 4 * runtime.NumCPU()
    62  	}
    63  	return nil
    64  }
    65  
    66  func gatherOptions() options {
    67  	var o options
    68  	flag.Var(&o.config, "config", "gs://path/to/config.pb")
    69  	flag.Var(&o.persistQueue, "persist-queue", "Load previous queue state from gs://path/to/queue-state.json and regularly save to it thereafter")
    70  	flag.StringVar(&o.creds, "gcp-service-account", "", "/path/to/gcp/creds (use local creds if empty)")
    71  	flag.BoolVar(&o.confirm, "confirm", false, "Upload data if set")
    72  	flag.Var(&o.dashboards, "dashboard", "Only update named dashboards if set (repeateable)")
    73  	flag.IntVar(&o.concurrency, "concurrency", 0, "Manually define the number of dashboards to concurrently update if non-zero")
    74  	flag.DurationVar(&o.wait, "wait", 0, "Ensure at least this much time has passed since the last loop (exit if zero).")
    75  	flag.StringVar(&o.summaryPathPrefix, "summary-path", "summary", "Write summaries under this GCS path.")
    76  	flag.StringVar(&o.pubsub, "pubsub", "", "listen for test group updates at project/subscription")
    77  	flag.StringVar(&o.tabPathPrefix, "tab-path", "tabs", "Read from tab state instead of test group")
    78  	flag.BoolVar(&o.features.AllowFuzzyFlakiness, "allow-fuzzy-flakiness", false, "Enable the functionality of further classifying flaky tabs (acceptable or not).")
    79  	flag.BoolVar(&o.features.AllowIgnoredColumns, "allow-ignored-columns", false, "Enable the functionality to ignore columns with specific test statuses during summarization.")
    80  	flag.BoolVar(&o.features.AllowMinNumberOfRuns, "allow-min-num-runs", false, "Enable the functionality to enforce a min limit to test runs.")
    81  
    82  	flag.BoolVar(&o.debug, "debug", false, "Log debug lines if set")
    83  	flag.BoolVar(&o.trace, "trace", false, "Log trace and debug lines if set")
    84  	flag.BoolVar(&o.jsonLogs, "json-logs", false, "Uses a json logrus formatter when set")
    85  
    86  	flag.Parse()
    87  	return o
    88  }
    89  
    90  func gcsFixer(ctx context.Context, projectSub string, configPath gcs.Path, tabPrefix, credPath string) (summarizer.Fixer, error) {
    91  	if projectSub == "" {
    92  		return nil, nil
    93  	}
    94  	parts := strings.SplitN(projectSub, "/", 2)
    95  	if len(parts) != 2 {
    96  		return nil, errors.New("malformed project/subscription")
    97  	}
    98  	projID, subID := parts[0], parts[1]
    99  	pubsubClient, err := gpubsub.NewClient(ctx, "", option.WithCredentialsFile(credPath))
   100  	if err != nil {
   101  		logrus.WithError(err).Fatal("Failed to create pubsub client")
   102  	}
   103  	client := pubsub.NewClient(pubsubClient)
   104  	return summarizer.FixGCS(client, logrus.StandardLogger(), projID, subID, configPath, tabPrefix)
   105  }
   106  
   107  func main() {
   108  
   109  	opt := gatherOptions()
   110  	if err := opt.validate(); err != nil {
   111  		logrus.Fatalf("Invalid flags: %v", err)
   112  	}
   113  	if !opt.confirm {
   114  		logrus.Warning("--confirm=false (DRY-RUN): will not write to gcs")
   115  	}
   116  
   117  	switch {
   118  	case opt.trace:
   119  		logrus.SetLevel(logrus.TraceLevel)
   120  	case opt.debug:
   121  		logrus.SetLevel(logrus.DebugLevel)
   122  	}
   123  
   124  	if opt.jsonLogs {
   125  		logrus.SetFormatter(&logrus.JSONFormatter{})
   126  	}
   127  	logrus.SetReportCaller(true)
   128  
   129  	ctx, cancel := context.WithCancel(context.Background())
   130  	defer cancel()
   131  	storageClient, err := gcs.ClientWithCreds(ctx, opt.creds)
   132  	if err != nil {
   133  		logrus.WithError(err).Fatal("Failed to read storage client")
   134  	}
   135  
   136  	client := gcs.NewClient(storageClient)
   137  	metrics := summarizer.CreateMetrics(prometheus.NewFactory())
   138  	fixer, err := gcsFixer(ctx, opt.pubsub, opt.config, opt.tabPathPrefix, opt.creds)
   139  	if err != nil {
   140  		logrus.WithError(err).WithField("subscription", opt.pubsub).Fatal("Failed to configure pubsub")
   141  	}
   142  
   143  	fixers := make([]summarizer.Fixer, 0, 2)
   144  	if fixer != nil {
   145  		fixers = append(fixers, fixer)
   146  	}
   147  	if path := opt.persistQueue; path.String() != "" {
   148  		const freq = time.Minute
   149  		ticker := time.NewTicker(freq)
   150  		log := logrus.WithField("frequency", freq)
   151  		fixers = append(fixers, summarizer.FixPersistent(log, client, path, ticker.C))
   152  	}
   153  
   154  	opts := &summarizer.UpdateOptions{
   155  		ConfigPath:        opt.config,
   156  		Concurrency:       opt.concurrency,
   157  		TabPathPrefix:     opt.tabPathPrefix,
   158  		SummaryPathPrefix: opt.summaryPathPrefix,
   159  		AllowedDashboards: opt.dashboards.Strings(),
   160  		Confirm:           opt.confirm,
   161  		Features:          opt.features,
   162  		Freq:              opt.wait,
   163  	}
   164  
   165  	if err := summarizer.Update(ctx, client, metrics, opts, fixers...); err != nil {
   166  		logrus.WithError(err).Error("Could not summarize")
   167  	}
   168  }