github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/prow/hook/server.go (about)

     1  /*
     2  Copyright 2016 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 hook
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"net/http"
    24  
    25  	"github.com/sirupsen/logrus"
    26  
    27  	"k8s.io/test-infra/prow/config"
    28  	"k8s.io/test-infra/prow/github"
    29  	"k8s.io/test-infra/prow/plugins"
    30  )
    31  
    32  // Server implements http.Handler. It validates incoming GitHub webhooks and
    33  // then dispatches them to the appropriate plugins.
    34  type Server struct {
    35  	Plugins     *plugins.PluginAgent
    36  	ConfigAgent *config.Agent
    37  	HMACSecret  []byte
    38  	Metrics     *Metrics
    39  }
    40  
    41  // ServeHTTP validates an incoming webhook and puts it into the event channel.
    42  func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    43  	defer r.Body.Close()
    44  
    45  	// Header checks: It must be a POST with an event type and a signature.
    46  	if r.Method != http.MethodPost {
    47  		http.Error(w, "405 Method not allowed", http.StatusMethodNotAllowed)
    48  		return
    49  	}
    50  	eventType := r.Header.Get("X-GitHub-Event")
    51  	if eventType == "" {
    52  		http.Error(w, "400 Bad Request: Missing X-GitHub-Event Header", http.StatusBadRequest)
    53  		return
    54  	}
    55  	eventGUID := r.Header.Get("X-GitHub-Delivery")
    56  	if eventGUID == "" {
    57  		http.Error(w, "400 Bad Request: Missing X-GitHub-Delivery Header", http.StatusBadRequest)
    58  		return
    59  	}
    60  	sig := r.Header.Get("X-Hub-Signature")
    61  	if sig == "" {
    62  		http.Error(w, "403 Forbidden: Missing X-Hub-Signature", http.StatusForbidden)
    63  		return
    64  	}
    65  	contentType := r.Header.Get("content-type")
    66  	if contentType != "application/json" {
    67  		http.Error(w, "400 Bad Request: Hook only accepts content-type: application/json - please reconfigure this hook on GitHub", http.StatusBadRequest)
    68  		return
    69  	}
    70  
    71  	payload, err := ioutil.ReadAll(r.Body)
    72  	if err != nil {
    73  		http.Error(w, "500 Internal Server Error: Failed to read request body", http.StatusInternalServerError)
    74  		return
    75  	}
    76  
    77  	// Validate the payload with our HMAC secret.
    78  	if !github.ValidatePayload(payload, sig, s.HMACSecret) {
    79  		http.Error(w, "403 Forbidden: Invalid X-Hub-Signature", http.StatusForbidden)
    80  		return
    81  	}
    82  	fmt.Fprint(w, "Event received. Have a nice day.")
    83  
    84  	if err := s.demuxEvent(eventType, eventGUID, payload); err != nil {
    85  		logrus.WithError(err).Error("Error parsing event.")
    86  	}
    87  }
    88  
    89  func (s *Server) demuxEvent(eventType, eventGUID string, payload []byte) error {
    90  	l := logrus.WithFields(
    91  		logrus.Fields{
    92  			"event-type": eventType,
    93  			"event-GUID": eventGUID,
    94  		},
    95  	)
    96  	// We don't want to fail the webhook due to a metrics error.
    97  	if counter, err := s.Metrics.WebhookCounter.GetMetricWithLabelValues(eventType); err != nil {
    98  		l.WithError(err).Warn("Failed to get metric for eventType " + eventType)
    99  	} else {
   100  		counter.Inc()
   101  	}
   102  	switch eventType {
   103  	case "issues":
   104  		var i github.IssueEvent
   105  		if err := json.Unmarshal(payload, &i); err != nil {
   106  			return err
   107  		}
   108  		go s.handleIssueEvent(l, i)
   109  	case "issue_comment":
   110  		var ic github.IssueCommentEvent
   111  		if err := json.Unmarshal(payload, &ic); err != nil {
   112  			return err
   113  		}
   114  		go s.handleIssueCommentEvent(l, ic)
   115  	case "pull_request":
   116  		var pr github.PullRequestEvent
   117  		if err := json.Unmarshal(payload, &pr); err != nil {
   118  			return err
   119  		}
   120  		go s.handlePullRequestEvent(l, pr)
   121  	case "pull_request_review":
   122  		var re github.ReviewEvent
   123  		if err := json.Unmarshal(payload, &re); err != nil {
   124  			return err
   125  		}
   126  		go s.handleReviewEvent(l, re)
   127  	case "pull_request_review_comment":
   128  		var rce github.ReviewCommentEvent
   129  		if err := json.Unmarshal(payload, &rce); err != nil {
   130  			return err
   131  		}
   132  		go s.handleReviewCommentEvent(l, rce)
   133  	case "push":
   134  		var pe github.PushEvent
   135  		if err := json.Unmarshal(payload, &pe); err != nil {
   136  			return err
   137  		}
   138  		go s.handlePushEvent(l, pe)
   139  	case "status":
   140  		var se github.StatusEvent
   141  		if err := json.Unmarshal(payload, &se); err != nil {
   142  			return err
   143  		}
   144  		go s.handleStatusEvent(l, se)
   145  	}
   146  	return nil
   147  }