diff --git a/pkg/model/context.go b/pkg/model/context.go index 09997284f..087d96e37 100644 --- a/pkg/model/context.go +++ b/pkg/model/context.go @@ -3,6 +3,7 @@ package model import ( "context" "path/filepath" + "time" "github.com/jmespath-community/go-jmespath/pkg/binding" "github.com/kyverno/chainsaw/pkg/apis/v1alpha1" @@ -12,8 +13,18 @@ import ( "k8s.io/client-go/rest" ) +type Timeouts struct { + Apply time.Duration + Assert time.Duration + Cleanup time.Duration + Delete time.Duration + Error time.Duration + Exec time.Duration +} + type TestContext struct { Summary + timeouts Timeouts bindings binding.Bindings clusters clusters.Registry cluster string @@ -44,18 +55,32 @@ func (tc *TestContext) Cluster() (*rest.Config, client.Client, error) { return tc.clusters.Resolve(false, tc.cluster) } +func (tc *TestContext) Timeouts() Timeouts { + return tc.timeouts +} + +func (tc TestContext) WithBinding(ctx context.Context, name string, value any) TestContext { + tc.bindings = apibindings.RegisterNamedBinding(ctx, tc.bindings, name, value) + return tc +} + func (tc TestContext) WithCluster(ctx context.Context, name string, cluster clusters.Cluster) TestContext { tc.clusters = tc.clusters.Register(name, cluster) return tc } -func (tc TestContext) WithBinding(ctx context.Context, name string, value any) TestContext { - tc.bindings = apibindings.RegisterNamedBinding(ctx, tc.bindings, name, value) +func (tc TestContext) WithTimeouts(ctx context.Context, timeouts Timeouts) TestContext { + tc.timeouts = timeouts return tc } -func WithValues(ctx context.Context, tc TestContext, values any) TestContext { - return tc.WithBinding(ctx, "values", values) +func WithBindings(ctx context.Context, tc TestContext, variables ...v1alpha1.Binding) (TestContext, error) { + bindings, err := apibindings.RegisterBindings(ctx, tc.Bindings(), variables...) + if err != nil { + return tc, err + } + tc.bindings = bindings + return tc, nil } func WithClusters(ctx context.Context, tc TestContext, basePath string, c map[string]v1alpha1.Cluster) TestContext { @@ -67,6 +92,44 @@ func WithClusters(ctx context.Context, tc TestContext, basePath string, c map[st return tc } +func WithDefaultTimeouts(ctx context.Context, tc TestContext, timeouts v1alpha1.DefaultTimeouts) TestContext { + return tc.WithTimeouts(ctx, Timeouts{ + Apply: timeouts.Apply.Duration, + Assert: timeouts.Assert.Duration, + Cleanup: timeouts.Cleanup.Duration, + Delete: timeouts.Delete.Duration, + Error: timeouts.Error.Duration, + Exec: timeouts.Exec.Duration, + }) +} + +func WithTimeouts(ctx context.Context, tc TestContext, timeouts v1alpha1.Timeouts) TestContext { + old := tc.timeouts + if new := timeouts.Apply; new != nil { + old.Apply = new.Duration + } + if new := timeouts.Assert; new != nil { + old.Assert = new.Duration + } + if new := timeouts.Cleanup; new != nil { + old.Cleanup = new.Duration + } + if new := timeouts.Delete; new != nil { + old.Delete = new.Duration + } + if new := timeouts.Error; new != nil { + old.Error = new.Duration + } + if new := timeouts.Exec; new != nil { + old.Exec = new.Duration + } + return tc.WithTimeouts(ctx, old) +} + +func WithValues(ctx context.Context, tc TestContext, values any) TestContext { + return tc.WithBinding(ctx, "values", values) +} + func UseCluster(ctx context.Context, tc TestContext, name string) (TestContext, error) { clusterConfig, clusterClient, err := tc.clusters.Resolve(false) if err != nil { diff --git a/pkg/model/evaluate.go b/pkg/model/evaluate.go new file mode 100644 index 000000000..a2c9c3852 --- /dev/null +++ b/pkg/model/evaluate.go @@ -0,0 +1,26 @@ +package model + +import ( + "context" + "fmt" + + "github.com/jmespath-community/go-jmespath/pkg/binding" + "github.com/kyverno/chainsaw/pkg/mutate" + "github.com/kyverno/chainsaw/pkg/runner/functions" + "github.com/kyverno/kyverno-json/pkg/engine/template" +) + +func Evaluate[T any](ctx context.Context, in string, bindings binding.Bindings) (T, error) { + var def T + if converted, err := mutate.Mutate(ctx, nil, mutate.Parse(ctx, in), nil, bindings, template.WithFunctionCaller(functions.Caller)); err != nil { + return def, err + } else if converted, ok := converted.(T); !ok { + return converted, fmt.Errorf("expression didn't evaluate to the expected type (%s)", in) + } else { + return converted, nil + } +} + +func String(ctx context.Context, in string, bindings binding.Bindings) (string, error) { + return Evaluate[string](ctx, in, bindings) +} diff --git a/pkg/runner/processors/step.go b/pkg/runner/processors/step.go index c2d6a2a61..64602ad82 100644 --- a/pkg/runner/processors/step.go +++ b/pkg/runner/processors/step.go @@ -42,16 +42,12 @@ import ( "k8s.io/utils/clock" ) -// TODO -// - create if not exists - type StepProcessor interface { - Run(context.Context, binding.Bindings) + Run(context.Context, model.TestContext) } func NewStepProcessor( config model.Configuration, - clusters clusters.Registry, namespacer namespacer.Namespacer, clock clock.PassiveClock, test discovery.Test, @@ -60,31 +56,24 @@ func NewStepProcessor( ) StepProcessor { return &stepProcessor{ config: config, - clusters: clusters, namespacer: namespacer, clock: clock, test: test, step: step, report: report, - timeouts: config.Timeouts.Combine(test.Test.Spec.Timeouts).Combine(step.Timeouts), } } type stepProcessor struct { config model.Configuration - clusters clusters.Registry namespacer namespacer.Namespacer clock clock.PassiveClock test discovery.Test step v1alpha1.TestStep report *report.StepReport - timeouts v1alpha1.DefaultTimeouts } -func (p *stepProcessor) Run(ctx context.Context, bindings binding.Bindings) { - if bindings == nil { - bindings = binding.NewBindings() - } +func (p *stepProcessor) Run(ctx context.Context, tc model.TestContext) { t := testing.FromContext(ctx) if p.report != nil { p.report.SetStartTime(time.Now()) @@ -93,13 +82,17 @@ func (p *stepProcessor) Run(ctx context.Context, bindings binding.Bindings) { }) } logger := logging.FromContext(ctx) - registeredClusters := clusters.Register(p.clusters, p.test.BasePath, p.step.Clusters) + if p.step.Timeouts != nil { + tc = model.WithTimeouts(ctx, tc, *p.step.Timeouts) + } + timeouts := tc.Timeouts() + registeredClusters := clusters.Register(tc.Clusters(), p.test.BasePath, p.step.Clusters) clusterConfig, clusterClient, err := registeredClusters.Resolve(false, p.test.Test.Spec.Cluster) if err != nil { logging.Log(ctx, logging.Internal, logging.ErrorStatus, color.BoldRed, logging.ErrSection(err)) failer.FailNow(ctx) } - bindings = apibindings.RegisterClusterBindings(ctx, bindings, clusterConfig, clusterClient) + bindings := apibindings.RegisterClusterBindings(ctx, tc.Bindings(), clusterConfig, clusterClient) bindings, err = apibindings.RegisterBindings(ctx, bindings, p.step.Bindings...) if err != nil { logging.Log(ctx, logging.Internal, logging.ErrorStatus, color.BoldRed, logging.ErrSection(err)) @@ -109,23 +102,23 @@ func (p *stepProcessor) Run(ctx context.Context, bindings binding.Bindings) { if p.test.Test.Spec.DelayBeforeCleanup != nil { delay = p.test.Test.Spec.DelayBeforeCleanup } - cleaner := cleanup.NewCleaner(p.timeouts.Cleanup.Duration, delay) - try, err := p.tryOperations(registeredClusters, cleaner) + cleaner := cleanup.NewCleaner(timeouts.Cleanup, delay) + try, err := p.tryOperations(timeouts, registeredClusters, cleaner) if err != nil { logger.Log(logging.Try, logging.ErrorStatus, color.BoldRed, logging.ErrSection(err)) failer.FailNow(ctx) } - catch, err := p.catchOperations(registeredClusters) + catch, err := p.catchOperations(timeouts, registeredClusters) if err != nil { logger.Log(logging.Catch, logging.ErrorStatus, color.BoldRed, logging.ErrSection(err)) failer.FailNow(ctx) } - finally, err := p.finallyOperations(registeredClusters, p.step.Finally...) + finally, err := p.finallyOperations(timeouts, registeredClusters, p.step.Finally...) if err != nil { logger.Log(logging.Finally, logging.ErrorStatus, color.BoldRed, logging.ErrSection(err)) failer.FailNow(ctx) } - cleanup, err := p.finallyOperations(registeredClusters, p.step.Cleanup...) + cleanup, err := p.finallyOperations(timeouts, registeredClusters, p.step.Cleanup...) if err != nil { logger.Log(logging.Cleanup, logging.ErrorStatus, color.BoldRed, logging.ErrSection(err)) failer.FailNow(ctx) @@ -180,7 +173,7 @@ func (p *stepProcessor) Run(ctx context.Context, bindings binding.Bindings) { } } -func (p *stepProcessor) tryOperations(registeredClusters clusters.Registry, cleaner cleanup.Cleaner) ([]operation, error) { +func (p *stepProcessor) tryOperations(timeouts model.Timeouts, registeredClusters clusters.Registry, cleaner cleanup.Cleaner) ([]operation, error) { var ops []operation for i, handler := range p.step.Try { register := func(o ...operation) { @@ -191,35 +184,35 @@ func (p *stepProcessor) tryOperations(registeredClusters clusters.Registry, clea } } if handler.Apply != nil { - loaded, err := p.applyOperation(i+1, registeredClusters, cleaner, *handler.Apply) + loaded, err := p.applyOperation(i+1, timeouts, registeredClusters, cleaner, *handler.Apply) if err != nil { return nil, err } register(loaded...) } else if handler.Assert != nil { - loaded, err := p.assertOperation(i+1, registeredClusters, *handler.Assert) + loaded, err := p.assertOperation(i+1, timeouts, registeredClusters, *handler.Assert) if err != nil { return nil, err } register(loaded...) } else if handler.Command != nil { - register(p.commandOperation(i+1, registeredClusters, *handler.Command)) + register(p.commandOperation(i+1, timeouts, registeredClusters, *handler.Command)) } else if handler.Create != nil { - loaded, err := p.createOperation(i+1, registeredClusters, cleaner, *handler.Create) + loaded, err := p.createOperation(i+1, timeouts, registeredClusters, cleaner, *handler.Create) if err != nil { return nil, err } register(loaded...) } else if handler.Delete != nil { - loaded, err := p.deleteOperation(i+1, registeredClusters, *handler.Delete) + loaded, err := p.deleteOperation(i+1, timeouts, registeredClusters, *handler.Delete) if err != nil { return nil, err } register(loaded...) } else if handler.Describe != nil { - register(p.describeOperation(i+1, registeredClusters, *handler.Describe)) + register(p.describeOperation(i+1, timeouts, registeredClusters, *handler.Describe)) } else if handler.Error != nil { - loaded, err := p.errorOperation(i+1, registeredClusters, *handler.Error) + loaded, err := p.errorOperation(i+1, timeouts, registeredClusters, *handler.Error) if err != nil { return nil, err } @@ -237,31 +230,31 @@ func (p *stepProcessor) tryOperations(registeredClusters clusters.Registry, clea ActionObjectSelector: handler.Events.ActionObjectSelector, }, } - register(p.getOperation(i+1, registeredClusters, get)) + register(p.getOperation(i+1, timeouts, registeredClusters, get)) } else if handler.Get != nil { - register(p.getOperation(i+1, registeredClusters, *handler.Get)) + register(p.getOperation(i+1, timeouts, registeredClusters, *handler.Get)) } else if handler.Patch != nil { - loaded, err := p.patchOperation(i+1, registeredClusters, *handler.Patch) + loaded, err := p.patchOperation(i+1, timeouts, registeredClusters, *handler.Patch) if err != nil { return nil, err } register(loaded...) } else if handler.PodLogs != nil { - register(p.logsOperation(i+1, registeredClusters, *handler.PodLogs)) + register(p.logsOperation(i+1, timeouts, registeredClusters, *handler.PodLogs)) } else if handler.Proxy != nil { - register(p.proxyOperation(i+1, registeredClusters, *handler.Proxy)) + register(p.proxyOperation(i+1, timeouts, registeredClusters, *handler.Proxy)) } else if handler.Script != nil { - register(p.scriptOperation(i+1, registeredClusters, *handler.Script)) + register(p.scriptOperation(i+1, timeouts, registeredClusters, *handler.Script)) } else if handler.Sleep != nil { register(p.sleepOperation(i+1, *handler.Sleep)) } else if handler.Update != nil { - loaded, err := p.updateOperation(i+1, registeredClusters, *handler.Update) + loaded, err := p.updateOperation(i+1, timeouts, registeredClusters, *handler.Update) if err != nil { return nil, err } register(loaded...) } else if handler.Wait != nil { - register(p.waitOperation(i+1, registeredClusters, *handler.Wait)) + register(p.waitOperation(i+1, timeouts, registeredClusters, *handler.Wait)) } else { return nil, errors.New("no operation found") } @@ -269,7 +262,7 @@ func (p *stepProcessor) tryOperations(registeredClusters clusters.Registry, clea return ops, nil } -func (p *stepProcessor) catchOperations(registeredClusters clusters.Registry) ([]operation, error) { +func (p *stepProcessor) catchOperations(timeouts model.Timeouts, registeredClusters clusters.Registry) ([]operation, error) { var ops []operation register := func(o ...operation) { for _, o := range o { @@ -283,7 +276,7 @@ func (p *stepProcessor) catchOperations(registeredClusters clusters.Registry) ([ handlers = append(handlers, p.step.Catch...) for i, handler := range handlers { if handler.PodLogs != nil { - register(p.logsOperation(i+1, registeredClusters, *handler.PodLogs)) + register(p.logsOperation(i+1, timeouts, registeredClusters, *handler.PodLogs)) } else if handler.Events != nil { get := v1alpha1.Get{ ActionClusters: handler.Events.ActionClusters, @@ -297,25 +290,25 @@ func (p *stepProcessor) catchOperations(registeredClusters clusters.Registry) ([ ActionObjectSelector: handler.Events.ActionObjectSelector, }, } - register(p.getOperation(i+1, registeredClusters, get)) + register(p.getOperation(i+1, timeouts, registeredClusters, get)) } else if handler.Describe != nil { - register(p.describeOperation(i+1, registeredClusters, *handler.Describe)) + register(p.describeOperation(i+1, timeouts, registeredClusters, *handler.Describe)) } else if handler.Get != nil { - register(p.getOperation(i+1, registeredClusters, *handler.Get)) + register(p.getOperation(i+1, timeouts, registeredClusters, *handler.Get)) } else if handler.Delete != nil { - loaded, err := p.deleteOperation(i+1, registeredClusters, *handler.Delete) + loaded, err := p.deleteOperation(i+1, timeouts, registeredClusters, *handler.Delete) if err != nil { return nil, err } register(loaded...) } else if handler.Command != nil { - register(p.commandOperation(i+1, registeredClusters, *handler.Command)) + register(p.commandOperation(i+1, timeouts, registeredClusters, *handler.Command)) } else if handler.Script != nil { - register(p.scriptOperation(i+1, registeredClusters, *handler.Script)) + register(p.scriptOperation(i+1, timeouts, registeredClusters, *handler.Script)) } else if handler.Sleep != nil { register(p.sleepOperation(i+1, *handler.Sleep)) } else if handler.Wait != nil { - register(p.waitOperation(i+1, registeredClusters, *handler.Wait)) + register(p.waitOperation(i+1, timeouts, registeredClusters, *handler.Wait)) } else { return nil, errors.New("no operation found") } @@ -323,7 +316,7 @@ func (p *stepProcessor) catchOperations(registeredClusters clusters.Registry) ([ return ops, nil } -func (p *stepProcessor) finallyOperations(registeredClusters clusters.Registry, operations ...v1alpha1.CatchFinally) ([]operation, error) { +func (p *stepProcessor) finallyOperations(timeouts model.Timeouts, registeredClusters clusters.Registry, operations ...v1alpha1.CatchFinally) ([]operation, error) { var ops []operation register := func(o ...operation) { for _, o := range o { @@ -333,7 +326,7 @@ func (p *stepProcessor) finallyOperations(registeredClusters clusters.Registry, } for i, handler := range operations { if handler.PodLogs != nil { - register(p.logsOperation(i+1, registeredClusters, *handler.PodLogs)) + register(p.logsOperation(i+1, timeouts, registeredClusters, *handler.PodLogs)) } else if handler.Events != nil { get := v1alpha1.Get{ ActionClusters: handler.Events.ActionClusters, @@ -347,25 +340,25 @@ func (p *stepProcessor) finallyOperations(registeredClusters clusters.Registry, ActionObjectSelector: handler.Events.ActionObjectSelector, }, } - register(p.getOperation(i+1, registeredClusters, get)) + register(p.getOperation(i+1, timeouts, registeredClusters, get)) } else if handler.Describe != nil { - register(p.describeOperation(i+1, registeredClusters, *handler.Describe)) + register(p.describeOperation(i+1, timeouts, registeredClusters, *handler.Describe)) } else if handler.Get != nil { - register(p.getOperation(i+1, registeredClusters, *handler.Get)) + register(p.getOperation(i+1, timeouts, registeredClusters, *handler.Get)) } else if handler.Delete != nil { - loaded, err := p.deleteOperation(i+1, registeredClusters, *handler.Delete) + loaded, err := p.deleteOperation(i+1, timeouts, registeredClusters, *handler.Delete) if err != nil { return nil, err } register(loaded...) } else if handler.Command != nil { - register(p.commandOperation(i+1, registeredClusters, *handler.Command)) + register(p.commandOperation(i+1, timeouts, registeredClusters, *handler.Command)) } else if handler.Script != nil { - register(p.scriptOperation(i+1, registeredClusters, *handler.Script)) + register(p.scriptOperation(i+1, timeouts, registeredClusters, *handler.Script)) } else if handler.Sleep != nil { register(p.sleepOperation(i+1, *handler.Sleep)) } else if handler.Wait != nil { - register(p.waitOperation(i+1, registeredClusters, *handler.Wait)) + register(p.waitOperation(i+1, timeouts, registeredClusters, *handler.Wait)) } else { return nil, errors.New("no operation found") } @@ -373,7 +366,7 @@ func (p *stepProcessor) finallyOperations(registeredClusters clusters.Registry, return ops, nil } -func (p *stepProcessor) applyOperation(id int, registeredClusters clusters.Registry, cleaner cleanup.Cleaner, op v1alpha1.Apply) ([]operation, error) { +func (p *stepProcessor) applyOperation(id int, timeouts model.Timeouts, registeredClusters clusters.Registry, cleaner cleanup.Cleaner, op v1alpha1.Apply) ([]operation, error) { resources, err := p.fileRefOrResource(op.ActionResourceRef) if err != nil { return nil, err @@ -397,7 +390,7 @@ func (p *stepProcessor) applyOperation(id int, registeredClusters clusters.Regis ResourceId: i + 1, }, false, - timeout.Get(op.Timeout, p.timeouts.Apply.Duration), + timeout.Get(op.Timeout, timeouts.Apply), func(ctx context.Context, bindings binding.Bindings) (operations.Operation, binding.Bindings, error) { config, client, err := clusterResolver(dryRun) if err != nil { @@ -413,7 +406,7 @@ func (p *stepProcessor) applyOperation(id int, registeredClusters clusters.Regis return ops, nil } -func (p *stepProcessor) assertOperation(id int, registeredClusters clusters.Registry, op v1alpha1.Assert) ([]operation, error) { +func (p *stepProcessor) assertOperation(id int, timeouts model.Timeouts, registeredClusters clusters.Registry, op v1alpha1.Assert) ([]operation, error) { resources, err := p.fileRefOrCheck(op.ActionCheckRef) if err != nil { return nil, err @@ -433,7 +426,7 @@ func (p *stepProcessor) assertOperation(id int, registeredClusters clusters.Regi ResourceId: i + 1, }, false, - timeout.Get(op.Timeout, p.timeouts.Assert.Duration), + timeout.Get(op.Timeout, timeouts.Assert), func(ctx context.Context, bindings binding.Bindings) (operations.Operation, binding.Bindings, error) { config, client, err := clusterResolver(false) if err != nil { @@ -449,7 +442,7 @@ func (p *stepProcessor) assertOperation(id int, registeredClusters clusters.Regi return ops, nil } -func (p *stepProcessor) commandOperation(id int, registeredClusters clusters.Registry, op v1alpha1.Command) operation { +func (p *stepProcessor) commandOperation(id int, timeouts model.Timeouts, registeredClusters clusters.Registry, op v1alpha1.Command) operation { var operationReport *report.OperationReport if p.report != nil { operationReport = p.report.ForOperation("Command ", report.OperationTypeCommand) @@ -465,7 +458,7 @@ func (p *stepProcessor) commandOperation(id int, registeredClusters clusters.Reg Id: id, }, false, - timeout.Get(op.Timeout, p.timeouts.Exec.Duration), + timeout.Get(op.Timeout, timeouts.Exec), func(ctx context.Context, bindings binding.Bindings) (operations.Operation, binding.Bindings, error) { config, client, err := clusterResolver(false) if err != nil { @@ -479,7 +472,7 @@ func (p *stepProcessor) commandOperation(id int, registeredClusters clusters.Reg ) } -func (p *stepProcessor) createOperation(id int, registeredClusters clusters.Registry, cleaner cleanup.Cleaner, op v1alpha1.Create) ([]operation, error) { +func (p *stepProcessor) createOperation(id int, timeouts model.Timeouts, registeredClusters clusters.Registry, cleaner cleanup.Cleaner, op v1alpha1.Create) ([]operation, error) { resources, err := p.fileRefOrResource(op.ActionResourceRef) if err != nil { return nil, err @@ -504,7 +497,7 @@ func (p *stepProcessor) createOperation(id int, registeredClusters clusters.Regi ResourceId: i + 1, }, false, - timeout.Get(op.Timeout, p.timeouts.Apply.Duration), + timeout.Get(op.Timeout, timeouts.Apply), func(ctx context.Context, bindings binding.Bindings) (operations.Operation, binding.Bindings, error) { config, client, err := clusterResolver(dryRun) if err != nil { @@ -520,7 +513,7 @@ func (p *stepProcessor) createOperation(id int, registeredClusters clusters.Regi return ops, nil } -func (p *stepProcessor) deleteOperation(id int, registeredClusters clusters.Registry, op v1alpha1.Delete) ([]operation, error) { +func (p *stepProcessor) deleteOperation(id int, timeouts model.Timeouts, registeredClusters clusters.Registry, op v1alpha1.Delete) ([]operation, error) { ref := v1alpha1.ActionResourceRef{ FileRef: v1alpha1.FileRef{ File: op.File, @@ -564,7 +557,7 @@ func (p *stepProcessor) deleteOperation(id int, registeredClusters clusters.Regi ResourceId: i + 1, }, false, - timeout.Get(op.Timeout, p.timeouts.Delete.Duration), + timeout.Get(op.Timeout, timeouts.Delete), func(ctx context.Context, bindings binding.Bindings) (operations.Operation, binding.Bindings, error) { config, client, err := clusterResolver(false) if err != nil { @@ -580,7 +573,7 @@ func (p *stepProcessor) deleteOperation(id int, registeredClusters clusters.Regi return ops, nil } -func (p *stepProcessor) describeOperation(id int, registeredClusters clusters.Registry, op v1alpha1.Describe) operation { +func (p *stepProcessor) describeOperation(id int, timeouts model.Timeouts, registeredClusters clusters.Registry, op v1alpha1.Describe) operation { var operationReport *report.OperationReport if p.report != nil { operationReport = p.report.ForOperation("Describe ", report.OperationTypeCommand) @@ -596,7 +589,7 @@ func (p *stepProcessor) describeOperation(id int, registeredClusters clusters.Re Id: id, }, false, - timeout.Get(op.Timeout, p.timeouts.Exec.Duration), + timeout.Get(op.Timeout, timeouts.Exec), func(ctx context.Context, bindings binding.Bindings) (operations.Operation, binding.Bindings, error) { config, client, err := clusterResolver(false) if err != nil { @@ -613,7 +606,7 @@ func (p *stepProcessor) describeOperation(id int, registeredClusters clusters.Re ) } -func (p *stepProcessor) errorOperation(id int, registeredClusters clusters.Registry, op v1alpha1.Error) ([]operation, error) { +func (p *stepProcessor) errorOperation(id int, timeouts model.Timeouts, registeredClusters clusters.Registry, op v1alpha1.Error) ([]operation, error) { resources, err := p.fileRefOrCheck(op.ActionCheckRef) if err != nil { return nil, err @@ -634,7 +627,7 @@ func (p *stepProcessor) errorOperation(id int, registeredClusters clusters.Regis ResourceId: i + 1, }, false, - timeout.Get(op.Timeout, p.timeouts.Error.Duration), + timeout.Get(op.Timeout, timeouts.Error), func(ctx context.Context, bindings binding.Bindings) (operations.Operation, binding.Bindings, error) { config, client, err := clusterResolver(false) if err != nil { @@ -650,7 +643,7 @@ func (p *stepProcessor) errorOperation(id int, registeredClusters clusters.Regis return ops, nil } -func (p *stepProcessor) getOperation(id int, registeredClusters clusters.Registry, op v1alpha1.Get) operation { +func (p *stepProcessor) getOperation(id int, timeouts model.Timeouts, registeredClusters clusters.Registry, op v1alpha1.Get) operation { var operationReport *report.OperationReport if p.report != nil { operationReport = p.report.ForOperation("Get ", report.OperationTypeCommand) @@ -666,7 +659,7 @@ func (p *stepProcessor) getOperation(id int, registeredClusters clusters.Registr Id: id, }, false, - timeout.Get(op.Timeout, p.timeouts.Exec.Duration), + timeout.Get(op.Timeout, timeouts.Exec), func(ctx context.Context, bindings binding.Bindings) (operations.Operation, binding.Bindings, error) { config, client, err := clusterResolver(false) if err != nil { @@ -683,7 +676,7 @@ func (p *stepProcessor) getOperation(id int, registeredClusters clusters.Registr ) } -func (p *stepProcessor) logsOperation(id int, registeredClusters clusters.Registry, op v1alpha1.PodLogs) operation { +func (p *stepProcessor) logsOperation(id int, timeouts model.Timeouts, registeredClusters clusters.Registry, op v1alpha1.PodLogs) operation { var operationReport *report.OperationReport if p.report != nil { operationReport = p.report.ForOperation("Logs ", report.OperationTypeCommand) @@ -699,7 +692,7 @@ func (p *stepProcessor) logsOperation(id int, registeredClusters clusters.Regist Id: id, }, false, - timeout.Get(op.Timeout, p.timeouts.Exec.Duration), + timeout.Get(op.Timeout, timeouts.Exec), func(ctx context.Context, bindings binding.Bindings) (operations.Operation, binding.Bindings, error) { config, client, err := clusterResolver(false) if err != nil { @@ -716,7 +709,7 @@ func (p *stepProcessor) logsOperation(id int, registeredClusters clusters.Regist ) } -func (p *stepProcessor) patchOperation(id int, registeredClusters clusters.Registry, op v1alpha1.Patch) ([]operation, error) { +func (p *stepProcessor) patchOperation(id int, timeouts model.Timeouts, registeredClusters clusters.Registry, op v1alpha1.Patch) ([]operation, error) { resources, err := p.fileRefOrResource(op.ActionResourceRef) if err != nil { return nil, err @@ -741,7 +734,7 @@ func (p *stepProcessor) patchOperation(id int, registeredClusters clusters.Regis ResourceId: i + 1, }, false, - timeout.Get(op.Timeout, p.timeouts.Apply.Duration), + timeout.Get(op.Timeout, timeouts.Apply), func(ctx context.Context, bindings binding.Bindings) (operations.Operation, binding.Bindings, error) { config, client, err := clusterResolver(dryRun) if err != nil { @@ -757,7 +750,7 @@ func (p *stepProcessor) patchOperation(id int, registeredClusters clusters.Regis return ops, nil } -func (p *stepProcessor) proxyOperation(id int, registeredClusters clusters.Registry, op v1alpha1.Proxy) operation { +func (p *stepProcessor) proxyOperation(id int, timeouts model.Timeouts, registeredClusters clusters.Registry, op v1alpha1.Proxy) operation { var operationReport *report.OperationReport if p.report != nil { operationReport = p.report.ForOperation("Proxy ", report.OperationTypeCommand) @@ -773,7 +766,7 @@ func (p *stepProcessor) proxyOperation(id int, registeredClusters clusters.Regis Id: id, }, false, - timeout.Get(op.Timeout, p.timeouts.Exec.Duration), + timeout.Get(op.Timeout, timeouts.Exec), func(ctx context.Context, bindings binding.Bindings) (operations.Operation, binding.Bindings, error) { config, client, err := clusterResolver(false) if err != nil { @@ -790,7 +783,7 @@ func (p *stepProcessor) proxyOperation(id int, registeredClusters clusters.Regis ) } -func (p *stepProcessor) scriptOperation(id int, registeredClusters clusters.Registry, op v1alpha1.Script) operation { +func (p *stepProcessor) scriptOperation(id int, timeouts model.Timeouts, registeredClusters clusters.Registry, op v1alpha1.Script) operation { var operationReport *report.OperationReport if p.report != nil { operationReport = p.report.ForOperation("Script ", report.OperationTypeScript) @@ -806,7 +799,7 @@ func (p *stepProcessor) scriptOperation(id int, registeredClusters clusters.Regi Id: id, }, false, - timeout.Get(op.Timeout, p.timeouts.Exec.Duration), + timeout.Get(op.Timeout, timeouts.Exec), func(ctx context.Context, bindings binding.Bindings) (operations.Operation, binding.Bindings, error) { config, client, err := clusterResolver(false) if err != nil { @@ -839,7 +832,7 @@ func (p *stepProcessor) sleepOperation(id int, op v1alpha1.Sleep) operation { ) } -func (p *stepProcessor) updateOperation(id int, registeredClusters clusters.Registry, op v1alpha1.Update) ([]operation, error) { +func (p *stepProcessor) updateOperation(id int, timeouts model.Timeouts, registeredClusters clusters.Registry, op v1alpha1.Update) ([]operation, error) { resources, err := p.fileRefOrResource(op.ActionResourceRef) if err != nil { return nil, err @@ -864,7 +857,7 @@ func (p *stepProcessor) updateOperation(id int, registeredClusters clusters.Regi ResourceId: i + 1, }, false, - timeout.Get(op.Timeout, p.timeouts.Apply.Duration), + timeout.Get(op.Timeout, timeouts.Apply), func(ctx context.Context, bindings binding.Bindings) (operations.Operation, binding.Bindings, error) { config, client, err := clusterResolver(dryRun) if err != nil { @@ -880,7 +873,7 @@ func (p *stepProcessor) updateOperation(id int, registeredClusters clusters.Regi return ops, nil } -func (p *stepProcessor) waitOperation(id int, registeredClusters clusters.Registry, op v1alpha1.Wait) operation { +func (p *stepProcessor) waitOperation(id int, timeouts model.Timeouts, registeredClusters clusters.Registry, op v1alpha1.Wait) operation { var operationReport *report.OperationReport if p.report != nil { operationReport = p.report.ForOperation("Wait ", report.OperationTypeCommand) @@ -890,7 +883,7 @@ func (p *stepProcessor) waitOperation(id int, registeredClusters clusters.Regist ns = p.namespacer.GetNamespace() } // make sure timeout is set to populate the command flag - op.Timeout = &metav1.Duration{Duration: *timeout.Get(op.Timeout, p.timeouts.Exec.Duration)} + op.Timeout = &metav1.Duration{Duration: *timeout.Get(op.Timeout, timeouts.Exec)} // shift operation timeout timeout := op.Timeout.Duration + 30*time.Second registeredClusters = clusters.Register(registeredClusters, p.test.BasePath, op.Clusters) diff --git a/pkg/runner/processors/step_test.go b/pkg/runner/processors/step_test.go index 4c8ce44bb..7fac3fddf 100644 --- a/pkg/runner/processors/step_test.go +++ b/pkg/runner/processors/step_test.go @@ -5,6 +5,7 @@ import ( "path/filepath" "time" + "github.com/jmespath-community/go-jmespath/pkg/binding" "github.com/kyverno/chainsaw/pkg/apis/v1alpha1" "github.com/kyverno/chainsaw/pkg/apis/v1alpha2" "github.com/kyverno/chainsaw/pkg/client" @@ -1012,7 +1013,6 @@ func TestStepProcessor_Run(t *testing.T) { } stepProcessor := NewStepProcessor( tc.config, - registry, tc.namespacer, tc.clock, tc.test, @@ -1022,7 +1022,9 @@ func TestStepProcessor_Run(t *testing.T) { nt := &testing.MockT{} ctx := testing.IntoContext(context.Background(), nt) ctx = logging.IntoContext(ctx, &fakeLogger.FakeLogger{}) - stepProcessor.Run(ctx, nil) + tcontext := model.MakeContext(binding.NewBindings(), registry) + tcontext = model.WithDefaultTimeouts(ctx, tcontext, config.Spec.Timeouts) + stepProcessor.Run(ctx, tcontext) nt.Cleanup(func() {}) if tc.expectedFail { assert.True(t, nt.FailedVar, "expected an error but got none") diff --git a/pkg/runner/processors/test.go b/pkg/runner/processors/test.go index b1b36b83f..e909f5d3b 100644 --- a/pkg/runner/processors/test.go +++ b/pkg/runner/processors/test.go @@ -11,7 +11,6 @@ import ( "github.com/kyverno/chainsaw/pkg/discovery" "github.com/kyverno/chainsaw/pkg/model" "github.com/kyverno/chainsaw/pkg/report" - apibindings "github.com/kyverno/chainsaw/pkg/runner/bindings" "github.com/kyverno/chainsaw/pkg/runner/cleanup" "github.com/kyverno/chainsaw/pkg/runner/clusters" "github.com/kyverno/chainsaw/pkg/runner/failer" @@ -65,8 +64,11 @@ func (p *testProcessor) Run(ctx context.Context, nspacer namespacer.Namespacer, size = len(name) } } - timeouts := p.config.Timeouts.Combine(test.Test.Spec.Timeouts) - cleaner := cleanup.NewCleaner(timeouts.Cleanup.Duration, nil) + if test.Test.Spec.Timeouts != nil { + tc = model.WithTimeouts(ctx, tc, *test.Test.Spec.Timeouts) + } + timeouts := tc.Timeouts() + cleaner := cleanup.NewCleaner(timeouts.Cleanup, nil) setupLogger := logging.NewLogger(t, p.clock, test.Test.Name, fmt.Sprintf("%-*s", size, "@setup")) cleanupLogger := logging.NewLogger(t, p.clock, test.Test.Name, fmt.Sprintf("%-*s", size, "@cleanup")) setupCtx := logging.IntoContext(ctx, setupLogger) @@ -124,20 +126,22 @@ func (p *testProcessor) Run(ctx context.Context, nspacer namespacer.Namespacer, if p.report != nil && nspacer != nil { p.report.SetNamespace(nspacer.GetNamespace()) } - bindings, err := apibindings.RegisterBindings(ctx, tc.Bindings(), test.Test.Spec.Bindings...) + _tc, err := model.WithBindings(ctx, tc, test.Test.Spec.Bindings...) if err != nil { logging.Log(ctx, logging.Internal, logging.ErrorStatus, color.BoldRed, logging.ErrSection(err)) failer.FailNow(ctx) } + tc = _tc for i, step := range test.Test.Spec.Steps { processor := p.createStepProcessor(nspacer, tc, test, step) name := step.Name if name == "" { name = fmt.Sprintf("step-%d", i+1) } + tc := tc.WithBinding(ctx, "step", StepInfo{Id: i + 1}) processor.Run( logging.IntoContext(ctx, logging.NewLogger(t, p.clock, test.Test.Name, fmt.Sprintf("%-*s", size, name))), - apibindings.RegisterNamedBinding(ctx, bindings, "step", StepInfo{Id: i + 1}), + tc, ) } } @@ -147,5 +151,5 @@ func (p *testProcessor) createStepProcessor(nspacer namespacer.Namespacer, tc mo if p.report != nil { report = p.report.ForStep(&step) } - return NewStepProcessor(p.config, tc.Clusters(), nspacer, p.clock, test, step, report) + return NewStepProcessor(p.config, nspacer, p.clock, test, step, report) } diff --git a/pkg/runner/processors/tests.go b/pkg/runner/processors/tests.go index 4cad4f70e..92cae2bac 100644 --- a/pkg/runner/processors/tests.go +++ b/pkg/runner/processors/tests.go @@ -143,7 +143,7 @@ func (p *testsProcessor) setup(ctx context.Context, tc model.TestContext) (model p.report.SetEndTime(time.Now()) }) } - cleaner := cleanup.NewCleaner(p.config.Timeouts.Cleanup.Duration, nil) + cleaner := cleanup.NewCleaner(tc.Timeouts().Cleanup, nil) t.Cleanup(func() { if !cleaner.Empty() { logging.Log(ctx, logging.Cleanup, logging.RunStatus, color.BoldFgCyan) diff --git a/pkg/runner/run.go b/pkg/runner/run.go index 7be5ed2fd..0e1b681de 100644 --- a/pkg/runner/run.go +++ b/pkg/runner/run.go @@ -87,7 +87,8 @@ func run( } func setupTestContext(ctx context.Context, values any, cluster *rest.Config, config model.Configuration) (model.TestContext, error) { - tc := model.WithValues(ctx, model.EmptyContext(), values) + tc := model.EmptyContext() + tc = model.WithValues(ctx, tc, values) if cluster != nil { cluster, err := clusters.NewClusterFromConfig(cluster) if err != nil { @@ -101,5 +102,6 @@ func setupTestContext(ctx context.Context, values any, cluster *rest.Config, con tc = _tc } tc = model.WithClusters(ctx, tc, "", config.Clusters) + tc = model.WithDefaultTimeouts(ctx, tc, config.Timeouts) return tc, nil } diff --git a/pkg/runner/run_test.go b/pkg/runner/run_test.go index 4e50192d9..635c50e0a 100644 --- a/pkg/runner/run_test.go +++ b/pkg/runner/run_test.go @@ -7,6 +7,7 @@ import ( "github.com/kyverno/chainsaw/pkg/apis/v1alpha2" "github.com/kyverno/chainsaw/pkg/discovery" + "github.com/kyverno/chainsaw/pkg/loaders/config" "github.com/kyverno/chainsaw/pkg/model" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -24,7 +25,10 @@ func (m *MockMainStart) Run() int { func TestRun(t *testing.T) { fakeClock := tclock.NewFakePassiveClock(time.Now()) - + config, err := config.DefaultConfiguration() + if err != nil { + assert.NoError(t, err) + } tests := []struct { name string tests []discovery.Test @@ -33,9 +37,11 @@ func TestRun(t *testing.T) { mockReturn int wantErr bool }{{ - name: "Zero Tests", - tests: []discovery.Test{}, - config: model.Configuration{}, + name: "Zero Tests", + tests: []discovery.Test{}, + config: model.Configuration{ + Timeouts: config.Spec.Timeouts, + }, restConfig: &rest.Config{}, wantErr: false, }, { @@ -51,6 +57,7 @@ func TestRun(t *testing.T) { }, }, config: model.Configuration{ + Timeouts: config.Spec.Timeouts, Report: &v1alpha2.ReportOptions{ Format: v1alpha2.JSONFormat, }, @@ -61,6 +68,7 @@ func TestRun(t *testing.T) { name: "Zero Tests with JSON Report", tests: []discovery.Test{}, config: model.Configuration{ + Timeouts: config.Spec.Timeouts, Report: &v1alpha2.ReportOptions{ Format: v1alpha2.JSONFormat, }, @@ -110,6 +118,7 @@ func TestRun(t *testing.T) { }, }, config: model.Configuration{ + Timeouts: config.Spec.Timeouts, Report: &v1alpha2.ReportOptions{ Format: v1alpha2.XMLFormat, Name: "chainsaw", @@ -131,6 +140,7 @@ func TestRun(t *testing.T) { }, }, config: model.Configuration{ + Timeouts: config.Spec.Timeouts, Report: &v1alpha2.ReportOptions{ Format: "abc", },