mirror of
https://github.com/adnanh/webhook.git
synced 2025-05-13 00:54:53 +00:00
Add support for naming env variables (#75)
* Adding ignore patterns * Adding support for env var naming * Fixed typo in docstring * Adding tests for the env var extraction w & w/o explicit naming * remove coverage script from ignore patterns * Adding the coverage script to help see which code is tested and which is not * remove coverage script from sources * Ignore coverage script from sources tree
This commit is contained in:
parent
e85e0592dd
commit
18b0573bc4
3 changed files with 113 additions and 41 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -0,0 +1,4 @@
|
||||||
|
.idea
|
||||||
|
.cover
|
||||||
|
coverage
|
||||||
|
webhook
|
13
hook/hook.go
13
hook/hook.go
|
@ -196,8 +196,9 @@ func ExtractParameterAsString(s string, params interface{}) (string, bool) {
|
||||||
// Argument type specifies the parameter key name and the source it should
|
// Argument type specifies the parameter key name and the source it should
|
||||||
// be extracted from
|
// be extracted from
|
||||||
type Argument struct {
|
type Argument struct {
|
||||||
Source string `json:"source,omitempty"`
|
Source string `json:"source,omitempty"`
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
|
EnvName string `json:"envname,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Argument method returns the value for the Argument's key name
|
// Get Argument method returns the value for the Argument's key name
|
||||||
|
@ -364,7 +365,13 @@ func (h *Hook) ExtractCommandArgumentsForEnv(headers, query, payload *map[string
|
||||||
|
|
||||||
for i := range h.PassEnvironmentToCommand {
|
for i := range h.PassEnvironmentToCommand {
|
||||||
if arg, ok := h.PassEnvironmentToCommand[i].Get(headers, query, payload); ok {
|
if arg, ok := h.PassEnvironmentToCommand[i].Get(headers, query, payload); ok {
|
||||||
args = append(args, EnvNamespace+h.PassEnvironmentToCommand[i].Name+"="+arg)
|
if h.PassEnvironmentToCommand[i].EnvName != "" {
|
||||||
|
// first try to use the EnvName if specified
|
||||||
|
args = append(args, EnvNamespace+h.PassEnvironmentToCommand[i].EnvName+"="+arg)
|
||||||
|
} else {
|
||||||
|
// then fallback on the name
|
||||||
|
args = append(args, EnvNamespace+h.PassEnvironmentToCommand[i].Name+"="+arg)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return args, &ArgumentError{h.PassEnvironmentToCommand[i]}
|
return args, &ArgumentError{h.PassEnvironmentToCommand[i]}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ var argumentGetTests = []struct {
|
||||||
|
|
||||||
func TestArgumentGet(t *testing.T) {
|
func TestArgumentGet(t *testing.T) {
|
||||||
for _, tt := range argumentGetTests {
|
for _, tt := range argumentGetTests {
|
||||||
a := Argument{tt.source, tt.name}
|
a := Argument{tt.source, tt.name, ""}
|
||||||
value, ok := a.Get(tt.headers, tt.query, tt.payload)
|
value, ok := a.Get(tt.headers, tt.query, tt.payload)
|
||||||
if ok != tt.ok || value != tt.value {
|
if ok != tt.ok || value != tt.value {
|
||||||
t.Errorf("failed to get {%q, %q}:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, ok:%#v}", tt.source, tt.name, tt.value, tt.ok, value, ok)
|
t.Errorf("failed to get {%q, %q}:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, ok:%#v}", tt.source, tt.name, tt.value, tt.ok, value, ok)
|
||||||
|
@ -94,14 +94,14 @@ var hookParseJSONParametersTests = []struct {
|
||||||
rheaders, rquery, rpayload *map[string]interface{}
|
rheaders, rquery, rpayload *map[string]interface{}
|
||||||
ok bool
|
ok bool
|
||||||
}{
|
}{
|
||||||
{[]Argument{Argument{"header", "a"}}, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, nil, true},
|
{[]Argument{Argument{"header", "a", ""}}, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, nil, true},
|
||||||
{[]Argument{Argument{"url", "a"}}, nil, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, true},
|
{[]Argument{Argument{"url", "a", ""}}, nil, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, true},
|
||||||
{[]Argument{Argument{"payload", "a"}}, nil, nil, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, true},
|
{[]Argument{Argument{"payload", "a", ""}}, nil, nil, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, true},
|
||||||
{[]Argument{Argument{"header", "z"}}, &map[string]interface{}{"z": `{}`}, nil, nil, &map[string]interface{}{"z": map[string]interface{}{}}, nil, nil, true},
|
{[]Argument{Argument{"header", "z", ""}}, &map[string]interface{}{"z": `{}`}, nil, nil, &map[string]interface{}{"z": map[string]interface{}{}}, nil, nil, true},
|
||||||
// failures
|
// failures
|
||||||
{[]Argument{Argument{"header", "z"}}, &map[string]interface{}{"z": ``}, nil, nil, &map[string]interface{}{"z": ``}, nil, nil, false}, // empty string
|
{[]Argument{Argument{"header", "z", ""}}, &map[string]interface{}{"z": ``}, nil, nil, &map[string]interface{}{"z": ``}, nil, nil, false}, // empty string
|
||||||
{[]Argument{Argument{"header", "y"}}, &map[string]interface{}{"X": `{}`}, nil, nil, &map[string]interface{}{"X": `{}`}, nil, nil, false}, // missing parameter
|
{[]Argument{Argument{"header", "y", ""}}, &map[string]interface{}{"X": `{}`}, nil, nil, &map[string]interface{}{"X": `{}`}, nil, nil, false}, // missing parameter
|
||||||
{[]Argument{Argument{"string", "z"}}, &map[string]interface{}{"z": ``}, nil, nil, &map[string]interface{}{"z": ``}, nil, nil, false}, // invalid argument source
|
{[]Argument{Argument{"string", "z", ""}}, &map[string]interface{}{"z": ``}, nil, nil, &map[string]interface{}{"z": ``}, nil, nil, false}, // invalid argument source
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHookParseJSONParameters(t *testing.T) {
|
func TestHookParseJSONParameters(t *testing.T) {
|
||||||
|
@ -121,9 +121,9 @@ var hookExtractCommandArgumentsTests = []struct {
|
||||||
value []string
|
value []string
|
||||||
ok bool
|
ok bool
|
||||||
}{
|
}{
|
||||||
{"test", []Argument{Argument{"header", "a"}}, &map[string]interface{}{"a": "z"}, nil, nil, []string{"test", "z"}, true},
|
{"test", []Argument{Argument{"header", "a", ""}}, &map[string]interface{}{"a": "z"}, nil, nil, []string{"test", "z"}, true},
|
||||||
// failures
|
// failures
|
||||||
{"fail", []Argument{Argument{"payload", "a"}}, &map[string]interface{}{"a": "z"}, nil, nil, []string{"fail", ""}, false},
|
{"fail", []Argument{Argument{"payload", "a", ""}}, &map[string]interface{}{"a": "z"}, nil, nil, []string{"fail", ""}, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHookExtractCommandArguments(t *testing.T) {
|
func TestHookExtractCommandArguments(t *testing.T) {
|
||||||
|
@ -136,6 +136,67 @@ func TestHookExtractCommandArguments(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Here we test the extraction of env variables when the user defined a hook
|
||||||
|
// with the "pass-environment-to-command" directive
|
||||||
|
// we test both cases where the name of the data is used as the name of the
|
||||||
|
// env key & the case where the hook definition sets the env var name to a
|
||||||
|
// fixed value using the envname construct like so::
|
||||||
|
// [
|
||||||
|
// {
|
||||||
|
// "id": "push",
|
||||||
|
// "execute-command": "bb2mm",
|
||||||
|
// "command-working-directory": "/tmp",
|
||||||
|
// "pass-environment-to-command":
|
||||||
|
// [
|
||||||
|
// {
|
||||||
|
// "source": "entire-payload",
|
||||||
|
// "envname": "PAYLOAD"
|
||||||
|
// },
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
var hookExtractCommandArgumentsForEnvTests = []struct {
|
||||||
|
exec string
|
||||||
|
args []Argument
|
||||||
|
headers, query, payload *map[string]interface{}
|
||||||
|
value []string
|
||||||
|
ok bool
|
||||||
|
}{
|
||||||
|
// successes
|
||||||
|
{
|
||||||
|
"test",
|
||||||
|
[]Argument{Argument{"header", "a", ""}},
|
||||||
|
&map[string]interface{}{"a": "z"}, nil, nil,
|
||||||
|
[]string{"HOOK_a=z"},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"test",
|
||||||
|
[]Argument{Argument{"header", "a", "MYKEY"}},
|
||||||
|
&map[string]interface{}{"a": "z"}, nil, nil,
|
||||||
|
[]string{"HOOK_MYKEY=z"},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
// failures
|
||||||
|
{
|
||||||
|
"fail",
|
||||||
|
[]Argument{Argument{"payload", "a", ""}},
|
||||||
|
&map[string]interface{}{"a": "z"}, nil, nil,
|
||||||
|
[]string{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHookExtractCommandArgumentsForEnv(t *testing.T) {
|
||||||
|
for _, tt := range hookExtractCommandArgumentsForEnvTests {
|
||||||
|
h := &Hook{ExecuteCommand: tt.exec, PassEnvironmentToCommand: tt.args}
|
||||||
|
value, err := h.ExtractCommandArgumentsForEnv(tt.headers, tt.query, tt.payload)
|
||||||
|
if (err == nil) != tt.ok || !reflect.DeepEqual(value, tt.value) {
|
||||||
|
t.Errorf("failed to extract args for env {cmd=%q, args=%v}:\nexpected %#v, ok: %v\ngot %#v, ok: %v", tt.exec, tt.args, tt.value, tt.ok, value, (err == nil))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var hooksLoadFromFileTests = []struct {
|
var hooksLoadFromFileTests = []struct {
|
||||||
path string
|
path string
|
||||||
ok bool
|
ok bool
|
||||||
|
@ -182,16 +243,16 @@ var matchRuleTests = []struct {
|
||||||
ok bool
|
ok bool
|
||||||
err bool
|
err bool
|
||||||
}{
|
}{
|
||||||
{"value", "", "", "z", Argument{"header", "a"}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, true, false},
|
{"value", "", "", "z", Argument{"header", "a", ""}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, true, false},
|
||||||
{"regex", "^z", "", "z", Argument{"header", "a"}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, true, false},
|
{"regex", "^z", "", "z", Argument{"header", "a", ""}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, true, false},
|
||||||
{"payload-hash-sha1", "", "secret", "", Argument{"header", "a"}, &map[string]interface{}{"a": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, []byte(`{"a": "z"}`), true, false},
|
{"payload-hash-sha1", "", "secret", "", Argument{"header", "a", ""}, &map[string]interface{}{"a": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, []byte(`{"a": "z"}`), true, false},
|
||||||
// failures
|
// failures
|
||||||
{"value", "", "", "X", Argument{"header", "a"}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false, false},
|
{"value", "", "", "X", Argument{"header", "a", ""}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false, false},
|
||||||
{"regex", "^X", "", "", Argument{"header", "a"}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false, false},
|
{"regex", "^X", "", "", Argument{"header", "a", ""}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false, false},
|
||||||
{"value", "", "2", "X", Argument{"header", "a"}, &map[string]interface{}{"y": "z"}, nil, nil, []byte{}, false, false}, // reference invalid header
|
{"value", "", "2", "X", Argument{"header", "a", ""}, &map[string]interface{}{"y": "z"}, nil, nil, []byte{}, false, false}, // reference invalid header
|
||||||
// errors
|
// errors
|
||||||
{"regex", "*", "", "", Argument{"header", "a"}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false, true}, // invalid regex
|
{"regex", "*", "", "", Argument{"header", "a", ""}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false, true}, // invalid regex
|
||||||
{"payload-hash-sha1", "", "secret", "", Argument{"header", "a"}, &map[string]interface{}{"a": ""}, nil, nil, []byte{}, false, true}, // invalid hmac
|
{"payload-hash-sha1", "", "secret", "", Argument{"header", "a", ""}, &map[string]interface{}{"a": ""}, nil, nil, []byte{}, false, true}, // invalid hmac
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMatchRule(t *testing.T) {
|
func TestMatchRule(t *testing.T) {
|
||||||
|
@ -215,8 +276,8 @@ var andRuleTests = []struct {
|
||||||
{
|
{
|
||||||
"(a=z, b=y): a=z && b=y",
|
"(a=z, b=y): a=z && b=y",
|
||||||
AndRule{
|
AndRule{
|
||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
||||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b"}}},
|
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"a": "z", "b": "y"}, nil, nil, []byte{},
|
&map[string]interface{}{"a": "z", "b": "y"}, nil, nil, []byte{},
|
||||||
true, false,
|
true, false,
|
||||||
|
@ -224,8 +285,8 @@ var andRuleTests = []struct {
|
||||||
{
|
{
|
||||||
"(a=z, b=Y): a=z && b=y",
|
"(a=z, b=Y): a=z && b=y",
|
||||||
AndRule{
|
AndRule{
|
||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
||||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b"}}},
|
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"a": "z", "b": "Y"}, nil, nil, []byte{},
|
&map[string]interface{}{"a": "z", "b": "Y"}, nil, nil, []byte{},
|
||||||
false, false,
|
false, false,
|
||||||
|
@ -234,22 +295,22 @@ var andRuleTests = []struct {
|
||||||
{
|
{
|
||||||
"(a=z, b=y, c=x, d=w=, e=X, f=X): a=z && (b=y && c=x) && (d=w || e=v) && !f=u",
|
"(a=z, b=y, c=x, d=w=, e=X, f=X): a=z && (b=y && c=x) && (d=w || e=v) && !f=u",
|
||||||
AndRule{
|
AndRule{
|
||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
||||||
{
|
{
|
||||||
And: &AndRule{
|
And: &AndRule{
|
||||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b"}}},
|
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
||||||
{Match: &MatchRule{"value", "", "", "x", Argument{"header", "c"}}},
|
{Match: &MatchRule{"value", "", "", "x", Argument{"header", "c", ""}}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Or: &OrRule{
|
Or: &OrRule{
|
||||||
{Match: &MatchRule{"value", "", "", "w", Argument{"header", "d"}}},
|
{Match: &MatchRule{"value", "", "", "w", Argument{"header", "d", ""}}},
|
||||||
{Match: &MatchRule{"value", "", "", "v", Argument{"header", "e"}}},
|
{Match: &MatchRule{"value", "", "", "v", Argument{"header", "e", ""}}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Not: &NotRule{
|
Not: &NotRule{
|
||||||
Match: &MatchRule{"value", "", "", "u", Argument{"header", "f"}},
|
Match: &MatchRule{"value", "", "", "u", Argument{"header", "f", ""}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -260,7 +321,7 @@ var andRuleTests = []struct {
|
||||||
// failures
|
// failures
|
||||||
{
|
{
|
||||||
"invalid rule",
|
"invalid rule",
|
||||||
AndRule{{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a"}}}},
|
AndRule{{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", ""}}}},
|
||||||
&map[string]interface{}{"y": "z"}, nil, nil, nil,
|
&map[string]interface{}{"y": "z"}, nil, nil, nil,
|
||||||
false, false,
|
false, false,
|
||||||
},
|
},
|
||||||
|
@ -286,8 +347,8 @@ var orRuleTests = []struct {
|
||||||
{
|
{
|
||||||
"(a=z, b=X): a=z || b=y",
|
"(a=z, b=X): a=z || b=y",
|
||||||
OrRule{
|
OrRule{
|
||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
||||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b"}}},
|
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"a": "z", "b": "X"}, nil, nil, []byte{},
|
&map[string]interface{}{"a": "z", "b": "X"}, nil, nil, []byte{},
|
||||||
true, false,
|
true, false,
|
||||||
|
@ -295,8 +356,8 @@ var orRuleTests = []struct {
|
||||||
{
|
{
|
||||||
"(a=X, b=y): a=z || b=y",
|
"(a=X, b=y): a=z || b=y",
|
||||||
OrRule{
|
OrRule{
|
||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
||||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b"}}},
|
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"a": "X", "b": "y"}, nil, nil, []byte{},
|
&map[string]interface{}{"a": "X", "b": "y"}, nil, nil, []byte{},
|
||||||
true, false,
|
true, false,
|
||||||
|
@ -304,8 +365,8 @@ var orRuleTests = []struct {
|
||||||
{
|
{
|
||||||
"(a=Z, b=Y): a=z || b=y",
|
"(a=Z, b=Y): a=z || b=y",
|
||||||
OrRule{
|
OrRule{
|
||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
||||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b"}}},
|
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"a": "Z", "b": "Y"}, nil, nil, []byte{},
|
&map[string]interface{}{"a": "Z", "b": "Y"}, nil, nil, []byte{},
|
||||||
false, false,
|
false, false,
|
||||||
|
@ -314,7 +375,7 @@ var orRuleTests = []struct {
|
||||||
{
|
{
|
||||||
"invalid rule",
|
"invalid rule",
|
||||||
OrRule{
|
OrRule{
|
||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"y": "Z"}, nil, nil, []byte{},
|
&map[string]interface{}{"y": "Z"}, nil, nil, []byte{},
|
||||||
false, false,
|
false, false,
|
||||||
|
@ -338,8 +399,8 @@ var notRuleTests = []struct {
|
||||||
ok bool
|
ok bool
|
||||||
err bool
|
err bool
|
||||||
}{
|
}{
|
||||||
{"(a=z): !a=X", NotRule{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a"}}}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, true, false},
|
{"(a=z): !a=X", NotRule{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", ""}}}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, true, false},
|
||||||
{"(a=z): !a=z", NotRule{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false, false},
|
{"(a=z): !a=z", NotRule{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNotRule(t *testing.T) {
|
func TestNotRule(t *testing.T) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue