You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by lb...@apache.org on 2018/10/08 14:19:42 UTC

[camel-k] 02/14: Moving logic to the trait area

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

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

commit 3bbfaad7fbb88453bcd2e5bae7af18ebd2419c7e
Author: nferraro <ni...@gmail.com>
AuthorDate: Thu Oct 4 12:12:56 2018 +0200

    Moving logic to the trait area
---
 pkg/stub/action/integration/deploy.go              | 281 ++-------------------
 pkg/stub/action/integration/util.go                |  83 ------
 .../action/integration/deploy.go => trait/base.go} | 158 +++---------
 pkg/trait/{catalog => }/catalog.go                 |  64 +++--
 pkg/trait/{catalog => }/expose.go                  |   9 +-
 pkg/trait/{catalog => }/identity.go                |   9 +-
 pkg/trait/owner.go                                 |  48 ++++
 pkg/trait/trait.go                                 |  35 ++-
 pkg/{stub/action/integration => trait}/util.go     |  50 +---
 pkg/util/kubernetes/collection.go                  |  19 +-
 10 files changed, 213 insertions(+), 543 deletions(-)

diff --git a/pkg/stub/action/integration/deploy.go b/pkg/stub/action/integration/deploy.go
index 8067b3c..c9c7f4e 100644
--- a/pkg/stub/action/integration/deploy.go
+++ b/pkg/stub/action/integration/deploy.go
@@ -18,17 +18,14 @@ limitations under the License.
 package integration
 
 import (
-	"fmt"
-	"github.com/sirupsen/logrus"
-	"strings"
-
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"github.com/apache/camel-k/pkg/trait"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
 	"github.com/operator-framework/operator-sdk/pkg/sdk"
 	"github.com/pkg/errors"
-	appsv1 "k8s.io/api/apps/v1"
-	corev1 "k8s.io/api/core/v1"
+	"github.com/sirupsen/logrus"
 	k8serrors "k8s.io/apimachinery/pkg/api/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
 )
 
 // NewDeployAction create an action that handles integration deploy
