Repository: apache/camel-k
Pull request #3418 (2022-07-06 08:56:11)

[Backport 1.9.x] fix(#3390): Fix Knative addressable resolver cluster role binding
Use proper operator namespace in the service account role binding subject for global operators. Was using empty global operator watch namespace before which caused errors in the cluster role binding.

(cherry picked from commit 2ffdcfab6f8fa4ee90f9581b26ebf7cbe48aa685)

**Release Note**
```release-note
Fix(Knative): Cluster role binding for Knative addressable resolver. Bring back permissions for messaging.knative.dev resources (e.g. channel)
```


christophd: seems to be a flaky e2e test. @squakez @tadayosi could you please respin the failed workflow to see if it runs successful?

Patch:
diff --git a/docs/modules/ROOT/pages/contributing/developers.adoc b/docs/modules/ROOT/pages/contributing/developers.adoc
index e4982ea97..ad3fe29dc 100644
--- a/docs/modules/ROOT/pages/contributing/developers.adoc
+++ b/docs/modules/ROOT/pages/contributing/developers.adoc
@@ -27,6 +27,7 @@ In order to build the project, you need to comply with the following requirement
 * **MinGW**: needed to compile the project on Windows. Refer to the https://www.mingw-w64.org/[MinGW website] for the installation.
 * **Windows Subsystem for Linux (WSL)**: for running Linux binary executables natively on Windows. Refer to https://docs.microsoft.com/en-us/windows/wsl/install[WSL Website] for installation. Alternatively, you can use https://www.cygwin.com/[Cygwin] or https://www.educative.io/edpresso/how-to-install-git-bash-in-windows[Git Bash].
 
+NOTE: MacOS users will need to use *gnu-sed* to successfully run the Make build scripts (e.g. for generating the Camel K bundle). Please install gnu-sed on your machine (e.g. `brew install gnu-sed`) and set your PATH accordingly to use gnu-sed with: `export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"`
 
 The Camel K Java runtime (camel-k-runtime) requires:
 
diff --git a/pkg/cmd/operator/operator.go b/pkg/cmd/operator/operator.go
index c58db2d00..8a8ffa2a7 100644
--- a/pkg/cmd/operator/operator.go
+++ b/pkg/cmd/operator/operator.go
@@ -23,6 +23,7 @@ import (
 	"fmt"
 	"math/rand"
 	"os"
+	"reflect"
 	"runtime"
 	"strconv"
 	"time"
@@ -31,10 +32,10 @@ import (
 
 	appsv1 "k8s.io/api/apps/v1"
 	batchv1 "k8s.io/api/batch/v1"
-	batchv1beta1 "k8s.io/api/batch/v1beta1"
 	coordination "k8s.io/api/coordination/v1"
 	corev1 "k8s.io/api/core/v1"
 	k8serrors "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/fields"
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/selection"
 	"k8s.io/client-go/tools/leaderelection/resourcelock"
@@ -153,6 +154,22 @@ func Run(healthPort, monitoringPort int32, leaderElection bool, leaderElectionID
 	exitOnError(err, "cannot create Integration label selector")
 	selector := labels.NewSelector().Add(*hasIntegrationLabel)
 
+	selectors := cache.SelectorsByObject{
+		&corev1.Pod{}:        {Label: selector},
+		&appsv1.Deployment{}: {Label: selector},
+		&batchv1.Job{}:       {Label: selector},
+		&servingv1.Service{}: {Label: selector},
+	}
+
+	if ok, err := kubernetes.IsAPIResourceInstalled(c, batchv1.SchemeGroupVersion.String(), reflect.TypeOf(batchv1.CronJob{}).Name()); ok && err == nil {
+		selectors[&batchv1.CronJob{}] = struct {
+			Label labels.Selector
+			Field fields.Selector
+		}{
+			Label: selector,
+		}
+	}
+
 	mgr, err := manager.New(c.GetConfig(), manager.Options{
 		Namespace:                     watchNamespace,
 		EventBroadcaster:              broadcaster,
@@ -165,13 +182,7 @@ func Run(healthPort, monitoringPort int32, leaderElection bool, leaderElectionID
 		MetricsBindAddress:            ":" + strconv.Itoa(int(monitoringPort)),
 		NewCache: cache.BuilderWithOptions(
 			cache.Options{
-				SelectorsByObject: cache.SelectorsByObject{
-					&corev1.Pod{}:           {Label: selector},
-					&appsv1.Deployment{}:    {Label: selector},
-					&batchv1beta1.CronJob{}: {Label: selector},
-					&batchv1.Job{}:          {Label: selector},
-					&servingv1.Service{}:    {Label: selector},
-				},
+				SelectorsByObject: selectors,
 			},
 		),
 	})
diff --git a/pkg/cmd/trait_help.go b/pkg/cmd/trait_help.go
index b56e8c69e..0ef7bc9af 100644
--- a/pkg/cmd/trait_help.go
+++ b/pkg/cmd/trait_help.go
@@ -26,14 +26,12 @@ import (
 	"strings"
 
 	"github.com/fatih/structs"
-	"github.com/mitchellh/mapstructure"
 	"github.com/spf13/cobra"
 	"gopkg.in/yaml.v2"
 
 	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
 	"github.com/apache/camel-k/pkg/resources"
 	"github.com/apache/camel-k/pkg/trait"
-	"github.com/apache/camel-k/pkg/util"
 	"github.com/apache/camel-k/pkg/util/indentedwriter"
 )
 
@@ -263,108 +261,3 @@ func outputTraits(descriptions []*traitDescription) (string, error) {
 		return nil
 	})
 }
-
-func validateTraits(catalog *trait.Catalog, traits []string) error {
-	tp := catalog.ComputeTraitsProperties()
-	for _, t := range traits {
-		kv := strings.SplitN(t, "=", 2)
-		prefix := kv[0]
-		if strings.Contains(prefix, "[") {
-			prefix = prefix[0:strings.Index(prefix, "[")]
-		}
-		if !util.StringSliceExists(tp, prefix) {
-			return fmt.Errorf("%s is not a valid trait property", t)
-		}
-	}
-
-	return nil
-}
-
-func configureTraits(options []string, catalog trait.Finder) (map[string]v1.TraitSpec, error) {
-	traits := make(map[string]map[string]interface{})
-
-	for _, option := range options {
-		parts := traitConfigRegexp.FindStringSubmatch(option)
-		if len(parts) < 4 {
-			return nil, errors.New("unrecognized config format (expected \"<trait>.<prop>=<value>\"): " + option)
-		}
-		id := parts[1]
-		fullProp := parts[2][1:]
-		value := parts[3]
-		if _, ok := traits[id]; !ok {
-			traits[id] = make(map[string]interface{})
-		}
-
-		propParts := util.ConfigTreePropertySplit(fullProp)
-		var current = traits[id]
-		if len(propParts) > 1 {
-			c, err := util.NavigateConfigTree(current, propParts[0:len(propParts)-1])
-			if err != nil {
-				return nil, err
-			}
-			if cc, ok := c.(map[string]interface{}); ok {
-				current = cc
-			} else {
-				return nil, errors.New("trait configuration cannot end with a slice")
-			}
-		}
-
-		prop := propParts[len(propParts)-1]
-		switch v := current[prop].(type) {
-		case []string:
-			current[prop] = append(v, value)
-		case string:
-			// Aggregate multiple occurrences of the same option into a string array, to emulate POSIX conventions.
-			// This enables executing:
-			// $ kamel run -t <trait>.<property>=<value_1> ... -t <trait>.<property>=<value_N>
-			// Or:
-			// $ kamel run --trait <trait>.<property>=<value_1>,...,<trait>.<property>=<value_N>
-			current[prop] = []string{v, value}
-		case nil:
-			current[prop] = value
-		}
-	}
-
-	specs := make(map[string]v1.TraitSpec)
-	for id, config := range traits {
-		t := catalog.GetTrait(id)
-		if t != nil {
-			// let's take a clone to prevent default values set at runtime from being serialized
-			zero := reflect.New(reflect.TypeOf(t)).Interface()
-			err := configureTrait(config, zero)
-			if err != nil {
-				return nil, err
-			}
-			data, err := json.Marshal(zero)
-			if err != nil {
-				return nil, err
-			}
-			var spec v1.TraitSpec
-			err = json.Unmarshal(data, &spec.Configuration)
-			if err != nil {
-				return nil, err
-			}
-			specs[id] = spec
-		}
-	}
-
-	return specs, nil
-}
-
-func configureTrait(config map[string]interface{}, trait interface{}) error {
-	md := mapstructure.Metadata{}
-
-	decoder, err := mapstructure.NewDecoder(
-		&mapstructure.DecoderConfig{
-			Metadata:         &md,
-			WeaklyTypedInput: true,
-			TagName:          "property",
-			Result:           &trait,
-		},
-	)
-	if err != nil {
-		return err
-	}
-
-	return decoder.Decode(config)
-}
diff --git a/pkg/cmd/trait_support.go b/pkg/cmd/trait_support.go
new file mode 100644
index 000000000..63fa23b9c
--- /dev/null
+++ b/pkg/cmd/trait_support.go
@@ -0,0 +1,136 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cmd
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"reflect"
+	"strings"
+
+	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+	"github.com/apache/camel-k/pkg/trait"
+	"github.com/apache/camel-k/pkg/util"
+	"github.com/mitchellh/mapstructure"
+)
+
+func validateTraits(catalog *trait.Catalog, traits []string) error {
+	tp := catalog.ComputeTraitsProperties()
+	for _, t := range traits {
+		kv := strings.SplitN(t, "=", 2)
+		prefix := kv[0]
+		if strings.Contains(prefix, "[") {
+			prefix = prefix[0:strings.Index(prefix, "[")]
+		}
+		if !util.StringSliceExists(tp, prefix) {
+			return fmt.Errorf("%s is not a valid trait property", t)
+		}
+	}
+
+	return nil
+}
+
+func configureTraits(options []string, catalog trait.Finder) (map[string]v1.TraitSpec, error) {
+	traits := make(map[string]map[string]interface{})
+
+	for _, option := range options {
+		parts := traitConfigRegexp.FindStringSubmatch(option)
+		if len(parts) < 4 {
+			return nil, errors.New("unrecognized config format (expected \"<trait>.<prop>=<value>\"): " + option)
+		}
+		id := parts[1]
+		fullProp := parts[2][1:]
+		value := parts[3]
+		if _, ok := traits[id]; !ok {
+			traits[id] = make(map[string]interface{})
+		}
+
+		propParts := util.ConfigTreePropertySplit(fullProp)
+		var current = traits[id]
+		if len(propParts) > 1 {
+			c, err := util.NavigateConfigTree(current, propParts[0:len(propParts)-1])
+			if err != nil {
+				return nil, err
+			}
+			if cc, ok := c.(map[string]interface{}); ok {
+				current = cc
+			} else {
+				return nil, errors.New("trait configuration cannot end with a slice")
+			}
+		}
+
+		prop := propParts[len(propParts)-1]
+		switch v := current[prop].(type) {
+		case []string:
+			current[prop] = append(v, value)
+		case string:
+			// Aggregate multiple occurrences of the same option into a string array, to emulate POSIX conventions.
+			// This enables executing:
+			// $ kamel run -t <trait>.<property>=<value_1> ... -t <trait>.<property>=<value_N>
+			// Or:
+			// $ kamel run --trait <trait>.<property>=<value_1>,...,<trait>.<property>=<value_N>
+			current[prop] = []string{v, value}
+		case nil:
+			current[prop] = value
+		}
+	}
+
+	specs := make(map[string]v1.TraitSpec)
+	for id, config := range traits {
+		t := catalog.GetTrait(id)
+		if t != nil {
+			// let's take a clone to prevent default values set at runtime from being serialized
+			zero := reflect.New(reflect.TypeOf(t)).Interface()
+			err := configureTrait(config, zero)
+			if err != nil {
+				return nil, err
+			}
+			data, err := json.Marshal(zero)
+			if err != nil {
+				return nil, err
+			}
+			var spec v1.TraitSpec
+			err = json.Unmarshal(data, &spec.Configuration)
+			if err != nil {
+				return nil, err
+			}
+			specs[id] = spec
+		}
+	}
+
+	return specs, nil
+}
+
+func configureTrait(config map[string]interface{}, trait interface{}) error {
+	md := mapstructure.Metadata{}
+
+	decoder, err := mapstructure.NewDecoder(
+		&mapstructure.DecoderConfig{
+			Metadata:         &md,
+			WeaklyTypedInput: true,
+			TagName:          "property",
+			Result:           &trait,
+		},
+	)
+	if err != nil {
+		return err
+	}
+
+	return decoder.Decode(config)
+}
diff --git a/pkg/controller/integration/integration_controller.go b/pkg/controller/integration/integration_controller.go
index 964adb18f..2ee389e2d 100644
--- a/pkg/controller/integration/integration_controller.go
+++ b/pkg/controller/integration/integration_controller.go
@@ -24,7 +24,7 @@ import (
 	"time"
 
 	appsv1 "k8s.io/api/apps/v1"
-	batchv1beta1 "k8s.io/api/batch/v1beta1"
+	batchv1 "k8s.io/api/batch/v1"
 	corev1 "k8s.io/api/core/v1"
 	k8serrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/runtime"
@@ -203,8 +203,6 @@ func add(mgr manager.Manager, c client.Client, r reconcile.Reconciler) error {
 			})).
 		// Watch for the owned Deployments
 		Owns(&appsv1.Deployment{}, builder.WithPredicates(StatusChangedPredicate{})).
-		// Watch for the owned CronJobs
-		Owns(&batchv1beta1.CronJob{}, builder.WithPredicates(StatusChangedPredicate{})).
 		// Watch for the Integration Pods
 		Watches(&source.Kind{Type: &corev1.Pod{}},
 			handler.EnqueueRequestsFromMapFunc(func(a ctrl.Object) []reconcile.Request {
@@ -223,6 +221,11 @@ func add(mgr manager.Manager, c client.Client, r reconcile.Reconciler) error {
 				}
 			}))
 
+	if ok, err := kubernetes.IsAPIResourceInstalled(c, batchv1.SchemeGroupVersion.String(), reflect.TypeOf(batchv1.CronJob{}).Name()); ok && err == nil {
+		// Watch for the owned CronJobs
+		b.Owns(&batchv1.CronJob{}, builder.WithPredicates(StatusChangedPredicate{}))
+	}
+
 	// Watch for the owned Knative Services conditionally
 	if ok, err := kubernetes.IsAPIResourceInstalled(c, servingv1.SchemeGroupVersion.String(), reflect.TypeOf(servingv1.Service{}).Name()); err != nil {
 		return err
diff --git a/pkg/controller/integration/monitor.go b/pkg/controller/integration/monitor.go
index 91ea73f63..3e68a3a24 100644
--- a/pkg/controller/integration/monitor.go
+++ b/pkg/controller/integration/monitor.go
@@ -25,7 +25,7 @@ import (
 	"strconv"
 
 	appsv1 "k8s.io/api/apps/v1"
-	batchv1beta1 "k8s.io/api/batch/v1beta1"
+	batchv1 "k8s.io/api/batch/v1"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/equality"
 	k8serrors "k8s.io/apimachinery/pkg/api/errors"
@@ -185,9 +185,9 @@ func (action *monitorAction) newController(ctx context.Context, env *trait.Envir
 			integration: integration,
 		}
 	case isConditionTrue(integration, v1.IntegrationConditionCronJobAvailable):
-		obj = getUpdatedController(env, &batchv1beta1.CronJob{})
+		obj = getUpdatedController(env, &batchv1.CronJob{})
 		controller = &cronJobController{
-			obj:         obj.(*batchv1beta1.CronJob),
+			obj:         obj.(*batchv1.CronJob),
 			integration: integration,
 			client:      action.client,
 			context:     ctx,
diff --git a/pkg/controller/integration/monitor_cronjob.go b/pkg/controller/integration/monitor_cronjob.go
index 8a024df16..77bf162b8 100644
--- a/pkg/controller/integration/monitor_cronjob.go
+++ b/pkg/controller/integration/monitor_cronjob.go
@@ -22,7 +22,6 @@ import (
 	"fmt"
 
 	batchv1 "k8s.io/api/batch/v1"
-	batchv1beta1 "k8s.io/api/batch/v1beta1"
 	corev1 "k8s.io/api/core/v1"
 
 	ctrl "sigs.k8s.io/controller-runtime/pkg/client"
@@ -33,7 +32,7 @@ import (
 )
 
 type cronJobController struct {
-	obj              *batchv1beta1.CronJob
+	obj              *batchv1.CronJob
 	integration      *v1.Integration
 	client           client.Client
 	context          context.Context
diff --git a/pkg/install/knative.go b/pkg/install/knative.go
index 6f30db5ab..23d779c60 100644
--- a/pkg/install/knative.go
+++ b/pkg/install/knative.go
@@ -32,26 +32,26 @@ import (
 
 const knativeAddressableResolverClusterRoleName = "addressable-resolver"
 
-// BindKnativeAddressableResolverClusterRole binds the Knative Addressable resolver aggregated ClusterRole
+// BindKnativeAddressableResolverClusterRole binds the Knative addressable resolver aggregated ClusterRole
 // to the operator ServiceAccount.
-func BindKnativeAddressableResolverClusterRole(ctx context.Context, c kubernetes.Interface, namespace string) error {
+func BindKnativeAddressableResolverClusterRole(ctx context.Context, c kubernetes.Interface, namespace string, operatorNamespace string) error {
 	if isKnative, err := knative.IsInstalled(ctx, c); err != nil {
 		return err
 	} else if !isKnative {
 		return nil
 	}
 	if namespace != "" {
-		return applyAddressableResolverRoleBinding(ctx, c, namespace)
+		return applyAddressableResolverRoleBinding(ctx, c, namespace, operatorNamespace)
 	}
-	return applyAddressableResolverClusterRoleBinding(ctx, c, namespace)
+	return applyAddressableResolverClusterRoleBinding(ctx, c, operatorNamespace)
 }
 
-func applyAddressableResolverRoleBinding(ctx context.Context, c kubernetes.Interface, namespace string) error {
+func applyAddressableResolverRoleBinding(ctx context.Context, c kubernetes.Interface, namespace string, operatorNamespace string) error {
 	rb := rbacv1ac.RoleBinding(fmt.Sprintf("%s-addressable-resolver", serviceAccountName), namespace).
 		WithSubjects(
 			rbacv1ac.Subject().
 				WithKind("ServiceAccount").
-				WithNamespace(namespace).
+				WithNamespace(operatorNamespace).
 				WithName(serviceAccountName),
 		).
 		WithRoleRef(rbacv1ac.RoleRef().
@@ -65,12 +65,12 @@ func applyAddressableResolverRoleBinding(ctx context.Context, c kubernetes.Inter
 	return err
 }
 
-func applyAddressableResolverClusterRoleBinding(ctx context.Context, c kubernetes.Interface, namespace string) error {
+func applyAddressableResolverClusterRoleBinding(ctx context.Context, c kubernetes.Interface, operatorNamespace string) error {
 	crb := rbacv1ac.ClusterRoleBinding(fmt.Sprintf("%s-addressable-resolver", serviceAccountName)).
 		WithSubjects(
 			rbacv1ac.Subject().
 				WithKind("ServiceAccount").
-				WithNamespace(namespace).
+				WithNamespace(operatorNamespace).
 				WithName(serviceAccountName),
 		).
 		WithRoleRef(rbacv1ac.RoleRef().
diff --git a/pkg/install/optional.go b/pkg/install/optional.go
index dd3d0fc42..5c47d0a7c 100644
--- a/pkg/install/optional.go
+++ b/pkg/install/optional.go
@@ -65,7 +65,7 @@ func OperatorStartupOptionalTools(ctx context.Context, c client.Client, namespac
 	}
 
 	// Try to bind the Knative Addressable resolver aggregated ClusterRole to the operator ServiceAccount
-	if err := BindKnativeAddressableResolverClusterRole(ctx, c, namespace); err != nil {
+	if err := BindKnativeAddressableResolverClusterRole(ctx, c, namespace, operatorNamespace); err != nil {
 		log.Info("Cannot bind the Knative Addressable resolver aggregated ClusterRole: skipping.")
 		log.V(8).Info("Error while binding the Knative Addressable resolver aggregated ClusterRole", "error", err)
 	}
diff --git a/pkg/trait/container.go b/pkg/trait/container.go
index 0f61820e6..cc18bdf56 100644
--- a/pkg/trait/container.go
+++ b/pkg/trait/container.go
@@ -22,7 +22,7 @@ import (
 	"path"
 
 	appsv1 "k8s.io/api/apps/v1"
-	"k8s.io/api/batch/v1beta1"
+	batchv1 "k8s.io/api/batch/v1"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/resource"
 	"k8s.io/apimachinery/pkg/util/intstr"
@@ -297,7 +297,7 @@ func (t *containerTrait) configureContainer(e *Environment) error {
 	}
 
 	// CronJob
-	if err := e.Resources.VisitCronJobE(func(cron *v1beta1.CronJob) error {
+	if err := e.Resources.VisitCronJobE(func(cron *batchv1.CronJob) error {
 		for _, envVar := range e.EnvVars {
 			envvar.SetVar(&container.Env, envVar)
 		}
diff --git a/pkg/trait/cron.go b/pkg/trait/cron.go
index dbfeb556e..fb717118f 100644
--- a/pkg/trait/cron.go
+++ b/pkg/trait/cron.go
@@ -24,7 +24,6 @@ import (
 	"strings"
 
 	batchv1 "k8s.io/api/batch/v1"
-	batchv1beta1 "k8s.io/api/batch/v1beta1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
@@ -172,7 +171,7 @@ func (t *cronTrait) Configure(e *Environment) (bool, error) {
 		}
 
 		if t.ConcurrencyPolicy == "" {
-			t.ConcurrencyPolicy = string(batchv1beta1.ForbidConcurrent)
+			t.ConcurrencyPolicy = string(batchv1.ForbidConcurrent)
 		}
 
 		if (t.Schedule == "" && t.Components == "") && t.Fallback == nil {
@@ -264,7 +263,7 @@ func (t *cronTrait) Apply(e *Environment) error {
 	return nil
 }
 
-func (t *cronTrait) getCronJobFor(e *Environment) *batchv1beta1.CronJob {
+func (t *cronTrait) getCronJobFor(e *Environment) *batchv1.CronJob {
 	annotations := make(map[string]string)
 	if e.Integration.Annotations != nil {
 		for k, v := range filterTransferableAnnotations(e.Integration.Annotations) {
@@ -282,10 +281,10 @@ func (t *cronTrait) getCronJobFor(e *Environment) *batchv1beta1.CronJob {
 		backoffLimit = *t.BackoffLimit
 	}
 
-	cronjob := batchv1beta1.CronJob{
+	cronjob := batchv1.CronJob{
 		TypeMeta: metav1.TypeMeta{
 			Kind:       "CronJob",
-			APIVersion: batchv1beta1.SchemeGroupVersion.String(),
+			APIVersion: batchv1.SchemeGroupVersion.String(),
 		},
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      e.Integration.Name,
@@ -295,11 +294,11 @@ func (t *cronTrait) getCronJobFor(e *Environment) *batchv1beta1.CronJob {
 			},
 			Annotations: e.Integration.Annotations,
 		},
-		Spec: batchv1beta1.CronJobSpec{
+		Spec: batchv1.CronJobSpec{
 			Schedule:                t.Schedule,
-			ConcurrencyPolicy:       batchv1beta1.ConcurrencyPolicy(t.ConcurrencyPolicy),
+			ConcurrencyPolicy:       batchv1.ConcurrencyPolicy(t.ConcurrencyPolicy),
 			StartingDeadlineSeconds: t.StartingDeadlineSeconds,
-			JobTemplate: batchv1beta1.JobTemplateSpec{
+			JobTemplate: batchv1.JobTemplateSpec{
 				Spec: batchv1.JobSpec{
 					ActiveDeadlineSeconds: &activeDeadline,
 					BackoffLimit:          &backoffLimit,
diff --git a/pkg/trait/mount.go b/pkg/trait/mount.go
index 6c8976a85..85b1f1742 100644
--- a/pkg/trait/mount.go
+++ b/pkg/trait/mount.go
@@ -23,7 +23,7 @@ import (
 	"strings"
 
 	appsv1 "k8s.io/api/apps/v1"
-	"k8s.io/api/batch/v1beta1"
+	batchv1 "k8s.io/api/batch/v1"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 
@@ -118,7 +118,7 @@ func (t *mountTrait) Apply(e *Environment) error {
 	}
 
 	// CronJob
-	if err := e.Resources.VisitCronJobE(func(cron *v1beta1.CronJob) error {
+	if err := e.Resources.VisitCronJobE(func(cron *batchv1.CronJob) error {
 		volumes = &cron.Spec.JobTemplate.Spec.Template.Spec.Volumes
 		visited = true
 		return nil
diff --git a/pkg/trait/pod.go b/pkg/trait/pod.go
index b3722f32b..b077328b0 100644
--- a/pkg/trait/pod.go
+++ b/pkg/trait/pod.go
@@ -21,7 +21,7 @@ import (
 	"fmt"
 
 	appsv1 "k8s.io/api/apps/v1"
-	"k8s.io/api/batch/v1beta1"
+	batchv1 "k8s.io/api/batch/v1"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/util/json"
 	"k8s.io/apimachinery/pkg/util/strategicpatch"
@@ -70,7 +70,7 @@ func (t *podTrait) Apply(e *Environment) error {
 	}
 	switch strategy {
 	case ControllerStrategyCronJob:
-		e.Resources.VisitCronJob(func(c *v1beta1.CronJob) {
+		e.Resources.VisitCronJob(func(c *batchv1.CronJob) {
 			if c.Name == e.Integration.Name {
 				if patchedPodSpec, err = t.applyChangesTo(&c.Spec.JobTemplate.Spec.Template.Spec, changes); err == nil {
 					c.Spec.JobTemplate.Spec.Template.Spec = *patchedPodSpec
diff --git a/pkg/trait/trait_types.go b/pkg/trait/trait_types.go
index 492c3c22d..a53ec9841 100644
--- a/pkg/trait/trait_types.go
+++ b/pkg/trait/trait_types.go
@@ -27,7 +27,7 @@ import (
 	"github.com/pkg/errors"
 
 	appsv1 "k8s.io/api/apps/v1"
-	"k8s.io/api/batch/v1beta1"
+	batchv1 "k8s.io/api/batch/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
@@ -326,7 +326,7 @@ func (e *Environment) GetIntegrationPodSpec() *corev1.PodSpec {
 	}
 
 	// Cronjob
-	cronJob := e.Resources.GetCronJob(func(c *v1beta1.CronJob) bool {
+	cronJob := e.Resources.GetCronJob(func(c *batchv1.CronJob) bool {
 		return c.Name == e.Integration.Name
 	})
 	if cronJob != nil {
diff --git a/pkg/util/kubernetes/collection.go b/pkg/util/kubernetes/collection.go
index 54e344905..f68b407b6 100644
--- a/pkg/util/kubernetes/collection.go
+++ b/pkg/util/kubernetes/collection.go
@@ -19,7 +19,7 @@ package kubernetes
 
 import (
 	appsv1 "k8s.io/api/apps/v1"
-	"k8s.io/api/batch/v1beta1"
+	batchv1 "k8s.io/api/batch/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
@@ -268,9 +268,9 @@ func (c *Collection) GetRoute(filter func(*routev1.Route) bool) *routev1.Route {
 }
 
 // GetCronJob returns a CronJob that matches the given function.
-func (c *Collection) GetCronJob(filter func(job *v1beta1.CronJob) bool) *v1beta1.CronJob {
-	var retValue *v1beta1.CronJob
-	c.VisitCronJob(func(re *v1beta1.CronJob) {
+func (c *Collection) GetCronJob(filter func(job *batchv1.CronJob) bool) *batchv1.CronJob {
+	var retValue *batchv1.CronJob
+	c.VisitCronJob(func(re *batchv1.CronJob) {
 		if filter(re) {
 			retValue = re
 		}
@@ -279,18 +279,18 @@ func (c *Collection) GetCronJob(filter func(job *v1beta1.CronJob) bool) *v1beta1
 }
 
 // VisitCronJob executes the visitor function on all CronJob resources.
-func (c *Collection) VisitCronJob(visitor func(*v1beta1.CronJob)) {
+func (c *Collection) VisitCronJob(visitor func(*batchv1.CronJob)) {
 	c.Visit(func(res runtime.Object) {
-		if conv, ok := res.(*v1beta1.CronJob); ok {
+		if conv, ok := res.(*batchv1.CronJob); ok {
 			visitor(conv)
 		}
 	})
 }
 
 // VisitCronJobE executes the visitor function on all CronJob resources.
-func (c *Collection) VisitCronJobE(visitor func(*v1beta1.CronJob) error) error {
+func (c *Collection) VisitCronJobE(visitor func(*batchv1.CronJob) error) error {
 	return c.VisitE(func(res runtime.Object) error {
-		if conv, ok := res.(*v1beta1.CronJob); ok {
+		if conv, ok := res.(*batchv1.CronJob); ok {
 			return visitor(conv)
 		}
 
@@ -373,7 +373,7 @@ func (c *Collection) VisitContainer(visitor func(container *corev1.Container)) {
 			visitor(cntref)
 		}
 	})
-	c.VisitCronJob(func(c *v1beta1.CronJob) {
+	c.VisitCronJob(func(c *batchv1.CronJob) {
 		for idx := range c.Spec.JobTemplate.Spec.Template.Spec.Containers {
 			cntref := &c.Spec.JobTemplate.Spec.Template.Spec.Containers[idx]
 			visitor(cntref)
@@ -395,7 +395,7 @@ func (c *Collection) GetController(filter func(object ctrl.Object) bool) ctrl.Ob
 	if svc != nil {
 		return svc
 	}
-	cj := c.GetCronJob(func(job *v1beta1.CronJob) bool {
+	cj := c.GetCronJob(func(job *batchv1.CronJob) bool {
 		return filter(job)
 	})
 	if cj != nil {
@@ -412,7 +412,7 @@ func (c *Collection) VisitPodSpec(visitor func(container *corev1.PodSpec)) {
 	c.VisitKnativeConfigurationSpec(func(cs *serving.ConfigurationSpec) {
 		visitor(&cs.Template.Spec.PodSpec)
 	})
-	c.VisitCronJob(func(d *v1beta1.CronJob) {
+	c.VisitCronJob(func(d *batchv1.CronJob) {
 		visitor(&d.Spec.JobTemplate.Spec.Template.Spec)
 	})
 }
@@ -425,7 +425,7 @@ func (c *Collection) VisitPodTemplateMeta(visitor func(meta *metav1.ObjectMeta))
 	c.VisitKnativeConfigurationSpec(func(cs *serving.ConfigurationSpec) {
 		visitor(&cs.Template.ObjectMeta)
 	})
-	c.VisitCronJob(func(d *v1beta1.CronJob) {
+	c.VisitCronJob(func(d *batchv1.CronJob) {
 		visitor(&d.Spec.JobTemplate.Spec.Template.ObjectMeta)
 	})
 }
diff --git a/script/Makefile b/script/Makefile
index 7ac662583..434997c74 100644
--- a/script/Makefile
+++ b/script/Makefile
@@ -530,11 +530,7 @@ bundle: set-version generate-crd $(BUNDLE_CAMEL_APIS) kustomize operator-sdk pre
 			-q --overwrite --version $(OPERATOR_VERSION) \
 			--kustomize-dir config/manifests $(BUNDLE_METADATA_OPTS)
 	@# Move the dockerfile into the bundle directory
-ifeq ($(shell uname -s 2>/dev/null || echo Unknown),Darwin)
-	@mv bundle.Dockerfile bundle/Dockerfile && sed -i '' 's/bundle\///g' bundle/Dockerfile
-else
 	@mv bundle.Dockerfile bundle/Dockerfile && sed -i 's/bundle\///g' bundle/Dockerfile
-endif
 	@# Adds the licence headers to the csv file
 	./script/add_license.sh bundle/manifests ./script/headers/yaml.txt
 	$(OPERATOR_SDK) bundle validate ./bundle
diff --git a/script/add_createdAt.sh b/script/add_createdAt.sh
index 1dd1ad86c..8029d508c 100755
--- a/script/add_createdAt.sh
+++ b/script/add_createdAt.sh
@@ -35,11 +35,6 @@ created=$(date -u +%FT%TZ)
 
 set +e
 for file in `find "$dir" -type f`; do
-  if [[ "$OSTYPE" == "linux-gnu"* ]]; then
-    sed -i "s/createdAt: .*/createdAt: ${created}/" "${file}"
-  elif [[ "$OSTYPE" == "darwin"* ]]; then
-    # Mac OSX
-    sed -i '' "s/createdAt: .*/createdAt: ${created}/" "${file}"
-  fi
+  sed -i "s/createdAt: .*/createdAt: ${created}/" "${file}"
 done
 set -e
diff --git a/script/gen_crd.sh b/script/gen_crd.sh
index 2bfb8c7ec..ad03de070 100755
--- a/script/gen_crd.sh
+++ b/script/gen_crd.sh
@@ -40,16 +40,9 @@ deploy_crd_file() {
   # Post-process source
   cat ./script/headers/yaml.txt > "$source"
   echo "" >> "$source"
-  if [[ "$OSTYPE" == "linux-gnu"* ]]; then
-    cat "${source}.orig" | sed -n '/^---/,/^status/p;/^status/q' \
-      | sed '1d;$d' \
-      | sed '/creationTimestamp:/a\  labels:\n    app: camel-k' >> "$source"
-  elif [[ "$OSTYPE" == "darwin"* ]]; then
-    # Mac OSX
-    cat "${source}.orig" | sed -n '/^---/,/^status/p;/^status/q' \
-      | sed '1d;$d' \
-      | sed -e $'/^  creationTimestamp:/a\\\n  labels:\\\n    app: camel-k' >> "$source"
-  fi
+  cat "${source}.orig" | sed -n '/^---/,/^status/p;/^status/q' \
+    | sed '1d;$d' \
+    | sed '/creationTimestamp:/a\  labels:\n    app: camel-k' >> "$source"
 
   for dest in "${@:2}"; do
     cp "$source" "$dest"
diff --git a/script/set_go_modules_version.sh b/script/set_go_modules_version.sh
index 091f8533a..d67ae5d59 100755
--- a/script/set_go_modules_version.sh
+++ b/script/set_go_modules_version.sh
@@ -30,19 +30,9 @@ api_rule="s/github.com\/apache\/camel-k\/pkg\/apis\/camel [A-Za-z0-9\.\-]+.*$/gi
 client_rule="s/github.com\/apache\/camel-k\/pkg\/client\/camel [A-Za-z0-9\.\-]+.*$/github.com\/apache\/camel-k\/pkg\/client\/camel $target_tag/"
 kr_rule="s/github.com\/apache\/camel-k\/pkg\/kamelet\/repository [A-Za-z0-9\.\-]+.*$/github.com\/apache\/camel-k\/pkg\/kamelet\/repository $target_tag/"
 
-if [[ "$OSTYPE" == "linux-gnu"* ]]; then
-  sed -i -r "$api_rule"    $location/../go.mod
-  sed -i -r "$client_rule" $location/../go.mod
-  sed -i -r "$kr_rule"     $location/../go.mod
-  sed -i -r "$api_rule"    $location/../pkg/client/camel/go.mod
-  sed -i -r "$api_rule"    $location/../pkg/kamelet/repository/go.mod
-  sed -i -r "$client_rule" $location/../pkg/kamelet/repository/go.mod
-elif [[ "$OSTYPE" == "darwin"* ]]; then
-  # Mac OSX
-  sed -i '' -E "$api_rule"    $location/../go.mod
-  sed -i '' -E "$client_rule" $location/../go.mod
-  sed -i '' -E "$kr_rule"     $location/../go.mod
-  sed -i '' -E "$api_rule"    $location/../pkg/client/camel/go.mod
-  sed -i '' -E "$api_rule"    $location/../pkg/kamelet/repository/go.mod
-  sed -i '' -E "$client_rule" $location/../pkg/kamelet/repository/go.mod
-fi
+sed -i -r "$api_rule"    $location/../go.mod
+sed -i -r "$client_rule" $location/../go.mod
+sed -i -r "$kr_rule"     $location/../go.mod
+sed -i -r "$api_rule"    $location/../pkg/client/camel/go.mod
+sed -i -r "$api_rule"    $location/../pkg/kamelet/repository/go.mod
+sed -i -r "$client_rule" $location/../pkg/kamelet/repository/go.mod
diff --git a/script/set_version.sh b/script/set_version.sh
index 0d6dd1183..d47714d9d 100755
--- a/script/set_version.sh
+++ b/script/set_version.sh
@@ -30,34 +30,17 @@ k8s_version_label="app.kubernetes.io\/version"
 
 for f in $(find $location/../config/manager -type f -name "*.yaml");
 do
-  if [[ "$OSTYPE" == "linux-gnu"* ]]; then
-    sed -i -r "s/image: .*/image: ${sanitized_image_name}:${version}/" $f
-    sed -i -r "s/${k8s_version_label}: .*/${k8s_version_label}: \""${version}"\"/" $f
-  elif [[ "$OSTYPE" == "darwin"* ]]; then
-    # Mac OSX
-    sed -i '' -E "s/image: .*/image: ${sanitized_image_name}:${version}/" $f
-    sed -i '' -E "s/${k8s_version_label}: .*/${k8s_version_label}: \""${version}"\"/" $f
-  fi
+  sed -i -r "s/image: .*/image: ${sanitized_image_name}:${version}/" $f
+  sed -i -r "s/${k8s_version_label}: .*/${k8s_version_label}: \""${version}"\"/" $f
 done
 
 for f in $(find $location/../config/manifests/bases -type f -name "*.yaml");
 do
-  if [[ "$OSTYPE" == "linux-gnu"* ]]; then
-    sed -i -r "s/containerImage: .*/containerImage: ${sanitized_image_name}:${version}/" $f
-  elif [[ "$OSTYPE" == "darwin"* ]]; then
-    # Mac OSX
-    sed -i '' -E "s/containerImage: .*/containerImage: ${sanitized_image_name}:${version}/" $f
-  fi
+  sed -i -r "s/containerImage: .*/containerImage: ${sanitized_image_name}:${version}/" $f
 done
 
 # Update helm chart
-if [[ "$OSTYPE" == "linux-gnu"* ]]; then
-  sed -i -r "s/image: .*/image: ${sanitized_image_name}:${version}/" $location/../helm/camel-k/values.yaml
-  sed -i -r "s/appVersion:\s([0-9]+[a-zA-Z0-9\-\.].*).*/appVersion: ${version}/" $location/../helm/camel-k/Chart.yaml
-elif [[ "$OSTYPE" == "darwin"* ]]; then
-  # Mac OSX
-  sed -i '' -E "s/docker.io\/apache\/camel-k:([0-9]+[a-zA-Z0-9\-\.].*).*/${sanitized_image_name}:${version}/" $location/../helm/camel-k/values.yaml
-  sed -i '' -E "s/appVersion:\s([0-9]+[a-zA-Z0-9\-\.].*).*/appVersion: ${version}/" $location/../helm/camel-k/Chart.yaml
-fi
+sed -i -r "s/image: .*/image: ${sanitized_image_name}:${version}/" $location/../helm/camel-k/values.yaml
+sed -i -r "s/appVersion:\s([0-9]+[a-zA-Z0-9\-\.].*).*/appVersion: ${version}/" $location/../helm/camel-k/Chart.yaml
 
 echo "Camel K version set to: $version and image name to: $image_name"


Test patch:
diff --git a/e2e/common/cli/install_test.go b/e2e/common/cli/install_test.go
index b52f6c772..dfcefa0f9 100644
--- a/e2e/common/cli/install_test.go
+++ b/e2e/common/cli/install_test.go
@@ -38,7 +38,7 @@ import (
 	"github.com/apache/camel-k/pkg/util/defaults"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 	"github.com/apache/camel-k/pkg/util/openshift"
-	console "github.com/openshift/api/console/v1"
+	consolev1 "github.com/openshift/api/console/v1"
 )
 
 func TestBasicInstallation(t *testing.T) {
@@ -97,11 +97,11 @@ func TestConsoleCliDownload(t *testing.T) {
 	ocp, err := openshift.IsOpenShift(TestClient())
 	assert.Nil(t, err)
 
-	ok, err := kubernetes.IsAPIResourceInstalled(TestClient(), "console.openshift.io/v1", reflect.TypeOf(console.ConsoleCLIDownload{}).Name())
+	ok, err := kubernetes.IsAPIResourceInstalled(TestClient(), "console.openshift.io/v1", reflect.TypeOf(consolev1.ConsoleCLIDownload{}).Name())
 	assert.Nil(t, err)
 
 	if !ocp || !ok {
-		t.Skip("This test requires ConsoleCliDownload object which is available on OpenShift 4 only.")
+		t.Skip("This test requires ConsoleCliDownload object which is available on OpenShift 4+ only.")
 		return
 	}
 
diff --git a/e2e/common/cron_test.go b/e2e/common/cron_test.go
index dc26197db..db5b64178 100644
--- a/e2e/common/cron_test.go
+++ b/e2e/common/cron_test.go
@@ -23,17 +23,29 @@ limitations under the License.
 package common
 
 import (
+	"reflect"
 	"testing"
 
 	. "github.com/onsi/gomega"
+	"github.com/stretchr/testify/assert"
 
+	batchv1 "k8s.io/api/batch/v1"
 	corev1 "k8s.io/api/core/v1"
 
 	. "github.com/apache/camel-k/e2e/support"
 	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
 )
 
 func TestRunCronExample(t *testing.T) {
+	ok, err := kubernetes.IsAPIResourceInstalled(TestClient(), batchv1.SchemeGroupVersion.String(), reflect.TypeOf(batchv1.CronJob{}).Name())
+	assert.Nil(t, err)
+
+	if !ok {
+		t.Skip("This test requires CronJob batch/v1 API installed.")
+		return
+	}
+
 	WithNewTestNamespace(t, func(ns string) {
 		Expect(Kamel("install", "-n", ns).Execute()).To(Succeed())
 
diff --git a/e2e/support/test_support.go b/e2e/support/test_support.go
index e1e87ec2c..3fdd06077 100644
--- a/e2e/support/test_support.go
+++ b/e2e/support/test_support.go
@@ -46,7 +46,7 @@ import (
 	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 	appsv1 "k8s.io/api/apps/v1"
-	"k8s.io/api/batch/v1beta1"
+	batchv1 "k8s.io/api/batch/v1"
 	coordination "k8s.io/api/coordination/v1"
 	corev1 "k8s.io/api/core/v1"
 	rbacv1 "k8s.io/api/rbac/v1"
@@ -599,12 +599,12 @@ func RouteStatus(ns string, name string) func() string {
 	}
 }
 
-func IntegrationCronJob(ns string, name string) func() *v1beta1.CronJob {
-	return func() *v1beta1.CronJob {
-		lst := v1beta1.CronJobList{
+func IntegrationCronJob(ns string, name string) func() *batchv1.CronJob {
+	return func() *batchv1.CronJob {
+		lst := batchv1.CronJobList{
 			TypeMeta: metav1.TypeMeta{
 				Kind:       "CronJob",
-				APIVersion: v1beta1.SchemeGroupVersion.String(),
+				APIVersion: batchv1.SchemeGroupVersion.String(),
 			},
 		}
 		err := TestClient().List(TestContext, &lst,
diff --git a/pkg/trait/cron_test.go b/pkg/trait/cron_test.go
index d6eb49169..0f2612473 100644
--- a/pkg/trait/cron_test.go
+++ b/pkg/trait/cron_test.go
@@ -24,7 +24,7 @@ import (
 	passert "github.com/magiconair/properties/assert"
 	"github.com/stretchr/testify/assert"
 
-	batchv1beta1 "k8s.io/api/batch/v1beta1"
+	batchv1 "k8s.io/api/batch/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
@@ -428,7 +428,7 @@ func TestCronWithActiveDeadline(t *testing.T) {
 	assert.Nil(t, ct.Fallback)
 	assert.Contains(t, environment.Interceptors, "cron")
 
-	cronJob := environment.Resources.GetCronJob(func(job *batchv1beta1.CronJob) bool { return true })
+	cronJob := environment.Resources.GetCronJob(func(job *batchv1.CronJob) bool { return true })
 	assert.NotNil(t, cronJob)
 
 	assert.NotNil(t, cronJob.Spec.JobTemplate.Spec.ActiveDeadlineSeconds)
@@ -501,7 +501,7 @@ func TestCronWithBackoffLimit(t *testing.T) {
 	assert.Nil(t, ct.Fallback)
 	assert.Contains(t, environment.Interceptors, "cron")
 
-	cronJob := environment.Resources.GetCronJob(func(job *batchv1beta1.CronJob) bool { return true })
+	cronJob := environment.Resources.GetCronJob(func(job *batchv1.CronJob) bool { return true })
 	assert.NotNil(t, cronJob)
 
 	assert.NotNil(t, cronJob.Spec.JobTemplate.Spec.ActiveDeadlineSeconds)
diff --git a/pkg/trait/test_support.go b/pkg/trait/test_support.go
new file mode 100644
index 000000000..31d77adc6
--- /dev/null
+++ b/pkg/trait/test_support.go
@@ -0,0 +1,117 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package trait
+
+import (
+	serving "knative.dev/serving/pkg/apis/serving/v1"
+
+	appsv1 "k8s.io/api/apps/v1"
+	batchv1 "k8s.io/api/batch/v1"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
+)
+
+func createNominalDeploymentTraitTest() (*Environment, *appsv1.Deployment) {
+	deployment := &appsv1.Deployment{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "integration-name",
+		},
+		Spec: appsv1.DeploymentSpec{
+			Template: corev1.PodTemplateSpec{},
+		},
+	}
+
+	environment := &Environment{
+		Integration: &v1.Integration{
+			ObjectMeta: metav1.ObjectMeta{
+				Name: "integration-name",
+			},
+			Status: v1.IntegrationStatus{
+				Phase: v1.IntegrationPhaseDeploying,
+			},
+		},
+		Resources: kubernetes.NewCollection(deployment),
+	}
+
+	return environment, deployment
+}
+
+func createNominalMissingDeploymentTraitTest() *Environment {
+	environment := &Environment{
+		Integration: &v1.Integration{
+			ObjectMeta: metav1.ObjectMeta{
+				Name: "integration-name",
+			},
+			Status: v1.IntegrationStatus{
+				Phase: v1.IntegrationPhaseDeploying,
+			},
+		},
+		Resources: kubernetes.NewCollection(),
+	}
+
+	return environment
+}
+
+func createNominalKnativeServiceTraitTest() (*Environment, *serving.Service) {
+	knativeService := &serving.Service{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "integration-name",
+		},
+		Spec: serving.ServiceSpec{},
+	}
+
+	environment := &Environment{
+		Integration: &v1.Integration{
+			ObjectMeta: metav1.ObjectMeta{
+				Name: "integration-name",
+			},
+			Status: v1.IntegrationStatus{
+				Phase: v1.IntegrationPhaseDeploying,
+			},
+		},
+		Resources: kubernetes.NewCollection(knativeService),
+	}
+
+	return environment, knativeService
+}
+
+func createNominalCronJobTraitTest() (*Environment, *batchv1.CronJob) {
+	cronJob := &batchv1.CronJob{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "integration-name",
+		},
+		Spec: batchv1.CronJobSpec{},
+	}
+
+	environment := &Environment{
+		Integration: &v1.Integration{
+			ObjectMeta: metav1.ObjectMeta{
+				Name: "integration-name",
+			},
+			Status: v1.IntegrationStatus{
+				Phase: v1.IntegrationPhaseDeploying,
+			},
+		},
+		Resources: kubernetes.NewCollection(cronJob),
+	}
+
+	return environment, cronJob
+}
diff --git a/pkg/trait/trait_types_test.go b/pkg/trait/trait_types_test.go
index 2a68853ae..9d3509de9 100644
--- a/pkg/trait/trait_types_test.go
+++ b/pkg/trait/trait_types_test.go
@@ -21,15 +21,8 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
-	serving "knative.dev/serving/pkg/apis/serving/v1"
-
-	appsv1 "k8s.io/api/apps/v1"
-	"k8s.io/api/batch/v1beta1"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
 	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
-	"github.com/apache/camel-k/pkg/util/kubernetes"
 )
 
 func TestMultilinePropertiesHandled(t *testing.T) {
@@ -45,89 +38,79 @@ func TestMultilinePropertiesHandled(t *testing.T) {
 	assert.Equal(t, "prop = multi\\nline\n", cm.Data["application.properties"])
 }
 
-func createNominalDeploymentTraitTest() (*Environment, *appsv1.Deployment) {
-	deployment := &appsv1.Deployment{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: "integration-name",
-		},
-		Spec: appsv1.DeploymentSpec{
-			Template: corev1.PodTemplateSpec{},
-		},
-	}
-
-	environment := &Environment{
+func TestCollectConfigurationValues(t *testing.T) {
+	e := Environment{
 		Integration: &v1.Integration{
-			ObjectMeta: metav1.ObjectMeta{
-				Name: "integration-name",
-			},
-			Status: v1.IntegrationStatus{
-				Phase: v1.IntegrationPhaseDeploying,
+			Spec: v1.IntegrationSpec{
+				Configuration: []v1.ConfigurationSpec{
+					{Type: "configmap", Value: "my-cm-integration"},
+					{Type: "env", Value: "my-env-integration"},
+				},
 			},
 		},
-		Resources: kubernetes.NewCollection(deployment),
-	}
-
-	return environment, deployment
-}
-
-func createNominalMissingDeploymentTraitTest() *Environment {
-	environment := &Environment{
-		Integration: &v1.Integration{
-			ObjectMeta: metav1.ObjectMeta{
-				Name: "integration-name",
+		IntegrationKit: &v1.IntegrationKit{
+			Spec: v1.IntegrationKitSpec{
+				Configuration: []v1.ConfigurationSpec{
+					{Type: "configmap", Value: "my-cm-kit"},
+					{Type: "property", Value: "my-p-kit"},
+				},
 			},
-			Status: v1.IntegrationStatus{
-				Phase: v1.IntegrationPhaseDeploying,
+		},
+		Platform: &v1.IntegrationPlatform{
+			Spec: v1.IntegrationPlatformSpec{
+				Configuration: []v1.ConfigurationSpec{
+					{Type: "configmap", Value: "my-cm-platform"},
+					{Type: "secret", Value: "my-secret-platform"},
+					{Type: "property", Value: "my-p-platform"},
+					{Type: "env", Value: "my-env-platform"},
+				},
 			},
 		},
-		Resources: kubernetes.NewCollection(),
 	}
+	e.Platform.ResyncStatusFullConfig()
 
-	return environment
+	assert.Contains(t, e.collectConfigurationValues("configmap"), "my-cm-integration")
+	assert.Contains(t, e.collectConfigurationValues("secret"), "my-secret-platform")
+	assert.Contains(t, e.collectConfigurationValues("property"), "my-p-kit")
+	assert.Contains(t, e.collectConfigurationValues("env"), "my-env-integration")
 }
 
-func createNominalKnativeServiceTraitTest() (*Environment, *serving.Service) {
-	knativeService := &serving.Service{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: "integration-name",
-		},
-		Spec: serving.ServiceSpec{},
-	}
-
-	environment := &Environment{
+func TestCollectConfigurationPairs(t *testing.T) {
+	e := Environment{
 		Integration: &v1.Integration{
-			ObjectMeta: metav1.ObjectMeta{
-				Name: "integration-name",
-			},
-			Status: v1.IntegrationStatus{
-				Phase: v1.IntegrationPhaseDeploying,
+			Spec: v1.IntegrationSpec{
+				Configuration: []v1.ConfigurationSpec{
+					{Type: "property", Value: "p1=integration"},
+					{Type: "property", Value: "p4=integration"},
+				},
 			},
 		},
-		Resources: kubernetes.NewCollection(knativeService),
-	}
-
-	return environment, knativeService
-}
-
-func createNominalCronJobTraitTest() (*Environment, *v1beta1.CronJob) {
-	cronJob := &v1beta1.CronJob{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: "integration-name",
-		},
-		Spec: v1beta1.CronJobSpec{},
-	}
-
-	environment := &Environment{
-		Integration: &v1.Integration{
-			ObjectMeta: metav1.ObjectMeta{
-				Name: "integration-name",
+		IntegrationKit: &v1.IntegrationKit{
+			Spec: v1.IntegrationKitSpec{
+				Configuration: []v1.ConfigurationSpec{
+					{Type: "property", Value: "p1=kit"},
+					{Type: "property", Value: "p2=kit"},
+				},
 			},
-			Status: v1.IntegrationStatus{
-				Phase: v1.IntegrationPhaseDeploying,
+		},
+		Platform: &v1.IntegrationPlatform{
+			Spec: v1.IntegrationPlatformSpec{
+				Configuration: []v1.ConfigurationSpec{
+					{Type: "property", Value: "p1=platform"},
+					{Type: "property", Value: "p2=platform"},
+					{Type: "property", Value: "p3=platform"},
+					{Type: "property", Value: "p4=platform"},
+				},
 			},
 		},
-		Resources: kubernetes.NewCollection(cronJob),
 	}
-
-	return environment, cronJob
+	e.Platform.ResyncStatusFullConfig()
+
+	pairs := e.collectConfigurationPairs("property")
+	assert.Equal(t, pairs, []variable{
+		{Name: "p1", Value: "integration"},
+		{Name: "p2", Value: "kit"},
+		{Name: "p3", Value: "platform"},
+		{Name: "p4", Value: "integration"},
+	})
 }
diff --git a/pkg/trait/util_test.go b/pkg/trait/util_test.go
index d1488ad41..da4cd9589 100644
--- a/pkg/trait/util_test.go
+++ b/pkg/trait/util_test.go
@@ -21,87 +21,8 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
-
-	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
 )
 
-func TestCollectConfigurationValues(t *testing.T) {
-	e := Environment{
-		Integration: &v1.Integration{
-			Spec: v1.IntegrationSpec{
-				Configuration: []v1.ConfigurationSpec{
-					{Type: "configmap", Value: "my-cm-integration"},
-					{Type: "env", Value: "my-env-integration"},
-				},
-			},
-		},
-		IntegrationKit: &v1.IntegrationKit{
-			Spec: v1.IntegrationKitSpec{
-				Configuration: []v1.ConfigurationSpec{
-					{Type: "configmap", Value: "my-cm-kit"},
-					{Type: "property", Value: "my-p-kit"},
-				},
-			},
-		},
-		Platform: &v1.IntegrationPlatform{
-			Spec: v1.IntegrationPlatformSpec{
-				Configuration: []v1.ConfigurationSpec{
-					{Type: "configmap", Value: "my-cm-platform"},
-					{Type: "secret", Value: "my-secret-platform"},
-					{Type: "property", Value: "my-p-platform"},
-					{Type: "env", Value: "my-env-platform"},
-				},
-			},
-		},
-	}
-	e.Platform.ResyncStatusFullConfig()
-
-	assert.Contains(t, e.collectConfigurationValues("configmap"), "my-cm-integration")
-	assert.Contains(t, e.collectConfigurationValues("secret"), "my-secret-platform")
-	assert.Contains(t, e.collectConfigurationValues("property"), "my-p-kit")
-	assert.Contains(t, e.collectConfigurationValues("env"), "my-env-integration")
-}
-
-func TestCollectConfigurationPairs(t *testing.T) {
-	e := Environment{
-		Integration: &v1.Integration{
-			Spec: v1.IntegrationSpec{
-				Configuration: []v1.ConfigurationSpec{
-					{Type: "property", Value: "p1=integration"},
-					{Type: "property", Value: "p4=integration"},
-				},
-			},
-		},
-		IntegrationKit: &v1.IntegrationKit{
-			Spec: v1.IntegrationKitSpec{
-				Configuration: []v1.ConfigurationSpec{
-					{Type: "property", Value: "p1=kit"},
-					{Type: "property", Value: "p2=kit"},
-				},
-			},
-		},
-		Platform: &v1.IntegrationPlatform{
-			Spec: v1.IntegrationPlatformSpec{
-				Configuration: []v1.ConfigurationSpec{
-					{Type: "property", Value: "p1=platform"},
-					{Type: "property", Value: "p2=platform"},
-					{Type: "property", Value: "p3=platform"},
-					{Type: "property", Value: "p4=platform"},
-				},
-			},
-		},
-	}
-	e.Platform.ResyncStatusFullConfig()
-
-	pairs := e.collectConfigurationPairs("property")
-	assert.Equal(t, pairs, []variable{
-		{Name: "p1", Value: "integration"},
-		{Name: "p2", Value: "kit"},
-		{Name: "p3", Value: "platform"},
-		{Name: "p4", Value: "integration"},
-	})
-}
-
 func TestBoolPointerFunctions(t *testing.T) {
 	trueP := BoolP(true)
 	falseP := BoolP(false)