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

[camel-k] branch master updated (0859b86 -> 490dd40)

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

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


    from 0859b86  Add badge
     new 23d0287  Initial work on traits
     new 3bbfaad  Moving logic to the trait area
     new 8052da5  Added service trait
     new 56a7b4d  Added route trait
     new e570a93  Fixed errors during redeploy
     new 5d004a8  Added trait configuration
     new de9bcfe  Added tests
     new 0d08f72  Added tests about traits configuration
     new 6243fb7  Added trait command line config
     new 0cf1d60  Fix error handling in trait command line config
     new a87a635  Adding trait doc
     new 8eff17c  Adding doc clarification
     new 19f4c40  Formalize auto-detection
     new 490dd40  Add role-binding for changing OpenShift routes

The 14 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:
 README.adoc                                        |   7 +-
 deploy/operator-role-openshift.yaml                |  14 ++
 deploy/resources.go                                |  14 ++
 docs/traits.adoc                                   |  88 +++++++
 pkg/apis/camel/v1alpha1/types.go                   |  19 +-
 pkg/client/cmd/run.go                              |  45 ++++
 pkg/stub/action/integration/deploy.go              | 274 +--------------------
 pkg/stub/action/integration/util.go                |  83 -------
 .../action/integration/deploy.go => trait/base.go} | 159 +++---------
 pkg/trait/catalog.go                               |  82 ++++++
 pkg/{apis/camel/v1alpha1 => trait}/doc.go          |   6 +-
 pkg/trait/owner.go                                 |  48 ++++
 pkg/trait/route.go                                 |  74 ++++++
 pkg/trait/service.go                               | 103 ++++++++
 pkg/trait/trait.go                                 |  60 +++++
 pkg/trait/trait_test.go                            | 161 ++++++++++++
 pkg/trait/types.go                                 | 111 +++++++++
 pkg/{stub/action/integration => trait}/util.go     |  50 +---
 pkg/util/kubernetes/collection.go                  | 144 +++++++++++
 pkg/util/kubernetes/replace.go                     |  97 ++++++++
 runtime/examples/routes-rest.js                    |   3 +-
 21 files changed, 1119 insertions(+), 523 deletions(-)
 create mode 100644 docs/traits.adoc
 copy pkg/{stub/action/integration/deploy.go => trait/base.go} (51%)
 create mode 100644 pkg/trait/catalog.go
 copy pkg/{apis/camel/v1alpha1 => trait}/doc.go (89%)
 create mode 100644 pkg/trait/owner.go
 create mode 100644 pkg/trait/route.go
 create mode 100644 pkg/trait/service.go
 create mode 100644 pkg/trait/trait.go
 create mode 100644 pkg/trait/trait_test.go
 create mode 100644 pkg/trait/types.go
 copy pkg/{stub/action/integration => trait}/util.go (73%)
 create mode 100644 pkg/util/kubernetes/collection.go
 create mode 100644 pkg/util/kubernetes/replace.go


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

Posted by lb...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

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

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

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


[camel-k] 11/14: Adding trait doc

Posted by lb...@apache.org.
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 a87a63575737fee240b2ce827a35fa7a41cbe8c2
Author: nferraro <ni...@gmail.com>
AuthorDate: Fri Oct 5 00:37:43 2018 +0200

    Adding trait doc
---
 README.adoc      |  7 ++++-
 docs/traits.adoc | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 86 insertions(+), 1 deletion(-)

diff --git a/README.adoc b/README.adoc
index 72aac21..f969ee3 100644
--- a/README.adoc
+++ b/README.adoc
@@ -150,7 +150,7 @@ Camel K supports multiple languages for writing integrations:
 | Kotlin			| Kotlin Script `.kts` files are supported (experimental).
 |=======================
 
-More information about supported languages is provided in the link:docs/languages.adoc[lanuguages guide]
+More information about supported languages is provided in the link:docs/languages.adoc[lanuguages guide].
 
 Integrations written in different languages are provided in the link:/runtime/examples[examples] directory.
 
@@ -173,6 +173,11 @@ To run it, you need just to execute:
 kamel run runtime/examples/dns.js
 ```
 
+=== Traits
+
+The details of how the integration is mapped into Kubernetes resources can be *customized using traits*.
+More information is provided in the link:docs/traits.adoc[traits section].
+
 === Monitoring the Status
 
 Camel K integrations follow a lifecycle composed of several steps before getting into the `Running` state.
diff --git a/docs/traits.adoc b/docs/traits.adoc
new file mode 100644
index 0000000..7ceb5de
--- /dev/null
+++ b/docs/traits.adoc
@@ -0,0 +1,80 @@
+[[traits]]
+= Traits
+
+Traits are high level named features of Camel K that can be enabled/disabled or reconfigured to customize the
+behavior of the final integration.
+
+Camel K provide sensible defaults for all such traits, taking into account the details of the target platform where
+the integration is going to run into. However, it's possible for a **expert user** to configure them in
+order to obtain a different behavior.
+
+== Configuration
+
+Each trait has a unique ID that can be used to configure it using the command line tool.
+
+E.g. in order to disable the creation of a Service for a integration, a user can execute:
+
+```
+kamel run --trait service.enabled=false file.groovy
+```
+
+The flag `--trait` can be also abbreviated with `-t`.
+
+The `enabled` property is available on all traits and can be used to enable/disable them. All traits are enabled
+by default, unless they are not applicable for the current platform (see "profiles" in the table) or other conditions.
+
+Some traits have additional properties that can be configured by the end user.
+
+E.g. the following command configure the container `port` that should be exposed by the service:
+
+```
+kamel run --trait service.enabled=true --trait service.port=8081 file.groovy
+```
+
+Or the equivalent command:
+
+```
+kamel run -t service.port=8081 file.groovy
+```
+
+NOTE: Enabling a *trait* does not force the trait to be activated, especially if the trait specific preconditions do not hold.
+E.g. enabling the `route` trait while the `service` trait is disabled does not produce automatically a route, since a service is needed
+for the `route` trait to work.
+
+== Common Traits
+
+The following is a list of common traits that can be configured by the end users:
+
+[options="header",cols="1,2,3a"]
+|=======================
+| Trait      | Profiles 				| Description
+
+| service
+| Kubernetes, OpenShift
+| Exposes the integration with a Service resource so that it can be accessed by other applications (or integrations) in the same namespace.
+
+[cols="m,"]
+!===
+
+! service.port
+! To configure a different port exposed by the container (default `8080`).
+
+!===
+
+| route
+| OpenShift
+| Exposes the service associated with the integration to the outside world with a OpenShift Route.
+
+|=======================
+
+
+== Platform Traits (Advanced)
+
+There are also platform traits that **normally should not be configured** by the end user. So change them **only if you know what you're doing**.
+
+[options="header"]
+|=======================
+| Trait      | Profiles 				| Description
+| base		 | Kubernetes, OpenShift	| Creates the basic Kubernetes resource needed for running the integration.
+| owner      | Kubernetes, OpenShift	| Makes sure that every resource created by the traits belongs to the integration custom resource (so they are deleted when the integration is deleted).
+|=======================


[camel-k] 09/14: Added trait command line config

Posted by lb...@apache.org.
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 6243fb71147e8aa57cef4d46a1b4e5d02ec5c10a
Author: nferraro <ni...@gmail.com>
AuthorDate: Fri Oct 5 00:01:34 2018 +0200

    Added trait command line config
---
 pkg/client/cmd/run.go | 43 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/pkg/client/cmd/run.go b/pkg/client/cmd/run.go
index 10e75ed..afd9667 100644
--- a/pkg/client/cmd/run.go
+++ b/pkg/client/cmd/run.go
@@ -22,6 +22,7 @@ import (
 	"io/ioutil"
 	"net/http"
 	"os"
+	"regexp"
 	"strconv"
 	"strings"
 
@@ -43,6 +44,10 @@ import (
 	"k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
+var (
+	traitConfigRegexp = regexp.MustCompile("^([a-z-]+)((?:\\.[a-z-]+)+)=(.*)$")
+)
+
 func newCmdRun(rootCmdOptions *RootCmdOptions) *cobra.Command {
 	options := runCmdOptions{
 		RootCmdOptions: rootCmdOptions,
@@ -69,6 +74,7 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) *cobra.Command {
 	cmd.Flags().BoolVar(&options.Sync, "sync", false, "Synchronize the local source file with the cluster, republishing at each change")
 	cmd.Flags().BoolVar(&options.Dev, "dev", false, "Enable Dev mode (equivalent to \"-w --logs --sync\")")
 	cmd.Flags().BoolVar(&options.DependenciesAutoDiscovery, "auto-discovery", true, "Automatically discover Camel modules by analyzing user code")
+	cmd.Flags().StringSliceVarP(&options.Traits, "trait", "t", nil, "Configure a trait. E.g. \"-t service.enabled=false\"")
 
 	// completion support
 	configureKnownCompletions(&cmd)
@@ -91,6 +97,7 @@ type runCmdOptions struct {
 	Sync                      bool
 	Dev                       bool
 	DependenciesAutoDiscovery bool
+	Traits                    []string
 }
 
 func (*runCmdOptions) validateArgs(cmd *cobra.Command, args []string) error {
@@ -318,6 +325,10 @@ func (o *runCmdOptions) updateIntegrationCode(filename string) (*v1alpha1.Integr
 		})
 	}
 
+	for _, traitConf := range o.Traits {
+		o.configureTrait(&integration, traitConf)
+	}
+
 	existed := false
 	err = sdk.Create(&integration)
 	if err != nil && k8serrors.IsAlreadyExists(err) {
@@ -361,3 +372,35 @@ func (*runCmdOptions) loadCode(fileName string) (string, error) {
 	bodyString := string(bodyBytes)
 	return string(bodyString), err
 }
+
+func (*runCmdOptions) configureTrait(integration *v1alpha1.Integration, config string) error {
+	if integration.Spec.Traits == nil {
+		integration.Spec.Traits = make(map[string]v1alpha1.IntegrationTraitSpec)
+	}
+
+	parts := traitConfigRegexp.FindStringSubmatch(config)
+	if len(parts) < 4 {
+		return errors.New("unrecognized config format (expected \"<trait>.<prop>=<val>\"): " + config)
+	}
+	traitID := parts[1]
+	prop := parts[2][1:]
+	val := parts[3]
+	var spec v1alpha1.IntegrationTraitSpec
+	var ok bool
+	if spec, ok = integration.Spec.Traits[traitID]; !ok {
+		spec = v1alpha1.IntegrationTraitSpec{
+			Configuration: make(map[string]string),
+		}
+	}
+	if prop == "enabled" {
+		boolVal, err := strconv.ParseBool(val)
+		if err != nil {
+			return errors.Wrap(err, "cannot parse bool value "+val)
+		}
+		spec.Enabled = &boolVal
+	} else {
+		spec.Configuration[prop] = val
+	}
+	integration.Spec.Traits[traitID] = spec
+	return nil
+}


[camel-k] 07/14: Added tests

Posted by lb...@apache.org.
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 de9bcfe2825da5ffd98367a12ea9e8cb8a5ded59
Author: nferraro <ni...@gmail.com>
AuthorDate: Thu Oct 4 23:11:58 2018 +0200

    Added tests
---
 pkg/trait/base.go                 |   6 +-
 pkg/trait/catalog.go              |   4 +-
 pkg/trait/owner.go                |   2 +-
 pkg/trait/route.go                |   4 +-
 pkg/trait/service.go              |   6 +-
 pkg/trait/trait.go                |   4 +-
 pkg/trait/trait_test.go           | 127 ++++++++++++++++++++++++++++++++++++++
 pkg/trait/types.go                |  14 ++---
 pkg/util/kubernetes/collection.go |  54 ++++++++++++++++
 9 files changed, 201 insertions(+), 20 deletions(-)

diff --git a/pkg/trait/base.go b/pkg/trait/base.go
index d5eea62..3432b93 100644
--- a/pkg/trait/base.go
+++ b/pkg/trait/base.go
@@ -34,7 +34,7 @@ func (*baseTrait) id() id {
 	return id("base")
 }
 
-func (d *baseTrait) customize(environment environment, resources *kubernetes.Collection) (bool, error) {
+func (d *baseTrait) customize(environment *environment, resources *kubernetes.Collection) (bool, error) {
 	resources.Add(d.getConfigMapFor(environment))
 	resources.Add(d.getDeploymentFor(environment))
 	return true, nil
@@ -46,7 +46,7 @@ func (d *baseTrait) customize(environment environment, resources *kubernetes.Col
 //
 // **********************************
 
-func (*baseTrait) getConfigMapFor(e environment) *corev1.ConfigMap {
+func (*baseTrait) getConfigMapFor(e *environment) *corev1.ConfigMap {
 	// combine properties of integration with context, integration
 	// properties have the priority
 	properties := CombineConfigurationAsMap("property", e.Context, e.Integration)
@@ -82,7 +82,7 @@ func (*baseTrait) getConfigMapFor(e environment) *corev1.ConfigMap {
 //
 // **********************************
 
-func (*baseTrait) getDeploymentFor(e environment) *appsv1.Deployment {
+func (*baseTrait) 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/catalog.go b/pkg/trait/catalog.go
index a2ba3b5..df8ad07 100644
--- a/pkg/trait/catalog.go
+++ b/pkg/trait/catalog.go
@@ -30,7 +30,7 @@ var (
 )
 
 // customizersFor returns a Catalog for the given integration details
-func customizersFor(environment environment) customizer {
+func customizersFor(environment *environment) customizer {
 	switch environment.Platform.Spec.Cluster {
 	case v1alpha1.IntegrationPlatformClusterOpenShift:
 		return compose(
@@ -66,7 +66,7 @@ func (c *chainedCustomizer) id() id {
 	return id("")
 }
 
-func (c *chainedCustomizer) customize(environment environment, resources *kubernetes.Collection) (bool, error) {
+func (c *chainedCustomizer) customize(environment *environment, resources *kubernetes.Collection) (bool, error) {
 	atLeastOne := false
 	for _, custom := range c.customizers {
 		if environment.isEnabled(custom.id()) {
diff --git a/pkg/trait/owner.go b/pkg/trait/owner.go
index 6d77ff9..8603bd5 100644
--- a/pkg/trait/owner.go
+++ b/pkg/trait/owner.go
@@ -28,7 +28,7 @@ func (*ownerTrait) id() id {
 	return id("owner")
 }
 
-func (*ownerTrait) customize(e environment, resources *kubernetes.Collection) (bool, error) {
+func (*ownerTrait) customize(e *environment, resources *kubernetes.Collection) (bool, error) {
 	controller := true
 	blockOwnerDeletion := true
 	resources.VisitMetaObject(func(res metav1.Object) {
diff --git a/pkg/trait/route.go b/pkg/trait/route.go
index ded0fe3..acf6984 100644
--- a/pkg/trait/route.go
+++ b/pkg/trait/route.go
@@ -32,7 +32,7 @@ func (*routeTrait) id() id {
 	return id("route")
 }
 
-func (e *routeTrait) customize(environment environment, resources *kubernetes.Collection) (bool, error) {
+func (e *routeTrait) customize(environment *environment, resources *kubernetes.Collection) (bool, error) {
 	var service *corev1.Service
 	resources.VisitService(func(s *corev1.Service) {
 		if s.ObjectMeta.Labels != nil {
@@ -50,7 +50,7 @@ func (e *routeTrait) customize(environment environment, resources *kubernetes.Co
 	return false, nil
 }
 
-func (*routeTrait) getRouteFor(e environment, service *corev1.Service) *routev1.Route {
+func (*routeTrait) getRouteFor(e *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 d7d116a..f52d08d 100644
--- a/pkg/trait/service.go
+++ b/pkg/trait/service.go
@@ -45,7 +45,7 @@ func (*serviceTrait) id() id {
 	return id("service")
 }
 
-func (e *serviceTrait) customize(environment environment, resources *kubernetes.Collection) (bool, error) {
+func (e *serviceTrait) customize(environment *environment, resources *kubernetes.Collection) (bool, error) {
 	if !e.requiresService(environment) {
 		return false, nil
 	}
@@ -57,7 +57,7 @@ func (e *serviceTrait) customize(environment environment, resources *kubernetes.
 	return true, nil
 }
 
-func (s *serviceTrait) getServiceFor(e environment) (*corev1.Service, error) {
+func (s *serviceTrait) getServiceFor(e *environment) (*corev1.Service, error) {
 	port, err := e.getIntConfigOr(s.id(), serviceTraitPortKey, 8080)
 	if err != nil {
 		return nil, err
@@ -93,7 +93,7 @@ func (s *serviceTrait) getServiceFor(e environment) (*corev1.Service, error) {
 	return &svc, nil
 }
 
-func (*serviceTrait) requiresService(environment environment) bool {
+func (*serviceTrait) requiresService(environment *environment) bool {
 	for _, dep := range environment.Integration.Spec.Dependencies {
 		if decision, present := webComponents[dep]; present {
 			return decision
diff --git a/pkg/trait/trait.go b/pkg/trait/trait.go
index a2b83c6..db3c56f 100644
--- a/pkg/trait/trait.go
+++ b/pkg/trait/trait.go
@@ -32,9 +32,9 @@ func ComputeDeployment(integration *v1alpha1.Integration) ([]runtime.Object, err
 		return nil, err
 	}
 	resources := kubernetes.NewCollection()
-	customizers := customizersFor(*environment)
+	customizers := customizersFor(environment)
 	// invoke the trait framework to determine the needed resources
-	if _, err = customizers.customize(*environment, resources); err != nil {
+	if _, err = customizers.customize(environment, resources); err != nil {
 		return nil, errors.Wrap(err, "error during trait customization")
 	}
 	return resources.Items(), nil
diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go
new file mode 100644
index 0000000..9ffea3a
--- /dev/null
+++ b/pkg/trait/trait_test.go
@@ -0,0 +1,127 @@
+/*
+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/util/kubernetes"
+	routev1 "github.com/openshift/api/route/v1"
+	"github.com/stretchr/testify/assert"
+	appsv1 "k8s.io/api/apps/v1"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"testing"
+)
+
+func TestOpenshiftTraits(t *testing.T) {
+	env := createTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, "camel:core")
+	res := processTestEnv(t, env)
+	assert.Contains(t, env.ExecutedCustomizers, id("base"))
+	assert.NotContains(t, env.ExecutedCustomizers, id("service"))
+	assert.NotContains(t, env.ExecutedCustomizers, id("route"))
+	assert.Contains(t, env.ExecutedCustomizers, id("owner"))
+	assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool {
+		return cm.Name == "test"
+	}))
+	assert.NotNil(t, res.GetDeployment(func(deployment *appsv1.Deployment) bool {
+		return deployment.Name == "test"
+	}))
+}
+
+func TestOpenshiftTraitsWithWeb(t *testing.T) {
+	env := createTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, "camel:core", "camel:undertow")
+	res := processTestEnv(t, env)
+	assert.Contains(t, env.ExecutedCustomizers, id("base"))
+	assert.Contains(t, env.ExecutedCustomizers, id("service"))
+	assert.Contains(t, env.ExecutedCustomizers, id("route"))
+	assert.Contains(t, env.ExecutedCustomizers, id("owner"))
+	assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool {
+		return cm.Name == "test"
+	}))
+	assert.NotNil(t, res.GetDeployment(func(deployment *appsv1.Deployment) bool {
+		return deployment.Name == "test"
+	}))
+	assert.NotNil(t, res.GetService(func(svc *corev1.Service) bool {
+		return svc.Name == "test"
+	}))
+	assert.NotNil(t, res.GetRoute(func(svc *routev1.Route) bool {
+		return svc.Name == "test"
+	}))
+}
+
+func TestKubernetesTraits(t *testing.T) {
+	env := createTestEnv(v1alpha1.IntegrationPlatformClusterKubernetes, "camel:core")
+	res := processTestEnv(t, env)
+	assert.Contains(t, env.ExecutedCustomizers, id("base"))
+	assert.NotContains(t, env.ExecutedCustomizers, id("service"))
+	assert.NotContains(t, env.ExecutedCustomizers, id("route"))
+	assert.Contains(t, env.ExecutedCustomizers, id("owner"))
+	assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool {
+		return cm.Name == "test"
+	}))
+	assert.NotNil(t, res.GetDeployment(func(deployment *appsv1.Deployment) bool {
+		return deployment.Name == "test"
+	}))
+}
+
+func TestKubernetesTraitsWithWeb(t *testing.T) {
+	env := createTestEnv(v1alpha1.IntegrationPlatformClusterKubernetes, "camel:core", "camel:servlet")
+	res := processTestEnv(t, env)
+	assert.Contains(t, env.ExecutedCustomizers, id("base"))
+	assert.Contains(t, env.ExecutedCustomizers, id("service"))
+	assert.NotContains(t, env.ExecutedCustomizers, id("route"))
+	assert.Contains(t, env.ExecutedCustomizers, id("owner"))
+	assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool {
+		return cm.Name == "test"
+	}))
+	assert.NotNil(t, res.GetDeployment(func(deployment *appsv1.Deployment) bool {
+		return deployment.Name == "test"
+	}))
+	assert.NotNil(t, res.GetService(func(svc *corev1.Service) bool {
+		return svc.Name == "test"
+	}))
+}
+
+func processTestEnv(t *testing.T, env *environment) *kubernetes.Collection {
+	resources := kubernetes.NewCollection()
+	customizers := customizersFor(env)
+	_, err := customizers.customize(env, resources)
+	assert.Nil(t, err)
+	return resources
+}
+
+func createTestEnv(cluster v1alpha1.IntegrationPlatformCluster, dependencies ...string) *environment {
+	return &environment{
+		Integration: &v1alpha1.Integration{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "test",
+				Namespace: "ns",
+			},
+			Spec: v1alpha1.IntegrationSpec{
+				Dependencies: dependencies,
+			},
+		},
+		Context: &v1alpha1.IntegrationContext{},
+		Platform: &v1alpha1.IntegrationPlatform{
+			Spec: v1alpha1.IntegrationPlatformSpec{
+				Cluster: cluster,
+			},
+		},
+		ExecutedCustomizers: make([]id, 0),
+	}
+}
diff --git a/pkg/trait/types.go b/pkg/trait/types.go
index dd8c269..ad002e1 100644
--- a/pkg/trait/types.go
+++ b/pkg/trait/types.go
@@ -32,7 +32,7 @@ type customizer interface {
 	// The Name of the customizer
 	id() id
 	// Customize executes the trait customization on the resources and return true if the resources have been changed
-	customize(environment environment, resources *kubernetes.Collection) (bool, error)
+	customize(environment *environment, resources *kubernetes.Collection) (bool, error)
 }
 
 // A environment provides the context where the trait is executed
@@ -43,7 +43,7 @@ type environment struct {
 	ExecutedCustomizers []id
 }
 
-func (e environment) getTraitSpec(traitID id) *v1alpha1.IntegrationTraitSpec {
+func (e *environment) getTraitSpec(traitID id) *v1alpha1.IntegrationTraitSpec {
 	if e.Integration.Spec.Traits == nil {
 		return nil
 	}
@@ -53,12 +53,12 @@ func (e environment) getTraitSpec(traitID id) *v1alpha1.IntegrationTraitSpec {
 	return nil
 }
 
-func (e environment) isEnabled(traitID id) bool {
+func (e *environment) isEnabled(traitID id) bool {
 	conf := e.getTraitSpec(traitID)
 	return conf == nil || conf.Enabled == nil || *conf.Enabled
 }
 
-func (e environment) getConfig(traitID id, key string) *string {
+func (e *environment) getConfig(traitID id, key string) *string {
 	conf := e.getTraitSpec(traitID)
 	if conf == nil || conf.Configuration == nil {
 		return nil
@@ -69,7 +69,7 @@ func (e environment) getConfig(traitID id, key string) *string {
 	return nil
 }
 
-func (e environment) getConfigOr(traitID id, key string, defaultValue string) string {
+func (e *environment) getConfigOr(traitID id, key string, defaultValue string) string {
 	val := e.getConfig(traitID, key)
 	if val != nil {
 		return *val
@@ -77,7 +77,7 @@ func (e environment) getConfigOr(traitID id, key string, defaultValue string) st
 	return defaultValue
 }
 
-func (e environment) getIntConfig(traitID id, key string) (*int, error) {
+func (e *environment) getIntConfig(traitID id, key string) (*int, error) {
 	val := e.getConfig(traitID, key)
 	if val == nil {
 		return nil, nil
@@ -89,7 +89,7 @@ func (e environment) getIntConfig(traitID id, key string) (*int, error) {
 	return &intVal, nil
 }
 
-func (e environment) getIntConfigOr(traitID id, key string, defaultValue int) (int, error) {
+func (e *environment) getIntConfigOr(traitID id, key string, defaultValue int) (int, error) {
 	val, err := e.getIntConfig(traitID, key)
 	if err != nil {
 		return 0, err
diff --git a/pkg/util/kubernetes/collection.go b/pkg/util/kubernetes/collection.go
index 0b5f217..8f099c3 100644
--- a/pkg/util/kubernetes/collection.go
+++ b/pkg/util/kubernetes/collection.go
@@ -18,6 +18,7 @@ limitations under the License.
 package kubernetes
 
 import (
+	routev1 "github.com/openshift/api/route/v1"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -55,6 +56,17 @@ func (c *Collection) VisitDeployment(visitor func(*appsv1.Deployment)) {
 	})
 }
 
+// GetDeployment returns a Deployment that matches the given function
+func (c *Collection) GetDeployment(filter func(*appsv1.Deployment)bool) *appsv1.Deployment {
+	var retValue *appsv1.Deployment
+	c.VisitDeployment(func(re *appsv1.Deployment) {
+		if filter(re) {
+			retValue = re
+		}
+	})
+	return retValue
+}
+
 // VisitConfigMap executes the visitor function on all ConfigMap resources
 func (c *Collection) VisitConfigMap(visitor func(*corev1.ConfigMap)) {
 	c.Visit(func(res runtime.Object) {
@@ -64,6 +76,17 @@ func (c *Collection) VisitConfigMap(visitor func(*corev1.ConfigMap)) {
 	})
 }
 
+// GetConfigMap returns a ConfigMap that matches the given function
+func (c *Collection) GetConfigMap(filter func(*corev1.ConfigMap)bool) *corev1.ConfigMap {
+	var retValue *corev1.ConfigMap
+	c.VisitConfigMap(func(re *corev1.ConfigMap) {
+		if filter(re) {
+			retValue = re
+		}
+	})
+	return retValue
+}
+
 // VisitService executes the visitor function on all Service resources
 func (c *Collection) VisitService(visitor func(*corev1.Service)) {
 	c.Visit(func(res runtime.Object) {
@@ -73,6 +96,37 @@ func (c *Collection) VisitService(visitor func(*corev1.Service)) {
 	})
 }
 
+// GetService returns a Service that matches the given function
+func (c *Collection) GetService(filter func(*corev1.Service)bool) *corev1.Service {
+	var retValue *corev1.Service
+	c.VisitService(func(re *corev1.Service) {
+		if filter(re) {
+			retValue = re
+		}
+	})
+	return retValue
+}
+
+// VisitRoute executes the visitor function on all Route resources
+func (c *Collection) VisitRoute(visitor func(*routev1.Route)) {
+	c.Visit(func(res runtime.Object) {
+		if conv, ok := res.(*routev1.Route); ok {
+			visitor(conv)
+		}
+	})
+}
+
+// GetRoute returns a Route that matches the given function
+func (c *Collection) GetRoute(filter func(*routev1.Route)bool) *routev1.Route {
+	var retValue *routev1.Route
+	c.VisitRoute(func(re *routev1.Route) {
+		if filter(re) {
+			retValue = re
+		}
+	})
+	return retValue
+}
+
 // VisitMetaObject executes the visitor function on all meta.Object resources
 func (c *Collection) VisitMetaObject(visitor func(metav1.Object)) {
 	c.Visit(func(res runtime.Object) {


[camel-k] 08/14: Added tests about traits configuration

Posted by lb...@apache.org.
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 0d08f7298b9fee7283edb65611c50568d6d5971a
Author: nferraro <ni...@gmail.com>
AuthorDate: Thu Oct 4 23:19:03 2018 +0200

    Added tests about traits configuration
---
 pkg/trait/trait_test.go | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go
index 9ffea3a..8427068 100644
--- a/pkg/trait/trait_test.go
+++ b/pkg/trait/trait_test.go
@@ -64,6 +64,40 @@ func TestOpenshiftTraitsWithWeb(t *testing.T) {
 	}))
 }
 
+func TestOpenshiftTraitsWithWebAndConfig(t *testing.T) {
+	env := createTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, "camel:core", "camel:undertow")
+	env.Integration.Spec.Traits = make(map[string]v1alpha1.IntegrationTraitSpec)
+	env.Integration.Spec.Traits["service"] = v1alpha1.IntegrationTraitSpec{
+		Configuration: map[string]string{
+			"port": "7071",
+		},
+	}
+	res := processTestEnv(t, env)
+	assert.Contains(t, env.ExecutedCustomizers, id("service"))
+	assert.Contains(t, env.ExecutedCustomizers, id("route"))
+	assert.NotNil(t, res.GetService(func(svc *corev1.Service) bool {
+		return svc.Name == "test" && svc.Spec.Ports[0].TargetPort.IntVal == int32(7071)
+	}))
+}
+
+func TestOpenshiftTraitsWithWebAndDisabledTrait(t *testing.T) {
+	falseBoolean := false
+	env := createTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, "camel:core", "camel:undertow")
+	env.Integration.Spec.Traits = make(map[string]v1alpha1.IntegrationTraitSpec)
+	env.Integration.Spec.Traits["service"] = v1alpha1.IntegrationTraitSpec{
+		Configuration: map[string]string{
+			"port": "7071",
+		},
+		Enabled: &falseBoolean,
+	}
+	res := processTestEnv(t, env)
+	assert.NotContains(t, env.ExecutedCustomizers, id("service"))
+	assert.NotContains(t, env.ExecutedCustomizers, id("route")) // No route without service
+	assert.Nil(t, res.GetService(func(svc *corev1.Service) bool {
+		return true
+	}))
+}
+
 func TestKubernetesTraits(t *testing.T) {
 	env := createTestEnv(v1alpha1.IntegrationPlatformClusterKubernetes, "camel:core")
 	res := processTestEnv(t, env)


[camel-k] 10/14: Fix error handling in trait command line config

Posted by lb...@apache.org.
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 0cf1d601ee85a9ebf356562a22cef45b46f848e7
Author: nferraro <ni...@gmail.com>
AuthorDate: Fri Oct 5 00:04:15 2018 +0200

    Fix error handling in trait command line config
---
 pkg/client/cmd/run.go | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/pkg/client/cmd/run.go b/pkg/client/cmd/run.go
index afd9667..30f6fa8 100644
--- a/pkg/client/cmd/run.go
+++ b/pkg/client/cmd/run.go
@@ -326,7 +326,9 @@ func (o *runCmdOptions) updateIntegrationCode(filename string) (*v1alpha1.Integr
 	}
 
 	for _, traitConf := range o.Traits {
-		o.configureTrait(&integration, traitConf)
+		if err := o.configureTrait(&integration, traitConf); err != nil {
+			return nil, err
+		}
 	}
 
 	existed := false


[camel-k] 14/14: Add role-binding for changing OpenShift routes

Posted by lb...@apache.org.
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 490dd401d4e48f15b06388860c0dc1dc7a6a3fa3
Author: nferraro <ni...@gmail.com>
AuthorDate: Fri Oct 5 10:46:12 2018 +0200

    Add role-binding for changing OpenShift routes
---
 deploy/operator-role-openshift.yaml | 14 ++++++++++++++
 deploy/resources.go                 | 14 ++++++++++++++
 2 files changed, 28 insertions(+)

diff --git a/deploy/operator-role-openshift.yaml b/deploy/operator-role-openshift.yaml
index 1cd659b..788a7cc 100644
--- a/deploy/operator-role-openshift.yaml
+++ b/deploy/operator-role-openshift.yaml
@@ -105,3 +105,17 @@ rules:
   - builds/clone
   verbs:
   - create
+- apiGroups:
+  - ""
+  - "route.openshift.io"
+  resources:
+  - routes
+  verbs:
+  - create
+  - delete
+  - deletecollection
+  - get
+  - list
+  - patch
+  - update
+  - watch
diff --git a/deploy/resources.go b/deploy/resources.go
index b30cd40..c3ec245 100644
--- a/deploy/resources.go
+++ b/deploy/resources.go
@@ -2484,6 +2484,20 @@ rules:
   - builds/clone
   verbs:
   - create
+- apiGroups:
+  - ""
+  - "route.openshift.io"
+  resources:
+  - routes
+  verbs:
+  - create
+  - delete
+  - deletecollection
+  - get
+  - list
+  - patch
+  - update
+  - watch
 
 `
 	Resources["operator-service-account.yaml"] =


