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 9ad69746c80092d6c767acbf9889fa81d8247201
Author: lburgazzoli <lburgazz...@gmail.com>
AuthorDate: Sat Dec 8 00:54:25 2018 +0100

    Refactor traits lifecycle #268
---
 pkg/trait/builder.go      | 29 +++++++++++++-----------
 pkg/trait/builder_test.go | 10 ++++-----
 pkg/trait/catalog.go      | 19 +++++++---------
 pkg/trait/debug.go        | 20 ++++++++---------
 pkg/trait/debug_test.go   | 49 +++++++++++++++++++++++++++++++++--------
 pkg/trait/dependencies.go | 14 ++++++++----
 pkg/trait/deployment.go   | 48 ++++++++++++++++++++++------------------
 pkg/trait/ingress.go      | 55 +++++++++++++++++++++++++++++-----------------
 pkg/trait/istio.go        | 16 +++++++++-----
 pkg/trait/knative.go      | 50 +++++++++++++++++++++++++-----------------
 pkg/trait/knative_test.go |  4 ++--
 pkg/trait/owner.go        | 14 ++++++++----
 pkg/trait/route.go        | 48 ++++++++++++++++++++++++----------------
 pkg/trait/service.go      | 39 ++++++++++++++++++++-------------
 pkg/trait/springboot.go   | 35 ++++++++++++-----------------
 pkg/trait/trait.go        |  3 ++-
 pkg/trait/trait_test.go   | 46 +++++++++++++++++++-------------------
 pkg/trait/types.go        | 56 +++++++++++++++--------------------------------
 18 files changed, 314 insertions(+), 241 deletions(-)

