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 }