diff --git a/utils/context.go b/utils/context.go index ae49475..3c911c6 100644 --- a/utils/context.go +++ b/utils/context.go @@ -1,6 +1,10 @@ package utils -import "context" +import ( + "context" + + utiltrace "k8s.io/utils/trace" +) // ContextForChannel derives a child context from a parent channel. // @@ -20,3 +24,23 @@ func ContextForChannel(parentCh <-chan struct{}) (context.Context, context.Cance }() return ctx, cancel } + +type traceCtxKey struct{} + +// ContextWithTrace return a new context with trace as value. +func ContextWithTrace(ctx context.Context, trace *utiltrace.Trace) context.Context { + if ctx == nil { + ctx = context.Background() + } + + return context.WithValue(ctx, traceCtxKey{}, trace) +} + +func TraceFromContext(ctx context.Context) *utiltrace.Trace { + v, ok := ctx.Value(traceCtxKey{}).(*utiltrace.Trace) + if ok { + return v + } + + return nil +} diff --git a/utils/interrupter/policy_interrupter_test.go b/utils/interrupter/policy_interrupter_test.go index 862b355..8042d21 100644 --- a/utils/interrupter/policy_interrupter_test.go +++ b/utils/interrupter/policy_interrupter_test.go @@ -1007,6 +1007,58 @@ validate: { } } } +`, + }, + }, + wantErr: false, + }, + { + name: "3.0", + args: args{ + operation: admissionv1.Create, + obj: &unstructured.Unstructured{Object: map[string]any{ + "apiVersion": "policy.kcloudlabs.io/v1alpha1", + "kind": "ClusterValidatePolicy", + "spec": map[string]any{ + "validateRules": []map[string]any{ + { + "template": map[string]any{ + "type": "condition", + "condition": map[string]any{ + "message": "forbidden", + "cond": "NotIn", + "affectMode": "reject", + "dataRef": map[string]any{ + "from": "current", + "path": "/spec/containers/0/image", + }, + "value": map[string]any{ + "stringSlice": []string{"fake-image", "fake-image-v2"}, + }, + }, + }, + }, + }, + }, + }}, + }, + want: []jsonpatchv2.JsonPatchOperation{ + { + Operation: "replace", + Path: "/spec/validateRules/0/renderedCue", + Value: `import "list" + +data: _ @tag(data) +object: data.object +oldObject: data.oldObject +validate: { + if object.spec.containers[0].image != _|_ { + if !list.Contains(["fake-image", "fake-image-v2"], object.spec.containers[0].image) { + valid: false + reason: "forbidden" + } + } +} `, }, }, diff --git a/utils/overridemanager/overridemanager.go b/utils/overridemanager/overridemanager.go index c52dc13..a9606c6 100644 --- a/utils/overridemanager/overridemanager.go +++ b/utils/overridemanager/overridemanager.go @@ -2,6 +2,7 @@ package overridemanager import ( "bytes" + "context" "encoding/json" "fmt" "sort" @@ -29,7 +30,7 @@ type OverrideManager interface { // For namespaced scoped resource, apply order is: // - First apply ClusterOverridePolicy; // - Then apply OverridePolicy; - ApplyOverridePolicies(rawObj, oldObj *unstructured.Unstructured, operation admissionv1.Operation) (appliedCOPs *AppliedOverrides, appliedOPs *AppliedOverrides, err error) + ApplyOverridePolicies(ctx context.Context, rawObj, oldObj *unstructured.Unstructured, operation admissionv1.Operation) (appliedCOPs *AppliedOverrides, appliedOPs *AppliedOverrides, err error) } // GeneralOverridePolicy is an abstract object of ClusterOverridePolicy and OverridePolicy @@ -69,14 +70,14 @@ func NewOverrideManager(dynamicClient dynamiclister.DynamicResourceLister, copLi } } -func (o *overrideManagerImpl) ApplyOverridePolicies(rawObj, oldObj *unstructured.Unstructured, operation admissionv1.Operation) (*AppliedOverrides, *AppliedOverrides, error) { +func (o *overrideManagerImpl) ApplyOverridePolicies(ctx context.Context, rawObj, oldObj *unstructured.Unstructured, operation admissionv1.Operation) (*AppliedOverrides, *AppliedOverrides, error) { var ( appliedCOPs *AppliedOverrides appliedOPs *AppliedOverrides err error ) - appliedCOPs, err = o.applyClusterOverridePolicies(rawObj, oldObj, operation) + appliedCOPs, err = o.applyClusterOverridePolicies(ctx, rawObj, oldObj, operation) if err != nil { klog.ErrorS(err, "Failed to apply cluster override policies.") return nil, nil, err @@ -84,7 +85,7 @@ func (o *overrideManagerImpl) ApplyOverridePolicies(rawObj, oldObj *unstructured if rawObj.GetNamespace() != "" { // Apply namespace scoped override policies - appliedOPs, err = o.applyOverridePolicies(rawObj, oldObj, operation) + appliedOPs, err = o.applyOverridePolicies(ctx, rawObj, oldObj, operation) if err != nil { klog.ErrorS(err, "Failed to apply override policies.") return nil, nil, err @@ -94,8 +95,11 @@ func (o *overrideManagerImpl) ApplyOverridePolicies(rawObj, oldObj *unstructured return appliedCOPs, appliedOPs, nil } -func (o *overrideManagerImpl) applyClusterOverridePolicies(rawObj, oldObj *unstructured.Unstructured, operation admissionv1.Operation) (*AppliedOverrides, error) { +func (o *overrideManagerImpl) applyClusterOverridePolicies(ctx context.Context, rawObj, oldObj *unstructured.Unstructured, operation admissionv1.Operation) (*AppliedOverrides, error) { + defer traceStep(ctx, "applyClusterOverridePolicies finished") + traceStep(ctx, "About to list cop") cops, err := o.copLister.List(labels.Everything()) + traceStep(ctx, "List cop done") if err != nil { klog.ErrorS(err, "Failed to list cluster override policies.", "resource", klog.KObj(rawObj), "operation", operation) return nil, err @@ -120,7 +124,7 @@ func (o *overrideManagerImpl) applyClusterOverridePolicies(rawObj, oldObj *unstr appliedOverrides := &AppliedOverrides{} for _, p := range matchingPolicyOverriders { metrics.OverridePolicyMatched(p.name, rawObj.GroupVersionKind()) - if err := o.applyPolicyOverriders(rawObj, oldObj, p); err != nil { + if err := o.applyPolicyOverriders(ctx, rawObj, oldObj, p); err != nil { klog.ErrorS(err, "Failed to apply cluster overriders.", "clusteroverridepolicy", p.name, "resource", klog.KObj(rawObj), "operation", operation) return nil, err } @@ -131,8 +135,11 @@ func (o *overrideManagerImpl) applyClusterOverridePolicies(rawObj, oldObj *unstr return appliedOverrides, nil } -func (o *overrideManagerImpl) applyOverridePolicies(rawObj, oldObj *unstructured.Unstructured, operation admissionv1.Operation) (*AppliedOverrides, error) { +func (o *overrideManagerImpl) applyOverridePolicies(ctx context.Context, rawObj, oldObj *unstructured.Unstructured, operation admissionv1.Operation) (*AppliedOverrides, error) { + defer traceStep(ctx, "applyOverridePolicies finished") + traceStep(ctx, "About to list op") ops, err := o.opLister.List(labels.Everything()) + traceStep(ctx, "List op done") if err != nil { klog.ErrorS(err, "Failed to list override policies.", "namespace", rawObj.GetNamespace(), "resource", klog.KObj(rawObj), "operation", operation) return nil, err @@ -157,7 +164,7 @@ func (o *overrideManagerImpl) applyOverridePolicies(rawObj, oldObj *unstructured appliedOverriders := &AppliedOverrides{} for _, p := range matchingPolicyOverriders { metrics.OverridePolicyMatched(p.namespace+"/"+p.name, rawObj.GroupVersionKind()) - if err := o.applyPolicyOverriders(rawObj, oldObj, p); err != nil { + if err := o.applyPolicyOverriders(ctx, rawObj, oldObj, p); err != nil { klog.ErrorS(err, "Failed to apply overriders.", "overridepolicy", fmt.Sprintf("%s/%s", p.namespace, p.name), "resource", klog.KObj(rawObj), "operation", operation) return nil, fmt.Errorf("appling policy(%v/%v) err=%v", p.namespace, p.name, err) @@ -205,13 +212,17 @@ func (o *overrideManagerImpl) getOverridersFromOverridePolicies(policies []Gener } // applyPolicyOverriders applies OverridePolicy/ClusterOverridePolicy overriders to target object -func (o *overrideManagerImpl) applyPolicyOverriders(rawObj, oldObj *unstructured.Unstructured, p policyOverriders) error { +func (o *overrideManagerImpl) applyPolicyOverriders(ctx context.Context, rawObj, oldObj *unstructured.Unstructured, p policyOverriders) error { + defer traceStep(ctx, "applyPolicyOverriders finished") + traceStep(ctx, "Start applyPolicyOverriders") policyName := p.name if p.namespace != "" { policyName = p.namespace + "/" + p.name } if p.overriders.Template != nil && p.overriders.RenderedCue != "" { + traceStep(ctx, "About to BuildCueParamsViaOverridePolicy") cp, err := cue.BuildCueParamsViaOverridePolicy(o.dynamicLister, rawObj, p.overriders.Template) + traceStep(ctx, "BuildCueParamsViaOverridePolicy done") if err != nil { metrics.PolicyGotError(policyName, rawObj.GroupVersionKind(), metrics.ErrTypePrepareCueParams) return fmt.Errorf("BuildCueParamsViaOverridePolicy error=%w", err) @@ -228,7 +239,9 @@ func (o *overrideManagerImpl) applyPolicyOverriders(rawObj, oldObj *unstructured }, } + traceStep(ctx, "About to execute template cue") patches, err := executeCueV2(p.overriders.RenderedCue, params) + traceStep(ctx, "execute template cue done") if err != nil { metrics.PolicyGotError(policyName, rawObj.GroupVersionKind(), metrics.ErrorTypeCueExecute) return err @@ -243,7 +256,9 @@ func (o *overrideManagerImpl) applyPolicyOverriders(rawObj, oldObj *unstructured } } if p.overriders.Cue != "" { + traceStep(ctx, "About to execute custom cue") patches, err := executeCue(rawObj, p.overriders.Cue) + traceStep(ctx, "execute custom cue done") if err != nil { metrics.PolicyGotError(policyName, rawObj.GroupVersionKind(), metrics.ErrorTypeCueExecute) return err @@ -354,3 +369,12 @@ func executeCue(rawObj *unstructured.Unstructured, template string) (*[]override return &result, nil } + +func traceStep(ctx context.Context, msg string) { + trace := utils.TraceFromContext(ctx) + if trace == nil { + return + } + + trace.Step(msg) +} diff --git a/utils/overridemanager/overridemanager_test.go b/utils/overridemanager/overridemanager_test.go index a18f809..797e0f9 100644 --- a/utils/overridemanager/overridemanager_test.go +++ b/utils/overridemanager/overridemanager_test.go @@ -1,6 +1,7 @@ package overridemanager import ( + "context" "flag" "reflect" "testing" @@ -392,7 +393,7 @@ patches: [ } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if cops, ops, err := m.ApplyOverridePolicies(tt.resource, tt.oldResource, tt.operation); !reflect.DeepEqual(cops, tt.wantedCOPs) || !reflect.DeepEqual(ops, tt.wantedOPs) || + if cops, ops, err := m.ApplyOverridePolicies(context.Background(), tt.resource, tt.oldResource, tt.operation); !reflect.DeepEqual(cops, tt.wantedCOPs) || !reflect.DeepEqual(ops, tt.wantedOPs) || !reflect.DeepEqual(tt.resource.GetAnnotations(), tt.wantedAnnotations) || !reflect.DeepEqual(err, tt.wantedErr) { t.Errorf("ApplyOverridePolicies(), cops= %v\n ops=%v\n, err=%v\n, want cops= %v\n ops=%v\n, err=%v", cops, ops, err, tt.wantedCOPs, tt.wantedOPs, tt.wantedErr) } diff --git a/utils/validatemanager/validatemanager.go b/utils/validatemanager/validatemanager.go index 38fef70..f7d2c38 100644 --- a/utils/validatemanager/validatemanager.go +++ b/utils/validatemanager/validatemanager.go @@ -2,6 +2,7 @@ package validatemanager import ( "bytes" + "context" "encoding/json" "fmt" @@ -23,7 +24,7 @@ import ( // ValidateManager managers validate policies for operation type ValidateManager interface { // ApplyValidatePolicies validate the object if one or more matched validate policy exist. - ApplyValidatePolicies(obj *unstructured.Unstructured, oldObj *unstructured.Unstructured, operation admissionv1.Operation) (*ValidateResult, error) + ApplyValidatePolicies(ctx context.Context, obj *unstructured.Unstructured, oldObj *unstructured.Unstructured, operation admissionv1.Operation) (*ValidateResult, error) } type validateManagerImpl struct { @@ -43,8 +44,11 @@ func NewValidateManager(dynamicClient dynamiclister.DynamicResourceLister, cvpLi } } -func (m *validateManagerImpl) ApplyValidatePolicies(rawObj *unstructured.Unstructured, oldObj *unstructured.Unstructured, operation admissionv1.Operation) (*ValidateResult, error) { +func (m *validateManagerImpl) ApplyValidatePolicies(ctx context.Context, rawObj *unstructured.Unstructured, oldObj *unstructured.Unstructured, operation admissionv1.Operation) (*ValidateResult, error) { + defer traceStep(ctx, "ApplyValidatePolicies finished") + traceStep(ctx, "About to list cvp") cvps, err := m.cvpLister.List(labels.Everything()) + traceStep(ctx, "List cvp done") if err != nil { klog.ErrorS(err, "Failed to list validate policies.", "resource", klog.KObj(rawObj), "operation", operation) return nil, err @@ -58,7 +62,7 @@ func (m *validateManagerImpl) ApplyValidatePolicies(rawObj *unstructured.Unstruc } for _, cvp := range cvps { - result, err := m.applyValidatePolicy(cvp, rawObj, oldObj, operation) + result, err := m.applyValidatePolicy(ctx, cvp, rawObj, oldObj, operation) if err != nil { klog.ErrorS(err, "Failed to applyValidatePolicy.", "validatepolicy", cvp.Name, "resource", klog.KObj(rawObj), "operation", operation) @@ -75,7 +79,7 @@ func (m *validateManagerImpl) ApplyValidatePolicies(rawObj *unstructured.Unstruc }, nil } -func (m *validateManagerImpl) applyValidatePolicy(cvp *policyv1alpha1.ClusterValidatePolicy, rawObj, oldObj *unstructured.Unstructured, +func (m *validateManagerImpl) applyValidatePolicy(ctx context.Context, cvp *policyv1alpha1.ClusterValidatePolicy, rawObj, oldObj *unstructured.Unstructured, operation admissionv1.Operation) (*ValidateResult, error) { if len(cvp.Spec.ResourceSelectors) > 0 && !utils.ResourceMatchSelectors(rawObj, cvp.Spec.ResourceSelectors...) { //no matched @@ -100,7 +104,9 @@ func (m *validateManagerImpl) applyValidatePolicy(cvp *policyv1alpha1.ClusterVal OldObject: oldObj, } + traceStep(ctx, "Before execute template cue") result, err := m.executeTemplate(params, &rule, cvp.Name) + traceStep(ctx, "After execute template cue") if err != nil { klog.ErrorS(err, "Failed to execute rendered cue.", "validatepolicy", cvp.Name, "resource", klog.KObj(rawObj), "operation", operation) @@ -118,7 +124,9 @@ func (m *validateManagerImpl) applyValidatePolicy(cvp *policyv1alpha1.ClusterVal } if rule.Cue != "" { + traceStep(ctx, "Before execute normal cue") result, err := executeCue(rawObj, oldObj, rule.Cue) + traceStep(ctx, "After execute normal cue") if err != nil { metrics.PolicyGotError(rawObj.GetName(), rawObj.GroupVersionKind(), metrics.ErrorTypeCueExecute) klog.ErrorS(err, "Failed to apply validate policy.", @@ -239,3 +247,12 @@ func getPodPhase(obj *unstructured.Unstructured) corev1.PodPhase { return corev1.PodPhase(val) } + +func traceStep(ctx context.Context, msg string) { + trace := utils.TraceFromContext(ctx) + if trace == nil { + return + } + + trace.Step(msg) +} diff --git a/utils/validatemanager/validatemanager_test.go b/utils/validatemanager/validatemanager_test.go index f4aea34..941a620 100644 --- a/utils/validatemanager/validatemanager_test.go +++ b/utils/validatemanager/validatemanager_test.go @@ -1,6 +1,7 @@ package validatemanager import ( + "context" "flag" "reflect" "testing" @@ -148,7 +149,7 @@ validate: { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := m.ApplyValidatePolicies(tt.object, tt.oldObject, tt.operation) + result, err := m.ApplyValidatePolicies(context.Background(), tt.object, tt.oldObject, tt.operation) if !reflect.DeepEqual(result, tt.wantedResult) || !reflect.DeepEqual(err, tt.wantedErr) { t.Errorf("ApplyValidatePolicies() = %v, %v want %v, %v", result, err, tt.wantedResult, tt.wantedErr) }