This commit is contained in:
Adnan Hajdarevic 2016-11-01 20:09:41 +01:00
parent e83d7029ff
commit ecbcf11153
2 changed files with 73 additions and 37 deletions

View file

@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"io/ioutil"
"net/textproto"
"reflect"
"regexp"
"strconv"
@ -18,6 +19,7 @@ import (
const (
SourceHeader string = "header"
SourceQuery string = "url"
SourceQueryAlias string = "query"
SourcePayload string = "payload"
SourceString string = "string"
SourceEntirePayload string = "entire-payload"
@ -205,11 +207,13 @@ type Argument struct {
// based on the Argument's source
func (ha *Argument) Get(headers, query, payload *map[string]interface{}) (string, bool) {
var source *map[string]interface{}
key := ha.Name
switch ha.Source {
case SourceHeader:
source = headers
case SourceQuery:
key = textproto.CanonicalMIMEHeaderKey(ha.Name)
case SourceQuery, SourceQueryAlias:
source = query
case SourcePayload:
source = payload
@ -242,7 +246,7 @@ func (ha *Argument) Get(headers, query, payload *map[string]interface{}) (string
}
if source != nil {
return ExtractParameterAsString(ha.Name, *source)
return ExtractParameterAsString(key, *source)
}
return "", false
@ -300,7 +304,9 @@ type Hook struct {
// ParseJSONParameters decodes specified arguments to JSON objects and replaces the
// string with the newly created object
func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface{}) error {
func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface{}) []error {
var errors = make([]error, 0)
for i := range h.JSONStringParameters {
if arg, ok := h.JSONStringParameters[i].Get(headers, query, payload); ok {
var newArg map[string]interface{}
@ -311,7 +317,8 @@ func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface
err := decoder.Decode(&newArg)
if err != nil {
return &ParseError{err}
errors = append(errors, &ParseError{err})
continue
}
var source *map[string]interface{}
@ -321,27 +328,38 @@ func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface
source = headers
case SourcePayload:
source = payload
case SourceQuery:
case SourceQuery, SourceQueryAlias:
source = query
}
if source != nil {
ReplaceParameter(h.JSONStringParameters[i].Name, source, newArg)
key := h.JSONStringParameters[i].Name
if h.JSONStringParameters[i].Source == SourceHeader {
key = textproto.CanonicalMIMEHeaderKey(h.JSONStringParameters[i].Name)
}
ReplaceParameter(key, source, newArg)
} else {
return &SourceError{h.JSONStringParameters[i]}
errors = append(errors, &SourceError{h.JSONStringParameters[i]})
}
} else {
return &ArgumentError{h.JSONStringParameters[i]}
errors = append(errors, &ArgumentError{h.JSONStringParameters[i]})
}
}
if len(errors) > 0 {
return errors
}
return nil
}
// ExtractCommandArguments creates a list of arguments, based on the
// PassArgumentsToCommand property that is ready to be used with exec.Command()
func (h *Hook) ExtractCommandArguments(headers, query, payload *map[string]interface{}) ([]string, error) {
func (h *Hook) ExtractCommandArguments(headers, query, payload *map[string]interface{}) ([]string, []error) {
var args = make([]string, 0)
var errors = make([]error, 0)
args = append(args, h.ExecuteCommand)
@ -350,19 +368,23 @@ func (h *Hook) ExtractCommandArguments(headers, query, payload *map[string]inter
args = append(args, arg)
} else {
args = append(args, "")
return args, &ArgumentError{h.PassArgumentsToCommand[i]}
errors = append(errors, &ArgumentError{h.PassArgumentsToCommand[i]})
}
}
if len(errors) > 0 {
return args, errors
}
return args, nil
}
// ExtractCommandArgumentsForEnv creates a list of arguments in key=value
// format, based on the PassEnvironmentToCommand property that is ready to be used
// with exec.Command().
func (h *Hook) ExtractCommandArgumentsForEnv(headers, query, payload *map[string]interface{}) ([]string, error) {
func (h *Hook) ExtractCommandArgumentsForEnv(headers, query, payload *map[string]interface{}) ([]string, []error) {
var args = make([]string, 0)
var errors = make([]error, 0)
for i := range h.PassEnvironmentToCommand {
if arg, ok := h.PassEnvironmentToCommand[i].Get(headers, query, payload); ok {
if h.PassEnvironmentToCommand[i].EnvName != "" {
@ -373,10 +395,14 @@ func (h *Hook) ExtractCommandArgumentsForEnv(headers, query, payload *map[string
args = append(args, EnvNamespace+h.PassEnvironmentToCommand[i].Name+"="+arg)
}
} else {
return args, &ArgumentError{h.PassEnvironmentToCommand[i]}
errors = append(errors, &ArgumentError{h.PassEnvironmentToCommand[i]})
}
}
if len(errors) > 0 {
return args, errors
}
return args, nil
}

View file

@ -21,20 +21,21 @@ import (
)
const (
version = "2.5.0"
version = "2.6.0"
)
var (
ip = flag.String("ip", "0.0.0.0", "ip the webhook should serve hooks on")
port = flag.Int("port", 9000, "port the webhook should serve hooks on")
verbose = flag.Bool("verbose", false, "show verbose output")
noPanic = flag.Bool("nopanic", false, "do not panic if hooks cannot be loaded when webhook is not running in verbose mode")
hotReload = flag.Bool("hotreload", false, "watch hooks file for changes and reload them automatically")
hooksFilePath = flag.String("hooks", "hooks.json", "path to the json file containing defined hooks the webhook should serve")
hooksURLPrefix = flag.String("urlprefix", "hooks", "url prefix to use for served hooks (protocol://yourserver:port/PREFIX/:hook-id)")
secure = flag.Bool("secure", false, "use HTTPS instead of HTTP")
cert = flag.String("cert", "cert.pem", "path to the HTTPS certificate pem file")
key = flag.String("key", "key.pem", "path to the HTTPS certificate private key pem file")
ip = flag.String("ip", "0.0.0.0", "ip the webhook should serve hooks on")
port = flag.Int("port", 9000, "port the webhook should serve hooks on")
verbose = flag.Bool("verbose", false, "show verbose output")
noPanic = flag.Bool("nopanic", false, "do not panic if hooks cannot be loaded when webhook is not running in verbose mode")
hotReload = flag.Bool("hotreload", false, "watch hooks file for changes and reload them automatically")
hooksFilePath = flag.String("hooks", "hooks.json", "path to the json file containing defined hooks the webhook should serve")
hooksURLPrefix = flag.String("urlprefix", "hooks", "url prefix to use for served hooks (protocol://yourserver:port/PREFIX/:hook-id)")
secure = flag.Bool("secure", false, "use HTTPS instead of HTTP")
cert = flag.String("cert", "cert.pem", "path to the HTTPS certificate pem file")
key = flag.String("key", "key.pem", "path to the HTTPS certificate private key pem file")
justDisplayVersion = flag.Bool("version", false, "display webhook version and quit")
responseHeaders hook.ResponseHeaders
@ -51,6 +52,11 @@ func main() {
flag.Parse()
if *justDisplayVersion {
fmt.Println("webhook version " + version)
os.Exit(0)
}
log.SetPrefix("[webhook] ")
log.SetFlags(log.Ldate | log.Ltime)
@ -191,12 +197,10 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
}
// handle hook
if err = matchedHook.ParseJSONParameters(&headers, &query, &payload); err != nil {
msg := fmt.Sprintf("error parsing JSON parameters: %s", err)
log.Printf(msg)
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "Unable to parse JSON parameters.")
return
if errors := matchedHook.ParseJSONParameters(&headers, &query, &payload); errors != nil {
for _, err := range errors {
log.Printf("error parsing JSON parameters: %s\n", err)
}
}
var ok bool
@ -249,21 +253,27 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
}
func handleHook(h *hook.Hook, headers, query, payload *map[string]interface{}, body *[]byte) (string, error) {
var err error
var errors []error
cmd := exec.Command(h.ExecuteCommand)
cmd.Dir = h.CommandWorkingDirectory
cmd.Args, err = h.ExtractCommandArguments(headers, query, payload)
if err != nil {
log.Printf("error extracting command arguments: %s", err)
cmd.Args, errors = h.ExtractCommandArguments(headers, query, payload)
if errors != nil {
for _, err := range errors {
log.Printf("error extracting command arguments: %s\n", err)
}
}
var envs []string
envs, err = h.ExtractCommandArgumentsForEnv(headers, query, payload)
if err != nil {
log.Printf("error extracting command arguments for environment: %s", err)
envs, errors = h.ExtractCommandArgumentsForEnv(headers, query, payload)
if errors != nil {
for _, err := range errors {
log.Printf("error extracting command arguments for environment: %s\n", err)
}
}
cmd.Env = append(os.Environ(), envs...)
log.Printf("executing %s (%s) with arguments %q and environment %s using %s as cwd\n", h.ExecuteCommand, cmd.Path, cmd.Args, envs, cmd.Dir)