@@ -48,15 +45,18 @@ func (action *deployAction) CanHandle(integration *v1alpha1.Integration) bool {
 }
 
 func (action *deployAction) Handle(integration *v1alpha1.Integration) error {
-	ctx, err := LookupContextForIntegration(integration)
+	environment, err := trait.NewEnvironment(integration)
 	if err != nil {
 		return err
 	}
-	err = createOrUpdateConfigMap(ctx, integration)
-	if err != nil {
-		return err
+	resources := kubernetes.NewCollection()
+	customizers := trait.CustomizersFor(*environment)
+	// invoke the trait framework to determine the needed resources
+	if _, err = customizers.Customize(*environment, resources); err != nil {
+		return errors.Wrap(err, "error during trait customization")
 	}
-	err = createOrUpdateDeployment(ctx, integration)
+	// TODO we should look for objects that are no longer present in the collection and remove them
+	err = action.createOrUpdateObjects(resources.Items(), integration)
 	if err != nil {
 		return err
 	}
@@ -68,254 +68,15 @@ func (action *deployAction) Handle(integration *v1alpha1.Integration) error {
 	return sdk.Update(target)
 }
 
-// **********************************
-//
-// ConfigMap
-//
-// **********************************
-
-func getConfigMapFor(ctx *v1alpha1.IntegrationContext, integration *v1alpha1.Integration) (*corev1.ConfigMap, error) {
-	controller := true
-	blockOwnerDeletion := true
-
-	// combine properties of integration with context, integration
-	// properties have the priority
-	properties := CombineConfigurationAsMap("property", ctx, integration)
-
-	cm := corev1.ConfigMap{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "ConfigMap",
-			APIVersion: "v1",
-		},
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      integration.Name,
-			Namespace: integration.Namespace,
-			Labels:    integration.Labels,
-			Annotations: map[string]string{
-				"camel.apache.org/source.language": string(integration.Spec.Source.Language),
-				"camel.apache.org/source.name":     integration.Spec.Source.Name,
-			},
-			OwnerReferences: []metav1.OwnerReference{
-				{
-					APIVersion:         integration.APIVersion,
-					Kind:               integration.Kind,
-					Name:               integration.Name,
-					UID:                integration.UID,
-					Controller:         &controller,
-					BlockOwnerDeletion: &blockOwnerDeletion,
-				},
-			},
-		},
-		Data: map[string]string{
-			"integration": integration.Spec.Source.Content,
-			"properties":  PropertiesString(properties),
-		},
-	}
-
-	return &cm, nil
-}
-
-func createOrUpdateConfigMap(ctx *v1alpha1.IntegrationContext, integration *v1alpha1.Integration) error {
-	cm, err := getConfigMapFor(ctx, integration)
-	if err != nil {
-		return err
-	}
-
-	err = sdk.Create(cm)
-	if err != nil && k8serrors.IsAlreadyExists(err) {
-		err = sdk.Update(cm)
-	}
-	if err != nil {
-		return errors.Wrap(err, "could not create or replace configmap for integration "+integration.Name)
-	}
-
-	return err
-}
-
-// **********************************
-//
-// Deployment
-//
-// **********************************
-
-func getDeploymentFor(ctx *v1alpha1.IntegrationContext, integration *v1alpha1.Integration) (*appsv1.Deployment, error) {
-	controller := true
-	blockOwnerDeletion := true
-	sourceName := strings.TrimPrefix(integration.Spec.Source.Name, "/")
-
-	// combine environment of integration with context, integration
-	// environment has the priority
-	environment := CombineConfigurationAsMap("env", ctx, integration)
-
-	// set env vars needed by the runtime
-	environment["JAVA_MAIN_CLASS"] = "org.apache.camel.k.jvm.Application"
-
-	// camel-k runtime
-	environment["CAMEL_K_ROUTES_URI"] = "file:/etc/camel/conf/" + sourceName
-	environment["CAMEL_K_ROUTES_LANGUAGE"] = string(integration.Spec.Source.Language)
-	environment["CAMEL_K_CONF"] = "/etc/camel/conf/application.properties"
-	environment["CAMEL_K_CONF_D"] = "/etc/camel/conf.d"
-
-	// add a dummy env var to trigger deployment if everything but the code
-	// has been changed
-	environment["CAMEL_K_DIGEST"] = integration.Status.Digest
-
-	// optimizations
-	environment["AB_JOLOKIA_OFF"] = "true"
-
-	labels := map[string]string{
-		"camel.apache.org/integration": integration.Name,
-	}
-	deployment := appsv1.Deployment{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "Deployment",
-			APIVersion: appsv1.SchemeGroupVersion.String(),
-		},
-		ObjectMeta: metav1.ObjectMeta{
-			Name:        integration.Name,
-			Namespace:   integration.Namespace,
-			Labels:      integration.Labels,
-			Annotations: integration.Annotations,
-			OwnerReferences: []metav1.OwnerReference{
-				{
-					APIVersion:         integration.APIVersion,
-					Kind:               integration.Kind,
-					Name:               integration.Name,
-					Controller:         &controller,
-					BlockOwnerDeletion: &blockOwnerDeletion,
-					UID:                integration.UID,
-				},
-			},
-		},
-		Spec: appsv1.DeploymentSpec{
-			Replicas: integration.Spec.Replicas,
-			Selector: &metav1.LabelSelector{
-				MatchLabels: labels,
-			},
-			Template: corev1.PodTemplateSpec{
-				ObjectMeta: metav1.ObjectMeta{
-					Labels: labels,
-				},
-				Spec: corev1.PodSpec{
-					Containers: []corev1.Container{
-						{
-							Name:  integration.Name,
-							Image: integration.Status.Image,
-							Env:   EnvironmentAsEnvVarSlice(environment),
-						},
-					},
-				},
-			},
-		},
-	}
-
-	//
-	// Volumes :: Setup
-	//
-
-	vols := make([]corev1.Volume, 0)
-	mnts := make([]corev1.VolumeMount, 0)
-	cnt := 0
-
-	//
-	// Volumes :: Defaults
-	//
-
-	vols = append(vols, corev1.Volume{
-		Name: "integration",
-		VolumeSource: corev1.VolumeSource{
-			ConfigMap: &corev1.ConfigMapVolumeSource{
-				LocalObjectReference: corev1.LocalObjectReference{
-					Name: integration.Name,
-				},
-				Items: []corev1.KeyToPath{
-					{
-						Key:  "integration",
-						Path: sourceName,
-					}, {
-						Key:  "properties",
-						Path: "application.properties",
-					},
-				},
-			},
-		},
-	})
-
-	mnts = append(mnts, corev1.VolumeMount{
-		Name:      "integration",
-		MountPath: "/etc/camel/conf",
-	})
-
-	//
-	// Volumes :: Additional ConfigMaps
-	//
-
-	cmList := CombineConfigurationAsSlice("configmap", ctx, integration)
-	for _, cmName := range cmList {
-		cnt++
-
-		vols = append(vols, corev1.Volume{
-			Name: "integration-cm-" + cmName,
-			VolumeSource: corev1.VolumeSource{
-				ConfigMap: &corev1.ConfigMapVolumeSource{
-					LocalObjectReference: corev1.LocalObjectReference{
-						Name: cmName,
-					},
-				},
-			},
-		})
-
-		mnts = append(mnts, corev1.VolumeMount{
-			Name:      "integration-cm-" + cmName,
-			MountPath: fmt.Sprintf("/etc/camel/conf.d/%03d_%s", cnt, cmName),
-		})
-	}
-
-	//
-	// Volumes :: Additional Secrets
-	//
-
-	secretList := CombineConfigurationAsSlice("secret", ctx, integration)
-	for _, secretName := range secretList {
-		cnt++
-
-		vols = append(vols, corev1.Volume{
-			Name: "integration-secret-" + secretName,
-			VolumeSource: corev1.VolumeSource{
-				Secret: &corev1.SecretVolumeSource{
-					SecretName: secretName,
-				},
-			},
-		})
-
-		mnts = append(mnts, corev1.VolumeMount{
-			Name:      "integration-secret-" + secretName,
-			MountPath: fmt.Sprintf("/etc/camel/conf.d/%03d_%s", cnt, secretName),
-		})
-	}
-
-	//
-	// Volumes
-	//
-
-	deployment.Spec.Template.Spec.Volumes = vols
-	deployment.Spec.Template.Spec.Containers[0].VolumeMounts = mnts
-
-	return &deployment, nil
-}
-
-func createOrUpdateDeployment(ctx *v1alpha1.IntegrationContext, integration *v1alpha1.Integration) error {
-	deployment, err := getDeploymentFor(ctx, integration)
-	if err != nil {
-		return err
-	}
-
-	err = sdk.Create(deployment)
-	if err != nil && k8serrors.IsAlreadyExists(err) {
-		err = sdk.Update(deployment)
-	}
-	if err != nil {
-		return errors.Wrap(err, "could not create or replace deployment for integration "+integration.Name)
+func (action *deployAction) createOrUpdateObjects(objects []runtime.Object, integration *v1alpha1.Integration) error {
+	for _, object := range objects {
+		err := sdk.Create(object)
+		if err != nil && k8serrors.IsAlreadyExists(err) {
+			err = sdk.Update(object)
+		}
+		if err != nil {
+			return errors.Wrap(err, "could not create or replace resource for integration "+integration.Name)
+		}
 	}
 
 	return nil
diff --git a/pkg/stub/action/integration/util.go b/pkg/stub/action/integration/util.go
index ab9cf79..27bfb29 100644
--- a/pkg/stub/action/integration/util.go
+++ b/pkg/stub/action/integration/util.go
@@ -18,15 +18,10 @@ limitations under the License.
 package integration
 
 import (
-	"fmt"
-	"strings"
-
 	"github.com/apache/camel-k/pkg/util"
 
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/operator-framework/operator-sdk/pkg/sdk"
-	"k8s.io/api/core/v1"
-
 	"github.com/pkg/errors"
 )
 
@@ -65,81 +60,3 @@ func LookupContextForIntegration(integration *v1alpha1.Integration) (*v1alpha1.I
 
 	return nil, nil
 }
-
-// PropertiesString --
-func PropertiesString(m map[string]string) string {
-	properties := ""
-	for k, v := range m {
-		properties += fmt.Sprintf("%s=%s\n", k, v)
-	}
-
-	return properties
-}
-
-// EnvironmentAsEnvVarSlice --
-func EnvironmentAsEnvVarSlice(m map[string]string) []v1.EnvVar {
-	env := make([]v1.EnvVar, 0, len(m))
-
-	for k, v := range m {
-		env = append(env, v1.EnvVar{Name: k, Value: v})
-	}
-
-	return env
-}
-
-// CombineConfigurationAsMap --
-func CombineConfigurationAsMap(configurationType string, context *v1alpha1.IntegrationContext, integration *v1alpha1.Integration) map[string]string {
-	result := make(map[string]string)
-	if context != nil {
-		// Add context properties first so integrations can
-		// override it
-		for _, c := range context.Spec.Configuration {
-			if c.Type == configurationType {
-				pair := strings.Split(c.Value, "=")
-				if len(pair) == 2 {
-					result[pair[0]] = pair[1]
-				}
-			}
-		}
-	}
-
-	if integration != nil {
-		for _, c := range integration.Spec.Configuration {
-			if c.Type == configurationType {
-				pair := strings.Split(c.Value, "=")
-				if len(pair) == 2 {
-					result[pair[0]] = pair[1]
-				}
-			}
-		}
-	}
-
-	return result
-}
-
-// CombineConfigurationAsSlice --
-func CombineConfigurationAsSlice(configurationType string, context *v1alpha1.IntegrationContext, integration *v1alpha1.Integration) []string {
-	result := make(map[string]bool, 0)
-	if context != nil {
-		// Add context properties first so integrations can
-		// override it
-		for _, c := range context.Spec.Configuration {
-			if c.Type == configurationType {
-				result[c.Value] = true
-			}
-		}
-	}
-
-	for _, c := range integration.Spec.Configuration {
-		if c.Type == configurationType {
-			result[c.Value] = true
-		}
-	}
-
-	keys := make([]string, 0, len(result))
-	for k := range result {
-		keys = append(keys, k)
-	}
-
-	return keys
-}
diff --git a/pkg/stub/action/integration/deploy.go b/pkg/trait/base.go
similarity index 51%
copy from pkg/stub/action/integration/deploy.go
copy to pkg/trait/base.go
index 8067b3c..69a46b9 100644
--- a/pkg/stub/action/integration/deploy.go
+++ b/pkg/trait/base.go
@@ -15,57 +15,38 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package integration
+package trait
 
 import (
 	"fmt"
-	"github.com/sirupsen/logrus"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
 	"strings"
 
-	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
-	"github.com/operator-framework/operator-sdk/pkg/sdk"
-	"github.com/pkg/errors"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
-	k8serrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
-// NewDeployAction create an action that handles integration deploy
-func NewDeployAction() Action {
-	return &deployAction{}
+type baseTrait struct {
 }
 
-type deployAction struct {
+func (*baseTrait) ID() ID {
+	return ID("base")
 }
 
-func (action *deployAction) Name() string {
-	return "deploy"
-}
-
-func (action *deployAction) CanHandle(integration *v1alpha1.Integration) bool {
-	return integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying
-}
-
-func (action *deployAction) Handle(integration *v1alpha1.Integration) error {
-	ctx, err := LookupContextForIntegration(integration)
-	if err != nil {
-		return err
+func (d *baseTrait) Customize(environment Environment, resources *kubernetes.Collection) (bool, error) {
+	var cm *corev1.ConfigMap
+	var err error
+	if cm, err = d.getConfigMapFor(environment); err != nil {
+		return false, err
 	}
-	err = createOrUpdateConfigMap(ctx, integration)
-	if err != nil {
-		return err
+	resources.Add(cm)
+	var depl *appsv1.Deployment
+	if depl, err = d.getDeploymentFor(environment); err != nil {
+		return false, err
 	}
-	err = createOrUpdateDeployment(ctx, integration)
-	if err != nil {
-		return err
-	}
-
-	target := integration.DeepCopy()
-	logrus.Info("Integration ", target.Name, " transitioning to state ", v1alpha1.IntegrationPhaseRunning)
-	target.Status.Phase = v1alpha1.IntegrationPhaseRunning
-
-	return sdk.Update(target)
+	resources.Add(depl)
+	return true, nil
 }
 
 // **********************************
@@ -74,13 +55,10 @@ func (action *deployAction) Handle(integration *v1alpha1.Integration) error {
 //
 // **********************************
 
-func getConfigMapFor(ctx *v1alpha1.IntegrationContext, integration *v1alpha1.Integration) (*corev1.ConfigMap, error) {
-	controller := true
-	blockOwnerDeletion := true
-
+func (*baseTrait) getConfigMapFor(e Environment) (*corev1.ConfigMap, error) {
 	// combine properties of integration with context, integration
 	// properties have the priority
-	properties := CombineConfigurationAsMap("property", ctx, integration)
+	properties := CombineConfigurationAsMap("property", e.Context, e.Integration)
 
 	cm := corev1.ConfigMap{
 		TypeMeta: metav1.TypeMeta{
@@ -88,26 +66,16 @@ func getConfigMapFor(ctx *v1alpha1.IntegrationContext, integration *v1alpha1.Int
 			APIVersion: "v1",
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      integration.Name,
-			Namespace: integration.Namespace,
-			Labels:    integration.Labels,
+			Name:      e.Integration.Name,
+			Namespace: e.Integration.Namespace,
+			Labels:    e.Integration.Labels,
 			Annotations: map[string]string{
-				"camel.apache.org/source.language": string(integration.Spec.Source.Language),
-				"camel.apache.org/source.name":     integration.Spec.Source.Name,
-			},
-			OwnerReferences: []metav1.OwnerReference{
-				{
-					APIVersion:         integration.APIVersion,
-					Kind:               integration.Kind,
-					Name:               integration.Name,
-					UID:                integration.UID,
-					Controller:         &controller,
-					BlockOwnerDeletion: &blockOwnerDeletion,
-				},
+				"camel.apache.org/source.language": string(e.Integration.Spec.Source.Language),
+				"camel.apache.org/source.name":     e.Integration.Spec.Source.Name,
 			},
 		},
 		Data: map[string]string{
-			"integration": integration.Spec.Source.Content,
+			"integration": e.Integration.Spec.Source.Content,
 			"properties":  PropertiesString(properties),
 		},
 	}
@@ -115,56 +83,37 @@ func getConfigMapFor(ctx *v1alpha1.IntegrationContext, integration *v1alpha1.Int
 	return &cm, nil
 }
 
-func createOrUpdateConfigMap(ctx *v1alpha1.IntegrationContext, integration *v1alpha1.Integration) error {
-	cm, err := getConfigMapFor(ctx, integration)
-	if err != nil {
-		return err
-	}
-
-	err = sdk.Create(cm)
-	if err != nil && k8serrors.IsAlreadyExists(err) {
-		err = sdk.Update(cm)
-	}
-	if err != nil {
-		return errors.Wrap(err, "could not create or replace configmap for integration "+integration.Name)
-	}
-
-	return err
-}
-
 // **********************************
 //
 // Deployment
 //
 // **********************************
 
-func getDeploymentFor(ctx *v1alpha1.IntegrationContext, integration *v1alpha1.Integration) (*appsv1.Deployment, error) {
-	controller := true
-	blockOwnerDeletion := true
-	sourceName := strings.TrimPrefix(integration.Spec.Source.Name, "/")
+func (*baseTrait) getDeploymentFor(e Environment) (*appsv1.Deployment, error) {
+	sourceName := strings.TrimPrefix(e.Integration.Spec.Source.Name, "/")
 
 	// combine environment of integration with context, integration
 	// environment has the priority
-	environment := CombineConfigurationAsMap("env", ctx, integration)
+	environment := CombineConfigurationAsMap("env", e.Context, e.Integration)
 
 	// set env vars needed by the runtime
 	environment["JAVA_MAIN_CLASS"] = "org.apache.camel.k.jvm.Application"
 
 	// camel-k runtime
 	environment["CAMEL_K_ROUTES_URI"] = "file:/etc/camel/conf/" + sourceName
-	environment["CAMEL_K_ROUTES_LANGUAGE"] = string(integration.Spec.Source.Language)
+	environment["CAMEL_K_ROUTES_LANGUAGE"] = string(e.Integration.Spec.Source.Language)
 	environment["CAMEL_K_CONF"] = "/etc/camel/conf/application.properties"
 	environment["CAMEL_K_CONF_D"] = "/etc/camel/conf.d"
 
 	// add a dummy env var to trigger deployment if everything but the code
 	// has been changed
-	environment["CAMEL_K_DIGEST"] = integration.Status.Digest
+	environment["CAMEL_K_DIGEST"] = e.Integration.Status.Digest
 
 	// optimizations
 	environment["AB_JOLOKIA_OFF"] = "true"
 
 	labels := map[string]string{
-		"camel.apache.org/integration": integration.Name,
+		"camel.apache.org/integration": e.Integration.Name,
 	}
 	deployment := appsv1.Deployment{
 		TypeMeta: metav1.TypeMeta{
@@ -172,23 +121,13 @@ func getDeploymentFor(ctx *v1alpha1.IntegrationContext, integration *v1alpha1.In
 			APIVersion: appsv1.SchemeGroupVersion.String(),
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Name:        integration.Name,
-			Namespace:   integration.Namespace,
-			Labels:      integration.Labels,
-			Annotations: integration.Annotations,
-			OwnerReferences: []metav1.OwnerReference{
-				{
-					APIVersion:         integration.APIVersion,
-					Kind:               integration.Kind,
-					Name:               integration.Name,
-					Controller:         &controller,
-					BlockOwnerDeletion: &blockOwnerDeletion,
-					UID:                integration.UID,
-				},
-			},
+			Name:        e.Integration.Name,
+			Namespace:   e.Integration.Namespace,
+			Labels:      e.Integration.Labels,
+			Annotations: e.Integration.Annotations,
 		},
 		Spec: appsv1.DeploymentSpec{
-			Replicas: integration.Spec.Replicas,
+			Replicas: e.Integration.Spec.Replicas,
 			Selector: &metav1.LabelSelector{
 				MatchLabels: labels,
 			},
@@ -199,8 +138,8 @@ func getDeploymentFor(ctx *v1alpha1.IntegrationContext, integration *v1alpha1.In
 				Spec: corev1.PodSpec{
 					Containers: []corev1.Container{
 						{
-							Name:  integration.Name,
-							Image: integration.Status.Image,
+							Name:  e.Integration.Name,
+							Image: e.Integration.Status.Image,
 							Env:   EnvironmentAsEnvVarSlice(environment),
 						},
 					},
@@ -226,7 +165,7 @@ func getDeploymentFor(ctx *v1alpha1.IntegrationContext, integration *v1alpha1.In
 		VolumeSource: corev1.VolumeSource{
 			ConfigMap: &corev1.ConfigMapVolumeSource{
 				LocalObjectReference: corev1.LocalObjectReference{
-					Name: integration.Name,
+					Name: e.Integration.Name,
 				},
 				Items: []corev1.KeyToPath{
 					{
@@ -250,7 +189,7 @@ func getDeploymentFor(ctx *v1alpha1.IntegrationContext, integration *v1alpha1.In
 	// Volumes :: Additional ConfigMaps
 	//
 
-	cmList := CombineConfigurationAsSlice("configmap", ctx, integration)
+	cmList := CombineConfigurationAsSlice("configmap", e.Context, e.Integration)
 	for _, cmName := range cmList {
 		cnt++
 
@@ -275,7 +214,7 @@ func getDeploymentFor(ctx *v1alpha1.IntegrationContext, integration *v1alpha1.In
 	// Volumes :: Additional Secrets
 	//
 
-	secretList := CombineConfigurationAsSlice("secret", ctx, integration)
+	secretList := CombineConfigurationAsSlice("secret", e.Context, e.Integration)
 	for _, secretName := range secretList {
 		cnt++
 
@@ -303,20 +242,3 @@ func getDeploymentFor(ctx *v1alpha1.IntegrationContext, integration *v1alpha1.In
 
 	return &deployment, nil
 }
-
-func createOrUpdateDeployment(ctx *v1alpha1.IntegrationContext, integration *v1alpha1.Integration) error {
-	deployment, err := getDeploymentFor(ctx, integration)
-	if err != nil {
-		return err
-	}
-
-	err = sdk.Create(deployment)
-	if err != nil && k8serrors.IsAlreadyExists(err) {
-		err = sdk.Update(deployment)
-	}
-	if err != nil {
-		return errors.Wrap(err, "could not create or replace deployment for integration "+integration.Name)
-	}
-
-	return nil
-}
diff --git a/pkg/trait/catalog/catalog.go b/pkg/trait/catalog.go
similarity index 58%
rename from pkg/trait/catalog/catalog.go
rename to pkg/trait/catalog.go
index 924ed37..3325860 100644
--- a/pkg/trait/catalog/catalog.go
+++ b/pkg/trait/catalog.go
@@ -15,39 +15,48 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package catalog
+package trait
 
 import (
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
-	"github.com/apache/camel-k/pkg/trait"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 )
 
-// TraitID uniquely identifies a trait
-type TraitID string
-
-const (
-	// Expose exposes a integration to the external world
-	Expose TraitID = "expose"
+var (
+	tExpose = &exposeTrait{}
+	tBase = &baseTrait{}
+	tOwner = &ownerTrait{}
 )
 
-// A Catalog is just a DeploymentCustomizer that applies multiple traits
-type Catalog trait.DeploymentCustomizer
-
-// For returns a Catalog for the given integration details
-func For(environment trait.Environment) Catalog {
-
+// CustomizersFor returns a Catalog for the given integration details
+func CustomizersFor(environment Environment) Customizer {
+	switch environment.Platform.Spec.Cluster {
+	case v1alpha1.IntegrationPlatformClusterOpenShift:
+		return compose(
+			tBase,
+			tExpose,
+			tOwner,
+		)
+	case v1alpha1.IntegrationPlatformClusterKubernetes:
+		return compose(
+			tBase,
+			tExpose,
+			tOwner,
+		)
+		// case Knative: ...
+	}
+	return nil
 }
 
-func compose(traits ...trait.DeploymentCustomizer) trait.DeploymentCustomizer {
+func compose(traits ...Customizer) Customizer {
 	if len(traits) == 0 {
 		return &identityTrait{}
 	} else if len(traits) == 1 {
 		return traits[0]
 	}
-	var composite trait.DeploymentCustomizer = &identityTrait{}
+	var composite Customizer = &identityTrait{}
 	for _, t := range traits {
-		composite = &catalogCustomizer{
+		composite = &chainedCustomizer{
 			t1: composite,
 			t2: t,
 		}
@@ -57,29 +66,28 @@ func compose(traits ...trait.DeploymentCustomizer) trait.DeploymentCustomizer {
 
 // -------------------------------------------
 
-type catalogCustomizer struct {
-	t1 trait.DeploymentCustomizer
-	t2 trait.DeploymentCustomizer
+type chainedCustomizer struct {
+	t1 Customizer
+	t2 Customizer
 }
 
-func (c *catalogCustomizer) Name() string {
-	return ""
+func (c *chainedCustomizer) ID() ID {
+	return ID("")
 }
 
-func (c *catalogCustomizer) Customize(environment trait.Environment, resources *kubernetes.Collection) (bool, error) {
+func (c *chainedCustomizer) Customize(environment Environment, resources *kubernetes.Collection) (bool, error) {
 	atLeastOnce := false
 	var done bool
 	var err error
 	if done, err = c.t1.Customize(environment, resources); err != nil {
 		return false, err
-	} else if done && c.t1.Name() != "" {
-		environment.ExecutedCustomizers = append(environment.ExecutedCustomizers, c.t1.Name())
+	} else if done && c.t1.ID() != "" {
+		environment.ExecutedCustomizers = append(environment.ExecutedCustomizers, c.t1.ID())
 	}
 	atLeastOnce = atLeastOnce || done
 	done2, err := c.t2.Customize(environment, resources)
-	if done2 && c.t2.Name() != "" {
-		environment.ExecutedCustomizers = append(environment.ExecutedCustomizers, c.t2.Name())
+	if done2 && c.t2.ID() != "" {
+		environment.ExecutedCustomizers = append(environment.ExecutedCustomizers, c.t2.ID())
 	}
-	environment.ExecutedCustomizers = append(environment.ExecutedCustomizers, c.t1.Name())
 	return atLeastOnce || done2, err
 }
diff --git a/pkg/trait/catalog/expose.go b/pkg/trait/expose.go
similarity index 80%
rename from pkg/trait/catalog/expose.go
rename to pkg/trait/expose.go
index 91890eb..2dac116 100644
--- a/pkg/trait/catalog/expose.go
+++ b/pkg/trait/expose.go
@@ -15,20 +15,19 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package catalog
+package trait
 
 import (
-	"github.com/apache/camel-k/pkg/trait"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 )
 
 type exposeTrait struct {
 }
 
-func (*exposeTrait) Name() string {
-	return "expose"
+func (*exposeTrait) ID() ID {
+	return ID("expose")
 }
 
-func (*exposeTrait) Customize(environment trait.Environment, resources *kubernetes.Collection) (bool, error) {
+func (*exposeTrait) Customize(environment Environment, resources *kubernetes.Collection) (bool, error) {
 	return false, nil
 }
diff --git a/pkg/trait/catalog/identity.go b/pkg/trait/identity.go
similarity index 79%
rename from pkg/trait/catalog/identity.go
rename to pkg/trait/identity.go
index cddd0f4..eda9d3a 100644
--- a/pkg/trait/catalog/identity.go
+++ b/pkg/trait/identity.go
@@ -15,20 +15,19 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package catalog
+package trait
 
 import (
-	"github.com/apache/camel-k/pkg/trait"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 )
 
 type identityTrait struct {
 }
 
-func (*identityTrait) Name() string {
-	return "identity"
+func (*identityTrait) ID() ID {
+	return ID("identity")
 }
 
-func (*identityTrait) Customize(environment trait.Environment, resources *kubernetes.Collection) (bool, error) {
+func (*identityTrait) Customize(environment Environment, resources *kubernetes.Collection) (bool, error) {
 	return false, nil
 }
diff --git a/pkg/trait/owner.go b/pkg/trait/owner.go
new file mode 100644
index 0000000..905800b
--- /dev/null
+++ b/pkg/trait/owner.go
@@ -0,0 +1,48 @@
+/*
+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 "github.com/apache/camel-k/pkg/util/kubernetes"
+import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+// ownerTrait ensures that all created resources belong to the integration being created
+type ownerTrait struct {
+}
+
+func (*ownerTrait) ID() ID {
+	return ID("identity")
+}
+
+func (*ownerTrait) Customize(e Environment, resources *kubernetes.Collection) (bool, error) {
+	controller := true
+	blockOwnerDeletion := true
+	resources.VisitMetaObject(func(res metav1.Object) {
+		references := []metav1.OwnerReference{
+			{
+				APIVersion:         e.Integration.APIVersion,
+				Kind:               e.Integration.Kind,
+				Name:               e.Integration.Name,
+				UID:                e.Integration.UID,
+				Controller:         &controller,
+				BlockOwnerDeletion: &blockOwnerDeletion,
+			},
+		}
+		res.SetOwnerReferences(references)
+	})
+	return true, nil
+}
diff --git a/pkg/trait/trait.go b/pkg/trait/trait.go
index e0bb7ef..90e4d29 100644
--- a/pkg/trait/trait.go
+++ b/pkg/trait/trait.go
@@ -19,6 +19,8 @@ package trait
 
 import (
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"github.com/apache/camel-k/pkg/discover"
+	"github.com/apache/camel-k/pkg/platform"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 )
 
@@ -27,13 +29,38 @@ type Environment struct {
 	Platform            *v1alpha1.IntegrationPlatform
 	Context             *v1alpha1.IntegrationContext
 	Integration         *v1alpha1.Integration
-	ExecutedCustomizers []string
+	Dependencies        []string
+	ExecutedCustomizers []ID
 }
 
-// A DeploymentCustomizer performs customization of the deployed objects
-type DeploymentCustomizer interface {
+// NewEnvironment creates a Environment from the given data
+func NewEnvironment(integration *v1alpha1.Integration) (*Environment, error) {
+	pl, err := platform.GetCurrentPlatform(integration.Namespace)
+	if err != nil {
+		return nil, err
+	}
+	ctx, err := GetIntegrationContext(integration)
+	if err != nil {
+		return nil, err
+	}
+	dependencies := discover.Dependencies(integration.Spec.Source)
+
+	return &Environment{
+		Platform:            pl,
+		Context:             ctx,
+		Integration:         integration,
+		Dependencies:        dependencies,
+		ExecutedCustomizers: make([]ID, 0),
+	}, nil
+}
+
+// ID uniquely identifies a trait
+type ID string
+
+// A Customizer performs customization of the deployed objects
+type Customizer interface {
 	// The Name of the customizer
-	Name() string
+	ID() ID
 	// Customize executes the trait customization on the resources and return true if the resources have been changed
 	Customize(environment Environment, resources *kubernetes.Collection) (bool, error)
 }
diff --git a/pkg/stub/action/integration/util.go b/pkg/trait/util.go
similarity index 73%
copy from pkg/stub/action/integration/util.go
copy to pkg/trait/util.go
index ab9cf79..0e5da55 100644
--- a/pkg/stub/action/integration/util.go
+++ b/pkg/trait/util.go
@@ -15,55 +15,27 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package integration
+package trait
 
 import (
 	"fmt"
-	"strings"
-
-	"github.com/apache/camel-k/pkg/util"
-
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/operator-framework/operator-sdk/pkg/sdk"
-	"k8s.io/api/core/v1"
-
 	"github.com/pkg/errors"
+	"k8s.io/api/core/v1"
+	"strings"
 )
 
-// LookupContextForIntegration --
-func LookupContextForIntegration(integration *v1alpha1.Integration) (*v1alpha1.IntegrationContext, error) {
-	if integration.Spec.Context != "" {
-		name := integration.Spec.Context
-		ctx := v1alpha1.NewIntegrationContext(integration.Namespace, name)
-
-		if err := sdk.Get(&ctx); err != nil {
-			return nil, errors.Wrapf(err, "unable to find integration context %s, %s", ctx.Name, err)
-		}
-
-		return &ctx, nil
-	}
-
-	ctxList := v1alpha1.NewIntegrationContextList()
-	if err := sdk.List(integration.Namespace, &ctxList); err != nil {
-		return nil, err
-	}
-
-	for _, ctx := range ctxList.Items {
-		if ctx.Labels["camel.apache.org/context.type"] == "platform" {
-			ideps := len(integration.Spec.Dependencies)
-			cdeps := len(ctx.Spec.Dependencies)
-
-			if ideps != cdeps {
-				continue
-			}
-
-			if util.StringSliceContains(ctx.Spec.Dependencies, integration.Spec.Dependencies) {
-				return &ctx, nil
-			}
-		}
+// GetIntegrationContext retrieves the context set on the integration
+func GetIntegrationContext(integration *v1alpha1.Integration) (*v1alpha1.IntegrationContext, error) {
+	if integration.Spec.Context == "" {
+		return nil, errors.New("no context set on the integration")
 	}
 
-	return nil, nil
+	name := integration.Spec.Context
+	ctx := v1alpha1.NewIntegrationContext(integration.Namespace, name)
+	err := sdk.Get(&ctx)
+	return &ctx, err
 }
 
 // PropertiesString --
diff --git a/pkg/util/kubernetes/collection.go b/pkg/util/kubernetes/collection.go
index a3ff2b7..91cb7ee 100644
--- a/pkg/util/kubernetes/collection.go
+++ b/pkg/util/kubernetes/collection.go
@@ -20,6 +20,7 @@ package kubernetes
 import (
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 )
 
@@ -28,6 +29,13 @@ type Collection struct {
 	items []runtime.Object
 }
 
+// NewCollection creates a new empty collection
+func NewCollection() *Collection {
+	return &Collection{
+		items: make([]runtime.Object, 0),
+	}
+}
+
 // Items returns all resources belonging to the collection
 func (c *Collection) Items() []runtime.Object {
 	return c.items
@@ -48,7 +56,7 @@ func (c *Collection) VisitDeployment(visitor func(*appsv1.Deployment)) {
 }
 
 // VisitConfigMap executes the visitor function on all ConfigMap resources
-func (c *Collection) VisitConfigMap(visitor func(configMap *corev1.ConfigMap)) {
+func (c *Collection) VisitConfigMap(visitor func(*corev1.ConfigMap)) {
 	c.Visit(func(res runtime.Object) {
 		if conv, ok := res.(*corev1.ConfigMap); ok {
 			visitor(conv)
@@ -56,6 +64,15 @@ func (c *Collection) VisitConfigMap(visitor func(configMap *corev1.ConfigMap)) {
 	})
 }
 
+// VisitMetaObject executes the visitor function on all meta.Object resources
+func (c *Collection) VisitMetaObject(visitor func(metav1.Object)) {
+	c.Visit(func(res runtime.Object) {
+		if conv, ok := res.(metav1.Object); ok {
+			visitor(conv)
+		}
+	})
+}
+
 // Visit executes the visitor function on all resources
 func (c *Collection) Visit(visitor func(runtime.Object)) {
 	for _, res := range c.items {