Allow hooks file to be parsed as a template

Add a -template command line option that instructs webhook to parse the
hooks files as Go text templates.

Includes a `getenv` template func for retrieving environment variables.
This commit is contained in:
Cameron Moore 2017-08-15 18:02:33 -05:00
parent ba0adb117a
commit f5f04ddaa2
5 changed files with 157 additions and 11 deletions

View file

@ -1,6 +1,7 @@
package hook
import (
"bytes"
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
@ -18,6 +19,7 @@ import (
"regexp"
"strconv"
"strings"
"text/template"
"github.com/ghodss/yaml"
)
@ -544,8 +546,10 @@ func (h *Hook) ExtractCommandArgumentsForFile(headers, query, payload *map[strin
// Hooks is an array of Hook objects
type Hooks []Hook
// LoadFromFile attempts to load hooks from specified JSON file
func (h *Hooks) LoadFromFile(path string) error {
// LoadFromFile attempts to load hooks from the specified file, which
// can be either JSON or YAML. The asTemplate parameter causes the file
// contents to be parsed as a Go text/template prior to unmarshalling.
func (h *Hooks) LoadFromFile(path string, asTemplate bool) error {
if path == "" {
return nil
}
@ -557,6 +561,24 @@ func (h *Hooks) LoadFromFile(path string) error {
return e
}
if asTemplate {
funcMap := template.FuncMap{"getenv": getenv}
tmpl, err := template.New("hooks").Funcs(funcMap).Parse(string(file))
if err != nil {
return err
}
var buf bytes.Buffer
err = tmpl.Execute(&buf, nil)
if err != nil {
return err
}
file = buf.Bytes()
}
e = yaml.Unmarshal(file, h)
return e
}
@ -705,3 +727,8 @@ func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, bod
}
return false, nil
}
// getenv provides a template function to retrieve OS environment variables.
func getenv(s string) string {
return os.Getenv(s)
}

View file

@ -1,6 +1,7 @@
package hook
import (
"os"
"reflect"
"strings"
"testing"
@ -229,26 +230,55 @@ func TestHookExtractCommandArgumentsForEnv(t *testing.T) {
}
var hooksLoadFromFileTests = []struct {
path string
ok bool
path string
asTemplate bool
ok bool
}{
{"../hooks.json.example", true},
{"../hooks.yaml.example", true},
{"", true},
{"../hooks.json.example", false, true},
{"../hooks.yaml.example", false, true},
{"../hooks.json.tmpl.example", true, true},
{"../hooks.yaml.tmpl.example", true, true},
{"", false, true},
// failures
{"missing.json", false},
{"missing.json", false, false},
}
func TestHooksLoadFromFile(t *testing.T) {
secret := `foo"123`
os.Setenv("XXXTEST_SECRET", secret)
for _, tt := range hooksLoadFromFileTests {
h := &Hooks{}
err := h.LoadFromFile(tt.path)
err := h.LoadFromFile(tt.path, tt.asTemplate)
if (err == nil) != tt.ok {
t.Errorf(err.Error())
}
}
}
func TestHooksTemplateLoadFromFile(t *testing.T) {
secret := `foo"123`
os.Setenv("XXXTEST_SECRET", secret)
for _, tt := range hooksLoadFromFileTests {
if !tt.asTemplate {
continue
}
h := &Hooks{}
err := h.LoadFromFile(tt.path, tt.asTemplate)
if (err == nil) != tt.ok {
t.Errorf(err.Error())
continue
}
s := (*h.Match("webhook").TriggerRule.And)[0].Match.Secret
if s != secret {
t.Errorf("Expected secret of %q, got %q", secret, s)
}
}
}
var hooksMatchTests = []struct {
id string
hooks Hooks