harness-drone/handler/web/hook.go
2020-03-13 13:27:07 -04:00

135 lines
3.4 KiB
Go

// Copyright 2019 Drone IO, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package web
import (
"context"
"net/http"
"net/http/httputil"
"os"
"strconv"
"time"
"github.com/sirupsen/logrus"
"github.com/drone/drone/core"
"github.com/drone/drone/logger"
"github.com/drone/go-scm/scm"
)
// this is intended for local testing and instructs the handler
// to print the contents of the hook to stdout.
var debugPrintHook = false
func init() {
debugPrintHook, _ = strconv.ParseBool(
os.Getenv("DRONE_DEBUG_DUMP_HOOK"),
)
}
// HandleHook returns an http.HandlerFunc that handles webhooks
// triggered by source code management.
func HandleHook(
repos core.RepositoryStore,
builds core.BuildStore,
triggerer core.Triggerer,
parser core.HookParser,
) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if debugPrintHook {
// if DRONE_DEBUG_DUMP_HOOK=true print the http.Request
// headers and body to stdout.
out, _ := httputil.DumpRequest(r, true)
os.Stderr.Write(out)
}
hook, remote, err := parser.Parse(r, func(slug string) string {
namespace, name := scm.Split(slug)
repo, err := repos.FindName(r.Context(), namespace, name)
if err != nil {
logrus.WithFields(
logrus.Fields{
"namespace": namespace,
"name": name,
}).Debugln("cannot find repository")
return ""
}
return repo.Signer
})
if err != nil {
logrus.Debugf("cannot parse webhook: %s", err)
writeBadRequest(w, err)
return
}
if hook == nil {
logrus.Debugf("webhook ignored")
return
}
// TODO handle ping requests
// TODO consider using scm.Repository in the function callback.
log := logrus.WithFields(logrus.Fields{
"namespace": remote.Namespace,
"name": remote.Name,
"event": hook.Event,
"commit": hook.After,
})
log.Debugln("webhook parsed")
repo, err := repos.FindName(r.Context(), remote.Namespace, remote.Name)
if err != nil {
log = log.WithError(err)
log.Debugln("cannot find repository")
writeNotFound(w, err)
return
}
if !repo.Active {
log.Debugln("ignore webhook, repository inactive")
w.WriteHeader(200)
return
}
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5)
ctx = logger.WithContext(ctx, log)
defer cancel()
if hook.Event == core.EventPush && hook.Action == core.ActionDelete {
log.WithField("branch", hook.Target).Debugln("branch deleted")
builds.DeleteBranch(ctx, repo.ID, hook.Target)
w.WriteHeader(http.StatusNoContent)
return
}
if hook.Event == core.EventPullRequest && hook.Action == core.ActionClose {
log.WithField("ref", hook.Ref).Debugln("pull request closed")
builds.DeletePull(ctx, repo.ID, scm.ExtractPullRequest(hook.Ref))
w.WriteHeader(http.StatusNoContent)
return
}
builds, err := triggerer.Trigger(ctx, repo, hook)
if err != nil {
writeError(w, err)
return
}
writeJSON(w, builds, 200)
}
}