From a7aec886b4bd3884e302a8f738f7334464ebda6e Mon Sep 17 00:00:00 2001 From: Shuting Zhao Date: Wed, 6 Nov 2019 16:16:29 -0800 Subject: [PATCH] handle processOverlay with overlayError --- pkg/engine/mutation.go | 4 +- pkg/engine/overlay.go | 64 ++++++++++++++++------------- pkg/engine/overlayCondition.go | 74 +++++++++++++++++----------------- 3 files changed, 75 insertions(+), 67 deletions(-) diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index 42741e72da..ea4973e990 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -1,7 +1,6 @@ package engine import ( - "strings" "time" "github.com/golang/glog" @@ -52,8 +51,9 @@ func Mutate(policy kyverno.ClusterPolicy, resource unstructured.Unstructured) (r if rule.Mutation.Overlay != nil { var ruleResponse RuleResponse ruleResponse, patchedResource = processOverlay(rule, resource) - if strings.Contains(ruleResponse.Message, "policy not applied") { + if ruleResponse.Success == true && ruleResponse.Patches == nil { // overlay pattern does not match the resource conditions + glog.Infof(ruleResponse.Message) continue } response.PolicyResponse.Rules = append(response.PolicyResponse.Rules, ruleResponse) diff --git a/pkg/engine/overlay.go b/pkg/engine/overlay.go index 4d9c9f34b8..cab8623c41 100644 --- a/pkg/engine/overlay.go +++ b/pkg/engine/overlay.go @@ -6,7 +6,6 @@ import ( "fmt" "reflect" "strconv" - "strings" "time" "github.com/golang/glog" @@ -17,10 +16,6 @@ import ( "github.com/nirmata/kyverno/pkg/engine/anchor" ) -// conditionalFieldEmpty is the message to indicate the conditional key -// is not present in the resource, the rule is skipped and is considered as successs -const conditionalFieldEmpty = "resource field is not present" - // processOverlay processes validation patterns on the resource func processOverlay(rule kyverno.Rule, resource unstructured.Unstructured) (response RuleResponse, patchedResource unstructured.Unstructured) { startTime := time.Now() @@ -32,34 +27,39 @@ func processOverlay(rule kyverno.Rule, resource unstructured.Unstructured) (resp glog.V(4).Infof("finished applying overlay rule %q (%v)", response.Name, response.RuleStats.ProcessingTime) }() - patches, err := processOverlayPatches(resource.UnstructuredContent(), rule.Mutation.Overlay) + patches, overlayerr := processOverlayPatches(resource.UnstructuredContent(), rule.Mutation.Overlay) // resource does not satisfy the overlay pattern, we don't apply this rule - if err != nil { + if !reflect.DeepEqual(overlayerr, overlayError{}) { + switch overlayerr.statusCode { // condition key is not present in the resource, don't apply this rule // consider as success - if strings.Contains(err.Error(), conditionalFieldEmpty) { + case conditionNotPresent: + glog.Infof("Resource %s/%s/%s: %s", resource.GetKind(), resource.GetNamespace(), resource.GetName(), overlayerr.ErrorMsg()) response.Success = true - response.Message = fmt.Sprintf("Resource %s/%s/%s: %v.", resource.GetKind(), resource.GetNamespace(), resource.GetName(), err) + response.Message = fmt.Sprintf("Resource %s/%s/%s: %s.", resource.GetKind(), resource.GetNamespace(), resource.GetName(), overlayerr.ErrorMsg()) return response, resource - } - // conditions are not met, don't apply this rule // consider as failure - if strings.Contains(err.Error(), "Conditions are not met") { + case conditionFailure: glog.Errorf("Resource %s/%s/%s does not meet the conditions in the rule %s with overlay pattern %s", resource.GetKind(), resource.GetNamespace(), resource.GetName(), rule.Name, rule.Mutation.Overlay) //TODO: send zero response and not consider this as applied? response.Success = false - response.Message = fmt.Sprintf("Resource %s/%s/%s: %v.", resource.GetKind(), resource.GetNamespace(), resource.GetName(), err) + response.Message = fmt.Sprintf("Resource %s/%s/%s: %v.", resource.GetKind(), resource.GetNamespace(), resource.GetName(), overlayerr.ErrorMsg()) + return response, resource + // rule application failed + case overlayFailure: + glog.Errorf("Resource %s/%s/%s: failed to process overlay: %v in the rule %s", resource.GetKind(), resource.GetNamespace(), resource.GetName(), overlayerr.ErrorMsg(), rule.Name) + response.Success = false + response.Message = fmt.Sprintf("failed to process overlay: %v", overlayerr.ErrorMsg()) + return response, resource + default: + glog.Errorf("Resource %s/%s/%s: Unknown type of error: %v", resource.GetKind(), resource.GetNamespace(), resource.GetName(), overlayerr.Error()) + response.Success = false + response.Message = fmt.Sprintf("Unknown type of error: %v", overlayerr.Error()) return response, resource } } - if err != nil { - // rule application failed - response.Success = false - response.Message = fmt.Sprintf("failed to process overlay: %v", err) - return response, resource - } // convert to RAW resourceRaw, err := resource.MarshalJSON() if err != nil { @@ -93,20 +93,26 @@ func processOverlay(rule kyverno.Rule, resource unstructured.Unstructured) (resp return response, patchedResource } -func processOverlayPatches(resource, overlay interface{}) ([][]byte, error) { - if path, err := meetConditions(resource, overlay); err != nil { +func processOverlayPatches(resource, overlay interface{}) ([][]byte, overlayError) { + if path, overlayerr := meetConditions(resource, overlay); !reflect.DeepEqual(overlayerr, overlayError{}) { + switch overlayerr.statusCode { // anchor key does not exist in the resource, skip applying policy - if strings.Contains(err.Error(), conditionalFieldEmpty) { - glog.V(4).Infof("Mutate rule: policy not applied: %v at %s", err, path) - return nil, fmt.Errorf("policy not applied: %v at %s", err, path) - } - + case conditionNotPresent: + glog.V(4).Infof("Mutate rule: policy not applied: %v at %s", overlayerr, path) + return nil, newOverlayError(overlayerr.statusCode, fmt.Sprintf("policy not applied: %v at %s", overlayerr.ErrorMsg(), path)) // anchor key is not satisfied in the resource, skip applying policy - glog.V(4).Infof("Mutate rule: failed to validate condition at %s, err: %v", path, err) - return nil, fmt.Errorf("Conditions are not met at %s, %v", path, err) + case conditionFailure: + glog.V(4).Infof("Mutate rule: failed to validate condition at %s, err: %v", path, overlayerr) + return nil, newOverlayError(overlayerr.statusCode, fmt.Sprintf("Conditions are not met at %s, %v", path, overlayerr)) + } } - return mutateResourceWithOverlay(resource, overlay) + patchBytes, err := mutateResourceWithOverlay(resource, overlay) + if err != nil { + return patchBytes, newOverlayError(overlayFailure, err.Error()) + } + + return patchBytes, overlayError{} } // mutateResourceWithOverlay is a start of overlaying process diff --git a/pkg/engine/overlayCondition.go b/pkg/engine/overlayCondition.go index 3754785341..bb7422354b 100755 --- a/pkg/engine/overlayCondition.go +++ b/pkg/engine/overlayCondition.go @@ -9,15 +9,15 @@ import ( "github.com/nirmata/kyverno/pkg/engine/anchor" ) -func meetConditions(resource, overlay interface{}) (string, error) { +func meetConditions(resource, overlay interface{}) (string, overlayError) { return checkConditions(resource, overlay, "/") } // resource and overlay should be the same type -func checkConditions(resource, overlay interface{}, path string) (string, error) { +func checkConditions(resource, overlay interface{}, path string) (string, overlayError) { // overlay has no anchor, return true if !hasNestedAnchors(overlay) { - return "", nil + return "", overlayError{} } // resource item exists but has different type @@ -26,10 +26,11 @@ func checkConditions(resource, overlay interface{}, path string) (string, error) if reflect.TypeOf(resource) != reflect.TypeOf(overlay) { if hasNestedAnchors(overlay) { glog.V(4).Infof("Found anchor on different types of element at path %s: overlay %T, resource %T", path, overlay, resource) - return path, fmt.Errorf("Found anchor on different types of element at path %s: overlay %T %v, resource %T %v", path, overlay, overlay, resource, resource) + return path, newOverlayError(conditionFailure, + fmt.Sprintf("Found anchor on different types of element at path %s: overlay %T %v, resource %T %v", path, overlay, overlay, resource, resource)) } - return "", nil + return "", overlayError{} } switch typedOverlay := overlay.(type) { @@ -43,42 +44,43 @@ func checkConditions(resource, overlay interface{}, path string) (string, error) // anchor on non map/array is invalid: // - anchor defined on values glog.Warningln("Found invalid conditional anchor: anchor defined on values") - return "", nil + return "", overlayError{} } } -func checkConditionOnMap(resourceMap, overlayMap map[string]interface{}, path string) (string, error) { +func checkConditionOnMap(resourceMap, overlayMap map[string]interface{}, path string) (string, overlayError) { anchors, overlayWithoutAnchor := getAnchorAndElementsFromMap(overlayMap) // validate resource with conditions - if newPath, err := validateConditionAnchorMap(resourceMap, anchors, path); err != nil { + if newPath, err := validateConditionAnchorMap(resourceMap, anchors, path); !reflect.DeepEqual(err, overlayError{}) { return newPath, err } // traverse overlay pattern to further validate conditions - if newPath, err := validateNonAnchorOverlayMap(resourceMap, overlayWithoutAnchor, path); err != nil { + if newPath, err := validateNonAnchorOverlayMap(resourceMap, overlayWithoutAnchor, path); !reflect.DeepEqual(err, overlayError{}) { return newPath, err } // empty overlayMap - return "", nil + return "", overlayError{} } -func checkConditionOnArray(resource, overlay []interface{}, path string) (string, error) { +func checkConditionOnArray(resource, overlay []interface{}, path string) (string, overlayError) { if 0 == len(overlay) { glog.Infof("Mutate overlay pattern is empty, path %s", path) - return "", nil + return "", overlayError{} } if reflect.TypeOf(resource[0]) != reflect.TypeOf(overlay[0]) { glog.V(4).Infof("Overlay array and resource array have elements of different types: %T and %T", overlay[0], resource[0]) - return path, fmt.Errorf("Overlay array and resource array have elements of different types: %T and %T", overlay[0], resource[0]) + return path, newOverlayError(conditionFailure, + fmt.Sprintf("Overlay array and resource array have elements of different types: %T and %T", overlay[0], resource[0])) } return checkConditionsOnArrayOfSameTypes(resource, overlay, path) } -func validateConditionAnchorMap(resourceMap, anchors map[string]interface{}, path string) (string, error) { +func validateConditionAnchorMap(resourceMap, anchors map[string]interface{}, path string) (string, overlayError) { for key, overlayValue := range anchors { // skip if key does not have condition anchor if !anchor.IsConditionAnchor(key) { @@ -91,25 +93,25 @@ func validateConditionAnchorMap(resourceMap, anchors map[string]interface{}, pat if resourceValue, ok := resourceMap[noAnchorKey]; ok { // compare entire resourceValue block // return immediately on err since condition fails on this block - if newPath, err := compareOverlay(resourceValue, overlayValue, curPath); err != nil { + if newPath, err := compareOverlay(resourceValue, overlayValue, curPath); !reflect.DeepEqual(err, overlayError{}) { return newPath, err } } else { // noAnchorKey doesn't exist in resource - return curPath, fmt.Errorf("resource field is not present %s", noAnchorKey) + return curPath, newOverlayError(conditionNotPresent, fmt.Sprintf("resource field is not present %s", noAnchorKey)) } } - return "", nil + return "", overlayError{} } // compareOverlay compare values in anchormap and resourcemap // i.e. check if B1 == B2 // overlay - (A): B1 // resource - A: B2 -func compareOverlay(resource, overlay interface{}, path string) (string, error) { +func compareOverlay(resource, overlay interface{}, path string) (string, overlayError) { if reflect.TypeOf(resource) != reflect.TypeOf(overlay) { - glog.Errorf("Found anchor on different types of element: overlay %T, resource %T\nSkip processing overlay.", overlay, resource) - return path, fmt.Errorf("") + glog.V(4).Infof("Found anchor on different types of element: overlay %T, resource %T\nSkip processing overlay.", overlay, resource) + return path, newOverlayError(conditionFailure, fmt.Sprintf("Found anchor on different types of element: overlay %T, resource %T\nSkip processing overlay.", overlay, resource)) } switch typedOverlay := overlay.(type) { @@ -120,9 +122,9 @@ func compareOverlay(resource, overlay interface{}, path string) (string, error) curPath := path + noAnchorKey + "/" resourceVal, ok := typedResource[noAnchorKey] if !ok { - return curPath, fmt.Errorf("Field %s is not present", noAnchorKey) + return curPath, newOverlayError(conditionFailure, fmt.Sprintf("field %s is not present", noAnchorKey)) } - if newPath, err := compareOverlay(resourceVal, overlayVal, curPath); err != nil { + if newPath, err := compareOverlay(resourceVal, overlayVal, curPath); !reflect.DeepEqual(err, overlayError{}) { return newPath, err } } @@ -130,7 +132,7 @@ func compareOverlay(resource, overlay interface{}, path string) (string, error) typedResource := resource.([]interface{}) for _, overlayElement := range typedOverlay { for _, resourceElement := range typedResource { - if newPath, err := compareOverlay(resourceElement, overlayElement, path); err != nil { + if newPath, err := compareOverlay(resourceElement, overlayElement, path); !reflect.DeepEqual(err, overlayError{}) { return newPath, err } } @@ -138,17 +140,17 @@ func compareOverlay(resource, overlay interface{}, path string) (string, error) case string, float64, int, int64, bool, nil: if !ValidateValueWithPattern(resource, overlay) { glog.V(4).Infof("Mutate rule: failed validating value %v with overlay %v", resource, overlay) - return path, fmt.Errorf("failed validating value %v with overlay %v", resource, overlay) + return path, newOverlayError(conditionFailure, fmt.Sprintf("failed validating value %v with overlay %v", resource, overlay)) } default: - return path, fmt.Errorf("overlay has unknown type %T, value %v", overlay, overlay) + return path, newOverlayError(conditionFailure, fmt.Sprintf("overlay has unknown type %T, value %v", overlay, overlay)) } - return "", nil + return "", overlayError{} } // validateNonAnchorOverlayMap validate anchor condition in overlay block without anchor -func validateNonAnchorOverlayMap(resourceMap, overlayWithoutAnchor map[string]interface{}, path string) (string, error) { +func validateNonAnchorOverlayMap(resourceMap, overlayWithoutAnchor map[string]interface{}, path string) (string, overlayError) { // validate resource map (anchors could exist in resource) for key, overlayValue := range overlayWithoutAnchor { curPath := path + key + "/" @@ -160,14 +162,14 @@ func validateNonAnchorOverlayMap(resourceMap, overlayWithoutAnchor map[string]in // the above case should be allowed continue } - if newPath, err := checkConditions(resourceValue, overlayValue, curPath); err != nil { + if newPath, err := checkConditions(resourceValue, overlayValue, curPath); !reflect.DeepEqual(err, overlayError{}) { return newPath, err } } - return "", nil + return "", overlayError{} } -func checkConditionsOnArrayOfSameTypes(resource, overlay []interface{}, path string) (string, error) { +func checkConditionsOnArrayOfSameTypes(resource, overlay []interface{}, path string) (string, overlayError) { switch overlay[0].(type) { case map[string]interface{}: return checkConditionsOnArrayOfMaps(resource, overlay, path) @@ -175,17 +177,17 @@ func checkConditionsOnArrayOfSameTypes(resource, overlay []interface{}, path str for i, overlayElement := range overlay { curPath := path + strconv.Itoa(i) + "/" path, err := checkConditions(resource[i], overlayElement, curPath) - if err != nil { + if !reflect.DeepEqual(err, overlayError{}) { return path, err } } } - return "", nil + return "", overlayError{} } -func checkConditionsOnArrayOfMaps(resource, overlay []interface{}, path string) (string, error) { +func checkConditionsOnArrayOfMaps(resource, overlay []interface{}, path string) (string, overlayError) { var newPath string - var err error + var err overlayError for i, overlayElement := range overlay { for _, resourceMap := range resource { @@ -194,8 +196,8 @@ func checkConditionsOnArrayOfMaps(resource, overlay []interface{}, path string) // when resource has multiple same blocks of the overlay block // return true if there is one resource block meet the overlay pattern // reference: TestMeetConditions_AtleastOneExist - if err == nil { - return "", nil + if reflect.DeepEqual(err, overlayError{}) { + return "", overlayError{} } } }