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

[camel-k] branch master updated (c9aadd1 -> 3e9d907)

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

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


    from c9aadd1  runtime: add a netty4-http based knative component
     new 0eea5e7  Support for multiple integration definitions #45
     new 3e9d907  fix findings

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


Summary of changes:
 pkg/apis/camel/v1alpha1/types.go                   |   7 +-
 pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go   |  32 ++---
 pkg/client/cmd/run.go                              | 129 +++++++++++----------
 pkg/metadata/metadata.go                           |  11 ++
 pkg/stub/action/integration/initialize.go          |  10 +-
 pkg/trait/dependencies.go                          |  26 +++--
 pkg/trait/deployment.go                            | 119 ++++++++++++++-----
 pkg/trait/knative.go                               |  43 +++++--
 pkg/trait/trait_test.go                            |   8 +-
 pkg/util/digest/digest.go                          |   7 +-
 pkg/util/kubernetes/collection.go                  |   5 +
 runtime/examples/routes.js                         |   8 +-
 runtime/examples/simple.groovy                     |   6 +
 runtime/examples/simple.js                         |   6 +
 .../org/apache/camel/k/groovy/LoaderTest.groovy    |   4 +-
 .../camel/k/groovy/dsl/IntegrationTest.groovy      |   6 +-
 .../k/groovy/dsl/extension/LogExtensionTest.groovy |   2 +-
 .../java/org/apache/camel/k/jvm/Application.java   |   9 +-
 .../java/org/apache/camel/k/jvm/Constants.java     |   5 +-
 .../java/org/apache/camel/k/jvm/RoutesLoaders.java |  38 +++---
 .../main/java/org/apache/camel/k/jvm/Runtime.java  |  27 +++--
 .../org/apache/camel/k/jvm/RuntimeSupport.java     |   5 +-
 .../java/org/apache/camel/k/jvm/URIResolver.java   |  31 +++--
 .../java/org/apache/camel/k/jvm/RuntimeTest.java   |  51 ++++++++
 runtime/jvm/src/test/resources/r1.js               |   4 +
 runtime/jvm/src/test/resources/r2.mytype           |   4 +
 .../apache/camel/k/kotlin/KotlinRoutesLoader.kt    |  10 +-
 .../apache/camel/k/kotlin/dsl/IntegrationTest.kt   |   6 +-
 28 files changed, 407 insertions(+), 212 deletions(-)
 create mode 100644 runtime/examples/simple.groovy
 create mode 100644 runtime/examples/simple.js
 create mode 100644 runtime/jvm/src/test/java/org/apache/camel/k/jvm/RuntimeTest.java
 create mode 100644 runtime/jvm/src/test/resources/r1.js
 create mode 100644 runtime/jvm/src/test/resources/r2.mytype


[camel-k] 01/02: Support for multiple integration definitions #45

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

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

commit 0eea5e78310fcc6f777207ec363cc3bdf12f5a37
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Wed Nov 21 23:53:09 2018 +0100

    Support for multiple integration definitions #45
---
 pkg/apis/camel/v1alpha1/types.go                   |   7 +-
 pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go   |  32 +++---
 pkg/client/cmd/run.go                              | 128 +++++++++++----------
 pkg/stub/action/integration/initialize.go          |  10 +-
 pkg/trait/dependencies.go                          |  26 +++--
 pkg/trait/deployment.go                            | 123 ++++++++++++++------
 pkg/trait/knative.go                               |  31 ++++-
 pkg/trait/trait_test.go                            |   8 +-
 pkg/util/digest/digest.go                          |   7 +-
 pkg/util/kubernetes/collection.go                  |   5 +
 runtime/examples/routes.js                         |   8 +-
 runtime/examples/simple.groovy                     |   6 +
 runtime/examples/simple.js                         |   6 +
 .../org/apache/camel/k/groovy/LoaderTest.groovy    |   4 +-
 .../camel/k/groovy/dsl/IntegrationTest.groovy      |   6 +-
 .../k/groovy/dsl/extension/LogExtensionTest.groovy |   2 +-
 .../java/org/apache/camel/k/jvm/Application.java   |   9 +-
 .../java/org/apache/camel/k/jvm/Constants.java     |   5 +-
 .../java/org/apache/camel/k/jvm/RoutesLoaders.java |  38 +++---
 .../main/java/org/apache/camel/k/jvm/Runtime.java  |  27 +++--
 .../org/apache/camel/k/jvm/RuntimeSupport.java     |   5 +-
 .../java/org/apache/camel/k/jvm/URIResolver.java   |  29 +++--
 .../java/org/apache/camel/k/jvm/RuntimeTest.java   |  51 ++++++++
 runtime/jvm/src/test/resources/r1.js               |   4 +
 runtime/jvm/src/test/resources/r2.mytype           |   4 +
 .../apache/camel/k/kotlin/KotlinRoutesLoader.kt    |  10 +-
 .../apache/camel/k/kotlin/dsl/IntegrationTest.kt   |   6 +-
 27 files changed, 391 insertions(+), 206 deletions(-)

diff --git a/pkg/apis/camel/v1alpha1/types.go b/pkg/apis/camel/v1alpha1/types.go
index b872ac3..691b528 100644
--- a/pkg/apis/camel/v1alpha1/types.go
+++ b/pkg/apis/camel/v1alpha1/types.go
@@ -50,7 +50,7 @@ type Integration struct {
 // IntegrationSpec --
 type IntegrationSpec struct {
 	Replicas      *int32                          `json:"replicas,omitempty"`
-	Source        SourceSpec                      `json:"source,omitempty"`
+	Sources       []SourceSpec                    `json:"sources,omitempty"`
 	Context       string                          `json:"context,omitempty"`
 	Dependencies  []string                        `json:"dependencies,omitempty"`
 	Profile       TraitProfile                    `json:"profile,omitempty"`
@@ -58,6 +58,11 @@ type IntegrationSpec struct {
 	Configuration []ConfigurationSpec             `json:"configuration,omitempty"`
 }
 
+// AddSource --
+func (is *IntegrationSpec) AddSource(name string, content string, language Language) {
+	is.Sources = append(is.Sources, SourceSpec{Name: name, Content: content, Language: language})
+}
+
 // SourceSpec --
 type SourceSpec struct {
 	Name     string   `json:"name,omitempty"`
diff --git a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
index 202fd25..4aa466b 100644
--- a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
@@ -337,28 +337,32 @@ func (in *IntegrationPlatformStatus) DeepCopy() *IntegrationPlatformStatus {
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *IntegrationSpec) DeepCopyInto(out *IntegrationSpec) {
-	*out = *in
-	if in.Replicas != nil {
-		in, out := &in.Replicas, &out.Replicas
+func (is *IntegrationSpec) DeepCopyInto(out *IntegrationSpec) {
+	*out = *is
+	if is.Replicas != nil {
+		in, out := &is.Replicas, &out.Replicas
 		*out = new(int32)
 		**out = **in
 	}
-	out.Source = in.Source
-	if in.Dependencies != nil {
-		in, out := &in.Dependencies, &out.Dependencies
+	if is.Sources != nil {
+		in, out := &is.Sources, &out.Sources
+		*out = make([]SourceSpec, len(*in))
+		copy(*out, *in)
+	}
+	if is.Dependencies != nil {
+		in, out := &is.Dependencies, &out.Dependencies
 		*out = make([]string, len(*in))
 		copy(*out, *in)
 	}
-	if in.Traits != nil {
-		in, out := &in.Traits, &out.Traits
+	if is.Traits != nil {
+		in, out := &is.Traits, &out.Traits
 		*out = make(map[string]IntegrationTraitSpec, len(*in))
 		for key, val := range *in {
 			(*out)[key] = *val.DeepCopy()
 		}
 	}
-	if in.Configuration != nil {
-		in, out := &in.Configuration, &out.Configuration
+	if is.Configuration != nil {
+		in, out := &is.Configuration, &out.Configuration
 		*out = make([]ConfigurationSpec, len(*in))
 		copy(*out, *in)
 	}
@@ -366,12 +370,12 @@ func (in *IntegrationSpec) DeepCopyInto(out *IntegrationSpec) {
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntegrationSpec.
-func (in *IntegrationSpec) DeepCopy() *IntegrationSpec {
-	if in == nil {
+func (is *IntegrationSpec) DeepCopy() *IntegrationSpec {
+	if is == nil {
 		return nil
 	}
 	out := new(IntegrationSpec)
-	in.DeepCopyInto(out)
+	is.DeepCopyInto(out)
 	return out
 }
 
diff --git a/pkg/client/cmd/run.go b/pkg/client/cmd/run.go
index bf2c268..bfc0445 100644
--- a/pkg/client/cmd/run.go
+++ b/pkg/client/cmd/run.go
@@ -20,29 +20,31 @@ package cmd
 import (
 	"encoding/json"
 	"fmt"
-	"github.com/operator-framework/operator-sdk/pkg/util/k8sutil"
-	"gopkg.in/yaml.v2"
 	"io/ioutil"
-	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
-	"k8s.io/apimachinery/pkg/runtime"
 	"net/http"
 	"os"
 	"os/signal"
+	"path"
 	"regexp"
 	"strconv"
 	"strings"
 	"syscall"
 
+	"github.com/operator-framework/operator-sdk/pkg/util/k8sutil"
+	"gopkg.in/yaml.v2"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/runtime"
+
 	"github.com/apache/camel-k/pkg/trait"
 	"github.com/apache/camel-k/pkg/util"
 
-	"github.com/apache/camel-k/pkg/util/sync"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 	"github.com/apache/camel-k/pkg/util/log"
+	"github.com/apache/camel-k/pkg/util/sync"
 	"github.com/apache/camel-k/pkg/util/watch"
 	"github.com/operator-framework/operator-sdk/pkg/sdk"
 	"github.com/spf13/cobra"
@@ -67,7 +69,6 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		RunE:  options.run,
 	}
 
-	cmd.Flags().StringVarP(&options.Language, "language", "l", "", "Programming Language used to write the file")
 	cmd.Flags().StringVarP(&options.Runtime, "runtime", "r", "", "Runtime used by the integration")
 	cmd.Flags().StringVar(&options.IntegrationName, "name", "", "The integration name")
 	cmd.Flags().StringSliceVarP(&options.Dependencies, "dependency", "d", nil, "The integration dependency")
@@ -93,7 +94,6 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) *cobra.Command {
 type runCmdOptions struct {
 	*RootCmdOptions
 	IntegrationContext string
-	Language           string
 	Runtime            string
 	IntegrationName    string
 	Dependencies       []string
@@ -111,22 +111,27 @@ type runCmdOptions struct {
 }
 
 func (o *runCmdOptions) validateArgs(cmd *cobra.Command, args []string) error {
-	if len(args) != 1 {
-		return errors.New("accepts 1 arg, received " + strconv.Itoa(len(args)))
+	if len(args) < 1 {
+		return errors.New("accepts at least 1 arg, received 0")
 	}
-	fileName := args[0]
-	if !strings.HasPrefix(fileName, "http://") && !strings.HasPrefix(fileName, "https://") {
-		if _, err := os.Stat(fileName); err != nil && os.IsNotExist(err) {
-			return errors.Wrap(err, "file "+fileName+" does not exist")
-		} else if err != nil {
-			return errors.Wrap(err, "error while accessing file "+fileName)
-		}
-	} else {
-		resp, err := http.Get(fileName)
-		if err != nil {
-			return errors.Wrap(err, "The URL provided is not reachable")
-		} else if resp.StatusCode != 200 {
-			return errors.New("The URL provided is not reachable " + fileName + " The error code returned is " + strconv.Itoa(resp.StatusCode))
+	if len(args) > 1 && o.IntegrationName == "" {
+		return errors.New("integration name is mandatory when loading multiple integrations")
+	}
+
+	for _, fileName := range args {
+		if !strings.HasPrefix(fileName, "http://") && !strings.HasPrefix(fileName, "https://") {
+			if _, err := os.Stat(fileName); err != nil && os.IsNotExist(err) {
+				return errors.Wrap(err, "file "+fileName+" does not exist")
+			} else if err != nil {
+				return errors.Wrap(err, "error while accessing file "+fileName)
+			}
+		} else {
+			resp, err := http.Get(fileName)
+			if err != nil {
+				return errors.Wrap(err, "The URL provided is not reachable")
+			} else if resp.StatusCode != 200 {
+				return errors.New("The URL provided is not reachable " + fileName + " The error code returned is " + strconv.Itoa(resp.StatusCode))
+			}
 		}
 	}
 
@@ -165,7 +170,7 @@ func (o *runCmdOptions) run(cmd *cobra.Command, args []string) error {
 	}
 
 	if o.Sync || o.Dev {
-		err = o.syncIntegration(args[0])
+		err = o.syncIntegration(args)
 		if err != nil {
 			return err
 		}
@@ -215,36 +220,35 @@ func (o *runCmdOptions) waitForIntegrationReady(integration *v1alpha1.Integratio
 	return watch.HandleStateChanges(o.Context, integration, handler)
 }
 
-func (o *runCmdOptions) syncIntegration(file string) error {
-	changes, err := sync.File(o.Context, file)
-	if err != nil {
-		return err
-	}
-	go func() {
-		for {
-			select {
-			case <-o.Context.Done():
-				return
-			case <-changes:
-				_, err := o.updateIntegrationCode(file)
-				if err != nil {
-					logrus.Error("Unable to sync integration: ", err)
+func (o *runCmdOptions) syncIntegration(sources []string) error {
+	for _, s := range sources {
+		changes, err := sync.File(o.Context, s)
+		if err != nil {
+			return err
+		}
+		go func() {
+			for {
+				select {
+				case <-o.Context.Done():
+					return
+				case <-changes:
+					_, err := o.updateIntegrationCode(sources)
+					if err != nil {
+						logrus.Error("Unable to sync integration: ", err)
+					}
 				}
 			}
-		}
-	}()
+		}()
+	}
+
 	return nil
 }
 
 func (o *runCmdOptions) createIntegration(cmd *cobra.Command, args []string) (*v1alpha1.Integration, error) {
-	return o.updateIntegrationCode(args[0])
+	return o.updateIntegrationCode(args)
 }
 
-func (o *runCmdOptions) updateIntegrationCode(filename string) (*v1alpha1.Integration, error) {
-	code, err := o.loadCode(filename)
-	if err != nil {
-		return nil, err
-	}
+func (o *runCmdOptions) updateIntegrationCode(sources []string) (*v1alpha1.Integration, error) {
 
 	namespace := o.Namespace
 
@@ -252,17 +256,13 @@ func (o *runCmdOptions) updateIntegrationCode(filename string) (*v1alpha1.Integr
 	if o.IntegrationName != "" {
 		name = o.IntegrationName
 		name = kubernetes.SanitizeName(name)
-	} else {
-		name = kubernetes.SanitizeName(filename)
+	} else if len(sources) == 1 {
+		name = kubernetes.SanitizeName(sources[0])
 		if name == "" {
 			name = "integration"
 		}
-	}
-
-	codeName := filename
-
-	if idx := strings.LastIndexByte(filename, os.PathSeparator); idx > -1 {
-		codeName = codeName[idx+1:]
+	} else {
+		return nil, errors.New("invalid argument combination")
 	}
 
 	integration := v1alpha1.Integration{
@@ -275,11 +275,6 @@ func (o *runCmdOptions) updateIntegrationCode(filename string) (*v1alpha1.Integr
 			Name:      name,
 		},
 		Spec: v1alpha1.IntegrationSpec{
-			Source: v1alpha1.SourceSpec{
-				Name:     codeName,
-				Content:  code,
-				Language: v1alpha1.Language(o.Language),
-			},
 			Dependencies:  make([]string, 0, len(o.Dependencies)),
 			Context:       o.IntegrationContext,
 			Configuration: make([]v1alpha1.ConfigurationSpec, 0),
@@ -287,6 +282,15 @@ func (o *runCmdOptions) updateIntegrationCode(filename string) (*v1alpha1.Integr
 		},
 	}
 
+	for _, source := range sources {
+		code, err := o.loadCode(source)
+		if err != nil {
+			return nil, err
+		}
+
+		integration.Spec.AddSource(path.Base(source), code, "")
+	}
+
 	for _, item := range o.Dependencies {
 		if strings.HasPrefix(item, "mvn:") {
 			integration.Spec.Dependencies = append(integration.Spec.Dependencies, item)
@@ -336,7 +340,7 @@ func (o *runCmdOptions) updateIntegrationCode(filename string) (*v1alpha1.Integr
 	case "":
 		// continue..
 	case "yaml":
-		jsondata, err := toJson(&integration)
+		jsondata, err := toJSON(&integration)
 		if err != nil {
 			return nil, err
 		}
@@ -348,7 +352,7 @@ func (o *runCmdOptions) updateIntegrationCode(filename string) (*v1alpha1.Integr
 		return nil, nil
 
 	case "json":
-		data, err := toJson(&integration)
+		data, err := toJSON(&integration)
 		if err != nil {
 			return nil, err
 		}
@@ -360,7 +364,7 @@ func (o *runCmdOptions) updateIntegrationCode(filename string) (*v1alpha1.Integr
 	}
 
 	existed := false
-	err = sdk.Create(&integration)
+	err := sdk.Create(&integration)
 	if err != nil && k8serrors.IsAlreadyExists(err) {
 		existed = true
 		clone := integration.DeepCopy()
@@ -384,7 +388,7 @@ func (o *runCmdOptions) updateIntegrationCode(filename string) (*v1alpha1.Integr
 	return &integration, nil
 }
 
-func toJson(value runtime.Object) ([]byte, error) {
+func toJSON(value runtime.Object) ([]byte, error) {
 	u, err := k8sutil.UnstructuredFromRuntimeObject(value)
 	if err != nil {
 		return nil, fmt.Errorf("error creating unstructured data: %v", err)
diff --git a/pkg/stub/action/integration/initialize.go b/pkg/stub/action/integration/initialize.go
index fa02315..b495b11 100644
--- a/pkg/stub/action/integration/initialize.go
+++ b/pkg/stub/action/integration/initialize.go
@@ -59,9 +59,13 @@ func (action *initializeAction) Handle(integration *v1alpha1.Integration) error
 		var defaultReplicas int32 = 1
 		target.Spec.Replicas = &defaultReplicas
 	}
-	// extract metadata
-	meta := metadata.Extract(target.Spec.Source)
-	target.Spec.Source.Language = meta.Language
+	for i := range target.Spec.Sources {
+		// extract metadata
+		s := &target.Spec.Sources[i]
+
+		meta := metadata.Extract(*s)
+		s.Language = meta.Language
+	}
 
 	// execute custom initialization
 	if _, err := trait.Apply(target, nil); err != nil {
diff --git a/pkg/trait/dependencies.go b/pkg/trait/dependencies.go
index f284678..b57d097 100644
--- a/pkg/trait/dependencies.go
+++ b/pkg/trait/dependencies.go
@@ -39,21 +39,23 @@ func (*dependenciesTrait) appliesTo(e *Environment) bool {
 	return e.Integration != nil && e.Integration.Status.Phase == ""
 }
 
-func (d *dependenciesTrait) apply(e *Environment) error {
-	meta := metadata.Extract(e.Integration.Spec.Source)
+func (*dependenciesTrait) apply(e *Environment) error {
+	for _, s := range e.Integration.Spec.Sources {
+		meta := metadata.Extract(s)
 
-	if meta.Language == v1alpha1.LanguageGroovy {
-		util.StringSliceUniqueAdd(&e.Integration.Spec.Dependencies, "runtime:groovy")
-	} else if meta.Language == v1alpha1.LanguageKotlin {
-		util.StringSliceUniqueAdd(&e.Integration.Spec.Dependencies, "runtime:kotlin")
-	}
+		if meta.Language == v1alpha1.LanguageGroovy {
+			util.StringSliceUniqueAdd(&e.Integration.Spec.Dependencies, "runtime:groovy")
+		} else if meta.Language == v1alpha1.LanguageKotlin {
+			util.StringSliceUniqueAdd(&e.Integration.Spec.Dependencies, "runtime:kotlin")
+		}
 
-	// jvm runtime and camel-core required by default
-	util.StringSliceUniqueAdd(&e.Integration.Spec.Dependencies, "runtime:jvm")
-	util.StringSliceUniqueAdd(&e.Integration.Spec.Dependencies, "camel:core")
+		// jvm runtime and camel-core required by default
+		util.StringSliceUniqueAdd(&e.Integration.Spec.Dependencies, "runtime:jvm")
+		util.StringSliceUniqueAdd(&e.Integration.Spec.Dependencies, "camel:core")
 
-	for _, d := range meta.Dependencies {
-		util.StringSliceUniqueAdd(&e.Integration.Spec.Dependencies, d)
+		for _, d := range meta.Dependencies {
+			util.StringSliceUniqueAdd(&e.Integration.Spec.Dependencies, d)
+		}
 	}
 
 	// sort the dependencies to get always the same list if they don't change
diff --git a/pkg/trait/deployment.go b/pkg/trait/deployment.go
index 8023050..cc16ef1 100644
--- a/pkg/trait/deployment.go
+++ b/pkg/trait/deployment.go
@@ -25,6 +25,7 @@ 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"
 )
 
 type deploymentTrait struct {
@@ -42,8 +43,8 @@ func (d *deploymentTrait) appliesTo(e *Environment) bool {
 }
 
 func (d *deploymentTrait) apply(e *Environment) error {
-	e.Resources.Add(getConfigMapFor(e))
-	e.Resources.Add(getDeploymentFor(e))
+	e.Resources.AddAll(d.getConfigMapFor(e))
+	e.Resources.Add(d.getDeploymentFor(e))
 	return nil
 }
 
@@ -53,34 +54,60 @@ func (d *deploymentTrait) apply(e *Environment) error {
 //
 // **********************************
 
-func getConfigMapFor(e *Environment) *corev1.ConfigMap {
+func (*deploymentTrait) getConfigMapFor(e *Environment) []runtime.Object {
+	maps := make([]runtime.Object, 0, len(e.Integration.Spec.Sources)+1)
+
 	// combine properties of integration with context, integration
 	// properties have the priority
 	properties := CombineConfigurationAsMap("property", e.Context, e.Integration)
 
-	cm := corev1.ConfigMap{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "ConfigMap",
-			APIVersion: "v1",
-		},
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      e.Integration.Name,
-			Namespace: e.Integration.Namespace,
-			Labels: map[string]string{
-				"camel.apache.org/integration": e.Integration.Name,
+	maps = append(
+		maps,
+		&corev1.ConfigMap{
+			TypeMeta: metav1.TypeMeta{
+				Kind:       "ConfigMap",
+				APIVersion: "v1",
 			},
-			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,
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      e.Integration.Name + "-properties",
+				Namespace: e.Integration.Namespace,
+				Labels: map[string]string{
+					"camel.apache.org/integration": e.Integration.Name,
+				},
+			},
+			Data: map[string]string{
+				"properties": PropertiesString(properties),
 			},
 		},
-		Data: map[string]string{
-			"integration": e.Integration.Spec.Source.Content,
-			"properties":  PropertiesString(properties),
-		},
+	)
+
+	for i, s := range e.Integration.Spec.Sources {
+		maps = append(
+			maps,
+			&corev1.ConfigMap{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "ConfigMap",
+					APIVersion: "v1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      fmt.Sprintf("%s-source-%03d", e.Integration.Name, i),
+					Namespace: e.Integration.Namespace,
+					Labels: map[string]string{
+						"camel.apache.org/integration": e.Integration.Name,
+					},
+					Annotations: map[string]string{
+						"camel.apache.org/source.language": string(s.Language),
+						"camel.apache.org/source.name":     s.Name,
+					},
+				},
+				Data: map[string]string{
+					"integration": s.Content,
+				},
+			},
+		)
 	}
 
-	return &cm
+	return maps
 }
 
 // **********************************
@@ -89,8 +116,16 @@ func getConfigMapFor(e *Environment) *corev1.ConfigMap {
 //
 // **********************************
 
-func getDeploymentFor(e *Environment) *appsv1.Deployment {
-	sourceName := strings.TrimPrefix(e.Integration.Spec.Source.Name, "/")
+func (*deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
+	sources := make([]string, 0, len(e.Integration.Spec.Sources))
+	for i, s := range e.Integration.Spec.Sources {
+		src := fmt.Sprintf("file:/etc/camel/integrations/%03d/%s", i, strings.TrimPrefix(s.Name, "/"))
+		if s.Language != "" {
+			src = src + "?language=" + string(s.Language)
+		}
+
+		sources = append(sources, src)
+	}
 
 	// combine Environment of integration with context, integration
 	// Environment has the priority
@@ -100,8 +135,7 @@ func getDeploymentFor(e *Environment) *appsv1.Deployment {
 	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(e.Integration.Spec.Source.Language)
+	environment["CAMEL_K_ROUTES"] = strings.Join(sources, ",")
 	environment["CAMEL_K_CONF"] = "/etc/camel/conf/application.properties"
 	environment["CAMEL_K_CONF_D"] = "/etc/camel/conf.d"
 
@@ -159,21 +193,18 @@ func getDeploymentFor(e *Environment) *appsv1.Deployment {
 	cnt := 0
 
 	//
-	// Volumes :: Defaults
+	// Volumes :: Properties
 	//
 
 	vols = append(vols, corev1.Volume{
-		Name: "integration",
+		Name: "integration-properties",
 		VolumeSource: corev1.VolumeSource{
 			ConfigMap: &corev1.ConfigMapVolumeSource{
 				LocalObjectReference: corev1.LocalObjectReference{
-					Name: e.Integration.Name,
+					Name: e.Integration.Name + "-properties",
 				},
 				Items: []corev1.KeyToPath{
 					{
-						Key:  "integration",
-						Path: sourceName,
-					}, {
 						Key:  "properties",
 						Path: "application.properties",
 					},
@@ -183,11 +214,39 @@ func getDeploymentFor(e *Environment) *appsv1.Deployment {
 	})
 
 	mnts = append(mnts, corev1.VolumeMount{
-		Name:      "integration",
+		Name:      "integration-properties",
 		MountPath: "/etc/camel/conf",
 	})
 
 	//
+	// Volumes :: Sources
+	//
+
+	for i, s := range e.Integration.Spec.Sources {
+		vols = append(vols, corev1.Volume{
+			Name: fmt.Sprintf("integration-source-%03d", i),
+			VolumeSource: corev1.VolumeSource{
+				ConfigMap: &corev1.ConfigMapVolumeSource{
+					LocalObjectReference: corev1.LocalObjectReference{
+						Name: fmt.Sprintf("%s-source-%03d", e.Integration.Name, i),
+					},
+					Items: []corev1.KeyToPath{
+						{
+							Key:  "integration",
+							Path: strings.TrimPrefix(s.Name, "/"),
+						},
+					},
+				},
+			},
+		})
+
+		mnts = append(mnts, corev1.VolumeMount{
+			Name:      fmt.Sprintf("integration-source-%03d", i),
+			MountPath: fmt.Sprintf("/etc/camel/integrations/%03d", i),
+		})
+	}
+
+	//
 	// Volumes :: Additional ConfigMaps
 	//
 
diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go
index b122f8d..0b93d87 100644
--- a/pkg/trait/knative.go
+++ b/pkg/trait/knative.go
@@ -19,6 +19,7 @@ package trait
 
 import (
 	"encoding/json"
+	"fmt"
 	"strings"
 
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
@@ -72,14 +73,26 @@ func (t *knativeTrait) getServiceFor(e *Environment) *serving.Service {
 	// Environment has the priority
 	environment := CombineConfigurationAsMap("env", e.Context, e.Integration)
 
+	sources := make([]string, 0, len(e.Integration.Spec.Sources))
+	for i, s := range e.Integration.Spec.Sources {
+		envName := fmt.Sprintf("KAMEL_K_ROUTE_%03d", i)
+		environment[envName] = s.Content
+
+		src := fmt.Sprintf("env:%s", envName)
+		if s.Language != "" {
+			src = src + "?language=" + string(s.Language)
+		}
+
+		sources = append(sources, src)
+	}
+
 	// 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"] = "inline:" + e.Integration.Spec.Source.Content
-	environment["CAMEL_K_ROUTES_LANGUAGE"] = string(e.Integration.Spec.Source.Language)
-	environment["CAMEL_K_CONF"] = "inline:" + PropertiesString(properties)
-	environment["CAMEL_K_CONF_D"] = "/etc/camel/conf.d"
+	environment["CAMEL_K_ROUTES"] = strings.Join(sources, ",")
+	environment["CAMEL_K_CONF"] = "env:CAMEL_K_PROPERTIES"
+	environment["CAMEL_K_PROPERTIES"] = PropertiesString(properties)
 
 	// add a dummy env var to trigger deployment if everything but the code
 	// has been changed
@@ -214,6 +227,12 @@ func (t *knativeTrait) getConfiguredSourceChannels() []string {
 }
 
 func (*knativeTrait) getSourceChannels(e *Environment) []string {
-	meta := metadata.Extract(e.Integration.Spec.Source)
-	return knativeutil.ExtractChannelNames(meta.FromURIs)
+	channels := make([]string, 0)
+
+	for _, s := range e.Integration.Spec.Sources {
+		meta := metadata.Extract(s)
+		channels = append(channels, knativeutil.ExtractChannelNames(meta.FromURIs)...)
+	}
+
+	return channels
 }
diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go
index 17d8909..cb3acd5 100644
--- a/pkg/trait/trait_test.go
+++ b/pkg/trait/trait_test.go
@@ -38,7 +38,7 @@ func TestOpenShiftTraits(t *testing.T) {
 	assert.NotContains(t, env.ExecutedTraits, ID("route"))
 	assert.Contains(t, env.ExecutedTraits, ID("owner"))
 	assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool {
-		return cm.Name == "test"
+		return cm.Name == "test-properties"
 	}))
 	assert.NotNil(t, res.GetDeployment(func(deployment *appsv1.Deployment) bool {
 		return deployment.Name == "test"
@@ -53,7 +53,7 @@ func TestOpenShiftTraitsWithWeb(t *testing.T) {
 	assert.Contains(t, env.ExecutedTraits, ID("route"))
 	assert.Contains(t, env.ExecutedTraits, ID("owner"))
 	assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool {
-		return cm.Name == "test"
+		return cm.Name == "test-properties"
 	}))
 	assert.NotNil(t, res.GetDeployment(func(deployment *appsv1.Deployment) bool {
 		return deployment.Name == "test"
@@ -107,7 +107,7 @@ func TestKubernetesTraits(t *testing.T) {
 	assert.NotContains(t, env.ExecutedTraits, ID("route"))
 	assert.Contains(t, env.ExecutedTraits, ID("owner"))
 	assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool {
-		return cm.Name == "test"
+		return cm.Name == "test-properties"
 	}))
 	assert.NotNil(t, res.GetDeployment(func(deployment *appsv1.Deployment) bool {
 		return deployment.Name == "test"
@@ -122,7 +122,7 @@ func TestKubernetesTraitsWithWeb(t *testing.T) {
 	assert.NotContains(t, env.ExecutedTraits, ID("route"))
 	assert.Contains(t, env.ExecutedTraits, ID("owner"))
 	assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool {
-		return cm.Name == "test"
+		return cm.Name == "test-properties"
 	}))
 	assert.NotNil(t, res.GetDeployment(func(deployment *appsv1.Deployment) bool {
 		return deployment.Name == "test"
diff --git a/pkg/util/digest/digest.go b/pkg/util/digest/digest.go
index 265c664..2be054f 100644
--- a/pkg/util/digest/digest.go
+++ b/pkg/util/digest/digest.go
@@ -37,9 +37,12 @@ func ComputeForIntegration(integration *v1alpha1.Integration) string {
 	hash.Write([]byte(integration.Spec.Context))
 
 	// Integration code
-	if integration.Spec.Source.Content != "" {
-		hash.Write([]byte(integration.Spec.Source.Content))
+	for _, s := range integration.Spec.Sources {
+		if s.Content != "" {
+			hash.Write([]byte(s.Content))
+		}
 	}
+
 	// Integration dependencies
 	for _, item := range integration.Spec.Dependencies {
 		hash.Write([]byte(item))
diff --git a/pkg/util/kubernetes/collection.go b/pkg/util/kubernetes/collection.go
index 54b6ba7..fbc9098 100644
--- a/pkg/util/kubernetes/collection.go
+++ b/pkg/util/kubernetes/collection.go
@@ -47,6 +47,11 @@ func (c *Collection) Add(resource runtime.Object) {
 	c.items = append(c.items, resource)
 }
 
+// AddAll adds all resources to the collection
+func (c *Collection) AddAll(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) {
diff --git a/runtime/examples/routes.js b/runtime/examples/routes.js
index a020511..edb7806 100644
--- a/runtime/examples/routes.js
+++ b/runtime/examples/routes.js
@@ -5,10 +5,10 @@
 //
 // ****************
 
-l = components.get('log')
-l.exchangeFormatter = function(e) {
-    return "log - body=" + e.in.body + ", headers=" + e.in.headers
-}
+//l = components.get('log')
+//l.exchangeFormatter = function(e) {
+//    return "log - body=" + e.in.body + ", headers=" + e.in.headers
+//}
 
 // ****************
 //
diff --git a/runtime/examples/simple.groovy b/runtime/examples/simple.groovy
new file mode 100644
index 0000000..7546207
--- /dev/null
+++ b/runtime/examples/simple.groovy
@@ -0,0 +1,6 @@
+
+from('timer:groovy?period=1s')
+    .routeId('groovy')
+    .setBody()
+        .simple('Hello Camel K from ${routeId}')
+    .to('log:info?showAll=false')
diff --git a/runtime/examples/simple.js b/runtime/examples/simple.js
new file mode 100644
index 0000000..918c544
--- /dev/null
+++ b/runtime/examples/simple.js
@@ -0,0 +1,6 @@
+
+from('timer:js?period=1s')
+    .routeId('js')
+    .setBody()
+        .simple('Hello Camel K from ${routeId}')
+    .to('log:info?multiline=true')
\ No newline at end of file
diff --git a/runtime/groovy/src/test/groovy/org/apache/camel/k/groovy/LoaderTest.groovy b/runtime/groovy/src/test/groovy/org/apache/camel/k/groovy/LoaderTest.groovy
index e24e0cf..16b4f0f 100644
--- a/runtime/groovy/src/test/groovy/org/apache/camel/k/groovy/LoaderTest.groovy
+++ b/runtime/groovy/src/test/groovy/org/apache/camel/k/groovy/LoaderTest.groovy
@@ -29,8 +29,8 @@ class LoaderTest extends Specification {
             def resource = "classpath:routes.groovy"
 
         when:
-            def loader = RoutesLoaders.loaderFor(resource, null);
-            def builder = loader.load(new RuntimeRegistry(), resource);
+            def loader = RoutesLoaders.loaderFor(resource, null)
+            def builder = loader.load(new RuntimeRegistry(), resource)
 
         then:
             loader instanceof GroovyRoutesLoader
diff --git a/runtime/groovy/src/test/groovy/org/apache/camel/k/groovy/dsl/IntegrationTest.groovy b/runtime/groovy/src/test/groovy/org/apache/camel/k/groovy/dsl/IntegrationTest.groovy
index 1b6a1e6..32904f1 100644
--- a/runtime/groovy/src/test/groovy/org/apache/camel/k/groovy/dsl/IntegrationTest.groovy
+++ b/runtime/groovy/src/test/groovy/org/apache/camel/k/groovy/dsl/IntegrationTest.groovy
@@ -32,7 +32,7 @@ class IntegrationTest extends Specification {
         when:
         def runtime = new Runtime()
         runtime.setDuration(5)
-        runtime.load('classpath:routes-with-rest.groovy', null)
+        runtime.load(['classpath:routes-with-rest.groovy'])
         runtime.addMainListener(new MainListenerSupport() {
             @Override
             void afterStart(MainSupport main) {
@@ -55,7 +55,7 @@ class IntegrationTest extends Specification {
         when:
         def runtime = new Runtime()
         runtime.setDuration(5)
-        runtime.load('classpath:routes-with-bindings.groovy', null)
+        runtime.load(['classpath:routes-with-bindings.groovy'])
         runtime.addMainListener(new MainListenerSupport() {
             @Override
             void afterStart(MainSupport main) {
@@ -82,7 +82,7 @@ class IntegrationTest extends Specification {
         when:
         def runtime = new Runtime()
         runtime.setDuration(5)
-        runtime.load('classpath:routes-with-component-configuration.groovy', null)
+        runtime.load(['classpath:routes-with-component-configuration.groovy'])
         runtime.addMainListener(new MainListenerSupport() {
             @Override
             void afterStart(MainSupport main) {
diff --git a/runtime/groovy/src/test/groovy/org/apache/camel/k/groovy/dsl/extension/LogExtensionTest.groovy b/runtime/groovy/src/test/groovy/org/apache/camel/k/groovy/dsl/extension/LogExtensionTest.groovy
index 68bad92..fee6475 100644
--- a/runtime/groovy/src/test/groovy/org/apache/camel/k/groovy/dsl/extension/LogExtensionTest.groovy
+++ b/runtime/groovy/src/test/groovy/org/apache/camel/k/groovy/dsl/extension/LogExtensionTest.groovy
@@ -30,7 +30,7 @@ class LogExtensionTest extends Specification {
         when:
         def log = new LogComponent()
         log.formatter {
-            "body: " + it.in.body
+            "body: $it.in.body"
         }
 
         def ex = new DefaultExchange(ctx)
diff --git a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Application.java b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Application.java
index 0c22f03..d26ddef 100644
--- a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Application.java
+++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Application.java
@@ -52,15 +52,14 @@ public class Application {
     // *******************************
 
     public static void main(String[] args) throws Exception {
-        final String resource = System.getenv(Constants.ENV_CAMEL_K_ROUTES_URI);
-        final String language = System.getenv(Constants.ENV_CAMEL_K_ROUTES_LANGUAGE);
+        final String routes = System.getenv(Constants.ENV_CAMEL_K_ROUTES);
 
-        if (ObjectHelper.isEmpty(resource)) {
-            throw new IllegalStateException("No valid resource found in " + Constants.ENV_CAMEL_K_ROUTES_URI + " environment variable");
+        if (ObjectHelper.isEmpty(routes)) {
+            throw new IllegalStateException("No valid routes found in " + Constants.ENV_CAMEL_K_ROUTES + " environment variable");
         }
 
         Runtime runtime = new Runtime();
-        runtime.load(resource, language);
+        runtime.load(routes.split(",", -1));
         runtime.addMainListener(new ComponentPropertiesBinder());
         runtime.run();
     }
diff --git a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Constants.java b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Constants.java
index e5a09ed..d3cb4b7 100644
--- a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Constants.java
+++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Constants.java
@@ -17,13 +17,12 @@
 package org.apache.camel.k.jvm;
 
 public final class Constants {
-    public static final String ENV_CAMEL_K_ROUTES_URI = "CAMEL_K_ROUTES_URI";
-    public static final String ENV_CAMEL_K_ROUTES_LANGUAGE = "CAMEL_K_ROUTES_LANGUAGE";
+    public static final String ENV_CAMEL_K_ROUTES = "CAMEL_K_ROUTES";
     public static final String ENV_CAMEL_K_CONF = "CAMEL_K_CONF";
     public static final String ENV_CAMEL_K_CONF_D = "CAMEL_K_CONF_D";
     public static final String SCHEME_CLASSPATH = "classpath:";
     public static final String SCHEME_FILE = "file:";
-    public static final String SCHEME_INLINE = "inline:";
+    public static final String SCHEME_ENV = "env:";
     public static final String LOGGING_LEVEL_PREFIX = "logging.level.";
 
     private Constants() {
diff --git a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RoutesLoaders.java b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RoutesLoaders.java
index 35cb47d..0d81738 100644
--- a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RoutesLoaders.java
+++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RoutesLoaders.java
@@ -16,6 +16,20 @@
  */
 package org.apache.camel.k.jvm;
 
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.List;
+import java.util.ServiceLoader;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import javax.script.Bindings;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.SimpleBindings;
+import javax.xml.bind.UnmarshalException;
+
 import org.apache.camel.CamelContext;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.k.jvm.dsl.Components;
@@ -29,24 +43,6 @@ import org.joor.Reflect;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.script.Bindings;
-import javax.script.ScriptEngine;
-import javax.script.ScriptEngineManager;
-import javax.script.SimpleBindings;
-import javax.xml.bind.UnmarshalException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.util.Collections;
-import java.util.List;
-import java.util.ServiceLoader;
-import java.util.function.Function;
-import java.util.function.Supplier;
-
-import static org.apache.camel.k.jvm.Constants.SCHEME_CLASSPATH;
-import static org.apache.camel.k.jvm.Constants.SCHEME_FILE;
-import static org.apache.camel.k.jvm.Constants.SCHEME_INLINE;
-
 public final class RoutesLoaders {
     private static final Logger LOGGER = LoggerFactory.getLogger(RoutesLoaders.class);
 
@@ -62,7 +58,7 @@ public final class RoutesLoaders {
         @Override
         public RouteBuilder load(RuntimeRegistry registry, String resource) throws Exception {
             String path = resource;
-            path = StringUtils.removeStart(path, SCHEME_CLASSPATH);
+            path = StringUtils.removeStart(path, Constants.SCHEME_CLASSPATH);
             path = StringUtils.removeEnd(path, ".class");
 
             Class<?> type = Class.forName(path);
@@ -173,7 +169,9 @@ public final class RoutesLoaders {
 
 
     public static RoutesLoader loaderFor(String resource, String languageName) {
-        if (!resource.startsWith(SCHEME_CLASSPATH) && !resource.startsWith(SCHEME_FILE) && !resource.startsWith(SCHEME_INLINE)) {
+        if (!resource.startsWith(Constants.SCHEME_CLASSPATH) &&
+            !resource.startsWith(Constants.SCHEME_FILE) &&
+            !resource.startsWith(Constants.SCHEME_ENV)) {
             throw new IllegalArgumentException("No valid resource format, expected scheme:path, found " + resource);
         }
 
diff --git a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Runtime.java b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Runtime.java
index 72753f7..d4a755c 100644
--- a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Runtime.java
+++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Runtime.java
@@ -27,6 +27,8 @@ import org.apache.camel.component.properties.PropertiesComponent;
 import org.apache.camel.impl.CompositeRegistry;
 import org.apache.camel.impl.DefaultCamelContext;
 import org.apache.camel.main.MainSupport;
+import org.apache.camel.util.URISupport;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -40,18 +42,25 @@ public final class Runtime extends MainSupport {
         this.contextMap = new ConcurrentHashMap<>();
     }
 
-    public void load(String resource, String language) throws Exception {
-        final RoutesLoader loader = RoutesLoaders.loaderFor(resource, language);
-        final RouteBuilder routes = loader.load(registry, resource);
+    public void load(String[] routes) throws Exception {
+        for (String route: routes) {
+            // determine location and language
+            final String location = StringUtils.substringBefore(route, "?");
+            final String query = StringUtils.substringAfter(route, "?");
+            final String language = (String)URISupport.parseQuery(query).get("language");
 
-        if (routes == null) {
-            throw new IllegalStateException("Unable to load route from: " + resource);
-        }
+            // load routes
+            final RoutesLoader loader = RoutesLoaders.loaderFor(location, language);
+            final RouteBuilder builder = loader.load(registry, location);
+
+            if (routes == null) {
+                throw new IllegalStateException("Unable to load route from: " + route);
+            }
 
-        LOGGER.info("Routes: {}", resource);
-        LOGGER.info("Language: {}", language);
+            LOGGER.info("Routes: {}", route);
 
-        addRouteBuilder(routes);
+            addRouteBuilder(builder);
+        }
     }
 
     public RuntimeRegistry getRegistry() {
diff --git a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RuntimeSupport.java b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RuntimeSupport.java
index 44dc3a1..8b4db47 100644
--- a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RuntimeSupport.java
+++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RuntimeSupport.java
@@ -36,7 +36,6 @@ import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.config.LoggerConfig;
 
-import static org.apache.camel.k.jvm.Constants.SCHEME_INLINE;
 
 public final class RuntimeSupport {
     private RuntimeSupport() {
@@ -49,8 +48,8 @@ public final class RuntimeSupport {
 
         // Main location
         if (ObjectHelper.isNotEmpty(conf)) {
-            if (conf.startsWith(SCHEME_INLINE)) {
-                try (Reader reader = URIResolver.resolveInline(conf)) {
+            if (conf.startsWith(Constants.SCHEME_ENV)) {
+                try (Reader reader = URIResolver.resolveEnv(conf)) {
                     properties.load(reader);
                 } catch (IOException e) {
                     throw new RuntimeException(e);
diff --git a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/URIResolver.java b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/URIResolver.java
index b52e81c..12ade89 100644
--- a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/URIResolver.java
+++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/URIResolver.java
@@ -16,36 +16,43 @@
  */
 package org.apache.camel.k.jvm;
 
-import org.apache.camel.CamelContext;
-import org.apache.camel.util.ResourceHelper;
-
 import java.io.ByteArrayInputStream;
-import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
 import java.io.StringReader;
 
-import static org.apache.camel.k.jvm.Constants.SCHEME_INLINE;
+import org.apache.camel.CamelContext;
+import org.apache.camel.util.ResourceHelper;
+import org.apache.camel.util.StringHelper;
+
 
 public class URIResolver {
 
-    public static InputStream resolve(CamelContext ctx, String uri) throws IOException {
+    public static InputStream resolve(CamelContext ctx, String uri) throws Exception {
         if (uri == null) {
             throw new IllegalArgumentException("Cannot resolve null URI");
         }
-        if (uri.startsWith(SCHEME_INLINE)) {
+
+        if (uri.startsWith(Constants.SCHEME_ENV)) {
+            final String envvar = StringHelper.after(uri, ":");
+            final String content = System.getenv(envvar);
+
             // Using platform encoding on purpose
-            return new ByteArrayInputStream(uri.substring(SCHEME_INLINE.length()).getBytes());
+            return new ByteArrayInputStream(content.getBytes());
         }
 
         return ResourceHelper.resolveMandatoryResourceAsInputStream(ctx, uri);
     }
 
-    public static Reader resolveInline(String uri) {
-        if (!uri.startsWith(SCHEME_INLINE)) {
+    public static Reader resolveEnv(String uri) {
+        if (!uri.startsWith(Constants.SCHEME_ENV)) {
             throw new IllegalArgumentException("The provided content is not inline: " + uri);
         }
-        return new StringReader(uri.substring(SCHEME_INLINE.length()));
+
+        final String envvar = StringHelper.after(uri, ":");
+        final String content = System.getenv(envvar);
+
+        return new StringReader(content);
     }
 
 }
diff --git a/runtime/jvm/src/test/java/org/apache/camel/k/jvm/RuntimeTest.java b/runtime/jvm/src/test/java/org/apache/camel/k/jvm/RuntimeTest.java
new file mode 100644
index 0000000..909ae23
--- /dev/null
+++ b/runtime/jvm/src/test/java/org/apache/camel/k/jvm/RuntimeTest.java
@@ -0,0 +1,51 @@
+/**
+ * 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 org.apache.camel.k.jvm;
+
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Route;
+import org.apache.camel.util.ObjectHelper;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Java6Assertions.assertThat;
+
+public class RuntimeTest {
+
+    @Test
+    void testLoadMultipleRoutes() throws Exception {
+        Runtime runtime = new Runtime();
+        runtime.load(new String[]{
+            "classpath:r1.js",
+            "classpath:r2.mytype?language=js",
+        });
+
+        try {
+            runtime.start();
+
+            CamelContext context = runtime.getCamelContext();
+            List<Route> routes = context.getRoutes();
+
+            assertThat(routes).hasSize(2);
+            assertThat(routes).anyMatch(p -> ObjectHelper.equal("r1", p.getId()));
+            assertThat(routes).anyMatch(p -> ObjectHelper.equal("r2", p.getId()));
+        } finally {
+            runtime.stop();
+        }
+    }
+}
diff --git a/runtime/jvm/src/test/resources/r1.js b/runtime/jvm/src/test/resources/r1.js
new file mode 100644
index 0000000..f6ca425
--- /dev/null
+++ b/runtime/jvm/src/test/resources/r1.js
@@ -0,0 +1,4 @@
+
+from('timer:tick1')
+    .id('r1')
+    .to('log:info1')
\ No newline at end of file
diff --git a/runtime/jvm/src/test/resources/r2.mytype b/runtime/jvm/src/test/resources/r2.mytype
new file mode 100644
index 0000000..a0b33cc
--- /dev/null
+++ b/runtime/jvm/src/test/resources/r2.mytype
@@ -0,0 +1,4 @@
+
+from('timer:tick2')
+    .id('r2')
+    .to('log:info2')
\ No newline at end of file
diff --git a/runtime/kotlin/src/main/kotlin/org/apache/camel/k/kotlin/KotlinRoutesLoader.kt b/runtime/kotlin/src/main/kotlin/org/apache/camel/k/kotlin/KotlinRoutesLoader.kt
index 8602709..afbeb4f 100644
--- a/runtime/kotlin/src/main/kotlin/org/apache/camel/k/kotlin/KotlinRoutesLoader.kt
+++ b/runtime/kotlin/src/main/kotlin/org/apache/camel/k/kotlin/KotlinRoutesLoader.kt
@@ -85,12 +85,10 @@ class KotlinRoutesLoader : RoutesLoader {
                     )
 
                     for (report in result.reports) {
-                        if (report.severity == ScriptDiagnostic.Severity.ERROR) {
-                            LOGGER.error("{}", report.message, report.exception)
-                        } else if (report.severity == ScriptDiagnostic.Severity.WARNING) {
-                            LOGGER.warn("{}", report.message, report.exception)
-                        } else {
-                            LOGGER.info("{}", report.message)
+                        when {
+                            report.severity == ScriptDiagnostic.Severity.ERROR -> LOGGER.error("{}", report.message, report.exception)
+                            report.severity == ScriptDiagnostic.Severity.WARNING -> LOGGER.warn("{}", report.message, report.exception)
+                            else -> LOGGER.info("{}", report.message)
                         }
                     }
                 }
diff --git a/runtime/kotlin/src/test/kotlin/org/apache/camel/k/kotlin/dsl/IntegrationTest.kt b/runtime/kotlin/src/test/kotlin/org/apache/camel/k/kotlin/dsl/IntegrationTest.kt
index 734f278..4b6e85f 100644
--- a/runtime/kotlin/src/test/kotlin/org/apache/camel/k/kotlin/dsl/IntegrationTest.kt
+++ b/runtime/kotlin/src/test/kotlin/org/apache/camel/k/kotlin/dsl/IntegrationTest.kt
@@ -16,7 +16,7 @@ class IntegrationTest {
     fun `load integration with rest`() {
         var runtime = org.apache.camel.k.jvm.Runtime()
         runtime.duration = 5
-        runtime.load("classpath:routes-with-rest.kts", null)
+        runtime.load(arrayOf("classpath:routes-with-rest.kts"))
         runtime.addMainListener(object: MainListenerSupport() {
             override fun afterStart(main: MainSupport) {
                 main.stop()
@@ -37,7 +37,7 @@ class IntegrationTest {
     fun `load integration with binding`() {
         var runtime = org.apache.camel.k.jvm.Runtime()
         runtime.duration = 5
-        runtime.load("classpath:routes-with-bindings.kts", null)
+        runtime.load(arrayOf("classpath:routes-with-bindings.kts"))
         runtime.addMainListener(object: MainListenerSupport() {
             override fun afterStart(main: MainSupport) {
                 main.stop()
@@ -60,7 +60,7 @@ class IntegrationTest {
 
         var runtime = org.apache.camel.k.jvm.Runtime()
         runtime.duration = 5
-        runtime.load("classpath:routes-with-component-configuration.kts", null)
+        runtime.load(arrayOf("classpath:routes-with-component-configuration.kts"))
         runtime.addMainListener(object : MainListenerSupport() {
             override fun afterStart(main: MainSupport) {
                 val seda = runtime.camelContext.getComponent("seda", SedaComponent::class.java)


[camel-k] 02/02: fix findings

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

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

commit 3e9d907fec2e08ea00d923a7d120e287bf0373d8
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Thu Nov 22 14:24:11 2018 +0100

    fix findings
---
 pkg/client/cmd/run.go                                  | 11 +++++------
 pkg/metadata/metadata.go                               | 11 +++++++++++
 pkg/trait/deployment.go                                |  8 ++++----
 pkg/trait/knative.go                                   | 18 +++++++++---------
 .../main/java/org/apache/camel/k/jvm/URIResolver.java  |  2 +-
 5 files changed, 30 insertions(+), 20 deletions(-)

diff --git a/pkg/client/cmd/run.go b/pkg/client/cmd/run.go
index bfc0445..490adfe 100644
--- a/pkg/client/cmd/run.go
+++ b/pkg/client/cmd/run.go
@@ -115,7 +115,7 @@ func (o *runCmdOptions) validateArgs(cmd *cobra.Command, args []string) error {
 		return errors.New("accepts at least 1 arg, received 0")
 	}
 	if len(args) > 1 && o.IntegrationName == "" {
-		return errors.New("integration name is mandatory when loading multiple integrations")
+		return errors.New("integration name is mandatory when using multiple sources")
 	}
 
 	for _, fileName := range args {
@@ -258,11 +258,10 @@ func (o *runCmdOptions) updateIntegrationCode(sources []string) (*v1alpha1.Integ
 		name = kubernetes.SanitizeName(name)
 	} else if len(sources) == 1 {
 		name = kubernetes.SanitizeName(sources[0])
-		if name == "" {
-			name = "integration"
-		}
-	} else {
-		return nil, errors.New("invalid argument combination")
+	}
+
+	if name == "" {
+		return nil, errors.New("unable to determine integration name")
 	}
 
 	integration := v1alpha1.Integration{
diff --git a/pkg/metadata/metadata.go b/pkg/metadata/metadata.go
index 46a7aef..6e43b0b 100644
--- a/pkg/metadata/metadata.go
+++ b/pkg/metadata/metadata.go
@@ -34,3 +34,14 @@ func Extract(source v1alpha1.SourceSpec) IntegrationMetadata {
 		Dependencies: dependencies,
 	}
 }
+
+// Each --
+func Each(sources []v1alpha1.SourceSpec, consumer func(int, IntegrationMetadata) bool) {
+	for i, s := range sources {
+		meta := Extract(s)
+
+		if !consumer(i, meta) {
+			break
+		}
+	}
+}
diff --git a/pkg/trait/deployment.go b/pkg/trait/deployment.go
index cc16ef1..bc3f121 100644
--- a/pkg/trait/deployment.go
+++ b/pkg/trait/deployment.go
@@ -43,8 +43,8 @@ func (d *deploymentTrait) appliesTo(e *Environment) bool {
 }
 
 func (d *deploymentTrait) apply(e *Environment) error {
-	e.Resources.AddAll(d.getConfigMapFor(e))
-	e.Resources.Add(d.getDeploymentFor(e))
+	e.Resources.AddAll(getConfigMapsFor(e))
+	e.Resources.Add(getDeploymentFor(e))
 	return nil
 }
 
@@ -54,7 +54,7 @@ func (d *deploymentTrait) apply(e *Environment) error {
 //
 // **********************************
 
-func (*deploymentTrait) getConfigMapFor(e *Environment) []runtime.Object {
+func getConfigMapsFor(e *Environment) []runtime.Object {
 	maps := make([]runtime.Object, 0, len(e.Integration.Spec.Sources)+1)
 
 	// combine properties of integration with context, integration
@@ -116,7 +116,7 @@ func (*deploymentTrait) getConfigMapFor(e *Environment) []runtime.Object {
 //
 // **********************************
 
-func (*deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
+func getDeploymentFor(e *Environment) *appsv1.Deployment {
 	sources := make([]string, 0, len(e.Integration.Spec.Sources))
 	for i, s := range e.Integration.Spec.Sources {
 		src := fmt.Sprintf("file:/etc/camel/integrations/%03d/%s", i, strings.TrimPrefix(s.Name, "/"))
diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go
index 0b93d87..e463746 100644
--- a/pkg/trait/knative.go
+++ b/pkg/trait/knative.go
@@ -50,7 +50,7 @@ func (t *knativeTrait) appliesTo(e *Environment) bool {
 
 func (t *knativeTrait) autoconfigure(e *Environment) error {
 	if t.Sources == "" {
-		channels := t.getSourceChannels(e)
+		channels := getSourceChannels(e)
 		t.Sources = strings.Join(channels, ",")
 	}
 	return nil
@@ -139,7 +139,7 @@ func (t *knativeTrait) getServiceFor(e *Environment) *serving.Service {
 }
 
 func (t *knativeTrait) getSubscriptionsFor(e *Environment) []*eventing.Subscription {
-	channels := t.getConfiguredSourceChannels()
+	channels := getConfiguredSourceChannels(t.Sources)
 	subs := make([]*eventing.Subscription, 0)
 	for _, ch := range channels {
 		subs = append(subs, t.getSubscriptionFor(e, ch))
@@ -185,7 +185,7 @@ func (t *knativeTrait) getConfigurationSerialized(e *Environment) string {
 }
 
 func (t *knativeTrait) getConfiguration(e *Environment) knativeutil.CamelEnvironment {
-	sourceChannels := t.getConfiguredSourceChannels()
+	sourceChannels := getConfiguredSourceChannels(t.Sources)
 	env := knativeutil.NewCamelEnvironment()
 	for _, ch := range sourceChannels {
 		svc := knativeutil.CamelServiceDefinition{
@@ -215,9 +215,9 @@ func (t *knativeTrait) getConfiguration(e *Environment) knativeutil.CamelEnviron
 	return env
 }
 
-func (t *knativeTrait) getConfiguredSourceChannels() []string {
+func getConfiguredSourceChannels(sources string) []string {
 	channels := make([]string, 0)
-	for _, ch := range strings.Split(t.Sources, ",") {
+	for _, ch := range strings.Split(sources, ",") {
 		cht := strings.Trim(ch, " \t\"")
 		if cht != "" {
 			channels = append(channels, cht)
@@ -226,13 +226,13 @@ func (t *knativeTrait) getConfiguredSourceChannels() []string {
 	return channels
 }
 
-func (*knativeTrait) getSourceChannels(e *Environment) []string {
+func getSourceChannels(e *Environment) []string {
 	channels := make([]string, 0)
 
-	for _, s := range e.Integration.Spec.Sources {
-		meta := metadata.Extract(s)
+	metadata.Each(e.Integration.Spec.Sources, func(_ int, meta metadata.IntegrationMetadata) bool {
 		channels = append(channels, knativeutil.ExtractChannelNames(meta.FromURIs)...)
-	}
+		return true
+	})
 
 	return channels
 }
diff --git a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/URIResolver.java b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/URIResolver.java
index 12ade89..69fb640 100644
--- a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/URIResolver.java
+++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/URIResolver.java
@@ -46,7 +46,7 @@ public class URIResolver {
 
     public static Reader resolveEnv(String uri) {
         if (!uri.startsWith(Constants.SCHEME_ENV)) {
-            throw new IllegalArgumentException("The provided content is not inline: " + uri);
+            throw new IllegalArgumentException("The provided content is not env: " + uri);
         }
 
         final String envvar = StringHelper.after(uri, ":");