Repository: kyverno/chainsaw
Pull request #550 (2023-11-29 23:25:35)

fix: make timeouts fields pointers
Make timeouts fields pointers.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/kyverno/chainsaw/pull/550?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) Report
Attention: `10 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`f83335d`)](https://app.codecov.io/gh/kyverno/chainsaw/commit/f83335d2376aa8852ce4098e65e01c443531d4a2?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) 86.64% compared to head [(`4c63abf`)](https://app.codecov.io/gh/kyverno/chainsaw/pull/550?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) 86.91%.
> Report is 1 commits behind head on main.

| [Files](https://app.codecov.io/gh/kyverno/chainsaw/pull/550?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) | Patch % | Lines |
|---|---|---|
| [pkg/commands/kuttl/migrate/tests/command.go](https://app.codecov.io/gh/kyverno/chainsaw/pull/550?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno#diff-cGtnL2NvbW1hbmRzL2t1dHRsL21pZ3JhdGUvdGVzdHMvY29tbWFuZC5nbw==) | 0.00% | [10 Missing :warning: ](https://app.codecov.io/gh/kyverno/chainsaw/pull/550?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #550      +/-   ##
==========================================
+ Coverage   86.64%   86.91%   +0.26%     
==========================================
  Files          75       75              
  Lines        2284     2277       -7     
==========================================
  Hits         1979     1979              
+ Misses        248      241       -7     
  Partials       57       57              
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/kyverno/chainsaw/pull/550?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno).


Patch:
diff --git a/.release-notes/main.md b/.release-notes/main.md
index 2324dcc8..16a26090 100644
--- a/.release-notes/main.md
+++ b/.release-notes/main.md
@@ -28,6 +28,7 @@ Release notes for `TODO`.
 ## :wrench: Fixes :wrench:
 
 - Don't show `command` or `script` logs if there's nothing to show
+- Fixed `timeouts` always set when migrating kuttl tests
 
 ## :guitar: Misc :guitar:
 
diff --git a/pkg/apis/v1alpha1/timeouts.go b/pkg/apis/v1alpha1/timeouts.go
index 4ab2e81f..54d9b9d2 100644
--- a/pkg/apis/v1alpha1/timeouts.go
+++ b/pkg/apis/v1alpha1/timeouts.go
@@ -1,9 +1,20 @@
 package v1alpha1
 
 import (
+	"time"
+
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
+const (
+	DefaultApplyTimeout   = 5 * time.Second
+	DefaultAssertTimeout  = 30 * time.Second
+	DefaultCleanupTimeout = 30 * time.Second
+	DefaultDeleteTimeout  = 15 * time.Second
+	DefaultErrorTimeout   = 30 * time.Second
+	DefaultExecTimeout    = 5 * time.Second
+)
+
 // Timeouts contains timeouts per operation.
 type Timeouts struct {
 	// Apply defines the timeout for the apply operation
@@ -12,15 +23,46 @@ type Timeouts struct {
 	// Assert defines the timeout for the assert operation
 	Assert *metav1.Duration `json:"assert,omitempty"`
 
-	// Error defines the timeout for the error operation
-	Error *metav1.Duration `json:"error,omitempty"`
+	// Cleanup defines the timeout for the cleanup operation
+	Cleanup *metav1.Duration `json:"cleanup,omitempty"`
 
 	// Delete defines the timeout for the delete operation
 	Delete *metav1.Duration `json:"delete,omitempty"`
 
-	// Cleanup defines the timeout for the cleanup operation
-	Cleanup *metav1.Duration `json:"cleanup,omitempty"`
+	// Error defines the timeout for the error operation
+	Error *metav1.Duration `json:"error,omitempty"`
 
 	// Exec defines the timeout for exec operations
 	Exec *metav1.Duration `json:"exec,omitempty"`
 }
+
+func durationOrDefault(to *metav1.Duration, def time.Duration) time.Duration {
+	if to != nil {
+		return to.Duration
+	}
+	return def
+}
+
+func (t Timeouts) ApplyDuration() time.Duration {
+	return durationOrDefault(t.Apply, DefaultApplyTimeout)
+}
+
+func (t Timeouts) AssertDuration() time.Duration {
+	return durationOrDefault(t.Assert, DefaultAssertTimeout)
+}
+
+func (t Timeouts) CleanupDuration() time.Duration {
+	return durationOrDefault(t.Cleanup, DefaultCleanupTimeout)
+}
+
+func (t Timeouts) DeleteDuration() time.Duration {
+	return durationOrDefault(t.Delete, DefaultDeleteTimeout)
+}
+
+func (t Timeouts) ErrorDuration() time.Duration {
+	return durationOrDefault(t.Error, DefaultErrorTimeout)
+}
+
+func (t Timeouts) ExecDuration() time.Duration {
+	return durationOrDefault(t.Exec, DefaultExecTimeout)
+}
diff --git a/pkg/apis/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/v1alpha1/zz_generated.deepcopy.go
index 16ea4b0b..c9a3e8bc 100644
--- a/pkg/apis/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/v1alpha1/zz_generated.deepcopy.go
@@ -559,7 +559,11 @@ func (in *Test) DeepCopyObject() runtime.Object {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *TestSpec) DeepCopyInto(out *TestSpec) {
 	*out = *in
-	in.Timeouts.DeepCopyInto(&out.Timeouts)
+	if in.Timeouts != nil {
+		in, out := &in.Timeouts, &out.Timeouts
+		*out = new(Timeouts)
+		(*in).DeepCopyInto(*out)
+	}
 	if in.Skip != nil {
 		in, out := &in.Skip, &out.Skip
 		*out = new(bool)
@@ -642,7 +646,11 @@ func (in *TestStep) DeepCopyObject() runtime.Object {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *TestStepSpec) DeepCopyInto(out *TestStepSpec) {
 	*out = *in
-	in.Timeouts.DeepCopyInto(&out.Timeouts)
+	if in.Timeouts != nil {
+		in, out := &in.Timeouts, &out.Timeouts
+		*out = new(Timeouts)
+		(*in).DeepCopyInto(*out)
+	}
 	if in.SkipDelete != nil {
 		in, out := &in.SkipDelete, &out.SkipDelete
 		*out = new(bool)
@@ -695,8 +703,8 @@ func (in *Timeouts) DeepCopyInto(out *Timeouts) {
 		*out = new(v1.Duration)
 		**out = **in
 	}
-	if in.Error != nil {
-		in, out := &in.Error, &out.Error
+	if in.Cleanup != nil {
+		in, out := &in.Cleanup, &out.Cleanup
 		*out = new(v1.Duration)
 		**out = **in
 	}
@@ -705,8 +713,8 @@ func (in *Timeouts) DeepCopyInto(out *Timeouts) {
 		*out = new(v1.Duration)
 		**out = **in
 	}
-	if in.Cleanup != nil {
-		in, out := &in.Cleanup, &out.Cleanup
+	if in.Error != nil {
+		in, out := &in.Error, &out.Error
 		*out = new(v1.Duration)
 		**out = **in
 	}
diff --git a/pkg/runner/processors/step.go b/pkg/runner/processors/step.go
index d000ad33..6e5d8c8a 100644
--- a/pkg/runner/processors/step.go
+++ b/pkg/runner/processors/step.go
@@ -247,12 +247,13 @@ func (p *stepProcessor) applyOperation(ctx context.Context, op v1alpha1.Apply, t
 		p.stepReport.AddOperation(operationReport)
 	}
 	dryRun := op.DryRun != nil && *op.DryRun
+	timeouts := timeout.Combine(p.config.Timeouts, p.test.Spec.Timeouts, p.step.TestStepSpec.Timeouts)
 	for _, resource := range resources {
 		if err := p.prepareResource(resource); err != nil {
 			return nil, err
 		}
 		ops = append(ops, operation{
-			timeout:   timeout.Get(timeout.DefaultApplyTimeout, p.config.Timeouts.Apply, p.test.Spec.Timeouts.Apply, p.step.TestStepSpec.Timeouts.Apply, to),
+			timeout:   timeout.Get(to, timeouts.ApplyDuration()),
 			operation: opapply.New(p.getClient(dryRun), resource, p.namespacer, p.getCleaner(ctx, dryRun), op.Expect...),
 		})
 	}
@@ -269,9 +270,10 @@ func (p *stepProcessor) assertOperation(ctx context.Context, op v1alpha1.Assert,
 	if p.stepReport != nil {
 		p.stepReport.AddOperation(operationReport)
 	}
+	timeouts := timeout.Combine(p.config.Timeouts, p.test.Spec.Timeouts, p.step.TestStepSpec.Timeouts)
 	for _, resource := range resources {
 		ops = append(ops, operation{
-			timeout:         timeout.Get(timeout.DefaultAssertTimeout, p.config.Timeouts.Assert, p.test.Spec.Timeouts.Assert, p.step.TestStepSpec.Timeouts.Assert, to),
+			timeout:         timeout.Get(to, timeouts.AssertDuration()),
 			operation:       opassert.New(p.client, resource, p.namespacer),
 			operationReport: operationReport,
 		})
@@ -284,8 +286,9 @@ func (p *stepProcessor) commandOperation(ctx context.Context, exec v1alpha1.Comm
 	if p.stepReport != nil {
 		p.stepReport.AddOperation(operationReport)
 	}
+	timeouts := timeout.Combine(p.config.Timeouts, p.test.Spec.Timeouts, p.step.TestStepSpec.Timeouts)
 	return operation{
-		timeout:         timeout.Get(timeout.DefaultExecTimeout, p.config.Timeouts.Exec, p.test.Spec.Timeouts.Exec, p.step.TestStepSpec.Timeouts.Exec, to),
+		timeout:         timeout.Get(to, timeouts.ExecDuration()),
 		operation:       opcommand.New(exec, p.test.BasePath, p.namespacer.GetNamespace()),
 		operationReport: operationReport,
 	}
@@ -302,12 +305,13 @@ func (p *stepProcessor) createOperation(ctx context.Context, op v1alpha1.Create,
 		p.stepReport.AddOperation(operationReport)
 	}
 	dryRun := op.DryRun != nil && *op.DryRun
+	timeouts := timeout.Combine(p.config.Timeouts, p.test.Spec.Timeouts, p.step.TestStepSpec.Timeouts)
 	for _, resource := range resources {
 		if err := p.prepareResource(resource); err != nil {
 			return nil, err
 		}
 		ops = append(ops, operation{
-			timeout:   timeout.Get(timeout.DefaultApplyTimeout, p.config.Timeouts.Apply, p.test.Spec.Timeouts.Apply, p.step.TestStepSpec.Timeouts.Apply, to),
+			timeout:   timeout.Get(to, timeouts.ApplyDuration()),
 			operation: opcreate.New(p.getClient(dryRun), resource, p.namespacer, p.getCleaner(ctx, dryRun), op.Expect...),
 		})
 	}
@@ -325,8 +329,9 @@ func (p *stepProcessor) deleteOperation(ctx context.Context, op v1alpha1.Delete,
 	if p.stepReport != nil {
 		p.stepReport.AddOperation(operationReport)
 	}
+	timeouts := timeout.Combine(p.config.Timeouts, p.test.Spec.Timeouts, p.step.TestStepSpec.Timeouts)
 	return &operation{
-		timeout:         timeout.Get(timeout.DefaultDeleteTimeout, p.config.Timeouts.Delete, p.test.Spec.Timeouts.Delete, p.step.TestStepSpec.Timeouts.Delete, to),
+		timeout:         timeout.Get(to, timeouts.DeleteDuration()),
 		operation:       opdelete.New(p.client, resource, p.namespacer, op.Expect...),
 		operationReport: operationReport,
 	}, nil
@@ -342,9 +347,10 @@ func (p *stepProcessor) errorOperation(ctx context.Context, op v1alpha1.Error, t
 	if p.stepReport != nil {
 		p.stepReport.AddOperation(operationReport)
 	}
+	timeouts := timeout.Combine(p.config.Timeouts, p.test.Spec.Timeouts, p.step.TestStepSpec.Timeouts)
 	for _, resource := range resources {
 		ops = append(ops, operation{
-			timeout:         timeout.Get(timeout.DefaultErrorTimeout, p.config.Timeouts.Error, p.test.Spec.Timeouts.Error, p.step.TestStepSpec.Timeouts.Error, to),
+			timeout:         timeout.Get(to, timeouts.ErrorDuration()),
 			operation:       operror.New(p.client, resource, p.namespacer),
 			operationReport: operationReport,
 		})
@@ -357,8 +363,9 @@ func (p *stepProcessor) scriptOperation(ctx context.Context, exec v1alpha1.Scrip
 	if p.stepReport != nil {
 		p.stepReport.AddOperation(operationReport)
 	}
+	timeouts := timeout.Combine(p.config.Timeouts, p.test.Spec.Timeouts, p.step.TestStepSpec.Timeouts)
 	return operation{
-		timeout:         timeout.Get(timeout.DefaultExecTimeout, p.config.Timeouts.Exec, p.test.Spec.Timeouts.Exec, p.step.TestStepSpec.Timeouts.Exec, to),
+		timeout:         timeout.Get(to, timeouts.ExecDuration()),
 		operation:       opscript.New(exec, p.test.BasePath, p.namespacer.GetNamespace()),
 		operationReport: operationReport,
 	}
@@ -411,9 +418,10 @@ func (p *stepProcessor) getCleaner(ctx context.Context, dryRun bool) cleanup.Cle
 		cleaner = func(obj unstructured.Unstructured, c client.Client) {
 			t := testing.FromContext(ctx)
 			t.Cleanup(func() {
+				timeouts := timeout.Combine(p.config.Timeouts, p.test.Spec.Timeouts, p.step.TestStepSpec.Timeouts)
 				operation := operation{
 					continueOnError: true,
-					timeout:         timeout.Get(timeout.DefaultCleanupTimeout, p.config.Timeouts.Cleanup, p.test.Spec.Timeouts.Cleanup, p.step.TestStepSpec.Timeouts.Cleanup, nil),
+					timeout:         timeouts.DeleteDuration(),
 					operation:       opdelete.New(c, obj, p.namespacer),
 				}
 				operation.execute(ctx)
diff --git a/pkg/runner/timeout/timeout.go b/pkg/runner/timeout/timeout.go
index a680f7b3..163c327e 100644
--- a/pkg/runner/timeout/timeout.go
+++ b/pkg/runner/timeout/timeout.go
@@ -1,38 +1,41 @@
 package timeout
 
 import (
-	"context"
 	"time"
 
+	"github.com/kyverno/chainsaw/pkg/apis/v1alpha1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
-const (
-	DefaultApplyTimeout   = 5 * time.Second
-	DefaultAssertTimeout  = 30 * time.Second
-	DefaultErrorTimeout   = 30 * time.Second
-	DefaultDeleteTimeout  = 15 * time.Second
-	DefaultCleanupTimeout = 30 * time.Second
-	DefaultExecTimeout    = 5 * time.Second
-)
+func Combine(config v1alpha1.Timeouts, next ...*v1alpha1.Timeouts) v1alpha1.Timeouts {
+	for _, next := range next {
+		if next != nil {
+			if next.Apply != nil {
+				config.Apply = next.Apply
+			}
+			if next.Assert != nil {
+				config.Assert = next.Assert
+			}
+			if next.Error != nil {
+				config.Error = next.Error
+			}
+			if next.Delete != nil {
+				config.Delete = next.Delete
+			}
+			if next.Cleanup != nil {
+				config.Cleanup = next.Cleanup
+			}
+			if next.Exec != nil {
+				config.Exec = next.Exec
+			}
+		}
+	}
+	return config
+}
 
-func Get(fallback time.Duration, config *metav1.Duration, test *metav1.Duration, step *metav1.Duration, operation *metav1.Duration) time.Duration {
+func Get(operation *metav1.Duration, fallback time.Duration) time.Duration {
 	if operation != nil {
 		return operation.Duration
 	}
-	if step != nil {
-		return step.Duration
-	}
-	if test != nil {
-		return test.Duration
-	}
-	if config != nil {
-		return config.Duration
-	}
 	return fallback
 }
-
-func Context(ctx context.Context, fallback time.Duration, config *metav1.Duration, test *metav1.Duration, step *metav1.Duration, operation *metav1.Duration) (context.Context, context.CancelFunc) {
-	timeout := Get(fallback, config, test, step, operation)
-	return context.WithTimeout(ctx, timeout)
-}
diff --git a/website/docs/apis/chainsaw.v1alpha1.md b/website/docs/apis/chainsaw.v1alpha1.md
index 7dda1d84..c2502baa 100644
--- a/website/docs/apis/chainsaw.v1alpha1.md
+++ b/website/docs/apis/chainsaw.v1alpha1.md
@@ -433,9 +433,9 @@ For multiple objects use labels.</p>
 |---|---|---|---|---|
 | `apply` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) | :white_check_mark: |  | <p>Apply defines the timeout for the apply operation</p> |
 | `assert` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) | :white_check_mark: |  | <p>Assert defines the timeout for the assert operation</p> |
-| `error` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) | :white_check_mark: |  | <p>Error defines the timeout for the error operation</p> |
-| `delete` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) | :white_check_mark: |  | <p>Delete defines the timeout for the delete operation</p> |
 | `cleanup` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) | :white_check_mark: |  | <p>Cleanup defines the timeout for the cleanup operation</p> |
+| `delete` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) | :white_check_mark: |  | <p>Delete defines the timeout for the delete operation</p> |
+| `error` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) | :white_check_mark: |  | <p>Error defines the timeout for the error operation</p> |
 | `exec` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) | :white_check_mark: |  | <p>Exec defines the timeout for exec operations</p> |
 
   
\ No newline at end of file


Test patch:
diff --git a/pkg/apis/v1alpha1/test_spec.go b/pkg/apis/v1alpha1/test_spec.go
index 54b10bd4..ae5ae96c 100644
--- a/pkg/apis/v1alpha1/test_spec.go
+++ b/pkg/apis/v1alpha1/test_spec.go
@@ -8,7 +8,7 @@ type TestSpec struct {
 
 	// Timeouts for the test. Overrides the global timeouts set in the Configuration on a per operation basis.
 	// +optional
-	Timeouts Timeouts `json:"timeouts"`
+	Timeouts *Timeouts `json:"timeouts,omitempty"`
 
 	// Skip determines whether the test should skipped.
 	// +optional
diff --git a/pkg/apis/v1alpha1/test_step_spec.go b/pkg/apis/v1alpha1/test_step_spec.go
index ce7dc077..22d9b3a9 100644
--- a/pkg/apis/v1alpha1/test_step_spec.go
+++ b/pkg/apis/v1alpha1/test_step_spec.go
@@ -8,7 +8,7 @@ type TestStepSpec struct {
 
 	// Timeouts for the test step. Overrides the global timeouts set in the Configuration and the timeouts eventually set in the Test.
 	// +optional
-	Timeouts Timeouts `json:"timeouts"`
+	Timeouts *Timeouts `json:"timeouts,omitempty"`
 
 	// SkipDelete determines whether the resources created by the step should be deleted after the test step is executed.
 	// +optional
diff --git a/pkg/commands/kuttl/migrate/tests/command.go b/pkg/commands/kuttl/migrate/tests/command.go
index 6a379d8e..1990ae57 100644
--- a/pkg/commands/kuttl/migrate/tests/command.go
+++ b/pkg/commands/kuttl/migrate/tests/command.go
@@ -262,25 +262,17 @@ func testStep(to *v1alpha1.TestStepSpec, in unstructured.Unstructured) error {
 		return err
 	}
 	for _, operation := range from.Apply {
-		action := &v1alpha1.Apply{
-			FileRefOrResource: v1alpha1.FileRefOrResource{
-				FileRef: v1alpha1.FileRef{
-					File: operation.File,
-				},
-			},
-		}
-		if operation.ShouldFail {
-			action.Expect = []v1alpha1.Expectation{{
-				Check: v1alpha1.Check{
-					Value: map[string]interface{}{
-						"($error != null)": true,
-					},
-				},
-			}}
-		}
 		to.Try = append(
 			to.Try,
-			v1alpha1.Operation{Apply: action},
+			v1alpha1.Operation{
+				Apply: &v1alpha1.Apply{
+					FileRefOrResource: v1alpha1.FileRefOrResource{
+						FileRef: v1alpha1.FileRef{
+							File: operation,
+						},
+					},
+				},
+			},
 		)
 	}
 	for _, operation := range from.Assert {
@@ -288,7 +280,7 @@ func testStep(to *v1alpha1.TestStepSpec, in unstructured.Unstructured) error {
 			Assert: &v1alpha1.Assert{
 				FileRefOrResource: v1alpha1.FileRefOrResource{
 					FileRef: v1alpha1.FileRef{
-						File: operation.File,
+						File: operation,
 					},
 				},
 			},
diff --git a/pkg/commands/test/command.go b/pkg/commands/test/command.go
index e36c6d5c..cb8fe0a4 100644
--- a/pkg/commands/test/command.go
+++ b/pkg/commands/test/command.go
@@ -12,7 +12,6 @@ import (
 	"github.com/kyverno/chainsaw/pkg/data"
 	"github.com/kyverno/chainsaw/pkg/discovery"
 	"github.com/kyverno/chainsaw/pkg/runner"
-	"github.com/kyverno/chainsaw/pkg/runner/timeout"
 	flagutils "github.com/kyverno/chainsaw/pkg/utils/flag"
 	restutils "github.com/kyverno/chainsaw/pkg/utils/rest"
 	"github.com/kyverno/kyverno/ext/output/color"
@@ -155,30 +154,18 @@ func Command() *cobra.Command {
 			fmt.Fprintf(out, "- FullName %v\n", configuration.Spec.FullName)
 			fmt.Fprintf(out, "- IncludeTestRegex '%v'\n", configuration.Spec.IncludeTestRegex)
 			fmt.Fprintf(out, "- ExcludeTestRegex '%v'\n", configuration.Spec.ExcludeTestRegex)
+			fmt.Fprintf(out, "- ApplyTimeout %v\n", configuration.Spec.Timeouts.ApplyDuration())
+			fmt.Fprintf(out, "- AssertTimeout %v\n", configuration.Spec.Timeouts.AssertDuration())
+			fmt.Fprintf(out, "- CleanupTimeout %v\n", configuration.Spec.Timeouts.CleanupDuration())
+			fmt.Fprintf(out, "- DeleteTimeout %v\n", configuration.Spec.Timeouts.DeleteDuration())
+			fmt.Fprintf(out, "- ErrorTimeout %v\n", configuration.Spec.Timeouts.ErrorDuration())
+			fmt.Fprintf(out, "- ExecTimeout %v\n", configuration.Spec.Timeouts.ExecDuration())
 			if configuration.Spec.Parallel != nil && *configuration.Spec.Parallel > 0 {
 				fmt.Fprintf(out, "- Parallel %d\n", *configuration.Spec.Parallel)
 			}
 			if configuration.Spec.RepeatCount != nil {
 				fmt.Fprintf(out, "- RepeatCount %v\n", *configuration.Spec.RepeatCount)
 			}
-			if configuration.Spec.Timeouts.Apply != nil {
-				fmt.Fprintf(out, "- ApplyTimeout %v\n", configuration.Spec.Timeouts.Apply.Duration)
-			}
-			if configuration.Spec.Timeouts.Assert != nil {
-				fmt.Fprintf(out, "- AssertTimeout %v\n", configuration.Spec.Timeouts.Assert.Duration)
-			}
-			if configuration.Spec.Timeouts.Error != nil {
-				fmt.Fprintf(out, "- ErrorTimeout %v\n", configuration.Spec.Timeouts.Error.Duration)
-			}
-			if configuration.Spec.Timeouts.Delete != nil {
-				fmt.Fprintf(out, "- DeleteTimeout %v\n", configuration.Spec.Timeouts.Delete.Duration)
-			}
-			if configuration.Spec.Timeouts.Cleanup != nil {
-				fmt.Fprintf(out, "- CleanupTimeout %v\n", configuration.Spec.Timeouts.Cleanup.Duration)
-			}
-			if configuration.Spec.Timeouts.Exec != nil {
-				fmt.Fprintf(out, "- ExecTimeout %v\n", configuration.Spec.Timeouts.Exec.Duration)
-			}
 			if configuration.Spec.ForceTerminationGracePeriod != nil {
 				fmt.Fprintf(out, "- ForceTerminationGracePeriod %v\n", configuration.Spec.ForceTerminationGracePeriod.Duration)
 			}
@@ -222,12 +209,12 @@ func Command() *cobra.Command {
 		},
 	}
 	cmd.Flags().StringVar(&options.testFile, "test-file", "chainsaw-test.yaml", "Name of the test file.")
-	cmd.Flags().DurationVar(&options.applyTimeout.Duration, "apply-timeout", timeout.DefaultApplyTimeout, "The apply timeout to use as default for configuration.")
-	cmd.Flags().DurationVar(&options.assertTimeout.Duration, "assert-timeout", timeout.DefaultAssertTimeout, "The assert timeout to use as default for configuration.")
-	cmd.Flags().DurationVar(&options.errorTimeout.Duration, "error-timeout", timeout.DefaultErrorTimeout, "The error timeout to use as default for configuration.")
-	cmd.Flags().DurationVar(&options.deleteTimeout.Duration, "delete-timeout", timeout.DefaultDeleteTimeout, "The delete timeout to use as default for configuration.")
-	cmd.Flags().DurationVar(&options.cleanupTimeout.Duration, "cleanup-timeout", timeout.DefaultCleanupTimeout, "The cleanup timeout to use as default for configuration.")
-	cmd.Flags().DurationVar(&options.execTimeout.Duration, "exec-timeout", timeout.DefaultExecTimeout, "The exec timeout to use as default for configuration.")
+	cmd.Flags().DurationVar(&options.applyTimeout.Duration, "apply-timeout", v1alpha1.DefaultApplyTimeout, "The apply timeout to use as default for configuration.")
+	cmd.Flags().DurationVar(&options.assertTimeout.Duration, "assert-timeout", v1alpha1.DefaultAssertTimeout, "The assert timeout to use as default for configuration.")
+	cmd.Flags().DurationVar(&options.errorTimeout.Duration, "error-timeout", v1alpha1.DefaultErrorTimeout, "The error timeout to use as default for configuration.")
+	cmd.Flags().DurationVar(&options.deleteTimeout.Duration, "delete-timeout", v1alpha1.DefaultDeleteTimeout, "The delete timeout to use as default for configuration.")
+	cmd.Flags().DurationVar(&options.cleanupTimeout.Duration, "cleanup-timeout", v1alpha1.DefaultCleanupTimeout, "The cleanup timeout to use as default for configuration.")
+	cmd.Flags().DurationVar(&options.execTimeout.Duration, "exec-timeout", v1alpha1.DefaultExecTimeout, "The exec timeout to use as default for configuration.")
 	cmd.Flags().StringVar(&options.config, "config", "", "Chainsaw configuration file.")
 	cmd.Flags().StringArrayVar(&options.testDirs, "test-dir", []string{}, "Directories containing test cases to run.")
 	cmd.Flags().BoolVar(&options.skipDelete, "skip-delete", false, "If set, do not delete the resources after running the tests.")
diff --git a/pkg/runner/processors/test.go b/pkg/runner/processors/test.go
index 6303df3b..32927f8e 100644
--- a/pkg/runner/processors/test.go
+++ b/pkg/runner/processors/test.go
@@ -121,9 +121,10 @@ func (p *testProcessor) Run(ctx context.Context, nspacer namespacer.Namespacer)
 				t.FailNow()
 			}
 			t.Cleanup(func() {
+				timeouts := timeout.Combine(p.config.Timeouts, p.test.Spec.Timeouts)
 				operation := operation{
 					continueOnError: false,
-					timeout:         timeout.Get(timeout.DefaultCleanupTimeout, p.config.Timeouts.Cleanup, p.test.Spec.Timeouts.Cleanup, nil, nil),
+					timeout:         timeouts.CleanupDuration(),
 					operation:       opdelete.New(p.client, client.ToUnstructured(namespace), nspacer),
 				}
 				operation.execute(ctx)
diff --git a/pkg/runner/processors/tests.go b/pkg/runner/processors/tests.go
index aa61877e..89f9628c 100644
--- a/pkg/runner/processors/tests.go
+++ b/pkg/runner/processors/tests.go
@@ -13,7 +13,6 @@ import (
 	"github.com/kyverno/chainsaw/pkg/runner/namespacer"
 	opdelete "github.com/kyverno/chainsaw/pkg/runner/operations/delete"
 	"github.com/kyverno/chainsaw/pkg/runner/summary"
-	"github.com/kyverno/chainsaw/pkg/runner/timeout"
 	"github.com/kyverno/chainsaw/pkg/testing"
 	"github.com/kyverno/kyverno/ext/output/color"
 	"k8s.io/apimachinery/pkg/api/errors"
@@ -73,7 +72,7 @@ func (p *testsProcessor) Run(ctx context.Context) {
 			t.Cleanup(func() {
 				operation := operation{
 					continueOnError: false,
-					timeout:         timeout.Get(timeout.DefaultCleanupTimeout, p.config.Timeouts.Cleanup, nil, nil, nil),
+					timeout:         p.config.Timeouts.CleanupDuration(),
 					operation:       opdelete.New(p.client, client.ToUnstructured(namespace), nspacer),
 				}
 				operation.execute(ctx)
diff --git a/pkg/runner/timeout/timeout_test.go b/pkg/runner/timeout/timeout_test.go
index 267d0290..dbf206f0 100644
--- a/pkg/runner/timeout/timeout_test.go
+++ b/pkg/runner/timeout/timeout_test.go
@@ -1,7 +1,6 @@
 package timeout
 
 import (
-	"context"
 	"testing"
 	"time"
 
@@ -10,125 +9,28 @@ import (
 )
 
 func TestGet(t *testing.T) {
-	config := metav1.Duration{Duration: 1 * time.Minute}
-	test := metav1.Duration{Duration: 2 * time.Minute}
-	step := metav1.Duration{Duration: 3 * time.Minute}
 	operation := metav1.Duration{Duration: 4 * time.Minute}
 	fallback := 10 * time.Second
 	tests := []struct {
 		name      string
 		fallback  time.Duration
-		config    *metav1.Duration
-		test      *metav1.Duration
-		step      *metav1.Duration
 		operation *metav1.Duration
 		want      time.Duration
 	}{{
-		name:      "none",
+		name:      "fallback",
 		fallback:  fallback,
-		config:    nil,
-		test:      nil,
-		step:      nil,
 		operation: nil,
 		want:      fallback,
 	}, {
-		name:      "from config",
+		name:      "operation",
 		fallback:  fallback,
-		config:    &config,
-		test:      nil,
-		step:      nil,
-		operation: nil,
-		want:      config.Duration,
-	}, {
-		name:      "from test",
-		fallback:  fallback,
-		config:    &config,
-		test:      &test,
-		step:      nil,
-		operation: nil,
-		want:      test.Duration,
-	}, {
-		name:      "from step",
-		fallback:  fallback,
-		config:    &config,
-		test:      &test,
-		step:      &step,
-		operation: nil,
-		want:      step.Duration,
-	}, {
-		name:      "from operation",
-		fallback:  fallback,
-		config:    &config,
-		test:      &test,
-		step:      &step,
 		operation: &operation,
 		want:      operation.Duration,
 	}}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got := Get(tt.fallback, tt.config, tt.test, tt.step, tt.operation)
+			got := Get(tt.operation, tt.fallback)
 			assert.Equal(t, tt.want, got)
 		})
 	}
 }
-
-func TestContext(t *testing.T) {
-	config := metav1.Duration{Duration: 1 * time.Minute}
-	test := metav1.Duration{Duration: 2 * time.Minute}
-	step := metav1.Duration{Duration: 3 * time.Minute}
-	operation := metav1.Duration{Duration: 4 * time.Minute}
-	fallback := 10 * time.Second
-	tests := []struct {
-		name      string
-		fallback  time.Duration
-		config    *metav1.Duration
-		test      *metav1.Duration
-		step      *metav1.Duration
-		operation *metav1.Duration
-	}{{
-		name:      "none",
-		fallback:  fallback,
-		config:    nil,
-		test:      nil,
-		step:      nil,
-		operation: nil,
-	}, {
-		name:      "from config",
-		fallback:  fallback,
-		config:    &config,
-		test:      nil,
-		step:      nil,
-		operation: nil,
-	}, {
-		name:      "from test",
-		fallback:  fallback,
-		config:    &config,
-		test:      &test,
-		step:      nil,
-		operation: nil,
-	}, {
-		name:      "from step",
-		fallback:  fallback,
-		config:    &config,
-		test:      &test,
-		step:      &step,
-		operation: nil,
-	}, {
-		name:      "from operation",
-		fallback:  fallback,
-		config:    &config,
-		test:      &test,
-		step:      &step,
-		operation: &operation,
-	}}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			got, cancel := Context(context.Background(), tt.fallback, tt.config, tt.test, tt.step, tt.operation)
-			defer cancel()
-			assert.NotNil(t, got)
-			assert.NotNil(t, cancel)
-			_, ok := got.Deadline()
-			assert.Equal(t, true, ok)
-		})
-	}
-}
diff --git a/testdata/commands/kuttl/migrate/tests/out.txt b/testdata/commands/kuttl/migrate/tests/out.txt
index bd156a18..743bffc7 100644
--- a/testdata/commands/kuttl/migrate/tests/out.txt
+++ b/testdata/commands/kuttl/migrate/tests/out.txt
@@ -14,7 +14,6 @@ spec:
     - podLogs:
         name: nginx
     name: step-01
-    timeouts: {}
     try:
     - delete:
         ref:
@@ -49,7 +48,6 @@ spec:
         - "1"
         entrypoint: sleep
     name: step-02
-    timeouts: {}
     try:
     - delete:
         ref:
@@ -74,5 +72,4 @@ spec:
         - hello world
         entrypoint: echo
         skipLogOutput: true
-  timeouts: {}
 
diff --git a/testdata/commands/test/all_flags.txt b/testdata/commands/test/all_flags.txt
index b1820947..191d3bbf 100644
--- a/testdata/commands/test/all_flags.txt
+++ b/testdata/commands/test/all_flags.txt
@@ -9,14 +9,14 @@ Loading default configuration...
 - FullName true
 - IncludeTestRegex '^.*$'
 - ExcludeTestRegex '^.*$'
-- Parallel 24
-- RepeatCount 12
 - ApplyTimeout 1m40s
 - AssertTimeout 1m40s
-- ErrorTimeout 1m40s
-- DeleteTimeout 1m40s
 - CleanupTimeout 1m40s
+- DeleteTimeout 1m40s
+- ErrorTimeout 1m40s
 - ExecTimeout 1m40s
+- Parallel 24
+- RepeatCount 12
 - ForceTerminationGracePeriod 5s
 Loading tests...
 Running tests...
diff --git a/testdata/commands/test/config_all_fields.txt b/testdata/commands/test/config_all_fields.txt
index e3734baa..5d468302 100644
--- a/testdata/commands/test/config_all_fields.txt
+++ b/testdata/commands/test/config_all_fields.txt
@@ -9,13 +9,13 @@ Loading config (../../../testdata/commands/test/config/config_all_fields.yaml)..
 - FullName true
 - IncludeTestRegex '^include-.*'
 - ExcludeTestRegex '^exclude-.*'
-- Parallel 5
 - ApplyTimeout 5s
 - AssertTimeout 10s
-- ErrorTimeout 10s
-- DeleteTimeout 5s
 - CleanupTimeout 5s
+- DeleteTimeout 5s
+- ErrorTimeout 10s
 - ExecTimeout 10s
+- Parallel 5
 Loading tests...
 Running tests...
 Tests Summary...
diff --git a/testdata/commands/test/default.txt b/testdata/commands/test/default.txt
index 1bea6f2a..a45ebca5 100644
--- a/testdata/commands/test/default.txt
+++ b/testdata/commands/test/default.txt
@@ -9,6 +9,12 @@ Loading default configuration...
 - FullName false
 - IncludeTestRegex ''
 - ExcludeTestRegex ''
+- ApplyTimeout 5s
+- AssertTimeout 30s
+- CleanupTimeout 30s
+- DeleteTimeout 15s
+- ErrorTimeout 30s
+- ExecTimeout 5s
 Loading tests...
 Running tests...
 Tests Summary...
diff --git a/testdata/commands/test/with_regex.txt b/testdata/commands/test/with_regex.txt
index bf294f60..e38742dd 100644
--- a/testdata/commands/test/with_regex.txt
+++ b/testdata/commands/test/with_regex.txt
@@ -9,6 +9,12 @@ Loading default configuration...
 - FullName false
 - IncludeTestRegex 'test[4-6]'
 - ExcludeTestRegex 'test[1-3]'
+- ApplyTimeout 5s
+- AssertTimeout 30s
+- CleanupTimeout 30s
+- DeleteTimeout 15s
+- ErrorTimeout 30s
+- ExecTimeout 5s
 Loading tests...
 Running tests...
 Tests Summary...
diff --git a/testdata/commands/test/with_repeat_count.txt b/testdata/commands/test/with_repeat_count.txt
index ee21269b..b9bf4c5f 100644
--- a/testdata/commands/test/with_repeat_count.txt
+++ b/testdata/commands/test/with_repeat_count.txt
@@ -9,6 +9,12 @@ Loading default configuration...
 - FullName false
 - IncludeTestRegex ''
 - ExcludeTestRegex ''
+- ApplyTimeout 5s
+- AssertTimeout 30s
+- CleanupTimeout 30s
+- DeleteTimeout 15s
+- ErrorTimeout 30s
+- ExecTimeout 5s
 - RepeatCount 3
 Loading tests...
 Running tests...
diff --git a/testdata/commands/test/with_suppress.txt b/testdata/commands/test/with_suppress.txt
index 4ace357b..658d368b 100644
--- a/testdata/commands/test/with_suppress.txt
+++ b/testdata/commands/test/with_suppress.txt
@@ -10,6 +10,12 @@ Loading default configuration...
 - FullName false
 - IncludeTestRegex ''
 - ExcludeTestRegex ''
+- ApplyTimeout 5s
+- AssertTimeout 30s
+- CleanupTimeout 30s
+- DeleteTimeout 15s
+- ErrorTimeout 30s
+- ExecTimeout 5s
 Loading tests...
 Running tests...
 Tests Summary...
diff --git a/testdata/commands/test/with_test_dirs.txt b/testdata/commands/test/with_test_dirs.txt
index 74540994..e27f6460 100644
--- a/testdata/commands/test/with_test_dirs.txt
+++ b/testdata/commands/test/with_test_dirs.txt
@@ -9,6 +9,12 @@ Loading default configuration...
 - FullName false
 - IncludeTestRegex ''
 - ExcludeTestRegex ''
+- ApplyTimeout 5s
+- AssertTimeout 30s
+- CleanupTimeout 30s
+- DeleteTimeout 15s
+- ErrorTimeout 30s
+- ExecTimeout 5s
 Loading tests...
 Running tests...
 Tests Summary...
diff --git a/testdata/commands/test/with_timeout.txt b/testdata/commands/test/with_timeout.txt
index 878de64b..f7eeded6 100644
--- a/testdata/commands/test/with_timeout.txt
+++ b/testdata/commands/test/with_timeout.txt
@@ -10,6 +10,11 @@ Loading default configuration...
 - IncludeTestRegex ''
 - ExcludeTestRegex ''
 - ApplyTimeout 10s
+- AssertTimeout 30s
+- CleanupTimeout 30s
+- DeleteTimeout 15s
+- ErrorTimeout 30s
+- ExecTimeout 5s
 Loading tests...
 Running tests...
 Tests Summary...
diff --git a/testdata/kuttl/chainsaw-test.yaml b/testdata/kuttl/chainsaw-test.yaml
index ba87b36a..1af8115b 100755
--- a/testdata/kuttl/chainsaw-test.yaml
+++ b/testdata/kuttl/chainsaw-test.yaml
@@ -13,7 +13,6 @@ spec:
     - podLogs:
         name: nginx
     name: step-01
-    timeouts: {}
     try:
     - delete:
         ref:
@@ -48,7 +47,6 @@ spec:
         - "1"
         entrypoint: sleep
     name: step-02
-    timeouts: {}
     try:
     - delete:
         ref:
@@ -73,4 +71,3 @@ spec:
         - hello world
         entrypoint: echo
         skipLogOutput: true
-  timeouts: {}