[camel-k] 06/14: Added trait configuration

Posted by lb...@apache.org.
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 5d004a81f682aa55ea59989e3b3636228b936548
Author: nferraro <ni...@gmail.com>
AuthorDate: Thu Oct 4 18:13:52 2018 +0200

    Added trait configuration
---
 pkg/stub/action/integration/deploy.go |  11 +---
 pkg/trait/base.go                     |  10 ++--
 pkg/trait/catalog.go                  |  59 ++++++++------------
 pkg/trait/identity.go                 |  33 -----------
 pkg/trait/owner.go                    |   6 +-
 pkg/trait/route.go                    |   8 +--
 pkg/trait/service.go                  |  37 ++++++++-----
 pkg/trait/trait.go                    |  40 +++++++-------
 pkg/trait/types.go                    | 101 ++++++++++++++++++++++++++++++++++
 9 files changed, 181 insertions(+), 124 deletions(-)

diff --git a/pkg/stub/action/integration/deploy.go b/pkg/stub/action/integration/deploy.go
index 9790bf8..2c814bc 100644
--- a/pkg/stub/action/integration/deploy.go
+++ b/pkg/stub/action/integration/deploy.go
@@ -22,7 +22,6 @@ import (
 	"github.com/apache/camel-k/pkg/trait"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 	"github.com/operator-framework/operator-sdk/pkg/sdk"
-	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 )
 
