Added code for fields with dots(.) in their names work as needed (#4591)

* Added code for fields with dots(.) in their names work as needed

Co-authored-by: sarjamil <sjamil@salesforce.com>

* Fixed imports

Co-authored-by: sarjamil <sjamil@salesforce.com>

* Removed comments

Co-authored-by: sarjamil <sjamil@salesforce.com>

* Fix filter method

Co-authored-by: sarjamil <sjamil@salesforce.com>

* Fixed failing tests

Co-authored-by: sarjamil <sjamil@salesforce.com>

* Remove comments

Add co-author to PR.

Co-authored-by: sarjamil <sjamil@salesforce.com>

* Add co-author to PR.

Co-authored-by: sarjamil sjamil@salesforce.com

Co-authored-by: sarjamil <sjamil@salesforce.com>
This commit is contained in:
annelausf
2022-05-02 14:30:33 -07:00
committed by GitHub
parent 233b6adf7e
commit 188e35fbfd
6 changed files with 7 additions and 9 deletions

View File

@@ -0,0 +1,64 @@
// Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package utils
import "strings"
// TODO: Move these to kyaml
// PathSplitter splits a delimited string, permitting escaped delimiters.
func PathSplitter(path string, delimiter string) []string {
ps := strings.Split(path, delimiter)
var res []string
res = append(res, ps[0])
for i := 1; i < len(ps); i++ {
last := len(res) - 1
if strings.HasSuffix(res[last], `\`) {
res[last] = strings.TrimSuffix(res[last], `\`) + delimiter + ps[i]
} else {
res = append(res, ps[i])
}
}
return res
}
// SmarterPathSplitter splits a path, retaining bracketed elements.
// If the element is a list entry identifier (defined by the '='),
// it will retain the brackets.
// E.g. "[name=com.foo.someapp]" survives as one thing after splitting
// "spec.template.spec.containers.[name=com.foo.someapp].image"
// See kyaml/yaml/match.go for use of list entry identifiers.
// If the element is a mapping entry identifier, it will remove the
// brackets.
// E.g. "a.b.c" survives as one thing after splitting
// "metadata.annotations.[a.b.c]
// This function uses `PathSplitter`, so it also respects escaped delimiters.
func SmarterPathSplitter(path string, delimiter string) []string {
var result []string
split := PathSplitter(path, delimiter)
for i := 0; i < len(split); i++ {
elem := split[i]
if strings.HasPrefix(elem, "[") && !strings.HasSuffix(elem, "]") {
// continue until we find the matching "]"
bracketed := []string{elem}
for i < len(split)-1 {
i++
bracketed = append(bracketed, split[i])
if strings.HasSuffix(split[i], "]") {
break
}
}
bracketedStr := strings.Join(bracketed, delimiter)
if strings.Contains(bracketedStr, "=") {
result = append(result, bracketedStr)
} else {
result = append(result, strings.Trim(bracketedStr, "[]"))
}
} else {
result = append(result, elem)
}
}
return result
}

View File

@@ -0,0 +1,94 @@
// Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package utils_test
import (
"testing"
"github.com/stretchr/testify/assert"
. "sigs.k8s.io/kustomize/kyaml/utils"
)
func TestPathSplitter(t *testing.T) {
for _, tc := range []struct {
exp []string
path string
}{
{
path: "",
exp: []string{""},
},
{
path: "s",
exp: []string{"s"},
},
{
path: "a/b/c",
exp: []string{"a", "b", "c"},
},
{
path: `a/b[]/c`,
exp: []string{"a", "b[]", "c"},
},
{
path: `a/b\/c/d\/e/f`,
exp: []string{"a", "b/c", "d/e", "f"},
},
{
// The actual reason for this.
path: `metadata/annotations/nginx.ingress.kubernetes.io\/auth-secret`,
exp: []string{
"metadata",
"annotations",
"nginx.ingress.kubernetes.io/auth-secret"},
},
} {
assert.Equal(t, tc.exp, PathSplitter(tc.path, "/"))
}
}
func TestSmarterPathSplitter(t *testing.T) {
testCases := map[string]struct {
input string
expected []string
}{
"simple": {
input: "spec.replicas",
expected: []string{"spec", "replicas"},
},
"sequence": {
input: "spec.data.[name=first].key",
expected: []string{"spec", "data", "[name=first]", "key"},
},
"key, value with . prefix": {
input: "spec.data.[.name=.first].key",
expected: []string{"spec", "data", "[.name=.first]", "key"},
},
"key, value with . suffix": {
input: "spec.data.[name.=first.].key",
expected: []string{"spec", "data", "[name.=first.]", "key"},
},
"multiple '.' in value": {
input: "spec.data.[name=f.i.r.s.t.].key",
expected: []string{"spec", "data", "[name=f.i.r.s.t.]", "key"},
},
"with escaped delimiter": {
input: `spec\.replicas`,
expected: []string{`spec.replicas`},
},
"unmatched bracket": {
input: "spec.data.[name=f.i.[r.s.t..key",
expected: []string{"spec", "data", "[name=f.i.[r.s.t..key"},
},
"mapping value with .": {
input: "metadata.annotations.[a.b.c/d.e.f-g.]",
expected: []string{"metadata", "annotations", "a.b.c/d.e.f-g."},
},
}
for tn, tc := range testCases {
t.Run(tn, func(t *testing.T) {
assert.Equal(t, tc.expected, SmarterPathSplitter(tc.input, "."))
})
}
}

View File

@@ -15,6 +15,7 @@ import (
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml"
"sigs.k8s.io/kustomize/kyaml/sliceutil"
"sigs.k8s.io/kustomize/kyaml/utils"
"sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/labels"
)
@@ -1207,7 +1208,7 @@ func (rn *RNode) GetString(path string) (string, error) {
// assuming we keep the var feature in kustomize.
// The other end of this is: refvar.go:updateNodeValue.
func (rn *RNode) GetFieldValue(path string) (interface{}, error) {
fields := convertSliceIndex(strings.Split(path, "."))
fields := convertSliceIndex(utils.SmarterPathSplitter(path, "."))
rn, err := rn.Pipe(Lookup(fields...))
if err != nil {
return nil, err

View File

@@ -2310,8 +2310,6 @@ func TestGetAnnotations(t *testing.T) {
}
func TestGetFieldValueWithDot(t *testing.T) {
t.Skip()
const input = `
kind: Pod
metadata:
@@ -2330,8 +2328,6 @@ metadata:
require.NoError(t, err)
require.Equal(t, "hello-world-app", app)
// TODO: doesn't currently work; we expect to be able to escape the dot in future
// https://github.com/kubernetes-sigs/kustomize/issues/4487
fooAppName, err := labelRNode.GetFieldValue(`foo\.appname`)
require.NoError(t, err)
require.Equal(t, "hello-world-foo", fooAppName) // no field named 'foo.appname'