Merge pull request #2 from jackspirou/master

Adding auth and debug options.
This commit is contained in:
Brad Rydzewski 2015-11-24 15:27:51 -08:00
commit 38877a0a7c
6 changed files with 180 additions and 27 deletions

View File

@ -1 +1 @@
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.Hsd15sfwSET0yXg92mosT1cs-1kiyW7VZlSZiWKkIu4EKDWnslBPZzbTKVi9OY4lXKJmXuAo38E3wtj5EvD7GA5azd090-Rnv-VOrQfXNmjZd9jxc7861IWQOFZb9XEDQZF6X2JM8fK_psSs6mrew9BjuOx6ws68h-zTl8ttcjHZC3Y7SZ5AjYj5i4T-Z0hJpLk2kwjCYF3SH599R3L3kZxDi6IcO9qW_Lj6zcBT5SttLgikUpvHwY409pbFBohl-AVhCZuDuyJPDns4n7jjWj4sELH4w-8dbc5oijzxI1FoxnUhXTRsrwNgfgCBAFM_umXGXLHAQlb2QzH2JHtB6g.rednhhAPc9V-KExp.qf0pSc_4pCgKIgK5T03yEmWz1yF5u2S0lSDBAvu7yOeCl469QBRIOxCv_RHImCG4tjOwYZjNaS8gaKVIoNNtxhKxC04Y2G7pxYDgNh2pU-sJOi05A1BaIbdIC209uK0r-mKkX_hfMDtNNWuOCyHXju4ugbNHwH64KvLzy4_RfFa0n9AOhpS1xNT3aa_eJvj0RQCfFs10BckWJoGlIfbS6VMyeeHeQEX6QWFSauxUwUPOAvrRlNjtNSxxVvNr1CbJso_8Cl0frD8DELaWxu3uKvLkSm2_GVMjPbvmZIqvcVxpxYnFMjwngQOAqnmiI-n_3LlO-dZb-Gf5AwN2F61PQDKXkdOcNiM4bNU_V11VKHP_BCJpVcaL6detQLNzHIuyU93zbw.fkefbX3B_-97eQdiQcyCYQ
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.Hsd15sfwSET0yXg92mosT1cs-1kiyW7VZlSZiWKkIu4EKDWnslBPZzbTKVi9OY4lXKJmXuAo38E3wtj5EvD7GA5azd090-Rnv-VOrQfXNmjZd9jxc7861IWQOFZb9XEDQZF6X2JM8fK_psSs6mrew9BjuOx6ws68h-zTl8ttcjHZC3Y7SZ5AjYj5i4T-Z0hJpLk2kwjCYF3SH599R3L3kZxDi6IcO9qW_Lj6zcBT5SttLgikUpvHwY409pbFBohl-AVhCZuDuyJPDns4n7jjWj4sELH4w-8dbc5oijzxI1FoxnUhXTRsrwNgfgCBAFM_umXGXLHAQlb2QzH2JHtB6g.rednhhAPc9V-KExp.qf0pSc_4pCgKIgK5T03yEmWz1yF5u2S0lSDBAvu7yOeCl469QBRIOxCv_RHImCG4tjOwYZjNaS8gaKVIoNNtxhKxC04Y2G7pxYDgNh2pU-sJOi05A1BaIbdIC209uK0r-mKkX_hfMDtNNWuOCyHXju4ugbNHwH64KvLzy4_RfFa0n9AOhpS1xNT3aa_eJvj0RQCfFs10BckWJoGlIfbS6VMyeeHeQEX6QWFSauxUwUPOAvrRlNjtNSxxVvNr1CbJso_8Cl0frD8DELaWxu3uKvLkSm2_GVMjPbvmZIqvcVxpxYnFMjwngQOAqnmiI-n_3LlO-dZb-Gf5AwN2F61PQDKXkdOcNiM4bNU_V11VKHP_BCJpVcaL6detQLNzHIuyU93zbw.fkefbX3B_-97eQdiQcyCYQ

75
DOCS.md
View File

@ -83,4 +83,77 @@ notify:
repo: {{.Repo.FullName}}
build: {{.Build.Number}}
commit: {{.Build.Commit}}
```
```
## Basic Authentication
>It is important to note that with HTTP Basic Authentication the provided username and password are not encrypted.
In some cases your webhook may need to authenticate with another service. You can set the basic `Authentication` header with a username and password. For these use cases we expose the following additional parameters:
* `auth` - Sets the request's `Authorization` header to use HTTP Basic Authentication with the provided username and password below.
* `username` - The username as a string.
* `password` - The password as a string.
Example configuration to include HTTP Basic Authentication:
```yaml
notify:
webhook:
method: POST
auth:
username: $$USERNAME
password: $$PASSWORD
urls:
- https://tower.example.com/...
content_type: application/yaml
template: >
repo: {{.Repo.FullName}}
build: {{.Build.Number}}
commit: {{.Build.Commit}}
```
## Debugging Webhooks
>If you have private variables that are encrypted and hidden in `.drone.sec`, remember that the `debug` flag may print out those sensitive values. Please use `dubug: true` wisely.
In some cases complicated webhooks may need debugging to ensure `urls`, `template`, `auth` and more a properly configured. For these use cases we expose the following `debug` parameter:
* `debug` - If `debug: true` it will print out each URL request and response information.
Example configuration to include the `debug` parameter:
```yaml
notify:
webhook:
debug: true
method: POST
auth:
username: $$TOWER_USER
password: $$TOWER_PASS
urls:
- http://tower.example.com/api/v1/job_templates/44/launch/
- http://tower.example.com/api/v1/job_templates/45/launch/
content_type: application/json
template: '{"name": "project.deploy","extra_vars": "{\"env\": \"dev\",\"git_branch\": \"{{ .Build.Branch }}\",\"hipchat_token\": \"$$HIPCHAT_TOKEN\"}"}'
```
Example of a debug print result:
```yaml
[debug] Webhook 1
URL: http://tower.example.com/api/v1/job_templates/44/launch/
METHOD: POST
HEADERS: map[Content-Type:[application/json] Authorization:[Basic EMfNB3fakB8EMfNB3fakB8==]]
REQUEST BODY: {"name": "project.deploy","extra_vars": "{\"env\": \"dev\",\"git_branch\": \"develop\",\"hipchat_token\": \"h1pchatT0k3n\"}"}
RESPONSE STATUS: 202 ACCEPTED
RESPONSE BODY: {"job": 236}
[debug] Webhook 2
URL: http://tower.example.com/api/v1/job_templates/45/launch/
METHOD: POST
HEADERS: map[Content-Type:[application/json] Authorization:[Basic EMfNB3fakB8EMfNB3fakB8==]]
REQUEST BODY: {"name": "project.deploy","extra_vars": "{\"env\": \"dev\",\"git_branch\": \"develop\",\"hipchat_token\": \"h1pchatT0k3n\"}"}
RESPONSE STATUS: 202 ACCEPTED
RESPONSE BODY: {"job": 406}
```

View File

@ -3,7 +3,7 @@
# CGO_ENABLED=0 go build -a -tags netgo
# docker build --rm=true -t plugins/drone-webhook .
FROM gliderlabs/alpine:3.1
FROM gliderlabs/alpine:3.2
RUN apk-install ca-certificates
ADD drone-webhook /bin/
ENTRYPOINT ["/bin/drone-webhook"]

View File

@ -1,5 +1,5 @@
# drone-webhook
Drone plugin for sending Webhook notifications by [@chromakode](https://github.com/chromakode).
Drone plugin for sending Webhook notifications.
## Overview
@ -25,7 +25,18 @@ This plugin is responsible for sending build notifications via Webhooks:
"author_email": "john.smith@gmail.com"
},
"vargs": {
"urls": [ "https://your.webhook/..." ]
"urls": ["https://your.webhook/..."],
"debug": true,
"auth": {
"username": "johnsmith",
"password": "secretPass"
},
"headers": {
"SomeHeader": "SomeHeaderValue"
},
"method": "POST",
"template": "{\"git_branch\": \"{{ .Build.Branch }}\"}",
"content_type": "application/json"
}
}
EOF
@ -63,7 +74,18 @@ docker run -i plugins/drone-webhook <<EOF
"author_email": "john.smith@gmail.com"
},
"vargs": {
"urls": [ "https://your.webhook/..." ]
"urls": ["https://your.webhook/..."],
"debug": true,
"auth": {
"username": "johnsmith",
"password": "secretPass"
},
"headers": {
"SomeHeader": "SomeHeaderValue"
},
"method": "POST",
"template": "{\"git_branch\": \"{{ .Build.Branch }}\"}",
"content_type": "application/json"
}
}
EOF

84
main.go
View File

@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
@ -14,26 +15,22 @@ import (
)
func main() {
// plugin settings
var repo = drone.Repo{}
var build = drone.Build{}
var vargs = struct {
Urls []string `json:"urls"`
Headers map[string]string `json:"header"`
Method string `json:"method"`
Template string `json:"template"`
ContentType string `json:"content_type"`
}{}
var vargs = Webhook{}
// set plugin parameters
plugin.Param("repo", &repo)
plugin.Param("build", &build)
plugin.Param("vargs", &vargs)
plugin.Parse()
// data structure
data := struct {
Repo drone.Repo `json:"repo"`
Build drone.Build `json:"build"`
}{repo, build}
// parse the parameters
if err := plugin.Parse(); err != nil {
fmt.Println(err)
os.Exit(1)
}
// set default values
if len(vargs.Method) == 0 {
@ -43,28 +40,40 @@ func main() {
vargs.ContentType = "application/json"
}
// data structure
data := struct {
Repo drone.Repo `json:"repo"`
Build drone.Build `json:"build"`
}{repo, build}
// creates the payload. by default the payload
// is the build details in json format, but a custom
// template may also be used.
var buf bytes.Buffer
if len(vargs.Template) == 0 {
json.NewEncoder(&buf).Encode(&data)
if err := json.NewEncoder(&buf).Encode(&data); err != nil {
fmt.Printf("Error encoding content template. %s\n", err)
os.Exit(1)
}
} else {
t, err := template.New("_").Parse(vargs.Template)
if err != nil {
fmt.Printf("Error parsing content template. %s\n", err)
os.Exit(1)
}
t.Execute(&buf, &data)
if err != nil {
if err := t.Execute(&buf, &data); err != nil {
fmt.Printf("Error executing content template. %s\n", err)
os.Exit(1)
}
}
// post payload to each url
for _, rawurl := range vargs.Urls {
// build and execute a request for each url.
// all auth, headers, method, template (payload),
// and content_type values will be applied to
// every webhook request.
for i, rawurl := range vargs.Urls {
uri, err := url.Parse(rawurl)
if err != nil {
@ -72,21 +81,54 @@ func main() {
os.Exit(1)
}
req, err := http.NewRequest(vargs.Method, uri.String(), &buf)
// vargs.Method defaults to POST, no need to check
b := buf.Bytes()
r := bytes.NewReader(b)
req, err := http.NewRequest(vargs.Method, uri.String(), r)
if err != nil {
fmt.Printf("Error creating http request. %s\n", err)
os.Exit(1)
}
// vargs.ContentType defaults to application/json, no need to check
req.Header.Set("Content-Type", vargs.ContentType)
for key, value := range vargs.Headers {
req.Header.Set(key, value)
}
// set basic auth if a user or user and pass is provided
if len(vargs.Auth.Username) > 0 {
if len(vargs.Auth.Password) > 0 {
req.SetBasicAuth(vargs.Auth.Username, vargs.Auth.Password)
} else {
req.SetBasicAuth(vargs.Auth.Username, "")
}
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Printf("Error executing http request. %s\n", err)
os.Exit(1)
}
resp.Body.Close()
defer resp.Body.Close()
// if debug is on or response status code is bad
if vargs.Debug || resp.StatusCode >= http.StatusBadRequest {
// read the response body
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// I do not think we need to os.Exit(1) if we are
// unable to read a http response body.
fmt.Printf("Error reading http response body. %s\n", err)
}
// debug/info print
if vargs.Debug {
fmt.Printf("[debug] Webhook %d\n URL: %s\n METHOD: %s\n HEADERS: %s\n REQUEST BODY: %s\n RESPONSE STATUS: %s\n RESPONSE BODY: %s\n", i+1, req.URL, req.Method, req.Header, string(b), resp.Status, string(body))
} else {
fmt.Printf("[info] Webhook %d\n URL: %s\n RESPONSE STATUS: %s\n RESPONSE BODY: %s\n", i+1, req.URL, resp.Status, string(body))
}
}
}
}

16
webhook.go Normal file
View File

@ -0,0 +1,16 @@
package main
type Webhook struct {
Urls []string `json:"urls"`
Debug bool `json:"debug"`
Auth BasicAuth `json:"auth"`
Headers map[string]string `json:"header"`
Method string `json:"method"`
Template string `json:"template"`
ContentType string `json:"content_type"`
}
type BasicAuth struct {
Username string `json:"username"`
Password string `json:"password"`
}