Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add EvaluationMeta empty interface to each Result #263

Merged
merged 7 commits into from
Dec 7, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions constraint/pkg/client/drivers/local/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"sort"
"strings"
"sync"
"time"

"github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints"
"github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers"
Expand Down Expand Up @@ -72,6 +73,14 @@ type Driver struct {
clientCertWatcher *certwatcher.CertWatcher
}

// RegoEvaluationMeta has rego specific metadata from evaluation.
type RegoEvaluationMeta struct {
// TemplateRunTime is the number of milliseconds it took to evaluate all constraints for a template.
TemplateRunTime float64 `json:"templateRunTime"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add the units to this field?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we avoid that given that the godoc for the field calls out the units?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// ConstraintCount indicates how many constraints were evaluated for an underlying rego engine eval call.
ConstraintCount uint `json:"constraintCount"`
}

// AddTemplate adds templ to Driver. Normalizes modules into usable forms for
// use in queries.
func (d *Driver) AddTemplate(ctx context.Context, templ *templates.ConstraintTemplate) error {
Expand Down Expand Up @@ -238,6 +247,7 @@ func (d *Driver) Query(ctx context.Context, target string, constraints []*unstru
defer d.mtx.RUnlock()

for kind, kindConstraints := range constraintsByKind {
evalStartTime := time.Now()
compiler := d.compilers.getCompiler(target, kind)
if compiler == nil {
// The Template was just removed, so the Driver is in an inconsistent
Expand All @@ -254,6 +264,7 @@ func (d *Driver) Query(ctx context.Context, target string, constraints []*unstru
}

resultSet, trace, err := d.eval(ctx, compiler, target, path, parsedInput, opts...)
evalEndTime := time.Since(evalStartTime)
if err != nil {
resultSet = make(rego.ResultSet, 0, len(kindConstraints))
for _, constraint := range kindConstraints {
Expand All @@ -279,6 +290,13 @@ func (d *Driver) Query(ctx context.Context, target string, constraints []*unstru
return nil, nil, err
}

for _, result := range kindResults {
result.EvaluationMeta = RegoEvaluationMeta{
TemplateRunTime: float64(evalEndTime.Nanoseconds()) / 1000000,
ConstraintCount: uint(len(kindResults)),
}
}

results = append(results, kindResults...)
}

Expand Down
13 changes: 13 additions & 0 deletions constraint/pkg/client/drivers/local/driver_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,19 @@ func TestDriver_Query(t *testing.T) {
if len(res) == 0 {
t.Fatalf("got 0 errors on data-less query; want 1")
}

stats, ok := res[0].EvaluationMeta.(RegoEvaluationMeta)
if !ok {
t.Fatalf("could not type convert to RegoEvaluationMeta")
}

if stats.TemplateRunTime == 0 {
t.Fatalf("expected %v's value to be positive was zero", "TemplateRunTime")
}

if stats.ConstraintCount != uint(1) {
t.Fatalf("expected %v constraint count, got %v", 1, "ConstraintCount")
}
}

func TestDriver_ExternalData(t *testing.T) {
Expand Down
63 changes: 58 additions & 5 deletions constraint/pkg/client/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package client_test
import (
"context"
"errors"
"strconv"
"testing"

"github.com/google/go-cmp/cmp"
Expand Down Expand Up @@ -439,7 +440,7 @@ func TestClient_Review(t *testing.T) {

results := responses.Results()

diffOpt := cmpopts.IgnoreFields(types.Result{}, "Metadata")
diffOpt := cmpopts.IgnoreFields(types.Result{}, "Metadata", "EvaluationMeta")
if diff := cmp.Diff(tt.wantResults, results, diffOpt); diff != "" {
t.Error(diff)
}
Expand Down Expand Up @@ -487,7 +488,8 @@ func TestClient_Review_Details(t *testing.T) {

results := responses.Results()

if diff := cmp.Diff(want, results); diff != "" {
diffOpt := cmpopts.IgnoreFields(types.Result{}, "EvaluationMeta")
if diff := cmp.Diff(want, results, diffOpt); diff != "" {
t.Error(diff)
}
}
Expand Down Expand Up @@ -567,7 +569,7 @@ func TestClient_Review_Print(t *testing.T) {

results := rsps.Results()
if diff := cmp.Diff(tc.wantResults, results,
cmpopts.IgnoreFields(types.Result{}, "Metadata")); diff != "" {
cmpopts.IgnoreFields(types.Result{}, "Metadata", "EvaluationMeta")); diff != "" {
t.Error(diff)
}

Expand Down Expand Up @@ -605,7 +607,7 @@ func TestE2E_RemoveConstraint(t *testing.T) {
EnforcementAction: constraints.EnforcementActionDeny,
}}

if diff := cmp.Diff(want, got, cmpopts.IgnoreFields(types.Result{}, "Metadata")); diff != "" {
if diff := cmp.Diff(want, got, cmpopts.IgnoreFields(types.Result{}, "Metadata", "EvaluationMeta")); diff != "" {
t.Fatal(diff)
}

Expand Down Expand Up @@ -654,7 +656,7 @@ func TestE2E_RemoveTemplate(t *testing.T) {
EnforcementAction: constraints.EnforcementActionDeny,
}}

if diff := cmp.Diff(want, got, cmpopts.IgnoreFields(types.Result{}, "Metadata")); diff != "" {
if diff := cmp.Diff(want, got, cmpopts.IgnoreFields(types.Result{}, "Metadata", "EvaluationMeta")); diff != "" {
t.Fatal(diff)
}

Expand Down Expand Up @@ -727,3 +729,54 @@ func TestE2E_Tracing(t *testing.T) {
})
}
}

// TestE2E_Review_RegoEvaluationMeta tests that we can get stats out of evaluated constraints.
func TestE2E_Review_RegoEvaluationMeta(t *testing.T) {
ctx := context.Background()
c := clienttest.New(t)
ct := clienttest.TemplateCheckData()
_, err := c.AddTemplate(ctx, ct)
if err != nil {
t.Fatal(err)
}
numConstrains := 3

for i := 1; i < numConstrains+1; i++ {
name := "constraint-" + strconv.Itoa(i)
constraint := cts.MakeConstraint(t, clienttest.KindCheckData, name, cts.WantData("bar"))
_, err = c.AddConstraint(ctx, constraint)
if err != nil {
t.Fatal(err)
}
}

review := handlertest.Review{
Object: handlertest.Object{
Name: "foo",
Data: "qux",
},
}

responses, err := c.Review(ctx, review)
if err != nil {
t.Fatal(err)
}

results := responses.Results()

// for each result check that we have the constraintCount == 3 and a positive templateRunTime
for _, result := range results {
stats, ok := result.EvaluationMeta.(local.RegoEvaluationMeta)
if !ok {
t.Fatalf("could not type convert to RegoEvaluationMeta")
}

if stats.TemplateRunTime == 0 {
t.Fatalf("expected %v's value to be positive was zero", "TemplateRunTime")
}

if stats.ConstraintCount != uint(numConstrains) {
t.Fatalf("expected %v constraint count, got %v", numConstrains, "ConstraintCount")
}
}
}
3 changes: 3 additions & 0 deletions constraint/pkg/types/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ type Result struct {

// The enforcement action of the constraint
EnforcementAction string `json:"enforcementAction,omitempty"`

// EvaluationMeta has metadata for a Result's evaluation.
EvaluationMeta interface{} `json:"evaluationMeta,omitempty"`
}

// Response is a collection of Constraint violations for a particular Target.
Expand Down