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