Repository: kyverno/chainsaw
Pull request #567 (2023-12-01 23:27:49)

refactor: timeout management
Refactor timeout management.
Timeouts were moved inside operations that support timeout.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/kyverno/chainsaw/pull/567?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) Report
Attention: `19 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`ad2691a`)](https://app.codecov.io/gh/kyverno/chainsaw/commit/ad2691a0265d630de5a10f3c9f7e8a761a04679d?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) 86.08% compared to head [(`cd3dc1a`)](https://app.codecov.io/gh/kyverno/chainsaw/pull/567?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) 85.33%.

| [Files](https://app.codecov.io/gh/kyverno/chainsaw/pull/567?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) | Patch % | Lines |
|---|---|---|
| [pkg/runner/operations/internal/log.go](https://app.codecov.io/gh/kyverno/chainsaw/pull/567?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno#diff-cGtnL3J1bm5lci9vcGVyYXRpb25zL2ludGVybmFsL2xvZy5nbw==) | 0.00% | [10 Missing :warning: ](https://app.codecov.io/gh/kyverno/chainsaw/pull/567?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) |
| [pkg/runner/operations/internal/namespace.go](https://app.codecov.io/gh/kyverno/chainsaw/pull/567?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno#diff-cGtnL3J1bm5lci9vcGVyYXRpb25zL2ludGVybmFsL25hbWVzcGFjZS5nbw==) | 0.00% | [5 Missing :warning: ](https://app.codecov.io/gh/kyverno/chainsaw/pull/567?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) |
| [pkg/runner/operations/apply/apply.go](https://app.codecov.io/gh/kyverno/chainsaw/pull/567?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno#diff-cGtnL3J1bm5lci9vcGVyYXRpb25zL2FwcGx5L2FwcGx5Lmdv) | 60.00% | [1 Missing and 1 partial :warning: ](https://app.codecov.io/gh/kyverno/chainsaw/pull/567?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) |
| [pkg/runner/operations/delete/delete.go](https://app.codecov.io/gh/kyverno/chainsaw/pull/567?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno#diff-cGtnL3J1bm5lci9vcGVyYXRpb25zL2RlbGV0ZS9kZWxldGUuZ28=) | 71.42% | [1 Missing and 1 partial :warning: ](https://app.codecov.io/gh/kyverno/chainsaw/pull/567?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     #567      +/-   ##
==========================================
- Coverage   86.08%   85.33%   -0.76%     
==========================================
  Files          78       79       +1     
  Lines        2414     2386      -28     
==========================================
- Hits         2078     2036      -42     
- Misses        274      288      +14     
  Partials       62       62              
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/kyverno/chainsaw/pull/567?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 b335f4b1..191400da 100644
--- a/.release-notes/main.md
+++ b/.release-notes/main.md
@@ -17,6 +17,7 @@ Release notes for `TODO`.
 
 - Changed `check` to `expect` in delete `operation`
 - Kuttl migration command split into `chainsaw kuttl migrate tests` and `chainsaw kuttl migrate config`
+- `timeout` was moved inside operations that support a timeout (all operations but sleep)
 
 ## :dizzy: New features :dizzy:
 
diff --git a/pkg/apis/v1alpha1/apply.go b/pkg/apis/v1alpha1/apply.go
index c69cb545..ef0e9cf8 100644
--- a/pkg/apis/v1alpha1/apply.go
+++ b/pkg/apis/v1alpha1/apply.go
@@ -1,8 +1,16 @@
 package v1alpha1
 
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
 // Apply represents a set of configurations or resources that
 // should be applied during testing.
 type Apply struct {
+	// Timeout for the operation. Overrides the global timeout set in the Configuration.
+	// +optional
+	Timeout *metav1.Duration `json:"timeout,omitempty"`
+
 	// FileRefOrResource provides a reference to the resources to be applied.
 	FileRefOrResource `json:",inline"`
 
diff --git a/pkg/apis/v1alpha1/assert.go b/pkg/apis/v1alpha1/assert.go
index f28d391d..01dde2fc 100644
--- a/pkg/apis/v1alpha1/assert.go
+++ b/pkg/apis/v1alpha1/assert.go
@@ -1,8 +1,16 @@
 package v1alpha1
 
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
 // Assert represents a test condition that is expected to hold true
 // during the testing process.
 type Assert struct {
+	// Timeout for the operation. Overrides the global timeout set in the Configuration.
+	// +optional
+	Timeout *metav1.Duration `json:"timeout,omitempty"`
+
 	// FileRefOrResource provides a reference to the assertion.
 	FileRefOrResource `json:",inline"`
 }
diff --git a/pkg/apis/v1alpha1/catch.go b/pkg/apis/v1alpha1/catch.go
index cf1f8068..40f875ea 100644
--- a/pkg/apis/v1alpha1/catch.go
+++ b/pkg/apis/v1alpha1/catch.go
@@ -1,19 +1,11 @@
 package v1alpha1
 
-import (
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-)
-
 // Catch defines actions to be executed on failure.
 type Catch struct {
 	// Description contains a description of the operation.
 	// +optional
 	Description string `json:"description,omitempty"`
 
-	// Timeout for the operation. Overrides the global timeout set in the Configuration.
-	// +optional
-	Timeout *metav1.Duration `json:"timeout,omitempty"`
-
 	// PodLogs determines the pod logs collector to execute.
 	// +optional
 	PodLogs *PodLogs `json:"podLogs,omitempty"`
diff --git a/pkg/apis/v1alpha1/command.go b/pkg/apis/v1alpha1/command.go
index de2a6dff..d2446594 100644
--- a/pkg/apis/v1alpha1/command.go
+++ b/pkg/apis/v1alpha1/command.go
@@ -1,7 +1,15 @@
 package v1alpha1
 
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
 // Command describes a command to run as a part of a test step.
 type Command struct {
+	// Timeout for the operation. Overrides the global timeout set in the Configuration.
+	// +optional
+	Timeout *metav1.Duration `json:"timeout,omitempty"`
+
 	// Entrypoint is the command entry point to run.
 	Entrypoint string `json:"entrypoint"`
 
diff --git a/pkg/apis/v1alpha1/create.go b/pkg/apis/v1alpha1/create.go
index 6e1b0254..03321279 100644
--- a/pkg/apis/v1alpha1/create.go
+++ b/pkg/apis/v1alpha1/create.go
@@ -1,8 +1,16 @@
 package v1alpha1
 
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
 // Create represents a set of resources that should be created.
 // If a resource already exists in the cluster it will fail.
 type Create struct {
+	// Timeout for the operation. Overrides the global timeout set in the Configuration.
+	// +optional
+	Timeout *metav1.Duration `json:"timeout,omitempty"`
+
 	// FileRefOrResource provides a reference to the file containing the resources to be created.
 	FileRefOrResource `json:",inline"`
 
diff --git a/pkg/apis/v1alpha1/delete.go b/pkg/apis/v1alpha1/delete.go
index 2bf301b9..74804fed 100644
--- a/pkg/apis/v1alpha1/delete.go
+++ b/pkg/apis/v1alpha1/delete.go
@@ -1,7 +1,15 @@
 package v1alpha1
 
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
 // Delete is a reference to an object that should be deleted
 type Delete struct {
+	// Timeout for the operation. Overrides the global timeout set in the Configuration.
+	// +optional
+	Timeout *metav1.Duration `json:"timeout,omitempty"`
+
 	// ObjectReference determines objects to be deleted.
 	ObjectReference `json:"ref"`
 
diff --git a/pkg/apis/v1alpha1/error.go b/pkg/apis/v1alpha1/error.go
index daa36508..247cda5e 100644
--- a/pkg/apis/v1alpha1/error.go
+++ b/pkg/apis/v1alpha1/error.go
@@ -1,8 +1,16 @@
 package v1alpha1
 
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
 // Error represents an anticipated error condition that may arise during testing.
 // Instead of treating such an error as a test failure, it acknowledges it as expected.
 type Error struct {
+	// Timeout for the operation. Overrides the global timeout set in the Configuration.
+	// +optional
+	Timeout *metav1.Duration `json:"timeout,omitempty"`
+
 	// FileRefOrResource provides a reference to the expected error.
 	FileRefOrResource `json:",inline"`
 }
diff --git a/pkg/apis/v1alpha1/events.go b/pkg/apis/v1alpha1/events.go
index 8bcdc0f6..faee9574 100644
--- a/pkg/apis/v1alpha1/events.go
+++ b/pkg/apis/v1alpha1/events.go
@@ -1,7 +1,15 @@
 package v1alpha1
 
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
 // Events defines how to collects events.
 type Events struct {
+	// Timeout for the operation. Overrides the global timeout set in the Configuration.
+	// +optional
+	Timeout *metav1.Duration `json:"timeout,omitempty"`
+
 	// Namespace of the referent.
 	// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
 	// +optional
diff --git a/pkg/apis/v1alpha1/finally.go b/pkg/apis/v1alpha1/finally.go
index 3e5ad7c2..aaf7e0d4 100644
--- a/pkg/apis/v1alpha1/finally.go
+++ b/pkg/apis/v1alpha1/finally.go
@@ -1,19 +1,11 @@
 package v1alpha1
 
-import (
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-)
-
 // Finally defines actions to be executed at the end of a test.
 type Finally struct {
 	// Description contains a description of the operation.
 	// +optional
 	Description string `json:"description,omitempty"`
 
-	// Timeout for the operation. Overrides the global timeout set in the Configuration.
-	// +optional
-	Timeout *metav1.Duration `json:"timeout,omitempty"`
-
 	// PodLogs determines the pod logs collector to execute.
 	// +optional
 	PodLogs *PodLogs `json:"podLogs,omitempty"`
diff --git a/pkg/apis/v1alpha1/operation.go b/pkg/apis/v1alpha1/operation.go
index 07fd046e..5a827c6f 100644
--- a/pkg/apis/v1alpha1/operation.go
+++ b/pkg/apis/v1alpha1/operation.go
@@ -1,19 +1,11 @@
 package v1alpha1
 
-import (
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-)
-
 // Operation defines a single operation, only one action is permitted for a given operation.
 type Operation struct {
 	// Description contains a description of the operation.
 	// +optional
 	Description string `json:"description,omitempty"`
 
-	// Timeout for the operation. Overrides the global timeout set in the Configuration.
-	// +optional
-	Timeout *metav1.Duration `json:"timeout,omitempty"`
-
 	// ContinueOnError determines whether a test should continue or not in case the operation was not successful.
 	// Even if the test continues executing, it will still be reported as failed.
 	// +optional
diff --git a/pkg/apis/v1alpha1/pod_logs.go b/pkg/apis/v1alpha1/pod_logs.go
index c9dccd63..60d88fa4 100644
--- a/pkg/apis/v1alpha1/pod_logs.go
+++ b/pkg/apis/v1alpha1/pod_logs.go
@@ -1,7 +1,15 @@
 package v1alpha1
 
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
 // PodLogs defines how to collects pod logs.
 type PodLogs struct {
+	// Timeout for the operation. Overrides the global timeout set in the Configuration.
+	// +optional
+	Timeout *metav1.Duration `json:"timeout,omitempty"`
+
 	// Namespace of the referent.
 	// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
 	// +optional
diff --git a/pkg/apis/v1alpha1/script.go b/pkg/apis/v1alpha1/script.go
index 55adec2b..707ec84e 100644
--- a/pkg/apis/v1alpha1/script.go
+++ b/pkg/apis/v1alpha1/script.go
@@ -1,7 +1,15 @@
 package v1alpha1
 
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
 // Script describes a script to run as a part of a test step.
 type Script struct {
+	// Timeout for the operation. Overrides the global timeout set in the Configuration.
+	// +optional
+	Timeout *metav1.Duration `json:"timeout,omitempty"`
+
 	// Content defines a shell script (run with "sh -c ...").
 	// +optional
 	Content string `json:"content,omitempty"`
diff --git a/pkg/apis/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/v1alpha1/zz_generated.deepcopy.go
index f11b659f..a5772565 100644
--- a/pkg/apis/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/v1alpha1/zz_generated.deepcopy.go
@@ -29,6 +29,11 @@ import (
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Apply) DeepCopyInto(out *Apply) {
 	*out = *in
+	if in.Timeout != nil {
+		in, out := &in.Timeout, &out.Timeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
 	in.FileRefOrResource.DeepCopyInto(&out.FileRefOrResource)
 	if in.DryRun != nil {
 		in, out := &in.DryRun, &out.DryRun
@@ -58,6 +63,11 @@ func (in *Apply) DeepCopy() *Apply {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Assert) DeepCopyInto(out *Assert) {
 	*out = *in
+	if in.Timeout != nil {
+		in, out := &in.Timeout, &out.Timeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
 	in.FileRefOrResource.DeepCopyInto(&out.FileRefOrResource)
 	return
 }
@@ -75,11 +85,6 @@ func (in *Assert) DeepCopy() *Assert {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Catch) DeepCopyInto(out *Catch) {
 	*out = *in
-	if in.Timeout != nil {
-		in, out := &in.Timeout, &out.Timeout
-		*out = new(v1.Duration)
-		**out = **in
-	}
 	if in.PodLogs != nil {
 		in, out := &in.PodLogs, &out.PodLogs
 		*out = new(PodLogs)
@@ -88,7 +93,7 @@ func (in *Catch) DeepCopyInto(out *Catch) {
 	if in.Events != nil {
 		in, out := &in.Events, &out.Events
 		*out = new(Events)
-		**out = **in
+		(*in).DeepCopyInto(*out)
 	}
 	if in.Command != nil {
 		in, out := &in.Command, &out.Command
@@ -121,6 +126,11 @@ func (in *Catch) DeepCopy() *Catch {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Command) DeepCopyInto(out *Command) {
 	*out = *in
+	if in.Timeout != nil {
+		in, out := &in.Timeout, &out.Timeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
 	if in.Args != nil {
 		in, out := &in.Args, &out.Args
 		*out = make([]string, len(*in))
@@ -210,6 +220,11 @@ func (in *ConfigurationSpec) DeepCopy() *ConfigurationSpec {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Create) DeepCopyInto(out *Create) {
 	*out = *in
+	if in.Timeout != nil {
+		in, out := &in.Timeout, &out.Timeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
 	in.FileRefOrResource.DeepCopyInto(&out.FileRefOrResource)
 	if in.DryRun != nil {
 		in, out := &in.DryRun, &out.DryRun
@@ -239,6 +254,11 @@ func (in *Create) DeepCopy() *Create {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Delete) DeepCopyInto(out *Delete) {
 	*out = *in
+	if in.Timeout != nil {
+		in, out := &in.Timeout, &out.Timeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
 	in.ObjectReference.DeepCopyInto(&out.ObjectReference)
 	if in.Expect != nil {
 		in, out := &in.Expect, &out.Expect
@@ -263,6 +283,11 @@ func (in *Delete) DeepCopy() *Delete {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Error) DeepCopyInto(out *Error) {
 	*out = *in
+	if in.Timeout != nil {
+		in, out := &in.Timeout, &out.Timeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
 	in.FileRefOrResource.DeepCopyInto(&out.FileRefOrResource)
 	return
 }
@@ -280,6 +305,11 @@ func (in *Error) DeepCopy() *Error {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Events) DeepCopyInto(out *Events) {
 	*out = *in
+	if in.Timeout != nil {
+		in, out := &in.Timeout, &out.Timeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
 	return
 }
 
@@ -354,11 +384,6 @@ func (in *FileRefOrResource) DeepCopy() *FileRefOrResource {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Finally) DeepCopyInto(out *Finally) {
 	*out = *in
-	if in.Timeout != nil {
-		in, out := &in.Timeout, &out.Timeout
-		*out = new(v1.Duration)
-		**out = **in
-	}
 	if in.PodLogs != nil {
 		in, out := &in.PodLogs, &out.PodLogs
 		*out = new(PodLogs)
@@ -367,7 +392,7 @@ func (in *Finally) DeepCopyInto(out *Finally) {
 	if in.Events != nil {
 		in, out := &in.Events, &out.Events
 		*out = new(Events)
-		**out = **in
+		(*in).DeepCopyInto(*out)
 	}
 	if in.Command != nil {
 		in, out := &in.Command, &out.Command
@@ -440,11 +465,6 @@ func (in *ObjectSelector) DeepCopy() *ObjectSelector {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Operation) DeepCopyInto(out *Operation) {
 	*out = *in
-	if in.Timeout != nil {
-		in, out := &in.Timeout, &out.Timeout
-		*out = new(v1.Duration)
-		**out = **in
-	}
 	if in.ContinueOnError != nil {
 		in, out := &in.ContinueOnError, &out.ContinueOnError
 		*out = new(bool)
@@ -506,6 +526,11 @@ func (in *Operation) DeepCopy() *Operation {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *PodLogs) DeepCopyInto(out *PodLogs) {
 	*out = *in
+	if in.Timeout != nil {
+		in, out := &in.Timeout, &out.Timeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
 	if in.Tail != nil {
 		in, out := &in.Tail, &out.Tail
 		*out = new(int)
@@ -527,6 +552,11 @@ func (in *PodLogs) DeepCopy() *PodLogs {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Script) DeepCopyInto(out *Script) {
 	*out = *in
+	if in.Timeout != nil {
+		in, out := &in.Timeout, &out.Timeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
 	if in.Check != nil {
 		in, out := &in.Check, &out.Check
 		*out = (*in).DeepCopy()
diff --git a/pkg/runner/operations/apply/apply.go b/pkg/runner/operations/apply/apply.go
index 851ded31..080fa61c 100644
--- a/pkg/runner/operations/apply/apply.go
+++ b/pkg/runner/operations/apply/apply.go
@@ -13,7 +13,6 @@ import (
 	"github.com/kyverno/chainsaw/pkg/runner/namespacer"
 	"github.com/kyverno/chainsaw/pkg/runner/operations"
 	"github.com/kyverno/chainsaw/pkg/runner/operations/internal"
-	"github.com/kyverno/kyverno/ext/output/color"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/types"
@@ -40,20 +39,14 @@ func New(client client.Client, obj unstructured.Unstructured, namespacer namespa
 }
 
 func (o *operation) Exec(ctx context.Context) (err error) {
-	logger := logging.FromContext(ctx).WithResource(&o.obj)
+	logger := internal.GetLogger(ctx, &o.obj)
 	defer func() {
-		if err != nil {
-			logger.Log(logging.Apply, logging.ErrorStatus, color.BoldRed, logging.ErrSection(err))
-		} else {
-			logger.Log(logging.Apply, logging.DoneStatus, color.BoldGreen)
-		}
+		internal.LogEnd(logger, logging.Apply, err)
 	}()
-	if o.namespacer != nil {
-		if err := o.namespacer.Apply(&o.obj); err != nil {
-			return err
-		}
+	if err := internal.ApplyNamespacer(o.namespacer, &o.obj); err != nil {
+		return err
 	}
-	logger.Log(logging.Apply, logging.RunStatus, color.BoldFgCyan)
+	internal.LogStart(logger, logging.Apply)
 	return o.applyResource(ctx, logger)
 }
 
diff --git a/pkg/runner/operations/delete/delete.go b/pkg/runner/operations/delete/delete.go
index 47b1d61b..689a2bef 100644
--- a/pkg/runner/operations/delete/delete.go
+++ b/pkg/runner/operations/delete/delete.go
@@ -11,7 +11,6 @@ import (
 	"github.com/kyverno/chainsaw/pkg/runner/namespacer"
 	"github.com/kyverno/chainsaw/pkg/runner/operations"
 	"github.com/kyverno/chainsaw/pkg/runner/operations/internal"
-	"github.com/kyverno/kyverno/ext/output/color"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/util/wait"
@@ -34,24 +33,16 @@ func New(client client.Client, obj unstructured.Unstructured, namespacer namespa
 }
 
 func (o *operation) Exec(ctx context.Context) (err error) {
-	logger := logging.FromContext(ctx).WithResource(&o.obj)
-	defer func() {
-		if err != nil {
-			logger.Log(logging.Delete, logging.ErrorStatus, color.BoldRed, logging.ErrSection(err))
-		} else {
-			logger.Log(logging.Delete, logging.DoneStatus, color.BoldGreen)
-		}
-	}()
-	if o.namespacer != nil {
-		if err := o.namespacer.Apply(&o.obj); err != nil {
-			return err
-		}
+	logger := internal.GetLogger(ctx, &o.obj)
+	defer func() { internal.LogEnd(logger, logging.Delete, err) }()
+	if err := internal.ApplyNamespacer(o.namespacer, &o.obj); err != nil {
+		return err
 	}
-	logger.Log(logging.Delete, logging.RunStatus, color.BoldFgCyan)
-	return o.deleteResource(ctx, logger)
+	internal.LogStart(logger, logging.Delete)
+	return o.deleteResource(ctx)
 }
 
-func (o *operation) deleteResource(ctx context.Context, logger logging.Logger) error {
+func (o *operation) deleteResource(ctx context.Context) error {
 	candidates, err := internal.Read(ctx, &o.obj, o.client)
 	if err != nil {
 		if kerrors.IsNotFound(err) {
diff --git a/pkg/runner/operations/internal/diff.go b/pkg/runner/operations/internal/diff.go
deleted file mode 100644
index 350c6cf1..00000000
--- a/pkg/runner/operations/internal/diff.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package internal
-
-import (
-	"fmt"
-	"strings"
-
-	"github.com/pmezard/go-difflib/difflib"
-	ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/yaml"
-)
-
-type (
-	diffLibInterface = func(difflib.UnifiedDiff) (string, error)
-	yamlMarshaler    = func(obj interface{}) ([]byte, error)
-)
-
-func diff(expected, actual ctrlclient.Object) (string, error) {
-	return diffHelper(expected, actual, nil, nil, nil)
-}
-
-func diffHelper(expected, actual interface{}, expectedMarshaler, actualMarshaler yamlMarshaler, getDiffString diffLibInterface) (string, error) {
-	if expectedMarshaler == nil {
-		expectedMarshaler = yaml.Marshal
-	}
-	if actualMarshaler == nil {
-		actualMarshaler = yaml.Marshal
-	}
-	if getDiffString == nil {
-		getDiffString = difflib.GetUnifiedDiffString
-	}
-
-	expectedBytes, err := expectedMarshaler(expected)
-	if err != nil {
-		return "", fmt.Errorf("failed to marshal expected content to YAML: %w", err)
-	}
-	candidateBytes, err := actualMarshaler(actual)
-	if err != nil {
-		return "", fmt.Errorf("failed to marshal candidate content to YAML: %w", err)
-	}
-
-	diff := difflib.UnifiedDiff{
-		A:        difflib.SplitLines(string(expectedBytes)),
-		B:        difflib.SplitLines(string(candidateBytes)),
-		FromFile: "Expected",
-		ToFile:   "Actual",
-		Context:  3,
-	}
-
-	diffStr, err := getDiffString(diff)
-	if err != nil {
-		return "", fmt.Errorf("failed to generate unified diff string: %w", err)
-	}
-	return strings.TrimSpace(diffStr), nil
-}
diff --git a/pkg/runner/operations/internal/log.go b/pkg/runner/operations/internal/log.go
new file mode 100644
index 00000000..4a9c3b8f
--- /dev/null
+++ b/pkg/runner/operations/internal/log.go
@@ -0,0 +1,25 @@
+package internal
+
+import (
+	"context"
+
+	"github.com/kyverno/chainsaw/pkg/runner/logging"
+	"github.com/kyverno/kyverno/ext/output/color"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+func GetLogger(ctx context.Context, obj client.Object) logging.Logger {
+	return logging.FromContext(ctx).WithResource(obj)
+}
+
+func LogStart(logger logging.Logger, op logging.Operation) {
+	logger.Log(op, logging.RunStatus, color.BoldFgCyan)
+}
+
+func LogEnd(logger logging.Logger, op logging.Operation, err error) {
+	if err != nil {
+		logger.Log(op, logging.ErrorStatus, color.BoldRed, logging.ErrSection(err))
+	} else {
+		logger.Log(op, logging.DoneStatus, color.BoldGreen)
+	}
+}
diff --git a/pkg/runner/operations/internal/namespace.go b/pkg/runner/operations/internal/namespace.go
new file mode 100644
index 00000000..e96df29b
--- /dev/null
+++ b/pkg/runner/operations/internal/namespace.go
@@ -0,0 +1,13 @@
+package internal
+
+import (
+	"github.com/kyverno/chainsaw/pkg/runner/namespacer"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+func ApplyNamespacer(namespacer namespacer.Namespacer, obj client.Object) error {
+	if namespacer == nil {
+		return nil
+	}
+	return namespacer.Apply(obj)
+}
diff --git a/pkg/runner/processors/operation.go b/pkg/runner/processors/operation.go
index fb899c65..26a40280 100644
--- a/pkg/runner/processors/operation.go
+++ b/pkg/runner/processors/operation.go
@@ -11,14 +11,17 @@ import (
 
 type operation struct {
 	continueOnError bool
-	timeout         time.Duration
+	timeout         *time.Duration
 	operation       operations.Operation
 	operationReport *report.OperationReport
 }
 
 func (o operation) execute(ctx context.Context) {
-	ctx, cancel := context.WithTimeout(ctx, o.timeout)
-	defer cancel()
+	if o.timeout != nil {
+		toCtx, cancel := context.WithTimeout(ctx, *o.timeout)
+		ctx = toCtx
+		defer cancel()
+	}
 	if err := o.operation.Exec(ctx); err != nil {
 		t := testing.FromContext(ctx)
 		if o.operationReport != nil {
diff --git a/pkg/runner/processors/step.go b/pkg/runner/processors/step.go
index 32651c03..8a6179cb 100644
--- a/pkg/runner/processors/step.go
+++ b/pkg/runner/processors/step.go
@@ -25,7 +25,6 @@ import (
 	"github.com/kyverno/chainsaw/pkg/runner/timeout"
 	"github.com/kyverno/chainsaw/pkg/testing"
 	"github.com/kyverno/kyverno/ext/output/color"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/utils/clock"
 )
@@ -133,39 +132,39 @@ func (p *stepProcessor) tryOperations(ctx context.Context, handlers ...v1alpha1.
 			}
 		}
 		if handler.Apply != nil {
-			loaded, err := p.applyOperation(ctx, *handler.Apply, handler.Timeout)
+			loaded, err := p.applyOperation(ctx, *handler.Apply)
 			if err != nil {
 				return nil, err
 			}
 			register(loaded...)
 		} else if handler.Assert != nil {
-			loaded, err := p.assertOperation(ctx, *handler.Assert, handler.Timeout)
+			loaded, err := p.assertOperation(ctx, *handler.Assert)
 			if err != nil {
 				return nil, err
 			}
 			register(loaded...)
 		} else if handler.Command != nil {
-			register(p.commandOperation(ctx, *handler.Command, handler.Timeout))
+			register(p.commandOperation(ctx, *handler.Command))
 		} else if handler.Create != nil {
-			loaded, err := p.createOperation(ctx, *handler.Create, handler.Timeout)
+			loaded, err := p.createOperation(ctx, *handler.Create)
 			if err != nil {
 				return nil, err
 			}
 			register(loaded...)
 		} else if handler.Delete != nil {
-			loaded, err := p.deleteOperation(ctx, *handler.Delete, handler.Timeout)
+			loaded, err := p.deleteOperation(ctx, *handler.Delete)
 			if err != nil {
 				return nil, err
 			}
 			register(*loaded)
 		} else if handler.Error != nil {
-			loaded, err := p.errorOperation(ctx, *handler.Error, handler.Timeout)
+			loaded, err := p.errorOperation(ctx, *handler.Error)
 			if err != nil {
 				return nil, err
 			}
 			register(loaded...)
 		} else if handler.Script != nil {
-			register(p.scriptOperation(ctx, *handler.Script, handler.Timeout))
+			register(p.scriptOperation(ctx, *handler.Script))
 		} else if handler.Sleep != nil {
 			register(p.sleepOperation(ctx, *handler.Sleep))
 		} else {
@@ -189,17 +188,17 @@ func (p *stepProcessor) catchOperations(ctx context.Context, handlers ...v1alpha
 			if err != nil {
 				return nil, err
 			}
-			register(p.commandOperation(ctx, *cmd, handler.Timeout))
+			register(p.commandOperation(ctx, *cmd))
 		} else if handler.Events != nil {
 			cmd, err := collect.Events(handler.Events)
 			if err != nil {
 				return nil, err
 			}
-			register(p.commandOperation(ctx, *cmd, handler.Timeout))
+			register(p.commandOperation(ctx, *cmd))
 		} else if handler.Command != nil {
-			register(p.commandOperation(ctx, *handler.Command, handler.Timeout))
+			register(p.commandOperation(ctx, *handler.Command))
 		} else if handler.Script != nil {
-			register(p.scriptOperation(ctx, *handler.Script, handler.Timeout))
+			register(p.scriptOperation(ctx, *handler.Script))
 		} else if handler.Sleep != nil {
 			register(p.sleepOperation(ctx, *handler.Sleep))
 		} else {
@@ -223,17 +222,17 @@ func (p *stepProcessor) finallyOperations(ctx context.Context, handlers ...v1alp
 			if err != nil {
 				return nil, err
 			}
-			register(p.commandOperation(ctx, *cmd, handler.Timeout))
+			register(p.commandOperation(ctx, *cmd))
 		} else if handler.Events != nil {
 			cmd, err := collect.Events(handler.Events)
 			if err != nil {
 				return nil, err
 			}
-			register(p.commandOperation(ctx, *cmd, handler.Timeout))
+			register(p.commandOperation(ctx, *cmd))
 		} else if handler.Command != nil {
-			register(p.commandOperation(ctx, *handler.Command, handler.Timeout))
+			register(p.commandOperation(ctx, *handler.Command))
 		} else if handler.Script != nil {
-			register(p.scriptOperation(ctx, *handler.Script, handler.Timeout))
+			register(p.scriptOperation(ctx, *handler.Script))
 		} else if handler.Sleep != nil {
 			register(p.sleepOperation(ctx, *handler.Sleep))
 		} else {
@@ -243,7 +242,7 @@ func (p *stepProcessor) finallyOperations(ctx context.Context, handlers ...v1alp
 	return ops, nil
 }
 
-func (p *stepProcessor) applyOperation(ctx context.Context, op v1alpha1.Apply, to *metav1.Duration) ([]operation, error) {
+func (p *stepProcessor) applyOperation(ctx context.Context, op v1alpha1.Apply) ([]operation, error) {
 	resources, err := p.fileRefOrResource(op.FileRefOrResource)
 	if err != nil {
 		return nil, err
@@ -260,14 +259,14 @@ func (p *stepProcessor) applyOperation(ctx context.Context, op v1alpha1.Apply, t
 			return nil, err
 		}
 		ops = append(ops, operation{
-			timeout:   timeout.Get(to, timeouts.ApplyDuration()),
+			timeout:   timeout.Get(op.Timeout, timeouts.ApplyDuration()),
 			operation: opapply.New(p.getClient(dryRun), resource, p.namespacer, p.getCleaner(ctx, dryRun), op.Expect...),
 		})
 	}
 	return ops, nil
 }
 
-func (p *stepProcessor) assertOperation(ctx context.Context, op v1alpha1.Assert, to *metav1.Duration) ([]operation, error) {
+func (p *stepProcessor) assertOperation(ctx context.Context, op v1alpha1.Assert) ([]operation, error) {
 	resources, err := p.fileRefOrResource(op.FileRefOrResource)
 	if err != nil {
 		return nil, err
@@ -280,7 +279,7 @@ func (p *stepProcessor) assertOperation(ctx context.Context, op v1alpha1.Assert,
 	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(to, timeouts.AssertDuration()),
+			timeout:         timeout.Get(op.Timeout, timeouts.AssertDuration()),
 			operation:       opassert.New(p.client, resource, p.namespacer),
 			operationReport: operationReport,
 		})
@@ -288,20 +287,20 @@ func (p *stepProcessor) assertOperation(ctx context.Context, op v1alpha1.Assert,
 	return ops, nil
 }
 
-func (p *stepProcessor) commandOperation(ctx context.Context, exec v1alpha1.Command, to *metav1.Duration) operation {
+func (p *stepProcessor) commandOperation(ctx context.Context, op v1alpha1.Command) operation {
 	operationReport := report.NewOperation("Command ", report.OperationTypeCommand)
 	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(to, timeouts.ExecDuration()),
-		operation:       opcommand.New(exec, p.test.BasePath, p.namespacer.GetNamespace()),
+		timeout:         timeout.Get(op.Timeout, timeouts.ExecDuration()),
+		operation:       opcommand.New(op, p.test.BasePath, p.namespacer.GetNamespace()),
 		operationReport: operationReport,
 	}
 }
 
-func (p *stepProcessor) createOperation(ctx context.Context, op v1alpha1.Create, to *metav1.Duration) ([]operation, error) {
+func (p *stepProcessor) createOperation(ctx context.Context, op v1alpha1.Create) ([]operation, error) {
 	resources, err := p.fileRefOrResource(op.FileRefOrResource)
 	if err != nil {
 		return nil, err
@@ -318,14 +317,14 @@ func (p *stepProcessor) createOperation(ctx context.Context, op v1alpha1.Create,
 			return nil, err
 		}
 		ops = append(ops, operation{
-			timeout:   timeout.Get(to, timeouts.ApplyDuration()),
+			timeout:   timeout.Get(op.Timeout, timeouts.ApplyDuration()),
 			operation: opcreate.New(p.getClient(dryRun), resource, p.namespacer, p.getCleaner(ctx, dryRun), op.Expect...),
 		})
 	}
 	return ops, nil
 }
 
-func (p *stepProcessor) deleteOperation(ctx context.Context, op v1alpha1.Delete, to *metav1.Duration) (*operation, error) {
+func (p *stepProcessor) deleteOperation(ctx context.Context, op v1alpha1.Delete) (*operation, error) {
 	var resource unstructured.Unstructured
 	resource.SetAPIVersion(op.APIVersion)
 	resource.SetKind(op.Kind)
@@ -338,13 +337,13 @@ func (p *stepProcessor) deleteOperation(ctx context.Context, op v1alpha1.Delete,
 	}
 	timeouts := timeout.Combine(p.config.Timeouts, p.test.Spec.Timeouts, p.step.TestStepSpec.Timeouts)
 	return &operation{
-		timeout:         timeout.Get(to, timeouts.DeleteDuration()),
+		timeout:         timeout.Get(op.Timeout, timeouts.DeleteDuration()),
 		operation:       opdelete.New(p.client, resource, p.namespacer, op.Expect...),
 		operationReport: operationReport,
 	}, nil
 }
 
-func (p *stepProcessor) errorOperation(ctx context.Context, op v1alpha1.Error, to *metav1.Duration) ([]operation, error) {
+func (p *stepProcessor) errorOperation(ctx context.Context, op v1alpha1.Error) ([]operation, error) {
 	resources, err := p.fileRefOrResource(op.FileRefOrResource)
 	if err != nil {
 		return nil, err
@@ -357,7 +356,7 @@ func (p *stepProcessor) errorOperation(ctx context.Context, op v1alpha1.Error, t
 	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(to, timeouts.ErrorDuration()),
+			timeout:         timeout.Get(op.Timeout, timeouts.ErrorDuration()),
 			operation:       operror.New(p.client, resource, p.namespacer),
 			operationReport: operationReport,
 		})
@@ -365,15 +364,15 @@ func (p *stepProcessor) errorOperation(ctx context.Context, op v1alpha1.Error, t
 	return ops, nil
 }
 
-func (p *stepProcessor) scriptOperation(ctx context.Context, exec v1alpha1.Script, to *metav1.Duration) operation {
+func (p *stepProcessor) scriptOperation(ctx context.Context, op v1alpha1.Script) operation {
 	operationReport := report.NewOperation("Script ", report.OperationTypeScript)
 	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(to, timeouts.ExecDuration()),
-		operation:       opscript.New(exec, p.test.BasePath, p.namespacer.GetNamespace()),
+		timeout:         timeout.Get(op.Timeout, timeouts.ExecDuration()),
+		operation:       opscript.New(op, p.test.BasePath, p.namespacer.GetNamespace()),
 		operationReport: operationReport,
 	}
 }
@@ -439,7 +438,7 @@ func (p *stepProcessor) getCleaner(ctx context.Context, dryRun bool) cleanup.Cle
 				timeouts := timeout.Combine(p.config.Timeouts, p.test.Spec.Timeouts, p.step.TestStepSpec.Timeouts)
 				operation := operation{
 					continueOnError: true,
-					timeout:         timeouts.DeleteDuration(),
+					timeout:         timeout.Get(nil, 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 163c327e..70d13015 100644
--- a/pkg/runner/timeout/timeout.go
+++ b/pkg/runner/timeout/timeout.go
@@ -33,9 +33,9 @@ func Combine(config v1alpha1.Timeouts, next ...*v1alpha1.Timeouts) v1alpha1.Time
 	return config
 }
 
-func Get(operation *metav1.Duration, fallback time.Duration) time.Duration {
+func Get(operation *metav1.Duration, fallback time.Duration) *time.Duration {
 	if operation != nil {
-		return operation.Duration
+		return &operation.Duration
 	}
-	return fallback
+	return &fallback
 }
diff --git a/website/docs/apis/chainsaw.v1alpha1.md b/website/docs/apis/chainsaw.v1alpha1.md
index 525dcc0a..fc787b1d 100644
--- a/website/docs/apis/chainsaw.v1alpha1.md
+++ b/website/docs/apis/chainsaw.v1alpha1.md
@@ -62,6 +62,7 @@ should be applied during testing.</p>
 
 | Field | Type | Required | Inline | Description |
 |---|---|---|---|---|
+| `timeout` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) |  |  | <p>Timeout for the operation. Overrides the global timeout set in the Configuration.</p> |
 | `FileRefOrResource` | [`FileRefOrResource`](#chainsaw-kyverno-io-v1alpha1-FileRefOrResource) | :white_check_mark: | :white_check_mark: | <p>FileRefOrResource provides a reference to the resources to be applied.</p> |
 | `dryRun` | `bool` |  |  | <p>DryRun determines whether the file should be applied in dry run mode.</p> |
 | `expect` | [`[]Expectation`](#chainsaw-kyverno-io-v1alpha1-Expectation) |  |  | <p>Expect defines a list of matched checks to validate the operation outcome.</p> |
@@ -78,6 +79,7 @@ during the testing process.</p>
 
 | Field | Type | Required | Inline | Description |
 |---|---|---|---|---|
+| `timeout` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) |  |  | <p>Timeout for the operation. Overrides the global timeout set in the Configuration.</p> |
 | `FileRefOrResource` | [`FileRefOrResource`](#chainsaw-kyverno-io-v1alpha1-FileRefOrResource) | :white_check_mark: | :white_check_mark: | <p>FileRefOrResource provides a reference to the assertion.</p> |
 
 ## `Catch`     {#chainsaw-kyverno-io-v1alpha1-Catch}
@@ -92,7 +94,6 @@ during the testing process.</p>
 | Field | Type | Required | Inline | Description |
 |---|---|---|---|---|
 | `description` | `string` |  |  | <p>Description contains a description of the operation.</p> |
-| `timeout` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) |  |  | <p>Timeout for the operation. Overrides the global timeout set in the Configuration.</p> |
 | `podLogs` | [`PodLogs`](#chainsaw-kyverno-io-v1alpha1-PodLogs) |  |  | <p>PodLogs determines the pod logs collector to execute.</p> |
 | `events` | [`Events`](#chainsaw-kyverno-io-v1alpha1-Events) |  |  | <p>Events determines the events collector to execute.</p> |
 | `command` | [`Command`](#chainsaw-kyverno-io-v1alpha1-Command) |  |  | <p>Command defines a command to run.</p> |
@@ -112,6 +113,7 @@ during the testing process.</p>
 
 | Field | Type | Required | Inline | Description |
 |---|---|---|---|---|
+| `timeout` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) |  |  | <p>Timeout for the operation. Overrides the global timeout set in the Configuration.</p> |
 | `entrypoint` | `string` | :white_check_mark: |  | <p>Entrypoint is the command entry point to run.</p> |
 | `args` | `[]string` |  |  | <p>Args is the command arguments.</p> |
 | `skipLogOutput` | `bool` |  |  | <p>SkipLogOutput removes the output from the command. Useful for sensitive logs or to reduce noise.</p> |
@@ -155,6 +157,7 @@ If a resource already exists in the cluster it will fail.</p>
 
 | Field | Type | Required | Inline | Description |
 |---|---|---|---|---|
+| `timeout` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) |  |  | <p>Timeout for the operation. Overrides the global timeout set in the Configuration.</p> |
 | `FileRefOrResource` | [`FileRefOrResource`](#chainsaw-kyverno-io-v1alpha1-FileRefOrResource) | :white_check_mark: | :white_check_mark: | <p>FileRefOrResource provides a reference to the file containing the resources to be created.</p> |
 | `dryRun` | `bool` |  |  | <p>DryRun determines whether the file should be applied in dry run mode.</p> |
 | `expect` | [`[]Expectation`](#chainsaw-kyverno-io-v1alpha1-Expectation) |  |  | <p>Expect defines a list of matched checks to validate the operation outcome.</p> |
@@ -170,6 +173,7 @@ If a resource already exists in the cluster it will fail.</p>
 
 | Field | Type | Required | Inline | Description |
 |---|---|---|---|---|
+| `timeout` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) |  |  | <p>Timeout for the operation. Overrides the global timeout set in the Configuration.</p> |
 | `ref` | [`ObjectReference`](#chainsaw-kyverno-io-v1alpha1-ObjectReference) | :white_check_mark: |  | <p>ObjectReference determines objects to be deleted.</p> |
 | `expect` | [`[]Expectation`](#chainsaw-kyverno-io-v1alpha1-Expectation) |  |  | <p>Expect defines a list of matched checks to validate the operation outcome.</p> |
 
@@ -185,6 +189,7 @@ Instead of treating such an error as a test failure, it acknowledges it as expec
 
 | Field | Type | Required | Inline | Description |
 |---|---|---|---|---|
+| `timeout` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) |  |  | <p>Timeout for the operation. Overrides the global timeout set in the Configuration.</p> |
 | `FileRefOrResource` | [`FileRefOrResource`](#chainsaw-kyverno-io-v1alpha1-FileRefOrResource) | :white_check_mark: | :white_check_mark: | <p>FileRefOrResource provides a reference to the expected error.</p> |
 
 ## `Events`     {#chainsaw-kyverno-io-v1alpha1-Events}
@@ -199,6 +204,7 @@ Instead of treating such an error as a test failure, it acknowledges it as expec
 
 | Field | Type | Required | Inline | Description |
 |---|---|---|---|---|
+| `timeout` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) |  |  | <p>Timeout for the operation. Overrides the global timeout set in the Configuration.</p> |
 | `namespace` | `string` |  |  | <p>Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/</p> |
 | `name` | `string` |  |  | <p>Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names</p> |
 | `selector` | `string` |  |  | <p>Selector defines labels selector.</p> |
@@ -262,7 +268,6 @@ with a match filter to determine if the verification should be considered.</p>
 | Field | Type | Required | Inline | Description |
 |---|---|---|---|---|
 | `description` | `string` |  |  | <p>Description contains a description of the operation.</p> |
-| `timeout` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) |  |  | <p>Timeout for the operation. Overrides the global timeout set in the Configuration.</p> |
 | `podLogs` | [`PodLogs`](#chainsaw-kyverno-io-v1alpha1-PodLogs) |  |  | <p>PodLogs determines the pod logs collector to execute.</p> |
 | `events` | [`Events`](#chainsaw-kyverno-io-v1alpha1-Events) |  |  | <p>Events determines the events collector to execute.</p> |
 | `command` | [`Command`](#chainsaw-kyverno-io-v1alpha1-Command) |  |  | <p>Command defines a command to run.</p> |
@@ -315,7 +320,6 @@ For multiple objects use labels.</p>
 | Field | Type | Required | Inline | Description |
 |---|---|---|---|---|
 | `description` | `string` |  |  | <p>Description contains a description of the operation.</p> |
-| `timeout` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) |  |  | <p>Timeout for the operation. Overrides the global timeout set in the Configuration.</p> |
 | `continueOnError` | `bool` |  |  | <p>ContinueOnError determines whether a test should continue or not in case the operation was not successful. Even if the test continues executing, it will still be reported as failed.</p> |
 | `apply` | [`Apply`](#chainsaw-kyverno-io-v1alpha1-Apply) |  |  | <p>Apply represents resources that should be applied for this test step. This can include things like configuration settings or any other resources that need to be available during the test.</p> |
 | `assert` | [`Assert`](#chainsaw-kyverno-io-v1alpha1-Assert) |  |  | <p>Assert represents an assertion to be made. It checks whether the conditions specified in the assertion hold true.</p> |
@@ -338,6 +342,7 @@ For multiple objects use labels.</p>
 
 | Field | Type | Required | Inline | Description |
 |---|---|---|---|---|
+| `timeout` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) |  |  | <p>Timeout for the operation. Overrides the global timeout set in the Configuration.</p> |
 | `namespace` | `string` |  |  | <p>Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/</p> |
 | `name` | `string` |  |  | <p>Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names</p> |
 | `selector` | `string` |  |  | <p>Selector defines labels selector.</p> |
@@ -365,6 +370,7 @@ For multiple objects use labels.</p>
 
 | Field | Type | Required | Inline | Description |
 |---|---|---|---|---|
+| `timeout` | [`meta/v1.Duration`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration) |  |  | <p>Timeout for the operation. Overrides the global timeout set in the Configuration.</p> |
 | `content` | `string` |  |  | <p>Content defines a shell script (run with "sh -c ...").</p> |
 | `skipLogOutput` | `bool` |  |  | <p>SkipLogOutput removes the output from the command. Useful for sensitive logs or to reduce noise.</p> |
 | `check` | `github.com/kyverno/kyverno-json/pkg/apis/v1alpha1.Any` |  |  | <p>Check is an assertion tree to validate the operation outcome.</p> |


Test patch:
diff --git a/.crds/chainsaw.kyverno.io_tests.yaml b/.crds/chainsaw.kyverno.io_tests.yaml
index d99e9b20..3bffddb5 100644
--- a/.crds/chainsaw.kyverno.io_tests.yaml
+++ b/.crds/chainsaw.kyverno.io_tests.yaml
@@ -86,6 +86,10 @@ spec:
                                   the command. Useful for sensitive logs or to reduce
                                   noise.
                                 type: boolean
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             required:
                             - entrypoint
                             type: object
@@ -107,6 +111,10 @@ spec:
                               selector:
                                 description: Selector defines labels selector.
                                 type: string
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           podLogs:
                             description: PodLogs determines the pod logs collector
@@ -133,6 +141,10 @@ spec:
                                   use a pod name. This matches default behavior of
                                   `kubectl logs`.
                                 type: integer
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           script:
                             description: Script defines a script to run.
@@ -151,6 +163,10 @@ spec:
                                   the command. Useful for sensitive logs or to reduce
                                   noise.
                                 type: boolean
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           sleep:
                             description: Sleep defines zzzz.
@@ -161,10 +177,6 @@ spec:
                             required:
                             - duration
                             type: object
-                          timeout:
-                            description: Timeout for the operation. Overrides the
-                              global timeout set in the Configuration.
-                            type: string
                         type: object
                       type: array
                     description:
@@ -200,6 +212,10 @@ spec:
                                   the command. Useful for sensitive logs or to reduce
                                   noise.
                                 type: boolean
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             required:
                             - entrypoint
                             type: object
@@ -221,6 +237,10 @@ spec:
                               selector:
                                 description: Selector defines labels selector.
                                 type: string
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           podLogs:
                             description: PodLogs determines the pod logs collector
@@ -247,6 +267,10 @@ spec:
                                   use a pod name. This matches default behavior of
                                   `kubectl logs`.
                                 type: integer
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           script:
                             description: Script defines a script to run.
@@ -265,6 +289,10 @@ spec:
                                   the command. Useful for sensitive logs or to reduce
                                   noise.
                                 type: boolean
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           sleep:
                             description: Sleep defines zzzz.
@@ -275,10 +303,6 @@ spec:
                             required:
                             - duration
                             type: object
-                          timeout:
-                            description: Timeout for the operation. Overrides the
-                              global timeout set in the Configuration.
-                            type: string
                         type: object
                       type: array
                     name:
@@ -358,6 +382,10 @@ spec:
                                 type: object
                                 x-kubernetes-embedded-resource: true
                                 x-kubernetes-preserve-unknown-fields: true
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           assert:
                             description: Assert represents an assertion to be made.
@@ -372,6 +400,10 @@ spec:
                                 type: object
                                 x-kubernetes-embedded-resource: true
                                 x-kubernetes-preserve-unknown-fields: true
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           command:
                             description: Command defines a command to run.
@@ -395,6 +427,10 @@ spec:
                                   the command. Useful for sensitive logs or to reduce
                                   noise.
                                 type: boolean
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             required:
                             - entrypoint
                             type: object
@@ -440,6 +476,10 @@ spec:
                                 type: object
                                 x-kubernetes-embedded-resource: true
                                 x-kubernetes-preserve-unknown-fields: true
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           delete:
                             description: Delete represents a creation operation.
@@ -494,6 +534,10 @@ spec:
                                 - apiVersion
                                 - kind
                                 type: object
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             required:
                             - ref
                             type: object
@@ -515,6 +559,10 @@ spec:
                                 type: object
                                 x-kubernetes-embedded-resource: true
                                 x-kubernetes-preserve-unknown-fields: true
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           script:
                             description: Script defines a script to run.
@@ -533,6 +581,10 @@ spec:
                                   the command. Useful for sensitive logs or to reduce
                                   noise.
                                 type: boolean
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           sleep:
                             description: Sleep defines zzzz.
@@ -543,10 +595,6 @@ spec:
                             required:
                             - duration
                             type: object
-                          timeout:
-                            description: Timeout for the operation. Overrides the
-                              global timeout set in the Configuration.
-                            type: string
                         type: object
                       type: array
                   required:
diff --git a/.crds/chainsaw.kyverno.io_teststeps.yaml b/.crds/chainsaw.kyverno.io_teststeps.yaml
index f126d468..44994d96 100644
--- a/.crds/chainsaw.kyverno.io_teststeps.yaml
+++ b/.crds/chainsaw.kyverno.io_teststeps.yaml
@@ -61,6 +61,10 @@ spec:
                           description: SkipLogOutput removes the output from the command.
                             Useful for sensitive logs or to reduce noise.
                           type: boolean
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       required:
                       - entrypoint
                       type: object
@@ -79,6 +83,10 @@ spec:
                         selector:
                           description: Selector defines labels selector.
                           type: string
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     podLogs:
                       description: PodLogs determines the pod logs collector to execute.
@@ -102,6 +110,10 @@ spec:
                             if you use a selector, or -1 (all) if you use a pod name.
                             This matches default behavior of `kubectl logs`.
                           type: integer
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     script:
                       description: Script defines a script to run.
@@ -119,6 +131,10 @@ spec:
                           description: SkipLogOutput removes the output from the command.
                             Useful for sensitive logs or to reduce noise.
                           type: boolean
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     sleep:
                       description: Sleep defines zzzz.
@@ -129,10 +145,6 @@ spec:
                       required:
                       - duration
                       type: object
-                    timeout:
-                      description: Timeout for the operation. Overrides the global
-                        timeout set in the Configuration.
-                      type: string
                   type: object
                 type: array
               description:
@@ -165,6 +177,10 @@ spec:
                           description: SkipLogOutput removes the output from the command.
                             Useful for sensitive logs or to reduce noise.
                           type: boolean
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       required:
                       - entrypoint
                       type: object
@@ -183,6 +199,10 @@ spec:
                         selector:
                           description: Selector defines labels selector.
                           type: string
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     podLogs:
                       description: PodLogs determines the pod logs collector to execute.
@@ -206,6 +226,10 @@ spec:
                             if you use a selector, or -1 (all) if you use a pod name.
                             This matches default behavior of `kubectl logs`.
                           type: integer
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     script:
                       description: Script defines a script to run.
@@ -223,6 +247,10 @@ spec:
                           description: SkipLogOutput removes the output from the command.
                             Useful for sensitive logs or to reduce noise.
                           type: boolean
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     sleep:
                       description: Sleep defines zzzz.
@@ -233,10 +261,6 @@ spec:
                       required:
                       - duration
                       type: object
-                    timeout:
-                      description: Timeout for the operation. Overrides the global
-                        timeout set in the Configuration.
-                      type: string
                   type: object
                 type: array
               skipDelete:
@@ -311,6 +335,10 @@ spec:
                           type: object
                           x-kubernetes-embedded-resource: true
                           x-kubernetes-preserve-unknown-fields: true
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     assert:
                       description: Assert represents an assertion to be made. It checks
@@ -324,6 +352,10 @@ spec:
                           type: object
                           x-kubernetes-embedded-resource: true
                           x-kubernetes-preserve-unknown-fields: true
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     command:
                       description: Command defines a command to run.
@@ -345,6 +377,10 @@ spec:
                           description: SkipLogOutput removes the output from the command.
                             Useful for sensitive logs or to reduce noise.
                           type: boolean
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       required:
                       - entrypoint
                       type: object
@@ -389,6 +425,10 @@ spec:
                           type: object
                           x-kubernetes-embedded-resource: true
                           x-kubernetes-preserve-unknown-fields: true
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     delete:
                       description: Delete represents a creation operation.
@@ -438,6 +478,10 @@ spec:
                           - apiVersion
                           - kind
                           type: object
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       required:
                       - ref
                       type: object
@@ -458,6 +502,10 @@ spec:
                           type: object
                           x-kubernetes-embedded-resource: true
                           x-kubernetes-preserve-unknown-fields: true
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     script:
                       description: Script defines a script to run.
@@ -475,6 +523,10 @@ spec:
                           description: SkipLogOutput removes the output from the command.
                             Useful for sensitive logs or to reduce noise.
                           type: boolean
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     sleep:
                       description: Sleep defines zzzz.
@@ -485,10 +537,6 @@ spec:
                       required:
                       - duration
                       type: object
-                    timeout:
-                      description: Timeout for the operation. Overrides the global
-                        timeout set in the Configuration.
-                      type: string
                   type: object
                 type: array
             required:
diff --git a/pkg/commands/kuttl/migrate/tests/command.go b/pkg/commands/kuttl/migrate/tests/command.go
index e53d9a98..795b4257 100644
--- a/pkg/commands/kuttl/migrate/tests/command.go
+++ b/pkg/commands/kuttl/migrate/tests/command.go
@@ -328,8 +328,8 @@ func testStep(to *v1alpha1.TestStepSpec, in unstructured.Unstructured) error {
 		}
 		if operation.Script != "" {
 			to.Try = append(to.Try, v1alpha1.Operation{
-				Timeout: timeout,
 				Script: &v1alpha1.Script{
+					Timeout:       timeout,
 					Content:       operation.Script,
 					SkipLogOutput: operation.SkipLogOutput,
 				},
@@ -345,8 +345,8 @@ func testStep(to *v1alpha1.TestStepSpec, in unstructured.Unstructured) error {
 				args = split[1:]
 			}
 			to.Try = append(to.Try, v1alpha1.Operation{
-				Timeout: timeout,
 				Command: &v1alpha1.Command{
+					Timeout:       timeout,
 					Entrypoint:    entrypoint,
 					Args:          args,
 					SkipLogOutput: operation.SkipLogOutput,
diff --git a/pkg/data/crds/chainsaw.kyverno.io_tests.yaml b/pkg/data/crds/chainsaw.kyverno.io_tests.yaml
index d99e9b20..3bffddb5 100644
--- a/pkg/data/crds/chainsaw.kyverno.io_tests.yaml
+++ b/pkg/data/crds/chainsaw.kyverno.io_tests.yaml
@@ -86,6 +86,10 @@ spec:
                                   the command. Useful for sensitive logs or to reduce
                                   noise.
                                 type: boolean
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             required:
                             - entrypoint
                             type: object
@@ -107,6 +111,10 @@ spec:
                               selector:
                                 description: Selector defines labels selector.
                                 type: string
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           podLogs:
                             description: PodLogs determines the pod logs collector
@@ -133,6 +141,10 @@ spec:
                                   use a pod name. This matches default behavior of
                                   `kubectl logs`.
                                 type: integer
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           script:
                             description: Script defines a script to run.
@@ -151,6 +163,10 @@ spec:
                                   the command. Useful for sensitive logs or to reduce
                                   noise.
                                 type: boolean
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           sleep:
                             description: Sleep defines zzzz.
@@ -161,10 +177,6 @@ spec:
                             required:
                             - duration
                             type: object
-                          timeout:
-                            description: Timeout for the operation. Overrides the
-                              global timeout set in the Configuration.
-                            type: string
                         type: object
                       type: array
                     description:
@@ -200,6 +212,10 @@ spec:
                                   the command. Useful for sensitive logs or to reduce
                                   noise.
                                 type: boolean
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             required:
                             - entrypoint
                             type: object
@@ -221,6 +237,10 @@ spec:
                               selector:
                                 description: Selector defines labels selector.
                                 type: string
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           podLogs:
                             description: PodLogs determines the pod logs collector
@@ -247,6 +267,10 @@ spec:
                                   use a pod name. This matches default behavior of
                                   `kubectl logs`.
                                 type: integer
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           script:
                             description: Script defines a script to run.
@@ -265,6 +289,10 @@ spec:
                                   the command. Useful for sensitive logs or to reduce
                                   noise.
                                 type: boolean
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           sleep:
                             description: Sleep defines zzzz.
@@ -275,10 +303,6 @@ spec:
                             required:
                             - duration
                             type: object
-                          timeout:
-                            description: Timeout for the operation. Overrides the
-                              global timeout set in the Configuration.
-                            type: string
                         type: object
                       type: array
                     name:
@@ -358,6 +382,10 @@ spec:
                                 type: object
                                 x-kubernetes-embedded-resource: true
                                 x-kubernetes-preserve-unknown-fields: true
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           assert:
                             description: Assert represents an assertion to be made.
@@ -372,6 +400,10 @@ spec:
                                 type: object
                                 x-kubernetes-embedded-resource: true
                                 x-kubernetes-preserve-unknown-fields: true
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           command:
                             description: Command defines a command to run.
@@ -395,6 +427,10 @@ spec:
                                   the command. Useful for sensitive logs or to reduce
                                   noise.
                                 type: boolean
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             required:
                             - entrypoint
                             type: object
@@ -440,6 +476,10 @@ spec:
                                 type: object
                                 x-kubernetes-embedded-resource: true
                                 x-kubernetes-preserve-unknown-fields: true
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           delete:
                             description: Delete represents a creation operation.
@@ -494,6 +534,10 @@ spec:
                                 - apiVersion
                                 - kind
                                 type: object
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             required:
                             - ref
                             type: object
@@ -515,6 +559,10 @@ spec:
                                 type: object
                                 x-kubernetes-embedded-resource: true
                                 x-kubernetes-preserve-unknown-fields: true
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           script:
                             description: Script defines a script to run.
@@ -533,6 +581,10 @@ spec:
                                   the command. Useful for sensitive logs or to reduce
                                   noise.
                                 type: boolean
+                              timeout:
+                                description: Timeout for the operation. Overrides
+                                  the global timeout set in the Configuration.
+                                type: string
                             type: object
                           sleep:
                             description: Sleep defines zzzz.
@@ -543,10 +595,6 @@ spec:
                             required:
                             - duration
                             type: object
-                          timeout:
-                            description: Timeout for the operation. Overrides the
-                              global timeout set in the Configuration.
-                            type: string
                         type: object
                       type: array
                   required:
diff --git a/pkg/data/crds/chainsaw.kyverno.io_teststeps.yaml b/pkg/data/crds/chainsaw.kyverno.io_teststeps.yaml
index f126d468..44994d96 100644
--- a/pkg/data/crds/chainsaw.kyverno.io_teststeps.yaml
+++ b/pkg/data/crds/chainsaw.kyverno.io_teststeps.yaml
@@ -61,6 +61,10 @@ spec:
                           description: SkipLogOutput removes the output from the command.
                             Useful for sensitive logs or to reduce noise.
                           type: boolean
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       required:
                       - entrypoint
                       type: object
@@ -79,6 +83,10 @@ spec:
                         selector:
                           description: Selector defines labels selector.
                           type: string
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     podLogs:
                       description: PodLogs determines the pod logs collector to execute.
@@ -102,6 +110,10 @@ spec:
                             if you use a selector, or -1 (all) if you use a pod name.
                             This matches default behavior of `kubectl logs`.
                           type: integer
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     script:
                       description: Script defines a script to run.
@@ -119,6 +131,10 @@ spec:
                           description: SkipLogOutput removes the output from the command.
                             Useful for sensitive logs or to reduce noise.
                           type: boolean
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     sleep:
                       description: Sleep defines zzzz.
@@ -129,10 +145,6 @@ spec:
                       required:
                       - duration
                       type: object
-                    timeout:
-                      description: Timeout for the operation. Overrides the global
-                        timeout set in the Configuration.
-                      type: string
                   type: object
                 type: array
               description:
@@ -165,6 +177,10 @@ spec:
                           description: SkipLogOutput removes the output from the command.
                             Useful for sensitive logs or to reduce noise.
                           type: boolean
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       required:
                       - entrypoint
                       type: object
@@ -183,6 +199,10 @@ spec:
                         selector:
                           description: Selector defines labels selector.
                           type: string
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     podLogs:
                       description: PodLogs determines the pod logs collector to execute.
@@ -206,6 +226,10 @@ spec:
                             if you use a selector, or -1 (all) if you use a pod name.
                             This matches default behavior of `kubectl logs`.
                           type: integer
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     script:
                       description: Script defines a script to run.
@@ -223,6 +247,10 @@ spec:
                           description: SkipLogOutput removes the output from the command.
                             Useful for sensitive logs or to reduce noise.
                           type: boolean
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     sleep:
                       description: Sleep defines zzzz.
@@ -233,10 +261,6 @@ spec:
                       required:
                       - duration
                       type: object
-                    timeout:
-                      description: Timeout for the operation. Overrides the global
-                        timeout set in the Configuration.
-                      type: string
                   type: object
                 type: array
               skipDelete:
@@ -311,6 +335,10 @@ spec:
                           type: object
                           x-kubernetes-embedded-resource: true
                           x-kubernetes-preserve-unknown-fields: true
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     assert:
                       description: Assert represents an assertion to be made. It checks
@@ -324,6 +352,10 @@ spec:
                           type: object
                           x-kubernetes-embedded-resource: true
                           x-kubernetes-preserve-unknown-fields: true
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     command:
                       description: Command defines a command to run.
@@ -345,6 +377,10 @@ spec:
                           description: SkipLogOutput removes the output from the command.
                             Useful for sensitive logs or to reduce noise.
                           type: boolean
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       required:
                       - entrypoint
                       type: object
@@ -389,6 +425,10 @@ spec:
                           type: object
                           x-kubernetes-embedded-resource: true
                           x-kubernetes-preserve-unknown-fields: true
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     delete:
                       description: Delete represents a creation operation.
@@ -438,6 +478,10 @@ spec:
                           - apiVersion
                           - kind
                           type: object
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       required:
                       - ref
                       type: object
@@ -458,6 +502,10 @@ spec:
                           type: object
                           x-kubernetes-embedded-resource: true
                           x-kubernetes-preserve-unknown-fields: true
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     script:
                       description: Script defines a script to run.
@@ -475,6 +523,10 @@ spec:
                           description: SkipLogOutput removes the output from the command.
                             Useful for sensitive logs or to reduce noise.
                           type: boolean
+                        timeout:
+                          description: Timeout for the operation. Overrides the global
+                            timeout set in the Configuration.
+                          type: string
                       type: object
                     sleep:
                       description: Sleep defines zzzz.
@@ -485,10 +537,6 @@ spec:
                       required:
                       - duration
                       type: object
-                    timeout:
-                      description: Timeout for the operation. Overrides the global
-                        timeout set in the Configuration.
-                      type: string
                   type: object
                 type: array
             required:
diff --git a/pkg/runner/operations/internal/diff_test.go b/pkg/runner/operations/internal/diff_test.go
deleted file mode 100644
index b3dc9ace..00000000
--- a/pkg/runner/operations/internal/diff_test.go
+++ /dev/null
@@ -1,124 +0,0 @@
-package internal
-
-import (
-	"fmt"
-	"testing"
-
-	"github.com/pmezard/go-difflib/difflib"
-	"github.com/stretchr/testify/assert"
-	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
-	ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
-)
-
-func mockCtrlClientObject(data map[string]interface{}) ctrlclient.Object {
-	return &unstructured.Unstructured{Object: data}
-}
-
-func TestDiff(t *testing.T) {
-	tests := []struct {
-		name         string
-		expectedData map[string]interface{}
-		actualData   map[string]interface{}
-		expectedDiff string
-	}{
-		{
-			name: "Test simple diff",
-			expectedData: map[string]interface{}{
-				"kind":       "Pod",
-				"apiVersion": "v1",
-				"metadata": map[string]interface{}{
-					"name": "test-pod",
-				},
-				"spec": map[string]interface{}{
-					"containers": []interface{}{
-						map[string]interface{}{
-							"name":  "test-container",
-							"image": "test-image:v1",
-						},
-					},
-				},
-			},
-			actualData: map[string]interface{}{
-				"kind":       "Pod",
-				"apiVersion": "v1",
-				"metadata": map[string]interface{}{
-					"name": "test-pod",
-				},
-				"spec": map[string]interface{}{
-					"containers": []interface{}{
-						map[string]interface{}{
-							"name":  "test-container",
-							"image": "test-image:v2",
-						},
-					},
-				},
-			},
-			expectedDiff: `--- Expected
-+++ Actual
-@@ -4,6 +4,6 @@
-   name: test-pod
- spec:
-   containers:
--  - image: test-image:v1
-+  - image: test-image:v2
-     name: test-container`,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			expected := mockCtrlClientObject(tt.expectedData)
-			actual := mockCtrlClientObject(tt.actualData)
-
-			gotDiff, err := diff(expected, actual)
-			if err != nil {
-				t.Fatalf("unexpected error: %v", err)
-			}
-
-			assert.EqualValues(t, tt.expectedDiff, gotDiff)
-		})
-	}
-}
-
-func Test_diffHelper_Error(t *testing.T) {
-	mockData := &unstructured.Unstructured{}
-
-	testCases := []struct {
-		name              string
-		expected          interface{}
-		actual            interface{}
-		expectedMarshaler yamlMarshaler
-		actualMarshaler   yamlMarshaler
-		getDiffString     diffLibInterface
-	}{
-		{
-			name:     "error in expected marshaler",
-			expected: mockData,
-			actual:   mockData,
-			expectedMarshaler: func(obj interface{}) ([]byte, error) {
-				return nil, fmt.Errorf("expected marshal error")
-			},
-		},
-		{
-			name:     "error in actual marshaler",
-			expected: mockData,
-			actual:   mockData,
-			actualMarshaler: func(obj interface{}) ([]byte, error) {
-				return nil, fmt.Errorf("actual marshal error")
-			},
-		},
-		{
-			name:          "error in getting diff string",
-			expected:      mockData,
-			actual:        mockData,
-			getDiffString: func(diff difflib.UnifiedDiff) (string, error) { return "", fmt.Errorf("diff string error") },
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			_, err := diffHelper(tc.expected, tc.actual, tc.expectedMarshaler, tc.actualMarshaler, tc.getDiffString)
-			assert.Error(t, err)
-		})
-	}
-}
diff --git a/pkg/runner/processors/test.go b/pkg/runner/processors/test.go
index 32927f8e..0fe5cd38 100644
--- a/pkg/runner/processors/test.go
+++ b/pkg/runner/processors/test.go
@@ -124,7 +124,7 @@ func (p *testProcessor) Run(ctx context.Context, nspacer namespacer.Namespacer)
 				timeouts := timeout.Combine(p.config.Timeouts, p.test.Spec.Timeouts)
 				operation := operation{
 					continueOnError: false,
-					timeout:         timeouts.CleanupDuration(),
+					timeout:         timeout.Get(nil, 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 89f9628c..bbe094dc 100644
--- a/pkg/runner/processors/tests.go
+++ b/pkg/runner/processors/tests.go
@@ -13,6 +13,7 @@ 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"
@@ -72,7 +73,7 @@ func (p *testsProcessor) Run(ctx context.Context) {
 			t.Cleanup(func() {
 				operation := operation{
 					continueOnError: false,
-					timeout:         p.config.Timeouts.CleanupDuration(),
+					timeout:         timeout.Get(nil, 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 dbf206f0..72128051 100644
--- a/pkg/runner/timeout/timeout_test.go
+++ b/pkg/runner/timeout/timeout_test.go
@@ -15,17 +15,17 @@ func TestGet(t *testing.T) {
 		name      string
 		fallback  time.Duration
 		operation *metav1.Duration
-		want      time.Duration
+		want      *time.Duration
 	}{{
 		name:      "fallback",
 		fallback:  fallback,
 		operation: nil,
-		want:      fallback,
+		want:      &fallback,
 	}, {
 		name:      "operation",
 		fallback:  fallback,
 		operation: &operation,
-		want:      operation.Duration,
+		want:      &operation.Duration,
 	}}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/testdata/e2e/examples/sleep/chainsaw-test.yaml b/testdata/e2e/examples/sleep/chainsaw-test.yaml
index 702b9bf0..c3b041b2 100644
--- a/testdata/e2e/examples/sleep/chainsaw-test.yaml
+++ b/testdata/e2e/examples/sleep/chainsaw-test.yaml
@@ -6,7 +6,7 @@ spec:
   steps:
   - try:
     - sleep:
-        duration: 5s
+        duration: 10s
     catch:
     - sleep:
         duration: 5s
diff --git a/testdata/e2e/examples/timeout/chainsaw-test.yaml b/testdata/e2e/examples/timeout/chainsaw-test.yaml
index 70bec762..9d3899fc 100644
--- a/testdata/e2e/examples/timeout/chainsaw-test.yaml
+++ b/testdata/e2e/examples/timeout/chainsaw-test.yaml
@@ -7,6 +7,6 @@ spec:
   - try:
     - script:
         content: sleep 5
+        timeout: 3s
         check:
           ($error != null): true
-      timeout: 3s
diff --git a/testdata/step/custom-step.yaml b/testdata/step/custom-step.yaml
index e9aaaf7e..c6ef4abe 100644
--- a/testdata/step/custom-step.yaml
+++ b/testdata/step/custom-step.yaml
@@ -7,15 +7,15 @@ spec:
   try:
   - assert:
       file: /assertions/verify-service.yaml
-    timeout: 30s
+      timeout: 30s
     continueOnError: false
   - apply:
       file: /configs/service-config.yaml
-    timeout: 45s
+      timeout: 45s
     continueOnError: true
   - error:
       file: /errors/expected-timeout-error.yaml
-    timeout: 15s
+      timeout: 15s
     continueOnError: true
   - delete:
       ref:
@@ -23,7 +23,7 @@ spec:
         kind: Pod
         name: example-pod
         namespace: test
-    timeout: 10s
+      timeout: 10s
     continueOnError: false
   - command:
       skipLogOutput: false
diff --git a/testdata/test/custom-test.yaml b/testdata/test/custom-test.yaml
index d9878d78..39e6adb3 100644
--- a/testdata/test/custom-test.yaml
+++ b/testdata/test/custom-test.yaml
@@ -10,20 +10,20 @@ spec:
   steps:
   - name: "test-step-initial-setup"
     timeouts:
-      apply: "5s"
+      apply: 5s
     skipDelete: false
     try:
     - apply:
         file: /configs/service-config.yaml
-      timeout: 45s
+        timeout: 45s
       continueOnError: true
   - name: "test-step-apply-changes"
     try:
     - apply:
         file: /configs/service-config.yaml
-      timeout: 45s
+        timeout: 45s
       continueOnError: true
     - assert:
         file: /assertions/verify-service.yaml
-      timeout: 30s
+        timeout: 30s
       continueOnError: false