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:25 UTC

(camel-k) branch main updated (f5187a361 -> 277124770)

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

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


    from f5187a361 chore(ci): simplify nightly updates action
     new 4e2bc7959 feat(pipeline): filter and sort tasks execution
     new dbea42fb0 feat(pipeline): publishing user task
     new 277124770 feat(pipeline): support custom user id execution

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 config/crd/bases/camel.apache.org_builds.yaml      |   7 +
 .../bases/camel.apache.org_integrationkits.yaml    |   7 +
 .../camel.apache.org_integrationplatforms.yaml     |  14 ++
 .../crd/bases/camel.apache.org_integrations.yaml   |   7 +
 .../bases/camel.apache.org_kameletbindings.yaml    |   7 +
 config/crd/bases/camel.apache.org_pipes.yaml       |   7 +
 docs/modules/ROOT/pages/pipeline/pipeline.adoc     |  61 ++++++--
 docs/modules/ROOT/partials/apis/camel-k-crds.adoc  |  23 +++
 docs/modules/traits/pages/builder.adoc             |   6 +
 helm/camel-k/crds/crd-build.yaml                   |   7 +
 helm/camel-k/crds/crd-integration-kit.yaml         |   7 +
 helm/camel-k/crds/crd-integration-platform.yaml    |  14 ++
 helm/camel-k/crds/crd-integration.yaml             |   7 +
 helm/camel-k/crds/crd-kamelet-binding.yaml         |   7 +
 helm/camel-k/crds/crd-pipe.yaml                    |   7 +
 pkg/apis/camel/v1/build_types.go                   |   4 +
 pkg/apis/camel/v1/trait/builder.go                 |   4 +
 pkg/apis/camel/v1/zz_generated.deepcopy.go         |   5 +
 .../camel/applyconfiguration/camel/v1/usertask.go  |  18 +++
 pkg/controller/build/build_pod.go                  |   7 +
 pkg/controller/build/monitor_pod.go                | 102 ++++++++++---
 pkg/resources/resources.go                         |  45 ++++--
 pkg/trait/builder.go                               | 145 +++++++++++++++---
 pkg/trait/builder_test.go                          | 167 +++++++++++++++++----
 pkg/util/test/client.go                            |   2 +
 resources/traits.yaml                              |   6 +
 26 files changed, 600 insertions(+), 93 deletions(-)


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

Posted by pc...@apache.org.
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


(camel-k) 01/03: feat(pipeline): filter and sort tasks execution

Posted by pc...@apache.org.
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 4e2bc7959185de72f32230a1f14255c3860d3999
Author: Pasquale Congiusti <pa...@gmail.com>
AuthorDate: Thu Dec 7 13:29:02 2023 +0100

    feat(pipeline): filter and sort tasks execution
---
 .../bases/camel.apache.org_integrationkits.yaml    |  7 ++
 .../camel.apache.org_integrationplatforms.yaml     | 14 +++
 .../crd/bases/camel.apache.org_integrations.yaml   |  7 ++
 .../bases/camel.apache.org_kameletbindings.yaml    |  8 ++
 config/crd/bases/camel.apache.org_pipes.yaml       |  8 ++
 docs/modules/ROOT/partials/apis/camel-k-crds.adoc  |  9 ++
 docs/modules/traits/pages/builder.adoc             |  6 ++
 helm/camel-k/crds/crd-integration-kit.yaml         |  7 ++
 helm/camel-k/crds/crd-integration-platform.yaml    | 14 +++
 helm/camel-k/crds/crd-integration.yaml             |  7 ++
 helm/camel-k/crds/crd-kamelet-binding.yaml         |  8 ++
 helm/camel-k/crds/crd-pipe.yaml                    |  8 ++
 pkg/apis/camel/v1/trait/builder.go                 |  4 +
 pkg/trait/builder.go                               | 46 ++++++++++
 pkg/trait/builder_test.go                          | 99 ++++++++++++++++++++--
 resources/traits.yaml                              |  6 ++
 16 files changed, 249 insertions(+), 9 deletions(-)

diff --git a/config/crd/bases/camel.apache.org_integrationkits.yaml b/config/crd/bases/camel.apache.org_integrationkits.yaml
index e3dece12f..3d4714344 100644
--- a/config/crd/bases/camel.apache.org_integrationkits.yaml
+++ b/config/crd/bases/camel.apache.org_integrationkits.yaml
@@ -284,6 +284,13 @@ spec:
                         items:
                           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.
+                        type: string
                       tasksLimitCPU:
                         description: A list of limit cpu configuration for the specific
                           task with format `<task-name>:<limit-cpu-conf>`.
diff --git a/config/crd/bases/camel.apache.org_integrationplatforms.yaml b/config/crd/bases/camel.apache.org_integrationplatforms.yaml
index f548fe157..240fb4143 100644
--- a/config/crd/bases/camel.apache.org_integrationplatforms.yaml
+++ b/config/crd/bases/camel.apache.org_integrationplatforms.yaml
@@ -589,6 +589,13 @@ spec:
                         items:
                           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.
+                        type: string
                       tasksLimitCPU:
                         description: A list of limit cpu configuration for the specific
                           task with format `<task-name>:<limit-cpu-conf>`.
@@ -2472,6 +2479,13 @@ spec:
                         items:
                           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.
+                        type: string
                       tasksLimitCPU:
                         description: A list of limit cpu configuration for the specific
                           task with format `<task-name>:<limit-cpu-conf>`.
diff --git a/config/crd/bases/camel.apache.org_integrations.yaml b/config/crd/bases/camel.apache.org_integrations.yaml
index fd9263a05..fa9fd5c0c 100644
--- a/config/crd/bases/camel.apache.org_integrations.yaml
+++ b/config/crd/bases/camel.apache.org_integrations.yaml
@@ -6502,6 +6502,13 @@ spec:
                         items:
                           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.
+                        type: string
                       tasksLimitCPU:
                         description: A list of limit cpu configuration for the specific
                           task with format `<task-name>:<limit-cpu-conf>`.
diff --git a/config/crd/bases/camel.apache.org_kameletbindings.yaml b/config/crd/bases/camel.apache.org_kameletbindings.yaml
index 0739d217f..ad82cf8c1 100644
--- a/config/crd/bases/camel.apache.org_kameletbindings.yaml
+++ b/config/crd/bases/camel.apache.org_kameletbindings.yaml
@@ -6786,6 +6786,14 @@ spec:
                             items:
                               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.
+                            type: string
                           tasksLimitCPU:
                             description: A list of limit cpu configuration for the
                               specific task with format `<task-name>:<limit-cpu-conf>`.
diff --git a/config/crd/bases/camel.apache.org_pipes.yaml b/config/crd/bases/camel.apache.org_pipes.yaml
index 8063ab981..d018e4349 100644
--- a/config/crd/bases/camel.apache.org_pipes.yaml
+++ b/config/crd/bases/camel.apache.org_pipes.yaml
@@ -6784,6 +6784,14 @@ spec:
                             items:
                               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.
+                            type: string
                           tasksLimitCPU:
                             description: A list of limit cpu configuration for the
                               specific task with format `<task-name>:<limit-cpu-conf>`.
diff --git a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
index 4d7208467..29cc535a7 100644
--- a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
+++ b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
@@ -5887,6 +5887,15 @@ Syntax: [configmap{vbar}secret]:name[/key], where name represents the resource n
 
 A list of tasks to be executed (available only when using `pod` strategy) with format `<name>;<container-image>;<container-command>`.
 
+|`tasksFilter` +
+string
+|
+
+
+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.
+
 |`tasksRequestCPU` +
 []string
 |
diff --git a/docs/modules/traits/pages/builder.adoc b/docs/modules/traits/pages/builder.adoc
index f86b3edab..6330dea1e 100755
--- a/docs/modules/traits/pages/builder.adoc
+++ b/docs/modules/traits/pages/builder.adoc
@@ -82,6 +82,12 @@ Syntax: [configmap\|secret]:name[/key], where name represents the resource name,
 | []string
 | A list of tasks to be executed (available only when using `pod` strategy) with format `<name>;<container-image>;<container-command>`.
 
