github.com/abayer/test-infra@v0.0.5/prow/external-plugins/needs-rebase/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  	"encoding/json"
    21  	"flag"
    22  	"fmt"
    23  	"net/http"
    24  	"net/url"
    25  	"os/signal"
    26  	"strconv"
    27  	"syscall"
    28  	"time"
    29  
    30  	"github.com/sirupsen/logrus"
    31  
    32  	"k8s.io/test-infra/prow/config"
    33  	"k8s.io/test-infra/prow/external-plugins/needs-rebase/plugin"
    34  	"k8s.io/test-infra/prow/flagutil"
    35  	"k8s.io/test-infra/prow/github"
    36  	"k8s.io/test-infra/prow/hook"
    37  	"k8s.io/test-infra/prow/pluginhelp/externalplugins"
    38  	"k8s.io/test-infra/prow/plugins"
    39  )
    40  
    41  var (
    42  	port              = flag.Int("port", 8888, "Port to listen on.")
    43  	dryRun            = flag.Bool("dry-run", true, "Dry run for testing. Uses API tokens but does not mutate.")
    44  	pluginConfig      = flag.String("plugin-config", "/etc/plugins/plugins.yaml", "Path to plugin config file.")
    45  	githubEndpoint    = flagutil.NewStrings("https://api.github.com")
    46  	githubTokenFile   = flag.String("github-token-file", "/etc/github/oauth", "Path to the file containing the GitHub OAuth secret.")
    47  	webhookSecretFile = flag.String("hmac-secret-file", "/etc/webhook/hmac", "Path to the file containing the GitHub HMAC secret.")
    48  	updatePeriod      = flag.Duration("update-period", time.Hour*24, "Period duration for periodic scans of all PRs.")
    49  )
    50  
    51  func init() {
    52  	flag.Var(&githubEndpoint, "github-endpoint", "GitHub's API endpoint.")
    53  }
    54  
    55  func main() {
    56  	flag.Parse()
    57  	logrus.SetFormatter(&logrus.JSONFormatter{})
    58  	// TODO: Use global option from the prow config.
    59  	logrus.SetLevel(logrus.InfoLevel)
    60  	log := logrus.StandardLogger().WithField("plugin", "needs-rebase")
    61  
    62  	// Ignore SIGTERM so that we don't drop hooks when the pod is removed.
    63  	// We'll get SIGTERM first and then SIGKILL after our graceful termination
    64  	// deadline.
    65  	signal.Ignore(syscall.SIGTERM)
    66  
    67  	secretAgent := &config.SecretAgent{}
    68  	if err := secretAgent.Start([]string{*githubTokenFile, *webhookSecretFile}); err != nil {
    69  		logrus.WithError(err).Fatal("Error starting secrets agent.")
    70  	}
    71  
    72  	var err error
    73  	for _, ep := range githubEndpoint.Strings() {
    74  		_, err = url.ParseRequestURI(ep)
    75  		if err != nil {
    76  			logrus.WithError(err).Fatalf("Invalid --endpoint URL %q.", ep)
    77  		}
    78  	}
    79  
    80  	pa := &plugins.PluginAgent{}
    81  	if err := pa.Start(*pluginConfig); err != nil {
    82  		log.WithError(err).Fatalf("Error loading plugin config from %q.", *pluginConfig)
    83  	}
    84  
    85  	githubClient := github.NewClient(secretAgent.GetTokenGenerator(*githubTokenFile), githubEndpoint.Strings()...)
    86  	if *dryRun {
    87  		githubClient = github.NewDryRunClient(secretAgent.GetTokenGenerator(*githubTokenFile), githubEndpoint.Strings()...)
    88  	}
    89  	githubClient.Throttle(360, 360)
    90  
    91  	server := &Server{
    92  		tokenGenerator: secretAgent.GetTokenGenerator(*webhookSecretFile),
    93  		ghc:            githubClient,
    94  		log:            log,
    95  	}
    96  
    97  	go periodicUpdate(log, pa, githubClient, *updatePeriod)
    98  
    99  	http.Handle("/", server)
   100  	externalplugins.ServeExternalPluginHelp(http.DefaultServeMux, log, plugin.HelpProvider)
   101  	logrus.Fatal(http.ListenAndServe(":"+strconv.Itoa(*port), nil))
   102  }
   103  
   104  // Server implements http.Handler. It validates incoming GitHub webhooks and
   105  // then dispatches them to the appropriate plugins.
   106  type Server struct {
   107  	tokenGenerator func() []byte
   108  	ghc            *github.Client
   109  	log            *logrus.Entry
   110  }
   111  
   112  // ServeHTTP validates an incoming webhook and puts it into the event channel.
   113  func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   114  	// TODO: Move webhook handling logic out of hook binary so that we don't have to import all
   115  	// plugins just to validate the webhook.
   116  	eventType, eventGUID, payload, ok := hook.ValidateWebhook(w, r, s.tokenGenerator())
   117  	if !ok {
   118  		return
   119  	}
   120  	fmt.Fprint(w, "Event received. Have a nice day.")
   121  
   122  	if err := s.handleEvent(eventType, eventGUID, payload); err != nil {
   123  		logrus.WithError(err).Error("Error parsing event.")
   124  	}
   125  }
   126  
   127  func (s *Server) handleEvent(eventType, eventGUID string, payload []byte) error {
   128  	l := s.log.WithFields(
   129  		logrus.Fields{
   130  			"event-type":     eventType,
   131  			github.EventGUID: eventGUID,
   132  		},
   133  	)
   134  	switch eventType {
   135  	case "pull_request":
   136  		var pre github.PullRequestEvent
   137  		if err := json.Unmarshal(payload, &pre); err != nil {
   138  			return err
   139  		}
   140  		go func() {
   141  			if err := plugin.HandleEvent(l, s.ghc, &pre); err != nil {
   142  				l.Info("Error handling event.")
   143  			}
   144  		}()
   145  	default:
   146  		s.log.Debugf("received an event of type %q but didn't ask for it", eventType)
   147  	}
   148  	return nil
   149  }
   150  
   151  func periodicUpdate(log *logrus.Entry, pa *plugins.PluginAgent, ghc *github.Client, period time.Duration) {
   152  	update := func() {
   153  		start := time.Now()
   154  		if err := plugin.HandleAll(log, ghc, pa.Config()); err != nil {
   155  			log.WithError(err).Error("Error during periodic update of all PRs.")
   156  		}
   157  		log.WithField("duration", fmt.Sprintf("%v", time.Since(start))).Info("Periodic update complete.")
   158  	}
   159  
   160  	update()
   161  	for range time.Tick(period) {
   162  		update()
   163  	}
   164  }