autogits/gitea-events-rabbitmq-publisher/main.go

429 lines
10 KiB
Go
Raw Normal View History

2024-08-13 16:42:20 +02:00
package main
import (
2024-08-24 13:32:39 +02:00
"encoding/json"
2024-08-15 10:47:59 +02:00
"flag"
2024-08-14 17:43:56 +02:00
"fmt"
2024-08-24 13:32:39 +02:00
"io"
2024-08-13 16:42:20 +02:00
"log"
"net/http"
2024-08-14 17:43:56 +02:00
"os"
2024-08-13 16:42:20 +02:00
"src.opensuse.org/autogits/common"
)
const (
2024-08-19 12:05:43 +02:00
ListenAddrDef = "[::1]:8002"
AppName = "rabbitmq-forwarder"
2024-08-13 16:42:20 +02:00
)
2024-08-15 10:47:59 +02:00
var DebugMode bool
2024-08-14 17:43:56 +02:00
func connectToRabbitMQ() {
host := os.Getenv("RABBITMQ_HOST")
username := os.Getenv("RABBITMQ_USERNAME")
password := os.Getenv("RABBITMQ_PASSWORD")
if len(host) == 0 || len(username) == 0 || len(password) == 0 {
2024-08-15 10:47:59 +02:00
fmt.Println("Missing RABBITMQ_HOST, RABBITMQ_USERNAME, RABBITMQ_PASSWORD")
2024-08-14 17:43:56 +02:00
os.Exit(1)
}
2024-08-15 10:47:59 +02:00
2024-08-14 17:58:22 +02:00
go ConnectToExchangeForPublish(host, username, password)
2024-08-14 17:43:56 +02:00
}
2024-08-24 16:46:57 +02:00
var id int
2024-08-24 18:37:08 +02:00
2024-08-24 16:46:57 +02:00
func dumpUnhandledData(reqType string, data []byte) {
id++
2024-08-24 18:37:08 +02:00
os.WriteFile("/tmp/unhandled-json-"+reqType+"-"+fmt.Sprint(id)+".json", data, 0600)
2024-08-24 16:46:57 +02:00
}
2024-08-24 21:39:50 +02:00
func parseRequestJSON(reqType string, data []byte) (org *common.Organization, extraAction string, err error) {
extraAction = ""
switch reqType {
2024-08-26 17:07:52 +02:00
case common.RequestType_CreateBrachTag, common.RequestType_DeleteBranchTag:
2024-08-24 21:39:50 +02:00
create := common.CreateWebhookEvent{}
if err = json.Unmarshal(data, &create); err != nil {
return
}
org = create.Repository.Owner
2024-08-26 17:07:52 +02:00
case common.RequestType_Fork:
2024-08-24 21:39:50 +02:00
fork := common.ForkWebhookEvent{}
if err = json.Unmarshal(data, &fork); err != nil {
return
}
org = fork.Forkee.Owner
2024-08-26 17:07:52 +02:00
case common.RequestType_Push:
2024-08-24 21:39:50 +02:00
push := common.PushRequest{}
if err = json.Unmarshal(data, &push); err != nil {
return
}
org = push.Repository.Owner
2024-08-26 17:07:52 +02:00
case common.RequestType_Repository:
2024-08-24 21:39:50 +02:00
repoAction := common.RepositoryAction{}
if err = json.Unmarshal(data, &repoAction); err != nil {
return
}
switch repoAction.Action {
case "created", "deleted":
break
default:
err = fmt.Errorf("Unknown repository webhook action type: %s", repoAction.Action)
return
}
org = repoAction.Organization
extraAction = repoAction.Action
2024-08-26 17:07:52 +02:00
case common.RequestType_Release:
2024-08-24 21:39:50 +02:00
release := common.ReleaseWebhookEvent{}
if err = json.Unmarshal(data, &release); err != nil {
return
}
switch release.Action {
case "published", "updated", "deleted":
break
default:
err = fmt.Errorf("Unknwon Release webhook action type: %s", release.Action)
return
}
org = release.Repository.Owner
extraAction = release.Action
2024-08-24 22:03:30 +02:00
2024-08-26 17:07:52 +02:00
case common.RequestType_Issue:
2024-08-24 22:03:30 +02:00
issue := common.IssueWebhookEvent{}
if err = json.Unmarshal(data, &issue); err != nil {
return
}
switch issue.Action {
case "opened", "closed", "reopened", "edited":
break
default:
err = fmt.Errorf("Unknown Issue webhook action type: %s", issue.Action)
return
}
org = issue.Repository.Owner
extraAction = issue.Action
2024-08-26 17:07:52 +02:00
case common.RequestType_IssueAssign:
2024-08-25 21:47:05 +02:00
issue := common.IssueWebhookEvent{}
if err = json.Unmarshal(data, &issue); err != nil {
return
}
switch issue.Action {
2024-08-25 21:48:53 +02:00
case "assigned", "unassigned":
2024-08-25 21:47:05 +02:00
break
default:
err = fmt.Errorf("Unknown Issue Assign webhook action type: %s", issue.Action)
return
}
org = issue.Repository.Owner
extraAction = issue.Action
2024-08-26 17:07:52 +02:00
case common.RequestType_IssueComment, common.RequestType_PRComment:
2024-08-25 21:57:54 +02:00
issue := common.IssueCommentWebhookEvent{}
if err = json.Unmarshal(data, &issue); err != nil {
return
}
switch issue.Action {
case "edited", "created", "deleted":
break
default:
2024-08-26 13:39:36 +02:00
err = fmt.Errorf("Unknown Issue/PR Comment webhook action type: %s", issue.Action)
2024-08-25 21:57:54 +02:00
return
}
org = issue.Repository.Owner
extraAction = issue.Action
2024-08-26 17:07:52 +02:00
case common.RequestType_IssueLabel:
2024-08-25 22:31:20 +02:00
issue := common.IssueWebhookEvent{}
if err = json.Unmarshal(data, &issue); err != nil {
return
}
switch issue.Action {
case "label_updated", "label_cleared":
break
default:
err = fmt.Errorf("Unknown Issue Assign webhook action type: %s", issue.Action)
return
}
org = issue.Repository.Owner
extraAction = issue.Action
2024-08-26 17:07:52 +02:00
case common.RequestType_IssueMilestone:
2024-08-25 22:31:20 +02:00
issue := common.IssueWebhookEvent{}
if err = json.Unmarshal(data, &issue); err != nil {
return
}
switch issue.Action {
case "milestoned", "demilestoned":
break
default:
err = fmt.Errorf("Unknown Issue Assign webhook action type: %s", issue.Action)
return
}
org = issue.Repository.Owner
extraAction = issue.Action
2024-08-26 17:07:52 +02:00
case common.RequestType_PR:
2024-08-25 22:47:44 +02:00
pr := common.PullRequestWebhookEvent{}
if err = json.Unmarshal(data, &pr); err != nil {
return
}
switch pr.Action {
case "opened", "closed", "reopened", "edited":
break
default:
err = fmt.Errorf("Unknown PR webhook action type: %s", pr.Action)
return
}
org = pr.Repository.Owner
extraAction = pr.Action
2024-08-25 22:31:20 +02:00
2024-08-26 12:37:50 +02:00
case "pull_request_label":
pr := common.PullRequestWebhookEvent{}
if err = json.Unmarshal(data, &pr); err != nil {
return
}
switch pr.Action {
case "label_updated", "label_cleared":
break
default:
err = fmt.Errorf("Unknown PR Label webhook action type: %s", pr.Action)
return
}
org = pr.Repository.Owner
extraAction = pr.Action
case "pull_request_milestone":
pr := common.PullRequestWebhookEvent{}
if err = json.Unmarshal(data, &pr); err != nil {
return
}
switch pr.Action {
case "milestoned", "demilestoned":
break
default:
err = fmt.Errorf("Unknown PR Milestone webhook action type: %s", pr.Action)
return
}
org = pr.Repository.Owner
extraAction = pr.Action
case "pull_request_assign":
issue := common.PullRequestWebhookEvent{}
if err = json.Unmarshal(data, &issue); err != nil {
return
}
switch issue.Action {
case "assigned", "unassigned":
break
default:
err = fmt.Errorf("Unknown PR Assign webhook action type: %s", issue.Action)
return
}
org = issue.Repository.Owner
extraAction = issue.Action
2024-08-25 21:47:05 +02:00
2024-08-26 13:06:44 +02:00
case "pull_request_review_request":
issue := common.PullRequestWebhookEvent{}
if err = json.Unmarshal(data, &issue); err != nil {
return
}
switch issue.Action {
case "review_requested", "review_request_removed":
break
default:
2024-08-26 13:39:36 +02:00
err = fmt.Errorf("Unknown PR Review Request webhook action type: %s", issue.Action)
2024-08-26 13:06:44 +02:00
return
}
org = issue.Repository.Owner
extraAction = issue.Action
2024-08-26 13:30:54 +02:00
case "pull_request_review_rejected", "pull_request_review_approved":
2024-08-26 13:39:36 +02:00
pr := common.PullRequestWebhookEvent{}
if err = json.Unmarshal(data, &pr); err != nil {
2024-08-26 13:30:54 +02:00
return
}
2024-08-26 13:39:36 +02:00
switch pr.Action {
2024-08-26 13:30:54 +02:00
case "reviewed":
break
default:
2024-08-26 13:39:36 +02:00
err = fmt.Errorf("Unknown PR Review webhook action type: %s", pr.Action)
2024-08-26 13:30:54 +02:00
return
}
2024-08-26 13:39:36 +02:00
org = pr.Repository.Owner
extraAction = ""
2024-08-26 13:23:30 +02:00
2024-08-26 17:07:52 +02:00
case common.RequestType_PR_sync:
2024-08-26 13:39:36 +02:00
pr := common.PullRequestWebhookEvent{}
if err = json.Unmarshal(data, &pr); err != nil {
return
}
switch pr.Action {
case "synchronized":
break
default:
err = fmt.Errorf("Unknown PR Sync webhook action type: %s", pr.Action)
return
}
org = pr.Repository.Owner
extraAction = ""
2024-08-26 13:23:30 +02:00
2024-08-26 14:09:23 +02:00
case "wiki":
wiki := common.WikiWebhookEvent{}
if err = json.Unmarshal(data, &wiki); err != nil {
return
}
switch wiki.Action {
case "created", "edited", "renamed", "deleted":
break
default:
err = fmt.Errorf("Unknown Wiki webhook action type: %s", wiki.Action)
return
}
org = wiki.Repository.Owner
extraAction = wiki.Action
2024-08-24 21:39:50 +02:00
default:
2024-08-26 14:09:23 +02:00
// TODO: package webhook
2024-08-24 21:39:50 +02:00
err = fmt.Errorf("Unknown webhook request type: %s", reqType)
}
return
}
2024-08-13 16:42:20 +02:00
func main() {
2024-08-19 12:05:43 +02:00
var listenAddr string
2024-08-15 10:47:59 +02:00
flag.BoolVar(&DebugMode, "debug", false, "enables debugging messages")
2024-08-19 12:05:43 +02:00
flag.StringVar(&listenAddr, "listen", ListenAddrDef, "HTTP listen socket address for webhook events")
2024-08-15 10:47:59 +02:00
flag.Parse()
2024-08-14 17:43:56 +02:00
connectToRabbitMQ()
2024-08-13 16:42:20 +02:00
2024-08-26 13:56:33 +02:00
http.HandleFunc("POST /rabbitmq-forwarder", func(res http.ResponseWriter, req *http.Request) {
2024-08-24 13:32:39 +02:00
if len(req.Header.Get("Content-Type")) == 0 ||
req.Header["Content-Type"][0] != "application/json" ||
req.Method != "POST" {
res.WriteHeader(http.StatusInternalServerError)
return
}
hdr := req.Header[common.GiteaRequestHeader]
if len(hdr) != 1 {
res.WriteHeader(http.StatusInternalServerError)
log.Printf("Multiple Gitea headers received. %#v\n", hdr)
if DebugMode {
log.Println(req.Header)
}
return
}
2024-08-24 16:46:57 +02:00
reqType := hdr[0]
2024-08-24 13:32:39 +02:00
data, err := io.ReadAll(req.Body)
if err != nil {
errorStr := fmt.Sprintf("error reading hook info: %v", err)
res.Header().Add("Content-Type", "plain/text")
res.Write([]byte(errorStr))
res.WriteHeader(http.StatusBadRequest)
if DebugMode {
log.Printf(errorStr)
}
}
if !json.Valid(data) {
if DebugMode {
log.Println("send invalid json request")
}
res.WriteHeader(http.StatusBadRequest)
}
2024-08-24 21:39:50 +02:00
org, extraAction, err := parseRequestJSON(reqType, data)
if err != nil {
2024-08-24 18:37:08 +02:00
res.WriteHeader(http.StatusBadRequest)
2024-08-24 22:03:30 +02:00
log.Printf("error parsing webhook %s JSON. err: %v", reqType, err)
2024-08-24 18:37:08 +02:00
if DebugMode {
dumpUnhandledData(reqType, data)
}
2024-08-26 13:56:33 +02:00
return
2024-08-24 16:46:57 +02:00
}
2024-08-26 13:56:33 +02:00
if org == nil {
res.WriteHeader(http.StatusBadRequest)
log.Printf("no `org` for message... type: %s", reqType)
if DebugMode {
dumpUnhandledData(reqType, data)
}
2024-08-24 22:03:30 +02:00
}
2024-08-24 13:32:39 +02:00
2024-08-26 13:56:33 +02:00
err = PublishActionMessage(org.Username, reqType, extraAction, data)
if err != nil {
errorStr := fmt.Sprintf("hook (%s) processing error: %v\n", reqType, err)
res.Header().Add("Content-Type", "plain/text")
res.Write([]byte(errorStr))
res.WriteHeader(http.StatusBadRequest)
2024-08-24 13:32:39 +02:00
2024-08-26 13:56:33 +02:00
if DebugMode {
log.Println(errorStr)
}
}
2024-08-24 13:32:39 +02:00
res.WriteHeader(http.StatusOK)
})
2024-08-14 17:43:56 +02:00
http.HandleFunc("POST /rabbitmq-forwarder/{Org}", func(res http.ResponseWriter, req *http.Request) {
2024-08-13 16:42:20 +02:00
if len(req.Header.Get("Content-Type")) == 0 ||
req.Header["Content-Type"][0] != "application/json" ||
req.Method != "POST" {
res.WriteHeader(http.StatusInternalServerError)
return
}
hdr := req.Header[common.GiteaRequestHeader]
if len(hdr) != 1 {
res.WriteHeader(http.StatusInternalServerError)
2024-08-15 10:47:59 +02:00
log.Printf("Multiple Gitea headers received. %#v\n", hdr)
if DebugMode {
log.Println(req.Header)
}
2024-08-13 16:42:20 +02:00
return
}
reqType := hdr[0]
2024-08-24 13:32:39 +02:00
data, err := io.ReadAll(req.Body)
if err != nil {
errorStr := fmt.Sprintf("error reading hook info: %v", err)
res.Header().Add("Content-Type", "plain/text")
res.Write([]byte(errorStr))
res.WriteHeader(http.StatusBadRequest)
if DebugMode {
log.Printf(errorStr)
}
}
err = PublishMessage(req.PathValue("Org"), reqType, data)
2024-08-13 16:42:20 +02:00
if err != nil {
2024-08-19 12:05:43 +02:00
errorStr := fmt.Sprintf("hook (%s) processing error: %v\n", reqType, err)
res.Header().Add("Content-Type", "plain/text")
res.Write([]byte(errorStr))
2024-08-13 16:42:20 +02:00
res.WriteHeader(http.StatusBadRequest)
2024-08-19 12:05:43 +02:00
if DebugMode {
log.Println(errorStr)
}
2024-08-13 16:42:20 +02:00
} else {
res.WriteHeader(http.StatusOK)
}
})
2024-08-19 12:05:43 +02:00
log.Fatal(http.ListenAndServe(listenAddr, nil))
2024-08-13 16:42:20 +02:00
}