Files
capsule/pkg/api/runtime/expression.go
T
77d1810bb9 feat: add metadata enforcement (#1990)
* fix(controller): decode old object for delete requests

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* fix: preserve ca-bundles injected from external providers

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat: add metadata enforcement

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat: add metadata enforcement

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* fix: add resourcepoolclaim validation

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* fix: add resourcepoolclaim validation

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* fix: add resourcepoolclaim validation

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix: add resourcepoolclaim validation

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-02 13:42:08 +02:00

142 lines
3.1 KiB
Go

// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package runtime
import (
"fmt"
"regexp"
"slices"
"strings"
)
// At least one of Exact or Exp must be set.
// Both may be set together.
// +kubebuilder:object:generate=true
// +kubebuilder:validation:XValidation:rule="has(self.exact) || has(self.exp)",message="at least one of exact or exp must be set"
type ExpressionMatch struct {
ExpressionRegex `json:",inline"`
// Exact matches one of the provided values exactly.
//
// +kubebuilder:validation:MinItems=1
// +kubebuilder:validation:Items:MinLength=1
// +optional
Exact []string `json:"exact,omitempty"`
}
type ExpressionRegex struct {
// Exp matches regular expression.
//
// +kubebuilder:validation:MinLength=1
// +optional
Expression string `json:"exp,omitempty"`
// Negate regular Expression
//+kubebuilder:default:=false
Negate bool `json:"negate,omitempty"`
}
type ExpressionRegexMatcher interface {
MatchRegex(expression ExpressionRegex, value string) (bool, error)
}
func (m ExpressionMatch) Matches(value string) (bool, error) {
matched, err := m.matches(value)
if err != nil {
return false, err
}
return m.applyNegate(matched), nil
}
func (m ExpressionMatch) MatchesWithExpressionMatcher(
matcher ExpressionRegexMatcher,
value string,
) (bool, error) {
if len(m.Exact) == 0 && m.Expression == "" {
return false, fmt.Errorf("expression match must define at least one of exact or exp")
}
matched := containsExact(m.Exact, value)
if matched {
return m.applyNegate(true), nil
}
if m.Expression == "" {
return m.applyNegate(false), nil
}
if matcher == nil {
return m.Matches(value)
}
matched, err := matcher.MatchRegex(m.ExpressionRegex, value)
if err != nil {
return false, err
}
// Important: assume MatchRegex already applies ExpressionRegex.Negate.
// If your RegexCache.MatchRegex already handles Negate, return directly.
return matched, nil
}
func (m ExpressionMatch) Describe() string {
return DescribeExpressionMatch(m)
}
func (m ExpressionMatch) matches(value string) (bool, error) {
if len(m.Exact) == 0 && m.Expression == "" {
return false, fmt.Errorf("expression match must define at least one of exact or exp")
}
if containsExact(m.Exact, value) {
return true, nil
}
if m.Expression == "" {
return false, nil
}
re, err := regexp.Compile(m.Expression)
if err != nil {
return false, fmt.Errorf("compile regexp %q: %w", m.Expression, err)
}
return re.MatchString(value), nil
}
func containsExact(values []string, value string) bool {
return slices.Contains(values, value)
}
func (m ExpressionMatch) applyNegate(matched bool) bool {
if m.Negate {
return !matched
}
return matched
}
func DescribeExpressionMatch(match ExpressionMatch) string {
parts := make([]string, 0, 3)
prefix := ""
if match.Negate {
prefix = "not "
}
if len(match.Exact) > 0 {
parts = append(parts, fmt.Sprintf("%sexact: %s", prefix, strings.Join(match.Exact, ", ")))
}
if match.Expression != "" {
parts = append(parts, fmt.Sprintf("%sexp: %s", prefix, match.Expression))
}
if len(parts) == 0 && match.Negate {
return "not <empty>"
}
return strings.Join(parts, "; ")
}