+| 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>,...`.
+Mind that you must include also the operator tasks (`builder`, `quarkus-native`, `package`, `jib`, `spectrum`, `s2i`)
+if you need to execute them.
+
 | builder.tasks-request-cpu
 | []string
 | A list of request cpu configuration for the specific task with format `<task-name>:<request-cpu-conf>`.
diff --git a/helm/camel-k/crds/crd-integration-kit.yaml b/helm/camel-k/crds/crd-integration-kit.yaml
index e3dece12f..3d4714344 100644
--- a/helm/camel-k/crds/crd-integration-kit.yaml
+++ b/helm/camel-k/crds/crd-integration-kit.yaml
@@ -284,6 +284,13 @@ spec:
                         items:
                           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.
+                        type: string
                       tasksLimitCPU:
                         description: A list of limit cpu configuration for the specific
                           task with format `<task-name>:<limit-cpu-conf>`.
diff --git a/helm/camel-k/crds/crd-integration-platform.yaml b/helm/camel-k/crds/crd-integration-platform.yaml
index f548fe157..240fb4143 100644
--- a/helm/camel-k/crds/crd-integration-platform.yaml
+++ b/helm/camel-k/crds/crd-integration-platform.yaml
@@ -589,6 +589,13 @@ spec:
                         items:
                           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.
+                        type: string
                       tasksLimitCPU:
                         description: A list of limit cpu configuration for the specific
                           task with format `<task-name>:<limit-cpu-conf>`.
@@ -2472,6 +2479,13 @@ spec:
                         items:
                           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.
+                        type: string
                       tasksLimitCPU:
                         description: A list of limit cpu configuration for the specific
                           task with format `<task-name>:<limit-cpu-conf>`.
diff --git a/helm/camel-k/crds/crd-integration.yaml b/helm/camel-k/crds/crd-integration.yaml
index fd9263a05..fa9fd5c0c 100644
--- a/helm/camel-k/crds/crd-integration.yaml
+++ b/helm/camel-k/crds/crd-integration.yaml
@@ -6502,6 +6502,13 @@ spec:
                         items:
                           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.
+                        type: string
                       tasksLimitCPU:
                         description: A list of limit cpu configuration for the specific
                           task with format `<task-name>:<limit-cpu-conf>`.
diff --git a/helm/camel-k/crds/crd-kamelet-binding.yaml b/helm/camel-k/crds/crd-kamelet-binding.yaml
index 0739d217f..ad82cf8c1 100644
--- a/helm/camel-k/crds/crd-kamelet-binding.yaml
+++ b/helm/camel-k/crds/crd-kamelet-binding.yaml
@@ -6786,6 +6786,14 @@ spec:
                             items:
                               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.
+                            type: string
                           tasksLimitCPU:
                             description: A list of limit cpu configuration for the
                               specific task with format `<task-name>:<limit-cpu-conf>`.
diff --git a/helm/camel-k/crds/crd-pipe.yaml b/helm/camel-k/crds/crd-pipe.yaml
index 8063ab981..d018e4349 100644
--- a/helm/camel-k/crds/crd-pipe.yaml
+++ b/helm/camel-k/crds/crd-pipe.yaml
@@ -6784,6 +6784,14 @@ spec:
                             items:
                               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.
+                            type: string
                           tasksLimitCPU:
                             description: A list of limit cpu configuration for the
                               specific task with format `<task-name>:<limit-cpu-conf>`.
diff --git a/pkg/apis/camel/v1/trait/builder.go b/pkg/apis/camel/v1/trait/builder.go
index 69441859f..9dc6c361a 100644
--- a/pkg/apis/camel/v1/trait/builder.go
+++ b/pkg/apis/camel/v1/trait/builder.go
@@ -58,6 +58,10 @@ type BuilderTrait struct {
 	MavenProfiles []string `property:"maven-profiles" json:"mavenProfiles,omitempty"`
 	// A list of tasks to be executed (available only when using `pod` strategy) with format `<name>;<container-image>;<container-command>`.
 	Tasks []string `property:"tasks" json:"tasks,omitempty"`
+	// 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.
+	TasksFilter string `property:"tasks-filter" json:"tasksFilter,omitempty"`
 	// A list of request cpu configuration for the specific task with format `<task-name>:<request-cpu-conf>`.
 	TasksRequestCPU []string `property:"tasks-request-cpu" json:"tasksRequestCPU,omitempty"`
 	// A list of request memory configuration for the specific task with format `<task-name>:<request-memory-conf>`.
diff --git a/pkg/trait/builder.go b/pkg/trait/builder.go
index fef01bcd8..87c3e484d 100644
--- a/pkg/trait/builder.go
+++ b/pkg/trait/builder.go
@@ -313,6 +313,13 @@ func (t *builderTrait) Apply(e *Environment) error {
 		}})
 	}
 
+	// filter only those tasks required by the user
+	if t.TasksFilter != "" {
+		flt := strings.Split(t.TasksFilter, ",")
+		if pipelineTasks, err = filter(pipelineTasks, flt); err != nil {
+			return err
+		}
+	}
 	// add local pipeline tasks to env pipeline
 	e.Pipeline = append(e.Pipeline, pipelineTasks...)
 	return nil
@@ -559,3 +566,42 @@ func splitContainerCommand(command string) []string {
 
 	return removeQuotes
 }
+
+func filter(tasks []v1.Task, filterTasks []string) ([]v1.Task, error) {
+	var filteredTasks []v1.Task
+	for _, f := range filterTasks {
+		found := false
+		for _, t := range tasks {
+			if t.Builder != nil && t.Builder.Name == f {
+				filteredTasks = append(filteredTasks, t)
+				found = true
+			} else if t.Custom != nil && t.Custom.Name == f {
+				filteredTasks = append(filteredTasks, t)
+				found = true
+			} else if t.Package != nil && t.Package.Name == f {
+				filteredTasks = append(filteredTasks, t)
+				found = true
+			} else if t.Spectrum != nil && t.Spectrum.Name == f {
+				filteredTasks = append(filteredTasks, t)
+				found = true
+			} else if t.S2i != nil && t.S2i.Name == f {
+				filteredTasks = append(filteredTasks, t)
+				found = true
+			} else if t.Jib != nil && t.Jib.Name == f {
+				filteredTasks = append(filteredTasks, t)
+				found = true
+			} else if t.Buildah != nil && t.Buildah.Name == f {
+				filteredTasks = append(filteredTasks, t)
+				found = true
+			} else if t.Kaniko != nil && t.Kaniko.Name == f {
+				filteredTasks = append(filteredTasks, t)
+				found = true
+			}
+		}
+		if !found {
+			// If we reach this point it means no tasks exists for the name
+			return nil, fmt.Errorf("no task exist for %s name", f)
+		}
+	}
+	return filteredTasks, nil
+}
diff --git a/pkg/trait/builder_test.go b/pkg/trait/builder_test.go
index 129c4505b..343dc5e31 100644
--- a/pkg/trait/builder_test.go
+++ b/pkg/trait/builder_test.go
@@ -251,15 +251,6 @@ func TestCustomTaskBuilderTraitInvalidStrategyOverride(t *testing.T) {
 	assert.Equal(t, env.IntegrationKit.Status.Conditions[0].Type, v1.IntegrationKitConditionType("IntegrationKitTasksValid"))
 }
 
-func findCustomTaskByName(tasks []v1.Task, name string) v1.Task {
-	for _, t := range tasks {
-		if t.Custom != nil && t.Custom.Name == name {
-			return t
-		}
-	}
-	return v1.Task{}
-}
-
 func TestMavenProfilesBuilderTrait(t *testing.T) {
 	env := createBuilderTestEnv(v1.IntegrationPlatformClusterKubernetes, v1.IntegrationPlatformBuildPublishStrategyKaniko, v1.BuildStrategyRoutine)
 	builderTrait := createNominalBuilderTraitTest()
@@ -486,3 +477,93 @@ func TestBuilderWithNodeSelector(t *testing.T) {
 	assert.Equal(t, map[string]string{"size": "large"}, env.Pipeline[0].Builder.Configuration.NodeSelector)
 	assert.Equal(t, map[string]string{"size": "large"}, builderTrait.NodeSelector)
 }
+
+func TestBuilderNoTasksFilter(t *testing.T) {
+	env := createBuilderTestEnv(v1.IntegrationPlatformClusterKubernetes, v1.IntegrationPlatformBuildPublishStrategyJib, v1.BuildStrategyPod)
+	builderTrait := createNominalBuilderTraitTest()
+
+	err := builderTrait.Apply(env)
+	assert.Nil(t, err)
+
+	pipelineTasks := tasksByName(env.Pipeline)
+	assert.Equal(t, []string{"builder", "package", "jib"}, pipelineTasks)
+}
+
+func TestBuilderTasksFilterNotExistingTasks(t *testing.T) {
+	env := createBuilderTestEnv(v1.IntegrationPlatformClusterKubernetes, v1.IntegrationPlatformBuildPublishStrategyJib, v1.BuildStrategyPod)
+	builderTrait := createNominalBuilderTraitTest()
+	builderTrait.TasksFilter = "builder,missing-task"
+
+	err := builderTrait.Apply(env)
+	assert.NotNil(t, err)
+	assert.Equal(t, "no task exist for missing-task name", err.Error())
+}
+
+func TestBuilderTasksFilterOperatorTasks(t *testing.T) {
+	env := createBuilderTestEnv(v1.IntegrationPlatformClusterKubernetes, v1.IntegrationPlatformBuildPublishStrategyJib, v1.BuildStrategyPod)
+	builderTrait := createNominalBuilderTraitTest()
+	builderTrait.TasksFilter = "builder,package"
+
+	err := builderTrait.Apply(env)
+	assert.Nil(t, err)
+	pipelineTasks := tasksByName(env.Pipeline)
+	assert.Equal(t, []string{"builder", "package"}, pipelineTasks)
+}
+
+func TestBuilderTasksFilterAndReorderOperatorTasks(t *testing.T) {
+	env := createBuilderTestEnv(v1.IntegrationPlatformClusterKubernetes, v1.IntegrationPlatformBuildPublishStrategyJib, v1.BuildStrategyPod)
+	builderTrait := createNominalBuilderTraitTest()
+	builderTrait.TasksFilter = "package,builder"
+
+	err := builderTrait.Apply(env)
+	assert.Nil(t, err)
+	pipelineTasks := tasksByName(env.Pipeline)
+	assert.Equal(t, []string{"package", "builder"}, pipelineTasks)
+}
+
+func TestBuilderTasksFilterAndReorderCustomTasks(t *testing.T) {
+	env := createBuilderTestEnv(v1.IntegrationPlatformClusterKubernetes, v1.IntegrationPlatformBuildPublishStrategyJib, v1.BuildStrategyPod)
+	builderTrait := createNominalBuilderTraitTest()
+	builderTrait.Tasks = append(builderTrait.Tasks, `my-custom-publish;alpine;mvn test`)
+	builderTrait.Tasks = append(builderTrait.Tasks, "my-custom-task;alpine;ls")
+	builderTrait.TasksFilter = "builder,my-custom-task,package,my-custom-publish"
+
+	err := builderTrait.Apply(env)
+	assert.Nil(t, err)
+	pipelineTasks := tasksByName(env.Pipeline)
+	assert.Equal(t, []string{"builder", "my-custom-task", "package", "my-custom-publish"}, pipelineTasks)
+}
+
+func findCustomTaskByName(tasks []v1.Task, name string) v1.Task {
+	for _, t := range tasks {
+		if t.Custom != nil && t.Custom.Name == name {
+			return t
+		}
+	}
+	return v1.Task{}
+}
+
+func tasksByName(tasks []v1.Task) []string {
+	pipelineTasks := make([]string, len(tasks))
+	for i, t := range tasks {
+		if t.Builder != nil {
+			pipelineTasks[i] = t.Builder.Name
+		}
+		if t.Custom != nil {
+			pipelineTasks[i] = t.Custom.Name
+		}
+		if t.Package != nil {
+			pipelineTasks[i] = t.Package.Name
+		}
+		if t.S2i != nil {
+			pipelineTasks[i] = t.S2i.Name
+		}
+		if t.Spectrum != nil {
+			pipelineTasks[i] = t.Spectrum.Name
+		}
+		if t.Jib != nil {
+			pipelineTasks[i] = t.Jib.Name
+		}
+	}
+	return pipelineTasks
+}
diff --git a/resources/traits.yaml b/resources/traits.yaml
index 0f9243967..dac366094 100755
--- a/resources/traits.yaml
+++ b/resources/traits.yaml
@@ -283,6 +283,12 @@ traits:
     type: '[]string'
     description: A list of tasks to be executed (available only when using `pod` strategy)
       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.
   - name: tasks-request-cpu
     type: '[]string'
     description: A list of request cpu configuration for the specific task with format


(camel-k) 03/03: feat(pipeline): support custom user id execution

Posted by pc...@apache.org.
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 2771247703e61f7e4511528e236bc8c0f50f95f7
Author: Pasquale Congiusti <pa...@gmail.com>
AuthorDate: Thu Jan 11 11:00:47 2024 +0100

    feat(pipeline): support custom user id execution
    
    Will add the container security context accordingly
---
 config/crd/bases/camel.apache.org_builds.yaml      |  4 ++
 docs/modules/ROOT/pages/pipeline/pipeline.adoc     | 61 ++++++++++++++++++----
 docs/modules/ROOT/partials/apis/camel-k-crds.adoc  |  7 +++
 helm/camel-k/crds/crd-build.yaml                   |  4 ++
 pkg/apis/camel/v1/build_types.go                   |  2 +
 pkg/apis/camel/v1/trait/builder.go                 |  2 +-
 pkg/apis/camel/v1/zz_generated.deepcopy.go         |  5 ++
 .../camel/applyconfiguration/camel/v1/usertask.go  |  9 ++++
 pkg/controller/build/build_pod.go                  |  6 +++
 pkg/controller/build/monitor_pod.go                | 58 +++++++++++---------
 pkg/resources/resources.go                         |  4 +-
 pkg/trait/builder.go                               | 56 +++++++++++---------
 pkg/trait/builder_test.go                          | 23 ++++++--
 13 files changed, 174 insertions(+), 67 deletions(-)

diff --git a/config/crd/bases/camel.apache.org_builds.yaml b/config/crd/bases/camel.apache.org_builds.yaml
index 02965a6bc..3cb3ea1b6 100644
--- a/config/crd/bases/camel.apache.org_builds.yaml
+++ b/config/crd/bases/camel.apache.org_builds.yaml
@@ -821,6 +821,10 @@ spec:
                         publishingImage:
                           description: the desired image build name
                           type: string
+                        userId:
+                          description: the user id used to run the container
+                          format: int64
+                          type: integer
                       type: object
                     jib:
                       description: a JibTask, for Jib strategy
diff --git a/docs/modules/ROOT/pages/pipeline/pipeline.adoc b/docs/modules/ROOT/pages/pipeline/pipeline.adoc
index b204f338c..6712c940d 100644
--- a/docs/modules/ROOT/pages/pipeline/pipeline.adoc
+++ b/docs/modules/ROOT/pages/pipeline/pipeline.adoc
@@ -1,22 +1,25 @@
 [[build-pipeline]]
 = Build Pipeline
-Since version 2.0, we've introduced the concept of `Pipeline` in order to provide a degree of flexibility for those user that want to customize the entire building process. We can think of a pipeline as a series of tasks that can be executed after the project generation and before the artifact publishing into the container registry.
 
-In Camel K version 1 we used to have 2 static tasks: `builder` and `publisher` (named after the strategy adopted, ie, `spectrum`). Now you can include any further task in the middle by opportunely configuring the Build. Here a diagram illustrating the pipeline:
+Since version 2.0, we've introduced the concept of `Pipeline` in order to provide a degree of flexibility for those user that want to customize the entire building process. We can think of a pipeline as a series of tasks that can be executed to generate a Camel application project, build it accordingly and publish into a container registry.
+
+Camel K operator creates a default pipeline containing an opinionated `build`, `package` and `publish` operations. Beside them you can include any additional task in order to enrich the building process:
 
 image::camel_k_pipeline.png[Camel K Pipeline, width=1024]
 
-We have 3 tasks which are required by Camel K to perform a build and provide a container image which will be used to start a Camel application. The **build** is a Maven process which is in charge to create a maven project based on the Integration sources provided. Then, after the Maven project is created and compiled, you can add any custom task (see section below). With this part we are introducing that level of flexibility required to accommodate any company build process. This part is [...]
+The main 3 tasks are required by Camel K to perform a build and provide a container image which will be used to start a Camel application. The **build** is a Maven process which is in charge to create a maven project based on the Integration sources provided.
+
+If you're building a Quarkus Native image, the quarkus trait influences the builder in order to include a custom task (named **quarkus-native**) which takes care of executing the native image compilation. But this is completely hidden to the final user.
 
-In reality we do make use of the custom task in one particular situation. In order to build a Quarkus Native image, the quarkus trait influences the builder in order to include a custom task which takes care of executing the native image compilation. But this is completely hidden to the final user.
+Once the build is over, the **package** task takes care to prepare the context later required by the publishing operations. Basically this task is in charge to copy all artifacts and also prepare a Dockerfile which could be used eventually by the rest of the process. Finally, the **publish** task is in charge to generate and push the container to the registry using all the artifacts generated in the package task.
 
-Once the build and any customization are over, the **package** task takes care to prepare the context later required by the publishing operations. Basically this task is in charge to copy all artifacts and also prepare a Dockerfile which could be used eventually by the rest of the process. Finally, the **publish** task is in charge to generate and push the container to the registry using all the artifacts generated in the package task.
+The **custom tasks** can be provided at any point of the pipeline (read task sorting section). With them we are introducing that level of flexibility required to accommodate any company build process. This part is optional, by default, Camel K does not need any **custom task**.
 
 [[add-custom-tasks]]
 == Add custom user tasks
-As you have seen, the final user can include any optional task after a build operation is performed. We think this is the best moment when any custom operation can be performed as it is the immediate step after the Maven project is generated. And ideally, the context of any custom operation is the project.
+The final user can include any optional task, which, by default is run after a build operation is performed. We think this is the best moment when any custom operation can be performed as it is the immediate step after the Maven project is generated. And ideally, the context of any custom operation is the project.
 
-NOTE: Please, notice that, since the customization may require any tool not available in the operator container, you will need to run any additional task using builder `pod` strategy.
+Custom tasks are only available using builder `pod` strategy in order to let the user provide each task with the tools required to run the specific customization.
 
 Let's see an example:
 
@@ -29,6 +32,7 @@ spec:
       command: tree
       image: alpine
       name: custom1
+      userId: 0
   - custom:
       command: cat maven/pom.xml
       image: alpine
@@ -36,7 +40,7 @@ spec:
   - spectrum:
       ...
 ```
-The custom tasks will be executed in the directory where the Camel K runtime Maven project was generated. In this example we're creating 2 tasks to retrieve certain values from the project just for the scope of illustrating the feature. For each task you need to specify a name, the container image which you want to use to run and the command to execute.
+The custom tasks will be executed in the directory where the Camel K runtime Maven project was generated. In this example we're creating 2 tasks to retrieve certain values from the project just for the scope of illustrating the feature. For each task you need to specify a name, the container image which you want to use to run, the command to execute and (optionally) the user ID that has to run in the container.
 
 The goal is to let the user perform custom tasks which may result in a success or a failure. If the task executed results in a failure, then, the entire build is stopped and fails accordingly.
 
@@ -47,7 +51,7 @@ We are aware that configuring the `Build` type directly is something that the ma
 Maintaining the example above as a reference, configuring a custom task will be as easy as adding a trait property when running your Integration, for instance, via CLI:
 
 ```
-kamel run Test.java -t builder.tasks=custom1;alpine;tree -t builder.tasks="custom2;alpine;cat maven/pom.xml"
+kamel run Test.java -t builder.tasks=custom1;alpine;tree;0 -t builder.tasks="custom2;alpine;cat maven/pom.xml"
 ```
 
 Another interesting configuration you can provide via Builder trait is the (https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/)[Kubernetes requests and limits]. Each of the task you are providing in the pipeline, can be configured with the proper resource settings. You can use, for instance the `-t builder.request-cpu <task-name>:1000m` to configure the container executed by the `task-name`. This configuration works for all the tasks including `builder`, `pack [...]
@@ -55,7 +59,7 @@ Another interesting configuration you can provide via Builder trait is the (http
 [[build-pipeline-result]]
 == Getting task execution status
 
-Altough the main goal of this custom task execution is to have a success/failure result, we thought it could be useful to get the log of each task to be consulted by the user. For this reason, you will be able to read it directly in the Build type. See the following example:
+Although the main goal of this custom task execution is to have a success/failure result, we thought it could be useful to get the log of each task to be consulted by the user. For this reason, you will be able to read it directly in the Build type. See the following example:
 
 ```
   conditions:
