Validate key subscript for metadata.annotations and metadata.labels separately

This commit is contained in:
Yang Guo
2017-11-22 12:38:09 -08:00
parent 34a7b3dea8
commit c74b97b29d
8 changed files with 153 additions and 71 deletions

View File

@@ -13,7 +13,10 @@ go_library(
"fieldpath.go",
],
importpath = "k8s.io/kubernetes/pkg/fieldpath",
deps = ["//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library"],
deps = [
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library",
],
)
go_test(

View File

@@ -21,6 +21,7 @@ import (
"strings"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/util/validation"
)
// FormatMap formats map[string]string to a string.
@@ -42,20 +43,24 @@ func ExtractFieldPathAsString(obj interface{}, fieldPath string) (string, error)
return "", nil
}
path, subscript := SplitMaybeSubscriptedPath(fieldPath)
if len(subscript) > 0 {
if path, subscript, ok := SplitMaybeSubscriptedPath(fieldPath); ok {
switch path {
case "metadata.annotations":
if errs := validation.IsQualifiedName(strings.ToLower(subscript)); len(errs) != 0 {
return "", fmt.Errorf("invalid key subscript in %s: %s", fieldPath, strings.Join(errs, ";"))
}
return accessor.GetAnnotations()[subscript], nil
case "metadata.labels":
if errs := validation.IsQualifiedName(subscript); len(errs) != 0 {
return "", fmt.Errorf("invalid key subscript in %s: %s", fieldPath, strings.Join(errs, ";"))
}
return accessor.GetLabels()[subscript], nil
default:
return "", fmt.Errorf("fieldPath %q does not support subscript", fieldPath)
}
}
switch path {
switch fieldPath {
case "metadata.annotations":
return FormatMap(accessor.GetAnnotations()), nil
case "metadata.labels":
@@ -74,25 +79,25 @@ func ExtractFieldPathAsString(obj interface{}, fieldPath string) (string, error)
// SplitMaybeSubscriptedPath checks whether the specified fieldPath is
// subscripted, and
// - if yes, this function splits the fieldPath into path and subscript, and
// returns (path, subscript).
// - if no, this function returns (fieldPath, "").
// returns (path, subscript, true).
// - if no, this function returns (fieldPath, "", false).
//
// Example inputs and outputs:
// - "metadata.annotations['myKey']" --> ("metadata.annotations", "myKey")
// - "metadata.annotations['a[b]c']" --> ("metadata.annotations", "a[b]c")
// - "metadata.labels" --> ("metadata.labels", "")
// - "metadata.labels['']" --> ("metadata.labels", "")
func SplitMaybeSubscriptedPath(fieldPath string) (string, string) {
// - "metadata.annotations['myKey']" --> ("metadata.annotations", "myKey", true)
// - "metadata.annotations['a[b]c']" --> ("metadata.annotations", "a[b]c", true)
// - "metadata.labels['']" --> ("metadata.labels", "", true)
// - "metadata.labels" --> ("metadata.labels", "", false)
func SplitMaybeSubscriptedPath(fieldPath string) (string, string, bool) {
if !strings.HasSuffix(fieldPath, "']") {
return fieldPath, ""
return fieldPath, "", false
}
s := strings.TrimSuffix(fieldPath, "']")
parts := strings.SplitN(s, "['", 2)
if len(parts) < 2 {
return fieldPath, ""
return fieldPath, "", false
}
if len(parts[0]) == 0 || len(parts[1]) == 0 {
return fieldPath, ""
if len(parts[0]) == 0 {
return fieldPath, "", false
}
return parts[0], parts[1]
return parts[0], parts[1], true
}

View File

@@ -36,7 +36,6 @@ func TestExtractFieldPathAsString(t *testing.T) {
name: "not an API object",
fieldPath: "metadata.name",
obj: "",
expectedMessageFragment: "expected struct",
},
{
name: "ok - namespace",
@@ -98,7 +97,16 @@ func TestExtractFieldPathAsString(t *testing.T) {
},
expectedValue: "1",
},
{
name: "ok - annotation",
fieldPath: "metadata.annotations['Www.k8s.io/test']",
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"Www.k8s.io/test": "1"},
},
},
expectedValue: "1",
},
{
name: "invalid expression",
fieldPath: "metadata.whoops",
@@ -110,14 +118,24 @@ func TestExtractFieldPathAsString(t *testing.T) {
expectedMessageFragment: "unsupported fieldPath",
},
{
name: "invalid annotation",
fieldPath: "metadata.annotations['unknown.key']",
name: "invalid annotation key",
fieldPath: "metadata.annotations['invalid~key']",
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"foo": "bar"},
},
},
expectedMessageFragment: "unsupported fieldPath",
expectedMessageFragment: "invalid key subscript in metadata.annotations",
},
{
name: "invalid label key",
fieldPath: "metadata.labels['Www.k8s.io/test']",
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"foo": "bar"},
},
},
expectedMessageFragment: "invalid key subscript in metadata.labels",
},
}
@@ -131,6 +149,8 @@ func TestExtractFieldPathAsString(t *testing.T) {
} else {
t.Errorf("%v: unexpected error: %v", tc.name, err)
}
} else if tc.expectedMessageFragment != "" {
t.Errorf("%v: expected error: %v", tc.name, tc.expectedMessageFragment)
} else if e := tc.expectedValue; e != "" && e != actual {
t.Errorf("%v: unexpected result; got %q, expected %q", tc.name, actual, e)
}
@@ -142,72 +162,79 @@ func TestSplitMaybeSubscriptedPath(t *testing.T) {
fieldPath string
expectedPath string
expectedSubscript string
expectedOK bool
}{
{
fieldPath: "metadata.annotations['key']",
expectedPath: "metadata.annotations",
expectedSubscript: "key",
expectedOK: true,
},
{
fieldPath: "metadata.annotations['a[b']c']",
expectedPath: "metadata.annotations",
expectedSubscript: "a[b']c",
expectedOK: true,
},
{
fieldPath: "metadata.labels['['key']",
expectedPath: "metadata.labels",
expectedSubscript: "['key",
expectedOK: true,
},
{
fieldPath: "metadata.labels['key']']",
expectedPath: "metadata.labels",
expectedSubscript: "key']",
},
{
fieldPath: "metadata.labels[ 'key' ]",
expectedPath: "metadata.labels[ 'key' ]",
expectedSubscript: "",
expectedOK: true,
},
{
fieldPath: "metadata.labels['']",
expectedPath: "metadata.labels['']",
expectedPath: "metadata.labels",
expectedSubscript: "",
expectedOK: true,
},
{
fieldPath: "metadata.labels[' ']",
expectedPath: "metadata.labels",
expectedSubscript: " ",
expectedOK: true,
},
{
fieldPath: "metadata.labels[]",
expectedPath: "metadata.labels[]",
expectedSubscript: "",
fieldPath: "metadata.labels[ 'key' ]",
expectedOK: false,
},
{
fieldPath: "metadata.labels[']",
expectedPath: "metadata.labels[']",
expectedSubscript: "",
fieldPath: "metadata.labels[]",
expectedOK: false,
},
{
fieldPath: "metadata.labels['key']foo",
expectedPath: "metadata.labels['key']foo",
expectedSubscript: "",
fieldPath: "metadata.labels[']",
expectedOK: false,
},
{
fieldPath: "['key']",
expectedPath: "['key']",
expectedSubscript: "",
fieldPath: "metadata.labels['key']foo",
expectedOK: false,
},
{
fieldPath: "metadata.labels",
expectedPath: "metadata.labels",
expectedSubscript: "",
fieldPath: "['key']",
expectedOK: false,
},
{
fieldPath: "metadata.labels",
expectedOK: false,
},
}
for _, tc := range cases {
path, subscript := SplitMaybeSubscriptedPath(tc.fieldPath)
path, subscript, ok := SplitMaybeSubscriptedPath(tc.fieldPath)
if !ok {
if tc.expectedOK {
t.Errorf("SplitMaybeSubscriptedPath(%q) expected to return (_, _, true)", tc.fieldPath)
}
continue
}
if path != tc.expectedPath || subscript != tc.expectedSubscript {
t.Errorf("SplitMaybeSubscriptedPath(%q) = (%q, %q), expect (%q, %q)",
t.Errorf("SplitMaybeSubscriptedPath(%q) = (%q, %q, true), expect (%q, %q, true)",
tc.fieldPath, path, subscript, tc.expectedPath, tc.expectedSubscript)
}
}