github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/cmd/sub/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 "context" 21 "flag" 22 "net/http" 23 "os" 24 "os/signal" 25 "strconv" 26 "sync" 27 "syscall" 28 "time" 29 30 "github.com/prometheus/client_golang/prometheus/promhttp" 31 "github.com/sirupsen/logrus" 32 "golang.org/x/sync/errgroup" 33 34 "k8s.io/test-infra/prow/client/clientset/versioned" 35 "k8s.io/test-infra/prow/config" 36 "k8s.io/test-infra/prow/config/secret" 37 "k8s.io/test-infra/prow/flagutil" 38 "k8s.io/test-infra/prow/kube" 39 "k8s.io/test-infra/prow/logrusutil" 40 "k8s.io/test-infra/prow/metrics" 41 "k8s.io/test-infra/prow/pubsub/subscriber" 42 ) 43 44 var ( 45 flagOptions *options 46 ) 47 48 type options struct { 49 client flagutil.KubernetesClientOptions 50 port int 51 pushSecretFile string 52 53 configPath string 54 jobConfigPath string 55 pluginConfig string 56 57 dryRun bool 58 gracePeriod time.Duration 59 } 60 61 type kubeClient struct { 62 client versioned.Interface 63 namespace string 64 dryRun bool 65 } 66 67 func (c *kubeClient) CreateProwJob(job *kube.ProwJob) (*kube.ProwJob, error) { 68 if c.dryRun { 69 return job, nil 70 } 71 return c.client.ProwV1().ProwJobs(c.namespace).Create(job) 72 } 73 74 func init() { 75 flagOptions = &options{} 76 fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError) 77 78 fs.IntVar(&flagOptions.port, "port", 80, "HTTP Port.") 79 fs.StringVar(&flagOptions.pushSecretFile, "push-secret-file", "", "Path to Pub/Sub Push secret file.") 80 81 fs.StringVar(&flagOptions.configPath, "config-path", "/etc/config/config.yaml", "Path to config.yaml.") 82 fs.StringVar(&flagOptions.jobConfigPath, "job-config-path", "", "Path to prow job configs.") 83 84 fs.BoolVar(&flagOptions.dryRun, "dry-run", true, "Dry run for testing. Uses API tokens but does not mutate.") 85 fs.DurationVar(&flagOptions.gracePeriod, "grace-period", 180*time.Second, "On shutdown, try to handle remaining events for the specified duration. ") 86 87 flagOptions.client.AddFlags(fs) 88 89 fs.Parse(os.Args[1:]) 90 } 91 92 func main() { 93 94 logrus.SetFormatter(logrusutil.NewDefaultFieldsFormatter(nil, logrus.Fields{"component": "pubsub-subscriber"})) 95 96 configAgent := &config.Agent{} 97 if err := configAgent.Start(flagOptions.configPath, flagOptions.jobConfigPath); err != nil { 98 logrus.WithError(err).Fatal("Error starting config agent.") 99 } 100 101 var tokenGenerator func() []byte 102 if flagOptions.pushSecretFile != "" { 103 var tokens []string 104 tokens = append(tokens, flagOptions.pushSecretFile) 105 106 secretAgent := &secret.Agent{} 107 if err := secretAgent.Start(tokens); err != nil { 108 logrus.WithError(err).Fatal("Error starting secrets agent.") 109 } 110 tokenGenerator = secretAgent.GetTokenGenerator(flagOptions.pushSecretFile) 111 } 112 113 prowjobClient, err := flagOptions.client.ProwJobClient() 114 if err != nil { 115 logrus.WithError(err).Fatal("unable to create prow job client") 116 } 117 kubeClient := &kubeClient{ 118 client: prowjobClient, 119 namespace: configAgent.Config().ProwJobNamespace, 120 dryRun: flagOptions.dryRun, 121 } 122 123 promMetrics := subscriber.NewMetrics() 124 125 // Push metrics to the configured prometheus pushgateway endpoint. 126 pushGateway := configAgent.Config().PushGateway 127 if pushGateway.Endpoint != "" { 128 go metrics.PushMetrics("sub", pushGateway.Endpoint, pushGateway.Interval) 129 } 130 131 s := &subscriber.Subscriber{ 132 ConfigAgent: configAgent, 133 Metrics: promMetrics, 134 KubeClient: kubeClient, 135 } 136 137 // Return 200 on / for health checks. 138 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {}) 139 http.Handle("/metrics", promhttp.Handler()) 140 141 // Will call shutdown which will stop the errGroup 142 shutdownCtx, shutdown := context.WithCancel(context.Background()) 143 errGroup, derivedCtx := errgroup.WithContext(shutdownCtx) 144 wg := sync.WaitGroup{} 145 146 // Setting up Push Server 147 logrus.Info("Setting up Push Server") 148 pushServer := &subscriber.PushServer{ 149 Subscriber: s, 150 TokenGenerator: tokenGenerator, 151 } 152 http.Handle("/push", pushServer) 153 154 // Setting up Pull Server 155 logrus.Info("Setting up Pull Server") 156 pullServer := subscriber.NewPullServer(s) 157 errGroup.Go(func() error { 158 wg.Add(1) 159 defer wg.Done() 160 logrus.Info("Starting Pull Server") 161 err := pullServer.Run(derivedCtx) 162 logrus.WithError(err).Warn("Pull Server exited.") 163 return err 164 }) 165 166 httpServer := &http.Server{Addr: ":" + strconv.Itoa(flagOptions.port)} 167 errGroup.Go(func() error { 168 wg.Add(1) 169 defer wg.Done() 170 logrus.Info("Starting HTTP Server") 171 err := httpServer.ListenAndServe() 172 logrus.WithError(err).Warn("HTTP Server exited.") 173 return err 174 }) 175 176 sig := make(chan os.Signal, 1) 177 signal.Notify(sig, os.Interrupt, syscall.SIGTERM, syscall.SIGABRT) 178 179 select { 180 case <-shutdownCtx.Done(): 181 err = shutdownCtx.Err() 182 break 183 case <-derivedCtx.Done(): 184 err = derivedCtx.Err() 185 break 186 case <-sig: 187 break 188 } 189 190 logrus.WithError(err).Warn("Starting Shutdown") 191 shutdown() 192 // Shutdown gracefully on SIGTERM or SIGINT 193 timeoutCtx, cancel := context.WithTimeout(context.Background(), flagOptions.gracePeriod) 194 defer cancel() 195 httpServer.Shutdown(timeoutCtx) 196 errGroup.Wait() 197 wg.Wait() 198 }