@@ -113,9 +117,44 @@ Given the limited space we can use in a Kubernetes custom resource, we are trunc
 
 If for any reason you still need to access the entire log of the execution, you can always access to the log of the builder `Pod` and the specific container that was executed, ie `kubectl logs camel-k-kit-chj2gpi9rcoc73cjfv2g-builder -c task1 -p`
 
+[[build-pipeline-sort]]
+=== Tasks filtering and sorting
+The default execution of tasks can be changed. You can include or remove tasks and provide your order of execution. When using the `builder` trait, this is done via `builder.tasks-filter` property. This parameter accepts a comma separated value list of tasks to execute. It must specify both operator tasks (ie, builder) and custom tasks.
+
+WARNING: altering the order of tasks may result in a disruptive build behavior.
+
+With this approach you can also provide your own publishing strategy. The pipeline run as a Kubernetes Pod. Each task is an `initContainer` and the last one is the final `container` which has to provide the published image digest into the `/dev/termination-log` in order to know the publish was completed. This is done out of the box when a supported publishing strategy is executed. If you want to provide your own publishing strategy, then, you need to make sure to follow this rule to have [...]
+
+There are certain environment variables we are injecting in each custom container in order to be able to get dynamic values required to perform certain operation (ie, publishing a container image):
+
+* INTEGRATION_KIT_IMAGE contains the name of the image expected to be used for the IntegrationKit generated during the pipeline execution
+
+We may add more if they are required and useful for a general case.
+
+[[build-pipeline-privileges]]
+=== Execution privileges
+The builder Pod will set a `PodSecurityContext` with a specific user ID privileges. This is a convention in order to maintain the same default user ID for the container images we use by default. Each container image will inherit this value unless specified with the user ID parameter. The value is by default 1001 unless you're using S2I publishing strategy (the default strategy for Openshift), where the value is defined by the platform.
+
+NOTE: you may run your task with root privileges using user ID 0. However, this is highly discouraged. You should instead make your container image being able to run with the default user ID privileges.
+
 [[build-pipeline-examples]]
 == Custom tasks examples
