Skip to content

Add whereLabel* functions #143

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 17, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,12 +310,15 @@ For example, this is a JSON version of an emitted RuntimeContainer struct:
* *`trimPrefix $prefix $string`*: If `$prefix` is a prefix of `$string`, return `$string` with `$prefix` trimmed from the beginning. Otherwise, return `$string` unchanged.
* *`trimSuffix $suffix $string`*: If `$suffix` is a suffix of `$string`, return `$string` with `$suffix` trimmed from the end. Otherwise, return `$string` unchanged.
* *`trim $string`*: Removes whitespace from both sides of `$string`.
* *`when $condition $trueValue $falseValue`*: Returns the `$trueValue` when the `$condition` is `true` and the `$falseValue` otherwise
* *`where $items $fieldPath $value`*: Filters an array or slice based on the values of a field path expression `$fieldPath`. A field path expression is a dot-delimited list of map keys or struct member names specifying the path from container to a nested value. Returns an array of items having that value.
* *`whereExist $items $fieldPath`*: Like `where`, but returns only items where `$fieldPath` exists (is not nil).
* *`whereNotExist $items $fieldPath`*: Like `where`, but returns only items where `$fieldPath` does not exist (is nil).
* *`whereAny $items $fieldPath $sep $values`*: Like `where`, but the string value specified by `$fieldPath` is first split by `$sep` into a list of strings. The comparison value is a string slice with possible matches. Returns items which OR intersect these values.
* *`whereAll $items $fieldPath $sep $values`*: Like `whereAny`, except all `$values` must exist in the `$fieldPath`.
* *`when $condition $trueValue $falseValue`*: Returns the `$trueValue` when the `$condition` is `true` and the `$falseValue` otherwise
* *`whereLabelExists $containers $label`*: Filters a slice of containers based on the existence of the label `$label`.
* *`whereLabelDoesNotExist $containers $label`*: Filters a slice of containers based on the non-existence of the label `$label`.
* *`whereLabelValueMatches $containers $label $pattern`*: Filters a slice of containers based on the existence of the label `$label` with values matching the regular expression `$pattern`.

===

