Add Dockerhub Webhook Plugin

This commit is contained in:
DazWilkin 2016-08-01 18:53:32 -07:00
parent 558afe7f04
commit 5a15d606a7
7 changed files with 278 additions and 13 deletions

View File

@ -0,0 +1,41 @@
# Docker Hub Webhook
Docker Hub can be configured to send events to Webhooks. The page describes the JSON format of the events:
https://docs.docker.com/docker-hub/webhooks/
Webhooks are configured by repository:
https://hub.docker.com/r/[[User]]/[Repository]]/~/settings/webhooks/
## Events
An event is generated by Docker Hub as the result of a docker push to the repository.
#### [`dockerhub_event`](https://nothing)
The measurement is called "dockerhub"
**Tags:**
* 'description' = `repository.description` string
* 'name' = `repository.name` string
* 'namespace' = `repository.namespace` string
* 'owner' = `repository.owner` string
* 'repo_name' = `repository.repo_name` string
* 'repo_url' = `repository.repo_url` string
* 'status' = `repository.status` string
* 'pusher' = `push_data.pusher` string
* 'tag' = `push_data.tag` string
**Fields:**
* 'comment_count' = `repository.comment_count` int
* 'date_created' = `repository.date_created` int64
* 'is_official' = `repository.is_official` bool
* 'is_private' = `repository.is_private` bool
* 'is_trusted' = `repository.is_trusted` bool
* 'star_count' = `repository.stat_count` int
* 'pushed_at' = `push_data.psuhed_at` int64

View File

@ -0,0 +1,50 @@
package dockerhub
import (
"encoding/json"
"io/ioutil"
"log"
"net/http"
"github.com/gorilla/mux"
"github.com/influxdata/telegraf"
)
type DockerhubWebhook struct {
Path string
acc telegraf.Accumulator
}
func (dhwh *DockerhubWebhook) Register(router *mux.Router, acc telegraf.Accumulator) {
router.HandleFunc(dhwh.Path, dhwh.eventHandler).Methods("POST")
log.Printf("Started '%s' on %s\n", meas, dhwh.Path)
dhwh.acc = acc
}
func (dhwh *DockerhubWebhook) eventHandler(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
data, err := ioutil.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
e, err := NewEvent(data, &DockerhubEvent{})
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
dh := e.NewMetric()
dhwh.acc.AddFields(meas, dh.Fields(), dh.Tags(), dh.Time())
w.WriteHeader(http.StatusOK)
}
func NewEvent(data []byte, event Event) (Event, error) {
err := json.Unmarshal(data, event)
if err != nil {
return nil, err
}
return event, nil
}

View File

@ -0,0 +1,65 @@
package dockerhub
import (
"fmt"
"math/rand"
"time"
)
// See https://docs.docker.com/docker-hub/webhooks/
const dockerid = "somerandomuser"
const hexBytes = "0123456789abcdef"
const imagename = "testimage"
const registry = "https://registry.hub.docker.com"
func init() {
rand.Seed(time.Now().UnixNano())
}
func RandStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = hexBytes[rand.Intn(len(hexBytes))]
}
return string(b)
}
func NewEventJSONEncoded() string {
return fmt.Sprintf(`{
"callback_url": %s,
"push_data": {
"images": [
%s,
%s,
],
"pushed_at" %v,
"pusher": %s
},
"repository": {
"comment_count": "0",
"date_created": %v,
"description: "",
"dockerfile": "",
"is_official": false,
"is_private": true,
"is_trusted": true,
"name": "testhook",
"namespace": "dazwilkin",
"owner": %s,
"repo_name": "dazwilkin/testwebhook",
"repo_url": %s,
"star_count": 0,
"status": "Active"
}
}`,
registry,
RandStringBytes(64),
RandStringBytes(64),
time.Now().Unix(),
dockerid,
time.Now().Unix(),
dockerid,
fmt.Sprintf("%s/%s", dockerid, imagename),
fmt.Sprintf("%s/u/%s/%s", registry, dockerid, imagename))
}

View File

@ -0,0 +1,81 @@
package dockerhub
import (
"fmt"
"log"
"time"
"github.com/influxdata/telegraf"
)
const meas = "dockerhub"
type Event interface {
NewMetric() telegraf.Metric
}
type PushData struct {
Images []string `json:"images"`
PushedAt int64 `json:"pushed_at"`
Pusher string `json:"pusher"`
Tag string `json:"tag"`
}
type Repository struct {
CommentCount int `json:"comment_count"`
DateCreated int64 `json:"date_created"`
Description string `json:"description"`
Dockerfile string `json:"dockerfile"`
FullDescription string `json:"full_description"`
IsOfficial bool `json:"is__official"`
IsPrivate bool `json:"is_private"`
IsTrusted bool `json:"is_trusted"`
Name string `json:"name"`
Namespace string `json:"namespace"`
Owner string `json:"owner"`
RepoName string `json:"repo_name"`
RepoURL string `json:"repo_url"`
StarCount int `json:"star_count"`
Status string `json:"status"`
}
type DockerhubEvent struct {
CallbackURL string `json:"callback_url"`
PushData PushData `json:"push_data"`
Repository Repository `json:"repository"`
}
func (dhe DockerhubEvent) String() string {
return fmt.Sprintf(`{
callback_url: %v
}`,
dhe.CallbackURL)
}
func (dhe DockerhubEvent) NewMetric() telegraf.Metric {
tags := map[string]string{
"description": dhe.Repository.Description,
"name": dhe.Repository.Name,
"namespace": dhe.Repository.Namespace,
"owner": dhe.Repository.Owner,
"pusher": dhe.PushData.Pusher,
"repo_name": dhe.Repository.RepoName,
"repo_url": dhe.Repository.RepoURL,
"status": dhe.Repository.Status,
"tag": dhe.PushData.Tag,
}
fields := map[string]interface{}{
"comment_count": dhe.Repository.CommentCount,
"date_created": dhe.Repository.DateCreated,
"is_official": dhe.Repository.IsOfficial,
"is_private": dhe.Repository.IsPrivate,
"is_trusted": dhe.Repository.IsTrusted,
"pushed_at": dhe.PushData.PushedAt,
"star_count": dhe.Repository.StarCount,
}
metric, err := telegraf.NewMetric(meas, tags, fields, time.Unix(dhe.PushData.PushedAt, 0))
if err != nil {
log.Fatalf("Failed to create %v event", meas)
}
return metric
}

View File

@ -0,0 +1,28 @@
package dockerhub
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/influxdata/telegraf/testutil"
)
func DockerhubWebhookRequest(event string, jsonString string, t *testing.T) {
var acc testutil.Accumulator
dhwh := &DockerhubWebhook{Path: "/dockerhub", acc: &acc}
req, _ := http.NewRequest("POST", "/dockerhub", strings.NewReader(jsonString))
w := httptest.NewRecorder()
dhwh.eventHandler(w, req)
if w.Code != http.StatusOK {
t.Errorf(
"POST"+event+" returned HTTP status code %v.\nExpected %v",
w.Code,
http.StatusOK)
}
}
func TestNewEvent(t *testing.T) {
DockerhubWebhookRequest("dockerhub_event", NewEventJSONEncoded(), t)
}

View File

@ -10,9 +10,9 @@ import (
"github.com/influxdata/telegraf" "github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/inputs" "github.com/influxdata/telegraf/plugins/inputs"
"github.com/influxdata/telegraf/plugins/inputs/webhooks/dockerhub"
"github.com/influxdata/telegraf/plugins/inputs/webhooks/github" "github.com/influxdata/telegraf/plugins/inputs/webhooks/github"
"github.com/influxdata/telegraf/plugins/inputs/webhooks/mandrill" "github.com/influxdata/telegraf/plugins/inputs/webhooks/mandrill"
"github.com/influxdata/telegraf/plugins/inputs/webhooks/particle"
"github.com/influxdata/telegraf/plugins/inputs/webhooks/rollbar" "github.com/influxdata/telegraf/plugins/inputs/webhooks/rollbar"
) )
@ -27,10 +27,10 @@ func init() {
type Webhooks struct { type Webhooks struct {
ServiceAddress string ServiceAddress string
Github *github.GithubWebhook Dockerhub *dockerhub.DockerhubWebhook
Mandrill *mandrill.MandrillWebhook Github *github.GithubWebhook
Particle *particle.ParticleWebhook Mandrill *mandrill.MandrillWebhook
Rollbar *rollbar.RollbarWebhook Rollbar *rollbar.RollbarWebhook
} }
func NewWebhooks() *Webhooks { func NewWebhooks() *Webhooks {
@ -42,15 +42,15 @@ func (wb *Webhooks) SampleConfig() string {
## Address and port to host Webhook listener on ## Address and port to host Webhook listener on
service_address = ":1619" service_address = ":1619"
[inputs.webhooks.dockerhub]
path = "/dockerhub"
[inputs.webhooks.github] [inputs.webhooks.github]
path = "/github" path = "/github"
[inputs.webhooks.mandrill] [inputs.webhooks.mandrill]
path = "/mandrill" path = "/mandrill"
[inputs.webhooks.particle]
path = "/particle"
[inputs.webhooks.rollbar] [inputs.webhooks.rollbar]
path = "/rollbar" path = "/rollbar"
` `

View File

@ -4,8 +4,8 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/influxdata/telegraf/plugins/inputs/webhooks/dockerhub"
"github.com/influxdata/telegraf/plugins/inputs/webhooks/github" "github.com/influxdata/telegraf/plugins/inputs/webhooks/github"
"github.com/influxdata/telegraf/plugins/inputs/webhooks/particle"
"github.com/influxdata/telegraf/plugins/inputs/webhooks/rollbar" "github.com/influxdata/telegraf/plugins/inputs/webhooks/rollbar"
) )
@ -16,14 +16,14 @@ func TestAvailableWebhooks(t *testing.T) {
t.Errorf("expected to %v.\nGot %v", expected, wb.AvailableWebhooks()) t.Errorf("expected to %v.\nGot %v", expected, wb.AvailableWebhooks())
} }
wb.Github = &github.GithubWebhook{Path: "/github"} wb.Dockerhub = &dockerhub.DockerhubWebhook{Path: "/dockerhub"}
expected = append(expected, wb.Github) expected = append(expected, wb.Dockerhub)
if !reflect.DeepEqual(wb.AvailableWebhooks(), expected) { if !reflect.DeepEqual(wb.AvailableWebhooks(), expected) {
t.Errorf("expected to be %v.\nGot %v", expected, wb.AvailableWebhooks()) t.Errorf("expected to be %v.\nGot %v", expected, wb.AvailableWebhooks())
} }
wb.Particle = &particle.ParticleWebhook{Path: "/particle"} wb.Github = &github.GithubWebhook{Path: "/github"}
expected = append(expected, wb.Particle) expected = append(expected, wb.Github)
if !reflect.DeepEqual(wb.AvailableWebhooks(), expected) { if !reflect.DeepEqual(wb.AvailableWebhooks(), expected) {
t.Errorf("expected to be %v.\nGot %v", expected, wb.AvailableWebhooks()) t.Errorf("expected to be %v.\nGot %v", expected, wb.AvailableWebhooks())
} }