package context import ( "encoding/json" "fmt" "strings" datautils "github.com/kyverno/kyverno/pkg/utils/data" ) // Query the JSON context with JMESPATH search path func (ctx *context) Query(query string) (interface{}, error) { if err := ctx.loadDeferred(query); err != nil { return nil, err } query = strings.TrimSpace(query) if query == "" { return nil, fmt.Errorf("invalid query (nil)") } // compile the query queryPath, err := ctx.jp.Query(query) if err != nil { logger.Error(err, "incorrect query", "query", query) return nil, fmt.Errorf("incorrect query %s: %v", query, err) } // search ctx.mutex.RLock() defer ctx.mutex.RUnlock() var data interface{} if err := json.Unmarshal(ctx.jsonRaw, &data); err != nil { return nil, fmt.Errorf("failed to unmarshal context: %w", err) } result, err := queryPath.Search(data) if err != nil { return nil, fmt.Errorf("JMESPath query failed: %w", err) } return result, nil } func (ctx *context) loadDeferred(query string) error { loaders := ctx.getMatchingLoaders(query) for _, l := range loaders { if err := l(); err != nil { return err } } return nil } func (ctx *context) getMatchingLoaders(query string) []DeferredLoader { ctx.deferred.mutex.Lock() defer ctx.deferred.mutex.Unlock() var matchingLoaders []DeferredLoader for name, deferredLoader := range ctx.deferred.loaders { if strings.Contains(query, name) { matchingLoaders = append(matchingLoaders, deferredLoader) delete(ctx.deferred.loaders, name) } } return matchingLoaders } func (ctx *context) HasChanged(jmespath string) (bool, error) { objData, err := ctx.Query("request.object." + jmespath) if err != nil { return false, fmt.Errorf("failed to query request.object: %w", err) } if objData == nil { return false, fmt.Errorf("request.object.%s not found", jmespath) } oldObjData, err := ctx.Query("request.oldObject." + jmespath) if err != nil { return false, fmt.Errorf("failed to query request.object: %w", err) } if oldObjData == nil { return false, fmt.Errorf("request.oldObject.%s not found", jmespath) } return !datautils.DeepEqual(objData, oldObjData), nil }