Expand Down
111 changes: 78 additions & 33 deletions template.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"os"
"path/filepath"
"reflect"
"regexp"
"strconv"
"strings"
"syscall"
Expand Down Expand Up @@ -103,7 +104,6 @@ func groupByKeys(entries interface{}, key string) ([]string, error) {

// Generalized where function
func generalizedWhere(funcName string, entries interface{}, key string, test func(interface{}) bool) (interface{}, error) {

entriesVal, err := getArrayValues(funcName, entries)

if err != nil {
Expand Down Expand Up @@ -169,6 +169,48 @@ func whereAll(entries interface{}, key, sep string, cmp []string) (interface{},
})
}

// generalized whereLabel function
func generalizedWhereLabel(funcName string, containers Context, label string, test func(string, bool) bool) (Context, error) {
selection := make([]*RuntimeContainer, 0)

for i := 0; i < len(containers); i++ {
container := containers[i]

value, ok := container.Labels[label]
if test(value, ok) {
selection = append(selection, container)
}
}

return selection, nil
}

// selects containers that have a particular label
func whereLabelExists(containers Context, label string) (Context, error) {
return generalizedWhereLabel("whereLabelExists", containers, label, func(_ string, ok bool) bool {
return ok
})
}

// selects containers that have don't have a particular label
func whereLabelDoesNotExist(containers Context, label string) (Context, error) {
return generalizedWhereLabel("whereLabelDoesNotExist", containers, label, func(_ string, ok bool) bool {
return !ok
})
}

// selects containers with a particular label whose value matches a regular expression
func whereLabelValueMatches(containers Context, label, pattern string) (Context, error) {
rx, err := regexp.Compile(pattern)
if err != nil {
return nil, err
}

return generalizedWhereLabel("whereLabelValueMatches", containers, label, func(value string, ok bool) bool {
return ok && rx.MatchString(value)
})
}

// hasPrefix returns whether a given string is a prefix of another string
func hasPrefix(prefix, s string) bool {
return strings.HasPrefix(s, prefix)
Expand Down Expand Up @@ -344,38 +386,41 @@ func when(condition bool, trueValue, falseValue interface{}) interface{} {

func newTemplate(name string) *template.Template {
tmpl := template.New(name).Funcs(template.FuncMap{
"closest": arrayClosest,
"coalesce": coalesce,
"contains": contains,
"dict": dict,
"dir": dirList,
"exists": exists,
"first": arrayFirst,
"groupBy": groupBy,
"groupByKeys": groupByKeys,
"groupByMulti": groupByMulti,
"hasPrefix": hasPrefix,
"hasSuffix": hasSuffix,
"json": marshalJson,
"intersect": intersect,
"keys": keys,
"last": arrayLast,
"replace": strings.Replace,
"parseBool": strconv.ParseBool,
"parseJson": unmarshalJson,
"queryEscape": url.QueryEscape,
"sha1": hashSha1,
"split": strings.Split,
"splitN": strings.SplitN,
"trimPrefix": trimPrefix,
"trimSuffix": trimSuffix,
"trim": trim,
"where": where,
"whereExist": whereExist,
"whereNotExist": whereNotExist,
"whereAny": whereAny,
"whereAll": whereAll,
"when": when,
"closest": arrayClosest,
"coalesce": coalesce,
"contains": contains,
"dict": dict,
"dir": dirList,
"exists": exists,
"first": arrayFirst,
"groupBy": groupBy,
"groupByKeys": groupByKeys,
"groupByMulti": groupByMulti,
"hasPrefix": hasPrefix,
"hasSuffix": hasSuffix,
"json": marshalJson,
"intersect": intersect,
"keys": keys,
"last": arrayLast,
"replace": strings.Replace,
"parseBool": strconv.ParseBool,
"parseJson": unmarshalJson,
"queryEscape": url.QueryEscape,
"sha1": hashSha1,
"split": strings.Split,
"splitN": strings.SplitN,
"trimPrefix": trimPrefix,
"trimSuffix": trimSuffix,
"trim": trim,
"when": when,
"where": where,
"whereExist": whereExist,
"whereNotExist": whereNotExist,
"whereAny": whereAny,
"whereAll": whereAll,
"whereLabelExists": whereLabelExists,
"whereLabelDoesNotExist": whereLabelDoesNotExist,
"whereLabelValueMatches": whereLabelValueMatches,
})
return tmpl
}
Expand Down
81 changes: 81 additions & 0 deletions template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,87 @@ func TestWhereRequires(t *testing.T) {
tests.run(t, "whereAll")
}

func TestWhereLabelExists(t *testing.T) {
containers := []*RuntimeContainer{
&RuntimeContainer{
Labels: map[string]string{
"com.example.foo": "foo",
"com.example.bar": "bar",
},
ID: "1",
},
&RuntimeContainer{
Labels: map[string]string{
"com.example.bar": "bar",
},
ID: "2",
},
}

tests := templateTestList{
{`{{whereLabelExists . "com.example.foo" | len}}`, containers, `1`},
{`{{whereLabelExists . "com.example.bar" | len}}`, containers, `2`},
{`{{whereLabelExists . "com.example.baz" | len}}`, containers, `0`},
}

tests.run(t, "whereLabelExists")
}

func TestWhereLabelDoesNotExist(t *testing.T) {
containers := []*RuntimeContainer{
&RuntimeContainer{
Labels: map[string]string{
"com.example.foo": "foo",
"com.example.bar": "bar",
},
ID: "1",
},
&RuntimeContainer{
Labels: map[string]string{
"com.example.bar": "bar",
},
ID: "2",
},
}

tests := templateTestList{
{`{{whereLabelDoesNotExist . "com.example.foo" | len}}`, containers, `1`},
{`{{whereLabelDoesNotExist . "com.example.bar" | len}}`, containers, `0`},
{`{{whereLabelDoesNotExist . "com.example.baz" | len}}`, containers, `2`},
}

tests.run(t, "whereLabelDoesNotExist")
}

func TestWhereLabelValueMatches(t *testing.T) {
containers := []*RuntimeContainer{
&RuntimeContainer{
Labels: map[string]string{
"com.example.foo": "foo",
"com.example.bar": "bar",
},
ID: "1",
},
&RuntimeContainer{
Labels: map[string]string{
"com.example.bar": "BAR",
},
ID: "2",
},
}

tests := templateTestList{
{`{{whereLabelValueMatches . "com.example.foo" "^foo$" | len}}`, containers, `1`},
{`{{whereLabelValueMatches . "com.example.foo" "\\d+" | len}}`, containers, `0`},
{`{{whereLabelValueMatches . "com.example.bar" "^bar$" | len}}`, containers, `1`},
{`{{whereLabelValueMatches . "com.example.bar" "^(?i)bar$" | len}}`, containers, `2`},
{`{{whereLabelValueMatches . "com.example.bar" ".*" | len}}`, containers, `2`},
{`{{whereLabelValueMatches . "com.example.baz" ".*" | len}}`, containers, `0`},
}

tests.run(t, "whereLabelValueMatches")
}

func TestHasPrefix(t *testing.T) {
const prefix = "tcp://"
const str = "tcp://127.0.0.1:2375"
Expand Down