hooksink

package module
v0.0.0-...-1c3c969 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 31, 2014 License: MIT Imports: 9 Imported by: 0

README

A Go based GitHub WebHook Listener

This is a simple package I created for my own purposes but I'm happy to share it with other people. At this point, I consider this alpha code. But my hope is to refine it into a solid chunk of reusable code for easily creating Go based servers for handling webhooks. I have several applications in mind for this and I suspect it will change significantly as I go.

This code does an HMAC check on the X-GitHub-Signature header to authenticate the request.

Example

The goal of this Go package is to allow users to easily create a GitHub webhook server and attach handlers to it. Different handlers can, for example, be attached to different URLs by specifying different paths. In this example, a simple logging handler is attached to /log:

package main

import "log"

import hs "github.com/xogeny/go-hooksink"

type Logger struct {}

func (l Logger) Push(msg hs.HubMessage) {
	log.Printf("PUSH: %v", msg);
}

func main() {
	h := hs.NewHookSink("<github webhook secret>");
	h.Add("/log", Logger{});
	h.Start();
o}

Acknowledgments

When I went looking for existing Go language libraries for creating servers to handle GitHub webhook calls, I came across dockerhub-webhook-listener by Brian Goff (@cpuguy83). I originally planned to reuse the code in his repository (which was mostly generic, but a little bit Docker specific) and refactor it into a more generic library. But I realized that once I started doing that there were so many things I wanted to change that I felt like it would be better to start from scratch.

But this work was inspired considerably by his design and his approach still supports several things that I currently don't (TLS, key checking, etc).

Documentation

Overview

Package hooksink providers a simple way to create servers that can be endpoints for the GitHub webhook API. It simplifies the process of associating code with different paths on the server by handling unmarshaling the GitHub payloads into native Go objects and also checking the GitHub HMAC signatures prior to invoking the handlers.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AuthFunction

type AuthFunction func(req *http.Request) bool

Type signature for function to authorize request. We include the entire request to give full access to headers, request path and query parameters.

See the Authenticate method on the HookSink object for more details.

type Config

type Config struct {
	// This indicates the address where we should listen for requests
	Addr string
}

These are configuration options that can be set after the HookSink object has been created but before it has been run.

type HeadCommit

type HeadCommit struct {
	Id string `json:"id"`
}

HeadCommit contains information about the commit that triggered the event

type HookSink

type HookSink struct {
	Config Config
	// contains filtered or unexported fields
}

This is the HookSink object. The only exported field is the Config object.

func NewHookSink

func NewHookSink(secret string) *HookSink

This creates a new HookSink object. Much of the work here is in setting up the underlying Martini server.

Note that you should definitely provide a (non-empty) secret here (and it should match the secret provided on the GitHub side). If you only provide an empty string (which happens, for example, during testing), then no GitHub signature checking can be performed.

func (*HookSink) Add

func (hs *HookSink) Add(path string, handler interface{})

The Add method allows a given handler to be associated with a specified path.

This method uses type assertions to determine what types of messages a handler is interested in. At the moment, only one particular kind of handler is supported (see PushHandler interface). If the given handler doesn't conform to any expected interface, a fatal error will be logged.

func (*HookSink) Authenticate

func (hs *HookSink) Authenticate(f AuthFunction)

The Authenticate function allows an authentication function to be associated with the endpoint.

We already automatically authenticate the GitHub push (i.e. that it came from GitHub using a shared secret). But this allows another potential level of checking if, for example, API keys are part of the URL. However, note that TLS is currently not supported so sending of sensitive information is a bad idea until TLS is supported for the endpoint.

func (HookSink) Handle

func (hs HookSink) Handle(res http.ResponseWriter, req *http.Request)

The Handle method is used during testing. It allows us to avoid having to call Start and instead allows us to dispatch a single request to the underlying Martini router. This avoids some of the awkwardness and complexity that comes from starting up and shutting down the Martini server.

func (*HookSink) Start

func (hs *HookSink) Start()

The Start method first off the underlying Martini server.

It uses the Config data to determine the address to listen on. If no address is specified, it uses the Martini defaults.

type Owner

type Owner struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

Owner provides information about the owner of a repository

type PushData

type PushData struct {
	PushedAt int `json:"pushed_at"`
	Images   []string
	Pusher   string
}

PushData contains information about the push itself

type PushHandler

type PushHandler interface {
	// The Push method gets the message from GitHub as well as
	// the values of any query parameters that were part of the
	// URL that was POSTED.
	Push(msg PushMessage, params map[string][]string)
}

This is the handler for a 'push' event. Not sure if I will ever support other event types (or whether that even makes sense), but I thought it was a good idea to give this handler a very specific name to protect for future enhancements, just in case.

type PushMessage

type PushMessage struct {
	// Repository contains information about the repository the push
	// was made to
	Repository Repository `json:"repository"`
	// HeadCommit contains information about the commit that triggered
	// the push
	HeadCommit HeadCommit `json:"head_commit"`
	// PushData contains information about the push itself
	PushData PushData `json:"push_data"`

	// After is the hash of the repository after the commit was completed
	After string `json:"after"`
}

PushMessage is a native Go representation of the (essential) information provided with a push event.

Quite a bit of information is missing. Pull requests filling in the rest of the data provided by the GitHub API are welcome. I just implemented what I needed for the moment.

type Repository

type Repository struct {
	Status    string
	RepoUrl   string `json:"repo_url"`
	Owner     Owner
	IsPrivate bool `json:"is_private"`
	Name      string
	StarCount int    `json:"star_count"`
	RepoName  string `json:"repo_name"`
	GitUrl    string `json:"git_url"`
}

Repository contains information about the repository where an event took place.

Directories

Path Synopsis
The package is can be used to build a trivial endpoint
The package is can be used to build a trivial endpoint

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL