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:
Florent Aide 2016-05-26 23:33:56 +02:00 committed by Adnan Hajdarević
parent e85e0592dd
commit 18b0573bc4
3 changed files with 113 additions and 41 deletions

4
.gitignore vendored
View file

@ -0,0 +1,4 @@
.idea
.cover
coverage
webhook

View file

@ -196,8 +196,9 @@ func ExtractParameterAsString(s string, params interface{}) (string, bool) {
// Argument type specifies the parameter key name and the source it should
// be extracted from
type Argument struct {
Source string `json:"source,omitempty"`
Name string `json:"name,omitempty"`
Source string `json:"source,omitempty"`
Name string `json:"name,omitempty"`
EnvName string `json:"envname,omitempty"`
}
// 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 {
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 {
return args, &ArgumentError{h.PassEnvironmentToCommand[i]}
}

View file

@ -80,7 +80,7 @@ var argumentGetTests = []struct {
func TestArgumentGet(t *testing.T) {
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)
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)
@ -94,14 +94,14 @@ var hookParseJSONParametersTests = []struct {
rheaders, rquery, rpayload *map[string]interface{}
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{"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{"header", "z"}}, &map[string]interface{}{"z": `{}`}, nil, nil, &map[string]interface{}{"z": map[string]interface{}{}}, 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{"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},
// failures
{[]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{"string", "z"}}, &map[string]interface{}{"z": ``}, nil, nil, &map[string]interface{}{"z": ``}, nil, nil, false}, // invalid argument source
{[]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{"string", "z", ""}}, &map[string]interface{}{"z": ``}, nil, nil, &map[string]interface{}{"z": ``}, nil, nil, false}, // invalid argument source
}
func TestHookParseJSONParameters(t *testing.T) {
@ -121,9 +121,9 @@ var hookExtractCommandArgumentsTests = []struct {
value []string
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
{"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) {
@ -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 {
path string
ok bool
@ -182,16 +243,16 @@ var matchRuleTests = []struct {
ok bool
err bool
}{
{"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},
{"payload-hash-sha1", "", "secret", "", Argument{"header", "a"}, &map[string]interface{}{"a": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, []byte(`{"a": "z"}`), 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},
{"payload-hash-sha1", "", "secret", "", Argument{"header", "a", ""}, &map[string]interface{}{"a": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, []byte(`{"a": "z"}`), true, false},
// failures
{"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},
{"value", "", "2", "X", Argument{"header", "a"}, &map[string]interface{}{"y": "z"}, nil, nil, []byte{}, false, false}, // reference invalid header
{"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},
{"value", "", "2", "X", Argument{"header", "a", ""}, &map[string]interface{}{"y": "z"}, nil, nil, []byte{}, false, false}, // reference invalid header
// errors
{"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
{"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
}
func TestMatchRule(t *testing.T) {
@ -215,8 +276,8 @@ var andRuleTests = []struct {
{
"(a=z, b=y): a=z && b=y",
AndRule{
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}},
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b"}}},
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
},
&map[string]interface{}{"a": "z", "b": "y"}, nil, nil, []byte{},
true, false,
@ -224,8 +285,8 @@ var andRuleTests = []struct {
{
"(a=z, b=Y): a=z && b=y",
AndRule{
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}},
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b"}}},
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
},
&map[string]interface{}{"a": "z", "b": "Y"}, nil, nil, []byte{},
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",
AndRule{
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}},
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
{
And: &AndRule{
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b"}}},
{Match: &MatchRule{"value", "", "", "x", Argument{"header", "c"}}},
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
{Match: &MatchRule{"value", "", "", "x", Argument{"header", "c", ""}}},
},
},
{
Or: &OrRule{
{Match: &MatchRule{"value", "", "", "w", Argument{"header", "d"}}},
{Match: &MatchRule{"value", "", "", "v", Argument{"header", "e"}}},
{Match: &MatchRule{"value", "", "", "w", Argument{"header", "d", ""}}},
{Match: &MatchRule{"value", "", "", "v", Argument{"header", "e", ""}}},
},
},
{
Not: &NotRule{
Match: &MatchRule{"value", "", "", "u", Argument{"header", "f"}},
Match: &MatchRule{"value", "", "", "u", Argument{"header", "f", ""}},
},
},
},
@ -260,7 +321,7 @@ var andRuleTests = []struct {
// failures
{
"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,
false, false,
},
@ -286,8 +347,8 @@ var orRuleTests = []struct {
{
"(a=z, b=X): a=z || b=y",
OrRule{
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}},
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b"}}},
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
},
&map[string]interface{}{"a": "z", "b": "X"}, nil, nil, []byte{},
true, false,
@ -295,8 +356,8 @@ var orRuleTests = []struct {
{
"(a=X, b=y): a=z || b=y",
OrRule{
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}},
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b"}}},
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
},
&map[string]interface{}{"a": "X", "b": "y"}, nil, nil, []byte{},
true, false,
@ -304,8 +365,8 @@ var orRuleTests = []struct {
{
"(a=Z, b=Y): a=z || b=y",
OrRule{
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}},
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b"}}},
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
},
&map[string]interface{}{"a": "Z", "b": "Y"}, nil, nil, []byte{},
false, false,
@ -314,7 +375,7 @@ var orRuleTests = []struct {
{
"invalid rule",
OrRule{
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}},
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
},
&map[string]interface{}{"y": "Z"}, nil, nil, []byte{},
false, false,
@ -338,8 +399,8 @@ var notRuleTests = []struct {
ok 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=z", NotRule{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false, 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},
}
func TestNotRule(t *testing.T) {