You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by pc...@apache.org on 2024/01/12 11:42:27 UTC

(camel-k) 02/03: feat(pipeline): publishing user task

This is an automated email from the ASF dual-hosted git repository.

pcongiusti pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit dbea42fb03cd0a4c180be3d5c285060af5bd2573
Author: Pasquale Congiusti <pa...@gmail.com>
AuthorDate: Wed Jan 10 17:23:39 2024 +0100

    feat(pipeline): publishing user task
    
    Assuming the last task of a pipeline is a publishing task and if it provides the digest of a published image, then, the build execution succeed
---
 config/crd/bases/camel.apache.org_builds.yaml      |  3 +
 .../bases/camel.apache.org_integrationkits.yaml    | 10 +--
 .../camel.apache.org_integrationplatforms.yaml     | 20 ++---
 .../crd/bases/camel.apache.org_integrations.yaml   | 10 +--
 .../bases/camel.apache.org_kameletbindings.yaml    |  9 +--
 config/crd/bases/camel.apache.org_pipes.yaml       |  9 +--
 docs/modules/ROOT/partials/apis/camel-k-crds.adoc  | 11 ++-
 docs/modules/traits/pages/builder.adoc             |  4 +-
 helm/camel-k/crds/crd-build.yaml                   |  3 +
 helm/camel-k/crds/crd-integration-kit.yaml         | 10 +--
 helm/camel-k/crds/crd-integration-platform.yaml    | 20 ++---
 helm/camel-k/crds/crd-integration.yaml             | 10 +--
 helm/camel-k/crds/crd-kamelet-binding.yaml         |  9 +--
 helm/camel-k/crds/crd-pipe.yaml                    |  9 +--
 pkg/apis/camel/v1/build_types.go                   |  2 +
 .../camel/applyconfiguration/camel/v1/usertask.go  |  9 +++
 pkg/controller/build/build_pod.go                  |  1 +
 pkg/controller/build/monitor_pod.go                | 94 +++++++++++++++++-----
 pkg/resources/resources.go                         | 45 +++++++----
 pkg/trait/builder.go                               | 75 +++++++++++++----
 pkg/trait/builder_test.go                          | 53 +++++++-----
 pkg/util/test/client.go                            |  2 +
 resources/traits.yaml                              |  8 +-
 23 files changed, 293 insertions(+), 133 deletions(-)

diff --git a/config/crd/bases/camel.apache.org_builds.yaml b/config/crd/bases/camel.apache.org_builds.yaml
index cb486ef73..02965a6bc 100644
--- a/config/crd/bases/camel.apache.org_builds.yaml
+++ b/config/crd/bases/camel.apache.org_builds.yaml
@@ -818,6 +818,9 @@ spec:
                         name:
                           description: name of the task
                           type: string
+                        publishingImage:
+                          description: the desired image build name
+                          type: string
                       type: object
                     jib:
                       description: a JibTask, for Jib strategy
diff --git a/config/crd/bases/camel.apache.org_integrationkits.yaml b/config/crd/bases/camel.apache.org_integrationkits.yaml
index 3d4714344..6ec3bd07d 100644
--- a/config/crd/bases/camel.apache.org_integrationkits.yaml
+++ b/config/crd/bases/camel.apache.org_integrationkits.yaml
@@ -285,11 +285,11 @@ spec:
                           type: string
                         type: array
                       tasksFilter:
-                        description: A list of tasks (available only when using `pod`
-                          strategy), sorted by the order of execution in a csv format,
-                          ie, `<taskName1>,<taskName2>,...`. Mind that you must include
-                          also the operator tasks (`builder`, `quarkus-native`, `package`,
-                          `jib`, `spectrum`, `s2i`) if you need to execute them.
+                        description: A list of tasks sorted by the order of execution
+                          in a csv format, ie, `<taskName1>,<taskName2>,...`. Mind
+                          that you must include also the operator tasks (`builder`,
+                          `quarkus-native`, `package`, `jib`, `spectrum`, `s2i`) if
+                          you need to execute them. Useful only wih `pod` strategy.
                         type: string
                       tasksLimitCPU:
                         description: A list of limit cpu configuration for the specific
diff --git a/config/crd/bases/camel.apache.org_integrationplatforms.yaml b/config/crd/bases/camel.apache.org_integrationplatforms.yaml
index 240fb4143..9a7c238c9 100644
--- a/config/crd/bases/camel.apache.org_integrationplatforms.yaml
+++ b/config/crd/bases/camel.apache.org_integrationplatforms.yaml
@@ -590,11 +590,11 @@ spec:
                           type: string
                         type: array
                       tasksFilter:
-                        description: A list of tasks (available only when using `pod`
-                          strategy), sorted by the order of execution in a csv format,
-                          ie, `<taskName1>,<taskName2>,...`. Mind that you must include
-                          also the operator tasks (`builder`, `quarkus-native`, `package`,
-                          `jib`, `spectrum`, `s2i`) if you need to execute them.
+                        description: A list of tasks sorted by the order of execution
+                          in a csv format, ie, `<taskName1>,<taskName2>,...`. Mind
+                          that you must include also the operator tasks (`builder`,
+                          `quarkus-native`, `package`, `jib`, `spectrum`, `s2i`) if
+                          you need to execute them. Useful only wih `pod` strategy.
                         type: string
                       tasksLimitCPU:
                         description: A list of limit cpu configuration for the specific
@@ -2480,11 +2480,11 @@ spec:
                           type: string
                         type: array
                       tasksFilter:
-                        description: A list of tasks (available only when using `pod`
-                          strategy), sorted by the order of execution in a csv format,
-                          ie, `<taskName1>,<taskName2>,...`. Mind that you must include
-                          also the operator tasks (`builder`, `quarkus-native`, `package`,
-                          `jib`, `spectrum`, `s2i`) if you need to execute them.
+                        description: A list of tasks sorted by the order of execution
+                          in a csv format, ie, `<taskName1>,<taskName2>,...`. Mind
+                          that you must include also the operator tasks (`builder`,
+                          `quarkus-native`, `package`, `jib`, `spectrum`, `s2i`) if
+                          you need to execute them. Useful only wih `pod` strategy.
                         type: string
                       tasksLimitCPU:
                         description: A list of limit cpu configuration for the specific
diff --git a/config/crd/bases/camel.apache.org_integrations.yaml b/config/crd/bases/camel.apache.org_integrations.yaml
index fa9fd5c0c..f0018b106 100644
--- a/config/crd/bases/camel.apache.org_integrations.yaml
+++ b/config/crd/bases/camel.apache.org_integrations.yaml
@@ -6503,11 +6503,11 @@ spec:
                           type: string
                         type: array
                       tasksFilter:
-                        description: A list of tasks (available only when using `pod`
-                          strategy), sorted by the order of execution in a csv format,
-                          ie, `<taskName1>,<taskName2>,...`. Mind that you must include
-                          also the operator tasks (`builder`, `quarkus-native`, `package`,
-                          `jib`, `spectrum`, `s2i`) if you need to execute them.
+                        description: A list of tasks sorted by the order of execution
+                          in a csv format, ie, `<taskName1>,<taskName2>,...`. Mind
+                          that you must include also the operator tasks (`builder`,
+                          `quarkus-native`, `package`, `jib`, `spectrum`, `s2i`) if
+                          you need to execute them. Useful only wih `pod` strategy.
                         type: string
                       tasksLimitCPU:
                         description: A list of limit cpu configuration for the specific
