Merge pull request #107 from adnanh/development

2.6.0
This commit is contained in:
Adnan Hajdarević 2016-11-01 20:11:07 +01:00 committed by GitHub
commit b66216675a
2 changed files with 73 additions and 37 deletions

View file

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

View file

@ -21,7 +21,7 @@ import (
) )
const ( const (
version = "2.5.0" version = "2.6.0"
) )
var ( var (
@ -35,6 +35,7 @@ var (
secure = flag.Bool("secure", false, "use HTTPS instead of HTTP") secure = flag.Bool("secure", false, "use HTTPS instead of HTTP")
cert = flag.String("cert", "cert.pem", "path to the HTTPS certificate pem file") 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") 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 responseHeaders hook.ResponseHeaders
@ -51,6 +52,11 @@ func main() {
flag.Parse() flag.Parse()
if *justDisplayVersion {
fmt.Println("webhook version " + version)
os.Exit(0)
}
log.SetPrefix("[webhook] ") log.SetPrefix("[webhook] ")
log.SetFlags(log.Ldate | log.Ltime) log.SetFlags(log.Ldate | log.Ltime)
@ -191,12 +197,10 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
} }
// handle hook // handle hook
if err = matchedHook.ParseJSONParameters(&headers, &query, &payload); err != nil { if errors := matchedHook.ParseJSONParameters(&headers, &query, &payload); errors != nil {
msg := fmt.Sprintf("error parsing JSON parameters: %s", err) for _, err := range errors {
log.Printf(msg) log.Printf("error parsing JSON parameters: %s\n", err)
w.WriteHeader(http.StatusBadRequest) }
fmt.Fprintf(w, "Unable to parse JSON parameters.")
return
} }
var ok bool 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) { 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 := exec.Command(h.ExecuteCommand)
cmd.Dir = h.CommandWorkingDirectory cmd.Dir = h.CommandWorkingDirectory
cmd.Args, err = h.ExtractCommandArguments(headers, query, payload) cmd.Args, errors = h.ExtractCommandArguments(headers, query, payload)
if err != nil { if errors != nil {
log.Printf("error extracting command arguments: %s", err) for _, err := range errors {
log.Printf("error extracting command arguments: %s\n", err)
}
} }
var envs []string var envs []string
envs, err = h.ExtractCommandArgumentsForEnv(headers, query, payload) envs, errors = h.ExtractCommandArgumentsForEnv(headers, query, payload)
if err != nil {
log.Printf("error extracting command arguments for environment: %s", err) if errors != nil {
for _, err := range errors {
log.Printf("error extracting command arguments for environment: %s\n", err)
} }
}
cmd.Env = append(os.Environ(), envs...) 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) 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)