github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/cmd/crier/main.go (about) 1 /* 2 Copyright 2018 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 "errors" 21 "flag" 22 "os" 23 "os/signal" 24 "sync" 25 "syscall" 26 "time" 27 28 "github.com/sirupsen/logrus" 29 "golang.org/x/time/rate" 30 "k8s.io/client-go/util/workqueue" 31 32 prowjobinformer "k8s.io/test-infra/prow/client/informers/externalversions" 33 "k8s.io/test-infra/prow/config" 34 "k8s.io/test-infra/prow/config/secret" 35 "k8s.io/test-infra/prow/crier" 36 prowflagutil "k8s.io/test-infra/prow/flagutil" 37 gerritclient "k8s.io/test-infra/prow/gerrit/client" 38 gerritreporter "k8s.io/test-infra/prow/gerrit/reporter" 39 githubreporter "k8s.io/test-infra/prow/github/reporter" 40 "k8s.io/test-infra/prow/logrusutil" 41 pubsubreporter "k8s.io/test-infra/prow/pubsub/reporter" 42 ) 43 44 const ( 45 resync = 0 * time.Minute 46 ) 47 48 type options struct { 49 client prowflagutil.KubernetesClientOptions 50 cookiefilePath string 51 gerritProjects gerritclient.ProjectsFlag 52 github prowflagutil.GitHubOptions 53 54 // TODO(krzyzacy): drop config agent! 55 configPath string 56 jobConfigPath string 57 58 gerritWorkers int 59 pubsubWorkers int 60 githubWorkers int 61 62 dryrun bool 63 } 64 65 func (o *options) validate() error { 66 if o.gerritWorkers > 1 { 67 // TODO(krzyzacy): try to see how to handle racy better for gerrit aggregate report. 68 logrus.Warn("gerrit reporter only supports one worker") 69 o.gerritWorkers = 1 70 } 71 72 if o.gerritWorkers+o.pubsubWorkers+o.githubWorkers <= 0 { 73 return errors.New("crier need to have at least one report worker to start") 74 } 75 76 if o.gerritWorkers > 0 { 77 if len(o.gerritProjects) == 0 { 78 return errors.New("--gerrit-projects must be set") 79 } 80 81 if o.cookiefilePath == "" { 82 logrus.Info("--cookiefile is not set, using anonymous authentication") 83 } 84 } 85 86 if o.githubWorkers > 0 { 87 if err := o.github.Validate(o.dryrun); err != nil { 88 return err 89 } 90 } 91 92 return nil 93 } 94 95 func (o *options) parseArgs(fs *flag.FlagSet, args []string) error { 96 fs.StringVar(&o.cookiefilePath, "cookiefile", "", "Path to git http.cookiefile, leave empty for anonymous") 97 fs.Var(&o.gerritProjects, "gerrit-projects", "Set of gerrit repos to monitor on a host example: --gerrit-host=https://android.googlesource.com=platform/build,toolchain/llvm, repeat flag for each host") 98 fs.IntVar(&o.gerritWorkers, "gerrit-workers", 0, "Number of gerrit report workers (0 means disabled)") 99 fs.IntVar(&o.pubsubWorkers, "pubsub-workers", 0, "Number of pubsub report workers (0 means disabled)") 100 fs.IntVar(&o.githubWorkers, "github-workers", 0, "Number of github report workers (0 means disabled)") 101 102 fs.StringVar(&o.configPath, "config-path", "", "Path to config.yaml.") 103 fs.StringVar(&o.jobConfigPath, "job-config-path", "", "Path to prow job configs.") 104 105 // TODO(krzyzacy): implement dryrun for gerrit/pubsub 106 fs.BoolVar(&o.dryrun, "dry-run", false, "Run in dry-run mode, not doing actual report (effective for github only)") 107 108 o.github.AddFlags(fs) 109 o.client.AddFlags(fs) 110 111 fs.Parse(args) 112 113 return o.validate() 114 } 115 116 func parseOptions() options { 117 o := options{ 118 gerritProjects: gerritclient.ProjectsFlag{}, 119 } 120 121 if err := o.parseArgs(flag.CommandLine, os.Args[1:]); err != nil { 122 logrus.WithError(err).Fatal("Invalid flag options") 123 } 124 125 return o 126 } 127 128 func main() { 129 o := parseOptions() 130 131 logrus.SetFormatter( 132 logrusutil.NewDefaultFieldsFormatter(nil, logrus.Fields{"component": "crier"}), 133 ) 134 135 prowjobClient, err := o.client.ProwJobClient() 136 if err != nil { 137 logrus.WithError(err).Fatal("unable to create prow job client") 138 } 139 140 prowjobInformerFactory := prowjobinformer.NewSharedInformerFactory(prowjobClient, resync) 141 142 queue := workqueue.NewRateLimitingQueue( 143 workqueue.NewMaxOfRateLimiter( 144 workqueue.NewItemExponentialFailureRateLimiter(1*time.Second, 60*time.Second), 145 // 10 qps, 100 bucket size. This is only for retry speed and its only the overall factor (not per item) 146 &workqueue.BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(10), 100)}, 147 )) 148 149 var controllers []*crier.Controller 150 151 // track all worker status before shutdown 152 wg := &sync.WaitGroup{} 153 154 if o.gerritWorkers > 0 { 155 informer := prowjobInformerFactory.Prow().V1().ProwJobs() 156 gerritReporter, err := gerritreporter.NewReporter(o.cookiefilePath, o.gerritProjects, informer.Lister()) 157 if err != nil { 158 logrus.WithError(err).Fatal("Error starting gerrit reporter") 159 } 160 161 controllers = append( 162 controllers, 163 crier.NewController( 164 prowjobClient, 165 queue, 166 informer, 167 gerritReporter, 168 o.gerritWorkers, 169 wg)) 170 } 171 172 if o.pubsubWorkers > 0 { 173 controllers = append( 174 controllers, 175 crier.NewController( 176 prowjobClient, 177 queue, 178 prowjobInformerFactory.Prow().V1().ProwJobs(), 179 pubsubreporter.NewReporter(), 180 o.pubsubWorkers, 181 wg)) 182 } 183 184 if o.githubWorkers > 0 { 185 secretAgent := &secret.Agent{} 186 if o.github.TokenPath != "" { 187 if err := secretAgent.Start([]string{o.github.TokenPath}); err != nil { 188 logrus.WithError(err).Fatal("Error starting secrets agent") 189 } 190 } 191 192 githubClient, err := o.github.GitHubClient(secretAgent, o.dryrun) 193 if err != nil { 194 logrus.WithError(err).Fatal("Error getting GitHub client.") 195 } 196 197 configAgent := &config.Agent{} 198 if err := configAgent.Start(o.configPath, o.jobConfigPath); err != nil { 199 logrus.WithError(err).Fatal("Error starting config agent.") 200 } 201 202 controllers = append( 203 controllers, 204 crier.NewController( 205 prowjobClient, 206 queue, 207 prowjobInformerFactory.Prow().V1().ProwJobs(), 208 githubreporter.NewReporter(githubClient, configAgent), 209 o.githubWorkers, 210 wg)) 211 } 212 213 if len(controllers) == 0 { 214 logrus.Fatalf("should have at least one controller to start crier.") 215 } 216 217 stopCh := make(chan struct{}) 218 defer close(stopCh) 219 220 // run the controller loop to process items 221 prowjobInformerFactory.Start(stopCh) 222 for _, controller := range controllers { 223 go controller.Run(stopCh) 224 } 225 226 sigTerm := make(chan os.Signal, 1) 227 signal.Notify(sigTerm, syscall.SIGTERM) 228 signal.Notify(sigTerm, syscall.SIGINT) 229 230 <-sigTerm 231 logrus.Info("Crier received a termination signal and is shutting down...") 232 for range controllers { 233 stopCh <- struct{}{} 234 } 235 236 // waiting for all crier worker to finish 237 c := make(chan struct{}) 238 go func() { 239 defer close(c) 240 wg.Wait() 241 }() 242 select { 243 case <-c: 244 logrus.Info("All worker finished, exiting crier") 245 case <-time.After(10 * time.Second): 246 logrus.Info("timed out waiting for all worker to finish") 247 } 248 }