mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-04 07:49:35 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			405 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			405 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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
 | 
						|
//
 | 
						|
//      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 interpreter
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
 | 
						|
	"github.com/google/cel-go/common/containers"
 | 
						|
	"github.com/google/cel-go/common/types"
 | 
						|
	"github.com/google/cel-go/common/types/ref"
 | 
						|
)
 | 
						|
 | 
						|
// AttributePattern represents a top-level variable with an optional set of qualifier patterns.
 | 
						|
//
 | 
						|
// When using a CEL expression within a container, e.g. a package or namespace, the variable name
 | 
						|
// in the pattern must match the qualified name produced during the variable namespace resolution.
 | 
						|
// For example, if variable `c` appears in an expression whose container is `a.b`, the variable
 | 
						|
// name supplied to the pattern must be `a.b.c`
 | 
						|
//
 | 
						|
// The qualifier patterns for attribute matching must be one of the following:
 | 
						|
//
 | 
						|
//   - valid map key type: string, int, uint, bool
 | 
						|
//   - wildcard (*)
 | 
						|
//
 | 
						|
// Examples:
 | 
						|
//
 | 
						|
//   1. ns.myvar["complex-value"]
 | 
						|
//   2. ns.myvar["complex-value"][0]
 | 
						|
//   3. ns.myvar["complex-value"].*.name
 | 
						|
//
 | 
						|
// The first example is simple: match an attribute where the variable is 'ns.myvar' with a
 | 
						|
// field access on 'complex-value'. The second example expands the match to indicate that only
 | 
						|
// a specific index `0` should match. And lastly, the third example matches any indexed access
 | 
						|
// that later selects the 'name' field.
 | 
						|
type AttributePattern struct {
 | 
						|
	variable          string
 | 
						|
	qualifierPatterns []*AttributeQualifierPattern
 | 
						|
}
 | 
						|
 | 
						|
// NewAttributePattern produces a new mutable AttributePattern based on a variable name.
 | 
						|
