Skip to content

Commit

Permalink
feat: add trace steps (#22)
Browse files Browse the repository at this point in the history
Signed-off-by: Yusan Kurban <[email protected]>
  • Loading branch information
yusank authored Mar 13, 2023
1 parent aa5597a commit 1a6d015
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 16 deletions.
26 changes: 25 additions & 1 deletion utils/context.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package utils

import "context"
import (
"context"

utiltrace "k8s.io/utils/trace"
)

// ContextForChannel derives a child context from a parent channel.
//
Expand All @@ -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
}
52 changes: 52 additions & 0 deletions utils/interrupter/policy_interrupter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
`,
},
},
Expand Down
42 changes: 33 additions & 9 deletions utils/overridemanager/overridemanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package overridemanager

import (
"bytes"
"context"
"encoding/json"
"fmt"
"sort"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -69,22 +70,22 @@ 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
}

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
Expand All @@ -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
Expand All @@ -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
}
Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)
}
3 changes: 2 additions & 1 deletion utils/overridemanager/overridemanager_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package overridemanager

import (
"context"
"flag"
"reflect"
"testing"
Expand Down Expand Up @@ -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)
}
Expand Down
25 changes: 21 additions & 4 deletions utils/validatemanager/validatemanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package validatemanager

import (
"bytes"
"context"
"encoding/json"
"fmt"

Expand All @@ -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 {
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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.",
Expand Down Expand Up @@ -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)
}
3 changes: 2 additions & 1 deletion utils/validatemanager/validatemanager_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package validatemanager

import (
"context"
"flag"
"reflect"
"testing"
Expand Down Expand Up @@ -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)
}
Expand Down

0 comments on commit 1a6d015

Please sign in to comment.