mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
rebase: update k8s.io packages to v0.29.0
Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
committed by
mergify[bot]
parent
328a264202
commit
f080b9e0c9
25
vendor/k8s.io/apiserver/pkg/cel/common/adaptor.go
generated
vendored
25
vendor/k8s.io/apiserver/pkg/cel/common/adaptor.go
generated
vendored
@ -56,12 +56,27 @@ type Schema interface {
|
||||
|
||||
// Validations contains OpenAPI validation that the CEL library uses.
|
||||
type Validations interface {
|
||||
Pattern() string
|
||||
Minimum() *float64
|
||||
IsExclusiveMinimum() bool
|
||||
Maximum() *float64
|
||||
IsExclusiveMaximum() bool
|
||||
MultipleOf() *float64
|
||||
MinItems() *int64
|
||||
MaxItems() *int64
|
||||
MinLength() *int64
|
||||
MaxLength() *int64
|
||||
MinProperties() *int64
|
||||
MaxProperties() *int64
|
||||
Required() []string
|
||||
Enum() []any
|
||||
Nullable() bool
|
||||
UniqueItems() bool
|
||||
|
||||
AllOf() []Schema
|
||||
OneOf() []Schema
|
||||
AnyOf() []Schema
|
||||
Not() Schema
|
||||
}
|
||||
|
||||
// KubeExtensions contains Kubernetes-specific extensions to the OpenAPI schema.
|
||||
@ -71,6 +86,16 @@ type KubeExtensions interface {
|
||||
IsXPreserveUnknownFields() bool
|
||||
XListType() string
|
||||
XListMapKeys() []string
|
||||
XMapType() string
|
||||
XValidations() []ValidationRule
|
||||
}
|
||||
|
||||
// ValidationRule represents a single x-kubernetes-validations rule.
|
||||
type ValidationRule interface {
|
||||
Rule() string
|
||||
Message() string
|
||||
MessageExpression() string
|
||||
FieldPath() string
|
||||
}
|
||||
|
||||
// SchemaOrBool contains either a schema or a boolean indicating if the object
|
||||
|
334
vendor/k8s.io/apiserver/pkg/cel/common/equality.go
generated
vendored
Normal file
334
vendor/k8s.io/apiserver/pkg/cel/common/equality.go
generated
vendored
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
Copyright 2023 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 common
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CorrelatedObject represents a node in a tree of objects that are being
|
||||
// validated. It is used to keep track of the old value of an object during
|
||||
// traversal of the new value. It is also used to cache the results of
|
||||
// DeepEqual comparisons between the old and new values of objects.
|
||||
//
|
||||
// All receiver functions support being called on `nil` to support ergonomic
|
||||
// recursive descent. The nil `CorrelatedObject` represents an uncorrelatable
|
||||
// node in the tree.
|
||||
//
|
||||
// CorrelatedObject is not thread-safe. It is the responsibility of the caller
|
||||
// to handle concurrency, if any.
|
||||
type CorrelatedObject struct {
|
||||
// Currently correlated old value during traversal of the schema/object
|
||||
OldValue interface{}
|
||||
|
||||
// Value being validated
|
||||
Value interface{}
|
||||
|
||||
// Schema used for validation of this value. The schema is also used
|
||||
// to determine how to correlate the old object.
|
||||
Schema Schema
|
||||
|
||||
// Duration spent on ratcheting validation for this object and all of its
|
||||
// children.
|
||||
Duration *time.Duration
|
||||
|
||||
// Scratch space below, may change during validation
|
||||
|
||||
// Cached comparison result of DeepEqual of `value` and `thunk.oldValue`
|
||||
comparisonResult *bool
|
||||
|
||||
// Cached map representation of a map-type list, or nil if not map-type list
|
||||
mapList MapList
|
||||
|
||||
// Children spawned by a call to `Validate` on this object
|
||||
// key is either a string or an index, depending upon whether `value` is
|
||||
// a map or a list, respectively.
|
||||
//
|
||||
// The list of children may be incomplete depending upon if the internal
|
||||
// logic of kube-openapi's SchemaValidator short-circuited before
|
||||
// reaching all of the children.
|
||||
//
|
||||
// It should be expected to have an entry for either all of the children, or
|
||||
// none of them.
|
||||
children map[interface{}]*CorrelatedObject
|
||||
}
|
||||
|
||||
func NewCorrelatedObject(new, old interface{}, schema Schema) *CorrelatedObject {
|
||||
d := time.Duration(0)
|
||||
return &CorrelatedObject{
|
||||
OldValue: old,
|
||||
Value: new,
|
||||
Schema: schema,
|
||||
Duration: &d,
|
||||
}
|
||||
}
|
||||
|
||||
// If OldValue or Value is not a list, or the index is out of bounds of the
|
||||
// Value list, returns nil
|
||||
// If oldValue is a list, this considers the x-list-type to decide how to
|
||||
// correlate old values:
|
||||
//
|
||||
// If listType is map, creates a map representation of the list using the designated
|
||||
// map-keys, caches it for future calls, and returns the map value, or nil if
|
||||
// the correlated key is not in the old map
|
||||
//
|
||||
// Otherwise, if the list type is not correlatable this funcion returns nil.
|
||||
func (r *CorrelatedObject) correlateOldValueForChildAtNewIndex(index int) interface{} {
|
||||
oldAsList, ok := r.OldValue.([]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
asList, ok := r.Value.([]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
} else if len(asList) <= index {
|
||||
// Cannot correlate out of bounds index
|
||||
return nil
|
||||
}
|
||||
|
||||
listType := r.Schema.XListType()
|
||||
switch listType {
|
||||
case "map":
|
||||
// Look up keys for this index in current object
|
||||
currentElement := asList[index]
|
||||
|
||||
oldList := r.mapList
|
||||
if oldList == nil {
|
||||
oldList = MakeMapList(r.Schema, oldAsList)
|
||||
r.mapList = oldList
|
||||
}
|
||||
return oldList.Get(currentElement)
|
||||
|
||||
case "set":
|
||||
// Are sets correlatable? Only if the old value equals the current value.
|
||||
// We might be able to support this, but do not currently see a lot
|
||||
// of value
|
||||
// (would allow you to add/remove items from sets with ratcheting but not change them)
|
||||
return nil
|
||||
case "":
|
||||
fallthrough
|
||||
case "atomic":
|
||||
// Atomic lists are the default are not correlatable by item
|
||||
// Ratcheting is not available on a per-index basis
|
||||
return nil
|
||||
default:
|
||||
// Unrecognized list type. Assume non-correlatable.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// CachedDeepEqual is equivalent to reflect.DeepEqual, but caches the
|
||||
// results in the tree of ratchetInvocationScratch objects on the way:
|
||||
//
|
||||
// For objects and arrays, this function will make a best effort to make
|
||||
// use of past DeepEqual checks performed by this Node's children, if available.
|
||||
//
|
||||
// If a lazy computation could not be found for all children possibly due
|
||||
// to validation logic short circuiting and skipping the children, then
|
||||
// this function simply defers to reflect.DeepEqual.
|
||||
func (r *CorrelatedObject) CachedDeepEqual() (res bool) {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
if r != nil && r.Duration != nil {
|
||||
*r.Duration += time.Since(start)
|
||||
}
|
||||
}()
|
||||
|
||||
if r == nil {
|
||||
// Uncorrelatable node is not considered equal to its old value
|
||||
return false
|
||||
} else if r.comparisonResult != nil {
|
||||
return *r.comparisonResult
|
||||
}
|
||||
|
||||
defer func() {
|
||||
r.comparisonResult = &res
|
||||
}()
|
||||
|
||||
if r.Value == nil && r.OldValue == nil {
|
||||
return true
|
||||
} else if r.Value == nil || r.OldValue == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
oldAsArray, oldIsArray := r.OldValue.([]interface{})
|
||||
newAsArray, newIsArray := r.Value.([]interface{})
|
||||
|
||||
oldAsMap, oldIsMap := r.OldValue.(map[string]interface{})
|
||||
newAsMap, newIsMap := r.Value.(map[string]interface{})
|
||||
|
||||
// If old and new are not the same type, they are not equal
|
||||
if (oldIsArray != newIsArray) || oldIsMap != newIsMap {
|
||||
return false
|
||||
}
|
||||
|
||||
// Objects are known to be same type of (map, slice, or primitive)
|
||||
switch {
|
||||
case oldIsArray:
|
||||
// Both arrays case. oldIsArray == newIsArray
|
||||
if len(oldAsArray) != len(newAsArray) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range newAsArray {
|
||||
child := r.Index(i)
|
||||
if child == nil {
|
||||
if r.mapList == nil {
|
||||
// Treat non-correlatable array as a unit with reflect.DeepEqual
|
||||
return reflect.DeepEqual(oldAsArray, newAsArray)
|
||||
}
|
||||
|
||||
// If array is correlatable, but old not found. Just short circuit
|
||||
// comparison
|
||||
return false
|
||||
|
||||
} else if !child.CachedDeepEqual() {
|
||||
// If one child is not equal the entire object is not equal
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
case oldIsMap:
|
||||
// Both maps case. oldIsMap == newIsMap
|
||||
if len(oldAsMap) != len(newAsMap) {
|
||||
return false
|
||||
}
|
||||
|
||||
for k := range newAsMap {
|
||||
child := r.Key(k)
|
||||
if child == nil {
|
||||
// Un-correlatable child due to key change.
|
||||
// Objects are not equal.
|
||||
return false
|
||||
} else if !child.CachedDeepEqual() {
|
||||
// If one child is not equal the entire object is not equal
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
default:
|
||||
// Primitive: use reflect.DeepEqual
|
||||
return reflect.DeepEqual(r.OldValue, r.Value)
|
||||
}
|
||||
}
|
||||
|
||||
// Key returns the child of the receiver with the given name.
|
||||
// Returns nil if the given name is does not exist in the new object, or its
|
||||
// value is not correlatable to an old value.
|
||||
// If receiver is nil or if the new value is not an object/map, returns nil.
|
||||
func (r *CorrelatedObject) Key(field string) *CorrelatedObject {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
if r != nil && r.Duration != nil {
|
||||
*r.Duration += time.Since(start)
|
||||
}
|
||||
}()
|
||||
|
||||
if r == nil || r.Schema == nil {
|
||||
return nil
|
||||
} else if existing, exists := r.children[field]; exists {
|
||||
return existing
|
||||
}
|
||||
|
||||
// Find correlated old value
|
||||
oldAsMap, okOld := r.OldValue.(map[string]interface{})
|
||||
newAsMap, okNew := r.Value.(map[string]interface{})
|
||||
if !okOld || !okNew {
|
||||
return nil
|
||||
}
|
||||
|
||||
oldValueForField, okOld := oldAsMap[field]
|
||||
newValueForField, okNew := newAsMap[field]
|
||||
if !okOld || !okNew {
|
||||
return nil
|
||||
}
|
||||
|
||||
var propertySchema Schema
|
||||
if prop, exists := r.Schema.Properties()[field]; exists {
|
||||
propertySchema = prop
|
||||
} else if addP := r.Schema.AdditionalProperties(); addP != nil && addP.Schema() != nil {
|
||||
propertySchema = addP.Schema()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.children == nil {
|
||||
r.children = make(map[interface{}]*CorrelatedObject, len(newAsMap))
|
||||
}
|
||||
|
||||
res := &CorrelatedObject{
|
||||
OldValue: oldValueForField,
|
||||
Value: newValueForField,
|
||||
Schema: propertySchema,
|
||||
Duration: r.Duration,
|
||||
}
|
||||
r.children[field] = res
|
||||
return res
|
||||
}
|
||||
|
||||
// Index returns the child of the receiver at the given index.
|
||||
// Returns nil if the given index is out of bounds, or its value is not
|
||||
// correlatable to an old value.
|
||||
// If receiver is nil or if the new value is not an array, returns nil.
|
||||
func (r *CorrelatedObject) Index(i int) *CorrelatedObject {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
if r != nil && r.Duration != nil {
|
||||
*r.Duration += time.Since(start)
|
||||
}
|
||||
}()
|
||||
|
||||
if r == nil || r.Schema == nil {
|
||||
return nil
|
||||
} else if existing, exists := r.children[i]; exists {
|
||||
return existing
|
||||
}
|
||||
|
||||
asList, ok := r.Value.([]interface{})
|
||||
if !ok || len(asList) <= i {
|
||||
return nil
|
||||
}
|
||||
|
||||
oldValueForIndex := r.correlateOldValueForChildAtNewIndex(i)
|
||||
if oldValueForIndex == nil {
|
||||
return nil
|
||||
}
|
||||
var itemSchema Schema
|
||||
if i := r.Schema.Items(); i != nil {
|
||||
itemSchema = i
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.children == nil {
|
||||
r.children = make(map[interface{}]*CorrelatedObject, len(asList))
|
||||
}
|
||||
|
||||
res := &CorrelatedObject{
|
||||
OldValue: oldValueForIndex,
|
||||
Value: asList[i],
|
||||
Schema: itemSchema,
|
||||
Duration: r.Duration,
|
||||
}
|
||||
r.children[i] = res
|
||||
return res
|
||||
}
|
19
vendor/k8s.io/apiserver/pkg/cel/common/schemas.go
generated
vendored
19
vendor/k8s.io/apiserver/pkg/cel/common/schemas.go
generated
vendored
@ -165,7 +165,11 @@ func SchemaDeclType(s Schema, isResourceRoot bool) *apiservercel.DeclType {
|
||||
// unicode code point can be up to 4 bytes long)
|
||||
strWithMaxLength.MaxElements = zeroIfNegative(*s.MaxLength()) * 4
|
||||
} else {
|
||||
strWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s)
|
||||
if len(s.Enum()) > 0 {
|
||||
strWithMaxLength.MaxElements = estimateMaxStringEnumLength(s)
|
||||
} else {
|
||||
strWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s)
|
||||
}
|
||||
}
|
||||
return strWithMaxLength
|
||||
case "boolean":
|
||||
@ -239,6 +243,19 @@ func estimateMaxStringLengthPerRequest(s Schema) int64 {
|
||||
}
|
||||
}
|
||||
|
||||
// estimateMaxStringLengthPerRequest estimates the maximum string length (in characters)
|
||||
// that has a set of enum values.
|
||||
// The result of the estimation is the length of the longest possible value.
|
||||
func estimateMaxStringEnumLength(s Schema) int64 {
|
||||
var maxLength int64
|
||||
for _, v := range s.Enum() {
|
||||
if s, ok := v.(string); ok && int64(len(s)) > maxLength {
|
||||
maxLength = int64(len(s))
|
||||
}
|
||||
}
|
||||
return maxLength
|
||||
}
|
||||
|
||||
// estimateMaxArrayItemsPerRequest estimates the maximum number of array items with
|
||||
// the provided minimum serialized size that can fit into a single request.
|
||||
func estimateMaxArrayItemsFromMinSize(minSize int64) int64 {
|
||||
|
26
vendor/k8s.io/apiserver/pkg/cel/common/values.go
generated
vendored
26
vendor/k8s.io/apiserver/pkg/cel/common/values.go
generated
vendored
@ -84,18 +84,22 @@ func UnstructuredToVal(unstructured interface{}, schema Schema) ref.Val {
|
||||
},
|
||||
}
|
||||
}
|
||||
// A object with x-kubernetes-preserve-unknown-fields but no properties or additionalProperties is treated
|
||||
// as an empty object.
|
||||
if schema.IsXPreserveUnknownFields() {
|
||||
return &unstructuredMap{
|
||||
value: m,
|
||||
schema: schema,
|
||||
propSchema: func(key string) (Schema, bool) {
|
||||
return nil, false
|
||||
},
|
||||
}
|
||||
|
||||
// properties and additionalProperties are mutual exclusive, but nothing prevents the situation
|
||||
// where both are missing.
|
||||
// An object that (1) has no properties (2) has no additionalProperties or additionalProperties == false
|
||||
// is treated as an empty object.
|
||||
// An object that has additionalProperties == true is treated as an unstructured map.
|
||||
// An object that has x-kubernetes-preserve-unknown-field extension set is treated as an unstructured map.
|
||||
// Empty object vs unstructured map is differentiated by unstructuredMap implementation with the set schema.
|
||||
// The resulting result remains the same.
|
||||
return &unstructuredMap{
|
||||
value: m,
|
||||
schema: schema,
|
||||
propSchema: func(key string) (Schema, bool) {
|
||||
return nil, false
|
||||
},
|
||||
}
|
||||
return types.NewErr("invalid object type, expected either Properties or AdditionalProperties with Allows=true and non-empty Schema")
|
||||
}
|
||||
|
||||
if schema.Type() == "array" {
|
||||
|
47
vendor/k8s.io/apiserver/pkg/cel/environment/base.go
generated
vendored
47
vendor/k8s.io/apiserver/pkg/cel/environment/base.go
generated
vendored
@ -22,7 +22,9 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/google/cel-go/cel"
|
||||
"github.com/google/cel-go/checker"
|
||||
"github.com/google/cel-go/ext"
|
||||
"github.com/google/cel-go/interpreter"
|
||||
"golang.org/x/sync/singleflight"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
@ -41,7 +43,7 @@ import (
|
||||
// desirable because it means that CEL expressions are portable across a wider range
|
||||
// of Kubernetes versions.
|
||||
func DefaultCompatibilityVersion() *version.Version {
|
||||
return version.MajorMinor(1, 27)
|
||||
return version.MajorMinor(1, 28)
|
||||
}
|
||||
|
||||
var baseOpts = []VersionedOptions{
|
||||
@ -57,7 +59,6 @@ var baseOpts = []VersionedOptions{
|
||||
cel.EagerlyValidateDeclarations(true),
|
||||
cel.DefaultUTCTimeZone(true),
|
||||
|
||||
ext.Strings(ext.StringsVersion(0)),
|
||||
library.URLs(),
|
||||
library.Regex(),
|
||||
library.Lists(),
|
||||
@ -81,7 +82,47 @@ var baseOpts = []VersionedOptions{
|
||||
library.Quantity(),
|
||||
},
|
||||
},
|
||||
// TODO: switch to ext.Strings version 2 once format() is fixed to work with HomogeneousAggregateLiterals.
|
||||
// add the new validator in 1.29
|
||||
{
|
||||
IntroducedVersion: version.MajorMinor(1, 29),
|
||||
EnvOptions: []cel.EnvOption{
|
||||
cel.ASTValidators(
|
||||
cel.ValidateDurationLiterals(),
|
||||
cel.ValidateTimestampLiterals(),
|
||||
cel.ValidateRegexLiterals(),
|
||||
cel.ValidateHomogeneousAggregateLiterals(),
|
||||
),
|
||||
},
|
||||
},
|
||||
// String library
|
||||
{
|
||||
IntroducedVersion: version.MajorMinor(1, 0),
|
||||
RemovedVersion: version.MajorMinor(1, 29),
|
||||
EnvOptions: []cel.EnvOption{
|
||||
ext.Strings(ext.StringsVersion(0)),
|
||||
},
|
||||
},
|
||||
{
|
||||
IntroducedVersion: version.MajorMinor(1, 29),
|
||||
EnvOptions: []cel.EnvOption{
|
||||
ext.Strings(ext.StringsVersion(2)),
|
||||
},
|
||||
},
|
||||
// Set library
|
||||
{
|
||||
IntroducedVersion: version.MajorMinor(1, 29),
|
||||
EnvOptions: []cel.EnvOption{
|
||||
ext.Sets(),
|
||||
// cel-go v0.17.7 introduced CostEstimatorOptions.
|
||||
// Previous the presence has a cost of 0 but cel fixed it to 1. We still set to 0 here to avoid breaking changes.
|
||||
cel.CostEstimatorOptions(checker.PresenceTestHasCost(false)),
|
||||
},
|
||||
ProgramOptions: []cel.ProgramOption{
|
||||
// cel-go v0.17.7 introduced CostTrackerOptions.
|
||||
// Previous the presence has a cost of 0 but cel fixed it to 1. We still set to 0 here to avoid breaking changes.
|
||||
cel.CostTrackerOptions(interpreter.PresenceTestHasCost(false)),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// MustBaseEnvSet returns the common CEL base environments for Kubernetes for Version, or panics
|
||||
|
2
vendor/k8s.io/apiserver/pkg/cel/lazy/lazy.go
generated
vendored
2
vendor/k8s.io/apiserver/pkg/cel/lazy/lazy.go
generated
vendored
@ -35,7 +35,7 @@ var _ traits.Mapper = (*MapValue)(nil)
|
||||
// MapValue is a map that lazily evaluate its value when a field is first accessed.
|
||||
// The map value is not designed to be thread-safe.
|
||||
type MapValue struct {
|
||||
typeValue *types.TypeValue
|
||||
typeValue *types.Type
|
||||
|
||||
// values are previously evaluated values obtained from callbacks
|
||||
values map[string]ref.Val
|
||||
|
6
vendor/k8s.io/apiserver/pkg/cel/library/authz.go
generated
vendored
6
vendor/k8s.io/apiserver/pkg/cel/library/authz.go
generated
vendored
@ -202,6 +202,10 @@ var authzLib = &authz{}
|
||||
|
||||
type authz struct{}
|
||||
|
||||
func (*authz) LibraryName() string {
|
||||
return "k8s.authz"
|
||||
}
|
||||
|
||||
var authzLibraryDecls = map[string][]cel.FunctionOpt{
|
||||
"path": {
|
||||
cel.MemberOverload("authorizer_path", []*cel.Type{AuthorizerType, cel.StringType}, PathCheckType,
|
||||
@ -578,7 +582,7 @@ type decisionVal struct {
|
||||
// any object type that has receiver functions but does not expose any fields to
|
||||
// CEL.
|
||||
type receiverOnlyObjectVal struct {
|
||||
typeValue *types.TypeValue
|
||||
typeValue *types.Type
|
||||
}
|
||||
|
||||
// receiverOnlyVal returns a receiverOnlyObjectVal for the given type.
|
||||
|
15
vendor/k8s.io/apiserver/pkg/cel/library/cost.go
generated
vendored
15
vendor/k8s.io/apiserver/pkg/cel/library/cost.go
generated
vendored
@ -101,8 +101,8 @@ func (l *CostEstimator) EstimateCallCost(function, overloadId string, target *ch
|
||||
// If the list contains strings or bytes, add the cost of traversing all the strings/bytes as a way
|
||||
// of estimating the additional comparison cost.
|
||||
if elNode := l.listElementNode(*target); elNode != nil {
|
||||
t := elNode.Type().GetPrimitive()
|
||||
if t == exprpb.Type_STRING || t == exprpb.Type_BYTES {
|
||||
k := elNode.Type().Kind()
|
||||
if k == types.StringKind || k == types.BytesKind {
|
||||
sz := l.sizeEstimate(elNode)
|
||||
elCost = elCost.Add(sz.MultiplyByCostFactor(common.StringTraversalCostFactor))
|
||||
}
|
||||
@ -247,7 +247,8 @@ func (l *CostEstimator) sizeEstimate(t checker.AstNode) checker.SizeEstimate {
|
||||
}
|
||||
|
||||
func (l *CostEstimator) listElementNode(list checker.AstNode) checker.AstNode {
|
||||
if lt := list.Type().GetListType(); lt != nil {
|
||||
if params := list.Type().Parameters(); len(params) > 0 {
|
||||
lt := params[0]
|
||||
nodePath := list.Path()
|
||||
if nodePath != nil {
|
||||
// Provide path if we have it so that a OpenAPIv3 maxLength validation can be looked up, if it exists
|
||||
@ -255,10 +256,10 @@ func (l *CostEstimator) listElementNode(list checker.AstNode) checker.AstNode {
|
||||
path := make([]string, len(nodePath)+1)
|
||||
copy(path, nodePath)
|
||||
path[len(nodePath)] = "@items"
|
||||
return &itemsNode{path: path, t: lt.GetElemType(), expr: nil}
|
||||
return &itemsNode{path: path, t: lt, expr: nil}
|
||||
} else {
|
||||
// Provide just the type if no path is available so that worst case size can be looked up based on type.
|
||||
return &itemsNode{t: lt.GetElemType(), expr: nil}
|
||||
return &itemsNode{t: lt, expr: nil}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -273,7 +274,7 @@ func (l *CostEstimator) EstimateSize(element checker.AstNode) *checker.SizeEstim
|
||||
|
||||
type itemsNode struct {
|
||||
path []string
|
||||
t *exprpb.Type
|
||||
t *types.Type
|
||||
expr *exprpb.Expr
|
||||
}
|
||||
|
||||
@ -281,7 +282,7 @@ func (i *itemsNode) Path() []string {
|
||||
return i.path
|
||||
}
|
||||
|
||||
func (i *itemsNode) Type() *exprpb.Type {
|
||||
func (i *itemsNode) Type() *types.Type {
|
||||
return i.t
|
||||
}
|
||||
|
||||
|
4
vendor/k8s.io/apiserver/pkg/cel/library/lists.go
generated
vendored
4
vendor/k8s.io/apiserver/pkg/cel/library/lists.go
generated
vendored
@ -95,6 +95,10 @@ var listsLib = &lists{}
|
||||
|
||||
type lists struct{}
|
||||
|
||||
func (*lists) LibraryName() string {
|
||||
return "k8s.lists"
|
||||
}
|
||||
|
||||
var paramA = cel.TypeParamType("A")
|
||||
|
||||
// CEL typeParams can be used to constraint to a specific trait (e.g. traits.ComparableType) if the 1st operand is the type to constrain.
|
||||
|
5
vendor/k8s.io/apiserver/pkg/cel/library/quantity.go
generated
vendored
5
vendor/k8s.io/apiserver/pkg/cel/library/quantity.go
generated
vendored
@ -22,6 +22,7 @@ import (
|
||||
"github.com/google/cel-go/cel"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
apiservercel "k8s.io/apiserver/pkg/cel"
|
||||
)
|
||||
@ -141,6 +142,10 @@ var quantityLib = &quantity{}
|
||||
|
||||
type quantity struct{}
|
||||
|
||||
func (*quantity) LibraryName() string {
|
||||
return "k8s.quantity"
|
||||
}
|
||||
|
||||
var quantityLibraryDecls = map[string][]cel.FunctionOpt{
|
||||
"quantity": {
|
||||
cel.Overload("string_to_quantity", []*cel.Type{cel.StringType}, apiservercel.QuantityType, cel.UnaryBinding((stringToQuantity))),
|
||||
|
4
vendor/k8s.io/apiserver/pkg/cel/library/regex.go
generated
vendored
4
vendor/k8s.io/apiserver/pkg/cel/library/regex.go
generated
vendored
@ -51,6 +51,10 @@ var regexLib = ®ex{}
|
||||
|
||||
type regex struct{}
|
||||
|
||||
func (*regex) LibraryName() string {
|
||||
return "k8s.regex"
|
||||
}
|
||||
|
||||
var regexLibraryDecls = map[string][]cel.FunctionOpt{
|
||||
"find": {
|
||||
cel.MemberOverload("string_find_string", []*cel.Type{cel.StringType, cel.StringType}, cel.StringType,
|
||||
|
4
vendor/k8s.io/apiserver/pkg/cel/library/test.go
generated
vendored
4
vendor/k8s.io/apiserver/pkg/cel/library/test.go
generated
vendored
@ -37,6 +37,10 @@ type testLib struct {
|
||||
version uint32
|
||||
}
|
||||
|
||||
func (*testLib) LibraryName() string {
|
||||
return "k8s.test"
|
||||
}
|
||||
|
||||
type TestOption func(*testLib) *testLib
|
||||
|
||||
func TestVersion(version uint32) func(lib *testLib) *testLib {
|
||||
|
4
vendor/k8s.io/apiserver/pkg/cel/library/urls.go
generated
vendored
4
vendor/k8s.io/apiserver/pkg/cel/library/urls.go
generated
vendored
@ -112,6 +112,10 @@ var urlsLib = &urls{}
|
||||
|
||||
type urls struct{}
|
||||
|
||||
func (*urls) LibraryName() string {
|
||||
return "k8s.urls"
|
||||
}
|
||||
|
||||
var urlLibraryDecls = map[string][]cel.FunctionOpt{
|
||||
"url": {
|
||||
cel.Overload("string_to_url", []*cel.Type{cel.StringType}, apiservercel.URLType,
|
||||
|
82
vendor/k8s.io/apiserver/pkg/cel/openapi/adaptor.go
generated
vendored
82
vendor/k8s.io/apiserver/pkg/cel/openapi/adaptor.go
generated
vendored
@ -54,6 +54,10 @@ func (s *Schema) Format() string {
|
||||
return s.Schema.Format
|
||||
}
|
||||
|
||||
func (s *Schema) Pattern() string {
|
||||
return s.Schema.Pattern
|
||||
}
|
||||
|
||||
func (s *Schema) Items() common.Schema {
|
||||
if s.Schema.Items == nil || s.Schema.Items.Schema == nil {
|
||||
return nil
|
||||
@ -86,14 +90,50 @@ func (s *Schema) Default() any {
|
||||
return s.Schema.Default
|
||||
}
|
||||
|
||||
func (s *Schema) Minimum() *float64 {
|
||||
return s.Schema.Minimum
|
||||
}
|
||||
|
||||
func (s *Schema) IsExclusiveMinimum() bool {
|
||||
return s.Schema.ExclusiveMinimum
|
||||
}
|
||||
|
||||
func (s *Schema) Maximum() *float64 {
|
||||
return s.Schema.Maximum
|
||||
}
|
||||
|
||||
func (s *Schema) IsExclusiveMaximum() bool {
|
||||
return s.Schema.ExclusiveMaximum
|
||||
}
|
||||
|
||||
func (s *Schema) MultipleOf() *float64 {
|
||||
return s.Schema.MultipleOf
|
||||
}
|
||||
|
||||
func (s *Schema) UniqueItems() bool {
|
||||
return s.Schema.UniqueItems
|
||||
}
|
||||
|
||||
func (s *Schema) MinItems() *int64 {
|
||||
return s.Schema.MinItems
|
||||
}
|
||||
|
||||
func (s *Schema) MaxItems() *int64 {
|
||||
return s.Schema.MaxItems
|
||||
}
|
||||
|
||||
func (s *Schema) MinLength() *int64 {
|
||||
return s.Schema.MinLength
|
||||
}
|
||||
|
||||
func (s *Schema) MaxLength() *int64 {
|
||||
return s.Schema.MaxLength
|
||||
}
|
||||
|
||||
func (s *Schema) MinProperties() *int64 {
|
||||
return s.Schema.MinProperties
|
||||
}
|
||||
|
||||
func (s *Schema) MaxProperties() *int64 {
|
||||
return s.Schema.MaxProperties
|
||||
}
|
||||
@ -110,6 +150,40 @@ func (s *Schema) Nullable() bool {
|
||||
return s.Schema.Nullable
|
||||
}
|
||||
|
||||
func (s *Schema) AllOf() []common.Schema {
|
||||
var res []common.Schema
|
||||
for _, nestedSchema := range s.Schema.AllOf {
|
||||
nestedSchema := nestedSchema
|
||||
res = append(res, &Schema{&nestedSchema})
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (s *Schema) AnyOf() []common.Schema {
|
||||
var res []common.Schema
|
||||
for _, nestedSchema := range s.Schema.AnyOf {
|
||||
nestedSchema := nestedSchema
|
||||
res = append(res, &Schema{&nestedSchema})
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (s *Schema) OneOf() []common.Schema {
|
||||
var res []common.Schema
|
||||
for _, nestedSchema := range s.Schema.OneOf {
|
||||
nestedSchema := nestedSchema
|
||||
res = append(res, &Schema{&nestedSchema})
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (s *Schema) Not() common.Schema {
|
||||
if s.Schema.Not == nil {
|
||||
return nil
|
||||
}
|
||||
return &Schema{s.Schema.Not}
|
||||
}
|
||||
|
||||
func (s *Schema) IsXIntOrString() bool {
|
||||
return isXIntOrString(s.Schema)
|
||||
}
|
||||
@ -126,10 +200,18 @@ func (s *Schema) XListType() string {
|
||||
return getXListType(s.Schema)
|
||||
}
|
||||
|
||||
func (s *Schema) XMapType() string {
|
||||
return getXMapType(s.Schema)
|
||||
}
|
||||
|
||||
func (s *Schema) XListMapKeys() []string {
|
||||
return getXListMapKeys(s.Schema)
|
||||
}
|
||||
|
||||
func (s *Schema) XValidations() []common.ValidationRule {
|
||||
return getXValidations(s.Schema)
|
||||
}
|
||||
|
||||
func (s *Schema) WithTypeAndObjectMeta() common.Schema {
|
||||
return &Schema{common.WithTypeAndObjectMeta(s.Schema)}
|
||||
}
|
||||
|
45
vendor/k8s.io/apiserver/pkg/cel/openapi/extensions.go
generated
vendored
45
vendor/k8s.io/apiserver/pkg/cel/openapi/extensions.go
generated
vendored
@ -18,6 +18,7 @@ package openapi
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/apiserver/pkg/cel/common"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
@ -47,6 +48,11 @@ func getXListType(schema *spec.Schema) string {
|
||||
return s
|
||||
}
|
||||
|
||||
func getXMapType(schema *spec.Schema) string {
|
||||
s, _ := schema.Extensions.GetString(extMapType)
|
||||
return s
|
||||
}
|
||||
|
||||
func getXListMapKeys(schema *spec.Schema) []string {
|
||||
mapKeys, ok := schema.Extensions.GetStringSlice(extListMapKeys)
|
||||
if !ok {
|
||||
@ -55,8 +61,47 @@ func getXListMapKeys(schema *spec.Schema) []string {
|
||||
return mapKeys
|
||||
}
|
||||
|
||||
type ValidationRule struct {
|
||||
RuleField string `json:"rule"`
|
||||
MessageField string `json:"message"`
|
||||
MessageExpressionField string `json:"messageExpression"`
|
||||
PathField string `json:"fieldPath"`
|
||||
}
|
||||
|
||||
func (v ValidationRule) Rule() string {
|
||||
return v.RuleField
|
||||
}
|
||||
|
||||
func (v ValidationRule) Message() string {
|
||||
return v.MessageField
|
||||
}
|
||||
|
||||
func (v ValidationRule) FieldPath() string {
|
||||
return v.PathField
|
||||
}
|
||||
|
||||
func (v ValidationRule) MessageExpression() string {
|
||||
return v.MessageExpressionField
|
||||
}
|
||||
|
||||
// TODO: simplify
|
||||
func getXValidations(schema *spec.Schema) []common.ValidationRule {
|
||||
var rules []ValidationRule
|
||||
err := schema.Extensions.GetObject(extValidations, &rules)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
results := make([]common.ValidationRule, len(rules))
|
||||
for i, rule := range rules {
|
||||
results[i] = rule
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
const extIntOrString = "x-kubernetes-int-or-string"
|
||||
const extEmbeddedResource = "x-kubernetes-embedded-resource"
|
||||
const extPreserveUnknownFields = "x-kubernetes-preserve-unknown-fields"
|
||||
const extListType = "x-kubernetes-list-type"
|
||||
const extMapType = "x-kubernetes-map-type"
|
||||
const extListMapKeys = "x-kubernetes-list-map-keys"
|
||||
const extValidations = "x-kubernetes-validations"
|
||||
|
45
vendor/k8s.io/apiserver/pkg/cel/openapi/resolver/combined.go
generated
vendored
Normal file
45
vendor/k8s.io/apiserver/pkg/cel/openapi/resolver/combined.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright 2023 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 resolver
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
// Combine combines the DefinitionsSchemaResolver with a secondary schema resolver.
|
||||
// The resulting schema resolver uses the DefinitionsSchemaResolver for a GVK that DefinitionsSchemaResolver knows,
|
||||
// and the secondary otherwise.
|
||||
func (d *DefinitionsSchemaResolver) Combine(secondary SchemaResolver) SchemaResolver {
|
||||
return &combinedSchemaResolver{definitions: d, secondary: secondary}
|
||||
}
|
||||
|
||||
type combinedSchemaResolver struct {
|
||||
definitions *DefinitionsSchemaResolver
|
||||
secondary SchemaResolver
|
||||
}
|
||||
|
||||
// ResolveSchema takes a GroupVersionKind (GVK) and returns the OpenAPI schema
|
||||
// identified by the GVK.
|
||||
// If the DefinitionsSchemaResolver knows the gvk, the DefinitionsSchemaResolver handles the resolution,
|
||||
// otherwise, the secondary does.
|
||||
func (r *combinedSchemaResolver) ResolveSchema(gvk schema.GroupVersionKind) (*spec.Schema, error) {
|
||||
if _, ok := r.definitions.gvkToRef[gvk]; ok {
|
||||
return r.definitions.ResolveSchema(gvk)
|
||||
}
|
||||
return r.secondary.ResolveSchema(gvk)
|
||||
}
|
27
vendor/k8s.io/apiserver/pkg/cel/openapi/resolver/definitions.go
generated
vendored
27
vendor/k8s.io/apiserver/pkg/cel/openapi/resolver/definitions.go
generated
vendored
@ -29,40 +29,39 @@ import (
|
||||
// DefinitionsSchemaResolver resolves the schema of a built-in type
|
||||
// by looking up the OpenAPI definitions.
|
||||
type DefinitionsSchemaResolver struct {
|
||||
defs map[string]common.OpenAPIDefinition
|
||||
gvkToSchema map[schema.GroupVersionKind]*spec.Schema
|
||||
defs map[string]common.OpenAPIDefinition
|
||||
gvkToRef map[schema.GroupVersionKind]string
|
||||
}
|
||||
|
||||
// NewDefinitionsSchemaResolver creates a new DefinitionsSchemaResolver.
|
||||
// An example working setup:
|
||||
// scheme = "k8s.io/client-go/kubernetes/scheme".Scheme
|
||||
// getDefinitions = "k8s.io/kubernetes/pkg/generated/openapi".GetOpenAPIDefinitions
|
||||
func NewDefinitionsSchemaResolver(scheme *runtime.Scheme, getDefinitions common.GetOpenAPIDefinitions) *DefinitionsSchemaResolver {
|
||||
gvkToSchema := make(map[schema.GroupVersionKind]*spec.Schema)
|
||||
namer := openapi.NewDefinitionNamer(scheme)
|
||||
// scheme = "k8s.io/client-go/kubernetes/scheme".Scheme
|
||||
func NewDefinitionsSchemaResolver(getDefinitions common.GetOpenAPIDefinitions, schemes ...*runtime.Scheme) *DefinitionsSchemaResolver {
|
||||
gvkToRef := make(map[schema.GroupVersionKind]string)
|
||||
namer := openapi.NewDefinitionNamer(schemes...)
|
||||
defs := getDefinitions(func(path string) spec.Ref {
|
||||
return spec.MustCreateRef(path)
|
||||
})
|
||||
for name, def := range defs {
|
||||
for name := range defs {
|
||||
_, e := namer.GetDefinitionName(name)
|
||||
gvks := extensionsToGVKs(e)
|
||||
s := def.Schema // map value not addressable, make copy
|
||||
for _, gvk := range gvks {
|
||||
gvkToSchema[gvk] = &s
|
||||
gvkToRef[gvk] = name
|
||||
}
|
||||
}
|
||||
return &DefinitionsSchemaResolver{
|
||||
gvkToSchema: gvkToSchema,
|
||||
defs: defs,
|
||||
gvkToRef: gvkToRef,
|
||||
defs: defs,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DefinitionsSchemaResolver) ResolveSchema(gvk schema.GroupVersionKind) (*spec.Schema, error) {
|
||||
s, ok := d.gvkToSchema[gvk]
|
||||
ref, ok := d.gvkToRef[gvk]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot resolve %v: %w", gvk, ErrSchemaNotFound)
|
||||
}
|
||||
s, err := populateRefs(func(ref string) (*spec.Schema, bool) {
|
||||
s, err := PopulateRefs(func(ref string) (*spec.Schema, bool) {
|
||||
// find the schema by the ref string, and return a deep copy
|
||||
def, ok := d.defs[ref]
|
||||
if !ok {
|
||||
@ -70,7 +69,7 @@ func (d *DefinitionsSchemaResolver) ResolveSchema(gvk schema.GroupVersionKind) (
|
||||
}
|
||||
s := def.Schema
|
||||
return &s, true
|
||||
}, s)
|
||||
}, ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
16
vendor/k8s.io/apiserver/pkg/cel/openapi/resolver/discovery.go
generated
vendored
16
vendor/k8s.io/apiserver/pkg/cel/openapi/resolver/discovery.go
generated
vendored
@ -53,34 +53,34 @@ func (r *ClientDiscoveryResolver) ResolveSchema(gvk schema.GroupVersionKind) (*s
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s, err := resolveType(resp, gvk)
|
||||
ref, err := resolveRef(resp, gvk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s, err = populateRefs(func(ref string) (*spec.Schema, bool) {
|
||||
s, err := PopulateRefs(func(ref string) (*spec.Schema, bool) {
|
||||
s, ok := resp.Components.Schemas[strings.TrimPrefix(ref, refPrefix)]
|
||||
return s, ok
|
||||
}, s)
|
||||
}, ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func resolveType(resp *schemaResponse, gvk schema.GroupVersionKind) (*spec.Schema, error) {
|
||||
for _, s := range resp.Components.Schemas {
|
||||
func resolveRef(resp *schemaResponse, gvk schema.GroupVersionKind) (string, error) {
|
||||
for ref, s := range resp.Components.Schemas {
|
||||
var gvks []schema.GroupVersionKind
|
||||
err := s.Extensions.GetObject(extGVK, &gvks)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
for _, g := range gvks {
|
||||
if g == gvk {
|
||||
return s, nil
|
||||
return ref, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("cannot resolve group version kind %q: %w", gvk, ErrSchemaNotFound)
|
||||
return "", fmt.Errorf("cannot resolve group version kind %q: %w", gvk, ErrSchemaNotFound)
|
||||
}
|
||||
|
||||
func resourcePathFromGV(gv schema.GroupVersion) string {
|
||||
|
32
vendor/k8s.io/apiserver/pkg/cel/openapi/resolver/refs.go
generated
vendored
32
vendor/k8s.io/apiserver/pkg/cel/openapi/resolver/refs.go
generated
vendored
@ -19,19 +19,41 @@ package resolver
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
// populateRefs recursively replaces Refs in the schema with the referred one.
|
||||
// PopulateRefs recursively replaces Refs in the schema with the referred one.
|
||||
// schemaOf is the callback to find the corresponding schema by the ref.
|
||||
// This function will not mutate the original schema. If the schema needs to be
|
||||
// mutated, a copy will be returned, otherwise it returns the original schema.
|
||||
func populateRefs(schemaOf func(ref string) (*spec.Schema, bool), schema *spec.Schema) (*spec.Schema, error) {
|
||||
func PopulateRefs(schemaOf func(ref string) (*spec.Schema, bool), rootRef string) (*spec.Schema, error) {
|
||||
visitedRefs := sets.New[string]()
|
||||
rootSchema, ok := schemaOf(rootRef)
|
||||
visitedRefs.Insert(rootRef)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("internal error: cannot resolve Ref for root schema %q: %w", rootRef, ErrSchemaNotFound)
|
||||
}
|
||||
return populateRefs(schemaOf, visitedRefs, rootSchema)
|
||||
}
|
||||
|
||||
func populateRefs(schemaOf func(ref string) (*spec.Schema, bool), visited sets.Set[string], schema *spec.Schema) (*spec.Schema, error) {
|
||||
result := *schema
|
||||
changed := false
|
||||
|
||||
ref, isRef := refOf(schema)
|
||||
if isRef {
|
||||
if visited.Has(ref) {
|
||||
return &spec.Schema{
|
||||
// for circular ref, return an empty object as placeholder
|
||||
SchemaProps: spec.SchemaProps{Type: []string{"object"}},
|
||||
}, nil
|
||||
}
|
||||
visited.Insert(ref)
|
||||
// restore visited state at the end of the recursion.
|
||||
defer func() {
|
||||
visited.Delete(ref)
|
||||
}()
|
||||
// replace the whole schema with the referred one.
|
||||
resolved, ok := schemaOf(ref)
|
||||
if !ok {
|
||||
@ -44,7 +66,7 @@ func populateRefs(schemaOf func(ref string) (*spec.Schema, bool), schema *spec.S
|
||||
props := make(map[string]spec.Schema, len(schema.Properties))
|
||||
propsChanged := false
|
||||
for name, prop := range result.Properties {
|
||||
populated, err := populateRefs(schemaOf, &prop)
|
||||
populated, err := populateRefs(schemaOf, visited, &prop)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -58,7 +80,7 @@ func populateRefs(schemaOf func(ref string) (*spec.Schema, bool), schema *spec.S
|
||||
result.Properties = props
|
||||
}
|
||||
if result.AdditionalProperties != nil && result.AdditionalProperties.Schema != nil {
|
||||
populated, err := populateRefs(schemaOf, result.AdditionalProperties.Schema)
|
||||
populated, err := populateRefs(schemaOf, visited, result.AdditionalProperties.Schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -69,7 +91,7 @@ func populateRefs(schemaOf func(ref string) (*spec.Schema, bool), schema *spec.S
|
||||
}
|
||||
// schema is a list, populate its items
|
||||
if result.Items != nil && result.Items.Schema != nil {
|
||||
populated, err := populateRefs(schemaOf, result.Items.Schema)
|
||||
populated, err := populateRefs(schemaOf, visited, result.Items.Schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Reference in New Issue
Block a user