Skip to content

Commit b0cfcc3

Browse files
Updates in response to code review feedback
1 parent f329213 commit b0cfcc3

File tree

2 files changed

+110
-55
lines changed

2 files changed

+110
-55
lines changed

pkg/util/template.go

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@ import (
1818

1919
var (
2020
extraTemplateFuncs = template.FuncMap{
21-
"lookup": lookup,
22-
"toYaml": toYaml,
23-
"urlEncode": url.QueryEscape,
21+
"lookup": lookup,
22+
"pathLookup": pathLookup,
23+
"toYaml": toYaml,
24+
"urlEncode": url.QueryEscape,
2425
}
25-
26-
zeroValue = reflect.Value{}
2726
)
2827

2928
// ApplyTemplate runs golang templating on all files in the provided path,
@@ -220,36 +219,49 @@ func configMapEntriesGenerator(
220219
}
221220
}
222221

223-
// lookup does a dot-separated path lookup on the input struct or map. If the input or any of
224-
// its children on the targeted path are not a map or struct, it returns nil.
225-
func lookup(path string, input interface{}) interface{} {
222+
// lookup does a dot-separated path lookup on the input map. If a key on the path is
223+
// not found, it returns nil. If the input or any of its children on the lookup path is not
224+
// a map, it returns an error.
225+
func lookup(input interface{}, path string) (interface{}, error) {
226226
obj := reflect.ValueOf(input)
227-
228227
components := strings.Split(path, ".")
229228

230229
for i := 0; i < len(components); {
231-
component := components[i]
232-
233230
switch obj.Kind() {
234-
case reflect.Struct:
235-
obj = obj.FieldByName(component)
236-
i++
237231
case reflect.Map:
238-
obj = obj.MapIndex(reflect.ValueOf(component))
232+
obj = obj.MapIndex(reflect.ValueOf(components[i]))
239233
i++
240234
case reflect.Ptr, reflect.Interface:
235+
if obj.IsNil() {
236+
return nil, nil
237+
}
238+
241239
// Get the thing being pointed to or interfaced, don't advance index
242240
obj = obj.Elem()
243241
default:
244-
// Got an unexpected type
245-
return nil
242+
if obj.IsValid() {
243+
// An object was found, but it's not a map. Return an error.
244+
return nil, fmt.Errorf(
245+
"Tried to traverse a value that's not a map (kind=%s)",
246+
obj.Kind(),
247+
)
248+
}
249+
250+
// An intermediate key wasn't found
251+
return nil, nil
246252
}
247253
}
248254

249-
if obj == zeroValue {
250-
return nil
255+
if !obj.IsValid() {
256+
// The last key wasn't found
257+
return nil, nil
251258
}
252-
return obj.Interface()
259+
return obj.Interface(), nil
260+
}
261+
262+
// pathLookup is the same as lookup, but with the arguments flipped.
263+
func pathLookup(path string, input interface{}) (interface{}, error) {
264+
return lookup(input, path)
253265
}
254266

255267
func toYaml(input interface{}) (string, error) {

pkg/util/template_test.go

Lines changed: 78 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -137,44 +137,87 @@ func fileContents(t *testing.T, path string) string {
137137
return string(contents)
138138
}
139139

140-
type TestStruct struct {
141-
Key string
142-
Inner TestStructInner
143-
Inner2 *TestStructInner
144-
}
145-
type TestStructInner struct {
146-
Map map[string]interface{}
147-
}
148-
149140
func TestLookup(t *testing.T) {
150-
s := TestStruct{
151-
Key: "value0",
152-
Inner: TestStructInner{
153-
Map: map[string]interface{}{
154-
"key1": "value1",
155-
"key2": map[string]interface{}{
156-
"key3": map[string]interface{}{
157-
"key4": "value4",
158-
},
159-
"key5": 1234,
160-
},
141+
m := map[string]interface{}{
142+
"key1": "value1",
143+
"key2": map[string]interface{}{
144+
"key3": map[string]interface{}{
145+
"key4": "value4",
161146
},
147+
"key5": 1234,
162148
},
163-
Inner2: &TestStructInner{
164-
Map: map[string]interface{}{
165-
"key6": "value6",
166-
},
149+
"key6": nil,
150+
}
151+
152+
type testCase struct {
153+
input interface{}
154+
path string
155+
expectedResult interface{}
156+
expectErr bool
157+
}
158+
159+
testCases := []testCase{
160+
{
161+
input: m,
162+
path: "bad-key",
163+
expectedResult: nil,
164+
},
165+
{
166+
input: m,
167+
path: "",
168+
expectedResult: nil,
169+
},
170+
{
171+
input: nil,
172+
path: "key1",
173+
expectedResult: nil,
167174
},
175+
{
176+
input: "not a map",
177+
path: "key1",
178+
expectedResult: nil,
179+
expectErr: true,
180+
},
181+
{
182+
input: m,
183+
path: "key1",
184+
expectedResult: "value1",
185+
},
186+
{
187+
input: &m,
188+
path: "key1",
189+
expectedResult: "value1",
190+
},
191+
{
192+
input: m,
193+
path: "key1.not-a-map",
194+
expectedResult: nil,
195+
expectErr: true,
196+
},
197+
{
198+
input: m,
199+
path: "key2.key3.key4",
200+
expectedResult: "value4",
201+
},
202+
{
203+
input: m,
204+
path: "key2.key5",
205+
expectedResult: 1234,
206+
},
207+
{
208+
input: m,
209+
path: "key6.nil-key",
210+
expectedResult: nil,
211+
},
212+
}
213+
214+
for index, tc := range testCases {
215+
result, err := lookup(tc.input, tc.path)
216+
assert.Equal(t, tc.expectedResult, result, "Unexpected result for case %d", index)
217+
if tc.expectErr {
218+
assert.Error(t, err, "Did not get expected error in case %d", index)
219+
} else {
220+
assert.NoError(t, err, "Got unexpected error in case %d", index)
221+
}
168222
}
169-
assert.Equal(t, "value0", lookup("Key", s))
170-
assert.Equal(t, nil, lookup("bad-key", s))
171-
assert.Equal(t, nil, lookup("", s))
172-
assert.Equal(t, nil, lookup("key", "not a map"))
173-
assert.Equal(t, "value1", lookup("Inner.Map.key1", s))
174-
assert.Equal(t, "value1", lookup("Inner.Map.key1", &s))
175-
assert.Equal(t, "value4", lookup("Inner.Map.key2.key3.key4", s))
176-
assert.Equal(t, 1234, lookup("Inner.Map.key2.key5", s))
177-
assert.Equal(t, nil, lookup("Inner.Map.non-existent-key", s))
178-
assert.Equal(t, nil, lookup("Inner.Map.key2.non-existent-key", s))
179-
assert.Equal(t, "value6", lookup("Inner2.Map.key6", s))
180223
}

0 commit comments

Comments
 (0)