init: working hooks
This commit is contained in:
commit
056a4a963c
24
auth.go
Normal file
24
auth.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthHandler struct {
|
||||||
|
Handler http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *AuthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
AuthHeader := r.Header.Get("Authorization")
|
||||||
|
if AuthHeader != task.Auth {
|
||||||
|
log.Printf("Dropping request from %s.", r.RemoteAddr)
|
||||||
|
return // drop
|
||||||
|
}
|
||||||
|
l.Handler.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuth(handler http.Handler) AuthHandler {
|
||||||
|
return AuthHandler{handler}
|
||||||
|
}
|
101
hooker.go
Normal file
101
hooker.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Listen for git web hooks and then execute a script.
|
||||||
|
*/
|
||||||
|
|
||||||
|
type TaskDefErr struct {
|
||||||
|
message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *TaskDefErr) Error() string {
|
||||||
|
return e.message
|
||||||
|
}
|
||||||
|
|
||||||
|
type TaskManifest struct {
|
||||||
|
Owner string
|
||||||
|
RepoID uint64
|
||||||
|
Auth string
|
||||||
|
Command string // Path to executable
|
||||||
|
Lock *sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpServer http.Server
|
||||||
|
var task TaskManifest
|
||||||
|
|
||||||
|
func InitHTTPHandler(handler *http.ServeMux) *LoggingHTTPHandler {
|
||||||
|
|
||||||
|
auth := NewAuth(handler)
|
||||||
|
loggingHandler := NewLogger(&auth)
|
||||||
|
return &loggingHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get an env var, if not set, panic or fatal
|
||||||
|
func getEnv(name string) string {
|
||||||
|
varVal := os.Getenv(name)
|
||||||
|
if varVal == "" {
|
||||||
|
panic(fmt.Sprintf("required %s env variable is not set", name))
|
||||||
|
}
|
||||||
|
return varVal
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitTask() TaskManifest {
|
||||||
|
|
||||||
|
RepoID := getEnv("REPO_ID")
|
||||||
|
repoID, err := strconv.ParseUint(RepoID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
panic("REPO_ID is not a valid int")
|
||||||
|
}
|
||||||
|
|
||||||
|
return TaskManifest{
|
||||||
|
Command: getEnv("CMD"),
|
||||||
|
Owner: getEnv("OWNER"),
|
||||||
|
Auth: getEnv("AUTH"),
|
||||||
|
RepoID: repoID,
|
||||||
|
Lock: &sync.Mutex{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
TAG := "[ init ]"
|
||||||
|
l := func(msg string) {
|
||||||
|
log.Println(TAG, msg)
|
||||||
|
}
|
||||||
|
l("starting")
|
||||||
|
|
||||||
|
l("Validating Task")
|
||||||
|
task = InitTask()
|
||||||
|
|
||||||
|
l("Registering handlers")
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/", handleRoot)
|
||||||
|
|
||||||
|
handler := InitHTTPHandler(mux)
|
||||||
|
httpServer = http.Server{
|
||||||
|
ReadTimeout: time.Second * 5,
|
||||||
|
WriteTimeout: time.Second * 5,
|
||||||
|
Addr: ":4184",
|
||||||
|
Handler: handler,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
log.Println("Task", task)
|
||||||
|
log.Printf("Listening on %s", httpServer.Addr)
|
||||||
|
log.Fatal(httpServer.ListenAndServe())
|
||||||
|
log.Println("Server Stopped")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
94
job.go
Normal file
94
job.go
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Owner struct {
|
||||||
|
Login string `json:"login"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Repo struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ID uint64 `json:"id"`
|
||||||
|
Owner Owner `json:"owner"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JsonRoot struct {
|
||||||
|
Repo Repo `json:"repository"`
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleRoot(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != "POST" {
|
||||||
|
return // Drop
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Could not read request body", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var JsonRoot JsonRoot
|
||||||
|
err = json.Unmarshal(body, &JsonRoot); if err != nil {
|
||||||
|
log.Println("Can not Unmarshal body json")
|
||||||
|
http.Error(w,"Can not Unmarshal body json", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("JsonRoot: %v\n", JsonRoot)
|
||||||
|
|
||||||
|
if JsonRoot.Repo.ID != task.RepoID {
|
||||||
|
log.Printf(
|
||||||
|
"Repo ID sent: %d, Expecting: %d.",
|
||||||
|
JsonRoot.Repo.ID,
|
||||||
|
task.RepoID,
|
||||||
|
)
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if JsonRoot.Repo.Owner.Login != task.Owner {
|
||||||
|
log.Printf(
|
||||||
|
"Owner sent: %s, Expecting: %s",
|
||||||
|
JsonRoot.Repo.Owner.Login,
|
||||||
|
task.Owner,
|
||||||
|
)
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DeliveryUUID := findXDeliveryUUID(r)
|
||||||
|
if DeliveryUUID == "" {
|
||||||
|
log.Println("Could not find Delivery UUID")
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Starting New Job", DeliveryUUID)
|
||||||
|
go RunJob(&task, DeliveryUUID, log.Default())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func findXDeliveryUUID(r *http.Request) string {
|
||||||
|
|
||||||
|
var headerVal string
|
||||||
|
|
||||||
|
possibleHeaders := []string{
|
||||||
|
"X-Gitea-Delivery",
|
||||||
|
"X-GitHub-Delivery",
|
||||||
|
"X-Gogs-Delivery",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, head := range possibleHeaders {
|
||||||
|
headerVal = r.Header.Get(head)
|
||||||
|
if headerVal != "" { break }
|
||||||
|
}
|
||||||
|
return headerVal
|
||||||
|
|
||||||
|
}
|
85
job_dep.go
Normal file
85
job_dep.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is scrapped for later use.
|
||||||
|
Now make a simple, one task runner with params
|
||||||
|
defined as env vars.
|
||||||
|
*/
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type JobStatus uint64
|
||||||
|
|
||||||
|
const (
|
||||||
|
NEW = iota
|
||||||
|
RUNNING
|
||||||
|
FINISHED
|
||||||
|
FAILED
|
||||||
|
)
|
||||||
|
|
||||||
|
type Job interface {
|
||||||
|
Start()
|
||||||
|
Result() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tasks are defined via config and are used to run jobs
|
||||||
|
type Task struct {
|
||||||
|
RepoFullName string
|
||||||
|
Owner string
|
||||||
|
Secret string
|
||||||
|
command exec.Cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShellCommandJob struct {
|
||||||
|
JobStatus
|
||||||
|
Task
|
||||||
|
UUID string
|
||||||
|
ExitCode uint8
|
||||||
|
stdout string
|
||||||
|
err *error
|
||||||
|
TimeCreated time.Time
|
||||||
|
TimeStarted time.Time
|
||||||
|
TimeFinished time.Time // This is redundant but keep it for now
|
||||||
|
TimeConsumed time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Replace with task method to generate jobs
|
||||||
|
func NewShellJob(t Task, uuid string) *ShellCommandJob {
|
||||||
|
|
||||||
|
timeNow := time.Now().UTC()
|
||||||
|
job := ShellCommandJob{
|
||||||
|
JobStatus: NEW,
|
||||||
|
Task: t,
|
||||||
|
UUID: uuid,
|
||||||
|
TimeCreated: timeNow,
|
||||||
|
}
|
||||||
|
return &job
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *ShellCommandJob) Start() {
|
||||||
|
j.TimeStarted = time.Now().UTC()
|
||||||
|
stdout, err := j.command.Output()
|
||||||
|
if err != nil {
|
||||||
|
j.JobStatus = FAILED
|
||||||
|
}
|
||||||
|
j.TimeFinished = time.Now().UTC()
|
||||||
|
j.TimeConsumed = time.Since(j.TimeStarted)
|
||||||
|
j.stdout = string(stdout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *ShellCommandJob) Result() string {
|
||||||
|
return j.stdout
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *ShellCommandJob) String() string {
|
||||||
|
switch j.JobStatus {
|
||||||
|
case 0: return "NEW"
|
||||||
|
case 1: return "RUNNING"
|
||||||
|
case 2: return "FINISHED"
|
||||||
|
case 3: return "FAILD"
|
||||||
|
default: return "UNDEFINED"
|
||||||
|
}
|
||||||
|
}
|
28
logging.go
Normal file
28
logging.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LoggingHTTPHandler struct {
|
||||||
|
Handler http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggingHTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
t1 := time.Now().UTC()
|
||||||
|
|
||||||
|
l.Handler.ServeHTTP(w, r)
|
||||||
|
|
||||||
|
log.Println(
|
||||||
|
fmt.Sprintf("%s %s %s %v", r.RemoteAddr, r.Method, r.URL.Path, time.Since(t1)),
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLogger(handler http.Handler) LoggingHTTPHandler {
|
||||||
|
return LoggingHTTPHandler{handler}
|
||||||
|
}
|
61
runner.go
Normal file
61
runner.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Starts the task report it's exit status
|
||||||
|
// To Logger
|
||||||
|
func RunJob(task *TaskManifest, deliveryUUID string, logger *log.Logger) {
|
||||||
|
|
||||||
|
var stderrBuf, stdoutBuf bytes.Buffer
|
||||||
|
|
||||||
|
execuatable, args := CommandStrtoProgArgs(task.Command)
|
||||||
|
|
||||||
|
cmd := exec.Command(execuatable, args...)
|
||||||
|
|
||||||
|
cmd.Stdout = &stdoutBuf
|
||||||
|
cmd.Stderr = &stderrBuf
|
||||||
|
|
||||||
|
|
||||||
|
task.Lock.Lock()
|
||||||
|
err := cmd.Run()
|
||||||
|
task.Lock.Unlock()
|
||||||
|
if err != nil {
|
||||||
|
logger.Println("Exec reported error", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout, err := io.ReadAll(&stdoutBuf)
|
||||||
|
if err != nil {
|
||||||
|
logger.Printf("Could not read stdout from command for job %s\n", deliveryUUID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stderr, err := io.ReadAll(&stderrBuf)
|
||||||
|
if err != nil {
|
||||||
|
logger.Printf("Could not read stderr from command for job %s\n", deliveryUUID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Printf("stdout: %v", string(stdout))
|
||||||
|
logger.Printf("stderr: %v", string(stderr))
|
||||||
|
logger.Printf("Job: %s exited with code: %d", deliveryUUID, cmd.ProcessState.ExitCode())
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seperate executable from args in command string
|
||||||
|
func CommandStrtoProgArgs(cmd string) (Execuatbale string, Args []string) {
|
||||||
|
tokens := strings.Split(cmd, " ")
|
||||||
|
|
||||||
|
if len(tokens) == 1 {
|
||||||
|
return tokens[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens[0], tokens[1:]
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user