You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@camel.apache.org by GitBox <gi...@apache.org> on 2018/12/20 11:30:33 UTC

[GitHub] nicolaferraro closed pull request #300: add resources to an integration

nicolaferraro closed pull request #300: add resources to an integration
URL: https://github.com/apache/camel-k/pull/300
 
 
   

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

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

diff --git a/examples/resources-data.txt b/examples/resources-data.txt
new file mode 100644
index 00000000..dff79473
--- /dev/null
+++ b/examples/resources-data.txt
@@ -0,0 +1 @@
+the file body
\ No newline at end of file
diff --git a/examples/resources-route.groovy b/examples/resources-route.groovy
new file mode 100644
index 00000000..4c7d61b6
--- /dev/null
+++ b/examples/resources-route.groovy
@@ -0,0 +1,11 @@
+//
+// To run this integrations use:
+//
+//     kamel run --resource examples/resources-data.txt examples/resources-route.groovy
+//
+
+from('timer:resources')
+    .routeId('resources')
+    .setBody()
+        .simple("resource:platform:resources-data.txt")
+    .log('file content is: ${body}')
diff --git a/pkg/apis/camel/v1alpha1/types.go b/pkg/apis/camel/v1alpha1/types.go
index 8965778a..0627da17 100644
--- a/pkg/apis/camel/v1alpha1/types.go
+++ b/pkg/apis/camel/v1alpha1/types.go
@@ -55,6 +55,7 @@ type Integration struct {
 type IntegrationSpec struct {
 	Replicas      *int32                          `json:"replicas,omitempty"`
 	Sources       []SourceSpec                    `json:"sources,omitempty"`
+	Resources     []ResourceSpec                  `json:"resources,omitempty"`
 	Context       string                          `json:"context,omitempty"`
 	Dependencies  []string                        `json:"dependencies,omitempty"`
 	Profile       TraitProfile                    `json:"profile,omitempty"`
@@ -65,7 +66,7 @@ type IntegrationSpec struct {
 
 // AddSource --
 func (is *IntegrationSpec) AddSource(name string, content string, language Language) {
-	is.Sources = append(is.Sources, SourceSpec{Name: name, Content: content, Language: language})
+	is.Sources = append(is.Sources, NewSourceSpec(name, content, language))
 }
 
 // AddSources --
@@ -73,6 +74,11 @@ func (is *IntegrationSpec) AddSources(sources ...SourceSpec) {
 	is.Sources = append(is.Sources, sources...)
 }
 
+// AddResources --
+func (is *IntegrationSpec) AddResources(resources ...ResourceSpec) {
+	is.Resources = append(is.Resources, resources...)
+}
+
 // AddConfiguration --
 func (is *IntegrationSpec) AddConfiguration(confType string, confValue string) {
 	is.Configuration = append(is.Configuration, ConfigurationSpec{
@@ -93,12 +99,22 @@ func (is *IntegrationSpec) AddDependency(dependency string) {
 	}
 }
 
+// DataSpec --
+type DataSpec struct {
+	Name        string `json:"name,omitempty"`
+	Content     string `json:"content,omitempty"`
+	Compression bool   `json:"compression,omitempty"`
+}
+
+// ResourceSpec --
+type ResourceSpec struct {
+	DataSpec
+}
+
 // SourceSpec --
 type SourceSpec struct {
-	Name        string   `json:"name,omitempty"`
-	Content     string   `json:"content,omitempty"`
-	Language    Language `json:"language,omitempty"`
-	Compression bool     `json:"compression,omitempty"`
+	DataSpec
+	Language Language `json:"language,omitempty"`
 }
 
 // Language --
diff --git a/pkg/apis/camel/v1alpha1/types_support.go b/pkg/apis/camel/v1alpha1/types_support.go
index 1a9a9c45..21df6053 100644
--- a/pkg/apis/camel/v1alpha1/types_support.go
+++ b/pkg/apis/camel/v1alpha1/types_support.go
@@ -40,6 +40,27 @@ func (spec ConfigurationSpec) String() string {
 //
 // **********************************
 
+// NewSourceSpec --
+func NewSourceSpec(name string, content string, language Language) SourceSpec {
+	return SourceSpec{
+		DataSpec: DataSpec{
+			Name:    name,
+			Content: content,
+		},
+		Language: language,
+	}
+}
+
+// NewResourceSpec --
+func NewResourceSpec(name string, content string, destination string) ResourceSpec {
+	return ResourceSpec{
+		DataSpec: DataSpec{
+			Name:    name,
+			Content: content,
+		},
+	}
+}
+
 // NewIntegrationPlatformList --
 func NewIntegrationPlatformList() IntegrationPlatformList {
 	return IntegrationPlatformList{
diff --git a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
index fd37d687..c61ab94f 100644
--- a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
@@ -57,6 +57,22 @@ func (in *ConfigurationSpec) DeepCopy() *ConfigurationSpec {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *DataSpec) DeepCopyInto(out *DataSpec) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataSpec.
+func (in *DataSpec) DeepCopy() *DataSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(DataSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Integration) DeepCopyInto(out *Integration) {
 	*out = *in
@@ -373,6 +389,11 @@ func (in *IntegrationSpec) DeepCopyInto(out *IntegrationSpec) {
 		*out = make([]SourceSpec, len(*in))
 		copy(*out, *in)
 	}
+	if in.Resources != nil {
+		in, out := &in.Resources, &out.Resources
+		*out = make([]ResourceSpec, len(*in))
+		copy(*out, *in)
+	}
 	if in.Dependencies != nil {
 		in, out := &in.Dependencies, &out.Dependencies
 		*out = make([]string, len(*in))
@@ -447,9 +468,27 @@ func (in *IntegrationTraitSpec) DeepCopy() *IntegrationTraitSpec {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ResourceSpec) DeepCopyInto(out *ResourceSpec) {
+	*out = *in
+	out.DataSpec = in.DataSpec
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceSpec.
+func (in *ResourceSpec) DeepCopy() *ResourceSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(ResourceSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *SourceSpec) DeepCopyInto(out *SourceSpec) {
 	*out = *in
+	out.DataSpec = in.DataSpec
 	return
 }
 
diff --git a/pkg/builder/builder.go b/pkg/builder/builder.go
index ffe9f92e..58d814e5 100644
--- a/pkg/builder/builder.go
+++ b/pkg/builder/builder.go
@@ -143,12 +143,11 @@ func (b *defaultBuilder) submit(request Request) {
 	b.request.Store(request.Meta.Name, r)
 
 	c := Context{
-		C:                b.ctx,
-		Path:             builderPath,
-		Namespace:        b.namespace,
-		Request:          request,
-		ComputeClasspath: true,
-		Image:            "fabric8/s2i-java:2.3", // TODO: externalize
+		C:         b.ctx,
+		Path:      builderPath,
+		Namespace: b.namespace,
+		Request:   request,
+		Image:     "fabric8/s2i-java:2.3", // TODO: externalize,
 	}
 
 	if request.Image != "" {
diff --git a/pkg/builder/builder_steps.go b/pkg/builder/builder_steps.go
index 91035e05..b50fb9b1 100644
--- a/pkg/builder/builder_steps.go
+++ b/pkg/builder/builder_steps.go
@@ -166,7 +166,7 @@ func IncrementalPackager(ctx *Context) error {
 		return StandardPackager(ctx)
 	}
 
-	images, err := ListPublishedImages(ctx.Namespace)
+	images, err := ListPublishedImages(ctx)
 	if err != nil {
 		return err
 	}
@@ -228,27 +228,16 @@ func packager(ctx *Context, selector ArtifactsSelector) error {
 		}
 	}
 
-	if ctx.ComputeClasspath && len(ctx.Artifacts) > 0 {
-		cp := ""
-		for _, entry := range ctx.Artifacts {
-			cp += entry.Target + "\n"
-		}
-
-		if err := tarAppender.AddData([]byte(cp), "classpath"); err != nil {
-			return err
-		}
-	}
-
 	ctx.Archive = tarFileName
 
 	return nil
 }
 
 // ListPublishedImages --
-func ListPublishedImages(namespace string) ([]PublishedImage, error) {
+func ListPublishedImages(context *Context) ([]PublishedImage, error) {
 	list := v1alpha1.NewIntegrationContextList()
 
-	err := sdk.List(namespace, &list, sdk.WithListOptions(&metav1.ListOptions{}))
+	err := sdk.List(context.Namespace, &list, sdk.WithListOptions(&metav1.ListOptions{}))
 	if err != nil {
 		return nil, err
 	}
@@ -257,6 +246,9 @@ func ListPublishedImages(namespace string) ([]PublishedImage, error) {
 		if ctx.Status.Phase != v1alpha1.IntegrationContextPhaseReady || ctx.Labels == nil {
 			continue
 		}
+		if context.ContextFilter != nil && !context.ContextFilter(&ctx) {
+			continue
+		}
 		if ctxType, present := ctx.Labels["camel.apache.org/context.type"]; !present || ctxType != v1alpha1.IntegrationContextTypePlatform {
 			continue
 		}
diff --git a/pkg/builder/builder_types.go b/pkg/builder/builder_types.go
index 54f9774b..d0e13b4a 100644
--- a/pkg/builder/builder_types.go
+++ b/pkg/builder/builder_types.go
@@ -147,8 +147,7 @@ type Context struct {
 	Artifacts         []v1alpha1.Artifact
 	SelectedArtifacts []v1alpha1.Artifact
 	Archive           string
-	ComputeClasspath  bool
-	MainClass         string
+	ContextFilter     func(integrationContext *v1alpha1.IntegrationContext) bool
 }
 
 // HasRequiredImage --
diff --git a/pkg/builder/springboot/initializer.go b/pkg/builder/springboot/initializer.go
index 2b8b5af7..d8e18f50 100644
--- a/pkg/builder/springboot/initializer.go
+++ b/pkg/builder/springboot/initializer.go
@@ -18,14 +18,24 @@ limitations under the License.
 package springboot
 
 import (
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/builder"
 )
 
 // Initialize --
 func Initialize(ctx *builder.Context) error {
-	// no need to compute classpath as we do use spring boot own
-	// loader: PropertiesLauncher
-	ctx.ComputeClasspath = false
+	// do not take into account any image that does not have spring-boot
+	// as required dependency to avoid picking up a base image with wrong
+	// classpath or layout
+	ctx.ContextFilter = func(context *v1alpha1.IntegrationContext) bool {
+		for _, i := range context.Spec.Dependencies {
+			if i == "runtime:spring" {
+				return true
+			}
+		}
+
+		return false
+	}
 
 	return nil
 }
diff --git a/pkg/client/cmd/run.go b/pkg/client/cmd/run.go
index de784b9f..376b91a6 100644
--- a/pkg/client/cmd/run.go
+++ b/pkg/client/cmd/run.go
@@ -85,6 +85,7 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		"E.g. \"--logging-level org.apache.camel=DEBUG\"")
 	cmd.Flags().StringVarP(&options.OutputFormat, "output", "o", "", "Output format. One of: json|yaml")
 	cmd.Flags().BoolVar(&options.Compression, "compression", false, "Enable store source as a compressed binary blob")
+	cmd.Flags().StringSliceVar(&options.Resources, "resource", nil, "Add a resource")
 
 	// completion support
 	configureKnownCompletions(&cmd)
@@ -104,6 +105,7 @@ type runCmdOptions struct {
 	IntegrationName    string
 	Profile            string
 	OutputFormat       string
+	Resources          []string
 	Dependencies       []string
 	Properties         []string
 	ConfigMaps         []string
@@ -247,8 +249,8 @@ func (o *runCmdOptions) syncIntegration(sources []string) error {
 	return nil
 }
 
-func (o *runCmdOptions) createIntegration(args []string) (*v1alpha1.Integration, error) {
-	return o.updateIntegrationCode(args)
+func (o *runCmdOptions) createIntegration(sources []string) (*v1alpha1.Integration, error) {
+	return o.updateIntegrationCode(sources)
 }
 
 func (o *runCmdOptions) updateIntegrationCode(sources []string) (*v1alpha1.Integration, error) {
@@ -285,25 +287,32 @@ func (o *runCmdOptions) updateIntegrationCode(sources []string) (*v1alpha1.Integ
 	}
 
 	for _, source := range sources {
-		code, err := o.loadCode(source)
+		data, err := o.loadData(source, o.Compression)
 		if err != nil {
 			return nil, err
 		}
 
-		if o.Compression {
-			var b bytes.Buffer
-
-			if err := gzip.Compress(&b, []byte(code)); err != nil {
-				return nil, err
-			}
+		integration.Spec.AddSources(v1alpha1.SourceSpec{
+			DataSpec: v1alpha1.DataSpec{
+				Name:        path.Base(source),
+				Content:     data,
+				Compression: o.Compression,
+			},
+		})
+	}
 
-			code = base64.StdEncoding.EncodeToString(b.Bytes())
+	for _, resource := range o.Resources {
+		data, err := o.loadData(resource, o.Compression)
+		if err != nil {
+			return nil, err
 		}
 
-		integration.Spec.AddSources(v1alpha1.SourceSpec{
-			Name:        path.Base(source),
-			Content:     code,
-			Compression: o.Compression,
+		integration.Spec.AddResources(v1alpha1.ResourceSpec{
+			DataSpec: v1alpha1.DataSpec{
+				Name:        path.Base(resource),
+				Content:     data,
+				Compression: o.Compression,
+			},
 		})
 	}
 
@@ -381,23 +390,39 @@ func (o *runCmdOptions) updateIntegrationCode(sources []string) (*v1alpha1.Integ
 	return &integration, nil
 }
 
-func (*runCmdOptions) loadCode(fileName string) (string, error) {
+func (*runCmdOptions) loadData(fileName string, compress bool) (string, error) {
+	var content []byte
+	var err error
+
 	if !strings.HasPrefix(fileName, "http://") && !strings.HasPrefix(fileName, "https://") {
-		content, err := ioutil.ReadFile(fileName)
+		content, err = ioutil.ReadFile(fileName)
+		if err != nil {
+			return "", err
+		}
+	} else {
+		resp, err := http.Get(fileName)
+		if err != nil {
+			return "", err
+		}
+		defer resp.Body.Close()
+
+		content, err = ioutil.ReadAll(resp.Body)
 		if err != nil {
 			return "", err
 		}
-		return string(content), nil
 	}
 
-	resp, err := http.Get(fileName)
-	if err != nil {
-		return "", err
+	if compress {
+		var b bytes.Buffer
+
+		if err := gzip.Compress(&b, content); err != nil {
+			return "", err
+		}
+
+		return base64.StdEncoding.EncodeToString(b.Bytes()), nil
 	}
-	defer resp.Body.Close()
-	bodyBytes, err := ioutil.ReadAll(resp.Body)
-	bodyString := string(bodyBytes)
-	return bodyString, err
+
+	return string(content), nil
 }
 
 func (*runCmdOptions) configureTrait(integration *v1alpha1.Integration, config string) error {
diff --git a/pkg/metadata/metadata_dependencies_test.go b/pkg/metadata/metadata_dependencies_test.go
index 8eea4874..9f952f57 100644
--- a/pkg/metadata/metadata_dependencies_test.go
+++ b/pkg/metadata/metadata_dependencies_test.go
@@ -26,14 +26,17 @@ import (
 
 func TestDependenciesJavaSource(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.java",
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.java",
+			Content: `
+			    from("telegram:bots/cippa").to("log:stash");
+			    from("timer:tick").to("amqp:queue");
+			    from("ine:xistent").to("amqp:queue");
+			`,
+		},
 		Language: v1alpha1.LanguageJavaSource,
-		Content: `
-			from("telegram:bots/cippa").to("log:stash");
-			from("timer:tick").to("amqp:queue");
-			from("ine:xistent").to("amqp:queue");
-		`,
 	}
+
 	meta := Extract(code)
 	// assert all dependencies are found and sorted (removing duplicates)
 	assert.Equal(t, []string{"camel:amqp", "camel:core", "camel:telegram"}, meta.Dependencies)
@@ -41,28 +44,33 @@ func TestDependenciesJavaSource(t *testing.T) {
 
 func TestDependenciesJavaClass(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.class",
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.class",
+			Content: `
+			    from("telegram:bots/cippa").to("log:stash");
+			    from("timer:tick").to("amqp:queue");
+			    from("ine:xistent").to("amqp:queue");
+		    `,
+		},
 		Language: v1alpha1.LanguageJavaClass,
-		Content: `
-			from("telegram:bots/cippa").to("log:stash");
-			from("timer:tick").to("amqp:queue");
-			from("ine:xistent").to("amqp:queue");
-		`,
 	}
+
 	meta := Extract(code)
 	assert.Empty(t, meta.Dependencies)
 }
 
 func TestDependenciesJavaScript(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "source.js",
+		DataSpec: v1alpha1.DataSpec{
+			Name: "source.js",
+			Content: `
+			    from('telegram:bots/cippa').to("log:stash");
+			    from('timer:tick').to("amqp:queue");
+			    from("ine:xistent").to("amqp:queue");
+			    '"'
+		    `,
+		},
 		Language: v1alpha1.LanguageJavaScript,
-		Content: `
-			from('telegram:bots/cippa').to("log:stash");
-			from('timer:tick').to("amqp:queue");
-			from("ine:xistent").to("amqp:queue");
-			'"'
-		`,
 	}
 	meta := Extract(code)
 	// assert all dependencies are found and sorted (removing duplicates)
@@ -71,15 +79,18 @@ func TestDependenciesJavaScript(t *testing.T) {
 
 func TestDependenciesGroovy(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "source.groovy",
+		DataSpec: v1alpha1.DataSpec{
+			Name: "source.groovy",
+			Content: `
+			    from('telegram:bots/cippa').to("log:stash");
+			    from('timer:tick').to("amqp:queue");
+			    from("ine:xistent").to("amqp:queue");
+			    '"'
+		    `,
+		},
 		Language: v1alpha1.LanguageGroovy,
-		Content: `
-			from('telegram:bots/cippa').to("log:stash");
-			from('timer:tick').to("amqp:queue");
-			from("ine:xistent").to("amqp:queue");
-			'"'
-		`,
 	}
+
 	meta := Extract(code)
 	// assert all dependencies are found and sorted (removing duplicates)
 	assert.Equal(t, []string{"camel:amqp", "camel:core", "camel:telegram"}, meta.Dependencies)
@@ -87,14 +98,17 @@ func TestDependenciesGroovy(t *testing.T) {
 
 func TestDependencies(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.java",
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.java",
+			Content: `
+			    from("http4:test").to("log:end");
+			    from("https4:test").to("log:end");
+			    from("twitter-timeline:test").to("mock:end");
+		    `,
+		},
 		Language: v1alpha1.LanguageJavaSource,
-		Content: `
-			from("http4:test").to("log:end");
-			from("https4:test").to("log:end");
-			from("twitter-timeline:test").to("mock:end");
-		`,
 	}
+
 	meta := Extract(code)
 	// assert all dependencies are found and sorted (removing duplicates)
 	assert.Equal(t, []string{"camel:core", "camel:http4", "camel:twitter"}, meta.Dependencies)
diff --git a/pkg/metadata/metadata_http_test.go b/pkg/metadata/metadata_http_test.go
index 59cc1cd3..adc5cdd6 100644
--- a/pkg/metadata/metadata_http_test.go
+++ b/pkg/metadata/metadata_http_test.go
@@ -26,13 +26,15 @@ import (
 
 func TestHttpJavaSource(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.java",
-		Language: v1alpha1.LanguageJavaSource,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.java",
+			Content: `
 			from("telegram:bots/cippa").to("log:stash");
 			from("undertow:uri").to("log:stash");
 			from("ine:xistent").to("log:stash");
 		`,
+		},
+		Language: v1alpha1.LanguageJavaSource,
 	}
 	meta := Extract(code)
 	assert.True(t, meta.RequiresHTTPService)
@@ -41,13 +43,16 @@ func TestHttpJavaSource(t *testing.T) {
 
 func TestHttpOnlyJavaSource(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.java",
-		Language: v1alpha1.LanguageJavaSource,
-		Content: `
+
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.java",
+			Content: `
 			from("direct:bots/cippa").to("log:stash");
 			from("undertow:uri").to("log:stash");
 			from("seda:path").to("log:stash");
 		`,
+		},
+		Language: v1alpha1.LanguageJavaSource,
 	}
 	meta := Extract(code)
 	assert.True(t, meta.RequiresHTTPService)
@@ -56,12 +61,14 @@ func TestHttpOnlyJavaSource(t *testing.T) {
 
 func TestHttpOnlyJavaSourceRest(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.java",
-		Language: v1alpha1.LanguageJavaSource,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.java",
+			Content: `
 			from("direct:bots/cippa").to("log:stash");
 			rest().get("").to("log:stash");
 		`,
+		},
+		Language: v1alpha1.LanguageJavaSource,
 	}
 	meta := Extract(code)
 	assert.True(t, meta.RequiresHTTPService)
@@ -70,12 +77,14 @@ func TestHttpOnlyJavaSourceRest(t *testing.T) {
 
 func TestHttpOnlyJavaSourceRest2(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.java",
-		Language: v1alpha1.LanguageJavaSource,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.java",
+			Content: `
 			from("vm:bots/cippa").to("log:stash");
 			rest( ).get("").to("log:stash");
 		`,
+		},
+		Language: v1alpha1.LanguageJavaSource,
 	}
 	meta := Extract(code)
 	assert.True(t, meta.RequiresHTTPService)
@@ -84,13 +93,15 @@ func TestHttpOnlyJavaSourceRest2(t *testing.T) {
 
 func TestNoHttpGroovySource(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.groovy",
-		Language: v1alpha1.LanguageGroovy,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.groovy",
+			Content: `
 			from('direct:bots/cippa').to("log:stash");
 			from('teelgram:uri').to("log:stash");
 			from('seda:path').to("log:stash");
 		`,
+		},
+		Language: v1alpha1.LanguageGroovy,
 	}
 	meta := Extract(code)
 	assert.False(t, meta.RequiresHTTPService)
@@ -99,13 +110,15 @@ func TestNoHttpGroovySource(t *testing.T) {
 
 func TestHttpOnlyGroovySource(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.groovy",
-		Language: v1alpha1.LanguageGroovy,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.groovy",
+			Content: `
 			from('direct:bots/cippa').to("log:stash");
 			from('undertow:uri').to("log:stash");
 			from('seda:path').to("log:stash");
 		`,
+		},
+		Language: v1alpha1.LanguageGroovy,
 	}
 	meta := Extract(code)
 	assert.True(t, meta.RequiresHTTPService)
@@ -114,13 +127,15 @@ func TestHttpOnlyGroovySource(t *testing.T) {
 
 func TestHttpXMLSource(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "routes.xml",
-		Language: v1alpha1.LanguageXML,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "routes.xml",
+			Content: `
 			<from uri="telegram:ciao" />
 			<rest path="/">
 			</rest>
 		`,
+		},
+		Language: v1alpha1.LanguageXML,
 	}
 	meta := Extract(code)
 	assert.True(t, meta.RequiresHTTPService)
@@ -129,13 +144,16 @@ func TestHttpXMLSource(t *testing.T) {
 
 func TestHttpOnlyXMLSource(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "routes.xml",
-		Language: v1alpha1.LanguageXML,
-		Content: `
+
+		DataSpec: v1alpha1.DataSpec{
+			Name: "routes.xml",
+			Content: `
 			<from uri="direct:ciao" />
 			<rest path="/">
 			</rest>
 		`,
+		},
+		Language: v1alpha1.LanguageXML,
 	}
 	meta := Extract(code)
 	assert.True(t, meta.RequiresHTTPService)
@@ -145,20 +163,25 @@ func TestHttpOnlyXMLSource(t *testing.T) {
 func TestMultilangHTTPOnlySource(t *testing.T) {
 	codes := []v1alpha1.SourceSpec{
 		{
-			Name:     "routes.xml",
-			Language: v1alpha1.LanguageXML,
-			Content: `
+			DataSpec: v1alpha1.DataSpec{
+				Name: "routes.xml",
+				Content: `
 				<from uri="direct:ciao" />
 				<rest path="/">
 				</rest>
 			`,
+			},
+			Language: v1alpha1.LanguageXML,
 		},
 		{
-			Name:     "routes2.groovy",
-			Language: v1alpha1.LanguageGroovy,
-			Content: `
+
+			DataSpec: v1alpha1.DataSpec{
+				Name: "routes2.groovy",
+				Content: `
 				from('seda:in').to('seda:out')
 			`,
+			},
+			Language: v1alpha1.LanguageGroovy,
 		},
 	}
 	meta := ExtractAll(codes)
@@ -169,21 +192,27 @@ func TestMultilangHTTPOnlySource(t *testing.T) {
 func TestMultilangHTTPSource(t *testing.T) {
 	codes := []v1alpha1.SourceSpec{
 		{
-			Name:     "routes.xml",
-			Language: v1alpha1.LanguageXML,
-			Content: `
+
+			DataSpec: v1alpha1.DataSpec{
+				Name: "routes.xml",
+				Content: `
 				<from uri="direct:ciao" />
 				<rest path="/">
 				</rest>
 			`,
+			},
+			Language: v1alpha1.LanguageXML,
 		},
 		{
-			Name:     "routes2.groovy",
-			Language: v1alpha1.LanguageGroovy,
-			Content: `
+
+			DataSpec: v1alpha1.DataSpec{
+				Name: "routes2.groovy",
+				Content: `
 				from('seda:in').to('seda:out')
 				from('timer:tick').to('log:info')
 			`,
+			},
+			Language: v1alpha1.LanguageGroovy,
 		},
 	}
 	meta := ExtractAll(codes)
diff --git a/pkg/metadata/metadata_languages_test.go b/pkg/metadata/metadata_languages_test.go
index 5382d388..8a11a56d 100644
--- a/pkg/metadata/metadata_languages_test.go
+++ b/pkg/metadata/metadata_languages_test.go
@@ -26,7 +26,9 @@ import (
 
 func TestLanguageJavaSource(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name: "Request.java",
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.java",
+		},
 	}
 	meta := Extract(code)
 	assert.Equal(t, v1alpha1.LanguageJavaSource, meta.Language)
@@ -34,7 +36,9 @@ func TestLanguageJavaSource(t *testing.T) {
 
 func TestLanguageAlreadySet(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.java",
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.java",
+		},
 		Language: v1alpha1.LanguageJavaScript,
 	}
 	meta := Extract(code)
diff --git a/pkg/metadata/metadata_uri_test.go b/pkg/metadata/metadata_uri_test.go
index 1214ef68..9dc26d49 100644
--- a/pkg/metadata/metadata_uri_test.go
+++ b/pkg/metadata/metadata_uri_test.go
@@ -26,9 +26,9 @@ import (
 
 func TestJava1(t *testing.T) {
 	source := v1alpha1.SourceSpec{
-		Name:     "test",
-		Language: v1alpha1.LanguageJavaSource,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "test",
+			Content: `
 			import org.apache.camel.builder.RouteBuilder;
 
 			public class Sample extends RouteBuilder {
@@ -40,6 +40,8 @@ func TestJava1(t *testing.T) {
   				}
 			}
 		`,
+		},
+		Language: v1alpha1.LanguageJavaSource,
 	}
 
 	metadata := Extract(source)
@@ -51,9 +53,9 @@ func TestJava1(t *testing.T) {
 
 func TestJava2(t *testing.T) {
 	source := v1alpha1.SourceSpec{
-		Name:     "test",
-		Language: v1alpha1.LanguageJavaSource,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "test",
+			Content: `
 			import org.apache.camel.builder.RouteBuilder;
 
 			public class Sample extends RouteBuilder {
@@ -71,6 +73,8 @@ func TestJava2(t *testing.T) {
   				}
 			}
 		`,
+		},
+		Language: v1alpha1.LanguageJavaSource,
 	}
 
 	metadata := Extract(source)
@@ -84,9 +88,9 @@ func TestJava2(t *testing.T) {
 
 func TestGroovy1(t *testing.T) {
 	source := v1alpha1.SourceSpec{
-		Name:     "test",
-		Language: v1alpha1.LanguageGroovy,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "test",
+			Content: `
 			
 		  	from( "timer:tick")
 		    	.setBody().constant("aa")
@@ -97,6 +101,8 @@ func TestGroovy1(t *testing.T) {
 		    	.setBody().constant("aa")
 				.to('uri:3')
 		`,
+		},
+		Language: v1alpha1.LanguageGroovy,
 	}
 
 	metadata := Extract(source)
@@ -111,10 +117,9 @@ func TestGroovy1(t *testing.T) {
 
 func TestGroovy2(t *testing.T) {
 	source := v1alpha1.SourceSpec{
-		Name:     "test",
-		Language: v1alpha1.LanguageGroovy,
-		Content: `
-			
+		DataSpec: v1alpha1.DataSpec{
+			Name: "test",
+			Content: `			
 			rest().get("/")
 				.to   ('log:info?skipBodyLineSeparator=false').to( 'http://url' )
 						.toD('dyn:1')
@@ -122,6 +127,8 @@ func TestGroovy2(t *testing.T) {
 						.toD( "dyn:2")
 						.toF( "f:%s", "2")
 		`,
+		},
+		Language: v1alpha1.LanguageGroovy,
 	}
 
 	metadata := Extract(source)
@@ -136,9 +143,9 @@ func TestGroovy2(t *testing.T) {
 
 func TestXml1(t *testing.T) {
 	source := v1alpha1.SourceSpec{
-		Name:     "test",
-		Language: v1alpha1.LanguageXML,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "test",
+			Content: `
 			<routes>
 			    <route id="hello">
         			<from uri="timer:hello?period=3s"/>
@@ -151,6 +158,8 @@ func TestXml1(t *testing.T) {
     			</route>
 			</routes>
 		`,
+		},
+		Language: v1alpha1.LanguageXML,
 	}
 
 	metadata := Extract(source)
@@ -164,9 +173,9 @@ func TestXml1(t *testing.T) {
 
 func TestKotlin1(t *testing.T) {
 	source := v1alpha1.SourceSpec{
-		Name:     "test",
-		Language: v1alpha1.LanguageKotlin,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "test",
+			Content: `
 			
 		  	from( "timer:tick")
 		    	.setBody().constant("aa")
@@ -179,6 +188,8 @@ func TestKotlin1(t *testing.T) {
 				.toD("uri:4")
 				.toF("uri:%s", 5)
 		`,
+		},
+		Language: v1alpha1.LanguageKotlin,
 	}
 
 	metadata := Extract(source)
@@ -195,15 +206,17 @@ func TestKotlin1(t *testing.T) {
 
 func TestJavascript1(t *testing.T) {
 	source := v1alpha1.SourceSpec{
-		Name:     "test",
-		Language: v1alpha1.LanguageJavaScript,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "test",
+			Content: `
 			
 			rest().get("/")
 				.to   ('log:info?skipBodyLineSeparator=false').to( 'http://url' )
 				.toD("uri:2")
 				.toF("uri:%s", "3") 
 		`,
+		},
+		Language: v1alpha1.LanguageJavaScript,
 	}
 
 	metadata := Extract(source)
diff --git a/pkg/stub/action/integration/build_image.go b/pkg/stub/action/integration/build_image.go
index e23e6d0d..44440cfb 100644
--- a/pkg/stub/action/integration/build_image.go
+++ b/pkg/stub/action/integration/build_image.go
@@ -101,6 +101,12 @@ func (action *buildImageAction) Handle(integration *v1alpha1.Integration) error
 			Target:  path.Join("sources", source.Name),
 		})
 	}
+	for _, resource := range integration.Spec.Resources {
+		r.Resources = append(r.Resources, builder.Resource{
+			Content: []byte(resource.Content),
+			Target:  path.Join("resources", resource.Name),
+		})
+	}
 
 	res := b.Submit(r)
 
diff --git a/pkg/stub/action/integration/deploy.go b/pkg/stub/action/integration/deploy.go
index 1ad22abe..ad0d04ad 100644
--- a/pkg/stub/action/integration/deploy.go
+++ b/pkg/stub/action/integration/deploy.go
@@ -22,6 +22,7 @@ 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"
 )
 
@@ -42,10 +43,18 @@ func (action *deployAction) CanHandle(integration *v1alpha1.Integration) bool {
 }
 
 func (action *deployAction) Handle(integration *v1alpha1.Integration) error {
-	env, err := trait.Apply(integration, nil)
+	ctxName := integration.Spec.Context
+	ctx := v1alpha1.NewIntegrationContext(integration.Namespace, ctxName)
+
+	if err := sdk.Get(&ctx); err != nil {
+		return errors.Wrapf(err, "unable to find integration context %s, %s", ctxName, err)
+	}
+
+	env, err := trait.Apply(integration, &ctx)
 	if err != nil {
 		return err
 	}
+
 	// TODO we should look for objects that are no longer present in the collection and remove them
 	err = kubernetes.ReplaceResources(env.Resources.Items())
 	if err != nil {
@@ -53,8 +62,8 @@ func (action *deployAction) Handle(integration *v1alpha1.Integration) error {
 	}
 
 	target := integration.DeepCopy()
-	logrus.Info("Integration ", target.Name, " transitioning to state ", v1alpha1.IntegrationPhaseRunning)
 	target.Status.Phase = v1alpha1.IntegrationPhaseRunning
+	logrus.Info("Integration ", target.Name, " transitioning to state ", target.Status.Phase)
 
 	return sdk.Update(target)
 }
diff --git a/pkg/trait/catalog.go b/pkg/trait/catalog.go
index 0fd56689..0e5b9255 100644
--- a/pkg/trait/catalog.go
+++ b/pkg/trait/catalog.go
@@ -41,6 +41,7 @@ type Catalog struct {
 	tSpringBoot   Trait
 	tIstio        Trait
 	tEnvironment  Trait
+	tClasspath    Trait
 }
 
 // NewCatalog creates a new trait Catalog
@@ -58,6 +59,7 @@ func NewCatalog() *Catalog {
 		tSpringBoot:   newSpringBootTrait(),
 		tIstio:        newIstioTrait(),
 		tEnvironment:  newEnvironmentTrait(),
+		tClasspath:    newClasspathTrait(),
 	}
 }
 
@@ -75,6 +77,7 @@ func (c *Catalog) allTraits() []Trait {
 		c.tSpringBoot,
 		c.tIstio,
 		c.tEnvironment,
+		c.tClasspath,
 	}
 }
 
@@ -85,8 +88,9 @@ func (c *Catalog) traitsFor(environment *Environment) []Trait {
 			c.tDebug,
 			c.tDependencies,
 			c.tBuilder,
-			c.tSpringBoot,
 			c.tEnvironment,
+			c.tClasspath,
+			c.tSpringBoot,
 			c.tDeployment,
 			c.tService,
 			c.tRoute,
@@ -97,8 +101,9 @@ func (c *Catalog) traitsFor(environment *Environment) []Trait {
 			c.tDebug,
 			c.tDependencies,
 			c.tBuilder,
-			c.tSpringBoot,
 			c.tEnvironment,
+			c.tClasspath,
+			c.tSpringBoot,
 			c.tDeployment,
 			c.tService,
 			c.tIngress,
@@ -109,8 +114,9 @@ func (c *Catalog) traitsFor(environment *Environment) []Trait {
 			c.tDebug,
 			c.tDependencies,
 			c.tBuilder,
-			c.tSpringBoot,
 			c.tEnvironment,
+			c.tClasspath,
+			c.tSpringBoot,
 			c.tKnative,
 			c.tDeployment,
 			c.tIstio,
diff --git a/pkg/trait/classpath.go b/pkg/trait/classpath.go
new file mode 100644
index 00000000..3936584b
--- /dev/null
+++ b/pkg/trait/classpath.go
@@ -0,0 +1,83 @@
+/*
+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 (
+	"fmt"
+	"strings"
+
+	"github.com/operator-framework/operator-sdk/pkg/sdk"
+	"github.com/pkg/errors"
+
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"github.com/apache/camel-k/pkg/util/envvar"
+)
+
+type classpathTrait struct {
+	BaseTrait `property:",squash"`
+}
+
+func newClasspathTrait() *classpathTrait {
+	return &classpathTrait{
+		BaseTrait: BaseTrait{
+			id: ID("classpath"),
+		},
+	}
+}
+
+func (t *classpathTrait) Configure(e *Environment) (bool, error) {
+	if t.Enabled != nil && !*t.Enabled {
+		return false, nil
+	}
+	if e.InPhase(v1alpha1.IntegrationContextPhaseReady, v1alpha1.IntegrationPhaseDeploying) {
+		return true, nil
+	}
+
+	return false, nil
+}
+
+func (t *classpathTrait) Apply(e *Environment) error {
+	ctx := e.Context
+
+	if ctx == nil && e.Integration.Spec.Context != "" {
+		name := e.Integration.Spec.Context
+		c := v1alpha1.NewIntegrationContext(e.Integration.Namespace, name)
+
+		if err := sdk.Get(&c); err != nil {
+			return errors.Wrapf(err, "unable to find integration context %s, %s", name, err)
+		}
+
+		ctx = &c
+	}
+
+	if ctx == nil {
+		return fmt.Errorf("unable to find integration context %s", e.Integration.Spec.Context)
+	}
+
+	deps := make([]string, 0, 2+len(ctx.Status.Artifacts))
+	deps = append(deps, "/etc/camel/resources")
+	deps = append(deps, "./resources")
+
+	for _, artifact := range ctx.Status.Artifacts {
+		deps = append(deps, artifact.Target)
+	}
+
+	envvar.SetVal(&e.EnvVars, "JAVA_CLASSPATH", strings.Join(deps, ":"))
+
+	return nil
+}
diff --git a/pkg/trait/deployment.go b/pkg/trait/deployment.go
index 6b334852..ec53ac31 100644
--- a/pkg/trait/deployment.go
+++ b/pkg/trait/deployment.go
@@ -126,8 +126,9 @@ func (t *deploymentTrait) getConfigMapsFor(e *Environment) []runtime.Object {
 
 	if !t.ContainerImage {
 
-		// do not create 'source' ConfigMap if a docker images for deployment
+		// do not create 'source' or 'resource' ConfigMap if a docker images for deployment
 		// is required
+
 		for i, s := range e.Integration.Spec.Sources {
 			cm := corev1.ConfigMap{
 				TypeMeta: metav1.TypeMeta{
@@ -147,7 +148,32 @@ func (t *deploymentTrait) getConfigMapsFor(e *Environment) []runtime.Object {
 					},
 				},
 				Data: map[string]string{
-					"integration": s.Content,
+					"content": s.Content,
+				},
+			}
+
+			maps = append(maps, &cm)
+		}
+
+		for i, s := range e.Integration.Spec.Resources {
+			cm := corev1.ConfigMap{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "ConfigMap",
+					APIVersion: "v1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      fmt.Sprintf("%s-resource-%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/resource.name":        s.Name,
+						"camel.apache.org/resource.compression": strconv.FormatBool(s.Compression),
+					},
+				},
+				Data: map[string]string{
+					"content": s.Content,
 				},
 			}
 
@@ -167,8 +193,8 @@ func (t *deploymentTrait) getConfigMapsFor(e *Environment) []runtime.Object {
 func (t *deploymentTrait) getSources(e *Environment) []string {
 	sources := make([]string, 0, len(e.Integration.Spec.Sources))
 
-	for i, s := range e.Integration.Spec.Sources {
-		root := fmt.Sprintf("/etc/camel/integrations/%03d", i)
+	for _, s := range e.Integration.Spec.Sources {
+		root := "/etc/camel/sources"
 
 		if t.ContainerImage {
 
@@ -176,7 +202,8 @@ func (t *deploymentTrait) getSources(e *Environment) []string {
 			root = "/deployments/sources"
 		}
 
-		src := path.Join(root, s.Name)
+		srcName := strings.TrimPrefix(s.Name, "/")
+		src := path.Join(root, srcName)
 		src = "file:" + src
 
 		params := make([]string, 0)
@@ -272,7 +299,6 @@ func (t *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
 
 	vols := make([]corev1.Volume, 0)
 	mnts := make([]corev1.VolumeMount, 0)
-	cnt := 0
 
 	//
 	// Volumes :: Properties
@@ -311,26 +337,48 @@ func (t *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
 		// do not need to mount any 'source' ConfigMap to the pod
 
 		for i, s := range e.Integration.Spec.Sources {
+			cmName := fmt.Sprintf("%s-source-%03d", e.Integration.Name, i)
+			refName := fmt.Sprintf("integration-source-%03d", i)
+			resName := strings.TrimPrefix(s.Name, "/")
+
 			vols = append(vols, corev1.Volume{
-				Name: fmt.Sprintf("integration-source-%03d", i),
+				Name: refName,
 				VolumeSource: corev1.VolumeSource{
 					ConfigMap: &corev1.ConfigMapVolumeSource{
 						LocalObjectReference: corev1.LocalObjectReference{
-							Name: fmt.Sprintf("%s-source-%03d", e.Integration.Name, i),
+							Name: cmName,
 						},
-						Items: []corev1.KeyToPath{
-							{
-								Key:  "integration",
-								Path: strings.TrimPrefix(s.Name, "/"),
-							},
+					},
+				},
+			})
+
+			mnts = append(mnts, corev1.VolumeMount{
+				Name:      refName,
+				MountPath: path.Join("/etc/camel/sources", resName),
+				SubPath:   "content",
+			})
+		}
+
+		for i, r := range e.Integration.Spec.Resources {
+			cmName := fmt.Sprintf("%s-resource-%03d", e.Integration.Name, i)
+			refName := fmt.Sprintf("integration-resource-%03d", i)
+			resName := strings.TrimPrefix(r.Name, "/")
+
+			vols = append(vols, corev1.Volume{
+				Name: refName,
+				VolumeSource: corev1.VolumeSource{
+					ConfigMap: &corev1.ConfigMapVolumeSource{
+						LocalObjectReference: corev1.LocalObjectReference{
+							Name: cmName,
 						},
 					},
 				},
 			})
 
 			mnts = append(mnts, corev1.VolumeMount{
-				Name:      fmt.Sprintf("integration-source-%03d", i),
-				MountPath: fmt.Sprintf("/etc/camel/integrations/%03d", i),
+				Name:      refName,
+				MountPath: path.Join("/etc/camel/resources", resName),
+				SubPath:   "content",
 			})
 		}
 	}
@@ -340,10 +388,10 @@ func (t *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
 	//
 
 	VisitConfigurations("configmap", e.Context, e.Integration, func(cmName string) {
-		cnt++
+		refName := "integration-cm-" + strings.ToLower(cmName)
 
 		vols = append(vols, corev1.Volume{
-			Name: "integration-cm-" + cmName,
+			Name: refName,
 			VolumeSource: corev1.VolumeSource{
 				ConfigMap: &corev1.ConfigMapVolumeSource{
 					LocalObjectReference: corev1.LocalObjectReference{
@@ -354,8 +402,8 @@ func (t *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
 		})
 
 		mnts = append(mnts, corev1.VolumeMount{
-			Name:      "integration-cm-" + cmName,
-			MountPath: fmt.Sprintf("/etc/camel/conf.d/%03d_%s", cnt, cmName),
+			Name:      refName,
+			MountPath: path.Join("/etc/camel/conf.d", refName),
 		})
 	})
 
@@ -364,10 +412,10 @@ func (t *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
 	//
 
 	VisitConfigurations("secret", e.Context, e.Integration, func(secretName string) {
-		cnt++
+		refName := "integration-secret-" + strings.ToLower(secretName)
 
 		vols = append(vols, corev1.Volume{
-			Name: "integration-secret-" + secretName,
+			Name: refName,
 			VolumeSource: corev1.VolumeSource{
 				Secret: &corev1.SecretVolumeSource{
 					SecretName: secretName,
@@ -376,8 +424,8 @@ func (t *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
 		})
 
 		mnts = append(mnts, corev1.VolumeMount{
-			Name:      "integration-secret-" + secretName,
-			MountPath: fmt.Sprintf("/etc/camel/conf.d/%03d_%s", cnt, secretName),
+			Name:      refName,
+			MountPath: path.Join("/etc/camel/conf.d", refName),
 		})
 	})
 
diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go
index b31ef07c..ae2e30da 100644
--- a/pkg/trait/knative.go
+++ b/pkg/trait/knative.go
@@ -156,6 +156,29 @@ func (t *knativeTrait) getServiceFor(e *Environment) *serving.Service {
 		sources = append(sources, src)
 	}
 
+	for i, r := range e.Integration.Spec.Resources {
+		envName := fmt.Sprintf("CAMEL_K_RESOURCE_%03d", i)
+		envvar.SetVal(&environment, envName, r.Content)
+
+		params := make([]string, 0)
+		if r.Compression {
+			params = append(params, "compression=true")
+		}
+
+		envValue := fmt.Sprintf("env:%s", envName)
+		if len(params) > 0 {
+			envValue = fmt.Sprintf("%s?%s", envValue, strings.Join(params, "&"))
+		}
+
+		envName = r.Name
+		envName = strings.ToUpper(envName)
+		envName = strings.Replace(envName, "-", "_", -1)
+		envName = strings.Replace(envName, ".", "_", -1)
+		envName = strings.Replace(envName, " ", "_", -1)
+
+		envvar.SetVal(&environment, envName, envValue)
+	}
+
 	// set env vars needed by the runtime
 	envvar.SetVal(&environment, "JAVA_MAIN_CLASS", "org.apache.camel.k.jvm.Application")
 
diff --git a/pkg/trait/knative_test.go b/pkg/trait/knative_test.go
index 4fd5c40d..9827ef90 100644
--- a/pkg/trait/knative_test.go
+++ b/pkg/trait/knative_test.go
@@ -50,10 +50,28 @@ func TestKnativeTraitWithCompressedSources(t *testing.T) {
 				Profile: v1alpha1.TraitProfileKnative,
 				Sources: []v1alpha1.SourceSpec{
 					{
-						Language:    v1alpha1.LanguageJavaScript,
-						Name:        "routes.js",
-						Content:     content,
-						Compression: true,
+						DataSpec: v1alpha1.DataSpec{
+							Name:        "routes.js",
+							Content:     content,
+							Compression: true,
+						},
+						Language: v1alpha1.LanguageJavaScript,
+					},
+				},
+				Resources: []v1alpha1.ResourceSpec{
+					{
+						DataSpec: v1alpha1.DataSpec{
+							Name:        "my-resource.txt",
+							Content:     content,
+							Compression: false,
+						},
+					},
+					{
+						DataSpec: v1alpha1.DataSpec{
+							Name:        "my-resource.gz",
+							Content:     content,
+							Compression: true,
+						},
 					},
 				},
 			},
@@ -92,6 +110,22 @@ func TestKnativeTraitWithCompressedSources(t *testing.T) {
 		route := util.LookupEnvVar(vars, "CAMEL_K_ROUTE_000")
 		assert.NotNil(t, route)
 		assert.Equal(t, content, route.Value)
+
+		resource := util.LookupEnvVar(vars, "MY_RESOURCE_TXT")
+		assert.NotNil(t, resource)
+		assert.Equal(t, "env:CAMEL_K_RESOURCE_000", resource.Value)
+
+		resource = util.LookupEnvVar(vars, "CAMEL_K_RESOURCE_000")
+		assert.NotNil(t, resource)
+		assert.Equal(t, content, resource.Value)
+
+		resource = util.LookupEnvVar(vars, "MY_RESOURCE_GZ")
+		assert.NotNil(t, resource)
+		assert.Equal(t, "env:CAMEL_K_RESOURCE_001?compression=true", resource.Value)
+
+		resource = util.LookupEnvVar(vars, "CAMEL_K_RESOURCE_001")
+		assert.NotNil(t, resource)
+		assert.Equal(t, content, resource.Value)
 	})
 
 	assert.True(t, services > 0)
diff --git a/pkg/trait/springboot.go b/pkg/trait/springboot.go
index f8d87b91..fb280ba2 100644
--- a/pkg/trait/springboot.go
+++ b/pkg/trait/springboot.go
@@ -23,9 +23,6 @@ import (
 
 	"github.com/apache/camel-k/pkg/util/envvar"
 
-	"github.com/operator-framework/operator-sdk/pkg/sdk"
-	"github.com/pkg/errors"
-
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/builder"
 	"github.com/apache/camel-k/pkg/builder/springboot"
@@ -52,7 +49,7 @@ func (t *springBootTrait) Configure(e *Environment) (bool, error) {
 	if e.IntegrationContextInPhase(v1alpha1.IntegrationContextPhaseBuilding) {
 		return true, nil
 	}
-	if e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) {
+	if e.InPhase(v1alpha1.IntegrationContextPhaseReady, v1alpha1.IntegrationPhaseDeploying) {
 		return true, nil
 	}
 	if e.IntegrationInPhase("") {
@@ -68,46 +65,47 @@ func (t *springBootTrait) Apply(e *Environment) error {
 	// Integration
 	//
 
-	if e.Integration != nil && e.Integration.Status.Phase == "" {
+	if e.IntegrationInPhase("") {
 		util.StringSliceUniqueAdd(&e.Integration.Spec.Dependencies, "runtime:spring-boot")
 
 		// sort the dependencies to get always the same list if they don't change
 		sort.Strings(e.Integration.Spec.Dependencies)
 	}
 
-	if e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying {
+	if e.InPhase(v1alpha1.IntegrationContextPhaseReady, v1alpha1.IntegrationPhaseDeploying) {
+		// Remove classpath
+		envvar.Remove(&e.EnvVars, "JAVA_CLASSPATH")
+
 		// Override env vars
 		envvar.SetVal(&e.EnvVars, "JAVA_MAIN_CLASS", "org.springframework.boot.loader.PropertiesLauncher")
 		envvar.SetVal(&e.EnvVars, "LOADER_PATH", "/deployments/dependencies/")
 
-		if e.Integration.Spec.Context != "" {
-			name := e.Integration.Spec.Context
-			ctx := v1alpha1.NewIntegrationContext(e.Integration.Namespace, name)
+		deps := make([]string, 0, 2+len(e.Context.Status.Artifacts))
+		deps = append(deps, "/etc/camel/resources")
+		deps = append(deps, "./resources")
 
-			if err := sdk.Get(&ctx); err != nil {
-				return errors.Wrapf(err, "unable to find integration context %s, %s", ctx.Name, err)
+		for _, artifact := range e.Context.Status.Artifacts {
+			if strings.HasPrefix(artifact.ID, "org.apache.camel.k:camel-k-runtime-spring-boot:") {
+				// do not include runner jar
+				continue
 			}
-
-			deps := make([]string, 0, len(ctx.Status.Artifacts))
-			for _, artifact := range ctx.Status.Artifacts {
-				if strings.HasPrefix(artifact.ID, "org.apache.camel.k:camel-k-runtime-spring-boot:") {
-					// do not include runner jar
-					continue
-				}
-
-				deps = append(deps, artifact.Target)
+			if strings.HasPrefix(artifact.ID, "org.apache.logging.log4j:") {
+				// do not include logging, deps are embedded in runner jar
+				continue
 			}
 
-			envvar.SetVal(&e.EnvVars, "LOADER_HOME", "/deployments")
-			envvar.SetVal(&e.EnvVars, "LOADER_PATH", strings.Join(deps, ","))
+			deps = append(deps, artifact.Target)
 		}
+
+		envvar.SetVal(&e.EnvVars, "LOADER_HOME", "/deployments")
+		envvar.SetVal(&e.EnvVars, "LOADER_PATH", strings.Join(deps, ","))
 	}
 
 	//
 	// Integration Context
 	//
 
-	if e.Context != nil && e.Context.Status.Phase == v1alpha1.IntegrationContextPhaseBuilding {
+	if e.IntegrationContextInPhase(v1alpha1.IntegrationContextPhaseBuilding) {
 		// add custom initialization logic
 		e.Steps = append(e.Steps, builder.NewStep("initialize/spring-boot", builder.InitPhase, springboot.Initialize))
 		e.Steps = append(e.Steps, builder.NewStep("build/compute-boot-dependencies", builder.ProjectBuildPhase+1, springboot.ComputeDependencies))
diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go
index 6c9b89af..e2490d2e 100644
--- a/pkg/trait/trait_test.go
+++ b/pkg/trait/trait_test.go
@@ -176,9 +176,11 @@ func createTestEnv(cluster v1alpha1.IntegrationPlatformCluster, script string) *
 			Spec: v1alpha1.IntegrationSpec{
 				Sources: []v1alpha1.SourceSpec{
 					{
-						Name:     "file.groovy",
+						DataSpec: v1alpha1.DataSpec{
+							Name:    "file.groovy",
+							Content: script,
+						},
 						Language: v1alpha1.LanguageGroovy,
-						Content:  script,
 					},
 				},
 			},
diff --git a/pkg/util/digest/digest.go b/pkg/util/digest/digest.go
index 5803a97d..67054fb0 100644
--- a/pkg/util/digest/digest.go
+++ b/pkg/util/digest/digest.go
@@ -49,12 +49,20 @@ func ComputeForIntegration(integration *v1alpha1.Integration) (string, error) {
 		}
 	}
 
+	// Integration resources
+	for _, item := range integration.Spec.Resources {
+		if _, err := hash.Write([]byte(item.Content)); err != nil {
+			return "", err
+		}
+	}
+
 	// Integration dependencies
 	for _, item := range integration.Spec.Dependencies {
 		if _, err := hash.Write([]byte(item)); err != nil {
 			return "", err
 		}
 	}
+
 	// Integration configuration
 	for _, item := range integration.Spec.Configuration {
 		if _, err := hash.Write([]byte(item.String())); err != nil {
diff --git a/pkg/util/envvar/envvar.go b/pkg/util/envvar/envvar.go
index 68f6ba7e..d8739cfe 100644
--- a/pkg/util/envvar/envvar.go
+++ b/pkg/util/envvar/envvar.go
@@ -30,6 +30,17 @@ func Get(vars []v1.EnvVar, name string) *v1.EnvVar {
 	return nil
 }
 
+// Remove --
+func Remove(vars *[]v1.EnvVar, name string) {
+	v := *vars
+	for i := 0; i < len(v); i++ {
+		if v[i].Name == name {
+			*vars = append(v[:i], v[i+1:]...)
+			return
+		}
+	}
+}
+
 // SetVal --
 func SetVal(vars *[]v1.EnvVar, name string, value string) {
 	envVar := Get(*vars, name)
diff --git a/runtime/camel-k-runtime-jvm/pom.xml b/runtime/camel-k-runtime-jvm/pom.xml
index aa486034..a4d18320 100644
--- a/runtime/camel-k-runtime-jvm/pom.xml
+++ b/runtime/camel-k-runtime-jvm/pom.xml
@@ -96,4 +96,19 @@
         </dependency>
     </dependencies>
 
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <environmentVariables>
+                        <CAMEL_K_RESOURCE_001>value from env</CAMEL_K_RESOURCE_001>
+                        <MY_OTHER_RESOURCE_TXT>env:CAMEL_K_RESOURCE_001</MY_OTHER_RESOURCE_TXT>
+                    </environmentVariables>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
 </project>
diff --git a/runtime/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/Application.java b/runtime/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/Application.java
index 682609cd..b98c95be 100644
--- a/runtime/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/Application.java
+++ b/runtime/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/Application.java
@@ -34,6 +34,13 @@
         // We now support setting the logging level only
         //
         RuntimeSupport.configureLogging();
+
+        //
+        // Install a custom protocol handler to support discovering resources
+        // from the platform i.e. in knative, resources are provided through
+        // env var as it is not possible to mount config maps / secrets.
+        //
+        RuntimeSupport.configureStreamHandler();
     }
 
     // *******************************
diff --git a/runtime/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/RuntimeSupport.java b/runtime/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/RuntimeSupport.java
index 700d70d8..60d1cf93 100644
--- a/runtime/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/RuntimeSupport.java
+++ b/runtime/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/RuntimeSupport.java
@@ -16,8 +16,15 @@
  */
 package org.apache.camel.k.jvm;
 
+import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.Reader;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
 import java.nio.file.FileVisitResult;
 import java.nio.file.FileVisitor;
 import java.nio.file.Files;
@@ -25,8 +32,11 @@
 import java.nio.file.Paths;
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Base64;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Properties;
+import java.util.zip.GZIPInputStream;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.NoFactoryAvailableException;
@@ -38,7 +48,9 @@
 import org.apache.camel.spi.FactoryFinder;
 import org.apache.camel.util.IntrospectionSupport;
 import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.URISupport;
 import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.core.LoggerContext;
@@ -203,4 +215,90 @@ public static RoutesLoader lookupLoaderFromResource(CamelContext context, Source
 
         return loader;
     }
+
+    public static void configureStreamHandler() {
+        URL.setURLStreamHandlerFactory(protocol -> "platform".equals(protocol) ? new PlatformStreamHandler() : null);
+    }
+
+    // ***************************************
+    //
+    //
+    //
+    // ***************************************
+
+    private static class PlatformStreamHandler extends URLStreamHandler {
+        @Override
+        protected URLConnection openConnection(URL url) throws IOException {
+            return new URLConnection(url) {
+                @Override
+                public void connect() throws IOException {
+                }
+
+                @Override
+                public InputStream getInputStream() throws IOException {
+                    InputStream is = null;
+
+                    // check if the file exists
+                    Path path = Paths.get(url.getPath());
+                    if (Files.exists(path)) {
+                        is = Files.newInputStream(path);
+                    }
+
+                    // check if the file exists in classpath
+                    if (is == null) {
+                        is = ObjectHelper.loadResourceAsStream(url.getPath());
+                    }
+
+                    if (is == null) {
+                        String name = getURL().getPath().toUpperCase();
+                        name = name.replace(" ", "_");
+                        name = name.replace(".", "_");
+                        name = name.replace("-", "_");
+
+                        String envName = System.getenv(name);
+                        String envType = StringUtils.substringBefore(envName, ":");
+                        String envQuery = StringUtils.substringAfter(envName, "?");
+
+                        envName = StringUtils.substringAfter(envName, ":");
+                        envName = StringUtils.substringBefore(envName, "?");
+
+                        if (envName != null) {
+                            try {
+                                final Map<String, Object> params = URISupport.parseQuery(envQuery);
+                                final boolean compression = Boolean.valueOf((String) params.get("compression"));
+
+                                if (StringUtils.equals(envType, "env")) {
+                                    String data = System.getenv(envName);
+
+                                    if (data == null) {
+                                        throw new IllegalArgumentException("Unknown env var: " + envName);
+                                    }
+
+                                    is = new ByteArrayInputStream(data.getBytes());
+                                } else if (StringUtils.equals(envType, "file")) {
+                                    Path data = Paths.get(envName);
+
+                                    if (!Files.exists(data)) {
+                                        throw new FileNotFoundException(envName);
+                                    }
+
+                                    is = Files.newInputStream(data);
+                                } else if (StringUtils.equals(envType, "classpath")) {
+                                    is = ObjectHelper.loadResourceAsStream(envName);
+                                }
+
+                                if (is != null && compression) {
+                                    is = new GZIPInputStream(Base64.getDecoder().wrap(is));
+                                }
+                            } catch (URISyntaxException e) {
+                                throw new IOException(e);
+                            }
+                        }
+                    }
+
+                    return is;
+                }
+            };
+        }
+    }
 }
diff --git a/runtime/camel-k-runtime-jvm/src/test/java/org/apache/camel/k/jvm/RuntimeTest.java b/runtime/camel-k-runtime-jvm/src/test/java/org/apache/camel/k/jvm/RuntimeTest.java
index 909ae233..e434c268 100644
--- a/runtime/camel-k-runtime-jvm/src/test/java/org/apache/camel/k/jvm/RuntimeTest.java
+++ b/runtime/camel-k-runtime-jvm/src/test/java/org/apache/camel/k/jvm/RuntimeTest.java
@@ -16,11 +16,15 @@
  */
 package org.apache.camel.k.jvm;
 
+import java.io.InputStream;
+import java.nio.charset.Charset;
 import java.util.List;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Route;
 import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.ResourceHelper;
+import org.apache.commons.io.IOUtils;
 import org.junit.jupiter.api.Test;
 
 import static org.assertj.core.api.Java6Assertions.assertThat;
@@ -48,4 +52,24 @@ void testLoadMultipleRoutes() throws Exception {
             runtime.stop();
         }
     }
+
+
+    @Test
+    void testLoadResource() throws Exception {
+        RuntimeSupport.configureStreamHandler();
+
+        CamelContext context = new Runtime().getCamelContext();
+
+        try (InputStream is = ResourceHelper.resolveMandatoryResourceAsInputStream(context, "platform:my-resource.txt")) {
+            String content = IOUtils.toString(is, Charset.defaultCharset());
+
+            assertThat(content).isEqualTo("value from file resource");
+        }
+
+        try (InputStream is = ResourceHelper.resolveMandatoryResourceAsInputStream(context, "platform:my-other-resource.txt")) {
+            String content = IOUtils.toString(is, Charset.defaultCharset());
+
+            assertThat(content).isEqualTo("value from env");
+        }
+    }
 }
diff --git a/runtime/camel-k-runtime-jvm/src/test/resources/my-resource.txt b/runtime/camel-k-runtime-jvm/src/test/resources/my-resource.txt
new file mode 100644
index 00000000..817b31fc
--- /dev/null
+++ b/runtime/camel-k-runtime-jvm/src/test/resources/my-resource.txt
@@ -0,0 +1 @@
+value from file resource
\ No newline at end of file
diff --git a/runtime/camel-k-runtime-kotlin/src/main/kotlin/org/apache/camel/k/kotlin/KotlinRoutesLoader.kt b/runtime/camel-k-runtime-kotlin/src/main/kotlin/org/apache/camel/k/kotlin/KotlinRoutesLoader.kt
index 79025393..d72bb605 100644
--- a/runtime/camel-k-runtime-kotlin/src/main/kotlin/org/apache/camel/k/kotlin/KotlinRoutesLoader.kt
+++ b/runtime/camel-k-runtime-kotlin/src/main/kotlin/org/apache/camel/k/kotlin/KotlinRoutesLoader.kt
@@ -21,7 +21,7 @@ import org.apache.camel.k.Language
 import org.apache.camel.k.RoutesLoader
 import org.apache.camel.k.RuntimeRegistry
 import org.apache.camel.k.Source
-import org.apache.camel.k.jvm.*
+import org.apache.camel.k.jvm.URIResolver
 import org.apache.camel.k.kotlin.dsl.IntegrationConfiguration
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
diff --git a/runtime/camel-k-runtime-spring-boot/src/main/java/org/apache/camel/k/spring/boot/Application.java b/runtime/camel-k-runtime-spring-boot/src/main/java/org/apache/camel/k/spring/boot/Application.java
index 427dd9ed..a6fbb5a9 100644
--- a/runtime/camel-k-runtime-spring-boot/src/main/java/org/apache/camel/k/spring/boot/Application.java
+++ b/runtime/camel-k-runtime-spring-boot/src/main/java/org/apache/camel/k/spring/boot/Application.java
@@ -42,6 +42,15 @@
 public class Application {
     private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);
 
+    static {
+        //
+        // Install a custom protocol handler to support discovering resources
+        // from the platform i.e. in knative, resources are provided through
+        // env var as it is not possible to mount config maps / secrets.
+        //
+        RuntimeSupport.configureStreamHandler();
+    }
+
     public static void main(String[] args) {
         SpringApplication.run(Application.class, args);
     }


 

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


With regards,
Apache Git Services