mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
Add function label_match, to use matchLabel in JMESPath, usage: label_match(labels_from_network_policy, labels_from pod) bool, Remove validation for JMESPath (#1862)
Signed-off-by: Thomas Rosenstein <thomas@thoro.at>
This commit is contained in:
parent
f921bf47d2
commit
e80d18e692
4 changed files with 143 additions and 1 deletions
|
@ -39,6 +39,7 @@ var (
|
|||
regexReplaceAll = "regex_replace_all"
|
||||
regexReplaceAllLiteral = "regex_replace_all_literal"
|
||||
regexMatch = "regex_match"
|
||||
labelMatch = "label_match"
|
||||
)
|
||||
|
||||
const errorPrefix = "JMESPath function '%s': "
|
||||
|
@ -146,6 +147,15 @@ func getFunctions() []*gojmespath.FunctionEntry {
|
|||
},
|
||||
Handler: jpRegexMatch,
|
||||
},
|
||||
{
|
||||
// Validates if label (param1) would match pod/host/etc labels (param2)
|
||||
Name: labelMatch,
|
||||
Arguments: []ArgSpec{
|
||||
{Types: []JpType{JpObject}},
|
||||
{Types: []JpType{JpObject}},
|
||||
},
|
||||
Handler: jpLabelMatch,
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -353,6 +363,28 @@ func jpRegexMatch(arguments []interface{}) (interface{}, error) {
|
|||
return regexp.Match(regex.String(), []byte(src))
|
||||
}
|
||||
|
||||
func jpLabelMatch(arguments []interface{}) (interface{}, error) {
|
||||
labelMap, ok := arguments[0].(map[string]interface{})
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(invalidArgumentTypeError, labelMatch, 0, "Object")
|
||||
}
|
||||
|
||||
matchMap, ok := arguments[1].(map[string]interface{})
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(invalidArgumentTypeError, labelMatch, 1, "Object")
|
||||
}
|
||||
|
||||
for key, value := range labelMap {
|
||||
if val, ok := matchMap[key]; !ok || val != value {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// InterfaceToString casts an interface to a string type
|
||||
func ifaceToString(iface interface{}) (string, error) {
|
||||
switch iface.(type) {
|
||||
|
|
|
@ -243,3 +243,61 @@ func Test_regexReplaceAllLiteral(t *testing.T) {
|
|||
|
||||
assert.Equal(t, string(result), expected)
|
||||
}
|
||||
|
||||
func Test_labelMatch(t *testing.T) {
|
||||
resourceRaw := []byte(`
|
||||
{
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"app": "test-app",
|
||||
"controller-name": "test-controller"
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
testCases := []struct {
|
||||
resource []byte
|
||||
test string
|
||||
expectedResult bool
|
||||
}{
|
||||
{
|
||||
resource: resourceRaw,
|
||||
test: `{ "app": "test-app" }`,
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
resource: resourceRaw,
|
||||
test: `{ "app": "test-app", "controller-name": "test-controller" }`,
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
resource: resourceRaw,
|
||||
test: `{ "app": "test-app2" }`,
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
resource: resourceRaw,
|
||||
test: `{ "app.kubernetes.io/name": "test-app" }`,
|
||||
expectedResult: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
var resource interface{}
|
||||
err := json.Unmarshal(testCase.resource, &resource)
|
||||
assert.NilError(t, err)
|
||||
|
||||
query, err := New("label_match(`" + testCase.test + "`, metadata.labels)")
|
||||
assert.NilError(t, err)
|
||||
|
||||
res, err := query.Search(resource)
|
||||
assert.NilError(t, err)
|
||||
|
||||
result, ok := res.(bool)
|
||||
assert.Assert(t, ok)
|
||||
|
||||
assert.Equal(t, result, testCase.expectedResult)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -690,7 +690,13 @@ func validateAPICall(entry kyverno.ContextEntry) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if entry.APICall.JMESPath != "" {
|
||||
// If JMESPath contains variables, the validation will fail because it's not possible to infer which value
|
||||
// will be inserted by the variable
|
||||
// Skip validation if a variable is detected
|
||||
|
||||
jmesPath := variables.ReplaceAllVars(entry.APICall.JMESPath, func(s string) string { return "kyvernojmespathvariable" })
|
||||
|
||||
if !strings.Contains(jmesPath, "kyvernojmespathvariable") && entry.APICall.JMESPath != "" {
|
||||
if _, err := jmespath.NewParser().Parse(entry.APICall.JMESPath); err != nil {
|
||||
return fmt.Errorf("failed to parse JMESPath %s: %v", entry.APICall.JMESPath, err)
|
||||
}
|
||||
|
|
|
@ -1286,6 +1286,7 @@ func Test_Validate_Kind(t *testing.T) {
|
|||
err = Validate(policy, nil, true, openAPIController)
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
||||
func Test_checkAutoGenRules(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
@ -1323,3 +1324,48 @@ func Test_checkAutoGenRules(t *testing.T) {
|
|||
assert.Equal(t, test.expectedResult, res, fmt.Sprintf("test %s failed", test.name))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Validate_ApiCall(t *testing.T) {
|
||||
testCases := []struct {
|
||||
resource kyverno.ContextEntry
|
||||
expectedResult interface{}
|
||||
}{
|
||||
{
|
||||
resource: kyverno.ContextEntry{
|
||||
APICall: &kyverno.APICall{
|
||||
URLPath: "/apis/networking.k8s.io/v1/namespaces/{{request.namespace}}/networkpolicies",
|
||||
JMESPath: "",
|
||||
},
|
||||
},
|
||||
expectedResult: nil,
|
||||
},
|
||||
{
|
||||
resource: kyverno.ContextEntry{
|
||||
APICall: &kyverno.APICall{
|
||||
URLPath: "/apis/networking.k8s.io/v1/namespaces/{{request.namespace}}/networkpolicies",
|
||||
JMESPath: "items[",
|
||||
},
|
||||
},
|
||||
expectedResult: "failed to parse JMESPath items[: SyntaxError: Expected tStar, received: tEOF",
|
||||
},
|
||||
{
|
||||
resource: kyverno.ContextEntry{
|
||||
APICall: &kyverno.APICall{
|
||||
URLPath: "/apis/networking.k8s.io/v1/namespaces/{{request.namespace}}/networkpolicies",
|
||||
JMESPath: "items[{{request.namespace}}",
|
||||
},
|
||||
},
|
||||
expectedResult: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
err := validateAPICall(testCase.resource)
|
||||
|
||||
if err == nil {
|
||||
assert.Equal(t, err, testCase.expectedResult)
|
||||
} else {
|
||||
assert.Equal(t, err.Error(), testCase.expectedResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue