Skip to content

Commit 53af4c6

Browse files
committed
Add additional template functions
closest, coalesce, first, groupByKeys and dir
1 parent 3abd5c0 commit 53af4c6

File tree

4 files changed

+91
-1
lines changed

4 files changed

+91
-1
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,14 @@ Within those templates, the object emitted by docker-gen will have [this structu
105105

106106
#### Functions
107107

108+
* *`closest $array $value`: Returns the longest matching substring in `$array` that matches `$value`
109+
* *`coalesce ...`*: Returns the first non-nil argument.
108110
* *`contains $map $key`*: Returns `true` if `$map` contains `$key`. Takes maps from `string` to `string`.
111+
* *`dir $path`: Returns an array of filenames in the specified `$path`.
109112
* *`exists $path`*: Returns `true` if `$path` refers to an existing file or directory. Takes a string.
113+
* *`first $array`*: Returns the first value of an array or nil if the arry is nil or empty.
110114
* *`groupBy $containers $fieldPath`*: Groups an array of `RuntimeContainer` instances 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, which must be a string. Returns a map from the value of the field path expression to an array of containers having that value. Containers that do not have a value for the field path in question are omitted.
115+
* *`groupByKeys $containers $fieldPath`*: Returns the same as `groupBy` but only returns the keys of the map.
111116
* *`groupByMulti $containers $fieldPath $sep`*: Like `groupBy`, but the string value specified by `$fieldPath` is first split by `$sep` into a list of strings. A container whose `$fieldPath` value contains a list of strings will show up in the map output under each of those strings.
112117
* *`split $string $sep`*: Splits `$string` into a slice of substrings delimited by `$sep`. Alias for [`strings.Split`](http://golang.org/pkg/strings/#Split)
113118
* *`replace $string $old $new $count`*: Replaces up to `$count` occurences of `$old` with `$new` in `$string`. Alias for [`strings.Replace`](http://golang.org/pkg/strings/#Replace)

docker-gen.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ func (c *Context) Env() map[string]string {
9393
env[parts[0]] = parts[1]
9494
}
9595
return env
96-
9796
}
9897

9998
func (c *ConfigFile) filterWatches() ConfigFile {

template.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func groupByMulti(entries []*RuntimeContainer, key, sep string) map[string][]*Ru
4343
return groups
4444
}
4545

46+
// groupBy groups a list of *RuntimeContainers by the path property key
4647
func groupBy(entries []*RuntimeContainer, key string) map[string][]*RuntimeContainer {
4748
groups := make(map[string][]*RuntimeContainer)
4849
for _, v := range entries {
@@ -54,6 +55,16 @@ func groupBy(entries []*RuntimeContainer, key string) map[string][]*RuntimeConta
5455
return groups
5556
}
5657

58+
// groupByKeys is the same as groupBy but only returns a list of keys
59+
func groupByKeys(entries []*RuntimeContainer, key string) []string {
60+
groups := groupBy(entries, key)
61+
ret := []string{}
62+
for k, _ := range groups {
63+
ret = append(ret, k)
64+
}
65+
return ret
66+
}
67+
5768
func contains(item map[string]string, key string) bool {
5869
if _, ok := item[key]; ok {
5970
return true
@@ -91,24 +102,81 @@ func marshalJson(input interface{}) (string, error) {
91102
return strings.TrimSuffix(buf.String(), "\n"), nil
92103
}
93104

105+
// arrayFirst returns first item in the array or nil if the
106+
// input is nil or empty
107+
func arrayFirst(input interface{}) interface{} {
108+
if input == nil {
109+
return nil
110+
}
111+
112+
arr := reflect.ValueOf(input)
113+
114+
if arr.Len() == 0 {
115+
return nil
116+
}
117+
118+
return arr.Index(0).Interface()
119+
}
120+
121+
// arrayLast returns last item in the array
94122
func arrayLast(input interface{}) interface{} {
95123
arr := reflect.ValueOf(input)
96124
return arr.Index(arr.Len() - 1).Interface()
97125
}
98126

127+
// arrayClosest find the longest matching substring in values
128+
// that matches input
129+
func arrayClosest(values []string, input string) string {
130+
best := ""
131+
for _, v := range values {
132+
if strings.Contains(input, v) && len(v) > len(best) {
133+
best = v
134+
}
135+
}
136+
return best
137+
}
138+
139+
// dirList returns a list of files in the specified path
140+
func dirList(path string) ([]string, error) {
141+
names := []string{}
142+
files, err := ioutil.ReadDir(path)
143+
if err != nil {
144+
return names, err
145+
}
146+
for _, f := range files {
147+
names = append(names, f.Name())
148+
}
149+
return names, nil
150+
}
151+
152+
// coalesce returns the first non nil argument
153+
func coalesce(input ...interface{}) interface{} {
154+
for _, v := range input {
155+
if v != nil {
156+
return v
157+
}
158+
}
159+
return nil
160+
}
161+
99162
func generateFile(config Config, containers Context) bool {
100163
templatePath := config.Template
101164
tmpl, err := template.New(filepath.Base(templatePath)).Funcs(template.FuncMap{
165+
"closest": arrayClosest,
166+
"coalesce": coalesce,
102167
"contains": contains,
103168
"exists": exists,
169+
"first": arrayFirst,
104170
"groupBy": groupBy,
105171
"groupByMulti": groupByMulti,
172+
"groupByKeys": groupByKeys,
106173
"split": strings.Split,
107174
"replace": strings.Replace,
108175
"dict": dict,
109176
"sha1": hashSha1,
110177
"json": marshalJson,
111178
"last": arrayLast,
179+
"dir": dirList,
112180
}).ParseFiles(templatePath)
113181
if err != nil {
114182
log.Fatalf("unable to parse template: %s", err)

template_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,21 @@ func TestJson(t *testing.T) {
183183
t.Fatal("Incorrect unmarshaled container count. Expected %d, got %d.", len(containers), len(decoded))
184184
}
185185
}
186+
187+
func TestArrayClosestExact(t *testing.T) {
188+
if arrayClosest([]string{"foo.bar.com", "bar.com"}, "foo.bar.com") != "foo.bar.com" {
189+
t.Fatal("Expected foo.bar.com")
190+
}
191+
}
192+
193+
func TestArrayClosestSubstring(t *testing.T) {
194+
if arrayClosest([]string{"foo.fo.com", "bar.com"}, "foo.bar.com") != "bar.com" {
195+
t.Fatal("Expected bar.com")
196+
}
197+
}
198+
199+
func TestArrayClosestNoMatch(t *testing.T) {
200+
if arrayClosest([]string{"foo.fo.com", "bip.com"}, "foo.bar.com") != "" {
201+
t.Fatal("Expected ''")
202+
}
203+
}

0 commit comments

Comments
 (0)