func NewAttributePattern(variable string) *AttributePattern {
 | 
						|
	return &AttributePattern{
 | 
						|
		variable:          variable,
 | 
						|
		qualifierPatterns: []*AttributeQualifierPattern{},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// QualString adds a string qualifier pattern to the AttributePattern. The string may be a valid
 | 
						|
// identifier, or string map key including empty string.
 | 
						|
func (apat *AttributePattern) QualString(pattern string) *AttributePattern {
 | 
						|
	apat.qualifierPatterns = append(apat.qualifierPatterns,
 | 
						|
		&AttributeQualifierPattern{value: pattern})
 | 
						|
	return apat
 | 
						|
}
 | 
						|
 | 
						|
// QualInt adds an int qualifier pattern to the AttributePattern. The index may be either a map or
 | 
						|
// list index.
 | 
						|
func (apat *AttributePattern) QualInt(pattern int64) *AttributePattern {
 | 
						|
	apat.qualifierPatterns = append(apat.qualifierPatterns,
 | 
						|
		&AttributeQualifierPattern{value: pattern})
 | 
						|
	return apat
 | 
						|
}
 | 
						|
 | 
						|
// QualUint adds an uint qualifier pattern for a map index operation to the AttributePattern.
 | 
						|
func (apat *AttributePattern) QualUint(pattern uint64) *AttributePattern {
 | 
						|
	apat.qualifierPatterns = append(apat.qualifierPatterns,
 | 
						|
		&AttributeQualifierPattern{value: pattern})
 | 
						|
	return apat
 | 
						|
}
 | 
						|
 | 
						|
// QualBool adds a bool qualifier pattern for a map index operation to the AttributePattern.
 | 
						|
func (apat *AttributePattern) QualBool(pattern bool) *AttributePattern {
 | 
						|
	apat.qualifierPatterns = append(apat.qualifierPatterns,
 | 
						|
		&AttributeQualifierPattern{value: pattern})
 | 
						|
	return apat
 | 
						|
}
 | 
						|
 | 
						|
// Wildcard adds a special sentinel qualifier pattern that will match any single qualifier.
 | 
						|
func (apat *AttributePattern) Wildcard() *AttributePattern {
 | 
						|
	apat.qualifierPatterns = append(apat.qualifierPatterns,
 | 
						|
		&AttributeQualifierPattern{wildcard: true})
 | 
						|
	return apat
 | 
						|
}
 | 
						|
 | 
						|
// VariableMatches returns true if the fully qualified variable matches the AttributePattern
 | 
						|
// fully qualified variable name.
 | 
						|
func (apat *AttributePattern) VariableMatches(variable string) bool {
 | 
						|
	return apat.variable == variable
 | 
						|
}
 | 
						|
 | 
						|
// QualifierPatterns returns the set of AttributeQualifierPattern values on the AttributePattern.
 | 
						|
func (apat *AttributePattern) QualifierPatterns() []*AttributeQualifierPattern {
 | 
						|
	return apat.qualifierPatterns
 | 
						|
}
 | 
						|
 | 
						|
// AttributeQualifierPattern holds a wildcard or valued qualifier pattern.
 | 
						|
type AttributeQualifierPattern struct {
 | 
						|
	wildcard bool
 | 
						|
	value    interface{}
 | 
						|
}
 | 
						|
 | 
						|
// Matches returns true if the qualifier pattern is a wildcard, or the Qualifier implements the
 | 
						|
// qualifierValueEquator interface and its IsValueEqualTo returns true for the qualifier pattern.
 | 
						|
func (qpat *AttributeQualifierPattern) Matches(q Qualifier) bool {
 | 
						|
	if qpat.wildcard {
 | 
						|
		return true
 | 
						|
	}
 | 
						|
	qve, ok := q.(qualifierValueEquator)
 | 
						|
	return ok && qve.QualifierValueEquals(qpat.value)
 | 
						|
}
 | 
						|
 | 
						|
// qualifierValueEquator defines an interface for determining if an input value, of valid map key
 | 
						|
// type, is equal to the value held in the Qualifier. This interface is used by the
 | 
						|
// AttributeQualifierPattern to determine pattern matches for non-wildcard qualifier patterns.
 | 
						|
//
 | 
						|
// Note: Attribute values are also Qualifier values; however, Attributes are resolved before
 | 
						|
// qualification happens. This is an implementation detail, but one relevant to why the Attribute
 | 
						|
// types do not surface in the list of implementations.
 | 
						|
//
 | 
						|
// See: partialAttributeFactory.matchesUnknownPatterns for more details on how this interface is
 | 
						|
// used.
 | 
						|
type qualifierValueEquator interface {
 | 
						|
	// QualifierValueEquals returns true if the input value is equal to the value held in the
 | 
						|
	// Qualifier.
 | 
						|
	QualifierValueEquals(value interface{}) bool
 | 
						|
}
 | 
						|
 | 
						|
// QualifierValueEquals implementation for boolean qualifiers.
 | 
						|
func (q *boolQualifier) QualifierValueEquals(value interface{}) bool {
 | 
						|
	bval, ok := value.(bool)
 | 
						|
	return ok && q.value == bval
 | 
						|
}
 | 
						|
 | 
						|
// QualifierValueEquals implementation for field qualifiers.
 | 
						|
func (q *fieldQualifier) QualifierValueEquals(value interface{}) bool {
 | 
						|
	sval, ok := value.(string)
 | 
						|
	return ok && q.Name == sval
 | 
						|
}
 | 
						|
 | 
						|
// QualifierValueEquals implementation for string qualifiers.
 | 
						|
func (q *stringQualifier) QualifierValueEquals(value interface{}) bool {
 | 
						|
	sval, ok := value.(string)
 | 
						|
	return ok && q.value == sval
 | 
						|
}
 | 
						|
 | 
						|
// QualifierValueEquals implementation for int qualifiers.
 | 
						|
func (q *intQualifier) QualifierValueEquals(value interface{}) bool {
 | 
						|
	return numericValueEquals(value, q.celValue)
 | 
						|
}
 | 
						|
 | 
						|
// QualifierValueEquals implementation for uint qualifiers.
 | 
						|
func (q *uintQualifier) QualifierValueEquals(value interface{}) bool {
 | 
						|
	return numericValueEquals(value, q.celValue)
 | 
						|
}
 | 
						|
 | 
						|
// QualifierValueEquals implementation for double qualifiers.
 | 
						|
func (q *doubleQualifier) QualifierValueEquals(value interface{}) bool {
 | 
						|
	return numericValueEquals(value, q.celValue)
 | 
						|
}
 | 
						|
 | 
						|
// numericValueEquals uses CEL equality to determine whether two number values are
 | 
						|
func numericValueEquals(value interface{}, celValue ref.Val) bool {
 | 
						|
	val := types.DefaultTypeAdapter.NativeToValue(value)
 | 
						|
	return celValue.Equal(val) == types.True
 | 
						|
}
 | 
						|
 | 
						|
// NewPartialAttributeFactory returns an AttributeFactory implementation capable of performing
 | 
						|
// AttributePattern matches with PartialActivation inputs.
 | 
						|
func NewPartialAttributeFactory(container *containers.Container,
 | 
						|
	adapter ref.TypeAdapter,
 | 
						|
	provider ref.TypeProvider) AttributeFactory {
 | 
						|
	fac := NewAttributeFactory(container, adapter, provider)
 | 
						|
	return &partialAttributeFactory{
 | 
						|
		AttributeFactory: fac,
 | 
						|
		container:        container,
 | 
						|
		adapter:          adapter,
 | 
						|
		provider:         provider,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type partialAttributeFactory struct {
 | 
						|
	AttributeFactory
 | 
						|
	container *containers.Container
 | 
						|
	adapter   ref.TypeAdapter
 | 
						|
	provider  ref.TypeProvider
 | 
						|
}
 | 
						|
 | 
						|
// AbsoluteAttribute implementation of the AttributeFactory interface which wraps the
 | 
						|
// NamespacedAttribute resolution in an internal attributeMatcher object to dynamically match
 | 
						|
// unknown patterns from PartialActivation inputs if given.
 | 
						|
func (fac *partialAttributeFactory) AbsoluteAttribute(id int64, names ...string) NamespacedAttribute {
 | 
						|
	attr := fac.AttributeFactory.AbsoluteAttribute(id, names...)
 | 
						|
	return &attributeMatcher{fac: fac, NamespacedAttribute: attr}
 | 
						|
}
 | 
						|
 | 
						|
// MaybeAttribute implementation of the AttributeFactory interface which ensure that the set of
 | 
						|
// 'maybe' NamespacedAttribute values are produced using the partialAttributeFactory rather than
 | 
						|
// the base AttributeFactory implementation.
 | 
						|
func (fac *partialAttributeFactory) MaybeAttribute(id int64, name string) Attribute {
 | 
						|
	return &maybeAttribute{
 | 
						|
		id: id,
 | 
						|
		attrs: []NamespacedAttribute{
 | 
						|
			fac.AbsoluteAttribute(id, fac.container.ResolveCandidateNames(name)...),
 | 
						|
		},
 | 
						|
		adapter:  fac.adapter,
 | 
						|
		provider: fac.provider,
 | 
						|
		fac:      fac,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// matchesUnknownPatterns returns true if the variable names and qualifiers for a given
 | 
						|
// Attribute value match any of the ActivationPattern objects in the set of unknown activation
 | 
						|
// patterns on the given PartialActivation.
 | 
						|
//
 | 
						|
// For example, in the expression `a.b`, the Attribute is composed of variable `a`, with string
 | 
						|
// qualifier `b`. When a PartialActivation is supplied, it indicates that some or all of the data
 | 
						|
// provided in the input is unknown by specifying unknown AttributePatterns. An AttributePattern
 | 
						|
// that refers to variable `a` with a string qualifier of `c` will not match `a.b`; however, any
 | 
						|
// of the following patterns will match Attribute `a.b`:
 | 
						|
//
 | 
						|
// - `AttributePattern("a")`
 | 
						|
// - `AttributePattern("a").Wildcard()`
 | 
						|
// - `AttributePattern("a").QualString("b")`
 | 
						|
// - `AttributePattern("a").QualString("b").QualInt(0)`
 | 
						|
//
 | 
						|
// Any AttributePattern which overlaps an Attribute or vice-versa will produce an Unknown result
 | 
						|
// for the last pattern matched variable or qualifier in the Attribute. In the first matching
 | 
						|
// example, the expression id representing variable `a` would be listed in the Unknown result,
 | 
						|
// whereas in the other pattern examples, the qualifier `b` would be returned as the Unknown.
 | 
						|
func (fac *partialAttributeFactory) matchesUnknownPatterns(
 | 
						|
	vars PartialActivation,
 | 
						|
	attrID int64,
 | 
						|
	variableNames []string,
 | 
						|
	qualifiers []Qualifier) (types.Unknown, error) {
 | 
						|
	patterns := vars.UnknownAttributePatterns()
 | 
						|
	candidateIndices := map[int]struct{}{}
 | 
						|
	for _, variable := range variableNames {
 | 
						|
		for i, pat := range patterns {
 | 
						|
			if pat.VariableMatches(variable) {
 | 
						|
				candidateIndices[i] = struct{}{}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	// Determine whether to return early if there are no candidate unknown patterns.
 | 
						|
	if len(candidateIndices) == 0 {
 | 
						|
		return nil, nil
 | 
						|
	}
 | 
						|
	// Determine whether to return early if there are no qualifiers.
 | 
						|
	if len(qualifiers) == 0 {
 | 
						|
		return types.Unknown{attrID}, nil
 | 
						|
	}
 | 
						|
	// Resolve the attribute qualifiers into a static set. This prevents more dynamic
 | 
						|
	// Attribute resolutions than necessary when there are multiple unknown patterns
 | 
						|
	// that traverse the same Attribute-based qualifier field.
 | 
						|
	newQuals := make([]Qualifier, len(qualifiers))
 | 
						|
	for i, qual := range qualifiers {
 | 
						|
		attr, isAttr := qual.(Attribute)
 | 
						|
		if isAttr {
 | 
						|
			val, err := attr.Resolve(vars)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			unk, isUnk := val.(types.Unknown)
 | 
						|
			if isUnk {
 | 
						|
				return unk, nil
 | 
						|
			}
 | 
						|
			// If this resolution behavior ever changes, new implementations of the
 | 
						|
			// qualifierValueEquator may be required to handle proper resolution.
 | 
						|
			qual, err = fac.NewQualifier(nil, qual.ID(), val)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
		}
 | 
						|
		newQuals[i] = qual
 | 
						|
	}
 | 
						|
	// Determine whether any of the unknown patterns match.
 | 
						|
	for patIdx := range candidateIndices {
 | 
						|
		pat := patterns[patIdx]
 | 
						|
		isUnk := true
 | 
						|
		matchExprID := attrID
 | 
						|
		qualPats := pat.QualifierPatterns()
 | 
						|
		for i, qual := range newQuals {
 | 
						|
			if i >= len(qualPats) {
 | 
						|
				break
 | 
						|
			}
 | 
						|
			matchExprID = qual.ID()
 | 
						|
			qualPat := qualPats[i]
 | 
						|
			// Note, the AttributeQualifierPattern relies on the input Qualifier not being an
 | 
						|
			// Attribute, since there is no way to resolve the Attribute with the information
 | 
						|
			// provided to the Matches call.
 | 
						|
			if !qualPat.Matches(qual) {
 | 
						|
				isUnk = false
 | 
						|
				break
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if isUnk {
 | 
						|
			return types.Unknown{matchExprID}, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil, nil
 | 
						|
}
 | 
						|
 | 
						|
// attributeMatcher embeds the NamespacedAttribute interface which allows it to participate in
 | 
						|
// AttributePattern matching against Attribute values without having to modify the code paths that
 | 
						|
// identify Attributes in expressions.
 | 
						|
type attributeMatcher struct {
 | 
						|
	NamespacedAttribute
 | 
						|
	qualifiers []Qualifier
 | 
						|
	fac        *partialAttributeFactory
 | 
						|
}
 | 
						|
 | 
						|
// AddQualifier implements the Attribute interface method.
 | 
						|
func (m *attributeMatcher) AddQualifier(qual Qualifier) (Attribute, error) {
 | 
						|
	// Add the qualifier to the embedded NamespacedAttribute. If the input to the Resolve
 | 
						|
	// method is not a PartialActivation, or does not match an unknown attribute pattern, the
 | 
						|
	// Resolve method is directly invoked on the underlying NamespacedAttribute.
 | 
						|
	_, err := m.NamespacedAttribute.AddQualifier(qual)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	// The attributeMatcher overloads TryResolve and will attempt to match unknown patterns against
 | 
						|
	// the variable name and qualifier set contained within the Attribute. These values are not
 | 
						|
	// directly inspectable on the top-level NamespacedAttribute interface and so are tracked within
 | 
						|
	// the attributeMatcher.
 | 
						|
	m.qualifiers = append(m.qualifiers, qual)
 | 
						|
	return m, nil
 | 
						|
}
 | 
						|
 | 
						|
// Resolve is an implementation of the Attribute interface method which uses the
 | 
						|
// attributeMatcher TryResolve implementation rather than the embedded NamespacedAttribute
 | 
						|
// Resolve implementation.
 | 
						|
func (m *attributeMatcher) Resolve(vars Activation) (interface{}, error) {
 | 
						|
	obj, found, err := m.TryResolve(vars)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if !found {
 | 
						|
		return nil, fmt.Errorf("no such attribute: %v", m.NamespacedAttribute)
 | 
						|
	}
 | 
						|
	return obj, nil
 | 
						|
}
 | 
						|
 | 
						|
// TryResolve is an implementation of the NamespacedAttribute interface method which tests
 | 
						|
// for matching unknown attribute patterns and returns types.Unknown if present. Otherwise,
 | 
						|
// the standard Resolve logic applies.
 | 
						|
func (m *attributeMatcher) TryResolve(vars Activation) (interface{}, bool, error) {
 | 
						|
	id := m.NamespacedAttribute.ID()
 | 
						|
	// Bug in how partial activation is resolved, should search parents as well.
 | 
						|
	partial, isPartial := toPartialActivation(vars)
 | 
						|
	if isPartial {
 | 
						|
		unk, err := m.fac.matchesUnknownPatterns(
 | 
						|
			partial,
 | 
						|
			id,
 | 
						|
			m.CandidateVariableNames(),
 | 
						|
			m.qualifiers)
 | 
						|
		if err != nil {
 | 
						|
			return nil, true, err
 | 
						|
		}
 | 
						|
		if unk != nil {
 | 
						|
			return unk, true, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return m.NamespacedAttribute.TryResolve(vars)
 | 
						|
}
 | 
						|
 | 
						|
// Qualify is an implementation of the Qualifier interface method.
 | 
						|
func (m *attributeMatcher) Qualify(vars Activation, obj interface{}) (interface{}, error) {
 | 
						|
	val, err := m.Resolve(vars)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	unk, isUnk := val.(types.Unknown)
 | 
						|
	if isUnk {
 | 
						|
		return unk, nil
 | 
						|
	}
 | 
						|
	qual, err := m.fac.NewQualifier(nil, m.ID(), val)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return qual.Qualify(vars, obj)
 | 
						|
}
 | 
						|
 | 
						|
func toPartialActivation(vars Activation) (PartialActivation, bool) {
 | 
						|
	pv, ok := vars.(PartialActivation)
 | 
						|
	if ok {
 | 
						|
		return pv, true
 | 
						|
	}
 | 
						|
	if vars.Parent() != nil {
 | 
						|
		return toPartialActivation(vars.Parent())
 | 
						|
	}
 | 
						|
	return nil, false
 | 
						|
}
 |