36 Star 395 Fork 71

GVPrancher / rancher

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
github.go 5.77 KB
一键复制 编辑 原始数据 按行查看 历史
phli 提交于 2018-08-28 15:52 . filter pull request events
package drivers
import (
"crypto/hmac"
"crypto/sha1"
"encoding/hex"
"encoding/json"
"fmt"
"github.com/google/go-github/github"
"github.com/pkg/errors"
"github.com/rancher/rancher/pkg/pipeline/providers"
"github.com/rancher/rancher/pkg/pipeline/remote/model"
"github.com/rancher/rancher/pkg/pipeline/utils"
"github.com/rancher/rancher/pkg/ref"
"github.com/rancher/types/apis/project.cattle.io/v3"
"io/ioutil"
"net/http"
"strings"
)
const (
GithubWebhookHeader = "X-GitHub-Event"
githubSignatureHeader = "X-Hub-Signature"
githubPingEvent = "ping"
githubPushEvent = "push"
githubPREvent = "pull_request"
githubActionOpen = "opened"
githubActionSync = "synchronize"
githubStateOpen = "open"
)
type GithubDriver struct {
PipelineLister v3.PipelineLister
PipelineExecutions v3.PipelineExecutionInterface
SourceCodeCredentialLister v3.SourceCodeCredentialLister
}
func (g GithubDriver) Execute(req *http.Request) (int, error) {
var signature string
if signature = req.Header.Get(githubSignatureHeader); len(signature) == 0 {
return http.StatusUnprocessableEntity, errors.New("github webhook missing signature")
}
event := req.Header.Get(GithubWebhookHeader)
if event == githubPingEvent {
return http.StatusOK, nil
} else if event != githubPushEvent && event != githubPREvent {
return http.StatusUnprocessableEntity, fmt.Errorf("not trigger for event:%s", event)
}
pipelineID := req.URL.Query().Get("pipelineId")
ns, name := ref.Parse(pipelineID)
pipeline, err := g.PipelineLister.Get(ns, name)
if err != nil {
return http.StatusInternalServerError, err
}
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return http.StatusUnprocessableEntity, err
}
if match := verifyGithubWebhookSignature([]byte(pipeline.Status.Token), signature, body); !match {
return http.StatusUnprocessableEntity, errors.New("github webhook invalid signature")
}
if pipeline.Status.PipelineState == "inactive" {
return http.StatusUnavailableForLegalReasons, errors.New("Pipeline is not active")
}
info := &model.BuildInfo{}
if event == githubPushEvent {
info, err = parsePushPayload(body)
if err != nil {
return http.StatusUnprocessableEntity, err
}
} else if event == githubPREvent {
info, err = parsePullRequestPayload(body)
if err != nil {
return http.StatusUnprocessableEntity, err
}
}
if (info.Event == utils.WebhookEventPush && !pipeline.Spec.TriggerWebhookPush) ||
(info.Event == utils.WebhookEventTag && !pipeline.Spec.TriggerWebhookTag) ||
(info.Event == utils.WebhookEventPullRequest && !pipeline.Spec.TriggerWebhookPr) {
return http.StatusUnavailableForLegalReasons, fmt.Errorf("trigger for event '%s' is disabled", info.Event)
}
pipelineConfig, err := providers.GetPipelineConfigByBranch(g.SourceCodeCredentialLister, pipeline, info.Branch)
if err != nil {
return http.StatusInternalServerError, err
}
if pipelineConfig == nil {
//no pipeline config to run
return http.StatusOK, nil
}
if !utils.Match(pipelineConfig.Branch, info.Branch) {
return http.StatusUnavailableForLegalReasons, fmt.Errorf("skipped branch '%s'", info.Branch)
}
if _, err := utils.GenerateExecution(g.PipelineExecutions, pipeline, pipelineConfig, info); err != nil {
return http.StatusInternalServerError, err
}
return http.StatusOK, nil
}
func verifyGithubWebhookSignature(secret []byte, signature string, body []byte) bool {
const signaturePrefix = "sha1="
const signatureLength = 45 // len(SignaturePrefix) + len(hex(sha1))
if len(signature) != signatureLength || !strings.HasPrefix(signature, signaturePrefix) {
return false
}
actual := make([]byte, 20)
hex.Decode(actual, []byte(signature[5:]))
computed := hmac.New(sha1.New, secret)
computed.Write(body)
return hmac.Equal([]byte(computed.Sum(nil)), actual)
}
func parsePushPayload(raw []byte) (*model.BuildInfo, error) {
info := &model.BuildInfo{}
payload := &github.PushEvent{}
if err := json.Unmarshal(raw, payload); err != nil {
return nil, err
}
info.TriggerType = utils.TriggerTypeWebhook
info.Commit = payload.HeadCommit.GetID()
info.Ref = payload.GetRef()
info.HTMLLink = payload.HeadCommit.GetURL()
info.Message = payload.HeadCommit.GetMessage()
info.Email = payload.HeadCommit.Author.GetEmail()
info.AvatarURL = payload.Sender.GetAvatarURL()
info.Author = payload.Sender.GetLogin()
info.Sender = payload.Sender.GetLogin()
ref := payload.GetRef()
if strings.HasPrefix(ref, RefsTagPrefix) {
//git tag is triggered as a push event
info.Event = utils.WebhookEventTag
info.Branch = strings.TrimPrefix(ref, RefsTagPrefix)
} else {
info.Event = utils.WebhookEventPush
info.Branch = strings.TrimPrefix(payload.GetRef(), RefsBranchPrefix)
}
return info, nil
}
func parsePullRequestPayload(raw []byte) (*model.BuildInfo, error) {
info := &model.BuildInfo{}
payload := &github.PullRequestEvent{}
if err := json.Unmarshal(raw, payload); err != nil {
return nil, err
}
action := payload.GetAction()
if action != githubActionOpen && action != githubActionSync {
return nil, fmt.Errorf("no trigger for %s action", action)
}
if payload.PullRequest.GetState() != githubStateOpen {
return nil, fmt.Errorf("no trigger for closed pull requests")
}
info.TriggerType = utils.TriggerTypeWebhook
info.Event = utils.WebhookEventPullRequest
info.Branch = payload.PullRequest.Base.GetRef()
info.Ref = fmt.Sprintf("refs/pull/%d/head", payload.PullRequest.GetNumber())
info.HTMLLink = payload.PullRequest.GetHTMLURL()
info.Title = payload.PullRequest.GetTitle()
info.Message = payload.PullRequest.GetTitle()
info.Commit = payload.PullRequest.Head.GetSHA()
info.Author = payload.PullRequest.User.GetLogin()
info.AvatarURL = payload.PullRequest.User.GetAvatarURL()
info.Email = payload.PullRequest.User.GetEmail()
info.Sender = payload.Sender.GetLogin()
return info, nil
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/rancher/rancher.git
git@gitee.com:rancher/rancher.git
rancher
rancher
rancher
v2.1.2-rc4

搜索帮助

344bd9b3 5694891 D2dac590 5694891