2023-03-27 10:09:46 +02:00
package mutation
import (
"context"
2024-07-25 20:36:19 +03:00
"strings"
2023-03-27 10:09:46 +02:00
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
2024-06-24 23:36:55 +07:00
kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2"
2023-03-27 10:09:46 +02:00
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/handlers"
"github.com/kyverno/kyverno/pkg/engine/internal"
"github.com/kyverno/kyverno/pkg/engine/mutate"
2023-11-13 17:43:25 +02:00
engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
2023-05-08 00:34:23 -07:00
stringutils "github.com/kyverno/kyverno/pkg/utils/strings"
2023-03-27 10:09:46 +02:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2023-11-13 17:43:25 +02:00
"k8s.io/client-go/tools/cache"
2023-03-27 10:09:46 +02:00
)
2023-03-28 07:47:53 +02:00
type mutateExistingHandler struct {
2023-06-10 11:20:34 +02:00
client engineapi . Client
2023-03-27 10:09:46 +02:00
}
func NewMutateExistingHandler (
2023-06-10 11:20:34 +02:00
client engineapi . Client ,
2023-04-03 21:58:58 +02:00
) ( handlers . Handler , error ) {
2023-03-28 07:47:53 +02:00
return mutateExistingHandler {
2023-04-03 06:57:48 +02:00
client : client ,
2023-04-03 21:58:58 +02:00
} , nil
2023-03-27 10:09:46 +02:00
}
2023-03-28 07:47:53 +02:00
func ( h mutateExistingHandler ) Process (
2023-03-27 10:09:46 +02:00
ctx context . Context ,
logger logr . Logger ,
policyContext engineapi . PolicyContext ,
resource unstructured . Unstructured ,
rule kyvernov1 . Rule ,
2023-04-03 06:57:48 +02:00
contextLoader engineapi . EngineContextLoader ,
2024-06-24 23:36:55 +07:00
exceptions [ ] * kyvernov2 . PolicyException ,
2023-03-27 10:09:46 +02:00
) ( unstructured . Unstructured , [ ] engineapi . RuleResponse ) {
2024-07-25 20:36:19 +03:00
// check if there are policy exceptions that match the incoming resource
matchedExceptions := engineutils . MatchesException ( exceptions , policyContext , logger )
if len ( matchedExceptions ) > 0 {
var keys [ ] string
for i , exception := range matchedExceptions {
key , err := cache . MetaNamespaceKeyFunc ( & matchedExceptions [ i ] )
if err != nil {
logger . Error ( err , "failed to compute policy exception key" , "namespace" , exception . GetNamespace ( ) , "name" , exception . GetName ( ) )
return resource , handlers . WithError ( rule , engineapi . Mutation , "failed to compute exception key" , err )
}
keys = append ( keys , key )
2023-11-13 17:43:25 +02:00
}
2024-07-25 20:36:19 +03:00
logger . V ( 3 ) . Info ( "policy rule is skipped due to policy exceptions" , "exceptions" , keys )
return resource , handlers . WithResponses (
2024-09-03 23:06:07 +05:30
engineapi . RuleSkip ( rule . Name , engineapi . Mutation , "rule is skipped due to policy exceptions" + strings . Join ( keys , ", " ) , rule . ReportProperties ) . WithExceptions ( matchedExceptions ) ,
2024-07-25 20:36:19 +03:00
)
2023-11-13 17:43:25 +02:00
}
2023-03-27 10:09:46 +02:00
var responses [ ] engineapi . RuleResponse
logger . V ( 3 ) . Info ( "processing mutate rule" )
2023-06-11 13:57:21 +02:00
targets , err := loadTargets ( ctx , h . client , rule . Mutation . Targets , policyContext , logger )
2023-03-27 10:09:46 +02:00
if err != nil {
2024-09-03 23:06:07 +05:30
rr := engineapi . RuleError ( rule . Name , engineapi . Mutation , "" , err , rule . ReportProperties )
2023-03-27 10:09:46 +02:00
responses = append ( responses , * rr )
}
2023-04-03 21:58:58 +02:00
for _ , target := range targets {
if target . unstructured . Object == nil {
2023-03-27 10:09:46 +02:00
continue
}
2024-03-13 21:54:53 +05:30
policyContext := policyContext
2023-05-12 17:43:36 +02:00
if err := policyContext . JSONContext ( ) . SetTargetResource ( target . unstructured . Object ) ; err != nil {
2023-03-27 10:09:46 +02:00
logger . Error ( err , "failed to add target resource to the context" )
continue
}
2023-04-03 21:58:58 +02:00
// load target specific context
if err := contextLoader ( ctx , target . context , policyContext . JSONContext ( ) ) ; err != nil {
2024-09-03 23:06:07 +05:30
rr := engineapi . RuleError ( rule . Name , engineapi . Mutation , "failed to load context" , err , rule . ReportProperties )
2023-04-03 21:58:58 +02:00
responses = append ( responses , * rr )
continue
}
// load target specific preconditions
2023-05-08 00:34:23 -07:00
preconditionsPassed , msg , err := internal . CheckPreconditions ( logger , policyContext . JSONContext ( ) , target . preconditions )
2023-04-03 21:58:58 +02:00
if err != nil {
2024-09-03 23:06:07 +05:30
rr := engineapi . RuleError ( rule . Name , engineapi . Mutation , "failed to evaluate preconditions" , err , rule . ReportProperties )
2023-04-03 21:58:58 +02:00
responses = append ( responses , * rr )
continue
}
if ! preconditionsPassed {
2023-05-08 00:34:23 -07:00
s := stringutils . JoinNonEmpty ( [ ] string { "preconditions not met" , msg } , "; " )
2024-09-03 23:06:07 +05:30
rr := engineapi . RuleSkip ( rule . Name , engineapi . Mutation , s , rule . ReportProperties )
2023-04-03 21:58:58 +02:00
responses = append ( responses , * rr )
continue
}
2023-03-27 10:09:46 +02:00
// logger.V(4).Info("apply rule to resource", "resource namespace", patchedResource.unstructured.GetNamespace(), "resource name", patchedResource.unstructured.GetName())
var mutateResp * mutate . Response
if rule . Mutation . ForEachMutation != nil {
m := & forEachMutator {
2023-04-03 21:58:58 +02:00
rule : rule ,
2023-03-27 10:09:46 +02:00
foreach : rule . Mutation . ForEachMutation ,
policyContext : policyContext ,
2023-04-03 21:58:58 +02:00
resource : target . resourceInfo ,
logger : logger ,
2023-03-27 10:09:46 +02:00
contextLoader : contextLoader ,
nesting : 0 ,
}
mutateResp = m . mutateForEach ( ctx )
} else {
2023-04-03 21:58:58 +02:00
mutateResp = mutate . Mutate ( & rule , policyContext . JSONContext ( ) , target . unstructured , logger )
2023-03-27 10:09:46 +02:00
}
2023-04-03 21:58:58 +02:00
if ruleResponse := buildRuleResponse ( & rule , mutateResp , target . resourceInfo ) ; ruleResponse != nil {
2023-03-27 10:09:46 +02:00
responses = append ( responses , * ruleResponse )
}
}
return resource , responses
}