diff --git a/config/crd/bases/camel.apache.org_kameletbindings.yaml b/config/crd/bases/camel.apache.org_kameletbindings.yaml
index ad82cf8c1..cc0b8e10e 100644
--- a/config/crd/bases/camel.apache.org_kameletbindings.yaml
+++ b/config/crd/bases/camel.apache.org_kameletbindings.yaml
@@ -6787,12 +6787,11 @@ spec:
                               type: string
                             type: array
                           tasksFilter:
-                            description: A list of tasks (available only when using
-                              `pod` strategy), sorted by the order of execution in
-                              a csv format, ie, `<taskName1>,<taskName2>,...`. Mind
-                              that you must include also the operator tasks (`builder`,
+                            description: A list of tasks sorted by the order of execution
+                              in a csv format, ie, `<taskName1>,<taskName2>,...`.
+                              Mind that you must include also the operator tasks (`builder`,
                               `quarkus-native`, `package`, `jib`, `spectrum`, `s2i`)
-                              if you need to execute them.
+                              if you need to execute them. Useful only wih `pod` strategy.
                             type: string
                           tasksLimitCPU:
                             description: A list of limit cpu configuration for the
diff --git a/config/crd/bases/camel.apache.org_pipes.yaml b/config/crd/bases/camel.apache.org_pipes.yaml
index d018e4349..35e8fdc8c 100644
--- a/config/crd/bases/camel.apache.org_pipes.yaml
+++ b/config/crd/bases/camel.apache.org_pipes.yaml
@@ -6785,12 +6785,11 @@ spec:
                               type: string
                             type: array
                           tasksFilter:
-                            description: A list of tasks (available only when using
-                              `pod` strategy), sorted by the order of execution in
-                              a csv format, ie, `<taskName1>,<taskName2>,...`. Mind
-                              that you must include also the operator tasks (`builder`,
+                            description: A list of tasks sorted by the order of execution
+                              in a csv format, ie, `<taskName1>,<taskName2>,...`.
+                              Mind that you must include also the operator tasks (`builder`,
                               `quarkus-native`, `package`, `jib`, `spectrum`, `s2i`)
-                              if you need to execute them.
+                              if you need to execute them. Useful only wih `pod` strategy.
                             type: string
                           tasksLimitCPU:
                             description: A list of limit cpu configuration for the
diff --git a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
index 29cc535a7..f2ec00ef0 100644
--- a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
+++ b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
@@ -5674,6 +5674,13 @@ Deprecated: use ContainerCommands
 
 the command to execute
 
+|`publishingImage` +
+string
+|
+
+
+the desired image build name
+
 
 |===
 
@@ -5892,9 +5899,9 @@ string
 |
 
 
-A list of tasks (available only when using `pod` strategy), sorted by the order of execution in a csv format, ie, `<taskName1>,<taskName2>,...`.
+A list of tasks sorted by the order of execution in a csv format, ie, `<taskName1>,<taskName2>,...`.
 Mind that you must include also the operator tasks (`builder`, `quarkus-native`, `package`, `jib`, `spectrum`, `s2i`)
-if you need to execute them.
+if you need to execute them. Useful only wih `pod` strategy.
 
 |`tasksRequestCPU` +
 []string
diff --git a/docs/modules/traits/pages/builder.adoc b/docs/modules/traits/pages/builder.adoc
index 6330dea1e..8560b38f9 100755
--- a/docs/modules/traits/pages/builder.adoc
+++ b/docs/modules/traits/pages/builder.adoc
@@ -84,9 +84,9 @@ Syntax: [configmap\|secret]:name[/key], where name represents the resource name,
 
 | builder.tasks-filter
 | string
-| A list of tasks (available only when using `pod` strategy), sorted by the order of execution in a csv format, ie, `<taskName1>,<taskName2>,...`.
+| A list of tasks sorted by the order of execution in a csv format, ie, `<taskName1>,<taskName2>,...`.
 Mind that you must include also the operator tasks (`builder`, `quarkus-native`, `package`, `jib`, `spectrum`, `s2i`)
-if you need to execute them.
+if you need to execute them. Useful only wih `pod` strategy.
 
 | builder.tasks-request-cpu
 | []string
diff --git a/helm/camel-k/crds/crd-build.yaml b/helm/camel-k/crds/crd-build.yaml
index cb486ef73..02965a6bc 100644
--- a/helm/camel-k/crds/crd-build.yaml
+++ b/helm/camel-k/crds/crd-build.yaml
@@ -818,6 +818,9 @@ spec:
                         name:
                           description: name of the task
                           type: string
+                        publishingImage:
+                          description: the desired image build name
+                          type: string
                       type: object
                     jib:
                       description: a JibTask, for Jib strategy
diff --git a/helm/camel-k/crds/crd-integration-kit.yaml b/helm/camel-k/crds/crd-integration-kit.yaml
index 3d4714344..6ec3bd07d 100644
--- a/helm/camel-k/crds/crd-integration-kit.yaml
+++ b/helm/camel-k/crds/crd-integration-kit.yaml
@@ -285,11 +285,11 @@ spec:
                           type: string
                         type: array
                       tasksFilter:
-                        description: A list of tasks (available only when using `pod`
-                          strategy), sorted by the order of execution in a csv format,
-                          ie, `<taskName1>,<taskName2>,...`. Mind that you must include
-                          also the operator tasks (`builder`, `quarkus-native`, `package`,
-                          `jib`, `spectrum`, `s2i`) if you need to execute them.
+                        description: A list of tasks sorted by the order of execution
+                          in a csv format, ie, `<taskName1>,<taskName2>,...`. Mind
+                          that you must include also the operator tasks (`builder`,
+                          `quarkus-native`, `package`, `jib`, `spectrum`, `s2i`) if
+                          you need to execute them. Useful only wih `pod` strategy.
                         type: string
                       tasksLimitCPU:
                         description: A list of limit cpu configuration for the specific
diff --git a/helm/camel-k/crds/crd-integration-platform.yaml b/helm/camel-k/crds/crd-integration-platform.yaml
index 240fb4143..9a7c238c9 100644
--- a/helm/camel-k/crds/crd-integration-platform.yaml
+++ b/helm/camel-k/crds/crd-integration-platform.yaml
@@ -590,11 +590,11 @@ spec:
                           type: string
                         type: array
                       tasksFilter:
-                        description: A list of tasks (available only when using `pod`
-                          strategy), sorted by the order of execution in a csv format,
-                          ie, `<taskName1>,<taskName2>,...`. Mind that you must include
-                          also the operator tasks (`builder`, `quarkus-native`, `package`,
-                          `jib`, `spectrum`, `s2i`) if you need to execute them.
+                        description: A list of tasks sorted by the order of execution
+                          in a csv format, ie, `<taskName1>,<taskName2>,...`. Mind
+                          that you must include also the operator tasks (`builder`,
+                          `quarkus-native`, `package`, `jib`, `spectrum`, `s2i`) if
+                          you need to execute them. Useful only wih `pod` strategy.
                         type: string
                       tasksLimitCPU:
                         description: A list of limit cpu configuration for the specific
@@ -2480,11 +2480,11 @@ spec:
                           type: string
                         type: array
                       tasksFilter:
-                        description: A list of tasks (available only when using `pod`
-                          strategy), sorted by the order of execution in a csv format,
-                          ie, `<taskName1>,<taskName2>,...`. Mind that you must include
-                          also the operator tasks (`builder`, `quarkus-native`, `package`,
-                          `jib`, `spectrum`, `s2i`) if you need to execute them.
+                        description: A list of tasks sorted by the order of execution
+                          in a csv format, ie, `<taskName1>,<taskName2>,...`. Mind
+                          that you must include also the operator tasks (`builder`,
+                          `quarkus-native`, `package`, `jib`, `spectrum`, `s2i`) if
+                          you need to execute them. Useful only wih `pod` strategy.
                         type: string
                       tasksLimitCPU:
                         description: A list of limit cpu configuration for the specific
diff --git a/helm/camel-k/crds/crd-integration.yaml b/helm/camel-k/crds/crd-integration.yaml
index fa9fd5c0c..f0018b106 100644
--- a/helm/camel-k/crds/crd-integration.yaml
+++ b/helm/camel-k/crds/crd-integration.yaml
@@ -6503,11 +6503,11 @@ spec:
                           type: string
                         type: array
                       tasksFilter:
-                        description: A list of tasks (available only when using `pod`
-                          strategy), sorted by the order of execution in a csv format,
-                          ie, `<taskName1>,<taskName2>,...`. Mind that you must include
-                          also the operator tasks (`builder`, `quarkus-native`, `package`,
-                          `jib`, `spectrum`, `s2i`) if you need to execute them.
+                        description: A list of tasks sorted by the order of execution
+                          in a csv format, ie, `<taskName1>,<taskName2>,...`. Mind
+                          that you must include also the operator tasks (`builder`,
+                          `quarkus-native`, `package`, `jib`, `spectrum`, `s2i`) if
+                          you need to execute them. Useful only wih `pod` strategy.
                         type: string
                       tasksLimitCPU:
                         description: A list of limit cpu configuration for the specific
diff --git a/helm/camel-k/crds/crd-kamelet-binding.yaml b/helm/camel-k/crds/crd-kamelet-binding.yaml
index ad82cf8c1..cc0b8e10e 100644
--- a/helm/camel-k/crds/crd-kamelet-binding.yaml
+++ b/helm/camel-k/crds/crd-kamelet-binding.yaml
@@ -6787,12 +6787,11 @@ spec:
                               type: string
                             type: array
                           tasksFilter:
-                            description: A list of tasks (available only when using
-                              `pod` strategy), sorted by the order of execution in
-                              a csv format, ie, `<taskName1>,<taskName2>,...`. Mind
-                              that you must include also the operator tasks (`builder`,
+                            description: A list of tasks sorted by the order of execution
+                              in a csv format, ie, `<taskName1>,<taskName2>,...`.
+                              Mind that you must include also the operator tasks (`builder`,
                               `quarkus-native`, `package`, `jib`, `spectrum`, `s2i`)
-                              if you need to execute them.
+                              if you need to execute them. Useful only wih `pod` strategy.
                             type: string
                           tasksLimitCPU:
                             description: A list of limit cpu configuration for the
diff --git a/helm/camel-k/crds/crd-pipe.yaml b/helm/camel-k/crds/crd-pipe.yaml
index d018e4349..35e8fdc8c 100644
--- a/helm/camel-k/crds/crd-pipe.yaml
+++ b/helm/camel-k/crds/crd-pipe.yaml
@@ -6785,12 +6785,11 @@ spec:
                               type: string
                             type: array
                           tasksFilter:
-                            description: A list of tasks (available only when using
-                              `pod` strategy), sorted by the order of execution in
-                              a csv format, ie, `<taskName1>,<taskName2>,...`. Mind
-                              that you must include also the operator tasks (`builder`,
+                            description: A list of tasks sorted by the order of execution
+                              in a csv format, ie, `<taskName1>,<taskName2>,...`.
+                              Mind that you must include also the operator tasks (`builder`,
                               `quarkus-native`, `package`, `jib`, `spectrum`, `s2i`)
-                              if you need to execute them.
+                              if you need to execute them. Useful only wih `pod` strategy.
                             type: string
                           tasksLimitCPU:
                             description: A list of limit cpu configuration for the
diff --git a/pkg/apis/camel/v1/build_types.go b/pkg/apis/camel/v1/build_types.go
index 5e2a99681..076cf0426 100644
--- a/pkg/apis/camel/v1/build_types.go
+++ b/pkg/apis/camel/v1/build_types.go
@@ -192,6 +192,8 @@ type UserTask struct {
 	ContainerCommand string `json:"command,omitempty"`
 	// the command to execute
 	ContainerCommands []string `json:"commands,omitempty"`
+	// the desired image build name
+	PublishingImage string `json:"publishingImage,omitempty"`
 }
 
 // BuildStatus defines the observed state of Build.
diff --git a/pkg/client/camel/applyconfiguration/camel/v1/usertask.go b/pkg/client/camel/applyconfiguration/camel/v1/usertask.go
index f4fc11c89..7a85991d1 100644
--- a/pkg/client/camel/applyconfiguration/camel/v1/usertask.go
+++ b/pkg/client/camel/applyconfiguration/camel/v1/usertask.go
@@ -26,6 +26,7 @@ type UserTaskApplyConfiguration struct {
 	ContainerImage             *string  `json:"image,omitempty"`
 	ContainerCommand           *string  `json:"command,omitempty"`
 	ContainerCommands          []string `json:"commands,omitempty"`
+	PublishingImage            *string  `json:"publishingImage,omitempty"`
 }
 
 // UserTaskApplyConfiguration constructs an declarative configuration of the UserTask type for use with
@@ -75,3 +76,11 @@ func (b *UserTaskApplyConfiguration) WithContainerCommands(values ...string) *Us
 	}
 	return b
 }
+
+// WithPublishingImage sets the PublishingImage field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the PublishingImage field is set to the value of the last call.
+func (b *UserTaskApplyConfiguration) WithPublishingImage(value string) *UserTaskApplyConfiguration {
+	b.PublishingImage = &value
+	return b
+}
diff --git a/pkg/controller/build/build_pod.go b/pkg/controller/build/build_pod.go
index 5396573a7..4914e771d 100644
--- a/pkg/controller/build/build_pod.go
+++ b/pkg/controller/build/build_pod.go
@@ -572,6 +572,7 @@ func addCustomTaskToPod(build *v1.Build, task *v1.UserTask, pod *corev1.Pod) {
 		WorkingDir:      filepath.Join(builderDir, build.Name),
 		Env:             proxyFromEnvironment(),
 	}
+	container.Env = append(container.Env, corev1.EnvVar{Name: "INTEGRATION_KIT_IMAGE", Value: task.PublishingImage})
 
 	configureResources(task.Name, build, &container)
 	addContainerToPod(build, container, pod)
diff --git a/pkg/controller/build/monitor_pod.go b/pkg/controller/build/monitor_pod.go
index 31fe5c540..0fcf60a72 100644
--- a/pkg/controller/build/monitor_pod.go
+++ b/pkg/controller/build/monitor_pod.go
@@ -146,24 +146,20 @@ func (action *monitorPodAction) Handle(ctx context.Context, build *v1.Build) (*v
 		// Account for the Build metrics
 		observeBuildResult(build, build.Status.Phase, buildCreator, duration)
 
-		for _, task := range build.Spec.Tasks {
-			if t := task.Buildah; t != nil {
-				build.Status.Image = t.Image
-
-				break
-			} else if t := task.Kaniko; t != nil {
-				build.Status.Image = t.Image
-
-				break
-			}
-		}
-		// Reconcile image digest from build container status if available
-		for _, container := range pod.Status.ContainerStatuses {
-			if container.Name == "buildah" {
-				build.Status.Digest = container.State.Terminated.Message
-
-				break
-			}
+		build.Status.Image = publishTaskImageName(build.Spec.Tasks)
+		build.Status.Digest = publishTaskDigest(build.Spec.Tasks, pod.Status.ContainerStatuses)
+		if build.Status.Digest == "" {
+			// Likely to happen for users provided publishing tasks and not providing the digest image among statuses
+			build.Status.Phase = v1.BuildPhaseError
+			build.Status.SetCondition(
+				"ImageDigestAvailable",
+				corev1.ConditionFalse,
+				"ImageDigestAvailable",
+				fmt.Sprintf(
+					"%s publishing task completed but no digest is available in container status. Make sure that the process successfully push the image to the registry and write its digest to /dev/termination-log",
+					publishTaskName(build.Spec.Tasks),
+				),
+			)
 		}
 
 	case corev1.PodFailed:
@@ -341,3 +337,65 @@ func (action *monitorPodAction) setConditionsFromTerminationMessages(ctx context
 	}
 
 }
+
+// we expect that the last task is any of the supported publishing task
+// or a custom user task
+func publishTask(tasks []v1.Task) *v1.Task {
+	if len(tasks) > 0 {
+		return &tasks[len(tasks)-1]
+
+	}
+
+	return nil
+}
+
+func publishTaskImageName(tasks []v1.Task) string {
+	t := publishTask(tasks)
+	if t == nil {
+		return ""
+	}
+	if t.Custom != nil {
+		return t.Custom.PublishingImage
+	} else if t.Spectrum != nil {
+		return t.Spectrum.Image
+	} else if t.Jib != nil {
+		return t.Jib.Image
+	} else if t.Buildah != nil {
+		return t.Buildah.Image
+	} else if t.Kaniko != nil {
+		return t.Kaniko.Image
+	}
+
+	return ""
+}
+
+func publishTaskName(tasks []v1.Task) string {
+	t := publishTask(tasks)
+	if t == nil {
+		return ""
+	}
+	if t.Custom != nil {
+		return t.Custom.Name
+	} else if t.Spectrum != nil {
+		return t.Spectrum.Name
+	} else if t.Jib != nil {
+		return t.Jib.Name
+	} else if t.Buildah != nil {
+		return t.Buildah.Name
+	} else if t.Kaniko != nil {
+		return t.Kaniko.Name
+	}
+
+	return ""
+}
+
+func publishTaskDigest(tasks []v1.Task, cntStates []corev1.ContainerStatus) string {
+	taskName := publishTaskName(tasks)
+	// Reconcile image digest from build container status if available
+	for _, container := range cntStates {
+		if container.Name == taskName {
+			return container.State.Terminated.Message
+		}
+	}
+	return ""
+}
diff --git a/pkg/resources/resources.go b/pkg/resources/resources.go
index 536db4af3..04bba969b 100644
--- a/pkg/resources/resources.go
+++ b/pkg/resources/resources.go
@@ -117,9 +117,9 @@ var assets = func() http.FileSystem {
 		"/crd/bases/camel.apache.org_builds.yaml": &vfsgen۰CompressedFileInfo{
 			name:             "camel.apache.org_builds.yaml",
 			modTime:          time.Time{},
-			uncompressedSize: 95394,
+			uncompressedSize: 95542,
 
-			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x6b\x73\x1b\x37\x96\xe8\x77\xfd\x8a\x53\xf1\x07\xcb\x55\x22\x35\xe3\xc9\xce\x66\x75\x6b\xeb\x96\x56\x4e\x66\x35\x4e\x6c\xaf\x29\x7b\x32\xb5\xb5\x55\x02\xbb\x0f\x49\x84\xdd\x40\x5f\x00\x2d\x9a\xb9\x75\xff\xfb\x2d\xbc\xfa\x21\xb2\xbb\x01\x8a\xb4\x9d\x72\xe3\x4b\x62\xaa\x01\x9c\x83\xc7\x79\xe1\x3c\x9e\xc1\xe4\x78\xed\xec\x19\xfc\x4c\x13\x64\x12\x53\x50\x1c\xd4\x0a\xe1\xba\x20\xc9\x0a\x61\xc6\x17\x6a\x43\x04\xc2\x [...]
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x6b\x73\x1b\x37\x96\xe8\x77\xfd\x8a\x53\xf1\x07\xcb\x55\x22\x35\xe3\x64\x67\xb3\xba\xb5\x75\x4b\x2b\x27\xb3\x1a\x27\xb6\xd7\x94\x3d\x99\xda\xda\x2a\x81\xdd\x87\x24\xc2\x6e\xa0\x2f\x80\x16\xcd\xdc\xba\xff\xfd\x16\x5e\xfd\x10\xd9\xdd\x00\x45\xda\x9e\x72\xe3\x4b\x62\xaa\x01\x9c\x83\xc7\x79\xe1\x3c\x9e\xc1\xe4\x78\xed\xec\x19\xfc\x42\x13\x64\x12\x53\x50\x1c\xd4\x0a\xe1\xba\x20\xc9\x0a\x61\xc6\x17\x6a\x43\x04\xc2\x [...]
 		},
 		"/crd/bases/camel.apache.org_camelcatalogs.yaml": &vfsgen۰CompressedFileInfo{
 			name:             "camel.apache.org_camelcatalogs.yaml",
@@ -131,30 +131,30 @@ var assets = func() http.FileSystem {
 		"/crd/bases/camel.apache.org_integrationkits.yaml": &vfsgen۰CompressedFileInfo{
 			name:             "camel.apache.org_integrationkits.yaml",
 			modTime:          time.Time{},
-			uncompressedSize: 25734,
+			uncompressedSize: 26227,
 
-			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x3c\x5d\x6f\x1b\xb7\xb2\xef\xfa\x15\x83\xf8\x21\x36\x20\xc9\xed\xb9\x45\x71\xa1\xd3\x5b\xc0\x75\x92\x1e\x9f\x38\x89\xaf\xe5\xb4\x38\x38\x2d\x20\x6a\x77\x24\x31\xda\x25\xb7\x24\x57\xb6\xee\xc7\x7f\xbf\xe0\x90\xdc\x0f\x69\x77\xb5\x96\xe3\xde\xf4\xa0\x7a\x49\xbc\x24\x87\xc3\x99\xe1\x7c\x92\x3c\x81\xd1\xe7\xfb\x0d\x4e\xe0\x9a\x47\x28\x34\xc6\x60\x24\x98\x15\xc2\x45\xc6\xa2\x15\xc2\x54\x2e\xcc\x3d\x53\x08\x6f\x64\x2e\x [...]
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x3c\x5d\x6f\xe3\xb6\xb2\xef\xfe\x15\x83\xcd\xc3\x26\x80\xed\xb4\xbd\x45\x71\xe1\xd3\xbb\x40\x9a\xdd\xed\xc9\xd9\xec\x6e\x6e\x9c\x6d\x71\x70\x7a\x00\xd3\xd2\xd8\xe6\x5a\x22\x55\x92\x72\xe2\xfb\xf1\xdf\x2f\x38\x24\xf5\x61\x4b\xb2\xe2\x6c\x7a\xdb\x83\xea\x25\xb1\x44\x0e\x87\x33\xc3\xf9\xe2\x90\x27\x30\xfa\x72\xcf\xe0\x04\xae\x79\x84\x42\x63\x0c\x46\x82\x59\x21\x5c\x64\x2c\x5a\x21\x4c\xe5\xc2\xdc\x33\x85\xf0\x56\xe6\x [...]
 		},
 		"/crd/bases/camel.apache.org_integrationplatforms.yaml": &vfsgen۰CompressedFileInfo{
 			name:             "camel.apache.org_integrationplatforms.yaml",
 			modTime:          time.Time{},
-			uncompressedSize: 202778,
+			uncompressedSize: 203764,
 
-			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x6b\x73\xe3\x36\xb2\xe8\xf7\xfc\x0a\x94\x53\xa7\xc6\x93\xb2\xa4\x99\xdd\x93\x6c\x8e\xcf\xe6\xdc\xeb\x78\x26\x89\x33\x0f\xfb\xda\x9e\xd9\xb3\x95\xa4\x22\x88\x6c\x49\x88\x49\x80\x0b\x80\xb2\x95\xda\x1f\x7f\x0b\x0d\x80\x0f\x49\x04\xa9\x87\x1f\x93\x48\xa9\xda\x1d\x4b\x24\xd0\x68\x34\xfa\x85\x7e\x7c\x4e\x7a\xbb\xfb\x7c\xf6\x39\x79\xcb\x22\xe0\x0a\x62\xa2\x05\xd1\x53\x20\x27\x19\x8d\xa6\x40\xae\xc4\x58\xdf\x52\x09\x [...]
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\xfb\x73\xdb\xb6\xb2\xf0\xef\xfd\x2b\x30\xee\xdc\xb1\xd3\xd1\x23\xe9\xb9\x7d\x5c\xdf\xd3\xf3\x7d\xae\x93\xb6\x6e\xe2\xd8\x9f\xed\xe4\xdc\x33\x6d\xa7\x82\xc8\x95\x84\x98\x04\x78\x01\x50\xb6\x3a\xe7\x8f\xff\x06\x0b\x80\xa4\x1e\x04\xa9\x87\x63\xa7\x95\x3a\xd3\xd8\x16\x09\x2c\x16\x8b\x7d\x61\x1f\x9f\x93\xee\xee\x3e\x9f\x7d\x4e\xde\xb0\x08\xb8\x82\x98\x68\x41\xf4\x04\xc8\x49\x46\xa3\x09\x90\x6b\x31\xd2\x77\x54\x02\x [...]
 		},
 		"/crd/bases/camel.apache.org_integrations.yaml": &vfsgen۰CompressedFileInfo{
 			name:             "camel.apache.org_integrations.yaml",
 			modTime:          time.Time{},
-			uncompressedSize: 519809,
+			uncompressedSize: 520302,
 
-			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xfd\x7b\x73\x1b\x37\x96\x30\x8c\xff\x9f\x4f\x81\xb2\x53\x8f\xa4\x8d\x48\xd9\x99\xd9\xa9\x1d\xff\xa6\x9e\x94\x56\x96\x13\xfd\x62\xcb\x2a\x49\x49\x9e\x94\x93\x4d\xc0\x6e\x90\xc4\xa3\x6e\xa0\x17\x40\x53\xe2\xbc\x7e\xbf\xfb\x5b\x38\x00\xfa\xc2\x5b\x1f\xb4\x44\xc7\x99\x6d\x4c\x55\xc6\x14\xd9\xa7\x71\x39\x38\xf7\xcb\x73\x32\x7a\xba\xf1\xc5\x73\xf2\x96\x27\x4c\x68\x96\x12\x23\x89\x99\x33\x72\x5a\xd0\x64\xce\xc8\x8d\x9c\x [...]
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xfd\x7b\x73\x1b\x37\x96\x30\x8c\xff\x9f\x4f\x81\xb2\x53\x8f\xa4\x8d\x48\xd9\x99\xd9\xa9\x1d\xff\xa6\x9e\x94\x56\x96\x13\xfd\x62\xcb\x2a\x49\x49\x9e\x94\x93\x4d\xc0\x6e\x90\xc4\xa3\x6e\xa0\x17\x40\x53\xe2\xbc\x7e\xbf\xfb\x5b\x38\x00\xfa\xc2\x5b\x1f\xb4\x44\xc7\x99\x6d\x4c\x55\xc6\x14\xd9\xa7\x71\x39\x38\xf7\xcb\x73\x32\x7a\xba\xf1\xc5\x73\xf2\x96\x27\x4c\x68\x96\x12\x23\x89\x99\x33\x72\x5a\xd0\x64\xce\xc8\x8d\x9c\x [...]
 		},
 		"/crd/bases/camel.apache.org_kameletbindings.yaml": &vfsgen۰CompressedFileInfo{
 			name:             "camel.apache.org_kameletbindings.yaml",
 			modTime:          time.Time{},
-			uncompressedSize: 601461,
+			uncompressedSize: 601982,
 
-			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xfd\x6b\x73\x1b\x37\xb6\x30\x0a\x7f\xf7\xaf\x40\xd9\xa9\x47\xd2\x0e\x49\xd9\x99\x99\xd4\x8c\xdf\xa9\x9d\xd2\xc8\x72\xa2\x37\xb6\xcc\xb2\x94\xe4\x49\x39\xd9\x09\xd8\x0d\x92\xd8\xea\x06\x7a\x00\x34\x25\xe6\xf8\xfc\xf7\x53\x58\x00\xfa\xc2\x9b\xb0\x9a\x92\xc6\x9e\x69\x4c\x55\xc6\xa4\xd8\xab\x71\x59\x58\xf7\xcb\x33\x32\xbc\xbf\xf1\xe4\x19\x79\xc3\x13\x26\x34\x4b\x89\x91\xc4\xcc\x19\x39\x29\x68\x32\x67\xe4\x52\x4e\xcd\x [...]
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xfd\x6b\x73\x1b\x37\xb6\x30\x0a\x7f\xf7\xaf\x40\xd9\xa9\x47\xd2\x0e\x49\xd9\x99\x99\xd4\x8c\xdf\xa9\x9d\xd2\xc8\x72\xa2\x37\xb6\xcc\xb2\x94\xe4\x49\x39\xd9\x09\xd8\x0d\x92\xd8\xea\x06\x7a\x00\x34\x25\xe6\xf8\xfc\xf7\x53\x58\x00\xfa\xc2\x9b\xb0\x9a\x92\xc6\x9e\x69\x4c\x55\xc6\xa4\xd8\xab\x71\x59\x58\xf7\xcb\x33\x32\xbc\xbf\xf1\xe4\x19\x79\xc3\x13\x26\x34\x4b\x89\x91\xc4\xcc\x19\x39\x29\x68\x32\x67\xe4\x52\x4e\xcd\x [...]
 		},
 		"/crd/bases/camel.apache.org_kamelets.yaml": &vfsgen۰CompressedFileInfo{
 			name:             "camel.apache.org_kamelets.yaml",
@@ -166,14 +166,26 @@ var assets = func() http.FileSystem {
 		"/crd/bases/camel.apache.org_pipes.yaml": &vfsgen۰CompressedFileInfo{
 			name:             "camel.apache.org_pipes.yaml",
 			modTime:          time.Time{},
-			uncompressedSize: 568839,
+			uncompressedSize: 569360,
 
-			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xfd\x7b\x73\x1b\x37\xb6\x28\x8a\xff\x9f\x4f\x81\x92\x53\x47\xd2\x8e\x48\xd9\x99\x99\xd4\x1e\xff\xa6\x4e\x4a\x5b\x96\x13\xfd\x62\xcb\x2c\x49\x49\x4e\xca\xc9\x4e\xc0\x6e\x90\xc4\x51\x37\xd0\x1b\x40\x53\x62\xae\xef\x77\xbf\x85\x05\xa0\x1f\x7c\x09\xab\x29\x69\xe4\x99\xc6\x54\x65\x4c\x8a\xbd\x1a\x8f\x85\xf5\x7e\xbc\x20\x83\x87\x1b\x5f\xbc\x20\xef\x78\xc2\x84\x66\x29\x31\x92\x98\x19\x23\x27\x05\x4d\x66\x8c\x5c\xc9\x89\x [...]
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xfd\x7b\x73\x1b\x37\xb6\x28\x8a\xff\x9f\x4f\x81\x92\x53\x47\xd2\x8e\x48\xd9\x99\x99\xd4\x1e\xff\xa6\x4e\x4a\x5b\x96\x13\xfd\x62\xcb\x2c\x49\x49\x4e\xca\xc9\x4e\xc0\x6e\x90\xc4\x51\x37\xd0\x1b\x40\x53\x62\xae\xef\x77\xbf\x85\x05\xa0\x1f\x7c\x09\xab\x29\x69\xe4\x99\xc6\x54\x65\x4c\x8a\xbd\x1a\x8f\x85\xf5\x7e\xbc\x20\x83\x87\x1b\x5f\xbc\x20\xef\x78\xc2\x84\x66\x29\x31\x92\x98\x19\x23\x27\x05\x4d\x66\x8c\x5c\xc9\x89\x [...]
 		},
 		"/manager": &vfsgen۰DirInfo{
 			name:    "manager",
 			modTime: time.Time{},
 		},
+		"/manager/bundle": &vfsgen۰DirInfo{
+			name:    "bundle",
+			modTime: time.Time{},
+		},
+		"/manager/bundle/manifests": &vfsgen۰DirInfo{
+			name:    "manifests",
+			modTime: time.Time{},
+		},
+		"/manager/bundle/metadata": &vfsgen۰DirInfo{
+			name:    "metadata",
+			modTime: time.Time{},
+		},
 		"/manager/operator-deployment.yaml": &vfsgen۰CompressedFileInfo{
 			name:             "operator-deployment.yaml",
 			modTime:          time.Time{},
@@ -753,9 +765,9 @@ var assets = func() http.FileSystem {
 		"/traits.yaml": &vfsgen۰CompressedFileInfo{
 			name:             "traits.yaml",
 			modTime:          time.Time{},
-			uncompressedSize: 73382,
+			uncompressedSize: 73737,
 
-			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xbd\x6b\x73\x1c\xb9\x91\x00\xf8\x5d\xbf\x02\xd1\x73\x1b\x24\x75\xfd\xa0\xc6\x6b\xef\x2c\xd7\xf2\x1e\x47\xa3\xb1\x69\xbd\x78\x22\x67\xbc\x0e\x9d\xc2\x8d\xae\x42\x77\x43\xac\x06\x6a\x00\x14\xa9\x9e\xdb\xfb\xef\x17\xc8\x4c\x3c\xaa\xba\x9a\xdd\x94\x48\xd9\xda\xf5\x6e\x84\x47\x24\x0b\x40\x22\x91\x48\xe4\x3b\xbf\x61\xa3\xfb\xfb\xbf\x47\xdf\xb0\x97\xb2\x10\xca\x8a\x92\x39\xcd\xdc\x52\xb0\xd3\x9a\x17\x4b\xc1\x2e\xf4\xdc\x [...]
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xbd\xfb\x73\x1c\xb9\x91\x30\xf8\xbb\xfe\x0a\x44\xcf\x7d\x41\x52\xd7\x0f\x4a\xfe\xec\x9d\xe5\x5a\xb3\xc7\xd1\x68\x6c\x7a\xf4\xe0\x89\x9c\xf1\xe7\xd0\x29\xdc\xe8\x2a\x74\x37\xc4\x6a\xa0\x06\x40\x91\xea\xb9\xbd\xff\xfd\x02\x99\x89\x47\x55\x57\xb3\x9b\x12\x29\x5b\xdf\x7a\x37\xc2\x23\x92\x05\x20\x91\x48\x24\xf2\x9d\xdf\xb0\xd1\xfd\xfd\xdf\xa3\x6f\xd8\x4b\x59\x08\x65\x45\xc9\x9c\x66\x6e\x29\xd8\x69\xcd\x8b\xa5\x60\x17\x [...]
 		},
 	}
 	fs["/"].(*vfsgen۰DirInfo).entries = []os.FileInfo{
@@ -800,6 +812,7 @@ var assets = func() http.FileSystem {
 		fs["/crd/bases/camel.apache.org_pipes.yaml"].(os.FileInfo),
 	}
 	fs["/manager"].(*vfsgen۰DirInfo).entries = []os.FileInfo{
+		fs["/manager/bundle"].(os.FileInfo),
 		fs["/manager/operator-deployment.yaml"].(os.FileInfo),
 		fs["/manager/operator-service-account.yaml"].(os.FileInfo),
 		fs["/manager/patch-image-pull-policy-always.yaml"].(os.FileInfo),
@@ -811,6 +824,10 @@ var assets = func() http.FileSystem {
 		fs["/manager/patch-toleration.yaml"].(os.FileInfo),
 		fs["/manager/patch-watch-namespace-global.yaml"].(os.FileInfo),
 	}
+	fs["/manager/bundle"].(*vfsgen۰DirInfo).entries = []os.FileInfo{
+		fs["/manager/bundle/manifests"].(os.FileInfo),
+		fs["/manager/bundle/metadata"].(os.FileInfo),
+	}
 	fs["/prometheus"].(*vfsgen۰DirInfo).entries = []os.FileInfo{
 		fs["/prometheus/operator-pod-monitor.yaml"].(os.FileInfo),
 		fs["/prometheus/operator-prometheus-rule.yaml"].(os.FileInfo),
diff --git a/pkg/trait/builder.go b/pkg/trait/builder.go
index 87c3e484d..843deb2e4 100644
--- a/pkg/trait/builder.go
+++ b/pkg/trait/builder.go
@@ -167,13 +167,18 @@ func (t *builderTrait) Apply(e *Environment) error {
 	if err != nil {
 		return err
 	}
+
+	imageName := getImageName(e)
 	// Building task
 	builderTask, err := t.builderTask(e, taskConfOrDefault(tasksConf, "builder"))
 	if err != nil {
-		e.IntegrationKit.Status.Phase = v1.IntegrationKitPhaseError
-		e.IntegrationKit.Status.SetCondition("IntegrationKitPropertiesFormatValid", corev1.ConditionFalse,
-			"IntegrationKitPropertiesFormatValid", fmt.Sprintf("One or more properties where not formatted as expected: %s", err.Error()))
-		if err := e.Client.Status().Update(e.Ctx, e.IntegrationKit); err != nil {
+		if err := failIntegrationKit(
+			e,
+			"IntegrationKitPropertiesFormatValid",
+			corev1.ConditionFalse,
+			"IntegrationKitPropertiesFormatValid",
+			fmt.Sprintf("One or more properties where not formatted as expected: %s", err.Error()),
+		); err != nil {
 			return err
 		}
 		return nil
@@ -188,21 +193,21 @@ func (t *builderTrait) Apply(e *Environment) error {
 			realBuildStrategy = e.Platform.Status.Build.BuildConfiguration.Strategy
 		}
 		if len(t.Tasks) > 0 && realBuildStrategy != v1.BuildStrategyPod {
-			e.IntegrationKit.Status.Phase = v1.IntegrationKitPhaseError
-			e.IntegrationKit.Status.SetCondition("IntegrationKitTasksValid",
+			if err := failIntegrationKit(
+				e,
+				"IntegrationKitTasksValid",
 				corev1.ConditionFalse,
 				"IntegrationKitTasksValid",
 				fmt.Sprintf("Pipeline tasks unavailable when using `%s` platform build strategy: use `%s` instead.",
 					realBuildStrategy,
 					v1.BuildStrategyPod),
-			)
-			if err := e.Client.Status().Update(e.Ctx, e.IntegrationKit); err != nil {
+			); err != nil {
 				return err
 			}
 			return nil
 		}
 
-		customTasks, err := t.customTasks(tasksConf)
+		customTasks, err := t.customTasks(tasksConf, imageName)
 		if err != nil {
 			return err
 		}
@@ -228,7 +233,7 @@ func (t *builderTrait) Apply(e *Environment) error {
 			},
 			PublishTask: v1.PublishTask{
 				BaseImage: t.getBaseImage(e),
-				Image:     getImageName(e),
+				Image:     imageName,
 				Registry:  e.Platform.Status.Build.Registry,
 			},
 		}})
@@ -241,7 +246,7 @@ func (t *builderTrait) Apply(e *Environment) error {
 			},
 			PublishTask: v1.PublishTask{
 				BaseImage: t.getBaseImage(e),
-				Image:     getImageName(e),
+				Image:     imageName,
 				Registry:  e.Platform.Status.Build.Registry,
 			},
 		}})
@@ -277,7 +282,7 @@ func (t *builderTrait) Apply(e *Environment) error {
 				Configuration: *taskConfOrDefault(tasksConf, "buildah"),
 			},
 			PublishTask: v1.PublishTask{
-				Image:    getImageName(e),
+				Image:    imageName,
 				Registry: e.Platform.Status.Build.Registry,
 			},
 			Verbose:       t.Verbose,
@@ -301,7 +306,7 @@ func (t *builderTrait) Apply(e *Environment) error {
 				Configuration: *taskConfOrDefault(tasksConf, "kaniko"),
 			},
 			PublishTask: v1.PublishTask{
-				Image:    getImageName(e),
+				Image:    imageName,
 				Registry: e.Platform.Status.Build.Registry,
 			},
 			Cache: v1.KanikoTaskCache{
@@ -317,6 +322,15 @@ func (t *builderTrait) Apply(e *Environment) error {
 	if t.TasksFilter != "" {
 		flt := strings.Split(t.TasksFilter, ",")
 		if pipelineTasks, err = filter(pipelineTasks, flt); err != nil {
+			if err := failIntegrationKit(
+				e,
+				"IntegrationKitTasksValid",
+				corev1.ConditionFalse,
+				"IntegrationKitTasksValid",
+				err.Error(),
+			); err != nil {
+				return err
+			}
 			return err
 		}
 	}
@@ -325,6 +339,16 @@ func (t *builderTrait) Apply(e *Environment) error {
 	return nil
 }
 
+// when this trait fails, we must report the failure into the related IntegrationKit if it affects the success of the Build.
+func failIntegrationKit(e *Environment, conditionType v1.IntegrationKitConditionType, status corev1.ConditionStatus, reason, message string) error {
+	e.IntegrationKit.Status.Phase = v1.IntegrationKitPhaseError
+	e.IntegrationKit.Status.SetCondition(conditionType, status, reason, message)
+	if err := e.Client.Status().Update(e.Ctx, e.IntegrationKit); err != nil {
+		return err
+	}
+	return nil
+}
+
 func (t *builderTrait) builderTask(e *Environment, taskConf *v1.BuildConfiguration) (*v1.BuilderTask, error) {
 	maven := v1.MavenBuildSpec{
 		MavenSpec: e.Platform.Status.Build.Maven,
@@ -454,7 +478,7 @@ func (t *builderTrait) getBaseImage(e *Environment) string {
 	return baseImage
 }
 
-func (t *builderTrait) customTasks(tasksConf map[string]*v1.BuildConfiguration) ([]v1.Task, error) {
+func (t *builderTrait) customTasks(tasksConf map[string]*v1.BuildConfiguration, imageName string) ([]v1.Task, error) {
 	customTasks := make([]v1.Task, len(t.Tasks))
 
 	for i, t := range t.Tasks {
@@ -476,6 +500,7 @@ func (t *builderTrait) customTasks(tasksConf map[string]*v1.BuildConfiguration)
 					Name:          splitted[0],
 					Configuration: *taskConfOrDefault(tasksConf, splitted[0]),
 				},
+				PublishingImage:   imageName,
 				ContainerImage:    splitted[1],
 				ContainerCommands: containerCommands,
 			},
@@ -603,5 +628,27 @@ func filter(tasks []v1.Task, filterTasks []string) ([]v1.Task, error) {
 			return nil, fmt.Errorf("no task exist for %s name", f)
 		}
 	}
+	// make sure the last task is either a publishing task or a custom task
+	if len(filteredTasks) == 0 || !publishingOrUserTask(filteredTasks[len(filteredTasks)-1]) {
+		return nil, fmt.Errorf("last pipeline task is not a publishing or a user task")
+	}
 	return filteredTasks, nil
 }
+
+// return true if the task is either a publishing task or a custom user task
+func publishingOrUserTask(t v1.Task) bool {
+	if t.Custom != nil {
+		return true
+	} else if t.Spectrum != nil {
+		return true
+	} else if t.S2i != nil {
+		return true
+	} else if t.Jib != nil {
+		return true
+	} else if t.Buildah != nil {
+		return true
+	} else if t.Kaniko != nil {
+		return true
+	}
+	return false
+}
diff --git a/pkg/trait/builder_test.go b/pkg/trait/builder_test.go
index 343dc5e31..bb0e66b0d 100644
--- a/pkg/trait/builder_test.go
+++ b/pkg/trait/builder_test.go
@@ -112,7 +112,16 @@ func createBuilderTestEnv(cluster v1.IntegrationPlatformCluster, strategy v1.Int
 	if err != nil {
 		panic(err)
 	}
-	client, _ := test.NewFakeClient()
+	itk := &v1.IntegrationKit{
+		Status: v1.IntegrationKitStatus{
+			Phase: v1.IntegrationKitPhaseBuildSubmitted,
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "ns",
+			Name:      "my-kit",
+		},
+	}
+	client, _ := test.NewFakeClient(itk)
 	res := &Environment{
 		Ctx:          context.TODO(),
 		CamelCatalog: c,
@@ -127,14 +136,7 @@ func createBuilderTestEnv(cluster v1.IntegrationPlatformCluster, strategy v1.Int
 				Phase: v1.IntegrationPhaseDeploying,
 			},
 		},
-		IntegrationKit: &v1.IntegrationKit{
-			Status: v1.IntegrationKitStatus{
-				Phase: v1.IntegrationKitPhaseBuildSubmitted,
-			},
-			ObjectMeta: metav1.ObjectMeta{
-				Name: "my-kit",
-			},
-		},
+		IntegrationKit: itk,
 		Platform: &v1.IntegrationPlatform{
 			Spec: v1.IntegrationPlatformSpec{
 				Cluster: cluster,
@@ -231,7 +233,8 @@ func TestCustomTaskBuilderTraitInvalidStrategy(t *testing.T) {
 
 	err := builderTrait.Apply(env)
 
-	assert.NotNil(t, err)
+	// The error will be reported to IntegrationKits
+	assert.Nil(t, err)
 	assert.Equal(t, env.IntegrationKit.Status.Phase, v1.IntegrationKitPhaseError)
 	assert.Equal(t, env.IntegrationKit.Status.Conditions[0].Status, corev1.ConditionFalse)
 	assert.Equal(t, env.IntegrationKit.Status.Conditions[0].Type, v1.IntegrationKitConditionType("IntegrationKitTasksValid"))
@@ -245,7 +248,8 @@ func TestCustomTaskBuilderTraitInvalidStrategyOverride(t *testing.T) {
 
 	err := builderTrait.Apply(env)
 
-	assert.NotNil(t, err)
+	// The error will be reported to IntegrationKits
+	assert.Nil(t, err)
 	assert.Equal(t, env.IntegrationKit.Status.Phase, v1.IntegrationKitPhaseError)
 	assert.Equal(t, env.IntegrationKit.Status.Conditions[0].Status, corev1.ConditionFalse)
 	assert.Equal(t, env.IntegrationKit.Status.Conditions[0].Type, v1.IntegrationKitConditionType("IntegrationKitTasksValid"))
@@ -285,7 +289,8 @@ func TestInvalidMavenProfilesBuilderTrait(t *testing.T) {
 
 	err := builderTrait.Apply(env)
 
-	assert.NotNil(t, err)
+	// The error will be reported to IntegrationKits
+	assert.Nil(t, err)
 	assert.Equal(t, env.IntegrationKit.Status.Phase, v1.IntegrationKitPhaseError)
 	assert.Equal(t, env.IntegrationKit.Status.Conditions[0].Status, corev1.ConditionFalse)
 	assert.Contains(t, env.IntegrationKit.Status.Conditions[0].Message, "fakeprofile")
@@ -314,7 +319,7 @@ func TestBuilderCustomTasks(t *testing.T) {
 	builderTrait.Tasks = append(builderTrait.Tasks, "test;alpine;ls")
 	builderTrait.Tasks = append(builderTrait.Tasks, `test;alpine;mvn test`)
 
-	tasks, err := builderTrait.customTasks(nil)
+	tasks, err := builderTrait.customTasks(nil, "my-kit-img")
 
 	assert.Nil(t, err)
 	assert.Equal(t, 2, len(tasks))
@@ -332,7 +337,7 @@ func TestBuilderCustomTasksFailure(t *testing.T) {
 	builderTrait := createNominalBuilderTraitTest()
 	builderTrait.Tasks = append(builderTrait.Tasks, "test;alpine")
 
-	_, err := builderTrait.customTasks(nil)
+	_, err := builderTrait.customTasks(nil, "my-kit-img")
 
 	assert.NotNil(t, err)
 }
@@ -341,7 +346,7 @@ func TestBuilderCustomTasksScript(t *testing.T) {
 	builderTrait := createNominalBuilderTraitTest()
 	builderTrait.Tasks = append(builderTrait.Tasks, "test;alpine;/bin/bash -c \"cd test && ls; echo 'helooo'\"")
 
-	tasks, err := builderTrait.customTasks(nil)
+	tasks, err := builderTrait.customTasks(nil, "my-kit-img")
 
 	assert.Nil(t, err)
 	assert.Equal(t, 1, len(tasks))
@@ -499,26 +504,36 @@ func TestBuilderTasksFilterNotExistingTasks(t *testing.T) {
 	assert.Equal(t, "no task exist for missing-task name", err.Error())
 }
 
-func TestBuilderTasksFilterOperatorTasks(t *testing.T) {
+func TestBuilderTasksFilterMissingPublishTasks(t *testing.T) {
 	env := createBuilderTestEnv(v1.IntegrationPlatformClusterKubernetes, v1.IntegrationPlatformBuildPublishStrategyJib, v1.BuildStrategyPod)
 	builderTrait := createNominalBuilderTraitTest()
 	builderTrait.TasksFilter = "builder,package"
 
+	err := builderTrait.Apply(env)
+	assert.NotNil(t, err)
+	assert.Equal(t, "last pipeline task is not a publishing or a user task", err.Error())
+}
+
+func TestBuilderTasksFilterOperatorTasks(t *testing.T) {
+	env := createBuilderTestEnv(v1.IntegrationPlatformClusterKubernetes, v1.IntegrationPlatformBuildPublishStrategyJib, v1.BuildStrategyPod)
+	builderTrait := createNominalBuilderTraitTest()
+	builderTrait.TasksFilter = "builder,package,jib"
+
 	err := builderTrait.Apply(env)
 	assert.Nil(t, err)
 	pipelineTasks := tasksByName(env.Pipeline)
-	assert.Equal(t, []string{"builder", "package"}, pipelineTasks)
+	assert.Equal(t, []string{"builder", "package", "jib"}, pipelineTasks)
 }
 
 func TestBuilderTasksFilterAndReorderOperatorTasks(t *testing.T) {
 	env := createBuilderTestEnv(v1.IntegrationPlatformClusterKubernetes, v1.IntegrationPlatformBuildPublishStrategyJib, v1.BuildStrategyPod)
 	builderTrait := createNominalBuilderTraitTest()
-	builderTrait.TasksFilter = "package,builder"
+	builderTrait.TasksFilter = "package,builder,jib"
 
 	err := builderTrait.Apply(env)
 	assert.Nil(t, err)
 	pipelineTasks := tasksByName(env.Pipeline)
-	assert.Equal(t, []string{"package", "builder"}, pipelineTasks)
+	assert.Equal(t, []string{"package", "builder", "jib"}, pipelineTasks)
 }
 
 func TestBuilderTasksFilterAndReorderCustomTasks(t *testing.T) {
diff --git a/pkg/util/test/client.go b/pkg/util/test/client.go
index 9105719eb..081a6733a 100644
--- a/pkg/util/test/client.go
+++ b/pkg/util/test/client.go
@@ -23,6 +23,7 @@ import (
 	"strings"
 
 	"github.com/apache/camel-k/v2/pkg/apis"
+	v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
 	"github.com/apache/camel-k/v2/pkg/client"
 	camel "github.com/apache/camel-k/v2/pkg/client/camel/clientset/versioned"
 	fakecamelclientset "github.com/apache/camel-k/v2/pkg/client/camel/clientset/versioned/fake"
@@ -68,6 +69,7 @@ func NewFakeClient(initObjs ...runtime.Object) (client.Client, error) {
 			},
 		).
 		WithRuntimeObjects(initObjs...).
+		WithStatusSubresource(&v1.IntegrationKit{}).
 		Build()
 
 	camelClientset := fakecamelclientset.NewSimpleClientset(filterObjects(scheme, initObjs, func(gvk schema.GroupVersionKind) bool {
diff --git a/resources/traits.yaml b/resources/traits.yaml
index dac366094..a63e521e9 100755
--- a/resources/traits.yaml
+++ b/resources/traits.yaml
@@ -285,10 +285,10 @@ traits:
       with format `<name>;<container-image>;<container-command>`.
   - name: tasks-filter
     type: string
-    description: A list of tasks (available only when using `pod` strategy), sorted
-      by the order of execution in a csv format, ie, `<taskName1>,<taskName2>,...`.
-      Mind that you must include also the operator tasks (`builder`, `quarkus-native`,
-      `package`, `jib`, `spectrum`, `s2i`) if you need to execute them.
+    description: A list of tasks sorted by the order of execution in a csv format,
+      ie, `<taskName1>,<taskName2>,...`. Mind that you must include also the operator
+      tasks (`builder`, `quarkus-native`, `package`, `jib`, `spectrum`, `s2i`) if
+      you need to execute them. Useful only wih `pod` strategy.
   - name: tasks-request-cpu
     type: '[]string'
     description: A list of request cpu configuration for the specific task with format