1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 16:06:56 +00:00
kyverno/pkg/admissionpolicy/mutate.go

135 lines
4.6 KiB
Go
Raw Normal View History

package admissionpolicy
import (
"context"
"strings"
"time"
celutils "github.com/kyverno/kyverno/pkg/cel/utils"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/managedfields"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/plugin/cel"
"k8s.io/apiserver/pkg/admission/plugin/policy/mutating/patch"
"k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions"
celconfig "k8s.io/apiserver/pkg/apis/cel"
)
func mutateResource(
policy admissionregistrationv1alpha1.MutatingAdmissionPolicy,
resource unstructured.Unstructured,
) (engineapi.EngineResponse, error) {
startTime := time.Now()
engineResponse := engineapi.NewEngineResponse(resource, engineapi.NewMutatingAdmissionPolicy(&policy), nil)
policyResp := engineapi.NewPolicyResponse()
gvk := resource.GroupVersionKind()
gvr := schema.GroupVersionResource{
Group: gvk.Group,
Version: gvk.Version,
Resource: strings.ToLower(gvk.Kind) + "s",
}
var namespace *corev1.Namespace
namespaceName := resource.GetNamespace()
// Special case, the namespace object has the namespace of itself.
// unset it if the incoming object is a namespace
if gvk.Kind == "Namespace" && gvk.Version == "v1" && gvk.Group == "" {
namespaceName = ""
}
if namespaceName != "" {
namespace = &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespaceName,
},
}
}
a := admission.NewAttributesRecord(resource.DeepCopyObject(), nil, resource.GroupVersionKind(), resource.GetNamespace(), resource.GetName(), gvr, "", admission.Create, nil, false, nil)
versionedAttributes, _ := admission.NewVersionedAttributes(a, a.GetKind(), nil)
o := admission.NewObjectInterfacesFromScheme(runtime.NewScheme())
matchConditions := make([]admissionregistrationv1.MatchCondition, 0, len(policy.Spec.MatchConditions))
for _, m := range policy.Spec.MatchConditions {
matchConditions = append(matchConditions, admissionregistrationv1.MatchCondition(m))
}
variables := make([]admissionregistrationv1.Variable, 0, len(policy.Spec.Variables))
for _, v := range policy.Spec.Variables {
variables = append(variables, admissionregistrationv1.Variable(v))
}
// create compiler
compiler, err := NewCompiler(matchConditions, variables)
if err != nil {
return engineResponse, err
}
compiler.WithMutations(policy.Spec.Mutations)
optionalVars := cel.OptionalVariableDeclarations{
HasParams: false,
HasAuthorizer: false,
HasPatchTypes: true,
StrictCost: true,
}
// compile variables
compiler.CompileVariables(optionalVars)
var failPolicy admissionregistrationv1.FailurePolicyType
if policy.Spec.FailurePolicy == nil {
failPolicy = admissionregistrationv1.Fail
} else {
failPolicy = admissionregistrationv1.FailurePolicyType(*policy.Spec.FailurePolicy)
}
// compile matchers
matcher := matchconditions.NewMatcher(compiler.CompileMatchConditions(optionalVars), &failPolicy, "policy", "mutate", policy.Name)
if matcher != nil {
matchResults := matcher.Match(context.TODO(), versionedAttributes, nil, nil)
// if preconditions are not met, then skip mutations
if !matchResults.Matches {
return engineResponse, nil
}
}
// compile mutations
patchers := compiler.CompileMutations(optionalVars)
// apply mutations
for _, patcher := range patchers {
patchRequest := patch.Request{
MatchedResource: gvr,
VersionedAttributes: versionedAttributes,
ObjectInterfaces: o,
OptionalVariables: cel.OptionalVariableBindings{VersionedParams: nil, Authorizer: nil},
Namespace: namespace,
TypeConverter: managedfields.NewDeducedTypeConverter(),
}
newVersionedObject, err := patcher.Patch(context.TODO(), patchRequest, celconfig.RuntimeCELCostBudget)
if err != nil {
return engineResponse, nil
}
versionedAttributes.Dirty = true
versionedAttributes.VersionedObject = newVersionedObject
}
patchedResource, err := celutils.ConvertObjectToUnstructured(versionedAttributes.VersionedObject)
if err != nil {
return engineResponse, err
}
ruleResp := engineapi.RulePass(policy.GetName(), engineapi.Mutation, "", nil)
policyResp.Add(engineapi.NewExecutionStats(startTime, time.Now()), *ruleResp)
engineResponse = engineResponse.
WithPatchedResource(*patchedResource).
WithPolicyResponse(policyResp)
return engineResponse, nil
}