diff --git a/staging/src/k8s.io/apiextensions-apiserver/go.mod b/staging/src/k8s.io/apiextensions-apiserver/go.mod index af27b69ba4f..da90a54ddc5 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/go.mod +++ b/staging/src/k8s.io/apiextensions-apiserver/go.mod @@ -21,7 +21,6 @@ require ( google.golang.org/grpc v1.40.0 google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b k8s.io/api v0.0.0 k8s.io/apimachinery v0.0.0 k8s.io/apiserver v0.0.0 diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/validation.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/validation.go index 3a29d9f7573..fdd69c22b6c 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/validation.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/validation.go @@ -18,15 +18,24 @@ package validation import ( "encoding/json" + "fmt" "strings" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker/decls" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/ext" + + expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" + + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" + "k8s.io/apiextensions-apiserver/pkg/apiserver/schema" + "k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model" + "k8s.io/apimachinery/pkg/util/validation/field" openapierrors "k8s.io/kube-openapi/pkg/validation/errors" "k8s.io/kube-openapi/pkg/validation/spec" "k8s.io/kube-openapi/pkg/validation/strfmt" "k8s.io/kube-openapi/pkg/validation/validate" - - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" - "k8s.io/apimachinery/pkg/util/validation/field" ) // NewSchemaValidator creates an openapi schema validator for the given CRD validation. @@ -330,3 +339,45 @@ func convertJSONSchemaPropsOrStringArray(in *apiextensions.JSONSchemaPropsOrStri } return nil } + +// CompileAndValidate provides a sanity check of the CEL validation functionality until we wire in the +// full functionality. +func CompileAndValidate(s *schema.Structural, bindings map[string]interface{}, rule string) (bool, error) { + env, err := cel.NewEnv() + if err != nil { + return false, err + } + reg := model.NewRegistry(env) + rt, err := model.NewRuleTypes("testType", s, reg) + if err != nil { + return false, err + } + opts, err := rt.EnvOptions(env.TypeProvider()) + if err != nil { + return false, err + } + root, ok := rt.FindDeclType("testType") + if !ok { + root = model.SchemaDeclType(s).MaybeAssignTypeName("testType") + } + propDecls := []*expr.Decl{decls.NewVar("self", root.ExprType())} + opts = append(opts, cel.Declarations(propDecls...)) + opts = append(opts, ext.Strings()) + env, err = env.Extend(opts...) + if err != nil { + return false, err + } + ast, issues := env.Compile(rule) + if issues != nil { + return false, fmt.Errorf("issues: %v", issues) + } + prog, err := env.Program(ast) + if err != nil { + return false, err + } + evalResult, _, err := prog.Eval(bindings) + if err != nil { + return false, err + } + return evalResult == types.True, nil +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/validation_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/validation_test.go index 2d71ead9e98..b80ccd42131 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/validation_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/validation_test.go @@ -23,6 +23,7 @@ import ( "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" apiextensionsfuzzer "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apiextensions-apiserver/pkg/apiserver/schema" "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" apiequality "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/runtime" @@ -484,3 +485,32 @@ func TestItemsProperty(t *testing.T) { }) } } + +// TestCompileAndValidate is a placeholder test of CEL validation that will be removed when the actual functionality +// is wired in. +func TestCompileAndValidate(t *testing.T) { + s := &schema.Structural{ + Generic: schema.Generic{ + Type: "object", + }, + Properties: map[string]schema.Structural{ + "name": { + Generic: schema.Generic{ + Type: "string", + }, + }, + }, + } + bindings := map[string]interface{}{ + "self": map[string]interface{}{ + "name": "kube", + }, + } + result, err := CompileAndValidate(s, bindings, "self.name == 'kube'") + if err != nil { + t.Fatal(err) + } + if !result { + t.Error("Expected expression to evaluate to true") + } +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/escaping.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/escaping.go index 3191ed64cff..238762e8595 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/escaping.go +++ b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/escaping.go @@ -23,6 +23,8 @@ import ( "k8s.io/apimachinery/pkg/util/sets" ) +// TODO: replace escaping with new rules described in kEP update + // celReservedSymbols is a list of RESERVED symbols defined in the CEL lexer. // No identifiers are allowed to collide with these symbols. // https://github.com/google/cel-spec/blob/master/doc/langdef.md#syntax diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/schemas.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/schemas.go index feab4148633..74b1c5ef009 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/schemas.go +++ b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/schemas.go @@ -128,6 +128,8 @@ var ( }) ) +// TODO: embedded objects should have objectMeta only, and name and generateName are both optional + // WithTypeAndObjectMeta ensures the kind, apiVersion and // metadata.name and metadata.generateName properties are specified, making a shallow copy of the provided schema if needed. func WithTypeAndObjectMeta(s *schema.Structural) *schema.Structural { diff --git a/vendor/modules.txt b/vendor/modules.txt index 8482cab4e66..1296d859687 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1354,7 +1354,6 @@ k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation k8s.io/apiextensions-apiserver/pkg/apiserver k8s.io/apiextensions-apiserver/pkg/apiserver/conversion k8s.io/apiextensions-apiserver/pkg/apiserver/schema -k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting k8s.io/apiextensions-apiserver/pkg/apiserver/schema/listtype k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta