Merge pull request #166 from gdubicki/add_CaptureCommandOutputOnError

Add option to capture output of failed commands
This commit is contained in:
Adnan Hajdarević 2017-11-13 19:24:53 +01:00 committed by GitHub
commit 514388e39e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 97 additions and 3 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@
.cover
coverage
webhook
/test/hookecho

View file

@ -9,6 +9,7 @@ Hooks are defined as JSON objects. Please note that in order to be considered va
* `response-message` - specifies the string that will be returned to the hook initiator
* `response-headers` - specifies the list of headers in format `{"name": "X-Example-Header", "value": "it works"}` that will be returned in HTTP response for the hook
* `include-command-output-in-response` - boolean whether webhook should wait for the command to finish and return the raw output as a response to the hook initiator. If the command fails to execute or encounters any errors while executing the response will result in 500 Internal Server Error HTTP status code, otherwise the 200 OK status code will be returned.
* `include-command-output-in-response-on-error` - boolean whether webhook should include command stdout & stderror as a response in failed executions. It only works if `include-command-output-in-response` is set to `true`.
* `parse-parameters-as-json` - specifies the list of arguments that contain JSON strings. These parameters will be decoded by webhook and you can access them like regular objects in rules and `pass-arguments-to-command`.
* `pass-arguments-to-command` - specifies the list of arguments that will be passed to the command. Check [Referencing request values page](Referencing-Request-Values) to see how to reference the values from the request. If you want to pass a static string value to your command you can specify it as
`{ "source": "string", "name": "argumentvalue" }`

View file

@ -384,6 +384,7 @@ type Hook struct {
ResponseMessage string `json:"response-message,omitempty"`
ResponseHeaders ResponseHeaders `json:"response-headers,omitempty"`
CaptureCommandOutput bool `json:"include-command-output-in-response,omitempty"`
CaptureCommandOutputOnError bool `json:"include-command-output-in-response-on-error,omitempty"`
PassEnvironmentToCommand []Argument `json:"pass-environment-to-command,omitempty"`
PassArgumentsToCommand []Argument `json:"pass-arguments-to-command,omitempty"`
PassFileToCommand []Argument `json:"pass-file-to-command,omitempty"`

View file

@ -6,6 +6,7 @@ import (
"fmt"
"os"
"strings"
"strconv"
)
func main() {
@ -23,4 +24,14 @@ func main() {
if len(env) > 0 {
fmt.Printf("env: %s\n", strings.Join(env, " "))
}
if (len(os.Args) > 1) && (strings.HasPrefix(os.Args[1], "exit=")) {
exit_code_str := os.Args[1][5:]
exit_code, err := strconv.Atoi(exit_code_str)
if err != nil {
fmt.Printf("Exit code %s not an int!", exit_code_str)
os.Exit(-1)
}
os.Exit(exit_code)
}
}

View file

@ -136,5 +136,49 @@
}
}
}
},
{
"id": "capture-command-output-on-success-not-by-default",
"pass-arguments-to-command": [
{
"source": "string",
"name": "exit=0"
}
],
"execute-command": "{{ .Hookecho }}"
},
{
"id": "capture-command-output-on-success-yes-with-flag",
"pass-arguments-to-command": [
{
"source": "string",
"name": "exit=0"
}
],
"execute-command": "{{ .Hookecho }}",
"include-command-output-in-response": true
},
{
"id": "capture-command-output-on-error-not-by-default",
"pass-arguments-to-command": [
{
"source": "string",
"name": "exit=1"
}
],
"execute-command": "{{ .Hookecho }}",
"include-command-output-in-response": true
},
{
"id": "capture-command-output-on-error-yes-with-extra-flag",
"pass-arguments-to-command": [
{
"source": "string",
"name": "exit=1"
}
],
"execute-command": "{{ .Hookecho }}",
"include-command-output-in-response": true,
"include-command-output-in-response-on-error": true
}
]

View file

@ -73,3 +73,27 @@
include-command-output-in-response: true
id: gitlab
command-working-directory: /
- id: capture-command-output-on-success-not-by-default
pass-arguments-to-command:
- source: string
name: exit=0
execute-command: '{{ .Hookecho }}'
- id: capture-command-output-on-success-yes-with-flag
pass-arguments-to-command:
- source: string
name: exit=0
execute-command: '{{ .Hookecho }}'
include-command-output-in-response: true
- id: capture-command-output-on-error-not-by-default
pass-arguments-to-command:
- source: string
name: exit=1
execute-command: '{{ .Hookecho }}'
include-command-output-in-response: true
- id: capture-command-output-on-error-yes-with-extra-flag
pass-arguments-to-command:
- source: string
name: exit=1
execute-command: '{{ .Hookecho }}'
include-command-output-in-response: true
include-command-output-in-response-on-error: true

View file

@ -282,9 +282,13 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
response, err := handleHook(matchedHook, rid, &headers, &query, &payload, &body)
if err != nil {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "Error occurred while executing the hook's command. Please check your logs for more details.")
if matchedHook.CaptureCommandOutputOnError {
fmt.Fprintf(w, response)
} else {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
fmt.Fprintf(w, "Error occurred while executing the hook's command. Please check your logs for more details.")
}
} else {
fmt.Fprintf(w, response)
}

View file

@ -51,7 +51,7 @@ func TestStaticParams(t *testing.T) {
}
// case 2: binary with spaces in its name
err = os.Symlink("/bin/true", "/tmp/with space")
err = os.Symlink("/bin/echo", "/tmp/with space")
if err != nil {
t.Fatalf("%v", err)
}
@ -613,4 +613,12 @@ env: HOOK_head_commit.timestamp=2013-03-12T08:14:29-07:00
{"empty payload", "bitbucket", nil, `{}`, false, http.StatusOK, `Hook rules were not satisfied.`},
// test with no configured http return code, should default to 200 OK
{"empty payload", "gitlab", nil, `{}`, false, http.StatusOK, `Hook rules were not satisfied.`},
// test capturing command output
{"don't capture output on success by default", "capture-command-output-on-success-not-by-default", nil, `{}`, false, http.StatusOK, ``},
{"capture output on success with flag set", "capture-command-output-on-success-yes-with-flag", nil, `{}`, false, http.StatusOK, `arg: exit=0
`},
{"don't capture output on error by default", "capture-command-output-on-error-not-by-default", nil, `{}`, false, http.StatusInternalServerError, `Error occurred while executing the hook's command. Please check your logs for more details.`},
{"capture output on error with extra flag set", "capture-command-output-on-error-yes-with-extra-flag", nil, `{}`, false, http.StatusInternalServerError, `arg: exit=1
`},
}