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:
[email protected]


With regards,
Apache Git Services

Reply via email to