package mutate import ( "encoding/json" "fmt" "strings" "time" "github.com/go-logr/logr" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" "github.com/nirmata/kyverno/pkg/engine/response" "github.com/nirmata/kyverno/pkg/engine/utils" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) // applyPatch applies patch for resource, returns patched resource. func applyPatch(resource []byte, patchRaw []byte) ([]byte, error) { patchesList := [][]byte{patchRaw} return utils.ApplyPatches(resource, patchesList) } //ProcessPatches applies the patches on the resource and returns the patched resource func ProcessPatches(log logr.Logger, rule kyverno.Rule, resource unstructured.Unstructured) (resp response.RuleResponse, patchedResource unstructured.Unstructured) { logger := log.WithValues("rule", rule.Name) startTime := time.Now() logger.V(4).Info("started JSON patch", "startTime", startTime) resp.Name = rule.Name resp.Type = utils.Mutation.String() defer func() { resp.RuleStats.ProcessingTime = time.Since(startTime) logger.V(4).Info("finished JSON patch", "processingTime", resp.RuleStats.ProcessingTime) }() // convert to RAW resourceRaw, err := resource.MarshalJSON() if err != nil { resp.Success = false logger.Error(err, "failed to marshal resource") resp.Message = fmt.Sprintf("failed to process JSON patches: %v", err) return resp, resource } var errs []error var patches [][]byte for _, patch := range rule.Mutation.Patches { // JSON patch patchRaw, err := json.Marshal(patch) if err != nil { logger.Error(err, "failed to marshal JSON patch") errs = append(errs, err) continue } patchResource, err := applyPatch(resourceRaw, patchRaw) // TODO: continue on error if one of the patches fails, will add the failure event in such case if err != nil && patch.Operation == "remove" { log.Error(err, "failed to process JSON path or patch is a 'remove' operation") continue } if err != nil { errs = append(errs, err) continue } resourceRaw = patchResource patches = append(patches, patchRaw) } // error while processing JSON patches if len(errs) > 0 { resp.Success = false resp.Message = fmt.Sprintf("failed to process JSON patches: %v", func() string { var str []string for _, err := range errs { str = append(str, err.Error()) } return strings.Join(str, ";") }()) return resp, resource } err = patchedResource.UnmarshalJSON(resourceRaw) if err != nil { logger.Error(err, "failed to unmmarshal resource") resp.Success = false resp.Message = fmt.Sprintf("failed to process JSON patches: %v", err) return resp, resource } // JSON patches processed successfully resp.Success = true resp.Message = fmt.Sprintf("successfully process JSON patches") resp.Patches = patches return resp, patchedResource }