github.com/yrj2011/jx-test-infra@v0.0.0-20190529031832-7a2065ee98eb/prow/cmd/tide/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 "context" 21 "flag" 22 "fmt" 23 "net/http" 24 "net/url" 25 "os" 26 "os/signal" 27 "strconv" 28 "syscall" 29 "time" 30 31 "github.com/sirupsen/logrus" 32 33 "k8s.io/test-infra/prow/config" 34 "k8s.io/test-infra/prow/flagutil" 35 "k8s.io/test-infra/prow/git" 36 "k8s.io/test-infra/prow/github" 37 "k8s.io/test-infra/prow/kube" 38 "k8s.io/test-infra/prow/logrusutil" 39 "k8s.io/test-infra/prow/tide" 40 ) 41 42 type options struct { 43 port int 44 45 dryRun bool 46 runOnce bool 47 deckURL string 48 49 configPath string 50 jobConfigPath string 51 cluster string 52 53 githubEndpoint flagutil.Strings 54 githubTokenFile string 55 } 56 57 func gatherOptions() options { 58 o := options{ 59 githubEndpoint: flagutil.NewStrings("https://api.github.com"), 60 } 61 flag.IntVar(&o.port, "port", 8888, "Port to listen on.") 62 63 flag.BoolVar(&o.dryRun, "dry-run", true, "Whether to mutate any real-world state.") 64 flag.BoolVar(&o.runOnce, "run-once", false, "If true, run only once then quit.") 65 flag.StringVar(&o.deckURL, "deck-url", "", "Deck URL for read-only access to the cluster.") 66 67 flag.StringVar(&o.configPath, "config-path", "/etc/config/config.yaml", "Path to config.yaml.") 68 flag.StringVar(&o.jobConfigPath, "job-config-path", "", "Path to prow job configs.") 69 flag.StringVar(&o.cluster, "cluster", "", "Path to kube.Cluster YAML file. If empty, uses the local cluster.") 70 71 flag.Var(&o.githubEndpoint, "github-endpoint", "GitHub's API endpoint.") 72 flag.StringVar(&o.githubTokenFile, "github-token-file", "/etc/github/oauth", "Path to the file containing the GitHub OAuth token.") 73 74 flag.Parse() 75 return o 76 } 77 78 func main() { 79 o := gatherOptions() 80 81 logrus.SetFormatter( 82 logrusutil.NewDefaultFieldsFormatter(nil, logrus.Fields{"component": "tide"}), 83 ) 84 85 configAgent := &config.Agent{} 86 if err := configAgent.Start(o.configPath, o.jobConfigPath); err != nil { 87 logrus.WithError(err).Fatal("Error starting config agent.") 88 } 89 90 var err error 91 for _, ep := range o.githubEndpoint.Strings() { 92 _, err = url.ParseRequestURI(ep) 93 if err != nil { 94 logrus.WithError(err).Fatalf("Invalid --endpoint URL %q.", ep) 95 } 96 } 97 98 var tokens []string 99 tokens = append(tokens, o.githubTokenFile) 100 101 secretAgent := &config.SecretAgent{} 102 if err := secretAgent.Start(tokens); err != nil { 103 logrus.WithError(err).Fatal("Error starting secrets agent.") 104 } 105 106 var ghcSync, ghcStatus *github.Client 107 var kc *kube.Client 108 if o.dryRun { 109 ghcSync = github.NewDryRunClient(secretAgent.GetTokenGenerator(o.githubTokenFile), o.githubEndpoint.Strings()...) 110 ghcStatus = github.NewDryRunClient(secretAgent.GetTokenGenerator(o.githubTokenFile), o.githubEndpoint.Strings()...) 111 if o.deckURL == "" { 112 logrus.Fatal("no deck URL was given for read-only ProwJob access") 113 } 114 kc = kube.NewFakeClient(o.deckURL) 115 } else { 116 ghcSync = github.NewClient(secretAgent.GetTokenGenerator(o.githubTokenFile), o.githubEndpoint.Strings()...) 117 ghcStatus = github.NewClient(secretAgent.GetTokenGenerator(o.githubTokenFile), o.githubEndpoint.Strings()...) 118 if o.cluster == "" { 119 kc, err = kube.NewClientInCluster(configAgent.Config().ProwJobNamespace) 120 if err != nil { 121 logrus.WithError(err).Fatal("Error getting kube client.") 122 } 123 } else { 124 kc, err = kube.NewClientFromFile(o.cluster, configAgent.Config().ProwJobNamespace) 125 if err != nil { 126 logrus.WithError(err).Fatal("Error getting kube client.") 127 } 128 } 129 } 130 // The sync loop should be allowed more tokens than the status loop because 131 // it has to list all PRs in the pool every loop while the status loop only 132 // has to list changed PRs every loop. 133 // The sync loop should have a much lower burst allowance than the status 134 // loop which may need to update many statuses upon restarting Tide after 135 // changing the context format or starting Tide on a new repo. 136 ghcSync.Throttle(800, 20) 137 ghcStatus.Throttle(400, 200) 138 139 gc, err := git.NewClient() 140 if err != nil { 141 logrus.WithError(err).Fatal("Error getting git client.") 142 } 143 defer gc.Clean() 144 // Get the bot's name in order to set credentials for the git client. 145 botName, err := ghcSync.BotName() 146 if err != nil { 147 logrus.WithError(err).Fatal("Error getting bot name.") 148 } 149 gc.SetCredentials(botName, secretAgent.GetTokenGenerator(o.githubTokenFile)) 150 151 c := tide.NewController(ghcSync, ghcStatus, kc, configAgent, gc, nil) 152 defer c.Shutdown() 153 154 server := &http.Server{Addr: ":" + strconv.Itoa(o.port), Handler: c} 155 156 start := time.Now() 157 sync(c) 158 if o.runOnce { 159 return 160 } 161 go func() { 162 sig := make(chan os.Signal, 1) 163 signal.Notify(sig, os.Interrupt, syscall.SIGTERM) 164 for { 165 select { 166 case <-time.After(time.Until(start.Add(configAgent.Config().Tide.SyncPeriod))): 167 start = time.Now() 168 sync(c) 169 case <-sig: 170 logrus.Info("Tide is shutting down...") 171 // Shutdown the http server with a 10s timeout then return to execute 172 // defered c.Shutdown() 173 ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) 174 defer cancel() // frees ctx resources 175 server.Shutdown(ctx) 176 return 177 } 178 } 179 }() 180 logrus.WithError(server.ListenAndServe()).Warn("Tide HTTP server stopped.") 181 } 182 183 func sync(c *tide.Controller) { 184 start := time.Now() 185 if err := c.Sync(); err != nil { 186 logrus.WithError(err).Error("Error syncing.") 187 } 188 logrus.WithField("duration", fmt.Sprintf("%v", time.Since(start))).Info("Synced") 189 }