diff --git a/pkg/validate/validate.go b/pkg/validate/validate.go new file mode 100644 index 000000000..94ae5de11 --- /dev/null +++ b/pkg/validate/validate.go @@ -0,0 +1,35 @@ +package validate + +import ( + "fmt" + "regexp" +) + +// TODO: these are rudimentary placeholder validation functions and need +// additional work to truly match expected syntax rules. + +// IsValidLabel checks whether a label key/value pair has correct syntax and +// character set +func IsValidLabel(keyval string) (bool, error) { + ok, err := regexp.MatchString(`\A([a-zA-Z0-9_.-]+):([a-zA-Z0-9_.-]+)\z`, keyval) + if err != nil { + return false, err + } + if !ok { + return false, fmt.Errorf("invalid label format: %s", keyval) + } + return true, nil +} + +// IsValidAnnotation checks whether an annotation key/value pair has correct +// syntax and character set +func IsValidAnnotation(keyval string) (bool, error) { + ok, err := regexp.MatchString(`\A([a-zA-Z0-9_.-]+):([a-zA-Z0-9_.-]+)\z`, keyval) + if err != nil { + return false, err + } + if !ok { + return false, fmt.Errorf("invalid annotation format: %s", keyval) + } + return true, nil +} diff --git a/pkg/validate/validate_test.go b/pkg/validate/validate_test.go new file mode 100644 index 000000000..ff84f82b2 --- /dev/null +++ b/pkg/validate/validate_test.go @@ -0,0 +1,103 @@ +package validate + +import "testing" + +func TestIsValidLabel(t *testing.T) { + testcases := []struct { + input, name string + valid bool + }{ + { + input: "otters:cute", + valid: true, + name: "Valid input format", + }, + { + input: "dogs,cats", + valid: false, + name: "Does not contain colon", + }, + { + input: ":noKey", + valid: false, + name: "Missing key", + }, + { + input: "noValue:", + valid: false, + name: "Missing value", + }, + { + input: "exclamation!:point", + valid: false, + name: "Non-alphanumeric input", + }, + { + input: "123:45", + valid: true, + name: "Numeric input is allowed", + }, + } + for _, tc := range testcases { + ok, err := IsValidLabel(tc.input) + if tc.valid && err != nil { + t.Errorf("unexpected error: for test case %s, expected no error but got: %s", tc.name, err.Error()) + } + if ok && !tc.valid { + t.Errorf("for test case %s, expected invalid label format error", tc.name) + } + if !ok && tc.valid { + t.Errorf("unexpected error: for test case %s, expected test to pass", tc.name) + } + } +} + +func TestIsValidAnnotation(t *testing.T) { + testcases := []struct { + input, name string + valid bool + }{ + { + input: "owls:adorable", + valid: true, + name: "Valid input format", + }, + { + input: "cake,cookies", + valid: false, + name: "Does not contain colon", + }, + { + input: ":noKey", + valid: false, + name: "Missing key", + }, + { + input: "noValue:", + valid: false, + name: "Missing value", + }, + { + input: "exclamation!:point", + valid: false, + name: "Input has a bang!", + }, + { + input: "987:65", + valid: true, + name: "Numeric input is valid", + }, + } + for _, tc := range testcases { + ok, err := IsValidAnnotation(tc.input) + if tc.valid && err != nil { + t.Errorf("unexpected error: for test case %s, expected no error but got: %s", tc.name, err.Error()) + } + if ok && !tc.valid { + t.Errorf("for test case %s, expected invalid annotation format error", tc.name) + } + if !ok && tc.valid { + t.Errorf("unexpected error: for test case %s, expected test to pass", tc.name) + } + } +}