diff --git a/gowebhook.go b/gowebhook.go index ab02469..66b1881 100644 --- a/gowebhook.go +++ b/gowebhook.go @@ -1,173 +1,24 @@ package main import ( - "crypto/hmac" - "crypto/sha1" - "encoding/hex" - "encoding/json" - "fmt" - "io/ioutil" + "GoWebhooks/src/utils" + "GoWebhooks/src/config" + "GoWebhooks/src/server" "log" - "net/http" "os" - "os/exec" - "time" ) -var ( - targetSecret = "" - targetURL = "" - targetPort = "" - targetHost = "" - targetShell = "" - targetLogDir = "/etc/gowebhook/" - targetLogFile = "log" - queue []string - running = false -) - -func generateHashSignature(message string) string { - h := hmac.New(sha1.New, []byte(targetSecret)) - h.Write([]byte(message)) - return "sha1=" + hex.EncodeToString(h.Sum(nil)) -} - -func index(w http.ResponseWriter, r *http.Request) { - log2file(string(r.URL.Host)) - fmt.Fprintln(w, "{\"code\":200, \"description\":\"service running...\"}") -} - -func autoBuild(w http.ResponseWriter, r *http.Request) { - if (r.Method == "post" || r.Method == "POST") && r.URL.RequestURI() == targetURL { - if r.Header.Get("x-github-event") == "push" { - bodyContent, _ := ioutil.ReadAll(r.Body) - r.Body.Close() - signature := r.Header.Get("X-Hub-Signature") - if verifySignature(signature, string(bodyContent)) { - fmt.Fprintln(w, "{\"code\":200, \"description\":\"OK\"}") - log2file("验证通过,启动部署任务") - queue = append(queue, "1") - checkoutTaskStatus() - } else { - log2file("验证失败") - fmt.Fprintln(w, "{\"code\":200, \"error\":\"Signature error\"}") - } - } else { - fmt.Fprintln(w, "{\"code\":200, \"error\":\"Unmatch x-github-event\"}") - } - } else { - fmt.Fprintln(w, "{\"code\":200, \"error\":\"Error Method or unknow request url\"}") - } -} +func main() { -func loadConfig() { - result, err := ioutil.ReadFile("/etc/gowebhook/config") - if err == nil { - var f interface{} - json.Unmarshal(result, &f) - m := f.(map[string]interface{}) - localUrl, ok0 := m["requestUrl"].(string) - localSecret, ok1 := m["secret"].(string) - localHost, ok2 := m["host"].(string) - localPort, ok3 := m["port"].(string) - localShell, ok4 := m["script"].(string) - if ok0 && ok1 && ok2 && ok3 && ok4 { - targetURL = localUrl - targetSecret = localSecret - targetHost = localHost - targetPort = localPort - targetShell = localShell - } else { - log2file("Broken config.") - os.Exit(0) - } - } else { - log2file("Can not find config file...in \"/etc/gowebhook/config\"") + if errorString := config.LoadConfig(); errorString != "" { + utils.Log2file(errorString) os.Exit(0) } -} - -func checkoutTaskStatus() { - if running { - return - } - if len(queue) > 0 { - data := []string{""} - queue = data[:0:0] - go startTask() - } -} - -func startService() { - http.HandleFunc("/", index) - http.HandleFunc("/auto_build", autoBuild) - - log2file(fmt.Sprintf("service starting... %s:%s", targetHost, targetPort)) - listenErr := http.ListenAndServe(fmt.Sprintf("%s:%s", targetHost, targetPort), nil) + listenErr := server.StartService(config.GetHost(), config.GetPort()) if listenErr != nil { - log.Fatal("ListenAndServe: ", listenErr) - } -} - -func startTask() { - running = true - cmd := exec.Command("/bin/sh", targetShell) - _, err := cmd.Output() - if err == nil { - running = false - log2file("部署成功") - checkoutTaskStatus() - } else { - running = false - log2file(fmt.Sprintf("部署失败:\n %s", err)) - checkoutTaskStatus() + log.Fatal("ListenAndServer error: ", listenErr) } -} -func verifySignature(signature string, data string) bool { - return signature == generateHashSignature(string(data)) } -func log2file(content string) { - var err error - - if _, err := os.Stat(targetLogDir); err == nil { - fmt.Println("Dir exists", targetLogDir) - } else { - fmt.Println("Dir not exists, try to create...", targetLogDir) - err := os.MkdirAll(targetLogDir, 0711) - if err != nil { - fmt.Println("Error creating directory", targetLogDir) - fmt.Println("err:", err) - return - } - } - - if _, err := os.Stat(targetLogDir + targetLogFile); err == nil { - fmt.Println("Path exists", targetLogDir + targetLogFile) - } else { - fmt.Println("Path not exists, try to create...", targetLogDir + targetLogFile) - _, err := os.Create(targetLogDir + targetLogFile) - if err != nil { - fmt.Println("Error creating file", targetLogDir + targetLogFile) - fmt.Println("err:", err) - return - } - } - - f, err := os.OpenFile(targetLogDir + targetLogFile, os.O_APPEND|os.O_WRONLY, 0600) - if err == nil { - timeString := time.Now().Format("2006-01-02 15:04:05") - f.WriteString("[" + timeString + "]" + "" + content) - f.WriteString("\n") - } else { - fmt.Println("Open file faild...", targetLogDir + targetLogFile) - fmt.Println("err:", err) - } -} - -func main() { - loadConfig() - startService() -} diff --git a/src/config/config.go b/src/config/config.go new file mode 100644 index 0000000..663b072 --- /dev/null +++ b/src/config/config.go @@ -0,0 +1,57 @@ +package config + +import ( + "io/ioutil" + "encoding/json" +) + +var ( + config map[string]string +) + +func LoadConfig() string { + result, err := ioutil.ReadFile("/etc/gowebhook/config") + if err == nil { + var f interface{} + json.Unmarshal(result, &f) + m := f.(map[string]interface{}) + localUrl, ok0 := m["requestUrl"].(string) + localSecret, ok1 := m["secret"].(string) + localHost, ok2 := m["host"].(string) + localPort, ok3 := m["port"].(string) + localShell, ok4 := m["script"].(string) + if ok0 && ok1 && ok2 && ok3 && ok4 { + config = make(map[string]string) + config["url"] = localUrl + config["secret"] = localSecret + config["host"] = localHost + config["port"] = localPort + config["shell"] = localShell + return "" + } else { + return "Broken config." + } + } else { + return "Can not find config file...in \"/etc/gowebhook/config\"" + } +} + +func GetURL() string { + return config["url"] +} + +func GetSecret() string { + return config["secret"] +} + +func GetHost() string { + return config["host"] +} + +func GetPort() string { + return config["port"] +} + +func GetShell() string { + return config["shell"] +} \ No newline at end of file diff --git a/src/server/server.go b/src/server/server.go new file mode 100644 index 0000000..65513e5 --- /dev/null +++ b/src/server/server.go @@ -0,0 +1,46 @@ +package server + +import ( + "net/http" + "fmt" + "GoWebhooks/src/utils" + "io/ioutil" + "GoWebhooks/src/task" + "GoWebhooks/src/config" +) + +func StartService(address string, port string) error { + http.HandleFunc("/", index) + http.HandleFunc("/auto_build", autoBuild) + + utils.Log2file(fmt.Sprintf("service starting... %s:%s", address, port)) + return http.ListenAndServe(fmt.Sprintf("%s:%s", address, port), nil) +} + +func index(w http.ResponseWriter, r *http.Request) { + utils.Log2file(string(r.URL.Host)) + fmt.Fprintln(w, "{\"code\":200, \"description\":\"service running...\"}") +} + +func autoBuild(w http.ResponseWriter, r *http.Request) { + if (r.Method == "post" || r.Method == "POST") && r.URL.RequestURI() == config.GetURL() { + if r.Header.Get("x-github-event") == "push" { + bodyContent, _ := ioutil.ReadAll(r.Body) + r.Body.Close() + signature := r.Header.Get("X-Hub-Signature") + if utils.VerifySignature(signature, string(bodyContent), config.GetSecret()) { + fmt.Fprintln(w, "{\"code\":200, \"description\":\"OK\"}") + utils.Log2file("验证通过,启动部署任务") + task.AddNewTask(string(bodyContent)) + task.CheckoutTaskStatus() + } else { + utils.Log2file("验证失败") + fmt.Fprintln(w, "{\"code\":200, \"error\":\"Signature error\"}") + } + } else { + fmt.Fprintln(w, "{\"code\":200, \"error\":\"Unmatch x-github-event\"}") + } + } else { + fmt.Fprintln(w, "{\"code\":200, \"error\":\"Error Method or unknow request url\"}") + } +} \ No newline at end of file diff --git a/src/task/task.go b/src/task/task.go new file mode 100644 index 0000000..5fb2531 --- /dev/null +++ b/src/task/task.go @@ -0,0 +1,48 @@ +package task + +import ( + "os/exec" + "GoWebhooks/src/utils" + "fmt" + "GoWebhooks/src/config" +) + +var running = false +var queue []*structTaskQueue + +type structTaskQueue struct { + requestBodyString string +} + +func AddNewTask(bodyContent string) { + queue = append(queue, NewStructTaskQueue(bodyContent)) +} + +func NewStructTaskQueue(body string) *structTaskQueue { + return &structTaskQueue{body} +} + +func CheckoutTaskStatus() { + if running { + return + } + if len(queue) > 0 { + queue = queue[:0:0] + go startTask() + } +} + +func startTask() { + running = true + cmd := exec.Command("/bin/sh", config.GetShell()) + _, err := cmd.Output() + if err == nil { + running = false + utils.Log2file("部署成功") + CheckoutTaskStatus() + } else { + running = false + utils.Log2file(fmt.Sprintf("部署失败:\n %s", err)) + CheckoutTaskStatus() + } +} \ No newline at end of file diff --git a/src/utils/log2file.go b/src/utils/log2file.go new file mode 100644 index 0000000..a46a441 --- /dev/null +++ b/src/utils/log2file.go @@ -0,0 +1,48 @@ +package utils + +import ( + "os" + "fmt" + "time" +) + +var targetLogDir = "/etc/gowebhook/" +var targetLogFile = "log" + +func Log2file(content string) { + var err error + + if _, err := os.Stat(targetLogDir); err == nil { + + } else { + fmt.Println("Dir not exists, try to create...", targetLogDir) + err := os.MkdirAll(targetLogDir, 0711) + if err != nil { + fmt.Println("Error creating directory", targetLogDir) + fmt.Println("err:", err) + return + } + } + + if _, err := os.Stat(targetLogDir + targetLogFile); err == nil { + + } else { + fmt.Println("Path not exists, try to create...", targetLogDir + targetLogFile) + _, err := os.Create(targetLogDir + targetLogFile) + if err != nil { + fmt.Println("Error creating file", targetLogDir + targetLogFile) + fmt.Println("err:", err) + return + } + } + + f, err := os.OpenFile(targetLogDir + targetLogFile, os.O_APPEND|os.O_WRONLY, 0600) + if err == nil { + timeString := time.Now().Format("2006-01-02 15:04:05") + f.WriteString("[" + timeString + "]" + "" + content) + f.WriteString("\n") + } else { + fmt.Println("Open file faild...", targetLogDir + targetLogFile) + fmt.Println("err:", err) + } +} \ No newline at end of file diff --git a/src/utils/signature.go b/src/utils/signature.go new file mode 100644 index 0000000..99caf78 --- /dev/null +++ b/src/utils/signature.go @@ -0,0 +1,17 @@ +package utils + +import ( + "crypto/hmac" + "crypto/sha1" + "encoding/hex" +) + +func generateHashSignature(message string, secret string) string { + h := hmac.New(sha1.New, []byte(secret)) + h.Write([]byte(message)) + return "sha1=" + hex.EncodeToString(h.Sum(nil)) +} + +func VerifySignature(signature string, data string, secret string) bool { + return signature == generateHashSignature(string(data), secret) +} \ No newline at end of file