@@ -43,18 +42,12 @@ func (action *deployAction) CanHandle(integration *v1alpha1.Integration) bool {
 }
 
 func (action *deployAction) Handle(integration *v1alpha1.Integration) error {
-	environment, err := trait.NewEnvironment(integration)
+	resources, err := trait.ComputeDeployment(integration)
 	if err != nil {
 		return err
 	}
-	resources := kubernetes.NewCollection()
-	customizers := trait.CustomizersFor(*environment)
-	// invoke the trait framework to determine the needed resources
-	if _, err = customizers.Customize(*environment, resources); err != nil {
-		return errors.Wrap(err, "error during trait customization")
-	}
 	// TODO we should look for objects that are no longer present in the collection and remove them
-	err = kubernetes.ReplaceResources(resources.Items())
+	err = kubernetes.ReplaceResources(resources)
 	if err != nil {
 		return err
 	}
diff --git a/pkg/trait/base.go b/pkg/trait/base.go
index 6ba4dfc..d5eea62 100644
--- a/pkg/trait/base.go
+++ b/pkg/trait/base.go
@@ -30,11 +30,11 @@ import (
 type baseTrait struct {
 }
 
-func (*baseTrait) ID() ID {
-	return ID("base")
+func (*baseTrait) id() id {
+	return id("base")
 }
 
-func (d *baseTrait) Customize(environment Environment, resources *kubernetes.Collection) (bool, error) {
+func (d *baseTrait) customize(environment environment, resources *kubernetes.Collection) (bool, error) {
 	resources.Add(d.getConfigMapFor(environment))
 	resources.Add(d.getDeploymentFor(environment))
 	return true, nil
@@ -46,7 +46,7 @@ func (d *baseTrait) Customize(environment Environment, resources *kubernetes.Col
 //
 // **********************************
 
-func (*baseTrait) getConfigMapFor(e Environment) *corev1.ConfigMap {
+func (*baseTrait) getConfigMapFor(e environment) *corev1.ConfigMap {
 	// combine properties of integration with context, integration
 	// properties have the priority
 	properties := CombineConfigurationAsMap("property", e.Context, e.Integration)
@@ -82,7 +82,7 @@ func (*baseTrait) getConfigMapFor(e Environment) *corev1.ConfigMap {
 //
 // **********************************
 
-func (*baseTrait) getDeploymentFor(e Environment) *appsv1.Deployment {
+func (*baseTrait) 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/catalog.go b/pkg/trait/catalog.go
index a969012..a2ba3b5 100644
--- a/pkg/trait/catalog.go
+++ b/pkg/trait/catalog.go
@@ -23,14 +23,14 @@ import (
 )
 
 var (
-	tBase = &baseTrait{}
+	tBase    = &baseTrait{}
 	tService = &serviceTrait{}
-	tRoute = &routeTrait{}
-	tOwner = &ownerTrait{}
+	tRoute   = &routeTrait{}
+	tOwner   = &ownerTrait{}
 )
 
-// CustomizersFor returns a Catalog for the given integration details
-func CustomizersFor(environment Environment) Customizer {
+// customizersFor returns a Catalog for the given integration details
+func customizersFor(environment environment) customizer {
 	switch environment.Platform.Spec.Cluster {
 	case v1alpha1.IntegrationPlatformClusterOpenShift:
 		return compose(
@@ -50,46 +50,33 @@ func CustomizersFor(environment Environment) Customizer {
 	return nil
 }
 
-func compose(traits ...Customizer) Customizer {
-	if len(traits) == 0 {
-		return &identityTrait{}
-	} else if len(traits) == 1 {
-		return traits[0]
+func compose(traits ...customizer) customizer {
+	return &chainedCustomizer{
+		customizers: traits,
 	}
-	var composite Customizer = &identityTrait{}
-	for _, t := range traits {
-		composite = &chainedCustomizer{
-			t1: composite,
-			t2: t,
-		}
-	}
-	return composite
 }
 
 // -------------------------------------------
 
 type chainedCustomizer struct {
-	t1 Customizer
-	t2 Customizer
+	customizers []customizer
 }
 
-func (c *chainedCustomizer) ID() ID {
-	return ID("")
+func (c *chainedCustomizer) id() id {
+	return id("")
 }
 
-func (c *chainedCustomizer) Customize(environment Environment, resources *kubernetes.Collection) (bool, error) {
-	atLeastOnce := false
-	var done bool
-	var err error
-	if done, err = c.t1.Customize(environment, resources); err != nil {
-		return false, err
-	} else if done && c.t1.ID() != "" {
-		environment.ExecutedCustomizers = append(environment.ExecutedCustomizers, c.t1.ID())
-	}
-	atLeastOnce = atLeastOnce || done
-	done2, err := c.t2.Customize(environment, resources)
-	if done2 && c.t2.ID() != "" {
-		environment.ExecutedCustomizers = append(environment.ExecutedCustomizers, c.t2.ID())
+func (c *chainedCustomizer) customize(environment environment, resources *kubernetes.Collection) (bool, error) {
+	atLeastOne := false
+	for _, custom := range c.customizers {
+		if environment.isEnabled(custom.id()) {
+			if done, err := custom.customize(environment, resources); err != nil {
+				return false, err
+			} else if done && custom.id() != "" {
+				environment.ExecutedCustomizers = append(environment.ExecutedCustomizers, custom.id())
+				atLeastOne = atLeastOne || done
+			}
+		}
 	}
-	return atLeastOnce || done2, err
+	return atLeastOne, nil
 }
diff --git a/pkg/trait/identity.go b/pkg/trait/identity.go
deleted file mode 100644
index eda9d3a..0000000
--- a/pkg/trait/identity.go
+++ /dev/null
@@ -1,33 +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 trait
-
-import (
-	"github.com/apache/camel-k/pkg/util/kubernetes"
-)
-
-type identityTrait struct {
-}
-
-func (*identityTrait) ID() ID {
-	return ID("identity")
-}
-
-func (*identityTrait) Customize(environment Environment, resources *kubernetes.Collection) (bool, error) {
-	return false, nil
-}
diff --git a/pkg/trait/owner.go b/pkg/trait/owner.go
index 905800b..6d77ff9 100644
--- a/pkg/trait/owner.go
+++ b/pkg/trait/owner.go
@@ -24,11 +24,11 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 type ownerTrait struct {
 }
 
-func (*ownerTrait) ID() ID {
-	return ID("identity")
+func (*ownerTrait) id() id {
+	return id("owner")
 }
 
-func (*ownerTrait) Customize(e Environment, resources *kubernetes.Collection) (bool, error) {
+func (*ownerTrait) customize(e environment, resources *kubernetes.Collection) (bool, error) {
 	controller := true
 	blockOwnerDeletion := true
 	resources.VisitMetaObject(func(res metav1.Object) {
diff --git a/pkg/trait/route.go b/pkg/trait/route.go
index 1906442..ded0fe3 100644
--- a/pkg/trait/route.go
+++ b/pkg/trait/route.go
@@ -28,11 +28,11 @@ import (
 type routeTrait struct {
 }
 
-func (*routeTrait) ID() ID {
-	return ID("route")
+func (*routeTrait) id() id {
+	return id("route")
 }
 
-func (e *routeTrait) Customize(environment Environment, resources *kubernetes.Collection) (bool, error) {
+func (e *routeTrait) customize(environment environment, resources *kubernetes.Collection) (bool, error) {
 	var service *corev1.Service
 	resources.VisitService(func(s *corev1.Service) {
 		if s.ObjectMeta.Labels != nil {
@@ -50,7 +50,7 @@ func (e *routeTrait) Customize(environment Environment, resources *kubernetes.Co
 	return false, nil
 }
 
-func (*routeTrait) getRouteFor(e Environment, service *corev1.Service) *routev1.Route {
+func (*routeTrait) getRouteFor(e 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 a6438d8..d7d116a 100644
--- a/pkg/trait/service.go
+++ b/pkg/trait/service.go
@@ -37,19 +37,32 @@ var webComponents = map[string]bool{
 type serviceTrait struct {
 }
 
-func (*serviceTrait) ID() ID {
-	return ID("service")
+const (
+	serviceTraitPortKey = "port"
+)
+
+func (*serviceTrait) id() id {
+	return id("service")
 }
 
-func (e *serviceTrait) Customize(environment Environment, resources *kubernetes.Collection) (bool, error) {
+func (e *serviceTrait) customize(environment environment, resources *kubernetes.Collection) (bool, error) {
 	if !e.requiresService(environment) {
 		return false, nil
 	}
-	resources.Add(e.getServiceFor(environment))
+	svc, err := e.getServiceFor(environment)
+	if err != nil {
+		return false, err
+	}
+	resources.Add(svc)
 	return true, nil
 }
 
-func (*serviceTrait) getServiceFor(e Environment) *corev1.Service {
+func (s *serviceTrait) getServiceFor(e environment) (*corev1.Service, error) {
+	port, err := e.getIntConfigOr(s.id(), serviceTraitPortKey, 8080)
+	if err != nil {
+		return nil, err
+	}
+
 	svc := corev1.Service{
 		TypeMeta: metav1.TypeMeta{
 			Kind:       "Service",
@@ -65,12 +78,10 @@ func (*serviceTrait) getServiceFor(e Environment) *corev1.Service {
 		Spec: corev1.ServiceSpec{
 			Ports: []corev1.ServicePort{
 				{
-					Name:     "http",
-					Port:     80,
-					Protocol: corev1.ProtocolTCP,
-					// TODO discovering the real port is hard - maybe we should just set 8080 as conventional port in the doc
-					// or allow users to configure it in the trait configuration section
-					TargetPort: intstr.FromInt(8080),
+					Name:       "http",
+					Port:       80,
+					Protocol:   corev1.ProtocolTCP,
+					TargetPort: intstr.FromInt(port),
 				},
 			},
 			Selector: map[string]string{
@@ -79,10 +90,10 @@ func (*serviceTrait) getServiceFor(e Environment) *corev1.Service {
 		},
 	}
 
-	return &svc
+	return &svc, nil
 }
 
-func (*serviceTrait) requiresService(environment Environment) bool {
+func (*serviceTrait) requiresService(environment environment) bool {
 	for _, dep := range environment.Integration.Spec.Dependencies {
 		if decision, present := webComponents[dep]; present {
 			return decision
diff --git a/pkg/trait/trait.go b/pkg/trait/trait.go
index d9c372f..a2b83c6 100644
--- a/pkg/trait/trait.go
+++ b/pkg/trait/trait.go
@@ -21,18 +21,27 @@ import (
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/platform"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
+	"github.com/pkg/errors"
+	"k8s.io/apimachinery/pkg/runtime"
 )
 
-// A Environment provides the context where the trait is executed
-type Environment struct {
-	Platform            *v1alpha1.IntegrationPlatform
-	Context             *v1alpha1.IntegrationContext
-	Integration         *v1alpha1.Integration
-	ExecutedCustomizers []ID
+// ComputeDeployment generates all required resources for deploying the given integration
+func ComputeDeployment(integration *v1alpha1.Integration) ([]runtime.Object, error) {
+	environment, err := newEnvironment(integration)
+	if err != nil {
+		return nil, err
+	}
+	resources := kubernetes.NewCollection()
+	customizers := customizersFor(*environment)
+	// invoke the trait framework to determine the needed resources
+	if _, err = customizers.customize(*environment, resources); err != nil {
+		return nil, errors.Wrap(err, "error during trait customization")
+	}
+	return resources.Items(), nil
 }
 
-// NewEnvironment creates a Environment from the given data
-func NewEnvironment(integration *v1alpha1.Integration) (*Environment, error) {
+// newEnvironment creates a environment from the given data
+func newEnvironment(integration *v1alpha1.Integration) (*environment, error) {
 	pl, err := platform.GetCurrentPlatform(integration.Namespace)
 	if err != nil {
 		return nil, err
@@ -42,21 +51,10 @@ func NewEnvironment(integration *v1alpha1.Integration) (*Environment, error) {
 		return nil, err
 	}
 
-	return &Environment{
+	return &environment{
 		Platform:            pl,
 		Context:             ctx,
 		Integration:         integration,
-		ExecutedCustomizers: make([]ID, 0),
+		ExecutedCustomizers: make([]id, 0),
 	}, nil
 }
-
-// ID uniquely identifies a trait
-type ID string
-
-// A Customizer performs customization of the deployed objects
-type Customizer interface {
-	// The Name of the customizer
-	ID() ID
-	// Customize executes the trait customization on the resources and return true if the resources have been changed
-	Customize(environment Environment, resources *kubernetes.Collection) (bool, error)
-}
diff --git a/pkg/trait/types.go b/pkg/trait/types.go
new file mode 100644
index 0000000..dd8c269
--- /dev/null
+++ b/pkg/trait/types.go
@@ -0,0 +1,101 @@
+/*
+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/util/kubernetes"
+	"github.com/pkg/errors"
+	"strconv"
+)
+
+// ID uniquely identifies a trait
+type id string
+
+// A Customizer performs customization of the deployed objects
+type customizer interface {
+	// The Name of the customizer
+	id() id
+	// Customize executes the trait customization on the resources and return true if the resources have been changed
+	customize(environment environment, resources *kubernetes.Collection) (bool, error)
+}
+
+// A environment provides the context where the trait is executed
+type environment struct {
+	Platform            *v1alpha1.IntegrationPlatform
+	Context             *v1alpha1.IntegrationContext
+	Integration         *v1alpha1.Integration
+	ExecutedCustomizers []id
+}
+
+func (e environment) getTraitSpec(traitID id) *v1alpha1.IntegrationTraitSpec {
+	if e.Integration.Spec.Traits == nil {
+		return nil
+	}
+	if conf, ok := e.Integration.Spec.Traits[string(traitID)]; ok {
+		return &conf
+	}
+	return nil
+}
+
+func (e environment) isEnabled(traitID id) bool {
+	conf := e.getTraitSpec(traitID)
+	return conf == nil || conf.Enabled == nil || *conf.Enabled
+}
+
+func (e environment) getConfig(traitID id, key string) *string {
+	conf := e.getTraitSpec(traitID)
+	if conf == nil || conf.Configuration == nil {
+		return nil
+	}
+	if v, ok := conf.Configuration[key]; ok {
+		return &v
+	}
+	return nil
+}
+
+func (e environment) getConfigOr(traitID id, key string, defaultValue string) string {
+	val := e.getConfig(traitID, key)
+	if val != nil {
+		return *val
+	}
+	return defaultValue
+}
+
+func (e environment) getIntConfig(traitID id, key string) (*int, error) {
+	val := e.getConfig(traitID, key)
+	if val == nil {
+		return nil, nil
+	}
+	intVal, err := strconv.Atoi(*val)
+	if err != nil {
+		return nil, errors.Wrap(err, "cannot extract a integer from property "+key+" with value "+*val)
+	}
+	return &intVal, nil
+}
+
+func (e environment) getIntConfigOr(traitID id, key string, defaultValue int) (int, error) {
+	val, err := e.getIntConfig(traitID, key)
+	if err != nil {
+		return 0, err
+	}
+	if val != nil {
+		return *val, nil
+	}
+	return defaultValue, nil
+}


[camel-k] 04/14: Added route trait

Posted by lb...@apache.org.
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 56a7b4db16e660a93a1106888704b5ad72cce6bd
Author: nferraro <ni...@gmail.com>
AuthorDate: Thu Oct 4 13:49:11 2018 +0200

    Added route trait
---
 pkg/trait/base.go                 | 12 +++++++----
 pkg/trait/route.go                | 42 ++++++++++++++++++++++++++++++++++++---
 pkg/trait/service.go              | 12 +++++------
 pkg/util/kubernetes/collection.go |  9 +++++++++
 4 files changed, 62 insertions(+), 13 deletions(-)

diff --git a/pkg/trait/base.go b/pkg/trait/base.go
index d831d79..6ba4dfc 100644
--- a/pkg/trait/base.go
+++ b/pkg/trait/base.go
@@ -59,7 +59,9 @@ func (*baseTrait) getConfigMapFor(e Environment) *corev1.ConfigMap {
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      e.Integration.Name,
 			Namespace: e.Integration.Namespace,
-			Labels:    e.Integration.Labels,
+			Labels: map[string]string{
+				"camel.apache.org/integration": e.Integration.Name,
+			},
 			Annotations: map[string]string{
 				"camel.apache.org/source.language": string(e.Integration.Spec.Source.Language),
 				"camel.apache.org/source.name":     e.Integration.Spec.Source.Name,
@@ -112,9 +114,11 @@ func (*baseTrait) getDeploymentFor(e Environment) *appsv1.Deployment {
 			APIVersion: appsv1.SchemeGroupVersion.String(),
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Name:        e.Integration.Name,
-			Namespace:   e.Integration.Namespace,
-			Labels:      e.Integration.Labels,
+			Name:      e.Integration.Name,
+			Namespace: e.Integration.Namespace,
+			Labels: map[string]string{
+				"camel.apache.org/integration": e.Integration.Name,
+			},
 			Annotations: e.Integration.Annotations,
 		},
 		Spec: appsv1.DeploymentSpec{
diff --git a/pkg/trait/route.go b/pkg/trait/route.go
index 681c8b1..1906442 100644
--- a/pkg/trait/route.go
+++ b/pkg/trait/route.go
@@ -20,6 +20,9 @@ package trait
 import (
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 	routev1 "github.com/openshift/api/route/v1"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/intstr"
 )
 
 type routeTrait struct {
@@ -30,9 +33,42 @@ func (*routeTrait) ID() ID {
 }
 
 func (e *routeTrait) Customize(environment Environment, resources *kubernetes.Collection) (bool, error) {
-	return true, nil
+	var service *corev1.Service
+	resources.VisitService(func(s *corev1.Service) {
+		if s.ObjectMeta.Labels != nil {
+			if intName, ok := s.ObjectMeta.Labels["camel.apache.org/integration"]; ok && intName == environment.Integration.Name {
+				service = s
+			}
+		}
+	})
+
+	if service != nil {
+		resources.Add(e.getRouteFor(environment, service))
+		return true, nil
+	}
+
+	return false, nil
 }
 
-func (*routeTrait) getRouteFor(e Environment) *routev1.Route {
-	return nil
+func (*routeTrait) getRouteFor(e Environment, service *corev1.Service) *routev1.Route {
+	route := routev1.Route{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "Route",
+			APIVersion: routev1.SchemeGroupVersion.String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      service.Name,
+			Namespace: service.Namespace,
+		},
+		Spec: routev1.RouteSpec{
+			Port: &routev1.RoutePort{
+				TargetPort: intstr.FromString("http"),
+			},
+			To: routev1.RouteTargetReference{
+				Kind: "Service",
+				Name: service.Name,
+			},
+		},
+	}
+	return &route
 }
diff --git a/pkg/trait/service.go b/pkg/trait/service.go
index 3576f94..a6438d8 100644
--- a/pkg/trait/service.go
+++ b/pkg/trait/service.go
@@ -58,7 +58,9 @@ func (*serviceTrait) getServiceFor(e Environment) *corev1.Service {
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      e.Integration.Name,
 			Namespace: e.Integration.Namespace,
-			Labels:    e.Integration.Labels,
+			Labels: map[string]string{
+				"camel.apache.org/integration": e.Integration.Name,
+			},
 		},
 		Spec: corev1.ServiceSpec{
 			Ports: []corev1.ServicePort{
@@ -66,11 +68,9 @@ func (*serviceTrait) getServiceFor(e Environment) *corev1.Service {
 					Name:     "http",
 					Port:     80,
 					Protocol: corev1.ProtocolTCP,
-					TargetPort: intstr.IntOrString{
-						// TODO discovering the real port is hard - maybe we should just set 8080 as conventional port in the doc
-						// or allow users to configure it in the trait configuration section
-						IntVal: 8080,
-					},
+					// TODO discovering the real port is hard - maybe we should just set 8080 as conventional port in the doc
+					// or allow users to configure it in the trait configuration section
+					TargetPort: intstr.FromInt(8080),
 				},
 			},
 			Selector: map[string]string{
diff --git a/pkg/util/kubernetes/collection.go b/pkg/util/kubernetes/collection.go
index 91cb7ee..0b5f217 100644
--- a/pkg/util/kubernetes/collection.go
+++ b/pkg/util/kubernetes/collection.go
@@ -64,6 +64,15 @@ func (c *Collection) VisitConfigMap(visitor func(*corev1.ConfigMap)) {
 	})
 }
 
+// VisitService executes the visitor function on all Service resources
+func (c *Collection) VisitService(visitor func(*corev1.Service)) {
+	c.Visit(func(res runtime.Object) {
+		if conv, ok := res.(*corev1.Service); ok {
+			visitor(conv)
+		}
+	})
+}
+
 // VisitMetaObject executes the visitor function on all meta.Object resources
 func (c *Collection) VisitMetaObject(visitor func(metav1.Object)) {
 	c.Visit(func(res runtime.Object) {


[camel-k] 13/14: Formalize auto-detection

Posted by lb...@apache.org.
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 19f4c403f9156319b05ccc1a83db256bae58bfc0
Author: nferraro <ni...@gmail.com>
AuthorDate: Fri Oct 5 10:45:46 2018 +0200

    Formalize auto-detection
---
 pkg/trait/catalog.go |  2 +-
 pkg/trait/service.go |  6 +++---
 pkg/trait/types.go   | 14 ++++++++++++--
 3 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/pkg/trait/catalog.go b/pkg/trait/catalog.go
index df8ad07..abc6415 100644
--- a/pkg/trait/catalog.go
+++ b/pkg/trait/catalog.go
@@ -69,7 +69,7 @@ func (c *chainedCustomizer) id() id {
 func (c *chainedCustomizer) customize(environment *environment, resources *kubernetes.Collection) (bool, error) {
 	atLeastOne := false
 	for _, custom := range c.customizers {
-		if environment.isEnabled(custom.id()) {
+		if environment.isExplicitlyEnabled(custom.id()) || environment.isAutoDetectionMode(custom.id()) {
 			if done, err := custom.customize(environment, resources); err != nil {
 				return false, err
 			} else if done && custom.id() != "" {
diff --git a/pkg/trait/service.go b/pkg/trait/service.go
index f52d08d..9907466 100644
--- a/pkg/trait/service.go
+++ b/pkg/trait/service.go
@@ -45,11 +45,11 @@ func (*serviceTrait) id() id {
 	return id("service")
 }
 
-func (e *serviceTrait) customize(environment *environment, resources *kubernetes.Collection) (bool, error) {
-	if !e.requiresService(environment) {
+func (s *serviceTrait) customize(environment *environment, resources *kubernetes.Collection) (bool, error) {
+	if environment.isAutoDetectionMode(s.id()) && !s.requiresService(environment) {
 		return false, nil
 	}
-	svc, err := e.getServiceFor(environment)
+	svc, err := s.getServiceFor(environment)
 	if err != nil {
 		return false, err
 	}
diff --git a/pkg/trait/types.go b/pkg/trait/types.go
index ad002e1..306f0b4 100644
--- a/pkg/trait/types.go
+++ b/pkg/trait/types.go
@@ -53,9 +53,19 @@ func (e *environment) getTraitSpec(traitID id) *v1alpha1.IntegrationTraitSpec {
 	return nil
 }
 
-func (e *environment) isEnabled(traitID id) bool {
+func (e *environment) isExplicitlyEnabled(traitID id) bool {
 	conf := e.getTraitSpec(traitID)
-	return conf == nil || conf.Enabled == nil || *conf.Enabled
+	return conf != nil && conf.Enabled != nil && *conf.Enabled
+}
+
+func (e *environment) isExplicitlyDisabled(traitID id) bool {
+	conf := e.getTraitSpec(traitID)
+	return conf != nil && conf.Enabled != nil && !*conf.Enabled
+}
+
+func (e *environment) isAutoDetectionMode(traitID id) bool {
+	conf := e.getTraitSpec(traitID)
+	return conf == nil || conf.Enabled == nil
 }
 
 func (e *environment) getConfig(traitID id, key string) *string {


[camel-k] 01/14: Initial work on traits

Posted by lb...@apache.org.
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 23d0287ae62446392b6f924fd56a86e25f0a8ff5
Author: nferraro <ni...@gmail.com>
AuthorDate: Wed Oct 3 22:40:03 2018 +0200

    Initial work on traits
---
 pkg/apis/camel/v1alpha1/types.go  | 19 ++++++---
 pkg/trait/catalog/catalog.go      | 85 +++++++++++++++++++++++++++++++++++++++
 pkg/trait/catalog/expose.go       | 34 ++++++++++++++++
 pkg/trait/catalog/identity.go     | 34 ++++++++++++++++
 pkg/trait/doc.go                  | 19 +++++++++
 pkg/trait/trait.go                | 39 ++++++++++++++++++
 pkg/util/kubernetes/collection.go | 64 +++++++++++++++++++++++++++++
 7 files changed, 288 insertions(+), 6 deletions(-)

diff --git a/pkg/apis/camel/v1alpha1/types.go b/pkg/apis/camel/v1alpha1/types.go
index 114942f..61141af 100644
--- a/pkg/apis/camel/v1alpha1/types.go
+++ b/pkg/apis/camel/v1alpha1/types.go
@@ -48,12 +48,13 @@ type Integration struct {
 
 // IntegrationSpec --
 type IntegrationSpec struct {
-	Replicas                  *int32              `json:"replicas,omitempty"`
-	Source                    SourceSpec          `json:"source,omitempty"`
-	Context                   string              `json:"context,omitempty"`
-	Dependencies              []string            `json:"dependencies,omitempty"`
-	DependenciesAutoDiscovery *bool               `json:"dependenciesAutoDiscovery,omitempty"`
-	Configuration             []ConfigurationSpec `json:"configuration,omitempty"`
+	Replicas                  *int32                          `json:"replicas,omitempty"`
+	Source                    SourceSpec                      `json:"source,omitempty"`
+	Context                   string                          `json:"context,omitempty"`
+	Dependencies              []string                        `json:"dependencies,omitempty"`
+	Traits                    map[string]IntegrationTraitSpec `json:"traits,omitempty"`
+	DependenciesAutoDiscovery *bool                           `json:"dependenciesAutoDiscovery,omitempty"`
+	Configuration             []ConfigurationSpec             `json:"configuration,omitempty"`
 }
 
 // SourceSpec --
@@ -81,6 +82,12 @@ const (
 	LanguageKotlin Language = "kts"
 )
 
+// A IntegrationTraitSpec contains the configuration of a trait
+type IntegrationTraitSpec struct {
+	Enabled       *bool             `json:"enabled,omitempty"`
+	Configuration map[string]string `json:"configuration,omitempty"`
+}
+
 // IntegrationStatus --
 type IntegrationStatus struct {
 	Phase  IntegrationPhase `json:"phase,omitempty"`
diff --git a/pkg/trait/catalog/catalog.go b/pkg/trait/catalog/catalog.go
new file mode 100644
index 0000000..924ed37
--- /dev/null
+++ b/pkg/trait/catalog/catalog.go
@@ -0,0 +1,85 @@
+/*
+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 catalog
+
+import (
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"github.com/apache/camel-k/pkg/trait"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
+)
+
+// TraitID uniquely identifies a trait
+type TraitID string
+
+const (
+	// Expose exposes a integration to the external world
+	Expose TraitID = "expose"
+)
+
+// A Catalog is just a DeploymentCustomizer that applies multiple traits
+type Catalog trait.DeploymentCustomizer
+
+// For returns a Catalog for the given integration details
+func For(environment trait.Environment) Catalog {
+
+}
+
+func compose(traits ...trait.DeploymentCustomizer) trait.DeploymentCustomizer {
+	if len(traits) == 0 {
+		return &identityTrait{}
+	} else if len(traits) == 1 {
+		return traits[0]
+	}
+	var composite trait.DeploymentCustomizer = &identityTrait{}
+	for _, t := range traits {
+		composite = &catalogCustomizer{
+			t1: composite,
+			t2: t,
+		}
+	}
+	return composite
+}
+
+// -------------------------------------------
+
+type catalogCustomizer struct {
+	t1 trait.DeploymentCustomizer
+	t2 trait.DeploymentCustomizer
+}
+
+func (c *catalogCustomizer) Name() string {
+	return ""
+}
+
+func (c *catalogCustomizer) Customize(environment trait.Environment, resources *kubernetes.Collection) (bool, error) {
+	atLeastOnce := false
+	var done bool
+	var err error
+	if done, err = c.t1.Customize(environment, resources); err != nil {
+		return false, err
+	} else if done && c.t1.Name() != "" {
+		environment.ExecutedCustomizers = append(environment.ExecutedCustomizers, c.t1.Name())
+	}
+	atLeastOnce = atLeastOnce || done
+	done2, err := c.t2.Customize(environment, resources)
+	if done2 && c.t2.Name() != "" {
+		environment.ExecutedCustomizers = append(environment.ExecutedCustomizers, c.t2.Name())
+	}
+	environment.ExecutedCustomizers = append(environment.ExecutedCustomizers, c.t1.Name())
+	return atLeastOnce || done2, err
+}
diff --git a/pkg/trait/catalog/expose.go b/pkg/trait/catalog/expose.go
new file mode 100644
index 0000000..91890eb
--- /dev/null
+++ b/pkg/trait/catalog/expose.go
@@ -0,0 +1,34 @@
+/*
+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 catalog
+
+import (
+	"github.com/apache/camel-k/pkg/trait"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
+)
+
+type exposeTrait struct {
+}
+
+func (*exposeTrait) Name() string {
+	return "expose"
+}
+
+func (*exposeTrait) Customize(environment trait.Environment, resources *kubernetes.Collection) (bool, error) {
+	return false, nil
+}
diff --git a/pkg/trait/catalog/identity.go b/pkg/trait/catalog/identity.go
new file mode 100644
index 0000000..cddd0f4
--- /dev/null
+++ b/pkg/trait/catalog/identity.go
@@ -0,0 +1,34 @@
+/*
+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 catalog
+
+import (
+	"github.com/apache/camel-k/pkg/trait"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
+)
+
+type identityTrait struct {
+}
+
+func (*identityTrait) Name() string {
+	return "identity"
+}
+
+func (*identityTrait) Customize(environment trait.Environment, resources *kubernetes.Collection) (bool, error) {
+	return false, nil
+}
diff --git a/pkg/trait/doc.go b/pkg/trait/doc.go
new file mode 100644
index 0000000..7216c27
--- /dev/null
+++ b/pkg/trait/doc.go
@@ -0,0 +1,19 @@
+/*
+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 contains implementations of all available traits (features)
+package trait
diff --git a/pkg/trait/trait.go b/pkg/trait/trait.go
new file mode 100644
index 0000000..e0bb7ef
--- /dev/null
+++ b/pkg/trait/trait.go
@@ -0,0 +1,39 @@
+/*
+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/util/kubernetes"
+)
+
+// A Environment provides the context where the trait is executed
+type Environment struct {
+	Platform            *v1alpha1.IntegrationPlatform
+	Context             *v1alpha1.IntegrationContext
+	Integration         *v1alpha1.Integration
+	ExecutedCustomizers []string
+}
+
+// A DeploymentCustomizer performs customization of the deployed objects
+type DeploymentCustomizer interface {
+	// The Name of the customizer
+	Name() string
+	// Customize executes the trait customization on the resources and return true if the resources have been changed
+	Customize(environment Environment, resources *kubernetes.Collection) (bool, error)
+}
diff --git a/pkg/util/kubernetes/collection.go b/pkg/util/kubernetes/collection.go
new file mode 100644
index 0000000..a3ff2b7
--- /dev/null
+++ b/pkg/util/kubernetes/collection.go
@@ -0,0 +1,64 @@
+/*
+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 kubernetes
+
+import (
+	appsv1 "k8s.io/api/apps/v1"
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+)
+
+// A Collection is a container of Kubernetes resources
+type Collection struct {
+	items []runtime.Object
+}
+
+// Items returns all resources belonging to the collection
+func (c *Collection) Items() []runtime.Object {
+	return c.items
+}
+
+// Add adds a resource to the collection
+func (c *Collection) Add(resource runtime.Object) {
+	c.items = append(c.items, resource)
+}
+
+// VisitDeployment executes the visitor function on all Deployment resources
+func (c *Collection) VisitDeployment(visitor func(*appsv1.Deployment)) {
+	c.Visit(func(res runtime.Object) {
+		if conv, ok := res.(*appsv1.Deployment); ok {
+			visitor(conv)
+		}
+	})
+}
+
+// VisitConfigMap executes the visitor function on all ConfigMap resources
+func (c *Collection) VisitConfigMap(visitor func(configMap *corev1.ConfigMap)) {
+	c.Visit(func(res runtime.Object) {
+		if conv, ok := res.(*corev1.ConfigMap); ok {
+			visitor(conv)
+		}
+	})
+}
+
+// Visit executes the visitor function on all resources
+func (c *Collection) Visit(visitor func(runtime.Object)) {
+	for _, res := range c.items {
+		visitor(res)
+	}
+}


[camel-k] 05/14: Fixed errors during redeploy

Posted by lb...@apache.org.
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 e570a930b172d4d44fbded6e266f97b21e55b5b5
Author: nferraro <ni...@gmail.com>
AuthorDate: Thu Oct 4 15:47:07 2018 +0200

    Fixed errors during redeploy
---
 pkg/stub/action/integration/deploy.go | 18 +------
 pkg/util/kubernetes/replace.go        | 97 +++++++++++++++++++++++++++++++++++
 runtime/examples/routes-rest.js       |  3 +-
 3 files changed, 100 insertions(+), 18 deletions(-)

diff --git a/pkg/stub/action/integration/deploy.go b/pkg/stub/action/integration/deploy.go
index c9c7f4e..9790bf8 100644
--- a/pkg/stub/action/integration/deploy.go
+++ b/pkg/stub/action/integration/deploy.go
@@ -24,8 +24,6 @@ import (
 	"github.com/operator-framework/operator-sdk/pkg/sdk"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
-	k8serrors "k8s.io/apimachinery/pkg/api/errors"
-	"k8s.io/apimachinery/pkg/runtime"
 )
 
 // NewDeployAction create an action that handles integration deploy
@@ -56,7 +54,7 @@ func (action *deployAction) Handle(integration *v1alpha1.Integration) error {
 		return errors.Wrap(err, "error during trait customization")
 	}
 	// TODO we should look for objects that are no longer present in the collection and remove them
-	err = action.createOrUpdateObjects(resources.Items(), integration)
+	err = kubernetes.ReplaceResources(resources.Items())
 	if err != nil {
 		return err
 	}
@@ -67,17 +65,3 @@ func (action *deployAction) Handle(integration *v1alpha1.Integration) error {
 
 	return sdk.Update(target)
 }
-
-func (action *deployAction) createOrUpdateObjects(objects []runtime.Object, integration *v1alpha1.Integration) error {
-	for _, object := range objects {
-		err := sdk.Create(object)
-		if err != nil && k8serrors.IsAlreadyExists(err) {
-			err = sdk.Update(object)
-		}
-		if err != nil {
-			return errors.Wrap(err, "could not create or replace resource for integration "+integration.Name)
-		}
-	}
-
-	return nil
-}
diff --git a/pkg/util/kubernetes/replace.go b/pkg/util/kubernetes/replace.go
new file mode 100644
index 0000000..45b908d
--- /dev/null
+++ b/pkg/util/kubernetes/replace.go
@@ -0,0 +1,97 @@
+/*
+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 kubernetes
+
+import (
+	routev1 "github.com/openshift/api/route/v1"
+	"github.com/operator-framework/operator-sdk/pkg/sdk"
+	"github.com/pkg/errors"
+	corev1 "k8s.io/api/core/v1"
+	k8serrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+)
+
+// ReplaceResources allows to completely replace a list of resources on Kubernetes, taking care of immutable fields and resource versions
+func ReplaceResources(objects []runtime.Object) error {
+	for _, object := range objects {
+		err := ReplaceResource(object)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// ReplaceResource allows to completely replace a resource on Kubernetes, taking care of immutable fields and resource versions
+func ReplaceResource(res runtime.Object) error {
+	err := sdk.Create(res)
+	if err != nil && k8serrors.IsAlreadyExists(err) {
+		existing := res.DeepCopyObject()
+		err = sdk.Get(existing)
+		if err != nil {
+			return err
+		}
+		mapRequiredMeta(existing, res)
+		mapRequiredServiceData(existing, res)
+		mapRequiredRouteData(existing, res)
+		err = sdk.Update(res)
+	}
+	if err != nil {
+		return errors.Wrap(err, "could not create or replace "+findResourceDetails(res))
+	}
+	return nil
+}
+
+func mapRequiredMeta(from runtime.Object, to runtime.Object) {
+	if fromC, ok := from.(metav1.Object); ok {
+		if toC, ok := to.(metav1.Object); ok {
+			toC.SetResourceVersion(fromC.GetResourceVersion())
+		}
+	}
+}
+
+func mapRequiredServiceData(from runtime.Object, to runtime.Object) {
+	if fromC, ok := from.(*corev1.Service); ok {
+		if toC, ok := to.(*corev1.Service); ok {
+			toC.Spec.ClusterIP = fromC.Spec.ClusterIP
+		}
+	}
+}
+
+func mapRequiredRouteData(from runtime.Object, to runtime.Object) {
+	if fromC, ok := from.(*routev1.Route); ok {
+		if toC, ok := to.(*routev1.Route); ok {
+			toC.Spec.Host = fromC.Spec.Host
+		}
+	}
+}
+
+func findResourceDetails(res runtime.Object) string {
+	if res == nil {
+		return "nil resource"
+	}
+	if meta, ok := res.(metav1.Object); ok {
+		name := meta.GetName()
+		if ty, ok := res.(metav1.Type); ok {
+			return ty.GetKind() + " " + name
+		}
+		return "resource " + name
+	}
+	return "unnamed resource"
+}
diff --git a/runtime/examples/routes-rest.js b/runtime/examples/routes-rest.js
index e888019..97d41ec 100644
--- a/runtime/examples/routes-rest.js
+++ b/runtime/examples/routes-rest.js
@@ -17,7 +17,7 @@ l.exchangeFormatter = function(e) {
 
 c = restConfiguration()
 c.component = 'undertow'
-c.port = 8081
+c.port = 8080
 
 // ****************
 //
@@ -37,6 +37,7 @@ function proc(e) {
 
 rest()
     .path('/say/hello')
+    .produces("text/plain")
     .get().route()
         .transform().constant("Hello World");
 


[camel-k] 03/14: Added service trait

Posted by lb...@apache.org.
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 8052da5abed8460b7fb0ca7aa375c52172edc0f1
Author: nferraro <ni...@gmail.com>
AuthorDate: Thu Oct 4 13:15:51 2018 +0200

    Added service trait
---
 pkg/trait/base.go                 | 21 +++------
 pkg/trait/catalog.go              |  8 ++--
 pkg/trait/{expose.go => route.go} | 15 ++++---
 pkg/trait/service.go              | 92 +++++++++++++++++++++++++++++++++++++++
 pkg/trait/trait.go                |  4 --
 5 files changed, 113 insertions(+), 27 deletions(-)

diff --git a/pkg/trait/base.go b/pkg/trait/base.go
index 69a46b9..d831d79 100644
--- a/pkg/trait/base.go
+++ b/pkg/trait/base.go
@@ -35,17 +35,8 @@ func (*baseTrait) ID() ID {
 }
 
 func (d *baseTrait) Customize(environment Environment, resources *kubernetes.Collection) (bool, error) {
-	var cm *corev1.ConfigMap
-	var err error
-	if cm, err = d.getConfigMapFor(environment); err != nil {
-		return false, err
-	}
-	resources.Add(cm)
-	var depl *appsv1.Deployment
-	if depl, err = d.getDeploymentFor(environment); err != nil {
-		return false, err
-	}
-	resources.Add(depl)
+	resources.Add(d.getConfigMapFor(environment))
+	resources.Add(d.getDeploymentFor(environment))
 	return true, nil
 }
 
@@ -55,7 +46,7 @@ func (d *baseTrait) Customize(environment Environment, resources *kubernetes.Col
 //
 // **********************************
 
-func (*baseTrait) getConfigMapFor(e Environment) (*corev1.ConfigMap, error) {
+func (*baseTrait) getConfigMapFor(e Environment) *corev1.ConfigMap {
 	// combine properties of integration with context, integration
 	// properties have the priority
 	properties := CombineConfigurationAsMap("property", e.Context, e.Integration)
@@ -80,7 +71,7 @@ func (*baseTrait) getConfigMapFor(e Environment) (*corev1.ConfigMap, error) {
 		},
 	}
 
-	return &cm, nil
+	return &cm
 }
 
 // **********************************
@@ -89,7 +80,7 @@ func (*baseTrait) getConfigMapFor(e Environment) (*corev1.ConfigMap, error) {
 //
 // **********************************
 
-func (*baseTrait) getDeploymentFor(e Environment) (*appsv1.Deployment, error) {
+func (*baseTrait) getDeploymentFor(e Environment) *appsv1.Deployment {
 	sourceName := strings.TrimPrefix(e.Integration.Spec.Source.Name, "/")
 
 	// combine environment of integration with context, integration
@@ -240,5 +231,5 @@ func (*baseTrait) getDeploymentFor(e Environment) (*appsv1.Deployment, error) {
 	deployment.Spec.Template.Spec.Volumes = vols
 	deployment.Spec.Template.Spec.Containers[0].VolumeMounts = mnts
 
-	return &deployment, nil
+	return &deployment
 }
diff --git a/pkg/trait/catalog.go b/pkg/trait/catalog.go
index 3325860..a969012 100644
--- a/pkg/trait/catalog.go
+++ b/pkg/trait/catalog.go
@@ -23,8 +23,9 @@ import (
 )
 
 var (
-	tExpose = &exposeTrait{}
 	tBase = &baseTrait{}
+	tService = &serviceTrait{}
+	tRoute = &routeTrait{}
 	tOwner = &ownerTrait{}
 )
 
@@ -34,13 +35,14 @@ func CustomizersFor(environment Environment) Customizer {
 	case v1alpha1.IntegrationPlatformClusterOpenShift:
 		return compose(
 			tBase,
-			tExpose,
+			tService,
+			tRoute,
 			tOwner,
 		)
 	case v1alpha1.IntegrationPlatformClusterKubernetes:
 		return compose(
 			tBase,
-			tExpose,
+			tService,
 			tOwner,
 		)
 		// case Knative: ...
diff --git a/pkg/trait/expose.go b/pkg/trait/route.go
similarity index 72%
rename from pkg/trait/expose.go
rename to pkg/trait/route.go
index 2dac116..681c8b1 100644
--- a/pkg/trait/expose.go
+++ b/pkg/trait/route.go
@@ -19,15 +19,20 @@ package trait
 
 import (
 	"github.com/apache/camel-k/pkg/util/kubernetes"
+	routev1 "github.com/openshift/api/route/v1"
 )
 
-type exposeTrait struct {
+type routeTrait struct {
 }
 
-func (*exposeTrait) ID() ID {
-	return ID("expose")
+func (*routeTrait) ID() ID {
+	return ID("route")
 }
 
-func (*exposeTrait) Customize(environment Environment, resources *kubernetes.Collection) (bool, error) {
-	return false, nil
+func (e *routeTrait) Customize(environment Environment, resources *kubernetes.Collection) (bool, error) {
+	return true, nil
+}
+
+func (*routeTrait) getRouteFor(e Environment) *routev1.Route {
+	return nil
 }
diff --git a/pkg/trait/service.go b/pkg/trait/service.go
new file mode 100644
index 0000000..3576f94
--- /dev/null
+++ b/pkg/trait/service.go
@@ -0,0 +1,92 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package trait
+
+import (
+	"github.com/apache/camel-k/pkg/util/kubernetes"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/intstr"
+)
+
+var webComponents = map[string]bool{
+	"camel:servlet":     true,
+	"camel:undertow":    true,
+	"camel:jetty":       true,
+	"camel:netty-http":  true,
+	"camel:netty4-http": true,
+	// TODO find a better way to discover need for exposure
+	// maybe using the resolved classpath of the context instead of the requested dependencies
+}
+
+type serviceTrait struct {
+}
+
+func (*serviceTrait) ID() ID {
+	return ID("service")
+}
+
+func (e *serviceTrait) Customize(environment Environment, resources *kubernetes.Collection) (bool, error) {
+	if !e.requiresService(environment) {
+		return false, nil
+	}
+	resources.Add(e.getServiceFor(environment))
+	return true, nil
+}
+
+func (*serviceTrait) getServiceFor(e Environment) *corev1.Service {
+	svc := corev1.Service{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "Service",
+			APIVersion: "v1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      e.Integration.Name,
+			Namespace: e.Integration.Namespace,
+			Labels:    e.Integration.Labels,
+		},
+		Spec: corev1.ServiceSpec{
+			Ports: []corev1.ServicePort{
+				{
+					Name:     "http",
+					Port:     80,
+					Protocol: corev1.ProtocolTCP,
+					TargetPort: intstr.IntOrString{
+						// TODO discovering the real port is hard - maybe we should just set 8080 as conventional port in the doc
+						// or allow users to configure it in the trait configuration section
+						IntVal: 8080,
+					},
+				},
+			},
+			Selector: map[string]string{
+				"camel.apache.org/integration": e.Integration.Name,
+			},
+		},
+	}
+
+	return &svc
+}
+
+func (*serviceTrait) requiresService(environment Environment) bool {
+	for _, dep := range environment.Integration.Spec.Dependencies {
+		if decision, present := webComponents[dep]; present {
+			return decision
+		}
+	}
+	return false
+}
diff --git a/pkg/trait/trait.go b/pkg/trait/trait.go
index 90e4d29..d9c372f 100644
--- a/pkg/trait/trait.go
+++ b/pkg/trait/trait.go
@@ -19,7 +19,6 @@ package trait
 
 import (
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
-	"github.com/apache/camel-k/pkg/discover"
 	"github.com/apache/camel-k/pkg/platform"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 )
@@ -29,7 +28,6 @@ type Environment struct {
 	Platform            *v1alpha1.IntegrationPlatform
 	Context             *v1alpha1.IntegrationContext
 	Integration         *v1alpha1.Integration
-	Dependencies        []string
 	ExecutedCustomizers []ID
 }
 
@@ -43,13 +41,11 @@ func NewEnvironment(integration *v1alpha1.Integration) (*Environment, error) {
 	if err != nil {
 		return nil, err
 	}
-	dependencies := discover.Dependencies(integration.Spec.Source)
 
 	return &Environment{
 		Platform:            pl,
 		Context:             ctx,
 		Integration:         integration,
-		Dependencies:        dependencies,
 		ExecutedCustomizers: make([]ID, 0),
 	}, nil
 }


[camel-k] 12/14: Adding doc clarification

Posted by lb...@apache.org.
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 8eff17ca7d179f7f37be454155a4849156a77262
Author: nferraro <ni...@gmail.com>
AuthorDate: Fri Oct 5 01:19:24 2018 +0200

    Adding doc clarification
---
 docs/traits.adoc | 26 +++++++++++++++++---------
 1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/docs/traits.adoc b/docs/traits.adoc
index 7ceb5de..c5ada79 100644
--- a/docs/traits.adoc
+++ b/docs/traits.adoc
@@ -1,7 +1,7 @@
 [[traits]]
 = Traits
 
-Traits are high level named features of Camel K that can be enabled/disabled or reconfigured to customize the
+Traits are high level named features of Camel K that can be enabled/disabled or configured to customize the
 behavior of the final integration.
 
 Camel K provide sensible defaults for all such traits, taking into account the details of the target platform where
@@ -20,24 +20,26 @@ kamel run --trait service.enabled=false file.groovy
 
 The flag `--trait` can be also abbreviated with `-t`.
 
-The `enabled` property is available on all traits and can be used to enable/disable them. All traits are enabled
-by default, unless they are not applicable for the current platform (see "profiles" in the table) or other conditions.
+The `enabled` property is available on all traits and can be used to enable/disable them. All traits have their own
+internal logic to determine if they need to be enabled when the user does not activate them explicitly.
 
-Some traits have additional properties that can be configured by the end user.
+NOTE: Some traits are applicable only to specific platforms (see "profiles" in the table).
 
-E.g. the following command configure the container `port` that should be exposed by the service:
+A trait may have additional properties that can be configured by the end user.
+
+E.g. the following command configures the container `port` that should be exposed by the service:
 
 ```
 kamel run --trait service.enabled=true --trait service.port=8081 file.groovy
 ```
 
-Or the equivalent command:
+Or the equivalent command (assuming that the service trait is enabled by auto-detection):
 
 ```
 kamel run -t service.port=8081 file.groovy
 ```
 
-NOTE: Enabling a *trait* does not force the trait to be activated, especially if the trait specific preconditions do not hold.
+NOTE: Enabling a trait does not force the trait to be activated, especially if the trait specific preconditions do not hold.
 E.g. enabling the `route` trait while the `service` trait is disabled does not produce automatically a route, since a service is needed
 for the `route` trait to work.
 
@@ -45,13 +47,16 @@ for the `route` trait to work.
 
 The following is a list of common traits that can be configured by the end users:
 
-[options="header",cols="1,2,3a"]
+[options="header",cols="1m,2,3a"]
 |=======================
 | Trait      | Profiles 				| Description
 
 | service
 | Kubernetes, OpenShift
 | Exposes the integration with a Service resource so that it can be accessed by other applications (or integrations) in the same namespace.
+  +
+  +
+  It's enabled by default if the integration depends on a Camel component that can expose a HTTP endpoint.
 
 [cols="m,"]
 !===
@@ -64,6 +69,9 @@ The following is a list of common traits that can be configured by the end users
 | route
 | OpenShift
 | Exposes the service associated with the integration to the outside world with a OpenShift Route.
+  +
+  +
+  It's enabled by default whenever a Service is added to the integration (through the `service` trait).
 
 |=======================
 
@@ -72,7 +80,7 @@ The following is a list of common traits that can be configured by the end users
 
 There are also platform traits that **normally should not be configured** by the end user. So change them **only if you know what you're doing**.
 
-[options="header"]
+[options="header",cols="m,,"]
 |=======================
 | Trait      | Profiles 				| Description
 | base		 | Kubernetes, OpenShift	| Creates the basic Kubernetes resource needed for running the integration.