-
 As we are using container registry for execution, you will be able to execute virtually any kind of task. You can provide your own container with tools required by your company or use any one available in the OSS.
 
 As the target of the execution is the project, before the artifact is published to the registry, you can execute any task to validate the project. We can think of any vulnerability tool scanner, quality of code or any other action you tipically perform in your build pipeline.
+
+[[build-pipeline-examples-buildah]]
+=== Use your own publishing strategy
+We suggest to use the supported publishing strategy as they are working out of the box and optimized for the operator. However you may choose to adopt your own publishing strategy. Here an example to run an Integration using a Buildah publishing strategy:
+
+```bash
+$ kamel run test.yaml -t builder.strategy=pod -t builder.base-image=docker.io/library/eclipse-temurin:17 -t builder.tasks="my-buildah;quay.io/buildah/stable;/bin/bash -c \"cd context && buildah bud --storage-driver=vfs --tls-verify=false -t \$(INTEGRATION_KIT_IMAGE) . && buildah push --storage-driver=vfs --digestfile=/dev/termination-log --tls-verify=false \$(INTEGRATION_KIT_IMAGE) docker://\$(INTEGRATION_KIT_IMAGE)\";0" -t builder.tasks-filter=builder,package,my-buildah
+```
+
+You can see in this case we have created a custom task named `my-buildah`. This is expecting to run the build and publish operations. We must change directory to `/context/` where the **package** task has written a `Dockerfile`. We are using the INTEGRATION_KIT_IMAGE environment variable and we are writing the pushed image digest to `/dev/termination-log` as expected by the procedure.
+
+NOTE: depending on the strategy used you may not have a direct feature. It would suffice to add some command and just write the digest of the generated image via `echo $my-image-digest > /dev/termination-log`.
+
+Finally we filter the tasks we want to execute, making sure to include the **builder** and the **package** tasks which are provided by the operator and are in charge to do the building and the packaging.
+
+NOTE: we had to run the `my-buildah` task with root privileges as this is required by the default container image. We had also to override the `builder.base-image` as it also require a special treatment for Buildah to work.
\ No newline at end of file
diff --git a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
index f2ec00ef0..16a74cf3a 100644
--- a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
+++ b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
@@ -5659,6 +5659,13 @@ string
 
 the container image to use
 
+|`userId` +
+int64
+|
+
+
+the user id used to run the container
+
 |`command` +
 string
 |
diff --git a/helm/camel-k/crds/crd-build.yaml b/helm/camel-k/crds/crd-build.yaml
index 02965a6bc..3cb3ea1b6 100644
--- a/helm/camel-k/crds/crd-build.yaml
+++ b/helm/camel-k/crds/crd-build.yaml
@@ -821,6 +821,10 @@ spec:
                         publishingImage:
                           description: the desired image build name
                           type: string
+                        userId:
+                          description: the user id used to run the container
+                          format: int64
+                          type: integer
                       type: object
                     jib:
                       description: a JibTask, for Jib strategy
