diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/LICENSE b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/LICENSE new file mode 100644 index 00000000000..7a4a3ea2424 --- /dev/null +++ b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/README.md b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/README.md new file mode 100644 index 00000000000..6b38d871535 --- /dev/null +++ b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/README.md @@ -0,0 +1,4 @@ +This directory contains a port of https://github.com/google/cel-policy-templates-go/tree/master/policy/model modified in a few ways: + +- Uses the Structural schema types +- All template related code has been removed diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/decisions.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/decisions.go deleted file mode 100644 index aa99f7efd80..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/decisions.go +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "fmt" - "strings" - - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" -) - -// NewDecision returns an empty Decision instance. -func NewDecision() *Decision { - return &Decision{} -} - -// Decision contains a decision name, or reference to a decision name, and an output expression. -type Decision struct { - Name string - Reference *cel.Ast - Output *cel.Ast -} - -// DecisionValue represents a named decision and value. -type DecisionValue interface { - fmt.Stringer - - // Name returns the decision name. - Name() string - - // IsFinal returns whether the decision value will change with additional rule evaluations. - // - // When a decision is final, additional productions and rules which may also trigger the same - // decision may be skipped. - IsFinal() bool -} - -// SingleDecisionValue extends the DecisionValue which contains a single decision value as well -// as some metadata about the evaluation details and the rule that spawned the value. -type SingleDecisionValue interface { - DecisionValue - - // Value returns the single value for the decision. - Value() ref.Val - - // Details returns the evaluation details, if present, that produced the value. - Details() *cel.EvalDetails - - // RuleID indicate which policy rule id within an instance that produced the decision. - RuleID() int64 -} - -// MultiDecisionValue extends the DecisionValue which contains a set of decision values as well as -// the corresponding metadata about how each value was produced. -type MultiDecisionValue interface { - DecisionValue - - // Values returns the collection of values produced for the decision. - Values() []ref.Val - - // Details returns the evaluation details for each value in the decision. - // The value index correponds to the details index. The details may be nil. - Details() []*cel.EvalDetails - - // RulesIDs returns the rule id within an instance which produce the decision values. - // The value index corresponds to the rule id index. - RuleIDs() []int64 -} - -// DecisionSelector determines whether the given decision is the decision set requested by the -// caller. -type DecisionSelector func(decision string) bool - -// NewBoolDecisionValue returns a boolean decision with an initial value. -func NewBoolDecisionValue(name string, value types.Bool) *BoolDecisionValue { - return &BoolDecisionValue{ - name: name, - value: value, - } -} - -// BoolDecisionValue represents the decision value type associated with a decision. -type BoolDecisionValue struct { - name string - value ref.Val - isFinal bool - details *cel.EvalDetails - ruleID int64 -} - -// And logically ANDs the current decision value with the incoming CEL value. -// -// And follows CEL semantics with respect to errors and unknown values where errors may be -// absorbed or short-circuited away by subsequent 'false' values. When unkonwns are encountered -// the unknown values combine and aggregate within the decision. Unknowns may also be absorbed -// per CEL semantics. -func (dv *BoolDecisionValue) And(other ref.Val) *BoolDecisionValue { - v, vBool := dv.value.(types.Bool) - if vBool && v == types.False { - return dv - } - o, oBool := other.(types.Bool) - if oBool && o == types.False { - dv.value = types.False - return dv - } - if vBool && oBool { - return dv - } - dv.value = logicallyMergeUnkErr(dv.value, other) - return dv -} - -// Details implements the SingleDecisionValue interface method. -func (dv *BoolDecisionValue) Details() *cel.EvalDetails { - return dv.details -} - -// Finalize marks the decision as immutable with additional input and indicates the rule and -// evaluation details which triggered the finalization. -func (dv *BoolDecisionValue) Finalize(details *cel.EvalDetails, rule Rule) DecisionValue { - dv.details = details - if rule != nil { - dv.ruleID = rule.GetID() - } - dv.isFinal = true - return dv -} - -// IsFinal returns whether the decision is final. -func (dv *BoolDecisionValue) IsFinal() bool { - return dv.isFinal -} - -// Or logically ORs the decision value with the incoming CEL value. -// -// The ORing logic follows CEL semantics with respect to errors and unknown values. -// Errors may be absorbed or short-circuited away by subsequent 'true' values. When unkonwns are -// encountered the unknown values combine and aggregate within the decision. Unknowns may also be -// absorbed per CEL semantics. -func (dv *BoolDecisionValue) Or(other ref.Val) *BoolDecisionValue { - v, vBool := dv.value.(types.Bool) - if vBool && v == types.True { - return dv - } - o, oBool := other.(types.Bool) - if oBool && o == types.True { - dv.value = types.True - return dv - } - if vBool && oBool { - return dv - } - dv.value = logicallyMergeUnkErr(dv.value, other) - return dv -} - -// Name implements the DecisionValue interface method. -func (dv *BoolDecisionValue) Name() string { - return dv.name -} - -// RuleID implements the SingleDecisionValue interface method. -func (dv *BoolDecisionValue) RuleID() int64 { - return dv.ruleID -} - -// String renders the decision value to a string for debug purposes. -func (dv *BoolDecisionValue) String() string { - var buf strings.Builder - buf.WriteString(dv.name) - buf.WriteString(": ") - buf.WriteString(fmt.Sprintf("rule[%d] -> ", dv.ruleID)) - buf.WriteString(fmt.Sprintf("%v", dv.value)) - return buf.String() -} - -// Value implements the SingleDecisionValue interface method. -func (dv *BoolDecisionValue) Value() ref.Val { - return dv.value -} - -// NewListDecisionValue returns a named decision value which contains a list of CEL values produced -// by one or more policy instances and / or production rules. -func NewListDecisionValue(name string) *ListDecisionValue { - return &ListDecisionValue{ - name: name, - values: []ref.Val{}, - details: []*cel.EvalDetails{}, - ruleIDs: []int64{}, - } -} - -// ListDecisionValue represents a named decision which collects into a list of values. -type ListDecisionValue struct { - name string - values []ref.Val - isFinal bool - details []*cel.EvalDetails - ruleIDs []int64 -} - -// Append accumulates the incoming CEL value into the decision's value list. -func (dv *ListDecisionValue) Append(val ref.Val, det *cel.EvalDetails, rule Rule) { - dv.values = append(dv.values, val) - dv.details = append(dv.details, det) - // Rule ids may be null if the policy is a singleton. - ruleID := int64(0) - if rule != nil { - ruleID = rule.GetID() - } - dv.ruleIDs = append(dv.ruleIDs, ruleID) -} - -// Details returns the list of evaluation details observed in computing the values in the decision. -// The details indices correlate 1:1 with the value indices. -func (dv *ListDecisionValue) Details() []*cel.EvalDetails { - return dv.details -} - -// Finalize marks the list decision complete. -func (dv *ListDecisionValue) Finalize() DecisionValue { - dv.isFinal = true - return dv -} - -// IsFinal implements the DecisionValue interface method. -func (dv *ListDecisionValue) IsFinal() bool { - return dv.isFinal -} - -// Name implements the DecisionValue interface method. -func (dv *ListDecisionValue) Name() string { - return dv.name -} - -// RuleIDs returns the list of rule ids which produced the evaluation results. -// The indices of the ruleIDs correlate 1:1 with the value indices. -func (dv *ListDecisionValue) RuleIDs() []int64 { - return dv.ruleIDs -} - -func (dv *ListDecisionValue) String() string { - var buf strings.Builder - buf.WriteString(dv.name) - buf.WriteString(": ") - for i, v := range dv.values { - if len(dv.ruleIDs) == len(dv.values) { - buf.WriteString(fmt.Sprintf("rule[%d] -> ", dv.ruleIDs[i])) - } - buf.WriteString(fmt.Sprintf("%v", v)) - buf.WriteString("\n") - if i < len(dv.values)-1 { - buf.WriteString("\t") - } - } - return buf.String() -} - -// Values implements the MultiDecisionValue interface method. -func (dv *ListDecisionValue) Values() []ref.Val { - return dv.values -} - -func logicallyMergeUnkErr(value, other ref.Val) ref.Val { - vUnk := types.IsUnknown(value) - oUnk := types.IsUnknown(other) - if vUnk && oUnk { - merged := types.Unknown{} - merged = append(merged, value.(types.Unknown)...) - merged = append(merged, other.(types.Unknown)...) - return merged - } - if vUnk { - return value - } - if oUnk { - return other - } - if types.IsError(value) { - return value - } - if types.IsError(other) { - return other - } - return types.NewErr( - "got values (%v, %v), wanted boolean values", - value, other) -} diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/decisions_test.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/decisions_test.go deleted file mode 100644 index 0623fcfcbc8..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/decisions_test.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "reflect" - "testing" - - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" -) - -func TestBoolDecisionValue_And(t *testing.T) { - tests := []struct { - name string - value types.Bool - ands []ref.Val - result ref.Val - }{ - { - name: "init_false_end_false", - value: types.False, - ands: []ref.Val{types.NewErr("err"), types.True}, - result: types.False, - }, - { - name: "init_true_end_false", - value: types.True, - ands: []ref.Val{types.NewErr("err"), types.False}, - result: types.False, - }, - { - name: "init_true_end_err", - value: types.True, - ands: []ref.Val{types.True, types.NewErr("err")}, - result: types.NewErr("err"), - }, - { - name: "init_true_end_unk", - value: types.True, - ands: []ref.Val{types.True, types.Unknown{1}, types.NewErr("err"), types.Unknown{2}}, - result: types.Unknown{1, 2}, - }, - } - for _, tst := range tests { - tc := tst - t.Run(tc.name, func(tt *testing.T) { - v := NewBoolDecisionValue(tc.name, tc.value) - for _, av := range tc.ands { - v = v.And(av) - } - v.Finalize(nil, nil) - if !reflect.DeepEqual(v.Value(), tc.result) { - tt.Errorf("decision AND failed. got %v, wanted %v", v.Value(), tc.result) - } - }) - } -} - -func TestBoolDecisionValue_Or(t *testing.T) { - tests := []struct { - name string - value types.Bool - ors []ref.Val - result ref.Val - }{ - { - name: "init_false_end_true", - value: types.False, - ors: []ref.Val{types.NewErr("err"), types.Unknown{1}, types.True}, - result: types.True, - }, - { - name: "init_true_end_true", - value: types.True, - ors: []ref.Val{types.NewErr("err"), types.False}, - result: types.True, - }, - { - name: "init_false_end_err", - value: types.False, - ors: []ref.Val{types.False, types.NewErr("err1"), types.NewErr("err2")}, - result: types.NewErr("err1"), - }, - { - name: "init_false_end_unk", - value: types.False, - ors: []ref.Val{types.False, types.Unknown{1}, types.NewErr("err"), types.Unknown{2}}, - result: types.Unknown{1, 2}, - }, - } - for _, tst := range tests { - tc := tst - t.Run(tc.name, func(tt *testing.T) { - v := NewBoolDecisionValue(tc.name, tc.value) - for _, av := range tc.ors { - v = v.Or(av) - } - // Test finalization - v.Finalize(nil, nil) - // Ensure that calling string on the value doesn't error. - _ = v.String() - // Compare the output result - if !reflect.DeepEqual(v.Value(), tc.result) { - tt.Errorf("decision OR failed. got %v, wanted %v", v.Value(), tc.result) - } - }) - } -} diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/env.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/env.go deleted file mode 100644 index ddc1d803259..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/env.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker/decls" - - exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" -) - -// NewEnv creates an empty Env instance with a fully qualified name that may be referenced -// within templates. -func NewEnv(name string) *Env { - return &Env{ - Name: name, - Functions: []*Function{}, - Vars: []*Var{}, - Types: map[string]*DeclType{}, - } -} - -// Env declares a set of variables, functions, and types available to a given set of CEL -// expressions. -// -// The Env name must be fully qualified as it will be referenced within template evaluators, -// validators, and possibly within the metadata of the instance rule schema. -// -// Note, the Types values currently only holds type definitions associated with a variable -// declaration. Any type mentioned in the environment which does not have a definition is -// treated as a reference to a type which must be supplied in the base CEL environment provided -// by the policy engine. -type Env struct { - Name string - Container string - Functions []*Function - Vars []*Var - Types map[string]*DeclType -} - -// ExprEnvOptions returns a set of CEL environment options to be used when extending the base -// policy engine CEL environment. -func (e *Env) ExprEnvOptions() []cel.EnvOption { - opts := []cel.EnvOption{} - if e.Container != "" { - opts = append(opts, cel.Container(e.Container)) - } - if len(e.Vars) > 0 { - vars := make([]*exprpb.Decl, len(e.Vars)) - for i, v := range e.Vars { - vars[i] = v.ExprDecl() - } - opts = append(opts, cel.Declarations(vars...)) - } - if len(e.Functions) > 0 { - funcs := make([]*exprpb.Decl, len(e.Functions)) - for i, f := range e.Functions { - funcs[i] = f.ExprDecl() - } - opts = append(opts, cel.Declarations(funcs...)) - } - return opts -} - -// NewVar creates a new variable with a name and a type. -func NewVar(name string, dt *DeclType) *Var { - return &Var{ - Name: name, - Type: dt, - } -} - -// Var represents a named instanced of a type. -type Var struct { - Name string - Type *DeclType -} - -// ExprDecl produces a CEL proto declaration for the variable. -func (v *Var) ExprDecl() *exprpb.Decl { - return decls.NewVar(v.Name, v.Type.ExprType()) -} - -// NewFunction creates a Function instance with a simple function name and a set of overload -// signatures. -func NewFunction(name string, overloads ...*Overload) *Function { - return &Function{ - Name: name, - Overloads: overloads, - } -} - -// Function represents a simple name and a set of overload signatures. -type Function struct { - Name string - Overloads []*Overload -} - -// ExprDecl produces a CEL proto declaration for the function and its overloads. -func (f *Function) ExprDecl() *exprpb.Decl { - overloadDecls := make([]*exprpb.Decl_FunctionDecl_Overload, len(f.Overloads)) - for i, o := range f.Overloads { - overloadDecls[i] = o.overloadDecl() - } - return decls.NewFunction(f.Name, overloadDecls...) -} - -// NewOverload returns a receiver-style overload declaration for a given function. -// -// The overload name must follow the conventions laid out within the CEL overloads.go file. -// -// // Receiver-style overload name: -// ___ -// -// Within this function, the first type supplied is the receiver type, and the last type supplied -// is used as the return type. At least two types must be specified for a zero-arity receiver -// function. -func NewOverload(name string, first *DeclType, rest ...*DeclType) *Overload { - argTypes := make([]*DeclType, 1+len(rest)) - argTypes[0] = first - for i := 1; i < len(rest)+1; i++ { - argTypes[i] = rest[i-1] - } - returnType := argTypes[len(argTypes)-1] - argTypes = argTypes[0 : len(argTypes)-1] - return newOverload(name, false, argTypes, returnType) -} - -// NewFreeFunctionOverload returns a free function overload for a given function name. -// -// The overload name must follow the conventions laid out within the CEL overloads.go file: -// -// // Free function style overload name: -// __ -// -// When the function name is global, will refer to the simple function name. When the -// function has a qualified name, replace the '.' characters in the fully-qualified name with -// underscores. -// -// Within this function, the last type supplied is used as the return type. At least one type must -// be specified for a zero-arity free function. -func NewFreeFunctionOverload(name string, first *DeclType, rest ...*DeclType) *Overload { - argTypes := make([]*DeclType, 1+len(rest)) - argTypes[0] = first - for i := 1; i < len(rest)+1; i++ { - argTypes[i] = rest[i-1] - } - returnType := argTypes[len(argTypes)-1] - argTypes = argTypes[0 : len(argTypes)-1] - return newOverload(name, true, argTypes, returnType) -} - -func newOverload(name string, - freeFunction bool, - argTypes []*DeclType, - returnType *DeclType) *Overload { - return &Overload{ - Name: name, - FreeFunction: freeFunction, - Args: argTypes, - ReturnType: returnType, - } -} - -// Overload represents a single function overload signature. -type Overload struct { - Name string - FreeFunction bool - Args []*DeclType - ReturnType *DeclType -} - -func (o *Overload) overloadDecl() *exprpb.Decl_FunctionDecl_Overload { - typeParams := map[string]struct{}{} - argExprTypes := make([]*exprpb.Type, len(o.Args)) - for i, a := range o.Args { - if a.TypeParam { - typeParams[a.TypeName()] = struct{}{} - } - argExprTypes[i] = a.ExprType() - } - returnType := o.ReturnType.ExprType() - if len(typeParams) == 0 { - if o.FreeFunction { - return decls.NewOverload(o.Name, argExprTypes, returnType) - } - return decls.NewInstanceOverload(o.Name, argExprTypes, returnType) - } - typeParamNames := make([]string, 0, len(typeParams)) - for param := range typeParams { - typeParamNames = append(typeParamNames, param) - } - if o.FreeFunction { - return decls.NewParameterizedOverload(o.Name, argExprTypes, returnType, typeParamNames) - } - return decls.NewParameterizedInstanceOverload(o.Name, argExprTypes, returnType, typeParamNames) -} diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/env_test.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/env_test.go deleted file mode 100644 index d36016f6ff6..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/env_test.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "testing" - - "github.com/google/cel-go/cel" -) - -func TestEnv_Vars(t *testing.T) { - env := NewEnv("test.v1.Environment") - env.Container = "test.v1" - env.Vars = []*Var{ - NewVar("greeting", StringType), - NewVar("replies", NewListType(StringType)), - } - expr := `greeting == 'hello' && replies.size() > 0` - stdEnv, _ := cel.NewEnv() - ast, iss := stdEnv.Compile(expr) - if iss.Err() == nil { - t.Errorf("got ast %v, expected error", ast) - } - custEnv, err := stdEnv.Extend(env.ExprEnvOptions()...) - if err != nil { - t.Fatal(err) - } - _, iss = custEnv.Compile(expr) - if iss.Err() != nil { - t.Errorf("got error %v, wanted ast", iss) - } -} - -func TestEnv_Funcs(t *testing.T) { - env := NewEnv("test.v1.Environment") - env.Container = "test.v1" - env.Functions = []*Function{ - NewFunction("greeting", - NewOverload("string_greeting_string", StringType, StringType, BoolType), - NewFreeFunctionOverload("greeting_string", StringType, BoolType)), - NewFunction("getOrDefault", - NewOverload("map_get_or_default_param", - NewMapType(NewTypeParam("K"), NewTypeParam("V")), - NewTypeParam("K"), NewTypeParam("V"), - NewTypeParam("V"))), - } - expr := `greeting('hello') && 'jim'.greeting('hello') && {'a': 0}.getOrDefault('b', 1) == 1` - stdEnv, _ := cel.NewEnv() - ast, iss := stdEnv.Compile(expr) - if iss.Err() == nil { - t.Errorf("got ast %v, expected error", ast) - } - custEnv, err := stdEnv.Extend(env.ExprEnvOptions()...) - if err != nil { - t.Fatal(err) - } - _, iss = custEnv.Compile(expr) - if iss.Err() != nil { - t.Errorf("got error %v, wanted ast", iss) - } -} 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 new file mode 100644 index 00000000000..3191ed64cff --- /dev/null +++ b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/escaping.go @@ -0,0 +1,80 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package model + +import ( + "fmt" + "strings" + + "k8s.io/apimachinery/pkg/util/sets" +) + +// 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 +var celReservedSymbols = sets.NewString( + "true", "false", "null", "in", + "as", "break", "const", "continue", "else", + "for", "function", "if", "import", "let", + "loop", "package", "namespace", "return", // !! 'namespace' is used heavily in Kubernetes + "var", "void", "while", +) + +// celLanguageIdentifiers is a list of identifiers that are part of the CEL language. +// This does NOT include builtin macro or function identifiers. +// https://github.com/google/cel-spec/blob/master/doc/langdef.md#values +var celLanguageIdentifiers = sets.NewString( + "int", "uint", "double", "bool", "string", "bytes", "list", "map", "null_type", "type", +) + +// IsRootReserved returns true if an identifier is reserved by CEL. Declaring root variables in CEL with +// these identifiers is not allowed and would result in an "overlapping identifier for name ''" +// CEL compilation error. +func IsRootReserved(prop string) bool { + return celLanguageIdentifiers.Has(prop) +} + +// Escape escapes identifiers in the AlwaysReservedIdentifiers set by prefixing ident with "_" and by prefixing +// any ident already prefixed with N '_' with N+1 '_'. +// For an identifier that does not require escaping, the identifier is returned as-is. +func Escape(ident string) string { + if strings.HasPrefix(ident, "_") || celReservedSymbols.Has(ident) { + return "_" + ident + } + return ident +} + +// EscapeSlice returns identifiers with Escape applied to each. +func EscapeSlice(idents []string) []string { + result := make([]string, len(idents)) + for i, prop := range idents { + result[i] = Escape(prop) + } + return result +} + +// Unescape unescapes an identifier escaped by Escape. +func Unescape(escaped string) string { + if strings.HasPrefix(escaped, "_") { + trimmed := strings.TrimPrefix(escaped, "_") + if strings.HasPrefix(trimmed, "_") || celReservedSymbols.Has(trimmed) { + return trimmed + } + panic(fmt.Sprintf("failed to unescape improperly escaped string: %v", escaped)) + } + return escaped +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/escaping_test.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/escaping_test.go new file mode 100644 index 00000000000..c0fee93ecc5 --- /dev/null +++ b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/escaping_test.go @@ -0,0 +1,115 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package model + +import ( + "testing" +) + +// TestEscaping tests that property names are escaped as expected. +func TestEscaping(t *testing.T) { + cases := []struct{ + unescaped string + escaped string + reservedAtRoot bool + } { + // CEL lexer RESERVED keywords must be escaped + { unescaped: "true", escaped: "_true" }, + { unescaped: "false", escaped: "_false" }, + { unescaped: "null", escaped: "_null" }, + { unescaped: "in", escaped: "_in" }, + { unescaped: "as", escaped: "_as" }, + { unescaped: "break", escaped: "_break" }, + { unescaped: "const", escaped: "_const" }, + { unescaped: "continue", escaped: "_continue" }, + { unescaped: "else", escaped: "_else" }, + { unescaped: "for", escaped: "_for" }, + { unescaped: "function", escaped: "_function" }, + { unescaped: "if", escaped: "_if" }, + { unescaped: "import", escaped: "_import" }, + { unescaped: "let", escaped: "_let" }, + { unescaped: "loop", escaped: "_loop" }, + { unescaped: "package", escaped: "_package" }, + { unescaped: "namespace", escaped: "_namespace" }, + { unescaped: "return", escaped: "_return" }, + { unescaped: "var", escaped: "_var" }, + { unescaped: "void", escaped: "_void" }, + { unescaped: "while", escaped: "_while" }, + // CEL language identifiers do not need to be escaped, but collide with builtin language identifier if bound as + // root variable names. + // i.e. "self.int == 1" is legal, but "int == 1" is not. + { unescaped: "int", escaped: "int", reservedAtRoot: true }, + { unescaped: "uint", escaped: "uint", reservedAtRoot: true }, + { unescaped: "double", escaped: "double", reservedAtRoot: true }, + { unescaped: "bool", escaped: "bool", reservedAtRoot: true }, + { unescaped: "string", escaped: "string", reservedAtRoot: true }, + { unescaped: "bytes", escaped: "bytes", reservedAtRoot: true }, + { unescaped: "list", escaped: "list", reservedAtRoot: true }, + { unescaped: "map", escaped: "map", reservedAtRoot: true }, + { unescaped: "null_type", escaped: "null_type", reservedAtRoot: true }, + { unescaped: "type", escaped: "type", reservedAtRoot: true }, + // To prevent escaping from colliding with other identifiers, all identifiers prefixed by _s are escaped by + // prefixing them with N+1 _s. + { unescaped: "_if", escaped: "__if" }, + { unescaped: "__if", escaped: "___if" }, + { unescaped: "___if", escaped: "____if" }, + { unescaped: "_has", escaped: "__has" }, + { unescaped: "_int", escaped: "__int" }, + { unescaped: "_anything", escaped: "__anything" }, + // CEL macro and function names do not need to be escaped because the parser can disambiguate them from the function and + // macro identifiers. + { unescaped: "has", escaped: "has" }, + { unescaped: "all", escaped: "all" }, + { unescaped: "exists", escaped: "exists" }, + { unescaped: "exists_one", escaped: "exists_one" }, + { unescaped: "filter", escaped: "filter" }, + { unescaped: "size", escaped: "size" }, + { unescaped: "contains", escaped: "contains" }, + { unescaped: "startsWith", escaped: "startsWith" }, + { unescaped: "endsWith", escaped: "endsWith" }, + { unescaped: "matches", escaped: "matches" }, + { unescaped: "duration", escaped: "duration" }, + { unescaped: "timestamp", escaped: "timestamp" }, + { unescaped: "getDate", escaped: "getDate" }, + { unescaped: "getDayOfMonth", escaped: "getDayOfMonth" }, + { unescaped: "getDayOfWeek", escaped: "getDayOfWeek" }, + { unescaped: "getFullYear", escaped: "getFullYear" }, + { unescaped: "getHours", escaped: "getHours" }, + { unescaped: "getMilliseconds", escaped: "getMilliseconds" }, + { unescaped: "getMinutes", escaped: "getMinutes" }, + { unescaped: "getMonth", escaped: "getMonth" }, + { unescaped: "getSeconds", escaped: "getSeconds" }, + } + + for _, tc := range cases { + t.Run(tc.unescaped, func(t *testing.T) { + e := Escape(tc.unescaped) + if tc.escaped != e { + t.Errorf("Expected %s to escape to %s, but got %s", tc.unescaped, tc.escaped, e) + } + u := Unescape(tc.escaped) + if tc.unescaped != u { + t.Errorf("Expected %s to unescape to %s, but got %s", tc.escaped, tc.unescaped, e) + } + + isRootReserved := IsRootReserved(tc.unescaped) + if tc.reservedAtRoot != isRootReserved { + t.Errorf("Expected isRootReserved=%t for %s, but got %t", tc.reservedAtRoot, tc.unescaped, isRootReserved) + } + }) + } +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/instance.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/instance.go deleted file mode 100644 index 20c70b6a28a..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/instance.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package model contains abstract representations of policy template and instance config objects. -package model - -import ( - "strings" -) - -// NewInstance returns an empty policy instance. -func NewInstance(info SourceMetadata) *Instance { - return &Instance{ - Metadata: &InstanceMetadata{}, - Selectors: []Selector{}, - Rules: []Rule{}, - Meta: info, - } -} - -// Instance represents the compiled, type-checked, and validated policy instance. -type Instance struct { - APIVersion string - Kind string - Metadata *InstanceMetadata - Description string - - // Selectors determine whether the instance applies to the current evaluation context. - // All Selector values must return true for the policy instance to be included in policy - // evaluation step. - Selectors []Selector - - // Rules represent reference data to be used in evaluation policy decisions. - // Depending on the nature of the decisions being emitted, some or all Rules may be evaluated - // and the results aggregated according to the decision types being emitted. - Rules []Rule - - // Meta represents the source metadata from the input instance. - Meta SourceMetadata -} - -// MetadataMap returns the metadata name to value map, which can be used in evaluation. -// Only "name" field is supported for now. -func (i *Instance) MetadataMap() map[string]interface{} { - return map[string]interface{}{ - "name": i.Metadata.Name, - } -} - -// InstanceMetadata contains standard metadata which may be associated with an instance. -type InstanceMetadata struct { - UID string - Name string - Namespace string -} - -// Selector interface indicates a pre-formatted instance selection condition. -// -// The implementations of such conditions are expected to be platform specific. -// -// Note, if there is a clear need to tailor selection more heavily, then the schema definition -// for a selector should be moved into the Template schema. -type Selector interface { - isSelector() -} - -// LabelSelector matches key, value pairs of labels associated with the evaluation context. -// -// In Kubernetes, the such labels are provided as 'resource.labels'. -type LabelSelector struct { - // LabelValues provides a map of the string keys and values expected. - LabelValues map[string]string -} - -func (*LabelSelector) isSelector() {} - -// ExpressionSelector matches a label against an existence condition. -type ExpressionSelector struct { - // Label name being matched. - Label string - - // Operator determines the evaluation behavior. Must be one of Exists, NotExists, In, or NotIn. - Operator string - - // Values set, optional, to be used in the NotIn, In set membership tests. - Values []interface{} -} - -func (*ExpressionSelector) isSelector() {} - -// Rule interface indicates the value types that may be used as Rule instances. -// -// Note, the code within the main repo deals exclusively with custom, yaml-based rules, but it -// is entirely possible to use a protobuf message as the rule container. -type Rule interface { - isRule() - GetID() int64 - GetFieldID(field string) int64 -} - -// CustomRule embeds the DynValue and represents rules whose type definition is provided in the -// policy template. -type CustomRule struct { - *DynValue -} - -func (*CustomRule) isRule() {} - -// GetID returns the parse-time generated ID of the rule node. -func (c *CustomRule) GetID() int64 { - return c.ID -} - -// GetFieldID returns the parse-time generated ID pointing to the rule field. If field is not -// specified or is not found, falls back to the ID of the rule node. -func (c *CustomRule) GetFieldID(field string) int64 { - if field == "" { - return c.GetID() - } - paths := strings.Split(field, ".") - val := c.DynValue - for _, path := range paths { - var f *Field - var ok bool - switch v := val.Value().(type) { - case *ObjectValue: - f, ok = v.GetField(path) - case *MapValue: - f, ok = v.GetField(path) - } - if !ok { - return c.GetID() - } - val = f.Ref - } - return val.ID -} diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/registry.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/registry.go index 99b316ac127..a5ed7295053 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/registry.go +++ b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/registry.go @@ -15,32 +15,14 @@ package model import ( - "fmt" "sync" "github.com/google/cel-go/cel" + "k8s.io/apiextensions-apiserver/pkg/apiserver/schema" ) // Resolver declares methods to find policy templates and related configuration objects. type Resolver interface { - // FindEnv returns an Env object by its fully-qualified name, if present. - FindEnv(name string) (*Env, bool) - - // FindExprEnv returns a CEL expression environment by its fully-qualified name, if present. - // - // Note, the CEL expression environment name corresponds with the model Environment name; - // however, the expression environment may inherit configuration via the CEL env.Extend method. - FindExprEnv(name string) (*cel.Env, bool) - - // FindSchema returns an Open API Schema instance by name, if present. - // - // Schema names start with a `#` sign as this method is only used to resolve references to - // relative schema elements within `$ref` schema nodes. - FindSchema(name string) (*OpenAPISchema, bool) - - // FindTemplate returns a Template by its fully-qualified name, if present. - FindTemplate(name string) (*Template, bool) - // FindType returns a DeclType instance corresponding to the given fully-qualified name, if // present. FindType(name string) (*DeclType, bool) @@ -50,25 +32,15 @@ type Resolver interface { // from a base cel.Env expression environment. func NewRegistry(stdExprEnv *cel.Env) *Registry { return &Registry{ - envs: map[string]*Env{}, exprEnvs: map[string]*cel.Env{"": stdExprEnv}, - schemas: map[string]*OpenAPISchema{ - "#anySchema": AnySchema, - "#envSchema": envSchema, - "#instanceSchema": instanceSchema, - "#openAPISchema": schemaDef, - "#templateSchema": templateSchema, - }, - templates: map[string]*Template{}, + schemas: map[string]*schema.Structural{}, types: map[string]*DeclType{ - AnyType.TypeName(): AnyType, BoolType.TypeName(): BoolType, BytesType.TypeName(): BytesType, DoubleType.TypeName(): DoubleType, DurationType.TypeName(): DurationType, IntType.TypeName(): IntType, NullType.TypeName(): NullType, - PlainTextType.TypeName(): PlainTextType, StringType.TypeName(): StringType, TimestampType.TypeName(): TimestampType, UintType.TypeName(): UintType, @@ -82,44 +54,10 @@ func NewRegistry(stdExprEnv *cel.Env) *Registry { // // Registry instances are concurrency-safe. type Registry struct { - rwMux sync.RWMutex - envs map[string]*Env - exprEnvs map[string]*cel.Env - schemas map[string]*OpenAPISchema - templates map[string]*Template - types map[string]*DeclType -} - -// FindEnv implements the Resolver interface method. -func (r *Registry) FindEnv(name string) (*Env, bool) { - r.rwMux.RLock() - defer r.rwMux.RUnlock() - env, found := r.envs[name] - return env, found -} - -// FindExprEnv implements the Resolver interface method. -func (r *Registry) FindExprEnv(name string) (*cel.Env, bool) { - r.rwMux.RLock() - defer r.rwMux.RUnlock() - exprEnv, found := r.exprEnvs[name] - return exprEnv, found -} - -// FindSchema implements the Resolver interface method. -func (r *Registry) FindSchema(name string) (*OpenAPISchema, bool) { - r.rwMux.RLock() - defer r.rwMux.RUnlock() - schema, found := r.schemas[name] - return schema, found -} - -// FindTemplate implements the Resolver interface method. -func (r *Registry) FindTemplate(name string) (*Template, bool) { - r.rwMux.RLock() - defer r.rwMux.RUnlock() - tmpl, found := r.templates[name] - return tmpl, found + rwMux sync.RWMutex + exprEnvs map[string]*cel.Env + schemas map[string]*schema.Structural + types map[string]*DeclType } // FindType implements the Resolver interface method. @@ -133,53 +71,6 @@ func (r *Registry) FindType(name string) (*DeclType, bool) { return typ, found } -// SetEnv registers an environment description by fully qualified name. -func (r *Registry) SetEnv(name string, env *Env) error { - r.rwMux.Lock() - defer r.rwMux.Unlock() - // Cleanup environment related artifacts when the env is reset. - priorEnv, found := r.envs[name] - if found { - for typeName := range priorEnv.Types { - delete(r.types, typeName) - } - } - // Configure the new environment. - baseExprEnv, found := r.exprEnvs[""] - if !found { - return fmt.Errorf("missing default expression environment") - } - exprEnv, err := baseExprEnv.Extend(env.ExprEnvOptions()...) - if err != nil { - return err - } - r.exprEnvs[name] = exprEnv - r.envs[name] = env - for typeName, typ := range env.Types { - r.types[typeName] = typ - } - return nil -} - -// SetSchema registers an OpenAPISchema fragment by its relative name so that it may be referenced -// as a reusable schema unit within other OpenAPISchema instances. -// -// Name format: '#'. -func (r *Registry) SetSchema(name string, schema *OpenAPISchema) error { - r.rwMux.Lock() - defer r.rwMux.Unlock() - r.schemas[name] = schema - return nil -} - -// SetTemplate registers a template by its fully qualified name. -func (r *Registry) SetTemplate(name string, tmpl *Template) error { - r.rwMux.Lock() - defer r.rwMux.Unlock() - r.templates[name] = tmpl - return nil -} - // SetType registers a DeclType descriptor by its fully qualified name. func (r *Registry) SetType(name string, declType *DeclType) error { r.rwMux.Lock() 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 440f19ac450..feab4148633 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 @@ -15,437 +15,151 @@ package model import ( - "strings" - - "gopkg.in/yaml.v3" + "k8s.io/apiextensions-apiserver/pkg/apiserver/schema" ) -// NewOpenAPISchema returns an empty instance of an OpenAPISchema object. -func NewOpenAPISchema() *OpenAPISchema { - return &OpenAPISchema{ - Enum: []interface{}{}, - Metadata: map[string]string{}, - Properties: map[string]*OpenAPISchema{}, - Required: []string{}, - } -} -// OpenAPISchema declares a struct capable of representing a subset of Open API Schemas -// supported by Kubernetes which can also be specified within Protocol Buffers. -// -// There are a handful of notable differences: -// - The validating constructs `allOf`, `anyOf`, `oneOf`, `not`, and type-related restrictsion are -// not supported as they can be better validated in the template 'validator' block. -// - The $ref field supports references to other schema definitions, but such aliases -// should be removed before being serialized. -// - The `additionalProperties` and `properties` fields are not currently mutually exclusive as is -// the case for Kubernetes. -// -// See: https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#validation -type OpenAPISchema struct { - Title string `yaml:"title,omitempty"` - Description string `yaml:"description,omitempty"` - Type string `yaml:"type,omitempty"` - TypeParam string `yaml:"type_param,omitempty"` - TypeRef string `yaml:"$ref,omitempty"` - DefaultValue interface{} `yaml:"default,omitempty"` - Enum []interface{} `yaml:"enum,omitempty"` - Format string `yaml:"format,omitempty"` - Items *OpenAPISchema `yaml:"items,omitempty"` - Metadata map[string]string `yaml:"metadata,omitempty"` - Required []string `yaml:"required,omitempty"` - Properties map[string]*OpenAPISchema `yaml:"properties,omitempty"` - AdditionalProperties *OpenAPISchema `yaml:"additionalProperties,omitempty"` -} - -// DeclTypes constructs a top-down set of DeclType instances whose name is derived from the root +// SchemaDeclTypes constructs a top-down set of DeclType instances whose name is derived from the root // type name provided on the call, if not set to a custom type. -func (s *OpenAPISchema) DeclTypes(maybeRootType string) (*DeclType, map[string]*DeclType) { - root := s.DeclType().MaybeAssignTypeName(maybeRootType) +func SchemaDeclTypes(s *schema.Structural, maybeRootType string) (*DeclType, map[string]*DeclType) { + root := SchemaDeclType(s).MaybeAssignTypeName(maybeRootType) types := FieldTypeMap(maybeRootType, root) return root, types } -// DeclType returns the CEL Policy Templates type name associated with the schema element. -func (s *OpenAPISchema) DeclType() *DeclType { - if s.TypeParam != "" { - return NewTypeParam(s.TypeParam) +// SchemaDeclType returns the cel type name associated with the schema element. +func SchemaDeclType(s *schema.Structural) *DeclType { + if s == nil { + return nil + } + if s.XIntOrString { + // schemas using this extension are not required to have a type, so they must be handled before type lookup + return intOrStringType } declType, found := openAPISchemaTypes[s.Type] if !found { - return NewObjectTypeRef("*error*") - } - switch declType.TypeName() { - case ListType.TypeName(): - return NewListType(s.Items.DeclType()) - case MapType.TypeName(): - if s.AdditionalProperties != nil { - return NewMapType(StringType, s.AdditionalProperties.DeclType()) - } - fields := make(map[string]*DeclField, len(s.Properties)) - required := make(map[string]struct{}, len(s.Required)) - for _, name := range s.Required { - required[name] = struct{}{} - } - for name, prop := range s.Properties { - _, isReq := required[name] - fields[name] = &DeclField{ - Name: name, - Required: isReq, - Type: prop.DeclType(), - defaultValue: prop.DefaultValue, - enumValues: prop.Enum, - } - } - customType, hasCustomType := s.Metadata["custom_type"] - if !hasCustomType { - return NewObjectType("object", fields) - } - return NewObjectType(customType, fields) - case StringType.TypeName(): - switch s.Format { - case "byte", "binary": - return BytesType - case "google-duration": - return DurationType - case "date", "date-time", "google-datetime": - return TimestampType - case "int64": - return IntType - case "uint64": - return UintType - } + return nil } + // We ignore XPreserveUnknownFields since we don't support validation rules on + // data that we don't have schema information for. + + if s.XEmbeddedResource { + // 'apiVersion', 'kind', 'metadata.name' and 'metadata.generateName' are always accessible + // to validation rules since this part of the schema is well known and validated when CRDs + // are created and updated. + s = WithTypeAndObjectMeta(s) + } + + switch declType.TypeName() { + case ListType.TypeName(): + return NewListType(SchemaDeclType(s.Items)) + case MapType.TypeName(): + if s.AdditionalProperties != nil && s.AdditionalProperties.Structural != nil { + return NewMapType(StringType, SchemaDeclType(s.AdditionalProperties.Structural)) + } + fields := make(map[string]*DeclField, len(s.Properties)) + + required := map[string]bool{} + if s.ValueValidation != nil { + for _, f := range s.ValueValidation.Required { + required[f] = true + } + } + for name, prop := range s.Properties { + var enumValues []interface{} + if prop.ValueValidation != nil { + for _, e := range prop.ValueValidation.Enum { + enumValues = append(enumValues, e.Object) + } + } + if fieldType := SchemaDeclType(&prop); fieldType != nil { + fields[Escape(name)] = &DeclField{ + Name: Escape(name), + Required: required[name], + Type: fieldType, + defaultValue: prop.Default.Object, + enumValues: enumValues, // Enum values are represented as strings in CEL + } + } + } + return NewObjectType("object", fields) + case StringType.TypeName(): + if s.ValueValidation != nil { + switch s.ValueValidation.Format { + case "byte": + return StringType // OpenAPIv3 byte format represents base64 encoded string + case "binary": + return BytesType + case "duration": + return DurationType + case "date", "date-time": + return TimestampType + } + } + } return declType } -// FindProperty returns the Open API Schema type for the given property name. -// -// A property may either be explicitly defined in a `properties` map or implicitly defined in an -// `additionalProperties` block. -func (s *OpenAPISchema) FindProperty(name string) (*OpenAPISchema, bool) { - if s.DeclType() == AnyType { - return s, true - } - if s.Properties != nil { - prop, found := s.Properties[name] - if found { - return prop, true - } - } - if s.AdditionalProperties != nil { - return s.AdditionalProperties, true - } - return nil, false -} - var ( - // SchemaDef defines an Open API Schema definition in terms of an Open API Schema. - schemaDef *OpenAPISchema - - // AnySchema indicates that the value may be of any type. - AnySchema *OpenAPISchema - - // EnvSchema defines the schema for CEL environments referenced within Policy Templates. - envSchema *OpenAPISchema - - // InstanceSchema defines a basic schema for defining Policy Instances where the instance rule - // references a TemplateSchema derived from the Instance's template kind. - instanceSchema *OpenAPISchema - - // TemplateSchema defines a schema for defining Policy Templates. - templateSchema *OpenAPISchema - - openAPISchemaTypes map[string]*DeclType = map[string]*DeclType{ + openAPISchemaTypes = map[string]*DeclType{ "boolean": BoolType, "number": DoubleType, "integer": IntType, "null": NullType, "string": StringType, - "google-duration": DurationType, - "google-datetime": TimestampType, - "date": TimestampType, - "date-time": TimestampType, + "date": DateType, "array": ListType, "object": MapType, - "": AnyType, } + + // intOrStringType represents the x-kubernetes-int-or-string union type in CEL expressions. + // In CEL, the type is represented as an object where either the srtVal + // or intVal field is set. In CEL, this allows for typesafe expressions like: + // + // require that the string representation be a percentage: + // `has(intOrStringField.strVal) && intOrStringField.strVal.matches(r'(\d+(\.\d+)?%)')` + // validate requirements on both the int and string representation: + // `has(intOrStringField.intVal) ? intOrStringField.intVal < 5 : double(intOrStringField.strVal.replace('%', '')) < 0.5 + // + intOrStringType = NewObjectType("intOrString", map[string]*DeclField{ + "strVal": {Name: "strVal", Type: StringType}, + "intVal": {Name: "intVal", Type: IntType}, + }) ) -const ( - schemaDefYaml = ` -type: object -properties: - $ref: - type: string - type: - type: string - type_param: # prohibited unless used within an environment. - type: string - format: - type: string - description: - type: string - required: - type: array - items: - type: string - enum: - type: array - items: - type: string - enumDescriptions: - type: array - items: - type: string - default: {} - items: - $ref: "#openAPISchema" - properties: - type: object - additionalProperties: - $ref: "#openAPISchema" - additionalProperties: - $ref: "#openAPISchema" - metadata: - type: object - additionalProperties: - type: string -` - - templateSchemaYaml = ` -type: object -required: - - apiVersion - - kind - - metadata - - evaluator -properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - required: - - name - properties: - uid: - type: string - name: - type: string - namespace: - type: string - default: "default" - etag: - type: string - labels: - type: object - additionalProperties: - type: string - pluralName: - type: string - description: - type: string - schema: - $ref: "#openAPISchema" - validator: - type: object - required: - - productions - properties: - description: - type: string - environment: - type: string - terms: - type: object - additionalProperties: {} - productions: - type: array - items: - type: object - required: - - message - properties: - match: - type: string - default: true - field: - type: string - message: - type: string - details: {} - evaluator: - type: object - required: - - productions - properties: - description: - type: string - environment: - type: string - ranges: - type: array - items: - type: object - required: - - in - properties: - in: - type: string - key: - type: string - index: - type: string - value: - type: string - terms: - type: object - additionalProperties: - type: string - productions: - type: array - items: - type: object - properties: - match: - type: string - default: "true" - decision: - type: string - decisionRef: - type: string - output: {} - decisions: - type: array - items: - type: object - required: - - output - properties: - decision: - type: string - decisionRef: - type: string - output: {} -` - - instanceSchemaYaml = ` -type: object -required: - - apiVersion - - kind - - metadata -properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - additionalProperties: - type: string - description: - type: string - selector: - type: object - properties: - matchLabels: - type: object - additionalProperties: - type: string - matchExpressions: - type: array - items: - type: object - required: - - key - - operator - properties: - key: - type: string - operator: - type: string - enum: ["DoesNotExist", "Exists", "In", "NotIn"] - values: - type: array - items: {} - default: [] - rule: - $ref: "#templateRuleSchema" - rules: - type: array - items: - $ref: "#templateRuleSchema" -` - - // TODO: support subsetting of built-in functions and macros - // TODO: support naming anonymous types within rule schema and making them accessible to - // declarations. - // TODO: consider supporting custom macros - envSchemaYaml = ` -type: object -required: - - name -properties: - name: - type: string - container: - type: string - variables: - type: object - additionalProperties: - $ref: "#openAPISchema" - functions: - type: object - properties: - extensions: - type: object - additionalProperties: - type: object # function name - additionalProperties: - type: object # overload name - required: - - return - properties: - free_function: - type: boolean - args: - type: array - items: - $ref: "#openAPISchema" - return: - $ref: "#openAPISchema" -` -) - -func init() { - AnySchema = NewOpenAPISchema() - - instanceSchema = NewOpenAPISchema() - in := strings.ReplaceAll(instanceSchemaYaml, "\t", " ") - err := yaml.Unmarshal([]byte(in), instanceSchema) - if err != nil { - panic(err) +// 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 { + if s.Properties != nil && + s.Properties["kind"].Type == "string" && + s.Properties["apiVersion"].Type == "string" && + s.Properties["metadata"].Type == "object" && + s.Properties["metadata"].Properties != nil && + s.Properties["metadata"].Properties["name"].Type == "string" && + s.Properties["metadata"].Properties["generateName"].Type == "string" { + return s } - envSchema = NewOpenAPISchema() - in = strings.ReplaceAll(envSchemaYaml, "\t", " ") - err = yaml.Unmarshal([]byte(in), envSchema) - if err != nil { - panic(err) + result := &schema.Structural{ + Generic: s.Generic, + Extensions: s.Extensions, + ValueValidation: s.ValueValidation, } - schemaDef = NewOpenAPISchema() - in = strings.ReplaceAll(schemaDefYaml, "\t", " ") - err = yaml.Unmarshal([]byte(in), schemaDef) - if err != nil { - panic(err) + props := make(map[string]schema.Structural, len(s.Properties)) + for k, prop := range s.Properties { + props[k] = prop } - templateSchema = NewOpenAPISchema() - in = strings.ReplaceAll(templateSchemaYaml, "\t", " ") - err = yaml.Unmarshal([]byte(in), templateSchema) - if err != nil { - panic(err) + stringType := schema.Structural{Generic: schema.Generic{Type: "string"}} + props["kind"] = stringType + props["apiVersion"] = stringType + props["metadata"] = schema.Structural{ + Generic: schema.Generic{Type: "object"}, + Properties: map[string]schema.Structural{ + "name": stringType, + "generateName": stringType, + }, } -} + result.Properties = props + + return result +} \ No newline at end of file diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/schemas_test.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/schemas_test.go index eb149767a37..52a412364a3 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/schemas_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/schemas_test.go @@ -18,43 +18,43 @@ import ( "reflect" "testing" - "github.com/google/cel-go/checker/decls" "github.com/google/cel-go/common/types" "google.golang.org/protobuf/proto" + "k8s.io/apiextensions-apiserver/pkg/apiserver/schema" ) func TestSchemaDeclType(t *testing.T) { ts := testSchema() - cust := ts.DeclType() - if cust.TypeName() != "CustomObject" { - t.Errorf("incorrect type name, got %v, wanted CustomObject", cust.TypeName()) + cust := SchemaDeclType(ts) + if cust.TypeName() != "object" { + t.Errorf("incorrect type name, got %v, wanted object", cust.TypeName()) } if len(cust.Fields) != 4 { t.Errorf("incorrect number of fields, got %d, wanted 4", len(cust.Fields)) } for _, f := range cust.Fields { - prop, found := ts.FindProperty(f.Name) + prop, found := ts.Properties[f.Name] if !found { t.Errorf("type field not found in schema, field: %s", f.Name) } fdv := f.DefaultValue() - if prop.DefaultValue != nil { - pdv := types.DefaultTypeAdapter.NativeToValue(prop.DefaultValue) + if prop.Default.Object != nil { + pdv := types.DefaultTypeAdapter.NativeToValue(prop.Default.Object) if !reflect.DeepEqual(fdv, pdv) { - t.Errorf("field and schema do not agree on default value, field: %s", f.Name) + t.Errorf("field and schema do not agree on default value for field: %s, field value: %v, schema default: %v", f.Name, fdv, pdv) } } - if prop.Enum == nil && len(f.EnumValues()) != 0 { + if (prop.ValueValidation == nil || len(prop.ValueValidation.Enum) == 0) && len(f.EnumValues()) != 0 { t.Errorf("field had more enum values than the property. field: %s", f.Name) } - if prop.Enum != nil { + if prop.ValueValidation != nil { fevs := f.EnumValues() for _, fev := range fevs { found := false - for _, pev := range prop.Enum { - pev = types.DefaultTypeAdapter.NativeToValue(pev) - if reflect.DeepEqual(fev, pev) { + for _, pev := range prop.ValueValidation.Enum { + celpev := types.DefaultTypeAdapter.NativeToValue(pev.Object) + if reflect.DeepEqual(fev, celpev) { found = true break } @@ -67,27 +67,28 @@ func TestSchemaDeclType(t *testing.T) { } } } - for _, name := range ts.Required { - df, found := cust.FindField(name) - if !found { - t.Errorf("custom type missing required field. field=%s", name) - } - if !df.Required { - t.Errorf("field marked as required in schema, but optional in type. field=%s", df.Name) + if ts.ValueValidation != nil { + for _, name := range ts.ValueValidation.Required { + df, found := cust.FindField(name) + if !found { + t.Errorf("custom type missing required field. field=%s", name) + } + if !df.Required { + t.Errorf("field marked as required in schema, but optional in type. field=%s", df.Name) + } } } } func TestSchemaDeclTypes(t *testing.T) { ts := testSchema() - cust, typeMap := ts.DeclTypes("mock_template") + cust, typeMap := SchemaDeclTypes(ts, "CustomObject") nested, _ := cust.FindField("nested") metadata, _ := cust.FindField("metadata") - metadataElem := metadata.Type.ElemType expectedObjTypeMap := map[string]*DeclType{ - "CustomObject": cust, - "CustomObject.nested": nested.Type, - "CustomObject.metadata.@elem": metadataElem, + "CustomObject": cust, + "CustomObject.nested": nested.Type, + "CustomObject.metadata": metadata.Type, } objTypeMap := map[string]*DeclType{} for name, t := range typeMap { @@ -96,7 +97,7 @@ func TestSchemaDeclTypes(t *testing.T) { } } if len(objTypeMap) != len(expectedObjTypeMap) { - t.Errorf("got different type set. got=%v, wanted=%v", typeMap, expectedObjTypeMap) + t.Errorf("got different type set. got=%v, wanted=%v", objTypeMap, expectedObjTypeMap) } for exp, expType := range expectedObjTypeMap { actType, found := objTypeMap[exp] @@ -108,17 +109,9 @@ func TestSchemaDeclTypes(t *testing.T) { t.Errorf("incompatible CEL types. got=%v, wanted=%v", actType.ExprType(), expType.ExprType()) } } - - metaExprType := metadata.Type.ExprType() - expectedMetaExprType := decls.NewMapType( - decls.String, - decls.NewObjectType("CustomObject.metadata.@elem")) - if !proto.Equal(expectedMetaExprType, metaExprType) { - t.Errorf("got metadata CEL type %v, wanted %v", metaExprType, expectedMetaExprType) - } } -func testSchema() *OpenAPISchema { +func testSchema() *schema.Structural { // Manual construction of a schema with the following definition: // // schema: @@ -160,44 +153,86 @@ func testSchema() *OpenAPISchema { // format: int64 // default: 1 // enum: [1,2,3] - nameField := NewOpenAPISchema() - nameField.Type = "string" - valueField := NewOpenAPISchema() - valueField.Type = "integer" - valueField.Format = "int64" - valueField.DefaultValue = int64(1) - valueField.Enum = []interface{}{int64(1), int64(2), int64(3)} - nestedObjField := NewOpenAPISchema() - nestedObjField.Type = "object" - nestedObjField.Properties["subname"] = NewOpenAPISchema() - nestedObjField.Properties["subname"].Type = "string" - nestedObjField.Properties["flags"] = NewOpenAPISchema() - nestedObjField.Properties["flags"].Type = "object" - nestedObjField.Properties["flags"].AdditionalProperties = NewOpenAPISchema() - nestedObjField.Properties["flags"].AdditionalProperties.Type = "boolean" - nestedObjField.Properties["dates"] = NewOpenAPISchema() - nestedObjField.Properties["dates"].Type = "array" - nestedObjField.Properties["dates"].Items = NewOpenAPISchema() - nestedObjField.Properties["dates"].Items.Type = "string" - nestedObjField.Properties["dates"].Items.Format = "date-time" - metadataKeyValue := NewOpenAPISchema() - metadataKeyValue.Type = "object" - metadataKeyValue.Properties["key"] = NewOpenAPISchema() - metadataKeyValue.Properties["key"].Type = "string" - metadataKeyValue.Properties["values"] = NewOpenAPISchema() - metadataKeyValue.Properties["values"].Type = "array" - metadataKeyValue.Properties["values"].Items = NewOpenAPISchema() - metadataKeyValue.Properties["values"].Items.Type = "string" - metadataObjField := NewOpenAPISchema() - metadataObjField.Type = "object" - metadataObjField.AdditionalProperties = metadataKeyValue - ts := NewOpenAPISchema() - ts.Type = "object" - ts.Metadata["custom_type"] = "CustomObject" - ts.Required = []string{"name", "value"} - ts.Properties["name"] = nameField - ts.Properties["value"] = valueField - ts.Properties["nested"] = nestedObjField - ts.Properties["metadata"] = metadataObjField + ts := &schema.Structural{ + Generic: schema.Generic{ + Type: "object", + }, + Properties: map[string]schema.Structural{ + "name": { + Generic: schema.Generic{ + Type: "string", + }, + }, + "value": { + Generic: schema.Generic{ + Type: "integer", + Default: schema.JSON{Object: int64(1)}, + }, + ValueValidation: &schema.ValueValidation{ + Format: "int64", + Enum: []schema.JSON{{Object: int64(1)}, {Object: int64(2)}, {Object: int64(3)}}, + }, + }, + "nested": { + Generic: schema.Generic{ + Type: "object", + }, + Properties: map[string]schema.Structural{ + "subname": { + Generic: schema.Generic{ + Type: "string", + }, + }, + "flags": { + Generic: schema.Generic{ + Type: "object", + AdditionalProperties: &schema.StructuralOrBool{ + Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "boolean", + }, + }, + }, + }, + }, + "dates": { + Generic: schema.Generic{ + Type: "array", + }, + Items: &schema.Structural{ + Generic: schema.Generic{ + Type: "string", + }, + ValueValidation: &schema.ValueValidation{ + Format: "date-time", + }, + }, + }, + }, + }, + "metadata": { + Generic: schema.Generic{ + Type: "object", + }, + Properties: map[string]schema.Structural{ + "name": { + Generic: schema.Generic{ + Type: "string", + }, + }, + "value": { + Generic: schema.Generic{ + Type: "array", + }, + Items: &schema.Structural{ + Generic: schema.Generic{ + Type: "string", + }, + }, + }, + }, + }, + }, + } return ts } diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/source.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/source.go deleted file mode 100644 index d49e0a0081d..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/source.go +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "github.com/google/cel-go/common" -) - -// ByteSource converts a byte sequence and location description to a model.Source. -func ByteSource(contents []byte, location string) *Source { - return StringSource(string(contents), location) -} - -// StringSource converts a string and location description to a model.Source. -func StringSource(contents, location string) *Source { - return &Source{ - Source: common.NewStringSource(contents, location), - } -} - -// Source represents the contents of a single source file. -type Source struct { - common.Source -} - -// Relative produces a RelativeSource object for the content provided at the absolute location -// within the parent Source as indicated by the line and column. -func (src *Source) Relative(content string, line, col int) *RelativeSource { - return &RelativeSource{ - Source: src.Source, - localSrc: common.NewStringSource(content, src.Description()), - absLoc: common.NewLocation(line, col), - } -} - -// RelativeSource represents an embedded source element within a larger source. -type RelativeSource struct { - common.Source - localSrc common.Source - absLoc common.Location -} - -// AbsoluteLocation returns the location within the parent Source where the RelativeSource starts. -func (rel *RelativeSource) AbsoluteLocation() common.Location { - return rel.absLoc -} - -// Content returns the embedded source snippet. -func (rel *RelativeSource) Content() string { - return rel.localSrc.Content() -} - -// OffsetLocation returns the absolute location given the relative offset, if found. -func (rel *RelativeSource) OffsetLocation(offset int32) (common.Location, bool) { - absOffset, found := rel.Source.LocationOffset(rel.absLoc) - if !found { - return common.NoLocation, false - } - return rel.Source.OffsetLocation(absOffset + offset) -} - -// NewLocation creates an absolute common.Location based on a local line, column -// position from a relative source. -func (rel *RelativeSource) NewLocation(line, col int) common.Location { - localLoc := common.NewLocation(line, col) - relOffset, found := rel.localSrc.LocationOffset(localLoc) - if !found { - return common.NoLocation - } - offset, _ := rel.Source.LocationOffset(rel.absLoc) - absLoc, _ := rel.Source.OffsetLocation(offset + relOffset) - return absLoc -} - -// NewSourceInfo creates SourceInfo metadata from a Source object. -func NewSourceInfo(src common.Source) *SourceInfo { - return &SourceInfo{ - Comments: make(map[int64][]*Comment), - LineOffsets: src.LineOffsets(), - Description: src.Description(), - Offsets: make(map[int64]int32), - } -} - -// SourceInfo contains metadata about the Source such as comments, line positions, and source -// element offsets. -type SourceInfo struct { - // Comments mapped by source element id to a comment set. - Comments map[int64][]*Comment - - // LineOffsets contains the list of character offsets where newlines occur in the source. - LineOffsets []int32 - - // Description indicates something about the source, such as its file name. - Description string - - // Offsets map from source element id to the character offset where the source element starts. - Offsets map[int64]int32 -} - -// SourceMetadata enables the lookup for expression source metadata by expression id. -type SourceMetadata interface { - // CommentsByID returns the set of comments associated with the expression id, if present. - CommentsByID(int64) ([]*Comment, bool) - - // LocationByID returns the CEL common.Location of the expression id, if present. - LocationByID(int64) (common.Location, bool) -} - -// CommentsByID returns the set of comments by expression id, if present. -func (info *SourceInfo) CommentsByID(id int64) ([]*Comment, bool) { - comments, found := info.Comments[id] - return comments, found -} - -// LocationByID returns the line and column location of source node by its id. -func (info *SourceInfo) LocationByID(id int64) (common.Location, bool) { - charOff, found := info.Offsets[id] - if !found { - return common.NoLocation, false - } - ln, lnOff := info.findLine(charOff) - return common.NewLocation(int(ln), int(charOff-lnOff)), true -} - -func (info *SourceInfo) findLine(characterOffset int32) (int32, int32) { - var line int32 = 1 - for _, lineOffset := range info.LineOffsets { - if lineOffset > characterOffset { - break - } else { - line++ - } - } - if line == 1 { - return line, 0 - } - return line, info.LineOffsets[line-2] -} - -// CommentStyle type used to indicate where a comment occurs. -type CommentStyle int - -const ( - // HeadComment indicates that the comment is defined in the lines preceding the source element. - HeadComment CommentStyle = iota + 1 - - // LineComment indicates that the comment occurs on the same line after the source element. - LineComment - - // FootComment indicates that the comment occurs after the source element with at least one - // blank line before the next source element. - FootComment -) - -// NewHeadComment creates a new HeadComment from the text. -func NewHeadComment(txt string) *Comment { - return &Comment{Text: txt, Style: HeadComment} -} - -// NewLineComment creates a new LineComment from the text. -func NewLineComment(txt string) *Comment { - return &Comment{Text: txt, Style: LineComment} -} - -// NewFootComment creates a new FootComment from the text. -func NewFootComment(txt string) *Comment { - return &Comment{Text: txt, Style: FootComment} -} - -// Comment represents a comment within source. -type Comment struct { - // Text contains the comment text. - Text string - - // Style indicates where the comment appears relative to a source element. - Style CommentStyle -} diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/template.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/template.go deleted file mode 100644 index 3137aabcedb..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/template.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "github.com/google/cel-go/cel" - - exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" -) - -// NewTemplate produces an empty policy Template instance. -func NewTemplate(info SourceMetadata) *Template { - return &Template{ - Metadata: NewTemplateMetadata(), - Evaluator: NewEvaluator(), - Meta: info, - } -} - -// Template represents the compiled and type-checked policy template. -type Template struct { - APIVersion string - Kind string - Metadata *TemplateMetadata - Description string - RuleTypes *RuleTypes - Validator *Evaluator - Evaluator *Evaluator - Meta SourceMetadata -} - -// EvaluatorDecisionCount returns the number of decisions which can be produced by the template -// evaluator production rules. -func (t *Template) EvaluatorDecisionCount() int { - return t.Evaluator.DecisionCount() -} - -// MetadataMap returns the metadata name to value map, which can be used in evaluation. -// Only "name" field is supported for now. -func (t *Template) MetadataMap() map[string]interface{} { - return map[string]interface{}{ - "name": t.Metadata.Name, - } -} - -// NewTemplateMetadata returns an empty *TemplateMetadata instance. -func NewTemplateMetadata() *TemplateMetadata { - return &TemplateMetadata{ - Properties: make(map[string]string), - } -} - -// TemplateMetadata contains the top-level information about the Template, including its name and -// namespace. -type TemplateMetadata struct { - UID string - Name string - Namespace string - - // PluralMame is the plural form of the template name to use when managing a collection of - // template instances. - PluralName string - - // Properties contains an optional set of key-value information which external applications - // might find useful. - Properties map[string]string -} - -// NewEvaluator returns an empty instance of a Template Evaluator. -func NewEvaluator() *Evaluator { - return &Evaluator{ - Terms: []*Term{}, - Productions: []*Production{}, - } -} - -// Evaluator contains a set of production rules used to validate policy templates or -// evaluate template instances. -// -// The evaluator may optionally specify a named and versioned Environment as the basis for the -// variables and functions exposed to the CEL expressions within the Evaluator, and an optional -// set of terms. -// -// Terms are like template-local variables. Terms may rely on other terms which precede them. -// Term order matters, and no cycles are permitted among terms by design and convention. -type Evaluator struct { - Environment string - Ranges []*Range - Terms []*Term - Productions []*Production -} - -// DecisionCount returns the number of possible decisions which could be emitted by this evaluator. -func (e *Evaluator) DecisionCount() int { - decMap := map[string]struct{}{} - for _, p := range e.Productions { - for _, d := range p.Decisions { - decMap[d.Name] = struct{}{} - } - } - return len(decMap) -} - -// Range expresses a looping condition where the key (or index) and value can be extracted from the -// range CEL expression. -type Range struct { - ID int64 - Key *exprpb.Decl - Value *exprpb.Decl - Expr *cel.Ast -} - -// NewTerm produces a named Term instance associated with a CEL Ast and a list of the input -// terms needed to evaluate the Ast successfully. -func NewTerm(id int64, name string, expr *cel.Ast) *Term { - return &Term{ - ID: id, - Name: name, - Expr: expr, - } -} - -// Term is a template-local variable whose name may shadow names in the Template environment and -// which may depend on preceding terms as input. -type Term struct { - ID int64 - Name string - Expr *cel.Ast -} - -// NewProduction returns an empty instance of a Production rule which minimally contains a single -// Decision. -func NewProduction(id int64, match *cel.Ast) *Production { - return &Production{ - ID: id, - Match: match, - Decisions: []*Decision{}, - } -} - -// Production describes an match-decision pair where the match, if set, indicates whether the -// Decision is applicable, and the decision indicates its name and output value. -type Production struct { - ID int64 - Match *cel.Ast - Decisions []*Decision -} diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/types.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/types.go index 61cb4af98f4..69e895b370a 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/types.go +++ b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/types.go @@ -27,6 +27,7 @@ import ( "google.golang.org/protobuf/proto" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" + "k8s.io/apiextensions-apiserver/pkg/apiserver/schema" ) // NewListType returns a parameterized list type with a specified element type. @@ -92,7 +93,7 @@ func newSimpleType(name string, exprType *exprpb.Type, zeroVal ref.Val) *DeclTyp } } -// DeclType represents the universal type descriptor for Policy Templates. +// DeclType represents the universal type descriptor for OpenAPIv3 types. type DeclType struct { fmt.Stringer @@ -304,7 +305,7 @@ func (f *DeclField) EnumValues() []ref.Val { // NewRuleTypes returns an Open API Schema-based type-system which is CEL compatible. func NewRuleTypes(kind string, - schema *OpenAPISchema, + schema *schema.Structural, res Resolver) (*RuleTypes, error) { // Note, if the schema indicates that it's actually based on another proto // then prefer the proto definition. For expressions in the proto, a new field @@ -325,13 +326,13 @@ func NewRuleTypes(kind string, // type-system. type RuleTypes struct { ref.TypeProvider - Schema *OpenAPISchema + Schema *schema.Structural ruleSchemaDeclTypes *schemaTypeProvider typeAdapter ref.TypeAdapter resolver Resolver } -// EnvOptions returns a set of cel.EnvOption values which includes the Template's declaration set +// EnvOptions returns a set of cel.EnvOption values which includes the declaration set // as well as a custom ref.TypeProvider. // // Note, the standard declaration set includes 'rule' which is defined as the top-level rule-schema @@ -358,7 +359,7 @@ func (rt *RuleTypes) EnvOptions(tp ref.TypeProvider) ([]cel.EnvOption, error) { tpType, found := tp.FindType(name) if found && !proto.Equal(tpType, declType.ExprType()) { return nil, fmt.Errorf( - "type %s definition differs between CEL environment and template", name) + "type %s definition differs between CEL environment and rule", name) } } return []cel.EnvOption{ @@ -370,7 +371,7 @@ func (rt *RuleTypes) EnvOptions(tp ref.TypeProvider) ([]cel.EnvOption, error) { }, nil } -// FindType attempts to resolve the typeName provided from the template's rule-schema, or if not +// FindType attempts to resolve the typeName provided from the rule's rule-schema, or if not // from the embedded ref.TypeProvider. // // FindType overrides the default type-finding behavior of the embedded TypeProvider. @@ -425,25 +426,10 @@ func (rt *RuleTypes) FindFieldType(typeName, fieldName string) (*ref.FieldType, return nil, false } -// ConvertToRule transforms an untyped DynValue into a typed object. -// -// Conversion is done deeply and will traverse the object graph represented by the dyn value. -func (rt *RuleTypes) ConvertToRule(dyn *DynValue) Rule { - ruleSchemaType := rt.ruleSchemaDeclTypes.root - // TODO: handle conversions to protobuf types. - dyn = rt.convertToCustomType(dyn, ruleSchemaType) - return &CustomRule{DynValue: dyn} -} - // NativeToValue is an implementation of the ref.TypeAdapater interface which supports conversion -// of policy template values to CEL ref.Val instances. +// of rule values to CEL ref.Val instances. func (rt *RuleTypes) NativeToValue(val interface{}) ref.Val { - switch v := val.(type) { - case *CustomRule: - return v.ExprValue() - default: - return rt.typeAdapter.NativeToValue(val) - } + return rt.typeAdapter.NativeToValue(val) } // TypeNames returns the list of type names declared within the RuleTypes object. @@ -499,8 +485,8 @@ func (rt *RuleTypes) convertToCustomType(dyn *DynValue, declType *DeclType) *Dyn } } -func newSchemaTypeProvider(kind string, schema *OpenAPISchema) (*schemaTypeProvider, error) { - root := schema.DeclType().MaybeAssignTypeName(kind) +func newSchemaTypeProvider(kind string, schema *schema.Structural) (*schemaTypeProvider, error) { + root := SchemaDeclType(schema).MaybeAssignTypeName(kind) types := FieldTypeMap(kind, root) return &schemaTypeProvider{ root: root, @@ -515,7 +501,7 @@ type schemaTypeProvider struct { var ( // AnyType is equivalent to the CEL 'protobuf.Any' type in that the value may have any of the - // types supported by CEL Policy Templates. + // types supported. AnyType = newSimpleType("any", decls.Any, nil) // BoolType is equivalent to the CEL 'bool' type. @@ -530,6 +516,9 @@ var ( // DurationType is equivalent to the CEL 'duration' type. DurationType = newSimpleType("duration", decls.Duration, types.Duration{Duration: time.Duration(0)}) + // DateType is equivalent to the CEL 'date' type. + DateType = newSimpleType("date", decls.Timestamp, types.Timestamp{Time: time.Time{}}) + // DynType is the equivalent of the CEL 'dyn' concept which indicates that the type will be // determined at runtime rather than compile time. DynType = newSimpleType("dyn", decls.Dyn, nil) @@ -544,10 +533,6 @@ var ( // StringType values may either be string literals or expression strings. StringType = newSimpleType("string", decls.String, types.String("")) - // PlainTextType is equivalent to the CEL 'string' type, but which has been specifically - // designated as a string literal. - PlainTextType = newSimpleType("string_lit", decls.String, types.String("")) - // TimestampType corresponds to the well-known protobuf.Timestamp type supported within CEL. TimestampType = newSimpleType("timestamp", decls.Timestamp, types.Timestamp{Time: time.Time{}}) diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/types_test.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/types_test.go index bfcd1891bfe..1251715cbd5 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/types_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/types_test.go @@ -67,10 +67,11 @@ func TestTypes_MapType(t *testing.T) { func TestTypes_RuleTypesFieldMapping(t *testing.T) { stdEnv, _ := cel.NewEnv() reg := NewRegistry(stdEnv) - rt, err := NewRuleTypes("mock_template", testSchema(), reg) + rt, err := NewRuleTypes("CustomObject", testSchema(), reg) if err != nil { t.Fatal(err) } + rt.TypeProvider = stdEnv.TypeProvider() nestedFieldType, found := rt.FindFieldType("CustomObject", "nested") if !found { t.Fatal("got field not found for 'CustomObject.nested', wanted found") @@ -119,17 +120,17 @@ func TestTypes_RuleTypesFieldMapping(t *testing.T) { mapVal := NewMapValue() mapVal.AddField(name) mapVal.AddField(nested) - rule := rt.ConvertToRule(testValue(t, 11, mapVal)) - if rule == nil { - t.Error("map could not be converted to rule") - } - if rule.GetID() != 11 { - t.Errorf("got %d as the rule id, wanted 11", rule.GetID()) - } - ruleVal := rt.NativeToValue(rule) - if ruleVal == nil { - t.Error("got CEL rule value of nil, wanted non-nil") - } + //rule := rt.ConvertToRule(testValue(t, 11, mapVal)) + //if rule == nil { + // t.Error("map could not be converted to rule") + //} + //if rule.GetID() != 11 { + // t.Errorf("got %d as the rule id, wanted 11", rule.GetID()) + //} + //ruleVal := rt.NativeToValue(rule) + //if ruleVal == nil { + // t.Error("got CEL rule value of nil, wanted non-nil") + //} opts, err := rt.EnvOptions(stdEnv.TypeProvider()) if err != nil { diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/value.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/value.go index 8f3634409bb..0861748d24a 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/value.go +++ b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/value.go @@ -45,13 +45,6 @@ const ( LiteralStyle ) -// ParsedValue represents a top-level object representing either a template or instance value. -type ParsedValue struct { - ID int64 - Value *MapValue - Meta SourceMetadata -} - // NewEmptyDynValue returns the zero-valued DynValue. func NewEmptyDynValue() *DynValue { // note: 0 is not a valid parse node identifier. @@ -158,10 +151,6 @@ func exprValue(value interface{}) (ref.Val, *DeclType, error) { return types.String(v), StringType, nil case uint64: return types.Uint(v), UintType, nil - case PlainTextValue: - return types.String(string(v)), PlainTextType, nil - case *MultilineStringValue: - return types.String(v.Value), StringType, nil case time.Duration: return types.Duration{Duration: v}, DurationType, nil case time.Time: diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/value_test.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/value_test.go index ca1f122fe6b..5dda06e415d 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/value_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/value_test.go @@ -35,8 +35,6 @@ func TestConvertToType(t *testing.T) { {float64(1.2), types.DoubleType}, {int64(-42), types.IntType}, {uint64(63), types.UintType}, - {PlainTextValue("plain text"), types.StringType}, - {&MultilineStringValue{Value: "multiline", Raw: "multi\nline"}, types.StringType}, {time.Duration(300), types.DurationType}, {time.Now().UTC(), types.TimestampType}, {types.NullValue, types.NullType}, @@ -63,8 +61,7 @@ func TestConvertToType(t *testing.T) { func TestEqual(t *testing.T) { vals := []interface{}{ - true, []byte("bytes"), float64(1.2), int64(-42), uint64(63), PlainTextValue("plain text"), - &MultilineStringValue{Value: "multiline", Raw: "multi\nline"}, time.Duration(300), + true, []byte("bytes"), float64(1.2), int64(-42), uint64(63), time.Duration(300), time.Now().UTC(), types.NullValue, NewListValue(), NewMapValue(), NewObjectValue(NewObjectType("TestObject", map[string]*DeclField{})), } @@ -327,7 +324,7 @@ func TestObjectValueEqual(t *testing.T) { objType := NewObjectType("Notice", map[string]*DeclField{ "name": &DeclField{Name: "name", Type: StringType}, "priority": &DeclField{Name: "priority", Type: IntType}, - "message": &DeclField{Name: "message", Type: PlainTextType, defaultValue: ""}, + "message": &DeclField{Name: "message", Type: StringType, defaultValue: ""}, }) name := NewField(1, "name") name.Ref = testValue(t, 2, "alert")