git.net

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GitHub] nicolaferraro closed pull request #233: trait: add builder trait


nicolaferraro closed pull request #233: trait: add builder trait
URL: https://github.com/apache/camel-k/pull/233
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/pkg/apis/camel/v1alpha1/types.go b/pkg/apis/camel/v1alpha1/types.go
index f8a11c3f..b872ac3c 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 33eac314..0cf38ed4 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
 }
@@ -79,6 +84,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 d0a8c0ec..00000000
--- 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 afc92683..d208c205 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 cc341016..d97cfce6 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/context/initialize.go b/pkg/stub/action/context/initialize.go
index 3161dd9d..f9e8d04d 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/stub/action/integration/deploy.go b/pkg/stub/action/integration/deploy.go
index 134a74df..1ad22abe 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/builder.go b/pkg/trait/builder.go
new file mode 100644
index 00000000..c8c4a508
--- /dev/null
+++ b/pkg/trait/builder.go
@@ -0,0 +1,50 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package trait
+
+import (
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"github.com/apache/camel-k/pkg/builder/kaniko"
+	"github.com/apache/camel-k/pkg/builder/s2i"
+	"github.com/apache/camel-k/pkg/platform"
+)
+
+// 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 newBuilderTrait() *builderTrait {
+	return &builderTrait{
+		BaseTrait: newBaseTrait("builder"),
+	}
+}
+
+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) {
+		e.Steps = kaniko.DefaultSteps
+	}
+
+	return nil
+}
diff --git a/pkg/trait/builder_test.go b/pkg/trait/builder_test.go
new file mode 100644
index 00000000..ef35e96c
--- /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(),
+	}
+}
diff --git a/pkg/trait/catalog.go b/pkg/trait/catalog.go
index 7ddeceae..9db06abd 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,22 +91,28 @@ 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 {
+		if !trait.appliesTo(environment) {
+			continue
+		}
+
 		if trait.IsAuto() {
 			if err := trait.autoconfigure(environment); err != nil {
 				return err
@@ -124,7 +138,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 d55c059f..f284678d 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 05df9eee..80230508 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,11 +89,11 @@ 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
-	// 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 660de260..b40d74f8 100644
--- a/pkg/trait/ingress.go
+++ b/pkg/trait/ingress.go
@@ -39,7 +39,11 @@ func newIngressTrait() *ingressTrait {
 	}
 }
 
-func (i *ingressTrait) autoconfigure(e *environment) error {
+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
 		hasHost := i.Host != ""
@@ -49,11 +53,7 @@ func (i *ingressTrait) autoconfigure(e *environment) error {
 	return nil
 }
 
-func (i *ingressTrait) apply(e *environment) error {
-	if e.Integration == nil || e.Integration.Status.Phase != v1alpha1.IntegrationPhaseDeploying {
-		return nil
-	}
-
+func (i *ingressTrait) apply(e *Environment) error {
 	if i.Host == "" {
 		return errors.New("cannot apply ingress trait: no host defined")
 	}
@@ -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 4b10c307..b122f8dd 100644
--- a/pkg/trait/knative.go
+++ b/pkg/trait/knative.go
@@ -43,7 +43,11 @@ func newKnativeTrait() *knativeTrait {
 	}
 }
 
-func (t *knativeTrait) autoconfigure(e *environment) error {
+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, ",")
@@ -51,11 +55,7 @@ func (t *knativeTrait) autoconfigure(e *environment) error {
 	return nil
 }
 
-func (t *knativeTrait) apply(e *environment) error {
-	if e.Integration == nil || e.Integration.Status.Phase != v1alpha1.IntegrationPhaseDeploying {
-		return nil
-	}
-
+func (t *knativeTrait) apply(e *Environment) error {
 	for _, sub := range t.getSubscriptionsFor(e) {
 		e.Resources.Add(sub)
 	}
@@ -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 82ba46eb..51e491ab 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 3477145b..b2ef6254 100644
--- a/pkg/trait/route.go
+++ b/pkg/trait/route.go
@@ -39,7 +39,11 @@ func newRouteTrait() *routeTrait {
 	}
 }
 
-func (r *routeTrait) autoconfigure(e *environment) error {
+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
 		r.Enabled = &hasService
@@ -47,7 +51,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 +65,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 +76,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 bc97b707..1c9b19be 100644
--- a/pkg/trait/service.go
+++ b/pkg/trait/service.go
@@ -50,7 +50,11 @@ func newServiceTrait() *serviceTrait {
 	}
 }
 
-func (s *serviceTrait) autoconfigure(e *environment) error {
+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)
 		s.Enabled = &required
@@ -58,11 +62,7 @@ func (s *serviceTrait) autoconfigure(e *environment) error {
 	return nil
 }
 
-func (s *serviceTrait) apply(e *environment) (err error) {
-	if e.Integration == nil || e.Integration.Status.Phase != v1alpha1.IntegrationPhaseDeploying {
-		return nil
-	}
-
+func (s *serviceTrait) apply(e *Environment) (err error) {
 	var svc *corev1.Service
 	if svc, err = s.getServiceFor(e); err != nil {
 		return err
@@ -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 65990857..1ab5f9e6 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 505a0dbc..17d89097 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 476cd14e..8c1d09ad 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"
 )
 
@@ -33,14 +34,16 @@ 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
-	apply(environment *environment) error
+	autoconfigure(environment *Environment) error
+	// apply executes a customization of the Environment
+	apply(environment *Environment) error
 }
 
 /* Base trait */
@@ -79,21 +82,32 @@ 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
 }
+
+// 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
+}


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@xxxxxxxxxxxxxxxx


With regards,
Apache Git Services