diff --git a/pkg/trait/builder.go b/pkg/trait/builder.go
index 2132a1b..e495510 100644
--- a/pkg/trait/builder.go
+++ b/pkg/trait/builder.go
@@ -32,25 +32,30 @@ type builderTrait struct {
 
 func newBuilderTrait() *builderTrait {
        return &builderTrait{
-               BaseTrait: newBaseTrait("builder"),
+               BaseTrait: BaseTrait{
+                       id: ID("builder"),
+               },
        }
 }
 
-func (*builderTrait) appliesTo(e *Environment) bool {
-       if e.Context != nil && e.Context.Status.Phase == 
v1alpha1.IntegrationContextPhaseBuilding {
-               return true
+func (t *builderTrait) Configure(e *Environment) (bool, error) {
+       if t.Enabled != nil && !*t.Enabled {
+               return false, nil
        }
 
-       if e.Integration != nil && e.Integration.Status.Phase == 
v1alpha1.IntegrationPhaseBuildingImage &&
-               e.Context != nil && e.Context.Status.Phase == 
v1alpha1.IntegrationContextPhaseReady {
-               return true
+       if 
e.IntegrationContextInPhase(v1alpha1.IntegrationContextPhaseBuilding) {
+               return true, nil
        }
 
-       return false
+       if e.InPhase(v1alpha1.IntegrationContextPhaseReady, 
v1alpha1.IntegrationPhaseBuildingImage) {
+               return true, nil
+       }
+
+       return false, nil
 }
 
-func (*builderTrait) apply(e *Environment) error {
-       if e.Context != nil && e.Context.Status.Phase == 
v1alpha1.IntegrationContextPhaseBuilding {
+func (t *builderTrait) Apply(e *Environment) error {
+       if 
e.IntegrationContextInPhase(v1alpha1.IntegrationContextPhaseBuilding) {
                if platform.SupportsS2iPublishStrategy(e.Platform) {
                        e.Steps = s2i.DefaultSteps
                } else if platform.SupportsKanikoPublishStrategy(e.Platform) {
@@ -59,9 +64,7 @@ func (*builderTrait) apply(e *Environment) error {
                }
        }
 
-       if e.Integration != nil && e.Integration.Status.Phase == 
v1alpha1.IntegrationPhaseBuildingImage &&
-               e.Context != nil && e.Context.Status.Phase == 
v1alpha1.IntegrationContextPhaseReady {
-
+       if e.InPhase(v1alpha1.IntegrationContextPhaseReady, 
v1alpha1.IntegrationPhaseBuildingImage) {
                if platform.SupportsS2iPublishStrategy(e.Platform) {
                        e.Steps = []builder.Step{
                                builder.NewStep("packager", 
builder.ApplicationPackagePhase, builder.StandardPackager),
diff --git a/pkg/trait/builder_test.go b/pkg/trait/builder_test.go
index cf6c064..c4bb0ee 100644
--- a/pkg/trait/builder_test.go
+++ b/pkg/trait/builder_test.go
@@ -44,7 +44,7 @@ func TestBuilderTraitNotAppliedBecauseOfNilContext(t 
*testing.T) {
 
                        assert.Nil(t, err)
                        assert.NotEmpty(t, e.ExecutedTraits)
-                       assert.NotContains(t, e.ExecutedTraits, ID("builder"))
+                       assert.Nil(t, e.GetTrait(ID("builder")))
                        assert.Empty(t, e.Steps)
                })
        }
@@ -65,7 +65,7 @@ func TestBuilderTraitNotAppliedBecauseOfNilPhase(t 
*testing.T) {
 
                        assert.Nil(t, err)
                        assert.NotEmpty(t, e.ExecutedTraits)
-                       assert.NotContains(t, e.ExecutedTraits, ID("builder"))
+                       assert.Nil(t, e.GetTrait(ID("builder")))
                        assert.Empty(t, e.Steps)
                })
        }
@@ -77,7 +77,7 @@ func TestS2IBuilderTrait(t *testing.T) {
 
        assert.Nil(t, err)
        assert.NotEmpty(t, env.ExecutedTraits)
-       assert.Contains(t, env.ExecutedTraits, ID("builder"))
+       assert.NotNil(t, env.GetTrait(ID("builder")))
        assert.NotEmpty(t, env.Steps)
        assert.Len(t, env.Steps, 5)
        assert.Condition(t, func() bool {
@@ -97,7 +97,7 @@ func TestKanikoBuilderTrait(t *testing.T) {
 
        assert.Nil(t, err)
        assert.NotEmpty(t, env.ExecutedTraits)
-       assert.Contains(t, env.ExecutedTraits, ID("builder"))
+       assert.NotNil(t, env.GetTrait(ID("builder")))
        assert.NotEmpty(t, env.Steps)
        assert.Len(t, env.Steps, 5)
        assert.Condition(t, func() bool {
@@ -137,7 +137,7 @@ func createBuilderTestEnv(cluster 
v1alpha1.IntegrationPlatformCluster, strategy
                        },
                },
                EnvVars:        make(map[string]string),
-               ExecutedTraits: make([]ID, 0),
+               ExecutedTraits: make([]Trait, 0),
                Resources:      kubernetes.NewCollection(),
        }
 }
diff --git a/pkg/trait/catalog.go b/pkg/trait/catalog.go
index 39ebdfd..ef0b9b3 100644
--- a/pkg/trait/catalog.go
+++ b/pkg/trait/catalog.go
@@ -122,23 +122,20 @@ func (c *Catalog) apply(environment *Environment) error {
        traits := c.traitsFor(environment)
 
        for _, trait := range traits {
-               if !trait.appliesTo(environment) {
-                       continue
+               enabled, err := trait.Configure(environment)
+               if err != nil {
+                       return err
                }
 
-               if trait.IsAuto() {
-                       if err := trait.autoconfigure(environment); err != nil {
-                               return err
-                       }
-               }
+               if enabled {
+                       logrus.Infof("Apply trait: %s", trait.ID())
 
-               if trait.IsEnabled() {
-                       logrus.Infof("apply trait: %s", trait.ID())
-                       if err := trait.apply(environment); err != nil {
+                       err = trait.Apply(environment)
+                       if err != nil {
                                return err
                        }
 
-                       environment.ExecutedTraits = 
append(environment.ExecutedTraits, trait.ID())
+                       environment.ExecutedTraits = 
append(environment.ExecutedTraits, trait)
                }
        }
 
diff --git a/pkg/trait/debug.go b/pkg/trait/debug.go
index e5e40d9..b78bcc6 100644
--- a/pkg/trait/debug.go
+++ b/pkg/trait/debug.go
@@ -27,23 +27,21 @@ type debugTrait struct {
 
 func newDebugTrait() *debugTrait {
        return &debugTrait{
-               BaseTrait: newBaseTrait("debug"),
+               BaseTrait: BaseTrait{
+                       id: ID("debug"),
+               },
        }
 }
 
-func (r *debugTrait) appliesTo(e *Environment) bool {
-       return e.Integration != nil && e.Integration.Status.Phase == 
v1alpha1.IntegrationPhaseDeploying
-}
-
-func (r *debugTrait) autoconfigure(e *Environment) error {
-       if r.Enabled == nil {
-               enabled := false
-               r.Enabled = &enabled
+func (t *debugTrait) Configure(e *Environment) (bool, error) {
+       if t.Enabled != nil && *t.Enabled {
+               return 
e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying), nil
        }
-       return nil
+
+       return false, nil
 }
 
-func (r *debugTrait) apply(e *Environment) error {
+func (t *debugTrait) Apply(e *Environment) error {
        // this is all that's needed as long as the base image is 
`fabric8/s2i-java` look into builder/builder.go
        e.EnvVars["JAVA_DEBUG"] = True
 
diff --git a/pkg/trait/debug_test.go b/pkg/trait/debug_test.go
index 3989009..6c24d82 100644
--- a/pkg/trait/debug_test.go
+++ b/pkg/trait/debug_test.go
@@ -24,26 +24,57 @@ import (
        "github.com/stretchr/testify/assert"
 )
 
-var (
-       env = &Environment{
+func TestDebugTraitApplicability(t *testing.T) {
+       env := Environment{
                Integration: &v1alpha1.Integration{
                        Status: v1alpha1.IntegrationStatus{
                                Phase: v1alpha1.IntegrationPhaseDeploying,
                        },
+                       Spec: v1alpha1.IntegrationSpec{
+                               Traits: 
map[string]v1alpha1.IntegrationTraitSpec{
+                                       "debug": {
+                                               Configuration: 
map[string]string{
+                                                       "enabled": "true",
+                                               },
+                                       },
+                               },
+                       },
                },
                EnvVars: make(map[string]string)}
 
-       trait = newDebugTrait()
-)
+       trait := newDebugTrait()
 
-func TestApplicability(t *testing.T) {
-       assert.True(t, trait.appliesTo(env))
+       enabled, err := trait.Configure(&env)
+       assert.Nil(t, err)
+       assert.False(t, enabled)
 
        env.Integration.Status.Phase = v1alpha1.IntegrationPhaseRunning
-       assert.False(t, trait.appliesTo(env))
+
+       enabled, err = trait.Configure(&env)
+       assert.Nil(t, err)
+       assert.False(t, enabled)
 }
 
-func TestApply(t *testing.T) {
-       assert.Nil(t, trait.apply(env))
+func TestApplyDebugTrait(t *testing.T) {
+       env := Environment{
+               Integration: &v1alpha1.Integration{
+                       Status: v1alpha1.IntegrationStatus{
+                               Phase: v1alpha1.IntegrationPhaseDeploying,
+                       },
+                       Spec: v1alpha1.IntegrationSpec{
+                               Traits: 
map[string]v1alpha1.IntegrationTraitSpec{
+                                       "debug": {
+                                               Configuration: 
map[string]string{
+                                                       "enabled": "true",
+                                               },
+                                       },
+                               },
+                       },
+               },
+               EnvVars: make(map[string]string)}
+
+       trait := newDebugTrait()
+
+       assert.Nil(t, trait.Apply(&env))
        assert.Equal(t, True, env.EnvVars["JAVA_DEBUG"])
 }
diff --git a/pkg/trait/dependencies.go b/pkg/trait/dependencies.go
index b57d097..ccbc41b 100644
--- a/pkg/trait/dependencies.go
+++ b/pkg/trait/dependencies.go
@@ -31,15 +31,21 @@ type dependenciesTrait struct {
 
 func newDependenciesTrait() *dependenciesTrait {
        return &dependenciesTrait{
-               BaseTrait: newBaseTrait("dependencies"),
+               BaseTrait: BaseTrait{
+                       id: ID("dependencies"),
+               },
        }
 }
 
-func (*dependenciesTrait) appliesTo(e *Environment) bool {
-       return e.Integration != nil && e.Integration.Status.Phase == ""
+func (t *dependenciesTrait) Configure(e *Environment) (bool, error) {
+       if t.Enabled != nil && !*t.Enabled {
+               return false, nil
+       }
+
+       return e.IntegrationInPhase(""), nil
 }
 
-func (*dependenciesTrait) apply(e *Environment) error {
+func (t *dependenciesTrait) Apply(e *Environment) error {
        for _, s := range e.Integration.Spec.Sources {
                meta := metadata.Extract(s)
 
diff --git a/pkg/trait/deployment.go b/pkg/trait/deployment.go
index 801f849..7e8d01f 100644
--- a/pkg/trait/deployment.go
+++ b/pkg/trait/deployment.go
@@ -37,43 +37,49 @@ type deploymentTrait struct {
 
 func newDeploymentTrait() *deploymentTrait {
        return &deploymentTrait{
-               BaseTrait: newBaseTrait("deployment"),
+               BaseTrait: BaseTrait{
+                       id: ID("deployment"),
+               },
        }
 }
 
-func (d *deploymentTrait) appliesTo(e *Environment) bool {
-       if e.Integration != nil && e.Integration.Status.Phase == 
v1alpha1.IntegrationPhaseDeploying {
+func (t *deploymentTrait) Configure(e *Environment) (bool, error) {
+       if t.Enabled != nil && !*t.Enabled {
+               return false, nil
+       }
+
+       if e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) {
                //
                // Don't deploy on knative
                //
-               return e.DetermineProfile() != v1alpha1.TraitProfileKnative
+               return e.DetermineProfile() != v1alpha1.TraitProfileKnative, nil
        }
 
-       if d.ContainerImage && e.InPhase(v1alpha1.IntegrationContextPhaseReady, 
v1alpha1.IntegrationPhaseBuildingContext) {
-               return true
+       if t.ContainerImage && e.InPhase(v1alpha1.IntegrationContextPhaseReady, 
v1alpha1.IntegrationPhaseBuildingContext) {
+               return true, nil
        }
 
-       if !d.ContainerImage && 
e.InPhase(v1alpha1.IntegrationContextPhaseReady, 
v1alpha1.IntegrationPhaseBuildingContext) {
-               return true
+       if !t.ContainerImage && 
e.InPhase(v1alpha1.IntegrationContextPhaseReady, 
v1alpha1.IntegrationPhaseBuildingContext) {
+               return true, nil
        }
 
-       return false
+       return false, nil
 }
 
-func (d *deploymentTrait) apply(e *Environment) error {
-       if d.ContainerImage && e.InPhase(v1alpha1.IntegrationContextPhaseReady, 
v1alpha1.IntegrationPhaseBuildingContext) {
+func (t *deploymentTrait) Apply(e *Environment) error {
+       if t.ContainerImage && e.InPhase(v1alpha1.IntegrationContextPhaseReady, 
v1alpha1.IntegrationPhaseBuildingContext) {
                // trigger container image build
                e.Integration.Status.Phase = 
v1alpha1.IntegrationPhaseBuildingImage
        }
 
-       if !d.ContainerImage && 
e.InPhase(v1alpha1.IntegrationContextPhaseReady, 
v1alpha1.IntegrationPhaseBuildingContext) {
+       if !t.ContainerImage && 
e.InPhase(v1alpha1.IntegrationContextPhaseReady, 
v1alpha1.IntegrationPhaseBuildingContext) {
                // trigger integration deploy
                e.Integration.Status.Phase = v1alpha1.IntegrationPhaseDeploying
        }
 
        if e.Integration != nil && e.Integration.Status.Phase == 
v1alpha1.IntegrationPhaseDeploying {
-               e.Resources.AddAll(d.getConfigMapsFor(e))
-               e.Resources.Add(d.getDeploymentFor(e))
+               e.Resources.AddAll(t.getConfigMapsFor(e))
+               e.Resources.Add(t.getDeploymentFor(e))
        }
 
        return nil
@@ -85,7 +91,7 @@ func (d *deploymentTrait) apply(e *Environment) error {
 //
 // **********************************
 
-func (d *deploymentTrait) getConfigMapsFor(e *Environment) []runtime.Object {
+func (t *deploymentTrait) getConfigMapsFor(e *Environment) []runtime.Object {
        maps := make([]runtime.Object, 0, len(e.Integration.Spec.Sources)+1)
 
        // combine properties of integration with context, integration
@@ -112,7 +118,7 @@ func (d *deploymentTrait) getConfigMapsFor(e *Environment) 
[]runtime.Object {
                },
        )
 
-       if !d.ContainerImage {
+       if !t.ContainerImage {
 
                // do not create 'source' ConfigMap if a docker images for 
deployment
                // is required
@@ -152,13 +158,13 @@ func (d *deploymentTrait) getConfigMapsFor(e 
*Environment) []runtime.Object {
 //
 // **********************************
 
-func (d *deploymentTrait) getSources(e *Environment) []string {
+func (t *deploymentTrait) getSources(e *Environment) []string {
        sources := make([]string, 0, len(e.Integration.Spec.Sources))
 
        for i, s := range e.Integration.Spec.Sources {
                root := fmt.Sprintf("/etc/camel/integrations/%03d", i)
 
-               if d.ContainerImage {
+               if t.ContainerImage {
 
                        // assume sources are copied over the standard 
deployments folder
                        root = "/deployments/sources"
@@ -185,8 +191,8 @@ func (d *deploymentTrait) getSources(e *Environment) 
[]string {
        return sources
 }
 
-func (d *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
-       sources := d.getSources(e)
+func (t *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
+       sources := t.getSources(e)
 
        // combine Environment of integration with context, integration
        // Environment has the priority
@@ -288,7 +294,7 @@ func (d *deploymentTrait) getDeploymentFor(e *Environment) 
*appsv1.Deployment {
        // Volumes :: Sources
        //
 
-       if !d.ContainerImage {
+       if !t.ContainerImage {
 
                // We can configure the operator to generate a container images 
that include
                // integration sources instead of mounting it at runtime and in 
such case we
diff --git a/pkg/trait/ingress.go b/pkg/trait/ingress.go
index 1688e23..f8bd7ec 100644
--- a/pkg/trait/ingress.go
+++ b/pkg/trait/ingress.go
@@ -30,43 +30,58 @@ import (
 type ingressTrait struct {
        BaseTrait `property:",squash"`
        Host      string `property:"host"`
+       Auto      *bool  `property:"auto"`
 }
 
 func newIngressTrait() *ingressTrait {
        return &ingressTrait{
-               BaseTrait: newBaseTrait("ingress"),
-               Host:      "",
+               BaseTrait: BaseTrait{
+                       id: ID("ingress"),
+               },
+               Host: "",
        }
 }
 
-func (*ingressTrait) appliesTo(e *Environment) bool {
-       return e.Integration != nil && e.Integration.Status.Phase == 
v1alpha1.IntegrationPhaseDeploying
-}
+func (t *ingressTrait) Configure(e *Environment) (bool, error) {
+       if t.Enabled != nil && !*t.Enabled {
+               return false, nil
+       }
 
-func (i *ingressTrait) autoconfigure(e *Environment) error {
-       if i.Enabled == nil {
-               hasService := i.getTargetService(e) != nil
-               hasHost := i.Host != ""
+       if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) {
+               return false, nil
+       }
+
+       if t.Auto == nil || *t.Auto {
+               hasService := t.getTargetService(e) != nil
+               hasHost := t.Host != ""
                enabled := hasService && hasHost
-               i.Enabled = &enabled
+
+               if !enabled {
+                       return false, nil
+               }
        }
-       return nil
+
+       if t.Host == "" {
+               return false, errors.New("cannot Apply ingress trait: no host 
defined")
+       }
+
+       return true, nil
 }
 
-func (i *ingressTrait) apply(e *Environment) error {
-       if i.Host == "" {
-               return errors.New("cannot apply ingress trait: no host defined")
+func (t *ingressTrait) Apply(e *Environment) error {
+       if t.Host == "" {
+               return errors.New("cannot Apply ingress trait: no host defined")
        }
-       service := i.getTargetService(e)
+       service := t.getTargetService(e)
        if service == nil {
-               return errors.New("cannot apply ingress trait: no target 
service")
+               return errors.New("cannot Apply ingress trait: no target 
service")
        }
 
-       e.Resources.Add(i.getIngressFor(service))
+       e.Resources.Add(t.getIngressFor(service))
        return nil
 }
 
-func (*ingressTrait) getTargetService(e *Environment) (service 
*corev1.Service) {
+func (t *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 +92,7 @@ func (*ingressTrait) getTargetService(e *Environment) 
(service *corev1.Service)
        return
 }
 
-func (i *ingressTrait) getIngressFor(service *corev1.Service) *v1beta1.Ingress 
{
+func (t *ingressTrait) getIngressFor(service *corev1.Service) *v1beta1.Ingress 
{
        ingress := v1beta1.Ingress{
                TypeMeta: metav1.TypeMeta{
                        Kind:       "Ingress",
@@ -94,7 +109,7 @@ func (i *ingressTrait) getIngressFor(service 
*corev1.Service) *v1beta1.Ingress {
                        },
                        Rules: []v1beta1.IngressRule{
                                {
-                                       Host: i.Host,
+                                       Host: t.Host,
                                },
                        },
                },
diff --git a/pkg/trait/istio.go b/pkg/trait/istio.go
index 765cbe8..2073220 100644
--- a/pkg/trait/istio.go
+++ b/pkg/trait/istio.go
@@ -34,16 +34,22 @@ const (
 
 func newIstioTrait() *istioTrait {
        return &istioTrait{
-               BaseTrait: newBaseTrait("istio"),
-               Allow:     "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16",
+               BaseTrait: BaseTrait{
+                       id: ID("istio"),
+               },
+               Allow: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16",
        }
 }
 
-func (t *istioTrait) appliesTo(e *Environment) bool {
-       return e.Integration != nil && e.Integration.Status.Phase == 
v1alpha1.IntegrationPhaseDeploying
+func (t *istioTrait) Configure(e *Environment) (bool, error) {
+       if t.Enabled != nil && !*t.Enabled {
+               return false, nil
+       }
+
+       return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying), nil
 }
 
-func (t *istioTrait) apply(e *Environment) error {
+func (t *istioTrait) Apply(e *Environment) error {
        if t.Allow != "" {
                e.Resources.VisitDeployment(func(d *appsv1.Deployment) {
                        d.Spec.Template.Annotations = 
t.injectIstioAnnotation(d.Spec.Template.Annotations)
diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go
index 17293a7..a40abb3 100644
--- a/pkg/trait/knative.go
+++ b/pkg/trait/knative.go
@@ -48,39 +48,49 @@ type knativeTrait struct {
        Sinks     string `property:"sinks"`
        MinScale  *int   `property:"minScale"`
        MaxScale  *int   `property:"maxScale"`
+       Auto      *bool  `property:"auto"`
 }
 
 func newKnativeTrait() *knativeTrait {
        return &knativeTrait{
-               BaseTrait: newBaseTrait("knative"),
+               BaseTrait: BaseTrait{
+                       id: ID("knative"),
+               },
        }
 }
 
-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)
-               t.Sources = strings.Join(channels, ",")
+func (t *knativeTrait) Configure(e *Environment) (bool, error) {
+       if t.Enabled != nil && !*t.Enabled {
+               return false, nil
        }
-       if t.Sinks == "" {
-               channels := t.getSinkChannels(e)
-               t.Sinks = strings.Join(channels, ",")
+
+       if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) {
+               return false, nil
        }
-       // Check the right value for minScale, as not all services are allowed 
to scale down to 0
-       if t.MinScale == nil {
-               meta := metadata.ExtractAll(e.Integration.Spec.Sources)
-               if !meta.RequiresHTTPService || !meta.PassiveEndpoints {
-                       single := 1
-                       t.MinScale = &single
+
+       if t.Auto == nil || *t.Auto {
+               if t.Sources == "" {
+                       channels := t.getSourceChannels(e)
+                       t.Sources = strings.Join(channels, ",")
+               }
+               if t.Sinks == "" {
+                       channels := t.getSinkChannels(e)
+                       t.Sinks = strings.Join(channels, ",")
+               }
+               // Check the right value for minScale, as not all services are 
allowed to scale down to 0
+               if t.MinScale == nil {
+                       meta := metadata.ExtractAll(e.Integration.Spec.Sources)
+                       if !meta.RequiresHTTPService || !meta.PassiveEndpoints {
+                               single := 1
+                               t.MinScale = &single
+                       }
                }
        }
-       return nil
+
+       return true, nil
 }
 
-func (t *knativeTrait) apply(e *Environment) error {
+func (t *knativeTrait) Apply(e *Environment) error {
        if err := t.prepareEnvVars(e); err != nil {
                return err
        }
diff --git a/pkg/trait/knative_test.go b/pkg/trait/knative_test.go
index 325e0f9..f701c88 100644
--- a/pkg/trait/knative_test.go
+++ b/pkg/trait/knative_test.go
@@ -64,7 +64,7 @@ func TestKnativeTraitWithCompressedSources(t *testing.T) {
                        },
                },
                EnvVars:        make(map[string]string),
-               ExecutedTraits: make([]ID, 0),
+               ExecutedTraits: make([]Trait, 0),
                Resources:      kubernetes.NewCollection(),
        }
 
@@ -72,7 +72,7 @@ func TestKnativeTraitWithCompressedSources(t *testing.T) {
 
        assert.Nil(t, err)
        assert.NotEmpty(t, env.ExecutedTraits)
-       assert.Contains(t, env.ExecutedTraits, ID("knative"))
+       assert.NotNil(t, env.GetTrait(ID("knative")))
        assert.NotNil(t, env.EnvVars["KAMEL_KNATIVE_CONFIGURATION"])
 
        services := 0
diff --git a/pkg/trait/owner.go b/pkg/trait/owner.go
index 51e491a..26a7906 100644
--- a/pkg/trait/owner.go
+++ b/pkg/trait/owner.go
@@ -29,15 +29,21 @@ type ownerTrait struct {
 
 func newOwnerTrait() *ownerTrait {
        return &ownerTrait{
-               BaseTrait: newBaseTrait("owner"),
+               BaseTrait: BaseTrait{
+                       id: ID("owner"),
+               },
        }
 }
 
-func (t *ownerTrait) appliesTo(e *Environment) bool {
-       return e.Integration != nil && e.Integration.Status.Phase == 
v1alpha1.IntegrationPhaseDeploying
+func (t *ownerTrait) Configure(e *Environment) (bool, error) {
+       if t.Enabled != nil && !*t.Enabled {
+               return false, nil
+       }
+
+       return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying), nil
 }
 
-func (*ownerTrait) apply(e *Environment) error {
+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 e65b0cb..1e6746c 100644
--- a/pkg/trait/route.go
+++ b/pkg/trait/route.go
@@ -30,42 +30,52 @@ import (
 
 type routeTrait struct {
        BaseTrait `property:",squash"`
+       Auto      *bool  `property:"auto"`
        Host      string `property:"host"`
+       service   *corev1.Service
 }
 
 func newRouteTrait() *routeTrait {
        return &routeTrait{
-               BaseTrait: newBaseTrait("route"),
+               BaseTrait: BaseTrait{
+                       id: ID("route"),
+               },
        }
 }
 
-func (r *routeTrait) appliesTo(e *Environment) bool {
-       return e.Integration != nil && e.Integration.Status.Phase == 
v1alpha1.IntegrationPhaseDeploying
-}
+func (t *routeTrait) Configure(e *Environment) (bool, error) {
+       if t.Enabled != nil && !*t.Enabled {
+               return false, nil
+       }
 
-func (r *routeTrait) autoconfigure(e *Environment) error {
-       if r.Enabled == nil {
-               hasService := r.getTargetService(e) != nil
-               r.Enabled = &hasService
+       if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) {
+               return false, nil
        }
-       return nil
+
+       if t.Auto == nil || *t.Auto {
+               t.service = t.getTargetService(e)
+               if t.service == nil {
+                       return false, nil
+               }
+       }
+
+       if t.service == nil {
+               return false, errors.New("cannot apply route trait: no target 
service")
+       }
+
+       return true, nil
 }
 
-func (r *routeTrait) apply(e *Environment) error {
+func (t *routeTrait) Apply(e *Environment) error {
        if e.Integration == nil || e.Integration.Status.Phase != 
v1alpha1.IntegrationPhaseDeploying {
                return nil
        }
 
-       service := r.getTargetService(e)
-       if service == nil {
-               return errors.New("cannot apply route trait: no target service")
-       }
-
-       e.Resources.Add(r.getRouteFor(service))
+       e.Resources.Add(t.getRouteFor(t.service))
        return nil
 }
 
-func (*routeTrait) getTargetService(e *Environment) (service *corev1.Service) {
+func (t *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 {
@@ -76,7 +86,7 @@ func (*routeTrait) getTargetService(e *Environment) (service 
*corev1.Service) {
        return
 }
 
-func (r *routeTrait) getRouteFor(service *corev1.Service) *routev1.Route {
+func (t *routeTrait) getRouteFor(service *corev1.Service) *routev1.Route {
        route := routev1.Route{
                TypeMeta: metav1.TypeMeta{
                        Kind:       "Route",
@@ -94,7 +104,7 @@ func (r *routeTrait) getRouteFor(service *corev1.Service) 
*routev1.Route {
                                Kind: "Service",
                                Name: service.Name,
                        },
-                       Host: r.Host,
+                       Host: t.Host,
                },
        }
        return &route
diff --git a/pkg/trait/service.go b/pkg/trait/service.go
index 0efaf45..007c0fe 100644
--- a/pkg/trait/service.go
+++ b/pkg/trait/service.go
@@ -28,36 +28,45 @@ import (
 type serviceTrait struct {
        BaseTrait `property:",squash"`
 
-       Port int `property:"port"`
+       Auto *bool `property:"auto"`
+       Port int   `property:"port"`
 }
 
 func newServiceTrait() *serviceTrait {
        return &serviceTrait{
-               BaseTrait: newBaseTrait("service"),
-               Port:      8080,
+               BaseTrait: BaseTrait{
+                       id: ID("service"),
+               },
+               Port: 8080,
        }
 }
 
-func (s *serviceTrait) appliesTo(e *Environment) bool {
-       return e.Integration != nil && e.Integration.Status.Phase == 
v1alpha1.IntegrationPhaseDeploying
-}
+func (t *serviceTrait) Configure(e *Environment) (bool, error) {
+       if t.Enabled != nil && !*t.Enabled {
+               return false, nil
+       }
 
-func (s *serviceTrait) autoconfigure(e *Environment) error {
-       if s.Enabled == nil {
+       if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) {
+               return false, nil
+       }
+
+       if t.Auto == nil || *t.Auto {
                meta := metadata.ExtractAll(e.Integration.Spec.Sources)
-               required := meta.RequiresHTTPService
-               s.Enabled = &required
+               if !meta.RequiresHTTPService {
+                       return false, nil
+               }
        }
-       return nil
+
+       return true, nil
 }
 
-func (s *serviceTrait) apply(e *Environment) (err error) {
-       svc := s.getServiceFor(e)
+func (t *serviceTrait) Apply(e *Environment) (err error) {
+       svc := t.getServiceFor(e)
        e.Resources.Add(svc)
        return nil
 }
 
-func (s *serviceTrait) getServiceFor(e *Environment) *corev1.Service {
+func (t *serviceTrait) getServiceFor(e *Environment) *corev1.Service {
        svc := corev1.Service{
                TypeMeta: metav1.TypeMeta{
                        Kind:       "Service",
@@ -76,7 +85,7 @@ func (s *serviceTrait) getServiceFor(e *Environment) 
*corev1.Service {
                                        Name:       "http",
                                        Port:       80,
                                        Protocol:   corev1.ProtocolTCP,
-                                       TargetPort: intstr.FromInt(s.Port),
+                                       TargetPort: intstr.FromInt(t.Port),
                                },
                        },
                        Selector: map[string]string{
diff --git a/pkg/trait/springboot.go b/pkg/trait/springboot.go
index 1314bfb..522a526 100644
--- a/pkg/trait/springboot.go
+++ b/pkg/trait/springboot.go
@@ -36,38 +36,31 @@ type springBootTrait struct {
 
 func newSpringBootTrait() *springBootTrait {
        return &springBootTrait{
-               BaseTrait: newBaseTrait("springboot"),
+               BaseTrait: BaseTrait{
+                       id: ID("springboot"),
+               },
        }
 }
 
-// IsAuto determines if we should apply automatic configuration
-func (trait *springBootTrait) IsAuto() bool {
-       return false
-}
-
-// IsEnabled is used to determine if the trait needs to be executed
-func (trait *springBootTrait) IsEnabled() bool {
-       if trait.Enabled == nil {
-               return false
+func (t *springBootTrait) Configure(e *Environment) (bool, error) {
+       if t.Enabled == nil || !*t.Enabled {
+               return false, nil
        }
-       return *trait.Enabled
-}
 
-func (trait *springBootTrait) appliesTo(e *Environment) bool {
-       if e.Context != nil && e.Context.Status.Phase == 
v1alpha1.IntegrationContextPhaseBuilding {
-               return true
+       if 
e.IntegrationContextInPhase(v1alpha1.IntegrationContextPhaseBuilding) {
+               return true, nil
        }
-       if e.Integration != nil && e.Integration.Status.Phase == 
v1alpha1.IntegrationPhaseDeploying {
-               return true
+       if e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) {
+               return true, nil
        }
-       if e.Integration != nil && e.Integration.Status.Phase == "" {
-               return true
+       if e.IntegrationInPhase("") {
+               return true, nil
        }
 
-       return false
+       return false, nil
 }
 
-func (trait *springBootTrait) apply(e *Environment) error {
+func (t *springBootTrait) Apply(e *Environment) error {
 
        //
        // Integration
diff --git a/pkg/trait/trait.go b/pkg/trait/trait.go
index 592796d..6684319 100644
--- a/pkg/trait/trait.go
+++ b/pkg/trait/trait.go
@@ -24,6 +24,7 @@ import (
        "github.com/pkg/errors"
 )
 
+// True --
 const True = "true"
 
 // Apply --
@@ -72,7 +73,7 @@ func newEnvironment(integration *v1alpha1.Integration, ctx 
*v1alpha1.Integration
                Platform:       pl,
                Context:        ctx,
                Integration:    integration,
-               ExecutedTraits: make([]ID, 0),
+               ExecutedTraits: make([]Trait, 0),
                Resources:      kubernetes.NewCollection(),
                EnvVars:        make(map[string]string),
        }, nil
diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go
index c6df05d..b48bd37 100644
--- a/pkg/trait/trait_test.go
+++ b/pkg/trait/trait_test.go
@@ -37,11 +37,12 @@ const (
 func TestOpenShiftTraits(t *testing.T) {
        env := createTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, 
"camel:core")
        res := processTestEnv(t, env)
+
        assert.NotEmpty(t, env.ExecutedTraits)
-       assert.Contains(t, env.ExecutedTraits, ID("deployment"))
-       assert.NotContains(t, env.ExecutedTraits, ID("service"))
-       assert.NotContains(t, env.ExecutedTraits, ID("route"))
-       assert.Contains(t, env.ExecutedTraits, ID("owner"))
+       assert.NotNil(t, env.GetTrait(ID("deployment")))
+       assert.Nil(t, env.GetTrait(ID("service")))
+       assert.Nil(t, env.GetTrait(ID("route")))
+       assert.NotNil(t, env.GetTrait(ID("owner")))
        assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool {
                return cm.Name == TestProperties
        }))
@@ -53,10 +54,10 @@ func TestOpenShiftTraits(t *testing.T) {
 func TestOpenShiftTraitsWithWeb(t *testing.T) {
        env := createTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, 
"from('undertow:http').to('log:info')")
        res := processTestEnv(t, env)
-       assert.Contains(t, env.ExecutedTraits, ID("deployment"))
-       assert.Contains(t, env.ExecutedTraits, ID("service"))
-       assert.Contains(t, env.ExecutedTraits, ID("route"))
-       assert.Contains(t, env.ExecutedTraits, ID("owner"))
+       assert.NotNil(t, env.GetTrait(ID("deployment")))
+       assert.NotNil(t, env.GetTrait(ID("service")))
+       assert.NotNil(t, env.GetTrait(ID("route")))
+       assert.NotNil(t, env.GetTrait(ID("owner")))
        assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool {
                return cm.Name == TestProperties
        }))
@@ -80,8 +81,8 @@ func TestOpenShiftTraitsWithWebAndConfig(t *testing.T) {
                },
        }
        res := processTestEnv(t, env)
-       assert.Contains(t, env.ExecutedTraits, ID("service"))
-       assert.Contains(t, env.ExecutedTraits, ID("route"))
+       assert.NotNil(t, env.GetTrait(ID("service")))
+       assert.NotNil(t, env.GetTrait(ID("route")))
        assert.NotNil(t, res.GetService(func(svc *corev1.Service) bool {
                return svc.Name == TestDeployment && 
svc.Spec.Ports[0].TargetPort.IntVal == int32(7071)
        }))
@@ -97,8 +98,8 @@ func TestOpenShiftTraitsWithWebAndDisabledTrait(t *testing.T) 
{
                },
        }
        res := processTestEnv(t, env)
-       assert.NotContains(t, env.ExecutedTraits, ID("service"))
-       assert.NotContains(t, env.ExecutedTraits, ID("route")) // No route 
without service
+       assert.Nil(t, env.GetTrait(ID("service")))
+       assert.Nil(t, env.GetTrait(ID("route"))) // No route without service
        assert.Nil(t, res.GetService(func(svc *corev1.Service) bool {
                return true
        }))
@@ -107,10 +108,10 @@ func TestOpenShiftTraitsWithWebAndDisabledTrait(t 
*testing.T) {
 func TestKubernetesTraits(t *testing.T) {
        env := createTestEnv(v1alpha1.IntegrationPlatformClusterKubernetes, 
"from('timer:tick').to('log:info')")
        res := processTestEnv(t, env)
-       assert.Contains(t, env.ExecutedTraits, ID("deployment"))
-       assert.NotContains(t, env.ExecutedTraits, ID("service"))
-       assert.NotContains(t, env.ExecutedTraits, ID("route"))
-       assert.Contains(t, env.ExecutedTraits, ID("owner"))
+       assert.NotNil(t, env.GetTrait(ID("deployment")))
+       assert.Nil(t, env.GetTrait(ID("service")))
+       assert.Nil(t, env.GetTrait(ID("route")))
+       assert.NotNil(t, env.GetTrait(ID("owner")))
        assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool {
                return cm.Name == TestProperties
        }))
@@ -122,10 +123,10 @@ func TestKubernetesTraits(t *testing.T) {
 func TestKubernetesTraitsWithWeb(t *testing.T) {
        env := createTestEnv(v1alpha1.IntegrationPlatformClusterKubernetes, 
"from('servlet:http').to('log:info')")
        res := processTestEnv(t, env)
-       assert.Contains(t, env.ExecutedTraits, ID("deployment"))
-       assert.Contains(t, env.ExecutedTraits, ID("service"))
-       assert.NotContains(t, env.ExecutedTraits, ID("route"))
-       assert.Contains(t, env.ExecutedTraits, ID("owner"))
+       assert.NotNil(t, env.GetTrait(ID("deployment")))
+       assert.NotNil(t, env.GetTrait(ID("service")))
+       assert.Nil(t, env.GetTrait(ID("route")))
+       assert.NotNil(t, env.GetTrait(ID("owner")))
        assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool {
                return cm.Name == TestProperties
        }))
@@ -154,7 +155,8 @@ func TestTraitDecode(t *testing.T) {
 
        assert.Nil(t, err)
        assert.Equal(t, 7071, svc.Port)
-       assert.Equal(t, false, svc.IsEnabled())
+       assert.NotNil(t, svc.Enabled)
+       assert.Equal(t, false, *svc.Enabled)
 }
 
 func processTestEnv(t *testing.T, env *Environment) *kubernetes.Collection {
@@ -191,7 +193,7 @@ func createTestEnv(cluster 
v1alpha1.IntegrationPlatformCluster, script string) *
                        },
                },
                EnvVars:        make(map[string]string),
-               ExecutedTraits: make([]ID, 0),
+               ExecutedTraits: make([]Trait, 0),
                Resources:      kubernetes.NewCollection(),
        }
 }
diff --git a/pkg/trait/types.go b/pkg/trait/types.go
index d61f352..a81f2f4 100644
--- a/pkg/trait/types.go
+++ b/pkg/trait/types.go
@@ -35,16 +35,12 @@ type ID string
 // Trait is the interface of all traits
 type Trait interface {
        Identifiable
-       // IsEnabled tells if the trait is enabled
-       IsEnabled() bool
-       // 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
-       apply(environment *Environment) error
+
+       // Configure the trait
+       Configure(environment *Environment) (bool, error)
+
+       // Apply executes a customization of the Environment
+       Apply(environment *Environment) error
 }
 
 /* Base trait */
@@ -53,13 +49,6 @@ type Trait interface {
 type BaseTrait struct {
        id      ID
        Enabled *bool `property:"enabled"`
-       Auto    *bool `property:"auto"`
-}
-
-func newBaseTrait(id string) BaseTrait {
-       return BaseTrait{
-               id: ID(id),
-       }
 }
 
 // ID returns the identifier of the trait
@@ -67,26 +56,6 @@ func (trait *BaseTrait) ID() ID {
        return trait.id
 }
 
-// IsAuto determines if we should apply automatic configuration
-func (trait *BaseTrait) IsAuto() bool {
-       if trait.Auto == nil {
-               return true
-       }
-       return *trait.Auto
-}
-
-// IsEnabled is used to determine if the trait needs to be executed
-func (trait *BaseTrait) IsEnabled() bool {
-       if trait.Enabled == nil {
-               return true
-       }
-       return *trait.Enabled
-}
-
-func (trait *BaseTrait) autoconfigure(environment *Environment) error {
-       return nil
-}
-
 /* Environment */
 
 // A Environment provides the context where the trait is executed
@@ -97,10 +66,21 @@ type Environment struct {
        Resources      *kubernetes.Collection
        Steps          []builder.Step
        BuildDir       string
-       ExecutedTraits []ID
+       ExecutedTraits []Trait
        EnvVars        map[string]string
 }
 
+// GetTrait --
+func (e *Environment) GetTrait(id ID) Trait {
+       for _, t := range e.ExecutedTraits {
+               if t.ID() == id {
+                       return t
+               }
+       }
+
+       return nil
+}
+
 // IntegrationInPhase --
 func (e *Environment) IntegrationInPhase(phase v1alpha1.IntegrationPhase) bool 
{
        return e.Integration != nil && e.Integration.Status.Phase == phase

Reply via email to