diff --git a/pkg/apis/v1alpha1/any.go b/pkg/apis/v1alpha1/any.go index cc8732ac..ad3bf042 100644 --- a/pkg/apis/v1alpha1/any.go +++ b/pkg/apis/v1alpha1/any.go @@ -13,7 +13,7 @@ type Any struct { // +kubebuilder:pruning:PreserveUnknownFields // +kubebuilder:validation:Schemaless // +optional - Value interface{} `json:",inline"` + Value any `json:",inline"` } func (in *Any) DeepCopyInto(out *Any) { @@ -36,7 +36,7 @@ func (a *Any) MarshalJSON() ([]byte, error) { } func (a *Any) UnmarshalJSON(data []byte) error { - var v interface{} + var v any err := json.Unmarshal(data, &v) if err != nil { return err diff --git a/pkg/apis/v1alpha1/any_test.go b/pkg/apis/v1alpha1/any_test.go index 95b85a5c..7846e6a0 100644 --- a/pkg/apis/v1alpha1/any_test.go +++ b/pkg/apis/v1alpha1/any_test.go @@ -25,7 +25,7 @@ func TestAny_DeepCopyInto(t *testing.T) { out: &Any{nil}, }, { name: "slice", - in: &Any{[]interface{}{42, "string"}}, + in: &Any{[]any{42, "string"}}, out: &Any{nil}, }} for _, tt := range tests { @@ -39,7 +39,7 @@ func TestAny_DeepCopyInto(t *testing.T) { func TestAny_MarshalJSON(t *testing.T) { tests := []struct { name string - value interface{} + value any want []byte wantErr bool }{{ @@ -59,7 +59,7 @@ func TestAny_MarshalJSON(t *testing.T) { wantErr: false, }, { name: "map", - value: map[string]interface{}{"foo": 42}, + value: map[string]any{"foo": 42}, want: []byte(`{"foo":42}`), wantErr: false, }, { @@ -108,7 +108,7 @@ func TestAny_UnmarshalJSON(t *testing.T) { }, { name: "map", data: []byte(`{"foo":42}`), - want: &Any{Value: map[string]interface{}{"foo": 42.0}}, + want: &Any{Value: map[string]any{"foo": 42.0}}, wantErr: false, }, { name: "error", diff --git a/pkg/commands/jp/query/command.go b/pkg/commands/jp/query/command.go index 040872bb..59a3eedb 100644 --- a/pkg/commands/jp/query/command.go +++ b/pkg/commands/jp/query/command.go @@ -126,21 +126,21 @@ func loadQueries(cmd *cobra.Command, args []string, files []string) ([]string, e return queries, nil } -func readInput(cmd *cobra.Command) (interface{}, error) { +func readInput(cmd *cobra.Command) (any, error) { fmt.Fprintln(cmd.OutOrStdout(), "Reading from terminal input.") fmt.Fprintln(cmd.OutOrStdout(), "Enter input object and hit Ctrl+D.") data, err := readFile(cmd.InOrStdin()) if err != nil { return nil, err } - var input interface{} + var input any if err := yaml.Unmarshal(data, &input); err != nil { return nil, fmt.Errorf("error parsing input json: %w", err) } return input, nil } -func loadInput(cmd *cobra.Command, file string) (interface{}, error) { +func loadInput(cmd *cobra.Command, file string) (any, error) { if file == "" { return nil, nil } @@ -148,14 +148,14 @@ func loadInput(cmd *cobra.Command, file string) (interface{}, error) { if err != nil { return nil, err } - var input interface{} + var input any if err := yaml.Unmarshal(data, &input); err != nil { return nil, fmt.Errorf("error parsing input json: %w", err) } return input, nil } -func evaluate(input interface{}, query string) (interface{}, error) { +func evaluate(input any, query string) (any, error) { result, err := template.Execute(context.Background(), query, input, nil) if err != nil { if syntaxError, ok := err.(parsing.SyntaxError); ok { @@ -166,7 +166,7 @@ func evaluate(input interface{}, query string) (interface{}, error) { return result, nil } -func printResult(cmd *cobra.Command, query string, result interface{}, unquoted bool, compact bool) error { +func printResult(cmd *cobra.Command, query string, result any, unquoted bool, compact bool) error { converted, isString := result.(string) fmt.Fprintln(cmd.OutOrStdout(), "#", query) if unquoted && isString { diff --git a/pkg/commands/scan/options.go b/pkg/commands/scan/options.go index 851762c4..3aaae08a 100644 --- a/pkg/commands/scan/options.go +++ b/pkg/commands/scan/options.go @@ -67,29 +67,33 @@ func (c *options) run(cmd *cobra.Command, _ []string) error { } payload = result } - var resources []interface{} - if slice, ok := payload.([]interface{}); ok { + var resources []any + if slice, ok := payload.([]any); ok { resources = slice } else { resources = append(resources, payload) } out.println("Running", "(", "evaluating", len(resources), pluralize.Pluralize(len(resources), "resource", "resources"), "against", len(policies), pluralize.Pluralize(len(policies), "policy", "policies"), ")", "...") e := jsonengine.New() - var responses []jsonengine.RuleResponse + var responses []jsonengine.Response for _, resource := range resources { responses = append(responses, e.Run(context.Background(), jsonengine.Request{ Resource: resource, Policies: policies, - })...) + })) } for _, response := range responses { - if response.Result == jsonengine.StatusFail { - out.println("-", response.PolicyName, "/", response.RuleName, "/", response.Identifier, "FAILED:", response.Message) - } else if response.Result == jsonengine.StatusError { - out.println("-", response.PolicyName, "/", response.RuleName, "/", response.Identifier, "ERROR:", response.Message) - } else { - // TODO: handle skip, warn - out.println("-", response.PolicyName, "/", response.RuleName, "/", response.Identifier, "PASSED") + for _, policy := range response.Policies { + for _, rule := range policy.Rules { + if rule.Result == jsonengine.StatusFail { + out.println("-", policy.Policy.Name, "/", rule.Rule.Name, "/", rule.Identifier, "FAILED:", rule.Message) + } else if rule.Result == jsonengine.StatusError { + out.println("-", policy.Policy.Name, "/", rule.Rule.Name, "/", rule.Identifier, "ERROR:", rule.Message) + } else { + // TODO: handle skip, warn + out.println("-", policy.Policy.Name, "/", rule.Rule.Name, "/", rule.Identifier, "PASSED") + } + } } } out.responses(responses...) diff --git a/pkg/commands/scan/output.go b/pkg/commands/scan/output.go index 80ab8d54..f6a1962d 100644 --- a/pkg/commands/scan/output.go +++ b/pkg/commands/scan/output.go @@ -10,7 +10,7 @@ import ( type output interface { println(args ...any) - responses(responses ...jsonengine.RuleResponse) + responses(responses ...jsonengine.Response) } type textOutput struct { @@ -21,7 +21,7 @@ func (t *textOutput) println(args ...any) { fmt.Fprintln(t.out, args...) } -func (t *textOutput) responses(responses ...jsonengine.RuleResponse) { +func (t *textOutput) responses(responses ...jsonengine.Response) { } type jsonOutput struct { @@ -31,8 +31,8 @@ type jsonOutput struct { func (t *jsonOutput) println(args ...any) { } -func (t *jsonOutput) responses(responses ...jsonengine.RuleResponse) { - payload, err := json.MarshalIndent(&jsonengine.Response{Results: responses}, "", " ") +func (t *jsonOutput) responses(responses ...jsonengine.Response) { + payload, err := json.MarshalIndent(responses, "", " ") if err != nil { fmt.Fprintln(t.out, err) } else { diff --git a/pkg/engine/blocks/constant/constant.go b/pkg/engine/blocks/constant/constant.go index 7c7ce9b0..ac4be520 100644 --- a/pkg/engine/blocks/constant/constant.go +++ b/pkg/engine/blocks/constant/constant.go @@ -7,15 +7,15 @@ import ( ) type constant[TREQUEST any, TRESPONSE any] struct { - responses []TRESPONSE + response TRESPONSE } -func (b *constant[TREQUEST, TRESPONSE]) Run(_ context.Context, _ TREQUEST) []TRESPONSE { - return b.responses +func (b *constant[TREQUEST, TRESPONSE]) Run(_ context.Context, _ TREQUEST) TRESPONSE { + return b.response } -func New[TREQUEST any, TRESPONSE any](responses ...TRESPONSE) engine.Engine[TREQUEST, TRESPONSE] { +func New[TREQUEST any, TRESPONSE any](response TRESPONSE) engine.Engine[TREQUEST, TRESPONSE] { return &constant[TREQUEST, TRESPONSE]{ - responses: responses, + response: response, } } diff --git a/pkg/engine/blocks/function/function.go b/pkg/engine/blocks/function/function.go index c2505a00..a5392c06 100644 --- a/pkg/engine/blocks/function/function.go +++ b/pkg/engine/blocks/function/function.go @@ -10,8 +10,8 @@ type function[TREQUEST any, TRESPONSE any] struct { function func(context.Context, TREQUEST) TRESPONSE } -func (b *function[TREQUEST, TRESPONSE]) Run(ctx context.Context, request TREQUEST) []TRESPONSE { - return []TRESPONSE{b.function(ctx, request)} +func (b *function[TREQUEST, TRESPONSE]) Run(ctx context.Context, request TREQUEST) TRESPONSE { + return b.function(ctx, request) } func New[TREQUEST any, TRESPONSE any](f func(context.Context, TREQUEST) TRESPONSE) engine.Engine[TREQUEST, TRESPONSE] { diff --git a/pkg/engine/blocks/loop/loop.go b/pkg/engine/blocks/loop/loop.go deleted file mode 100644 index 4da87a55..00000000 --- a/pkg/engine/blocks/loop/loop.go +++ /dev/null @@ -1,27 +0,0 @@ -package loop - -import ( - "context" - - "github.com/kyverno/kyverno-json/pkg/engine" -) - -type loop[TPARENT any, TCHILD any, TRESPONSE any] struct { - inner engine.Engine[TCHILD, TRESPONSE] - looper func(TPARENT) []TCHILD -} - -func (b *loop[TPARENT, TCHILD, TRESPONSE]) Run(ctx context.Context, parent TPARENT) []TRESPONSE { - var responses []TRESPONSE - for _, child := range b.looper(parent) { - responses = append(responses, b.inner.Run(ctx, child)...) - } - return responses -} - -func New[TPARENT any, TCHILD any, TRESPONSE any](inner engine.Engine[TCHILD, TRESPONSE], looper func(TPARENT) []TCHILD) engine.Engine[TPARENT, TRESPONSE] { - return &loop[TPARENT, TCHILD, TRESPONSE]{ - inner: inner, - looper: looper, - } -} diff --git a/pkg/engine/blocks/null/null.go b/pkg/engine/blocks/null/null.go deleted file mode 100644 index 34bf32c5..00000000 --- a/pkg/engine/blocks/null/null.go +++ /dev/null @@ -1,17 +0,0 @@ -package null - -import ( - "context" - - "github.com/kyverno/kyverno-json/pkg/engine" -) - -type null[TREQUEST any, TRESPONSE any] struct{} - -func (b *null[TREQUEST, TRESPONSE]) Run(_ context.Context, _ TREQUEST) []TRESPONSE { - return nil -} - -func New[TREQUEST any, TRESPONSE any]() engine.Engine[TREQUEST, TRESPONSE] { - return &null[TREQUEST, TRESPONSE]{} -} diff --git a/pkg/engine/blocks/predicate/predicate.go b/pkg/engine/blocks/predicate/predicate.go deleted file mode 100644 index 9e8b9274..00000000 --- a/pkg/engine/blocks/predicate/predicate.go +++ /dev/null @@ -1,26 +0,0 @@ -package predicate - -import ( - "context" - - "github.com/kyverno/kyverno-json/pkg/engine" -) - -type predicate[TREQUEST any, TRESPONSE any] struct { - inner engine.Engine[TREQUEST, TRESPONSE] - predicate func(context.Context, TREQUEST) bool -} - -func (b *predicate[TREQUEST, TRESPONSE]) Run(ctx context.Context, request TREQUEST) []TRESPONSE { - if !b.predicate(ctx, request) { - return nil - } - return b.inner.Run(ctx, request) -} - -func New[TREQUEST any, TRESPONSE any](inner engine.Engine[TREQUEST, TRESPONSE], condition func(context.Context, TREQUEST) bool) engine.Engine[TREQUEST, TRESPONSE] { - return &predicate[TREQUEST, TRESPONSE]{ - inner: inner, - predicate: condition, - } -} diff --git a/pkg/engine/builder/builder.go b/pkg/engine/builder/builder.go index 4ca4bc22..673e39e2 100644 --- a/pkg/engine/builder/builder.go +++ b/pkg/engine/builder/builder.go @@ -6,7 +6,6 @@ import ( "github.com/kyverno/kyverno-json/pkg/engine" "github.com/kyverno/kyverno-json/pkg/engine/blocks/constant" "github.com/kyverno/kyverno-json/pkg/engine/blocks/function" - "github.com/kyverno/kyverno-json/pkg/engine/blocks/predicate" ) type Engine[TREQUEST any, TRESPONSE any] struct { @@ -17,12 +16,8 @@ func new[TREQUEST any, TRESPONSE any](engine engine.Engine[TREQUEST, TRESPONSE]) return Engine[TREQUEST, TRESPONSE]{engine} } -func Constant[TREQUEST any, TRESPONSE any](responses ...TRESPONSE) Engine[TREQUEST, TRESPONSE] { - return new(constant.New[TREQUEST](responses...)) -} - -func (inner Engine[TREQUEST, TRESPONSE]) Predicate(condition func(context.Context, TREQUEST) bool) Engine[TREQUEST, TRESPONSE] { - return new(predicate.New(inner, condition)) +func Constant[TREQUEST any, TRESPONSE any](response TRESPONSE) Engine[TREQUEST, TRESPONSE] { + return new(constant.New[TREQUEST](response)) } func Function[TREQUEST any, TRESPONSE any](f func(context.Context, TREQUEST) TRESPONSE) Engine[TREQUEST, TRESPONSE] { diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 3a332989..167b6d8f 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -9,5 +9,5 @@ import ( // - explain type Engine[TREQUEST any, TRESPONSE any] interface { - Run(context.Context, TREQUEST) []TRESPONSE + Run(context.Context, TREQUEST) TRESPONSE } diff --git a/pkg/json-engine/engine.go b/pkg/json-engine/engine.go index 4a8e2ca3..7126bc57 100644 --- a/pkg/json-engine/engine.go +++ b/pkg/json-engine/engine.go @@ -8,7 +8,6 @@ import ( "github.com/kyverno/kyverno-json/pkg/apis/v1alpha1" "github.com/kyverno/kyverno-json/pkg/binding" "github.com/kyverno/kyverno-json/pkg/engine" - "github.com/kyverno/kyverno-json/pkg/engine/blocks/loop" "github.com/kyverno/kyverno-json/pkg/engine/builder" "github.com/kyverno/kyverno-json/pkg/engine/template" "github.com/kyverno/kyverno-json/pkg/matching" @@ -16,112 +15,155 @@ import ( ) type Request struct { - Resource interface{} + Resource any Policies []*v1alpha1.ValidatingPolicy } type Response struct { - Results []RuleResponse `json:"results"` + Resource any + Policies []PolicyResponse +} + +type PolicyResponse struct { + Policy *v1alpha1.ValidatingPolicy + Rules []RuleResponse } type RuleResponse struct { - PolicyName string `json:"policy"` - RuleName string `json:"rule"` - Identifier string `json:"identifier,omitempty"` - Result PolicyResult `json:"result"` - Message string `json:"message"` + Rule v1alpha1.ValidatingRule + Identifier string + Result PolicyResult + Message string } // PolicyResult specifies state of a policy result type PolicyResult string const ( - StatusPass PolicyResult = "pass" - StatusFail PolicyResult = "fail" - StatusWarn PolicyResult = "warn" + StatusPass PolicyResult = "pass" + StatusFail PolicyResult = "fail" + // StatusWarn PolicyResult = "warn" StatusError PolicyResult = "error" - StatusSkip PolicyResult = "skip" + // StatusSkip PolicyResult = "skip" ) -type request struct { - policy *v1alpha1.ValidatingPolicy - rule v1alpha1.ValidatingRule - value interface{} - bindings jpbinding.Bindings -} - -func New() engine.Engine[Request, RuleResponse] { - looper := func(r Request) []request { - var requests []request - bindings := jpbinding.NewBindings() - bindings = bindings.Register("$payload", jpbinding.NewBinding(r.Resource)) - for _, policy := range r.Policies { - bindings = bindings.Register("$policy", jpbinding.NewBinding(policy)) - for _, rule := range policy.Spec.Rules { - bindings = bindings.Register("$rule", jpbinding.NewBinding(rule)) - bindings = binding.NewContextBindings(bindings, r.Resource, rule.Context...) - requests = append(requests, request{ - policy: policy, +func New() engine.Engine[Request, Response] { + type ruleRequest struct { + rule v1alpha1.ValidatingRule + resource any + bindings jpbinding.Bindings + } + type policyRequest struct { + policy *v1alpha1.ValidatingPolicy + resource any + bindings jpbinding.Bindings + } + ruleEngine := builder. + Function(func(ctx context.Context, r ruleRequest) []RuleResponse { + bindings := r.bindings.Register("$rule", jpbinding.NewBinding(r.rule)) + bindings = binding.NewContextBindings(bindings, r.resource, r.rule.Context...) + identifier := "" + if r.rule.Identifier != "" { + result, err := template.Execute(context.Background(), r.rule.Identifier, r.resource, bindings) + if err != nil { + identifier = fmt.Sprintf("(error: %s)", err) + } else { + identifier = fmt.Sprint(result) + } + } + if r.rule.Match != nil { + errs, err := matching.Match(ctx, nil, r.rule.Match, r.resource, bindings) + if err != nil { + return []RuleResponse{{ + Rule: r.rule, + Identifier: identifier, + Result: StatusError, + Message: err.Error(), + }} + } + // didn't match + if len(errs) != 0 { + return nil + } + } + if r.rule.Exclude != nil { + errs, err := matching.Match(ctx, nil, r.rule.Exclude, r.resource, bindings) + if err != nil { + return []RuleResponse{{ + Rule: r.rule, + Identifier: identifier, + Result: StatusError, + Message: err.Error(), + }} + } + // matched + if len(errs) == 0 { + return nil + } + } + errs, err := matching.MatchAssert(ctx, nil, r.rule.Assert, r.resource, bindings) + if err != nil { + return []RuleResponse{{ + Rule: r.rule, + Identifier: identifier, + Result: StatusError, + Message: err.Error(), + }} + } + if len(errs) == 0 { + return []RuleResponse{{ + Rule: r.rule, + Identifier: identifier, + Result: StatusPass, + Message: "", + }} + } + return []RuleResponse{{ + Rule: r.rule, + Identifier: identifier, + Result: StatusFail, + Message: multierr.Combine(errs...).Error(), + }} + // var failures []RuleResponse + // for _, err := range errs { + // failures = append(failures, RuleResponse{ + // Rule: r.rule, + // Identifier: identifier, + // Result: StatusFail, + // Message: err.Error(), + // }) + // } + // return failures + }) + policyEngine := builder. + Function(func(ctx context.Context, r policyRequest) PolicyResponse { + response := PolicyResponse{ + Policy: r.policy, + } + bindings := r.bindings.Register("$policy", jpbinding.NewBinding(r.policy)) + for _, rule := range r.policy.Spec.Rules { + response.Rules = append(response.Rules, ruleEngine.Run(ctx, ruleRequest{ rule: rule, - value: r.Resource, - bindings: bindings, - }) + resource: r.resource, + bindings: bindings.Register("$rule", jpbinding.NewBinding(rule)), + })...) } - } - return requests - } - inner := builder. - Function(func(ctx context.Context, r request) RuleResponse { - errs, err := matching.MatchAssert(ctx, nil, r.rule.Assert, r.value, r.bindings) - response := buildResponse(r, errs, err) return response - }). - Predicate(func(ctx context.Context, r request) bool { - if r.rule.Exclude == nil { - return true + }) + resourceEngine := builder. + Function(func(ctx context.Context, r Request) Response { + response := Response{ + Resource: r.Resource, } - errs, err := matching.Match(ctx, nil, r.rule.Exclude, r.value, r.bindings) - // TODO: handle error and skip - return err == nil && len(errs) != 0 - }). - Predicate(func(ctx context.Context, r request) bool { - if r.rule.Match == nil { - return true + bindings := jpbinding.NewBindings().Register("$payload", jpbinding.NewBinding(r.Resource)) + for _, policy := range r.Policies { + response.Policies = append(response.Policies, policyEngine.Run(ctx, policyRequest{ + policy: policy, + resource: r.Resource, + bindings: bindings, + })) } - errs, err := matching.Match(ctx, nil, r.rule.Match, r.value, r.bindings) - // TODO: handle error and skip - return err == nil && len(errs) == 0 + return response }) - // TODO: we can't use the builder package for loops :( - return loop.New(inner, looper) -} - -func buildResponse(req request, fails []error, ruleErr error) RuleResponse { - response := RuleResponse{ - PolicyName: req.policy.Name, - RuleName: req.rule.Name, - } - - response.Identifier = "" - if req.rule.Identifier != "" { - result, subjectErr := template.Execute(context.Background(), req.rule.Identifier, req.value, nil) - if subjectErr != nil { - response.Identifier = fmt.Sprintf("(error: %s)", subjectErr) - } else { - response.Identifier = fmt.Sprint(result) - } - } - - if ruleErr != nil { - response.Result = StatusError - response.Message = ruleErr.Error() - } else if err := multierr.Combine(fails...); err != nil { - response.Result = StatusFail - response.Message = err.Error() - } else { - // TODO: handle skip result - response.Result = StatusPass - } - - return response + return resourceEngine } diff --git a/pkg/payload/load.go b/pkg/payload/load.go index e50ff440..b4e5fcaa 100644 --- a/pkg/payload/load.go +++ b/pkg/payload/load.go @@ -11,12 +11,12 @@ import ( "gopkg.in/yaml.v3" ) -func Load(path string) (interface{}, error) { +func Load(path string) (any, error) { content, err := os.ReadFile(filepath.Clean(path)) if err != nil { return nil, err } - var payload interface{} + var payload any switch { case file.IsJson(path): if err := json.Unmarshal(content, &payload); err != nil { @@ -27,9 +27,9 @@ func Load(path string) (interface{}, error) { if err != nil { return nil, err } - var objects []interface{} + var objects []any for _, document := range documents { - var object map[string]interface{} + var object map[string]any if err := yaml.Unmarshal(document, &object); err != nil { return nil, err } diff --git a/pkg/policy/load_test.go b/pkg/policy/load_test.go index 380475e1..0193e242 100644 --- a/pkg/policy/load_test.go +++ b/pkg/policy/load_test.go @@ -60,7 +60,7 @@ func TestLoad(t *testing.T) { Name: "pod-no-latest", Match: &v1alpha1.Match{ Any: []v1alpha1.Any{{ - Value: map[string]interface{}{ + Value: map[string]any{ "apiVersion": "v1", "kind": "Pod", }, @@ -69,10 +69,10 @@ func TestLoad(t *testing.T) { Assert: &v1alpha1.Assert{ All: []v1alpha1.Assertion{{ Check: v1alpha1.Any{ - Value: map[string]interface{}{ - "spec": map[string]interface{}{ - "~foo.containers->foos": map[string]interface{}{ - "(at($foos, $foo).image)->foo": map[string]interface{}{ + Value: map[string]any{ + "spec": map[string]any{ + "~foo.containers->foos": map[string]any{ + "(at($foos, $foo).image)->foo": map[string]any{ "(contains($foo, ':'))": true, "(ends_with($foo, ':latest'))": false, }, diff --git a/pkg/server/model/response.go b/pkg/server/model/response.go new file mode 100644 index 00000000..3af29365 --- /dev/null +++ b/pkg/server/model/response.go @@ -0,0 +1,36 @@ +package model + +import ( + jsonengine "github.com/kyverno/kyverno-json/pkg/json-engine" +) + +type Response struct { + Results []RuleResponse `json:"results"` +} + +type RuleResponse struct { + PolicyName string `json:"policy"` + RuleName string `json:"rule"` + Identifier string `json:"identifier,omitempty"` + Result jsonengine.PolicyResult `json:"result"` + Message string `json:"message"` +} + +func MakeResponse(from ...jsonengine.Response) Response { + var response Response + for _, resource := range from { + for _, policy := range resource.Policies { + for _, rule := range policy.Rules { + ruleResponse := RuleResponse{ + PolicyName: policy.Policy.Name, + RuleName: rule.Rule.Name, + Identifier: rule.Identifier, + Result: rule.Result, + Message: rule.Message, + } + response.Results = append(response.Results, ruleResponse) + } + } + } + return response +} diff --git a/pkg/server/playground/handler.go b/pkg/server/playground/handler.go index 30e6f7f6..773562a6 100644 --- a/pkg/server/playground/handler.go +++ b/pkg/server/playground/handler.go @@ -10,12 +10,13 @@ import ( "github.com/kyverno/kyverno-json/pkg/apis/v1alpha1" "github.com/kyverno/kyverno-json/pkg/engine/template" jsonengine "github.com/kyverno/kyverno-json/pkg/json-engine" + "github.com/kyverno/kyverno-json/pkg/server/model" "github.com/loopfz/gadgeto/tonic" "sigs.k8s.io/yaml" ) func newHandler() (gin.HandlerFunc, error) { - return tonic.Handler(func(ctx *gin.Context, in *Request) (*jsonengine.Response, error) { + return tonic.Handler(func(ctx *gin.Context, in *Request) (*model.Response, error) { // check input if in == nil { return nil, errors.New("input is null") @@ -26,7 +27,7 @@ func newHandler() (gin.HandlerFunc, error) { if in.Policy == "" { return nil, errors.New("input policy is null") } - var payload interface{} + var payload any err := yaml.Unmarshal([]byte(in.Payload), &payload) if err != nil { return nil, fmt.Errorf("failed to parse payload (%w)", err) @@ -43,8 +44,8 @@ func newHandler() (gin.HandlerFunc, error) { payload = result } // load resources - var resources []interface{} - if slice, ok := payload.([]interface{}); ok { + var resources []any + if slice, ok := payload.([]any); ok { resources = slice } else { resources = append(resources, payload) @@ -56,13 +57,14 @@ func newHandler() (gin.HandlerFunc, error) { } // run engine e := jsonengine.New() - var results []jsonengine.RuleResponse + var results []jsonengine.Response for _, resource := range resources { results = append(results, e.Run(context.Background(), jsonengine.Request{ Resource: resource, Policies: []*v1alpha1.ValidatingPolicy{&policy}, - })...) + })) } - return &jsonengine.Response{Results: results}, nil + response := model.MakeResponse(results...) + return &response, nil }, http.StatusOK), nil } diff --git a/pkg/server/scan/handler.go b/pkg/server/scan/handler.go index e59d0184..7c45e5dc 100644 --- a/pkg/server/scan/handler.go +++ b/pkg/server/scan/handler.go @@ -10,11 +10,12 @@ import ( "github.com/kyverno/kyverno-json/pkg/apis/v1alpha1" "github.com/kyverno/kyverno-json/pkg/engine/template" jsonengine "github.com/kyverno/kyverno-json/pkg/json-engine" + "github.com/kyverno/kyverno-json/pkg/server/model" "github.com/loopfz/gadgeto/tonic" ) func newHandler(policyProvider PolicyProvider) (gin.HandlerFunc, error) { - return tonic.Handler(func(ctx *gin.Context, in *Request) (*jsonengine.Response, error) { + return tonic.Handler(func(ctx *gin.Context, in *Request) (*model.Response, error) { // check input if in == nil { return nil, errors.New("input is null") @@ -35,8 +36,8 @@ func newHandler(policyProvider PolicyProvider) (gin.HandlerFunc, error) { payload = result } // load resources - var resources []interface{} - if slice, ok := payload.([]interface{}); ok { + var resources []any + if slice, ok := payload.([]any); ok { resources = slice } else { resources = append(resources, payload) @@ -52,14 +53,15 @@ func newHandler(policyProvider PolicyProvider) (gin.HandlerFunc, error) { } // run engine e := jsonengine.New() - var results []jsonengine.RuleResponse + var results []jsonengine.Response for _, resource := range resources { results = append(results, e.Run(context.Background(), jsonengine.Request{ Resource: resource, Policies: pols, - })...) + })) } // TODO: return HTTP 403 for policy failure and HTTP 406 for policy errors - return &jsonengine.Response{Results: results}, nil + response := model.MakeResponse(results...) + return &response, nil }, http.StatusOK), nil } diff --git a/pkg/server/scan/request.go b/pkg/server/scan/request.go index 09566724..132cb882 100644 --- a/pkg/server/scan/request.go +++ b/pkg/server/scan/request.go @@ -1,6 +1,6 @@ package scan type Request struct { - Payload interface{} `json:"payload"` - Preprocessors []string `json:"preprocessors"` + Payload any `json:"payload"` + Preprocessors []string `json:"preprocessors"` } diff --git a/pkg/utils/reflect/kind.go b/pkg/utils/reflect/kind.go index 01aa09b4..6a1a116a 100644 --- a/pkg/utils/reflect/kind.go +++ b/pkg/utils/reflect/kind.go @@ -4,7 +4,7 @@ import ( "reflect" ) -func GetKind(value interface{}) reflect.Kind { +func GetKind(value any) reflect.Kind { if value == nil { return reflect.Invalid } diff --git a/pkg/utils/reflect/kind_test.go b/pkg/utils/reflect/kind_test.go index 08201e6a..64c4f5d8 100644 --- a/pkg/utils/reflect/kind_test.go +++ b/pkg/utils/reflect/kind_test.go @@ -8,7 +8,7 @@ import ( func TestGetKind(t *testing.T) { tests := []struct { name string - value interface{} + value any want reflect.Kind }{{ name: "nil", @@ -32,11 +32,11 @@ func TestGetKind(t *testing.T) { want: reflect.String, }, { name: "map", - value: map[interface{}]interface{}{}, + value: map[any]any{}, want: reflect.Map, }, { name: "slice", - value: []interface{}{}, + value: []any{}, want: reflect.Slice, }} for _, tt := range tests { diff --git a/pkg/utils/reflect/match.go b/pkg/utils/reflect/match.go index ea6b807f..1a35c6d4 100644 --- a/pkg/utils/reflect/match.go +++ b/pkg/utils/reflect/match.go @@ -5,7 +5,7 @@ import ( "reflect" ) -func MatchScalar(expected, actual interface{}) (bool, error) { +func MatchScalar(expected, actual any) (bool, error) { if actual == nil && expected == nil { return true, nil } else if actual == nil && expected != nil { diff --git a/pkg/utils/reflect/number_test.go b/pkg/utils/reflect/number_test.go index 70d44ca7..d8a99779 100644 --- a/pkg/utils/reflect/number_test.go +++ b/pkg/utils/reflect/number_test.go @@ -8,7 +8,7 @@ import ( func TestToNumber(t *testing.T) { tests := []struct { name string - value interface{} + value any want float64 wantOk bool }{{ diff --git a/test/api/go/main/main.go b/test/api/go/main/main.go index 35d98145..58cde6bd 100644 --- a/test/api/go/main/main.go +++ b/test/api/go/main/main.go @@ -44,7 +44,7 @@ func main() { } }` - var payload interface{} + var payload any if err := json.Unmarshal([]byte(requestJSON), &payload); err != nil { panic(err) } @@ -59,17 +59,20 @@ func main() { engine := jsonengine.New() // apply polices to get the response - responses := engine.Run(context.Background(), request) + response := engine.Run(context.Background(), request) // process the engine response logger := log.Default() - for _, r := range responses { - if r.Result == jsonengine.StatusFail { - logger.Printf("fail: %s/%s -> %s: %s", r.PolicyName, r.RuleName, r.Identifier, r.Message) - } else if r.Result == jsonengine.StatusError { - logger.Printf("error: %s/%s -> %s: %s", r.PolicyName, r.RuleName, r.Identifier, r.Message) - } else { - logger.Printf("%s: %s/%s -> %s", r.Result, r.PolicyName, r.RuleName, r.Identifier) + + for _, policy := range response.Policies { + for _, rule := range policy.Rules { + if rule.Result == jsonengine.StatusFail { + logger.Printf("fail: %s/%s -> %s: %s", policy.Policy.Name, rule.Rule.Name, rule.Identifier, rule.Message) + } else if rule.Result == jsonengine.StatusError { + logger.Printf("error: %s/%s -> %s: %s", policy.Policy.Name, rule.Rule.Name, rule.Identifier, rule.Message) + } else { + logger.Printf("%s: %s/%s -> %s", rule.Result, policy.Policy.Name, rule.Rule.Name, rule.Identifier) + } } } } diff --git a/website/catalog/main.go b/website/catalog/main.go index db1fd601..4d150cd9 100644 --- a/website/catalog/main.go +++ b/website/catalog/main.go @@ -145,7 +145,7 @@ func main() { panic(err) } defer mkdocs.Close() - if err := template.Execute(mkdocs, map[string]interface{}{ + if err := template.Execute(mkdocs, map[string]any{ "Policies": pols, }); err != nil { panic(err) diff --git a/website/docs/apis/kyverno-json.v1alpha1.md b/website/docs/apis/kyverno-json.v1alpha1.md index 5e9b8db5..8fd72599 100644 --- a/website/docs/apis/kyverno-json.v1alpha1.md +++ b/website/docs/apis/kyverno-json.v1alpha1.md @@ -54,7 +54,7 @@ auto_generated: true | Field | Type | Required | Inline | Description | |---|---|---|---|---| -| `Value` | `interface{}` | | :white_check_mark: |

Value contains the value of the Any object.

| +| `Value` | `any` | | :white_check_mark: |

Value contains the value of the Any object.

| ## `Assert` {#json-kyverno-io-v1alpha1-Assert} diff --git a/website/docs/go-library/index.md b/website/docs/go-library/index.md index 21176b38..cc2c8d82 100644 --- a/website/docs/go-library/index.md +++ b/website/docs/go-library/index.md @@ -58,14 +58,14 @@ func main() { } }` - var payload interface{} + var payload any if err := json.Unmarshal([]byte(requestJSON), &payload); err != nil { panic(err) } // create a JsonEngineRequest request := jsonengine.JsonEngineRequest{ - Resources: []interface{}{payload}, + Resources: []any{payload}, Policies: policies, } diff --git a/website/playground-examples/main.go b/website/playground-examples/main.go index 5598c608..0ff98d86 100644 --- a/website/playground-examples/main.go +++ b/website/playground-examples/main.go @@ -34,7 +34,7 @@ func load(file string) string { if err != nil { panic(err) } - var obj interface{} + var obj any if err := json.Unmarshal(content, &obj); err != nil { panic(err) }