github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/cmd/plank/main.go (about) 1 /* 2 Copyright 2017 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 "flag" 21 "fmt" 22 "net/http" 23 "os" 24 "os/signal" 25 "syscall" 26 "time" 27 28 "github.com/prometheus/client_golang/prometheus/promhttp" 29 "github.com/sirupsen/logrus" 30 "k8s.io/apimachinery/pkg/labels" 31 32 "k8s.io/test-infra/pkg/flagutil" 33 "k8s.io/test-infra/prow/config" 34 "k8s.io/test-infra/prow/config/secret" 35 prowflagutil "k8s.io/test-infra/prow/flagutil" 36 "k8s.io/test-infra/prow/kube" 37 "k8s.io/test-infra/prow/logrusutil" 38 "k8s.io/test-infra/prow/metrics" 39 "k8s.io/test-infra/prow/plank" 40 ) 41 42 type options struct { 43 totURL string 44 45 configPath string 46 jobConfigPath string 47 buildCluster string 48 selector string 49 skipReport bool 50 51 dryRun bool 52 kubernetes prowflagutil.KubernetesOptions 53 github prowflagutil.GitHubOptions 54 } 55 56 func gatherOptions() options { 57 o := options{} 58 fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError) 59 60 fs.StringVar(&o.totURL, "tot-url", "", "Tot URL") 61 62 fs.StringVar(&o.configPath, "config-path", "/etc/config/config.yaml", "Path to config.yaml.") 63 fs.StringVar(&o.jobConfigPath, "job-config-path", "", "Path to prow job configs.") 64 fs.StringVar(&o.buildCluster, "build-cluster", "", "Path to file containing a YAML-marshalled kube.Cluster object. If empty, uses the local cluster.") 65 fs.StringVar(&o.selector, "label-selector", kube.EmptySelector, "Label selector to be applied in prowjobs. See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors for constructing a label selector.") 66 fs.BoolVar(&o.skipReport, "skip-report", false, "Whether or not to ignore report with githubClient") 67 68 fs.BoolVar(&o.dryRun, "dry-run", true, "Whether or not to make mutating API calls to GitHub.") 69 for _, group := range []flagutil.OptionGroup{&o.kubernetes, &o.github} { 70 group.AddFlags(fs) 71 } 72 73 fs.Parse(os.Args[1:]) 74 return o 75 } 76 77 func (o *options) Validate() error { 78 for _, group := range []flagutil.OptionGroup{&o.kubernetes, &o.github} { 79 if err := group.Validate(o.dryRun); err != nil { 80 return err 81 } 82 } 83 84 if _, err := labels.Parse(o.selector); err != nil { 85 return fmt.Errorf("parse label selector: %v", err) 86 } 87 88 return nil 89 } 90 91 func main() { 92 o := gatherOptions() 93 if err := o.Validate(); err != nil { 94 logrus.WithError(err).Fatal("Invalid options") 95 } 96 97 logrus.SetFormatter( 98 logrusutil.NewDefaultFieldsFormatter(nil, logrus.Fields{"component": "plank"}), 99 ) 100 101 configAgent := &config.Agent{} 102 if err := configAgent.Start(o.configPath, o.jobConfigPath); err != nil { 103 logrus.WithError(err).Fatal("Error starting config agent.") 104 } 105 106 secretAgent := &secret.Agent{} 107 if o.github.TokenPath != "" { 108 if err := secretAgent.Start([]string{o.github.TokenPath}); err != nil { 109 logrus.WithError(err).Fatal("Error starting secrets agent.") 110 } 111 } 112 113 githubClient, err := o.github.GitHubClient(secretAgent, o.dryRun) 114 if err != nil { 115 logrus.WithError(err).Fatal("Error getting GitHub client.") 116 } 117 118 kubeClient, err := o.kubernetes.Client(configAgent.Config().ProwJobNamespace, o.dryRun) 119 if err != nil { 120 logrus.WithError(err).Fatal("Error getting kube client.") 121 } 122 123 var pkcs map[string]*kube.Client 124 if o.dryRun { 125 pkcs = map[string]*kube.Client{kube.DefaultClusterAlias: kubeClient} 126 } else { 127 if o.buildCluster == "" { 128 pkc, err := kube.NewClientInCluster(configAgent.Config().PodNamespace) 129 if err != nil { 130 logrus.WithError(err).Fatal("Error getting kube client.") 131 } 132 pkcs = map[string]*kube.Client{kube.DefaultClusterAlias: pkc} 133 } else { 134 pkcs, err = kube.ClientMapFromFile(o.buildCluster, configAgent.Config().PodNamespace) 135 if err != nil { 136 logrus.WithError(err).Fatal("Error getting kube client to build cluster.") 137 } 138 } 139 } 140 141 c, err := plank.NewController(kubeClient, pkcs, githubClient, nil, configAgent, o.totURL, o.selector, o.skipReport) 142 if err != nil { 143 logrus.WithError(err).Fatal("Error creating plank controller.") 144 } 145 146 // Push metrics to the configured prometheus pushgateway endpoint. 147 pushGateway := configAgent.Config().PushGateway 148 if pushGateway.Endpoint != "" { 149 go metrics.PushMetrics("plank", pushGateway.Endpoint, pushGateway.Interval) 150 } 151 // serve prometheus metrics. 152 go serve() 153 // gather metrics for the jobs handled by plank. 154 go gather(c) 155 156 tick := time.Tick(30 * time.Second) 157 sig := make(chan os.Signal, 1) 158 signal.Notify(sig, os.Interrupt, syscall.SIGTERM) 159 160 for { 161 select { 162 case <-tick: 163 start := time.Now() 164 if err := c.Sync(); err != nil { 165 logrus.WithError(err).Error("Error syncing.") 166 } 167 logrus.WithField("duration", fmt.Sprintf("%v", time.Since(start))).Info("Synced") 168 case <-sig: 169 logrus.Info("Plank is shutting down...") 170 return 171 } 172 } 173 } 174 175 // serve starts a http server and serves prometheus metrics. 176 // Meant to be called inside a goroutine. 177 func serve() { 178 http.Handle("/metrics", promhttp.Handler()) 179 logrus.WithError(http.ListenAndServe(":8080", nil)).Fatal("ListenAndServe returned.") 180 } 181 182 // gather metrics from plank. 183 // Meant to be called inside a goroutine. 184 func gather(c *plank.Controller) { 185 tick := time.Tick(30 * time.Second) 186 sig := make(chan os.Signal, 1) 187 signal.Notify(sig, os.Interrupt, syscall.SIGTERM) 188 189 for { 190 select { 191 case <-tick: 192 start := time.Now() 193 c.SyncMetrics() 194 logrus.WithField("metrics-duration", fmt.Sprintf("%v", time.Since(start))).Debug("Metrics synced") 195 case <-sig: 196 logrus.Debug("Plank gatherer is shutting down...") 197 return 198 } 199 } 200 }