mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-30 23:15:14 +00:00
errors improvement.
This commit is contained in:
parent
1904a56e30
commit
b846c39047
@ -163,11 +163,12 @@ type variableDeclEnvs map[OptionalVariableDeclarations]*environment.EnvSet
|
||||
// CompileCELExpression returns a compiled CEL expression.
|
||||
// perCallLimit was added for testing purpose only. Callers should always use const PerCallLimit from k8s.io/apiserver/pkg/apis/cel/config.go as input.
|
||||
func (c compiler) CompileCELExpression(expressionAccessor ExpressionAccessor, options OptionalVariableDeclarations, envType environment.Type) CompilationResult {
|
||||
resultError := func(errorString string, errType apiservercel.ErrorType) CompilationResult {
|
||||
resultError := func(errorString string, errType apiservercel.ErrorType, errors ...error) CompilationResult {
|
||||
return CompilationResult{
|
||||
Error: &apiservercel.Error{
|
||||
Type: errType,
|
||||
Detail: errorString,
|
||||
Errors: errors,
|
||||
},
|
||||
ExpressionAccessor: expressionAccessor,
|
||||
}
|
||||
@ -180,7 +181,7 @@ func (c compiler) CompileCELExpression(expressionAccessor ExpressionAccessor, op
|
||||
|
||||
ast, issues := env.Compile(expressionAccessor.GetExpression())
|
||||
if issues != nil {
|
||||
return resultError("compilation failed: "+issues.String(), apiservercel.ErrorTypeInvalid)
|
||||
return resultError("compilation failed: "+issues.String(), apiservercel.ErrorTypeInvalid, apiservercel.NewCompilationError(issues))
|
||||
}
|
||||
found := false
|
||||
returnTypes := expressionAccessor.ReturnTypes()
|
||||
|
@ -192,6 +192,7 @@ func (f *filter) ForInput(ctx context.Context, versionedAttr *admission.Versione
|
||||
evaluation.Error = &cel.Error{
|
||||
Type: cel.ErrorTypeInvalid,
|
||||
Detail: fmt.Sprintf("compilation error: %v", compilationResult.Error),
|
||||
Errors: []error{compilationResult.Error},
|
||||
}
|
||||
continue
|
||||
}
|
||||
@ -211,6 +212,7 @@ func (f *filter) ForInput(ctx context.Context, versionedAttr *admission.Versione
|
||||
return nil, -1, &cel.Error{
|
||||
Type: cel.ErrorTypeInvalid,
|
||||
Detail: fmt.Sprintf("validation failed due to running out of cost budget, no further validation rules will be run"),
|
||||
Errors: []error{cel.ErrOutOfBudget},
|
||||
}
|
||||
}
|
||||
remainingBudget -= compositionCost
|
||||
@ -228,12 +230,14 @@ func (f *filter) ForInput(ctx context.Context, versionedAttr *admission.Versione
|
||||
return nil, -1, &cel.Error{
|
||||
Type: cel.ErrorTypeInvalid,
|
||||
Detail: fmt.Sprintf("runtime cost could not be calculated for expression: %v, no further expression will be run", compilationResult.ExpressionAccessor.GetExpression()),
|
||||
Errors: []error{cel.ErrOutOfBudget},
|
||||
}
|
||||
} else {
|
||||
if *rtCost > math.MaxInt64 || int64(*rtCost) > remainingBudget {
|
||||
return nil, -1, &cel.Error{
|
||||
Type: cel.ErrorTypeInvalid,
|
||||
Detail: fmt.Sprintf("validation failed due to running out of cost budget, no further validation rules will be run"),
|
||||
Errors: []error{cel.ErrOutOfBudget},
|
||||
}
|
||||
}
|
||||
remainingBudget -= int64(*rtCost)
|
||||
|
@ -18,6 +18,7 @@ package validating
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@ -132,19 +133,14 @@ func (v *validator) Validate(ctx context.Context, matchedResource schema.GroupVe
|
||||
}
|
||||
|
||||
var messageResult *cel.EvaluationResult
|
||||
var messageError *apiservercel.Error
|
||||
if len(messageResults) > i {
|
||||
messageResult = &messageResults[i]
|
||||
}
|
||||
messageError, _ = err.(*apiservercel.Error)
|
||||
if evalResult.Error != nil {
|
||||
decision.Action = policyDecisionActionForError(f)
|
||||
decision.Evaluation = EvalError
|
||||
decision.Message = evalResult.Error.Error()
|
||||
} else if messageError != nil &&
|
||||
(messageError.Type == apiservercel.ErrorTypeInternal ||
|
||||
(messageError.Type == apiservercel.ErrorTypeInvalid &&
|
||||
strings.HasPrefix(messageError.Detail, "validation failed due to running out of cost budget"))) {
|
||||
} else if errors.Is(err, apiservercel.ErrInternal) || errors.Is(err, apiservercel.ErrOutOfBudget) {
|
||||
decision.Action = policyDecisionActionForError(f)
|
||||
decision.Evaluation = EvalError
|
||||
decision.Message = fmt.Sprintf("failed messageExpression: %s", err)
|
||||
|
@ -53,6 +53,7 @@ func (f *fakeCelFilter) ForInput(ctx context.Context, versionedAttr *admission.V
|
||||
return nil, -1, &apiservercel.Error{
|
||||
Type: apiservercel.ErrorTypeInvalid,
|
||||
Detail: fmt.Sprintf("validation failed due to running out of cost budget, no further validation rules will be run"),
|
||||
Errors: []error{apiservercel.ErrOutOfBudget},
|
||||
}
|
||||
}
|
||||
if f.throwError {
|
||||
|
@ -16,11 +16,46 @@ limitations under the License.
|
||||
|
||||
package cel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/cel-go/cel"
|
||||
)
|
||||
|
||||
// ErrInternal the basic error that occurs when the expression fails to evaluate
|
||||
// due to internal reasons. Any Error that has the Type of
|
||||
// ErrorInternal is considered equal to ErrInternal
|
||||
var ErrInternal = fmt.Errorf("internal")
|
||||
|
||||
// ErrInvalid is the basic error that occurs when the expression fails to
|
||||
// evaluate but not due to internal reasons. Any Error that has the Type of
|
||||
// ErrorInvalid is considered equal to ErrInvalid.
|
||||
var ErrInvalid = fmt.Errorf("invalid")
|
||||
|
||||
// ErrRequired is the basic error that occurs when the expression is required
|
||||
// but absent.
|
||||
// Any Error that has the Type of ErrorRequired is considered equal
|
||||
// to ErrRequired.
|
||||
var ErrRequired = fmt.Errorf("required")
|
||||
|
||||
// ErrCompilation is the basic error that occurs when the expression fails to
|
||||
// compile. Any CompilationError wraps ErrCompilation.
|
||||
// ErrCompilation wraps ErrInvalid
|
||||
var ErrCompilation = fmt.Errorf("%w: compilation error", ErrInvalid)
|
||||
|
||||
// ErrOutOfBudget is the basic error that occurs when the expression fails due to
|
||||
// exceeding budget.
|
||||
var ErrOutOfBudget = fmt.Errorf("out of budget")
|
||||
|
||||
// Error is an implementation of the 'error' interface, which represents a
|
||||
// XValidation error.
|
||||
type Error struct {
|
||||
Type ErrorType
|
||||
Detail string
|
||||
|
||||
// Errors are optional wrapped errors that can be useful to
|
||||
// programmatically retrieve detailed errors.
|
||||
Errors []error
|
||||
}
|
||||
|
||||
var _ error = &Error{}
|
||||
@ -30,7 +65,24 @@ func (v *Error) Error() string {
|
||||
return v.Detail
|
||||
}
|
||||
|
||||
// ErrorType is a machine readable value providing more detail about why
|
||||
func (v *Error) Is(err error) bool {
|
||||
switch v.Type {
|
||||
case ErrorTypeRequired:
|
||||
return err == ErrRequired
|
||||
case ErrorTypeInvalid:
|
||||
return err == ErrInvalid
|
||||
case ErrorTypeInternal:
|
||||
return err == ErrInternal
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Unwrap returns wrapped errors.
|
||||
func (v *Error) Unwrap() []error {
|
||||
return v.Errors
|
||||
}
|
||||
|
||||
// ErrorType is a machine-readable value providing more detail about why
|
||||
// a XValidation is invalid.
|
||||
type ErrorType string
|
||||
|
||||
@ -45,3 +97,28 @@ const (
|
||||
// to user input. See InternalError().
|
||||
ErrorTypeInternal ErrorType = "InternalError"
|
||||
)
|
||||
|
||||
// CompilationError indicates an error during expression compilation.
|
||||
// It wraps ErrCompilation.
|
||||
type CompilationError struct {
|
||||
err *Error
|
||||
Issues *cel.Issues
|
||||
}
|
||||
|
||||
// NewCompilationError wraps a cel.Issues to indicate a compilation failure.
|
||||
func NewCompilationError(issues *cel.Issues) *CompilationError {
|
||||
return &CompilationError{
|
||||
Issues: issues,
|
||||
err: &Error{
|
||||
Type: ErrorTypeInvalid,
|
||||
Detail: fmt.Sprintf("compilation error: %s", issues),
|
||||
}}
|
||||
}
|
||||
|
||||
func (e *CompilationError) Error() string {
|
||||
return e.err.Error()
|
||||
}
|
||||
|
||||
func (e *CompilationError) Unwrap() []error {
|
||||
return []error{e.err, ErrCompilation}
|
||||
}
|
||||
|
63
staging/src/k8s.io/apiserver/pkg/cel/errors_test.go
Normal file
63
staging/src/k8s.io/apiserver/pkg/cel/errors_test.go
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright 2024 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 cel
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/google/cel-go/cel"
|
||||
)
|
||||
|
||||
func TestOutOfBudgetError(t *testing.T) {
|
||||
err := &Error{
|
||||
Type: ErrorTypeInvalid,
|
||||
Detail: "expression out of budget",
|
||||
Errors: []error{ErrOutOfBudget},
|
||||
}
|
||||
if !errors.Is(err, ErrOutOfBudget) {
|
||||
t.Errorf("unexpected %v is not %v", err, ErrOutOfBudget)
|
||||
}
|
||||
if !errors.Is(err, ErrInvalid) {
|
||||
t.Errorf("unexpected %v is not %v", err, ErrInvalid)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompilationError(t *testing.T) {
|
||||
if !errors.Is(ErrCompilation, ErrInvalid) {
|
||||
t.Errorf("unexpected %v is not %v", ErrCompilation, ErrInvalid)
|
||||
}
|
||||
issues := &cel.Issues{}
|
||||
err := &Error{
|
||||
Type: ErrorTypeInvalid,
|
||||
Detail: "fake compilation failed",
|
||||
Errors: []error{NewCompilationError(issues)},
|
||||
}
|
||||
if !errors.Is(err, ErrCompilation) {
|
||||
t.Errorf("unexpected %v is not %v", err, ErrCompilation)
|
||||
}
|
||||
if !errors.Is(err, ErrInvalid) {
|
||||
t.Errorf("unexpected %v is not %v", err, ErrInvalid)
|
||||
}
|
||||
var compilationErr *CompilationError
|
||||
if errors.As(err, &compilationErr); compilationErr == nil {
|
||||
t.Errorf("unexpected %v cannot be fitted into CompilationError", err)
|
||||
}
|
||||
if compilationErr.Issues != issues {
|
||||
t.Errorf("retrieved issues is not the original")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user