You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by nf...@apache.org on 2018/11/22 10:48:54 UTC

[camel-k] 01/04: traits: add builder trait

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

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

commit b093089427f4adefd3816a6aa7a2612c11ec40d8
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Tue Nov 20 16:48:16 2018 +0100

    traits: add builder trait
---
 pkg/apis/camel/v1alpha1/types.go      |  8 ++--
 pkg/builder/builder_types.go          |  8 ++++
 pkg/platform/build.go                 | 73 -----------------------------------
 pkg/platform/{get.go => platform.go}  | 17 ++++++++
 pkg/stub/action/context/build.go      | 11 +++++-
 pkg/stub/action/integration/deploy.go |  4 +-
 pkg/trait/{owner.go => builder.go}    | 39 ++++++++-----------
 pkg/trait/catalog.go                  | 18 +++++++--
 pkg/trait/dependencies.go             |  2 +-
 pkg/trait/deployment.go               | 10 ++---
 pkg/trait/ingress.go                  |  8 ++--
 pkg/trait/knative.go                  | 20 +++++-----
 pkg/trait/owner.go                    |  2 +-
 pkg/trait/route.go                    |  8 ++--
 pkg/trait/service.go                  | 32 +++++++++++----
 pkg/trait/trait.go                    | 25 ++++++++----
 pkg/trait/trait_test.go               |  6 +--
 pkg/trait/types.go                    | 16 ++++----
 18 files changed, 151 insertions(+), 156 deletions(-)

diff --git a/pkg/apis/camel/v1alpha1/types.go b/pkg/apis/camel/v1alpha1/types.go
index f8a11c3..b872ac3 100644
--- a/pkg/apis/camel/v1alpha1/types.go
+++ b/pkg/apis/camel/v1alpha1/types.go
@@ -52,8 +52,8 @@ type IntegrationSpec struct {
 	Replicas      *int32                          `json:"replicas,omitempty"`
 	Source        SourceSpec                      `json:"source,omitempty"`
 	Context       string                          `json:"context,omitempty"`
-	Profile       TraitProfile                    `json:"profile,omitempty"`
 	Dependencies  []string                        `json:"dependencies,omitempty"`
+	Profile       TraitProfile                    `json:"profile,omitempty"`
 	Traits        map[string]IntegrationTraitSpec `json:"traits,omitempty"`
 	Configuration []ConfigurationSpec             `json:"configuration,omitempty"`
 }
@@ -153,8 +153,10 @@ type IntegrationContext struct {
 
 // IntegrationContextSpec --
 type IntegrationContextSpec struct {
-	Dependencies  []string            `json:"dependencies,omitempty"`
-	Configuration []ConfigurationSpec `json:"configuration,omitempty"`
+	Dependencies  []string                        `json:"dependencies,omitempty"`
+	Profile       TraitProfile                    `json:"profile,omitempty"`
+	Traits        map[string]IntegrationTraitSpec `json:"traits,omitempty"`
+	Configuration []ConfigurationSpec             `json:"configuration,omitempty"`
 }
 
 // IntegrationContextStatus --
diff --git a/pkg/builder/builder_types.go b/pkg/builder/builder_types.go
index 33eac31..e2c8a24 100644
--- a/pkg/builder/builder_types.go
+++ b/pkg/builder/builder_types.go
@@ -79,6 +79,14 @@ func NewStep(ID string, phase int, task func(*Context) error) Step {
 	return &s
 }
 
+// NewIdentifierForContext --
+func NewIdentifierForContext(context *v1alpha1.IntegrationContext) Identifier {
+	return Identifier{
+		Name:      "context-" + context.Name,
+		Qualifier: context.ResourceVersion,
+	}
+}
+
 // Identifier --
 type Identifier struct {
 	Name      string
diff --git a/pkg/platform/build.go b/pkg/platform/build.go
deleted file mode 100644
index d0a8c0e..0000000
--- a/pkg/platform/build.go
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
-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 platform
-
-import (
-	"context"
-	"errors"
-
-	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
-	"github.com/apache/camel-k/pkg/builder"
-	"github.com/apache/camel-k/pkg/builder/kaniko"
-	"github.com/apache/camel-k/pkg/builder/s2i"
-)
-
-// gBuilder is the current builder
-// Note: it cannot be changed at runtime, needs a operator restart
-var gBuilder builder.Builder
-
-// GetPlatformBuilder --
-func GetPlatformBuilder(ctx context.Context, namespace string) (builder.Builder, error) {
-	if gBuilder != nil {
-		return gBuilder, nil
-	}
-
-	gBuilder = builder.New(ctx, namespace)
-
-	return gBuilder, nil
-}
-
-// NewBuildRequest --
-func NewBuildRequest(ctx context.Context, context *v1alpha1.IntegrationContext) (builder.Request, error) {
-	req := builder.Request{
-		Identifier: builder.Identifier{
-			Name:      "context-" + context.Name,
-			Qualifier: context.ResourceVersion,
-		},
-		Dependencies: context.Spec.Dependencies,
-		Steps:        kaniko.DefaultSteps,
-	}
-
-	p, err := GetCurrentPlatform(context.Namespace)
-	if err != nil {
-		return req, err
-	}
-
-	req.Platform = p.Spec
-
-	if SupportsS2iPublishStrategy(p) {
-		req.Steps = s2i.DefaultSteps
-	} else if SupportsKanikoPublishStrategy(p) {
-		req.Steps = kaniko.DefaultSteps
-		req.BuildDir = kaniko.BuildDir
-	} else {
-		return req, errors.New("unsupported platform configuration")
-	}
-
-	return req, nil
-}
diff --git a/pkg/platform/get.go b/pkg/platform/platform.go
similarity index 86%
rename from pkg/platform/get.go
rename to pkg/platform/platform.go
index afc9268..d208c20 100644
--- a/pkg/platform/get.go
+++ b/pkg/platform/platform.go
@@ -18,12 +18,29 @@ limitations under the License.
 package platform
 
 import (
+	"context"
 	"errors"
 
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"github.com/apache/camel-k/pkg/builder"
 	"github.com/operator-framework/operator-sdk/pkg/sdk"
 )
 
+// gBuilder is the current builder
+// Note: it cannot be changed at runtime, needs a operator restart
+var gBuilder builder.Builder
+
+// GetPlatformBuilder --
+func GetPlatformBuilder(ctx context.Context, namespace string) (builder.Builder, error) {
+	if gBuilder != nil {
+		return gBuilder, nil
+	}
+
+	gBuilder = builder.New(ctx, namespace)
+
+	return gBuilder, nil
+}
+
 // GetCurrentPlatform returns the currently installed platform
 func GetCurrentPlatform(namespace string) (*v1alpha1.IntegrationPlatform, error) {
 	lst, err := ListPlatforms(namespace)
diff --git a/pkg/stub/action/context/build.go b/pkg/stub/action/context/build.go
index cc34101..d97cfce 100644
--- a/pkg/stub/action/context/build.go
+++ b/pkg/stub/action/context/build.go
@@ -20,6 +20,8 @@ package context
 import (
 	"context"
 
+	"github.com/apache/camel-k/pkg/trait"
+
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/builder"
 	"github.com/apache/camel-k/pkg/platform"
@@ -54,11 +56,18 @@ func (action *buildAction) Handle(context *v1alpha1.IntegrationContext) error {
 	if err != nil {
 		return err
 	}
-	r, err := platform.NewBuildRequest(action.Context, context)
+	env, err := trait.Apply(nil, context)
 	if err != nil {
 		return err
 	}
 
+	r := builder.Request{
+		Identifier:   builder.NewIdentifierForContext(context),
+		Dependencies: context.Spec.Dependencies,
+		Steps:        env.Steps,
+		Platform:     env.Platform.Spec,
+	}
+
 	res := b.Submit(r)
 	if res.Status == builder.StatusSubmitted {
 		logrus.Info("Build submitted")
diff --git a/pkg/stub/action/integration/deploy.go b/pkg/stub/action/integration/deploy.go
index 134a74d..1ad22ab 100644
--- a/pkg/stub/action/integration/deploy.go
+++ b/pkg/stub/action/integration/deploy.go
@@ -42,12 +42,12 @@ func (action *deployAction) CanHandle(integration *v1alpha1.Integration) bool {
 }
 
 func (action *deployAction) Handle(integration *v1alpha1.Integration) error {
-	resources, err := trait.Apply(integration, nil)
+	env, err := trait.Apply(integration, nil)
 	if err != nil {
 		return err
 	}
 	// TODO we should look for objects that are no longer present in the collection and remove them
-	err = kubernetes.ReplaceResources(resources)
+	err = kubernetes.ReplaceResources(env.Resources.Items())
 	if err != nil {
 		return err
 	}
diff --git a/pkg/trait/owner.go b/pkg/trait/builder.go
similarity index 51%
copy from pkg/trait/owner.go
copy to pkg/trait/builder.go
index 82ba46e..0f9b71a 100644
--- a/pkg/trait/owner.go
+++ b/pkg/trait/builder.go
@@ -19,39 +19,32 @@ package trait
 
 import (
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"github.com/apache/camel-k/pkg/builder/kaniko"
+	"github.com/apache/camel-k/pkg/builder/s2i"
+	"github.com/apache/camel-k/pkg/platform"
 )
 
-// ownerTrait ensures that all created resources belong to the integration being created
-type ownerTrait struct {
+// TODO: we should add a way to label a trait as platform so it cannot be disabled/removed
+type builderTrait struct {
 	BaseTrait `property:",squash"`
 }
 
-func newOwnerTrait() *ownerTrait {
-	return &ownerTrait{
-		BaseTrait: newBaseTrait("owner"),
+func newBuilderTrait() *builderTrait {
+	return &builderTrait{
+		BaseTrait: newBaseTrait("builder"),
 	}
 }
 
-func (*ownerTrait) apply(e *environment) error {
-	if e.Integration == nil || e.Integration.Status.Phase != v1alpha1.IntegrationPhaseDeploying {
+func (*builderTrait) apply(e *Environment) error {
+	if e.Context == nil || e.Context.Status.Phase != v1alpha1.IntegrationContextPhaseBuilding {
 		return nil
 	}
 
-	controller := true
-	blockOwnerDeletion := true
-	e.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)
-	})
+	if platform.SupportsS2iPublishStrategy(e.Platform) {
+		e.Steps = s2i.DefaultSteps
+	} else if platform.SupportsKanikoPublishStrategy(e.Platform) {
+		e.Steps = kaniko.DefaultSteps
+	}
+
 	return nil
 }
diff --git a/pkg/trait/catalog.go b/pkg/trait/catalog.go
index 7ddecea..1f95c82 100644
--- a/pkg/trait/catalog.go
+++ b/pkg/trait/catalog.go
@@ -35,6 +35,7 @@ type Catalog struct {
 	tRoute        Trait
 	tIngress      Trait
 	tOwner        Trait
+	tBuilder      Trait
 }
 
 // NewCatalog creates a new trait Catalog
@@ -47,6 +48,7 @@ func NewCatalog() *Catalog {
 		tRoute:        newRouteTrait(),
 		tIngress:      newIngressTrait(),
 		tOwner:        newOwnerTrait(),
+		tBuilder:      newBuilderTrait(),
 	}
 }
 
@@ -59,14 +61,19 @@ func (c *Catalog) allTraits() []Trait {
 		c.tRoute,
 		c.tIngress,
 		c.tOwner,
+		c.tBuilder,
 	}
 }
 
-func (c *Catalog) traitsFor(environment *environment) []Trait {
+func (c *Catalog) traitsFor(environment *Environment) []Trait {
 	profile := platform.GetProfile(environment.Platform)
-	if environment.Integration.Spec.Profile != "" {
+	if environment.Context != nil && environment.Context.Spec.Profile != "" {
+		profile = environment.Context.Spec.Profile
+	}
+	if environment.Integration != nil && environment.Integration.Spec.Profile != "" {
 		profile = environment.Integration.Spec.Profile
 	}
+
 	switch profile {
 	case v1alpha1.TraitProfileOpenShift:
 		return []Trait{
@@ -75,6 +82,7 @@ func (c *Catalog) traitsFor(environment *environment) []Trait {
 			c.tService,
 			c.tRoute,
 			c.tOwner,
+			c.tBuilder,
 		}
 	case v1alpha1.TraitProfileKubernetes:
 		return []Trait{
@@ -83,19 +91,21 @@ func (c *Catalog) traitsFor(environment *environment) []Trait {
 			c.tService,
 			c.tIngress,
 			c.tOwner,
+			c.tBuilder,
 		}
 	case v1alpha1.TraitProfileKnative:
 		return []Trait{
 			c.tDependencies,
 			c.tKnative,
 			c.tOwner,
+			c.tBuilder,
 		}
 	}
 
 	return nil
 }
 
-func (c *Catalog) apply(environment *environment) error {
+func (c *Catalog) apply(environment *Environment) error {
 	c.configure(environment)
 	traits := c.traitsFor(environment)
 	for _, trait := range traits {
@@ -124,7 +134,7 @@ func (c *Catalog) GetTrait(id string) Trait {
 	return nil
 }
 
-func (c *Catalog) configure(env *environment) {
+func (c *Catalog) configure(env *Environment) {
 	if env.Integration == nil || env.Integration.Spec.Traits == nil {
 		return
 	}
diff --git a/pkg/trait/dependencies.go b/pkg/trait/dependencies.go
index d55c059..9dceefa 100644
--- a/pkg/trait/dependencies.go
+++ b/pkg/trait/dependencies.go
@@ -35,7 +35,7 @@ func newDependenciesTrait() *dependenciesTrait {
 	}
 }
 
-func (d *dependenciesTrait) apply(e *environment) error {
+func (d *dependenciesTrait) apply(e *Environment) error {
 	if e.Integration == nil || e.Integration.Status.Phase != "" {
 		return nil
 	}
diff --git a/pkg/trait/deployment.go b/pkg/trait/deployment.go
index 05df9ee..9669492 100644
--- a/pkg/trait/deployment.go
+++ b/pkg/trait/deployment.go
@@ -37,7 +37,7 @@ func newDeploymentTrait() *deploymentTrait {
 	}
 }
 
-func (d *deploymentTrait) apply(e *environment) error {
+func (d *deploymentTrait) apply(e *Environment) error {
 	if e.Integration == nil || e.Integration.Status.Phase != v1alpha1.IntegrationPhaseDeploying {
 		return nil
 	}
@@ -53,7 +53,7 @@ func (d *deploymentTrait) apply(e *environment) error {
 //
 // **********************************
 
-func (*deploymentTrait) getConfigMapFor(e *environment) *corev1.ConfigMap {
+func (*deploymentTrait) getConfigMapFor(e *Environment) *corev1.ConfigMap {
 	// combine properties of integration with context, integration
 	// properties have the priority
 	properties := CombineConfigurationAsMap("property", e.Context, e.Integration)
@@ -89,11 +89,11 @@ func (*deploymentTrait) getConfigMapFor(e *environment) *corev1.ConfigMap {
 //
 // **********************************
 
-func (*deploymentTrait) getDeploymentFor(e *environment) *appsv1.Deployment {
+func (*deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
 	sourceName := strings.TrimPrefix(e.Integration.Spec.Source.Name, "/")
 
-	// combine environment of integration with context, integration
-	// environment has the priority
+	// combine Environment of integration with context, integration
+	// Environment has the priority
 	environment := CombineConfigurationAsMap("env", e.Context, e.Integration)
 
 	// set env vars needed by the runtime
diff --git a/pkg/trait/ingress.go b/pkg/trait/ingress.go
index 660de26..b468d12 100644
--- a/pkg/trait/ingress.go
+++ b/pkg/trait/ingress.go
@@ -39,7 +39,7 @@ func newIngressTrait() *ingressTrait {
 	}
 }
 
-func (i *ingressTrait) autoconfigure(e *environment) error {
+func (i *ingressTrait) autoconfigure(e *Environment) error {
 	if i.Enabled == nil {
 		hasService := i.getTargetService(e) != nil
 		hasHost := i.Host != ""
@@ -49,7 +49,7 @@ func (i *ingressTrait) autoconfigure(e *environment) error {
 	return nil
 }
 
-func (i *ingressTrait) apply(e *environment) error {
+func (i *ingressTrait) apply(e *Environment) error {
 	if e.Integration == nil || e.Integration.Status.Phase != v1alpha1.IntegrationPhaseDeploying {
 		return nil
 	}
@@ -66,7 +66,7 @@ func (i *ingressTrait) apply(e *environment) error {
 	return nil
 }
 
-func (*ingressTrait) getTargetService(e *environment) (service *corev1.Service) {
+func (*ingressTrait) getTargetService(e *Environment) (service *corev1.Service) {
 	e.Resources.VisitService(func(s *corev1.Service) {
 		if s.ObjectMeta.Labels != nil {
 			if intName, ok := s.ObjectMeta.Labels["camel.apache.org/integration"]; ok && intName == e.Integration.Name {
@@ -77,7 +77,7 @@ func (*ingressTrait) getTargetService(e *environment) (service *corev1.Service)
 	return
 }
 
-func (i *ingressTrait) getIngressFor(env *environment, service *corev1.Service) *v1beta1.Ingress {
+func (i *ingressTrait) getIngressFor(env *Environment, service *corev1.Service) *v1beta1.Ingress {
 	ingress := v1beta1.Ingress{
 		TypeMeta: metav1.TypeMeta{
 			Kind:       "Ingress",
diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go
index 4b10c30..5bd3c95 100644
--- a/pkg/trait/knative.go
+++ b/pkg/trait/knative.go
@@ -43,7 +43,7 @@ func newKnativeTrait() *knativeTrait {
 	}
 }
 
-func (t *knativeTrait) autoconfigure(e *environment) error {
+func (t *knativeTrait) autoconfigure(e *Environment) error {
 	if t.Sources == "" {
 		channels := t.getSourceChannels(e)
 		t.Sources = strings.Join(channels, ",")
@@ -51,7 +51,7 @@ func (t *knativeTrait) autoconfigure(e *environment) error {
 	return nil
 }
 
-func (t *knativeTrait) apply(e *environment) error {
+func (t *knativeTrait) apply(e *Environment) error {
 	if e.Integration == nil || e.Integration.Status.Phase != v1alpha1.IntegrationPhaseDeploying {
 		return nil
 	}
@@ -63,13 +63,13 @@ func (t *knativeTrait) apply(e *environment) error {
 	return nil
 }
 
-func (t *knativeTrait) getServiceFor(e *environment) *serving.Service {
+func (t *knativeTrait) getServiceFor(e *Environment) *serving.Service {
 	// combine properties of integration with context, integration
 	// properties have the priority
 	properties := CombineConfigurationAsMap("property", e.Context, e.Integration)
 
-	// combine environment of integration with context, integration
-	// environment has the priority
+	// combine Environment of integration with context, integration
+	// Environment has the priority
 	environment := CombineConfigurationAsMap("env", e.Context, e.Integration)
 
 	// set env vars needed by the runtime
@@ -125,7 +125,7 @@ func (t *knativeTrait) getServiceFor(e *environment) *serving.Service {
 	return &svc
 }
 
-func (t *knativeTrait) getSubscriptionsFor(e *environment) []*eventing.Subscription {
+func (t *knativeTrait) getSubscriptionsFor(e *Environment) []*eventing.Subscription {
 	channels := t.getConfiguredSourceChannels()
 	subs := make([]*eventing.Subscription, 0)
 	for _, ch := range channels {
@@ -134,7 +134,7 @@ func (t *knativeTrait) getSubscriptionsFor(e *environment) []*eventing.Subscript
 	return subs
 }
 
-func (*knativeTrait) getSubscriptionFor(e *environment, channel string) *eventing.Subscription {
+func (*knativeTrait) getSubscriptionFor(e *Environment, channel string) *eventing.Subscription {
 	return &eventing.Subscription{
 		TypeMeta: metav1.TypeMeta{
 			APIVersion: eventing.SchemeGroupVersion.String(),
@@ -161,7 +161,7 @@ func (*knativeTrait) getSubscriptionFor(e *environment, channel string) *eventin
 	}
 }
 
-func (t *knativeTrait) getConfigurationSerialized(e *environment) string {
+func (t *knativeTrait) getConfigurationSerialized(e *Environment) string {
 	env := t.getConfiguration(e)
 	res, err := json.Marshal(env)
 	if err != nil {
@@ -171,7 +171,7 @@ func (t *knativeTrait) getConfigurationSerialized(e *environment) string {
 	return string(res)
 }
 
-func (t *knativeTrait) getConfiguration(e *environment) knativeutil.CamelEnvironment {
+func (t *knativeTrait) getConfiguration(e *Environment) knativeutil.CamelEnvironment {
 	sourceChannels := t.getConfiguredSourceChannels()
 	env := knativeutil.NewCamelEnvironment()
 	for _, ch := range sourceChannels {
@@ -213,7 +213,7 @@ func (t *knativeTrait) getConfiguredSourceChannels() []string {
 	return channels
 }
 
-func (*knativeTrait) getSourceChannels(e *environment) []string {
+func (*knativeTrait) getSourceChannels(e *Environment) []string {
 	meta := metadata.Extract(e.Integration.Spec.Source)
 	return knativeutil.ExtractChannelNames(meta.FromURIs)
 }
diff --git a/pkg/trait/owner.go b/pkg/trait/owner.go
index 82ba46e..accc341 100644
--- a/pkg/trait/owner.go
+++ b/pkg/trait/owner.go
@@ -33,7 +33,7 @@ func newOwnerTrait() *ownerTrait {
 	}
 }
 
-func (*ownerTrait) apply(e *environment) error {
+func (*ownerTrait) apply(e *Environment) error {
 	if e.Integration == nil || e.Integration.Status.Phase != v1alpha1.IntegrationPhaseDeploying {
 		return nil
 	}
diff --git a/pkg/trait/route.go b/pkg/trait/route.go
index 3477145..97fec93 100644
--- a/pkg/trait/route.go
+++ b/pkg/trait/route.go
@@ -39,7 +39,7 @@ func newRouteTrait() *routeTrait {
 	}
 }
 
-func (r *routeTrait) autoconfigure(e *environment) error {
+func (r *routeTrait) autoconfigure(e *Environment) error {
 	if r.Enabled == nil {
 		hasService := r.getTargetService(e) != nil
 		r.Enabled = &hasService
@@ -47,7 +47,7 @@ func (r *routeTrait) autoconfigure(e *environment) error {
 	return nil
 }
 
-func (r *routeTrait) apply(e *environment) error {
+func (r *routeTrait) apply(e *Environment) error {
 	if e.Integration == nil || e.Integration.Status.Phase != v1alpha1.IntegrationPhaseDeploying {
 		return nil
 	}
@@ -61,7 +61,7 @@ func (r *routeTrait) apply(e *environment) error {
 	return nil
 }
 
-func (*routeTrait) getTargetService(e *environment) (service *corev1.Service) {
+func (*routeTrait) getTargetService(e *Environment) (service *corev1.Service) {
 	e.Resources.VisitService(func(s *corev1.Service) {
 		if s.ObjectMeta.Labels != nil {
 			if intName, ok := s.ObjectMeta.Labels["camel.apache.org/integration"]; ok && intName == e.Integration.Name {
@@ -72,7 +72,7 @@ func (*routeTrait) getTargetService(e *environment) (service *corev1.Service) {
 	return
 }
 
-func (r *routeTrait) getRouteFor(env *environment, service *corev1.Service) *routev1.Route {
+func (r *routeTrait) getRouteFor(env *Environment, service *corev1.Service) *routev1.Route {
 	route := routev1.Route{
 		TypeMeta: metav1.TypeMeta{
 			Kind:       "Route",
diff --git a/pkg/trait/service.go b/pkg/trait/service.go
index bc97b70..b92a2fc 100644
--- a/pkg/trait/service.go
+++ b/pkg/trait/service.go
@@ -50,7 +50,7 @@ func newServiceTrait() *serviceTrait {
 	}
 }
 
-func (s *serviceTrait) autoconfigure(e *environment) error {
+func (s *serviceTrait) autoconfigure(e *Environment) error {
 	if s.Enabled == nil {
 		required := s.requiresService(e)
 		s.Enabled = &required
@@ -58,7 +58,7 @@ func (s *serviceTrait) autoconfigure(e *environment) error {
 	return nil
 }
 
-func (s *serviceTrait) apply(e *environment) (err error) {
+func (s *serviceTrait) apply(e *Environment) (err error) {
 	if e.Integration == nil || e.Integration.Status.Phase != v1alpha1.IntegrationPhaseDeploying {
 		return nil
 	}
@@ -71,7 +71,7 @@ func (s *serviceTrait) apply(e *environment) (err error) {
 	return nil
 }
 
-func (s *serviceTrait) getServiceFor(e *environment) (*corev1.Service, error) {
+func (s *serviceTrait) getServiceFor(e *Environment) (*corev1.Service, error) {
 	svc := corev1.Service{
 		TypeMeta: metav1.TypeMeta{
 			Kind:       "Service",
@@ -102,11 +102,27 @@ func (s *serviceTrait) getServiceFor(e *environment) (*corev1.Service, error) {
 	return &svc, nil
 }
 
-func (*serviceTrait) requiresService(environment *environment) bool {
-	for _, dep := range environment.Integration.Spec.Dependencies {
-		if decision, present := webComponents[dep]; present {
-			return decision
+func (*serviceTrait) requiresService(environment *Environment) bool {
+	cweb := false
+	iweb := false
+
+	if environment.Context != nil {
+		for _, dep := range environment.Context.Spec.Dependencies {
+			if decision, present := webComponents[dep]; present {
+				cweb = decision
+				break
+			}
 		}
 	}
-	return false
+
+	if environment.Integration != nil {
+		for _, dep := range environment.Integration.Spec.Dependencies {
+			if decision, present := webComponents[dep]; present {
+				iweb = decision
+				break
+			}
+		}
+	}
+
+	return cweb || iweb
 }
diff --git a/pkg/trait/trait.go b/pkg/trait/trait.go
index 6599085..1ab5f9e 100644
--- a/pkg/trait/trait.go
+++ b/pkg/trait/trait.go
@@ -22,28 +22,39 @@ import (
 	"github.com/apache/camel-k/pkg/platform"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 	"github.com/pkg/errors"
-	"k8s.io/apimachinery/pkg/runtime"
 )
 
 // Apply --
-func Apply(integration *v1alpha1.Integration, ctx *v1alpha1.IntegrationContext) ([]runtime.Object, error) {
+func Apply(integration *v1alpha1.Integration, ctx *v1alpha1.IntegrationContext) (*Environment, error) {
 	environment, err := newEnvironment(integration, ctx)
 	if err != nil {
 		return nil, err
 	}
 
 	catalog := NewCatalog()
+
 	// invoke the trait framework to determine the needed resources
 	if err := catalog.apply(environment); err != nil {
 		return nil, errors.Wrap(err, "error during trait customization before deployment")
 	}
 
-	return environment.Resources.Items(), nil
+	return environment, nil
 }
 
-// newEnvironment creates a environment from the given data
-func newEnvironment(integration *v1alpha1.Integration, ctx *v1alpha1.IntegrationContext) (*environment, error) {
-	pl, err := platform.GetCurrentPlatform(integration.Namespace)
+// newEnvironment creates a Environment from the given data
+func newEnvironment(integration *v1alpha1.Integration, ctx *v1alpha1.IntegrationContext) (*Environment, error) {
+	if integration == nil && ctx == nil {
+		return nil, errors.New("neither integration nor context are ste")
+	}
+
+	namespace := ""
+	if integration != nil {
+		namespace = integration.Namespace
+	} else if ctx != nil {
+		namespace = ctx.Namespace
+	}
+
+	pl, err := platform.GetCurrentPlatform(namespace)
 	if err != nil {
 		return nil, err
 	}
@@ -55,7 +66,7 @@ func newEnvironment(integration *v1alpha1.Integration, ctx *v1alpha1.Integration
 		}
 	}
 
-	return &environment{
+	return &Environment{
 		Platform:       pl,
 		Context:        ctx,
 		Integration:    integration,
diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go
index 505a0db..17d8909 100644
--- a/pkg/trait/trait_test.go
+++ b/pkg/trait/trait_test.go
@@ -152,15 +152,15 @@ func TestTraitDecode(t *testing.T) {
 	assert.Equal(t, false, svc.IsEnabled())
 }
 
-func processTestEnv(t *testing.T, env *environment) *kubernetes.Collection {
+func processTestEnv(t *testing.T, env *Environment) *kubernetes.Collection {
 	catalog := NewCatalog()
 	err := catalog.apply(env)
 	assert.Nil(t, err)
 	return env.Resources
 }
 
-func createTestEnv(cluster v1alpha1.IntegrationPlatformCluster, dependencies ...string) *environment {
-	return &environment{
+func createTestEnv(cluster v1alpha1.IntegrationPlatformCluster, dependencies ...string) *Environment {
+	return &Environment{
 		Integration: &v1alpha1.Integration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      "test",
diff --git a/pkg/trait/types.go b/pkg/trait/types.go
index 476cd14..e9d3cc5 100644
--- a/pkg/trait/types.go
+++ b/pkg/trait/types.go
@@ -19,6 +19,7 @@ package trait
 
 import (
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"github.com/apache/camel-k/pkg/builder"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 )
 
@@ -38,9 +39,9 @@ type Trait interface {
 	// auto determine if the trait should be configured automatically
 	IsAuto() bool
 	// autoconfigure is called before any customization to ensure the trait is fully configured
-	autoconfigure(environment *environment) error
-	// apply executes a customization of the environment
-	apply(environment *environment) error
+	autoconfigure(environment *Environment) error
+	// apply executes a customization of the Environment
+	apply(environment *Environment) error
 }
 
 /* Base trait */
@@ -79,21 +80,22 @@ func (trait *BaseTrait) IsEnabled() bool {
 	return *trait.Enabled
 }
 
-func (trait *BaseTrait) autoconfigure(environment *environment) error {
+func (trait *BaseTrait) autoconfigure(environment *Environment) error {
 	return nil
 }
 
-func (trait *BaseTrait) apply(environment *environment) error {
+func (trait *BaseTrait) apply(environment *Environment) error {
 	return nil
 }
 
 /* Environment */
 
-// A environment provides the context where the trait is executed
-type environment struct {
+// A Environment provides the context where the trait is executed
+type Environment struct {
 	Platform       *v1alpha1.IntegrationPlatform
 	Context        *v1alpha1.IntegrationContext
 	Integration    *v1alpha1.Integration
 	Resources      *kubernetes.Collection
+	Steps          []builder.Step
 	ExecutedTraits []ID
 }