mirror of
https://github.com/ncarlier/webhookd.git
synced 2025-04-06 08:36:15 +00:00
feat: Add notification system and docker hook support.
This commit is contained in:
parent
dbdd9f5776
commit
474610e25b
28
assets/docker.json
Normal file
28
assets/docker.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"push_data":{
|
||||
"pushed_at":1385141110,
|
||||
"images":[
|
||||
"imagehash1",
|
||||
"imagehash2",
|
||||
"imagehash3"
|
||||
],
|
||||
"pusher":"username"
|
||||
},
|
||||
"repository":{
|
||||
"status":"Active",
|
||||
"description":"my docker repo that does cool things",
|
||||
"is_trusted":false,
|
||||
"full_description":"This is my full description",
|
||||
"repo_url":"https://registry.hub.docker.com/u/username/reponame/",
|
||||
"owner":"username",
|
||||
"is_official":false,
|
||||
"is_private":false,
|
||||
"name":"reponame",
|
||||
"namespace":"username",
|
||||
"star_count":1,
|
||||
"comment_count":1,
|
||||
"date_created":1370174400,
|
||||
"dockerfile":"my full dockerfile is listed here",
|
||||
"repo_name":"username/reponame"
|
||||
}
|
||||
}
|
4
scripts/docker/echo.sh
Executable file
4
scripts/docker/echo.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
|
||||
echo "docker echo: $@"
|
||||
|
|
@ -14,7 +14,7 @@ type BitbucketRecord struct {
|
|||
User string `json:"user"`
|
||||
}
|
||||
|
||||
func (r BitbucketRecord) GetGitURL() string {
|
||||
func (r BitbucketRecord) GetURL() string {
|
||||
return fmt.Sprintf("%s%s", r.BaseURL, r.Repository.URL)
|
||||
}
|
||||
|
||||
|
|
16
src/hooks/docker.go
Normal file
16
src/hooks/docker.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
package hooks
|
||||
|
||||
type DockerRecord struct {
|
||||
Repository struct {
|
||||
Name string `json:"repo_name"`
|
||||
URL string `json:"repo_url"`
|
||||
} `json:"repository"`
|
||||
}
|
||||
|
||||
func (r DockerRecord) GetURL() string {
|
||||
return r.Repository.URL
|
||||
}
|
||||
|
||||
func (r DockerRecord) GetName() string {
|
||||
return r.Repository.Name
|
||||
}
|
|
@ -5,7 +5,7 @@ import (
|
|||
)
|
||||
|
||||
type Record interface {
|
||||
GetGitURL() string
|
||||
GetURL() string
|
||||
GetName() string
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,8 @@ func RecordFactory(hookname string) (Record, error) {
|
|||
return new(BitbucketRecord), nil
|
||||
case "github":
|
||||
return new(GithubRecord), nil
|
||||
case "docker":
|
||||
return new(DockerRecord), nil
|
||||
default:
|
||||
return nil, errors.New("Unknown hookname.")
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ type GithubRecord struct {
|
|||
} `json:"repository"`
|
||||
}
|
||||
|
||||
func (r GithubRecord) GetGitURL() string {
|
||||
func (r GithubRecord) GetURL() string {
|
||||
return r.Repository.URL
|
||||
}
|
||||
|
||||
|
|
59
src/main.go
59
src/main.go
|
@ -2,11 +2,11 @@ package main
|
|||
|
||||
import (
|
||||
"./hooks"
|
||||
"./notifications"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
|
@ -16,40 +16,49 @@ var (
|
|||
laddr = flag.String("l", ":8080", "HTTP service address (e.g.address, ':8080')")
|
||||
)
|
||||
|
||||
type flushWriter struct {
|
||||
f http.Flusher
|
||||
w io.Writer
|
||||
type HookContext struct {
|
||||
Hook string
|
||||
Action string
|
||||
args []string
|
||||
}
|
||||
|
||||
func (fw *flushWriter) Write(p []byte) (n int, err error) {
|
||||
n, err = fw.w.Write(p)
|
||||
if fw.f != nil {
|
||||
fw.f.Flush()
|
||||
func Notify(text string, context *HookContext) {
|
||||
var subject = fmt.Sprintf("Action %s executed.", context.Action)
|
||||
var notifier, err = notifications.NotifierFactory()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
return
|
||||
if notifier == nil {
|
||||
log.Println("Notification provider not found.")
|
||||
return
|
||||
}
|
||||
notifier.Notify(text, subject)
|
||||
}
|
||||
|
||||
func RunScript(w http.ResponseWriter, hook string, action string, params ...string) {
|
||||
fw := flushWriter{w: w}
|
||||
if f, ok := w.(http.Flusher); ok {
|
||||
fw.f = f
|
||||
}
|
||||
scriptname := fmt.Sprintf("./scripts/%s/%s.sh", hook, action)
|
||||
func RunScript(w http.ResponseWriter, context *HookContext) {
|
||||
scriptname := fmt.Sprintf("./scripts/%s/%s.sh", context.Hook, context.Action)
|
||||
log.Println("Exec script: ", scriptname)
|
||||
cmd := exec.Command(scriptname, params...)
|
||||
cmd.Stdout = &fw
|
||||
cmd.Stderr = &fw
|
||||
cmd.Run()
|
||||
|
||||
out, err := exec.Command(scriptname, context.args...).Output()
|
||||
if err != nil {
|
||||
Notify(err.Error(), context)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
Notify(fmt.Sprintf("%s", out), context)
|
||||
fmt.Fprintf(w, "Action '%s' executed!", context.Action)
|
||||
}
|
||||
|
||||
func Handler(w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
hookname := params["hookname"]
|
||||
action := params["action"]
|
||||
context := new(HookContext)
|
||||
context.Hook = params["hookname"]
|
||||
context.Action = params["action"]
|
||||
|
||||
log.Println("Hook name: ", hookname)
|
||||
log.Println("Hook name: ", context.Hook)
|
||||
|
||||
var record, err = hooks.RecordFactory(hookname)
|
||||
var record, err = hooks.RecordFactory(context.Hook)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
return
|
||||
|
@ -62,7 +71,9 @@ func Handler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
RunScript(w, hookname, action, record.GetGitURL(), record.GetName())
|
||||
context.args = []string{record.GetURL(), record.GetName()}
|
||||
|
||||
RunScript(w, context)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
|
22
src/notifications/factory.go
Normal file
22
src/notifications/factory.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package notifications
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Notifier interface {
|
||||
Notify(text string, subject string)
|
||||
}
|
||||
|
||||
func NotifierFactory() (Notifier, error) {
|
||||
notifier := os.Getenv("APP_NOTIFIER")
|
||||
switch notifier {
|
||||
case "http":
|
||||
return NewHttpNotifier(), nil
|
||||
case "smtp":
|
||||
return NewSmtpNotifier(), nil
|
||||
default:
|
||||
return nil, errors.New("Unknown notification provider.")
|
||||
}
|
||||
}
|
48
src/notifications/http.go
Normal file
48
src/notifications/http.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
package notifications
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
)
|
||||
|
||||
type HttpNotifier struct {
|
||||
URL string
|
||||
From string
|
||||
To string
|
||||
}
|
||||
|
||||
func NewHttpNotifier() *HttpNotifier {
|
||||
notifier := new(HttpNotifier)
|
||||
notifier.URL = os.Getenv("APP_HTTP_NOTIFIER_URL")
|
||||
if notifier.URL == "" {
|
||||
log.Println("Unable to create HTTP notifier. APP_HTTP_NOTIFIER_URL not set.")
|
||||
return nil
|
||||
}
|
||||
notifier.From = os.Getenv("APP_NOTIFIER_FROM")
|
||||
if notifier.From == "" {
|
||||
notifier.From = "webhookd <noreply@nunux.org>"
|
||||
}
|
||||
notifier.To = os.Getenv("APP_NOTIFIER_TO")
|
||||
if notifier.To == "" {
|
||||
notifier.To = "hostmaster@nunux.org"
|
||||
}
|
||||
return notifier
|
||||
}
|
||||
|
||||
func (n HttpNotifier) Notify(text string, subject string) {
|
||||
log.Println("HTTP notification: ", subject)
|
||||
data := make(url.Values)
|
||||
data.Set("from", n.From)
|
||||
data.Set("to", n.To)
|
||||
data.Set("subject", subject)
|
||||
data.Set("text", text)
|
||||
|
||||
// Submit form
|
||||
resp, err := http.PostForm(n.URL, data)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
}
|
74
src/notifications/smtp.go
Normal file
74
src/notifications/smtp.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
package notifications
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/smtp"
|
||||
"os"
|
||||
)
|
||||
|
||||
type SmtpNotifier struct {
|
||||
Host string
|
||||
From string
|
||||
To string
|
||||
}
|
||||
|
||||
func NewSmtpNotifier() *SmtpNotifier {
|
||||
notifier := new(SmtpNotifier)
|
||||
notifier.Host = os.Getenv("APP_SMTP_NOTIFIER_HOST")
|
||||
if notifier.Host == "" {
|
||||
notifier.Host = "localhost:25"
|
||||
}
|
||||
notifier.From = os.Getenv("APP_NOTIFIER_FROM")
|
||||
if notifier.From == "" {
|
||||
notifier.From = "webhookd <noreply@nunux.org>"
|
||||
}
|
||||
notifier.To = os.Getenv("APP_NOTIFIER_TO")
|
||||
if notifier.To == "" {
|
||||
notifier.To = "hostmaster@nunux.org"
|
||||
}
|
||||
return notifier
|
||||
}
|
||||
|
||||
func (n SmtpNotifier) Notify(text string, subject string) {
|
||||
log.Println("SMTP notification: ", subject)
|
||||
// Connect to the remote SMTP server.
|
||||
c, err := smtp.Dial(n.Host)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Set the sender and recipient first
|
||||
if err := c.Mail(n.From); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if err := c.Rcpt(n.To); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Send the email body.
|
||||
wc, err := c.Data()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
_, err = fmt.Fprintf(wc, text)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
err = wc.Close()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Send the QUIT command and close the connection.
|
||||
err = c.Quit()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
7
start
Executable file
7
start
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
export APP_NOTIFIER="http"
|
||||
export APP_HTTP_NOTIFIER_URL="http://requestb.in/v9b229v9"
|
||||
|
||||
go run src/main.go
|
||||
|
Loading…
Reference in New Issue
Block a user