diff --git a/pkg/apis/camel/v1/build_types.go b/pkg/apis/camel/v1/build_types.go
index 076cf0426..a843eea5b 100644
--- a/pkg/apis/camel/v1/build_types.go
+++ b/pkg/apis/camel/v1/build_types.go
@@ -187,6 +187,8 @@ type UserTask struct {
 	BaseTask `json:",inline"`
 	// the container image to use
 	ContainerImage string `json:"image,omitempty"`
+	// the user id used to run the container
+	ContainerUserID *int64 `json:"userId,omitempty"`
 	// the command to execute
 	// Deprecated: use ContainerCommands
 	ContainerCommand string `json:"command,omitempty"`
diff --git a/pkg/apis/camel/v1/trait/builder.go b/pkg/apis/camel/v1/trait/builder.go
index 9dc6c361a..c98feafa6 100644
--- a/pkg/apis/camel/v1/trait/builder.go
+++ b/pkg/apis/camel/v1/trait/builder.go
@@ -60,7 +60,7 @@ type BuilderTrait struct {
 	Tasks []string `property:"tasks" json:"tasks,omitempty"`
 	// 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.
+	// if you need to execute them. Useful only with `pod` strategy.
 	TasksFilter string `property:"tasks-filter" json:"tasksFilter,omitempty"`
 	// A list of request cpu configuration for the specific task with format `<task-name>:<request-cpu-conf>`.
 	TasksRequestCPU []string `property:"tasks-request-cpu" json:"tasksRequestCPU,omitempty"`
diff --git a/pkg/apis/camel/v1/zz_generated.deepcopy.go b/pkg/apis/camel/v1/zz_generated.deepcopy.go
index e630aa257..f0d23440d 100644
--- a/pkg/apis/camel/v1/zz_generated.deepcopy.go
+++ b/pkg/apis/camel/v1/zz_generated.deepcopy.go
@@ -3090,6 +3090,11 @@ func (in *Traits) DeepCopy() *Traits {
 func (in *UserTask) DeepCopyInto(out *UserTask) {
 	*out = *in
 	in.BaseTask.DeepCopyInto(&out.BaseTask)
+	if in.ContainerUserID != nil {
+		in, out := &in.ContainerUserID, &out.ContainerUserID
+		*out = new(int64)
+		**out = **in
+	}
 	if in.ContainerCommands != nil {
 		in, out := &in.ContainerCommands, &out.ContainerCommands
 		*out = make([]string, len(*in))
diff --git a/pkg/client/camel/applyconfiguration/camel/v1/usertask.go b/pkg/client/camel/applyconfiguration/camel/v1/usertask.go
index 7a85991d1..5f396fcd5 100644
--- a/pkg/client/camel/applyconfiguration/camel/v1/usertask.go
+++ b/pkg/client/camel/applyconfiguration/camel/v1/usertask.go
@@ -24,6 +24,7 @@ package v1
 type UserTaskApplyConfiguration struct {
 	BaseTaskApplyConfiguration `json:",inline"`
 	ContainerImage             *string  `json:"image,omitempty"`
+	ContainerUserID            *int64   `json:"userId,omitempty"`
 	ContainerCommand           *string  `json:"command,omitempty"`
 	ContainerCommands          []string `json:"commands,omitempty"`
 	PublishingImage            *string  `json:"publishingImage,omitempty"`
@@ -59,6 +60,14 @@ func (b *UserTaskApplyConfiguration) WithContainerImage(value string) *UserTaskA
 	return b
 }
 
+// WithContainerUserID sets the ContainerUserID 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 ContainerUserID field is set to the value of the last call.
+func (b *UserTaskApplyConfiguration) WithContainerUserID(value int64) *UserTaskApplyConfiguration {
+	b.ContainerUserID = &value
+	return b
+}
+
 // WithContainerCommand sets the ContainerCommand 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 ContainerCommand field is set to the value of the last call.
diff --git a/pkg/controller/build/build_pod.go b/pkg/controller/build/build_pod.go
index 4914e771d..46fd34d55 100644
--- a/pkg/controller/build/build_pod.go
+++ b/pkg/controller/build/build_pod.go
@@ -573,6 +573,12 @@ func addCustomTaskToPod(build *v1.Build, task *v1.UserTask, pod *corev1.Pod) {
 		Env:             proxyFromEnvironment(),
 	}
 	container.Env = append(container.Env, corev1.EnvVar{Name: "INTEGRATION_KIT_IMAGE", Value: task.PublishingImage})
+	if task.ContainerUserID != nil {
+		container.SecurityContext = &corev1.SecurityContext{
+			RunAsUser:  task.ContainerUserID,
+			RunAsGroup: task.ContainerUserID,
+		}
+	}
 
 	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 0fcf60a72..0af446104 100644
--- a/pkg/controller/build/monitor_pod.go
+++ b/pkg/controller/build/monitor_pod.go
@@ -147,19 +147,22 @@ func (action *monitorPodAction) Handle(ctx context.Context, build *v1.Build) (*v
 		observeBuildResult(build, build.Status.Phase, buildCreator, duration)
 
 		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),
-				),
-			)
+		// operator supported publishing tasks should provide the digest in the builder command process execution
+		if !operatorSupportedPublishingStrategy(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:
@@ -339,7 +342,7 @@ func (action *monitorPodAction) setConditionsFromTerminationMessages(ctx context
 }
 
 // we expect that the last task is any of the supported publishing task
-// or a custom user task
+// or a custom user task.
 func publishTask(tasks []v1.Task) *v1.Task {
 	if len(tasks) > 0 {
 		return &tasks[len(tasks)-1]
@@ -354,15 +357,16 @@ func publishTaskImageName(tasks []v1.Task) string {
 	if t == nil {
 		return ""
 	}
-	if t.Custom != nil {
+	switch {
+	case t.Custom != nil:
 		return t.Custom.PublishingImage
-	} else if t.Spectrum != nil {
+	case t.Spectrum != nil:
 		return t.Spectrum.Image
-	} else if t.Jib != nil {
+	case t.Jib != nil:
 		return t.Jib.Image
-	} else if t.Buildah != nil {
+	case t.Buildah != nil:
 		return t.Buildah.Image
-	} else if t.Kaniko != nil {
+	case t.Kaniko != nil:
 		return t.Kaniko.Image
 	}
 
@@ -374,15 +378,16 @@ func publishTaskName(tasks []v1.Task) string {
 	if t == nil {
 		return ""
 	}
-	if t.Custom != nil {
+	switch {
+	case t.Custom != nil:
 		return t.Custom.Name
-	} else if t.Spectrum != nil {
+	case t.Spectrum != nil:
 		return t.Spectrum.Name
-	} else if t.Jib != nil {
+	case t.Jib != nil:
 		return t.Jib.Name
-	} else if t.Buildah != nil {
+	case t.Buildah != nil:
 		return t.Buildah.Name
-	} else if t.Kaniko != nil {
+	case t.Kaniko != nil:
 		return t.Kaniko.Name
 	}
 
@@ -399,3 +404,8 @@ func publishTaskDigest(tasks []v1.Task, cntStates []corev1.ContainerStatus) stri
 	}
 	return ""
 }
+
+func operatorSupportedPublishingStrategy(tasks []v1.Task) bool {
+	taskName := publishTaskName(tasks)
+	return taskName == "jib" || taskName == "spectrum" || taskName == "s2i"
+}
diff --git a/pkg/resources/resources.go b/pkg/resources/resources.go
index 04bba969b..3d0b93b67 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: 95542,
+			uncompressedSize: 95731,
 
-			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 [...]
+			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",
diff --git a/pkg/trait/builder.go b/pkg/trait/builder.go
index 843deb2e4..a0da38970 100644
--- a/pkg/trait/builder.go
+++ b/pkg/trait/builder.go
@@ -21,6 +21,7 @@ import (
 	"fmt"
 	"regexp"
 	"sort"
+	"strconv"
 	"strings"
 
 	corev1 "k8s.io/api/core/v1"
@@ -478,22 +479,15 @@ func (t *builderTrait) getBaseImage(e *Environment) string {
 	return baseImage
 }
 
+// the format expected is "<task-name>;<task-image>;<task-container-command>[;<task-container-user-id>]".
 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 {
 		splitted := strings.Split(t, ";")
 		if len(splitted) < 3 {
-			return nil, fmt.Errorf(`provide a custom task with at least 3 arguments, ie "my-task-name;my-image;echo 'hello', was %v"`, t)
+			return nil, fmt.Errorf(`provide a custom task with at least 3 arguments, ie "my-task-name;my-image;echo 'hello'", was %v`, t)
 		}
-		var containerCommand string
-		if len(splitted) > 3 {
-			// recompose in case of usage of separator char in the script
-			containerCommand = strings.Join(splitted[2:], ";")
-		} else {
-			containerCommand = splitted[2]
-		}
-		containerCommands := splitContainerCommand(containerCommand)
 		customTasks[i] = v1.Task{
 			Custom: &v1.UserTask{
 				BaseTask: v1.BaseTask{
@@ -502,9 +496,16 @@ func (t *builderTrait) customTasks(tasksConf map[string]*v1.BuildConfiguration,
 				},
 				PublishingImage:   imageName,
 				ContainerImage:    splitted[1],
-				ContainerCommands: containerCommands,
+				ContainerCommands: splitContainerCommand(splitted[2]),
 			},
 		}
+		if len(splitted) > 3 {
+			uid, err := strconv.ParseInt(splitted[3], 10, 64)
+			if err != nil {
+				return nil, fmt.Errorf(`provide a custom task with a correct numeric user id, was %v`, splitted[3])
+			}
+			customTasks[i].Custom.ContainerUserID = &uid
+		}
 	}
 	return customTasks, nil
 }
@@ -596,33 +597,36 @@ func filter(tasks []v1.Task, filterTasks []string) ([]v1.Task, error) {
 	var filteredTasks []v1.Task
 	for _, f := range filterTasks {
 		found := false
+
 		for _, t := range tasks {
-			if t.Builder != nil && t.Builder.Name == f {
+			switch {
+			case t.Builder != nil && t.Builder.Name == f:
 				filteredTasks = append(filteredTasks, t)
 				found = true
-			} else if t.Custom != nil && t.Custom.Name == f {
+			case t.Custom != nil && t.Custom.Name == f:
 				filteredTasks = append(filteredTasks, t)
 				found = true
-			} else if t.Package != nil && t.Package.Name == f {
+			case t.Package != nil && t.Package.Name == f:
 				filteredTasks = append(filteredTasks, t)
 				found = true
-			} else if t.Spectrum != nil && t.Spectrum.Name == f {
+			case t.Spectrum != nil && t.Spectrum.Name == f:
 				filteredTasks = append(filteredTasks, t)
 				found = true
-			} else if t.S2i != nil && t.S2i.Name == f {
+			case t.S2i != nil && t.S2i.Name == f:
 				filteredTasks = append(filteredTasks, t)
 				found = true
-			} else if t.Jib != nil && t.Jib.Name == f {
+			case t.Jib != nil && t.Jib.Name == f:
 				filteredTasks = append(filteredTasks, t)
 				found = true
-			} else if t.Buildah != nil && t.Buildah.Name == f {
+			case t.Buildah != nil && t.Buildah.Name == f:
 				filteredTasks = append(filteredTasks, t)
 				found = true
-			} else if t.Kaniko != nil && t.Kaniko.Name == f {
+			case t.Kaniko != nil && t.Kaniko.Name == f:
 				filteredTasks = append(filteredTasks, t)
 				found = true
 			}
 		}
+
 		if !found {
 			// If we reach this point it means no tasks exists for the name
 			return nil, fmt.Errorf("no task exist for %s name", f)
@@ -635,20 +639,20 @@ func filter(tasks []v1.Task, filterTasks []string) ([]v1.Task, error) {
 	return filteredTasks, nil
 }
 
-// return true if the task is either a publishing task or a custom user task
+// 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 {
+	switch {
+	case t.Custom != nil:
 		return true
-	} else if t.Spectrum != nil {
+	case t.Spectrum != nil:
 		return true
-	} else if t.S2i != nil {
+	case t.Jib != nil:
 		return true
-	} else if t.Jib != nil {
+	case t.Buildah != nil:
 		return true
-	} else if t.Buildah != nil {
-		return true
-	} else if t.Kaniko != nil {
+	case t.Kaniko != nil:
 		return true
 	}
+
 	return false
 }
diff --git a/pkg/trait/builder_test.go b/pkg/trait/builder_test.go
index bb0e66b0d..0a8224516 100644
--- a/pkg/trait/builder_test.go
+++ b/pkg/trait/builder_test.go
@@ -340,11 +340,12 @@ func TestBuilderCustomTasksFailure(t *testing.T) {
 	_, err := builderTrait.customTasks(nil, "my-kit-img")
 
 	assert.NotNil(t, err)
+	assert.Equal(t, "provide a custom task with at least 3 arguments, ie \"my-task-name;my-image;echo 'hello'\", was test;alpine", err.Error())
 }
 
-func TestBuilderCustomTasksScript(t *testing.T) {
+func TestBuilderCustomTasksBashScript(t *testing.T) {
 	builderTrait := createNominalBuilderTraitTest()
-	builderTrait.Tasks = append(builderTrait.Tasks, "test;alpine;/bin/bash -c \"cd test && ls; echo 'helooo'\"")
+	builderTrait.Tasks = append(builderTrait.Tasks, "test;alpine;/bin/bash -c \"cd test && echo 'helooo'\"")
 
 	tasks, err := builderTrait.customTasks(nil, "my-kit-img")
 
@@ -354,7 +355,23 @@ func TestBuilderCustomTasksScript(t *testing.T) {
 	assert.Equal(t, "alpine", tasks[0].Custom.ContainerImage)
 	assert.Equal(t, "/bin/bash", tasks[0].Custom.ContainerCommands[0])
 	assert.Equal(t, "-c", tasks[0].Custom.ContainerCommands[1])
-	assert.Equal(t, "cd test && ls; echo 'helooo'", tasks[0].Custom.ContainerCommands[2])
+	assert.Equal(t, "cd test && echo 'helooo'", tasks[0].Custom.ContainerCommands[2])
+}
+
+func TestBuilderCustomTasksSecurityContextScript(t *testing.T) {
+	builderTrait := createNominalBuilderTraitTest()
+	builderTrait.Tasks = append(builderTrait.Tasks, "test;alpine;/bin/bash -c \"cd test && echo 'helooo'\";1000")
+
+	tasks, err := builderTrait.customTasks(nil, "my-kit-img")
+
+	assert.Nil(t, err)
+	assert.Equal(t, 1, len(tasks))
+	assert.Equal(t, "test", tasks[0].Custom.Name)
+	assert.Equal(t, "alpine", tasks[0].Custom.ContainerImage)
+	assert.Equal(t, "/bin/bash", tasks[0].Custom.ContainerCommands[0])
+	assert.Equal(t, "-c", tasks[0].Custom.ContainerCommands[1])
+	assert.Equal(t, "cd test && echo 'helooo'", tasks[0].Custom.ContainerCommands[2])
+	assert.Equal(t, int64(1000), *tasks[0].Custom.ContainerUserID)
 }
 
 func TestBuilderCustomTasksConfiguration(t *testing.T) {