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

[camel-k] branch master updated (62ed749 -> c104443)

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

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


    from 62ed749  GKE configuration working
     new b093089  traits: add builder trait
     new 5c5f527  traits: conditional apply
     new 6f9b34c  traits: add test for builder trait
     new c104443  traits: fix wrong comment

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


Summary of changes:
 pkg/apis/camel/v1alpha1/types.go           |   8 +-
 pkg/builder/builder_types.go               |  13 +++
 pkg/platform/build.go                      |  73 ---------------
 pkg/platform/{get.go => platform.go}       |  17 ++++
 pkg/stub/action/context/build.go           |  11 ++-
 pkg/stub/action/context/initialize.go      |   2 +-
 pkg/stub/action/integration/deploy.go      |   4 +-
 pkg/{util/log/util.go => trait/builder.go} |  36 +++++---
 pkg/trait/builder_test.go                  | 140 +++++++++++++++++++++++++++++
 pkg/trait/catalog.go                       |  22 ++++-
 pkg/trait/dependencies.go                  |  28 ++----
 pkg/trait/deployment.go                    |  20 ++---
 pkg/trait/ingress.go                       |  16 ++--
 pkg/trait/knative.go                       |  28 +++---
 pkg/trait/owner.go                         |   8 +-
 pkg/trait/route.go                         |  12 ++-
 pkg/trait/service.go                       |  40 ++++++---
 pkg/trait/trait.go                         |  25 ++++--
 pkg/trait/trait_test.go                    |   6 +-
 pkg/trait/types.go                         |  32 +++++--
 20 files changed, 353 insertions(+), 188 deletions(-)
 delete mode 100644 pkg/platform/build.go
 rename pkg/platform/{get.go => platform.go} (86%)
 copy pkg/{util/log/util.go => trait/builder.go} (51%)
 create mode 100644 pkg/trait/builder_test.go


[camel-k] 04/04: traits: fix wrong comment

Posted by nf...@apache.org.
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 c104443b2d1d7049761ddf6bd45fe28c25468920
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Wed Nov 21 18:42:53 2018 +0100

    traits: fix wrong comment
---
 pkg/trait/trait.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pkg/trait/trait.go b/pkg/trait/trait.go
index 766e56b..1ab5f9e 100644
--- a/pkg/trait/trait.go
+++ b/pkg/trait/trait.go
@@ -24,7 +24,7 @@ import (
 	"github.com/pkg/errors"
 )
 
-// apply --
+// Apply --
 func Apply(integration *v1alpha1.Integration, ctx *v1alpha1.IntegrationContext) (*Environment, error) {
 	environment, err := newEnvironment(integration, ctx)
 	if err != nil {


[camel-k] 03/04: traits: add test for builder trait

Posted by nf...@apache.org.
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 6f9b34c8a4a4068827047316052a9592eaeeaa60
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Tue Nov 20 23:41:06 2018 +0100

    traits: add test for builder trait
---
 pkg/trait/builder_test.go | 140 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 140 insertions(+)

diff --git a/pkg/trait/builder_test.go b/pkg/trait/builder_test.go
new file mode 100644
index 0000000..ef35e96
--- /dev/null
+++ b/pkg/trait/builder_test.go
@@ -0,0 +1,140 @@
+/*
+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 (
+	"testing"
+
+	"github.com/apache/camel-k/pkg/builder"
+
+	"github.com/apache/camel-k/pkg/util/kubernetes"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestBuilderTraitNotAppliedBecauseOfNilContext(t *testing.T) {
+	environments := []*Environment{
+		createBuilderTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, v1alpha1.IntegrationPlatformBuildPublishStrategyS2I),
+		createBuilderTestEnv(v1alpha1.IntegrationPlatformClusterKubernetes, v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko),
+	}
+
+	for _, e := range environments {
+		e.Context = nil
+
+		t.Run(string(e.Platform.Spec.Cluster), func(t *testing.T) {
+			err := NewCatalog().apply(e)
+
+			assert.Nil(t, err)
+			assert.NotEmpty(t, e.ExecutedTraits)
+			assert.NotContains(t, e.ExecutedTraits, ID("builder"))
+			assert.Empty(t, e.Steps)
+		})
+	}
+}
+
+func TestBuilderTraitNotAppliedBecauseOfNilPhase(t *testing.T) {
+	environments := []*Environment{
+		createBuilderTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, v1alpha1.IntegrationPlatformBuildPublishStrategyS2I),
+		createBuilderTestEnv(v1alpha1.IntegrationPlatformClusterKubernetes, v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko),
+	}
+
+	for _, e := range environments {
+		e.Context.Status.Phase = ""
+
+		t.Run(string(e.Platform.Spec.Cluster), func(t *testing.T) {
+			err := NewCatalog().apply(e)
+
+			assert.Nil(t, err)
+			assert.NotEmpty(t, e.ExecutedTraits)
+			assert.NotContains(t, e.ExecutedTraits, ID("builder"))
+			assert.Empty(t, e.Steps)
+		})
+	}
+}
+
+func TestS2IBuilderTrait(t *testing.T) {
+	env := createBuilderTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, v1alpha1.IntegrationPlatformBuildPublishStrategyS2I)
+	err := NewCatalog().apply(env)
+
+	assert.Nil(t, err)
+	assert.NotEmpty(t, env.ExecutedTraits)
+	assert.Contains(t, env.ExecutedTraits, ID("builder"))
+	assert.NotEmpty(t, env.Steps)
+	assert.Len(t, env.Steps, 4)
+	assert.Condition(t, func() bool {
+		for _, s := range env.Steps {
+			if s.ID() == "publisher/s2i" && s.Phase() == builder.ApplicationPublishPhase {
+				return true
+			}
+		}
+
+		return false
+	})
+}
+
+func TestKanikoBuilderTrait(t *testing.T) {
+	env := createBuilderTestEnv(v1alpha1.IntegrationPlatformClusterKubernetes, v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko)
+	err := NewCatalog().apply(env)
+
+	assert.Nil(t, err)
+	assert.NotEmpty(t, env.ExecutedTraits)
+	assert.Contains(t, env.ExecutedTraits, ID("builder"))
+	assert.NotEmpty(t, env.Steps)
+	assert.Len(t, env.Steps, 4)
+	assert.Condition(t, func() bool {
+		for _, s := range env.Steps {
+			if s.ID() == "publisher/kaniko" && s.Phase() == builder.ApplicationPublishPhase {
+				return true
+			}
+		}
+
+		return false
+	})
+}
+
+func createBuilderTestEnv(cluster v1alpha1.IntegrationPlatformCluster, strategy v1alpha1.IntegrationPlatformBuildPublishStrategy) *Environment {
+	return &Environment{
+		Integration: &v1alpha1.Integration{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "test",
+				Namespace: "ns",
+			},
+			Status: v1alpha1.IntegrationStatus{
+				Phase: v1alpha1.IntegrationPhaseDeploying,
+			},
+		},
+		Context: &v1alpha1.IntegrationContext{
+			Status: v1alpha1.IntegrationContextStatus{
+				Phase: v1alpha1.IntegrationContextPhaseBuilding,
+			},
+		},
+		Platform: &v1alpha1.IntegrationPlatform{
+			Spec: v1alpha1.IntegrationPlatformSpec{
+				Cluster: cluster,
+				Build: v1alpha1.IntegrationPlatformBuildSpec{
+					PublishStrategy: strategy,
+					Registry:        "registry",
+				},
+			},
+		},
+		ExecutedTraits: make([]ID, 0),
+		Resources:      kubernetes.NewCollection(),
+	}
+}


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

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


[camel-k] 02/04: traits: conditional apply

Posted by nf...@apache.org.
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 5c5f527f46cd42113ab3108f7fe20290b8896a41
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Tue Nov 20 23:40:46 2018 +0100

    traits: conditional apply
---
 pkg/builder/builder_types.go          |  5 +++++
 pkg/stub/action/context/initialize.go |  2 +-
 pkg/trait/builder.go                  |  8 ++++----
 pkg/trait/catalog.go                  |  4 ++++
 pkg/trait/dependencies.go             | 28 ++++++++--------------------
 pkg/trait/deployment.go               | 16 ++++++++--------
 pkg/trait/ingress.go                  |  8 ++++----
 pkg/trait/knative.go                  |  8 ++++----
 pkg/trait/owner.go                    |  8 ++++----
 pkg/trait/route.go                    |  4 ++++
 pkg/trait/service.go                  |  8 ++++----
 pkg/trait/trait.go                    |  2 +-
 pkg/trait/types.go                    | 16 ++++++++++++++--
 13 files changed, 65 insertions(+), 52 deletions(-)

diff --git a/pkg/builder/builder_types.go b/pkg/builder/builder_types.go
index e2c8a24..0cf38ed 100644
--- a/pkg/builder/builder_types.go
+++ b/pkg/builder/builder_types.go
@@ -19,6 +19,7 @@ package builder
 
 import (
 	"context"
+	"fmt"
 	"time"
 
 	"github.com/apache/camel-k/pkg/util/maven"
@@ -56,6 +57,10 @@ type stepWrapper struct {
 	task  func(*Context) error
 }
 
+func (s *stepWrapper) String() string {
+	return fmt.Sprintf("%s@%d", s.id, s.phase)
+}
+
 func (s *stepWrapper) ID() string {
 	return s.id
 }
diff --git a/pkg/stub/action/context/initialize.go b/pkg/stub/action/context/initialize.go
index 3161dd9..f9e8d04 100644
--- a/pkg/stub/action/context/initialize.go
+++ b/pkg/stub/action/context/initialize.go
@@ -51,7 +51,7 @@ func (action *initializeAction) Handle(context *v1alpha1.IntegrationContext) err
 	target := context.DeepCopy()
 
 	// execute custom initialization
-	//if err := trait.Apply(nil, context); err != nil {
+	//if err := trait.apply(nil, context); err != nil {
 	//	return err
 	//}
 
diff --git a/pkg/trait/builder.go b/pkg/trait/builder.go
index 0f9b71a..c8c4a50 100644
--- a/pkg/trait/builder.go
+++ b/pkg/trait/builder.go
@@ -35,11 +35,11 @@ func newBuilderTrait() *builderTrait {
 	}
 }
 
-func (*builderTrait) apply(e *Environment) error {
-	if e.Context == nil || e.Context.Status.Phase != v1alpha1.IntegrationContextPhaseBuilding {
-		return nil
-	}
+func (*builderTrait) appliesTo(e *Environment) bool {
+	return e.Context != nil && e.Context.Status.Phase == v1alpha1.IntegrationContextPhaseBuilding
+}
 
+func (*builderTrait) apply(e *Environment) error {
 	if platform.SupportsS2iPublishStrategy(e.Platform) {
 		e.Steps = s2i.DefaultSteps
 	} else if platform.SupportsKanikoPublishStrategy(e.Platform) {
diff --git a/pkg/trait/catalog.go b/pkg/trait/catalog.go
index 1f95c82..9db06ab 100644
--- a/pkg/trait/catalog.go
+++ b/pkg/trait/catalog.go
@@ -109,6 +109,10 @@ func (c *Catalog) apply(environment *Environment) error {
 	c.configure(environment)
 	traits := c.traitsFor(environment)
 	for _, trait := range traits {
+		if !trait.appliesTo(environment) {
+			continue
+		}
+
 		if trait.IsAuto() {
 			if err := trait.autoconfigure(environment); err != nil {
 				return err
diff --git a/pkg/trait/dependencies.go b/pkg/trait/dependencies.go
index 9dceefa..f284678 100644
--- a/pkg/trait/dependencies.go
+++ b/pkg/trait/dependencies.go
@@ -35,11 +35,11 @@ func newDependenciesTrait() *dependenciesTrait {
 	}
 }
 
-func (d *dependenciesTrait) apply(e *Environment) error {
-	if e.Integration == nil || e.Integration.Status.Phase != "" {
-		return nil
-	}
+func (*dependenciesTrait) appliesTo(e *Environment) bool {
+	return e.Integration != nil && e.Integration.Status.Phase == ""
+}
 
+func (d *dependenciesTrait) apply(e *Environment) error {
 	meta := metadata.Extract(e.Integration.Spec.Source)
 
 	if meta.Language == v1alpha1.LanguageGroovy {
@@ -52,23 +52,11 @@ func (d *dependenciesTrait) apply(e *Environment) error {
 	util.StringSliceUniqueAdd(&e.Integration.Spec.Dependencies, "runtime:jvm")
 	util.StringSliceUniqueAdd(&e.Integration.Spec.Dependencies, "camel:core")
 
-	e.Integration.Spec.Dependencies = d.mergeDependencies(e.Integration.Spec.Dependencies, meta.Dependencies)
+	for _, d := range meta.Dependencies {
+		util.StringSliceUniqueAdd(&e.Integration.Spec.Dependencies, d)
+	}
+
 	// sort the dependencies to get always the same list if they don't change
 	sort.Strings(e.Integration.Spec.Dependencies)
 	return nil
 }
-
-func (d *dependenciesTrait) mergeDependencies(list1 []string, list2 []string) []string {
-	set := make(map[string]bool, 0)
-	for _, d := range list1 {
-		set[d] = true
-	}
-	for _, d := range list2 {
-		set[d] = true
-	}
-	ret := make([]string, 0, len(set))
-	for d := range set {
-		ret = append(ret, d)
-	}
-	return ret
-}
diff --git a/pkg/trait/deployment.go b/pkg/trait/deployment.go
index 9669492..8023050 100644
--- a/pkg/trait/deployment.go
+++ b/pkg/trait/deployment.go
@@ -37,13 +37,13 @@ func newDeploymentTrait() *deploymentTrait {
 	}
 }
 
-func (d *deploymentTrait) apply(e *Environment) error {
-	if e.Integration == nil || e.Integration.Status.Phase != v1alpha1.IntegrationPhaseDeploying {
-		return nil
-	}
+func (d *deploymentTrait) appliesTo(e *Environment) bool {
+	return e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying
+}
 
-	e.Resources.Add(d.getConfigMapFor(e))
-	e.Resources.Add(d.getDeploymentFor(e))
+func (d *deploymentTrait) apply(e *Environment) error {
+	e.Resources.Add(getConfigMapFor(e))
+	e.Resources.Add(getDeploymentFor(e))
 	return nil
 }
 
@@ -53,7 +53,7 @@ func (d *deploymentTrait) apply(e *Environment) error {
 //
 // **********************************
 
-func (*deploymentTrait) getConfigMapFor(e *Environment) *corev1.ConfigMap {
+func getConfigMapFor(e *Environment) *corev1.ConfigMap {
 	// combine properties of integration with context, integration
 	// properties have the priority
 	properties := CombineConfigurationAsMap("property", e.Context, e.Integration)
@@ -89,7 +89,7 @@ func (*deploymentTrait) getConfigMapFor(e *Environment) *corev1.ConfigMap {
 //
 // **********************************
 
-func (*deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
+func getDeploymentFor(e *Environment) *appsv1.Deployment {
 	sourceName := strings.TrimPrefix(e.Integration.Spec.Source.Name, "/")
 
 	// combine Environment of integration with context, integration
diff --git a/pkg/trait/ingress.go b/pkg/trait/ingress.go
index b468d12..b40d74f 100644
--- a/pkg/trait/ingress.go
+++ b/pkg/trait/ingress.go
@@ -39,6 +39,10 @@ func newIngressTrait() *ingressTrait {
 	}
 }
 
+func (*ingressTrait) appliesTo(e *Environment) bool {
+	return e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying
+}
+
 func (i *ingressTrait) autoconfigure(e *Environment) error {
 	if i.Enabled == nil {
 		hasService := i.getTargetService(e) != nil
@@ -50,10 +54,6 @@ func (i *ingressTrait) autoconfigure(e *Environment) error {
 }
 
 func (i *ingressTrait) apply(e *Environment) error {
-	if e.Integration == nil || e.Integration.Status.Phase != v1alpha1.IntegrationPhaseDeploying {
-		return nil
-	}
-
 	if i.Host == "" {
 		return errors.New("cannot apply ingress trait: no host defined")
 	}
diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go
index 5bd3c95..b122f8d 100644
--- a/pkg/trait/knative.go
+++ b/pkg/trait/knative.go
@@ -43,6 +43,10 @@ func newKnativeTrait() *knativeTrait {
 	}
 }
 
+func (t *knativeTrait) appliesTo(e *Environment) bool {
+	return e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying
+}
+
 func (t *knativeTrait) autoconfigure(e *Environment) error {
 	if t.Sources == "" {
 		channels := t.getSourceChannels(e)
@@ -52,10 +56,6 @@ func (t *knativeTrait) autoconfigure(e *Environment) error {
 }
 
 func (t *knativeTrait) apply(e *Environment) error {
-	if e.Integration == nil || e.Integration.Status.Phase != v1alpha1.IntegrationPhaseDeploying {
-		return nil
-	}
-
 	for _, sub := range t.getSubscriptionsFor(e) {
 		e.Resources.Add(sub)
 	}
diff --git a/pkg/trait/owner.go b/pkg/trait/owner.go
index accc341..51e491a 100644
--- a/pkg/trait/owner.go
+++ b/pkg/trait/owner.go
@@ -33,11 +33,11 @@ func newOwnerTrait() *ownerTrait {
 	}
 }
 
-func (*ownerTrait) apply(e *Environment) error {
-	if e.Integration == nil || e.Integration.Status.Phase != v1alpha1.IntegrationPhaseDeploying {
-		return nil
-	}
+func (t *ownerTrait) appliesTo(e *Environment) bool {
+	return e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying
+}
 
+func (*ownerTrait) apply(e *Environment) error {
 	controller := true
 	blockOwnerDeletion := true
 	e.Resources.VisitMetaObject(func(res metav1.Object) {
diff --git a/pkg/trait/route.go b/pkg/trait/route.go
index 97fec93..b2ef625 100644
--- a/pkg/trait/route.go
+++ b/pkg/trait/route.go
@@ -39,6 +39,10 @@ func newRouteTrait() *routeTrait {
 	}
 }
 
+func (r *routeTrait) appliesTo(e *Environment) bool {
+	return e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying
+}
+
 func (r *routeTrait) autoconfigure(e *Environment) error {
 	if r.Enabled == nil {
 		hasService := r.getTargetService(e) != nil
diff --git a/pkg/trait/service.go b/pkg/trait/service.go
index b92a2fc..1c9b19b 100644
--- a/pkg/trait/service.go
+++ b/pkg/trait/service.go
@@ -50,6 +50,10 @@ func newServiceTrait() *serviceTrait {
 	}
 }
 
+func (s *serviceTrait) appliesTo(e *Environment) bool {
+	return e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying
+}
+
 func (s *serviceTrait) autoconfigure(e *Environment) error {
 	if s.Enabled == nil {
 		required := s.requiresService(e)
@@ -59,10 +63,6 @@ func (s *serviceTrait) autoconfigure(e *Environment) error {
 }
 
 func (s *serviceTrait) apply(e *Environment) (err error) {
-	if e.Integration == nil || e.Integration.Status.Phase != v1alpha1.IntegrationPhaseDeploying {
-		return nil
-	}
-
 	var svc *corev1.Service
 	if svc, err = s.getServiceFor(e); err != nil {
 		return err
diff --git a/pkg/trait/trait.go b/pkg/trait/trait.go
index 1ab5f9e..766e56b 100644
--- a/pkg/trait/trait.go
+++ b/pkg/trait/trait.go
@@ -24,7 +24,7 @@ import (
 	"github.com/pkg/errors"
 )
 
-// Apply --
+// apply --
 func Apply(integration *v1alpha1.Integration, ctx *v1alpha1.IntegrationContext) (*Environment, error) {
 	environment, err := newEnvironment(integration, ctx)
 	if err != nil {
diff --git a/pkg/trait/types.go b/pkg/trait/types.go
index e9d3cc5..8c1d09a 100644
--- a/pkg/trait/types.go
+++ b/pkg/trait/types.go
@@ -34,10 +34,12 @@ type ID string
 // Trait is the interface of all traits
 type Trait interface {
 	Identifiable
-	// enabled tells if the trait is enabled
+	// IsEnabled tells if the trait is enabled
 	IsEnabled() bool
-	// auto determine if the trait should be configured automatically
+	// IsAuto determine if the trait should be configured automatically
 	IsAuto() bool
+	// appliesTo tells if the trait supports the given environment
+	appliesTo(environment *Environment) 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
@@ -99,3 +101,13 @@ type Environment struct {
 	Steps          []builder.Step
 	ExecutedTraits []ID
 }
+
+// IntegrationInPhase --
+func (e *Environment) IntegrationInPhase(phase v1alpha1.IntegrationPhase) bool {
+	return e.Integration != nil && e.Integration.Status.Phase == phase
+}
+
+// IntegrationContextInPhase --
+func (e *Environment) IntegrationContextInPhase(phase v1alpha1.IntegrationContextPhase) bool {
+	return e.Context != nil && e.Context.Status.Phase == phase
+}