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

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

commit 69c2ecc6ad3100514b5c1eef258aaa596d460f89
Author: Nicola Ferraro <[email protected]>
AuthorDate: Wed Jun 24 12:54:55 2020 +0200

    kamelets: adding mount paths and implement kamelets trait
---
 pkg/apis/camel/v1alpha1/kamelet_types.go         |   2 +-
 pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go |   5 +-
 pkg/trait/kamelets.go                            | 112 +++++++++----
 pkg/trait/kamelets_test.go                       | 197 ++++++++++++++++++++++-
 pkg/trait/trait_test.go                          |   2 +-
 pkg/trait/trait_types.go                         |  17 +-
 6 files changed, 291 insertions(+), 44 deletions(-)

diff --git a/pkg/apis/camel/v1alpha1/kamelet_types.go 
b/pkg/apis/camel/v1alpha1/kamelet_types.go
index fa53584..a8e67fa 100644
--- a/pkg/apis/camel/v1alpha1/kamelet_types.go
+++ b/pkg/apis/camel/v1alpha1/kamelet_types.go
@@ -31,7 +31,7 @@ const (
 type KameletSpec struct {
        Definition    JSONSchemaProps             `json:"definition,omitempty"`
        Sources       []camelv1.SourceSpec        `json:"sources,omitempty"`
-       Flow          camelv1.Flow                `json:"flow,omitempty"`
+       Flow          *camelv1.Flow               `json:"flow,omitempty"`
        Authorization AuthorizationSpec           
`json:"authorization,omitempty"`
        Types         map[EventSlot]EventTypeSpec `json:"types,omitempty"`
        Dependencies  []string                    
`json:"dependencies,omitempty"`
diff --git a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go 
b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
index 2f7512c..61e8aa0 100644
--- a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
@@ -474,7 +474,10 @@ func (in *KameletSpec) DeepCopyInto(out *KameletSpec) {
                        (*in)[i].DeepCopyInto(&(*out)[i])
                }
        }
-       in.Flow.DeepCopyInto(&out.Flow)
+       if in.Flow != nil {
+               in, out := &in.Flow, &out.Flow
+               *out = (*in).DeepCopy()
+       }
        out.Authorization = in.Authorization
        if in.Types != nil {
                in, out := &in.Types, &out.Types
diff --git a/pkg/trait/kamelets.go b/pkg/trait/kamelets.go
index 4ba9d67..180985a 100644
--- a/pkg/trait/kamelets.go
+++ b/pkg/trait/kamelets.go
@@ -28,6 +28,7 @@ import (
        corev1 "k8s.io/api/core/v1"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "regexp"
+       "sigs.k8s.io/controller-runtime/pkg/client"
        "sort"
        "strconv"
        "strings"
@@ -54,6 +55,11 @@ func newKameletsTrait() Trait {
        }
 }
 
+// IsPlatformTrait overrides base class method
+func (t *kameletsTrait) IsPlatformTrait() bool {
+       return true
+}
+
 func (t *kameletsTrait) Configure(e *Environment) (bool, error) {
        if t.Enabled != nil && !*t.Enabled {
                return false, nil
@@ -73,47 +79,85 @@ func (t *kameletsTrait) Configure(e *Environment) (bool, 
error) {
 
        }
 
-       return t.List != "", nil
+       return len(t.getKamelets()) > 0, nil
 }
 
 func (t *kameletsTrait) Apply(e *Environment) error {
-
+       if err := t.addKamelets(e); err != nil {
+               return err
+       }
        return nil
 }
 
-// IsPlatformTrait overrides base class method
-func (t *kameletsTrait) IsPlatformTrait() bool {
-       return true
+func (t *kameletsTrait) addKamelets(e *Environment) error {
+       for _, k := range t.getKamelets() {
+               var kamelet v1alpha1.Kamelet
+               key := client.ObjectKey{
+                       Namespace: e.Integration.Namespace,
+                       Name:      k,
+               }
+               if err := t.Client.Get(t.Ctx, key, &kamelet); err != nil {
+                       return err
+               }
+               if err := t.addKameletAsSource(e, kamelet); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 
-func (t *kameletsTrait) addKameletAsSource(e *Environment, kamelet 
*v1alpha1.Kamelet) error {
+func (t *kameletsTrait) addKameletAsSource(e *Environment, kamelet 
v1alpha1.Kamelet) error {
        var sources []v1.SourceSpec
 
-       flowData, err := flows.Marshal([]v1.Flow{kamelet.Spec.Flow})
-       if err != nil {
-               return err
-       }
-       flowSource := v1.SourceSpec{
-               DataSpec: v1.DataSpec{
-                       Name:    "flow.yaml",
-                       Content: string(flowData),
-               },
-               Language: v1.LanguageYaml,
-               Type:     v1.SourceTypeKamelet,
-       }
-       flowSource, err = integrationSourceFromKameletSource(e, kamelet, 
flowSource, fmt.Sprintf("%s-kamelet-%s-flow", e.Integration.Name, kamelet.Name))
-       if err != nil {
-               return err
+       if kamelet.Spec.Flow != nil {
+               flowData, err := flows.Marshal([]v1.Flow{*kamelet.Spec.Flow})
+               if err != nil {
+                       return err
+               }
+               flowSource := v1.SourceSpec{
+                       DataSpec: v1.DataSpec{
+                               Name:    fmt.Sprintf("%s.yaml", kamelet.Name),
+                               Content: string(flowData),
+                       },
+                       Language: v1.LanguageYaml,
+                       Type:     v1.SourceTypeKamelet,
+               }
+               flowSource, err = integrationSourceFromKameletSource(e, 
kamelet, flowSource, fmt.Sprintf("%s-kamelet-%s-flow", e.Integration.Name, 
kamelet.Name))
+               if err != nil {
+                       return err
+               }
+               sources = append(sources, flowSource)
        }
-       sources = append(sources, flowSource)
 
        for idx, s := range kamelet.Spec.Sources {
-               intSource, err := integrationSourceFromKameletSource(e, 
kamelet, s, fmt.Sprintf("%s-kamelet-%s-source-%03d", e.Integration.Name, 
kamelet.Name, idx))
+               intSource, err := integrationSourceFromKameletSource(e, 
kamelet, s, fmt.Sprintf("%s-kamelet-%s-%03d", e.Integration.Name, kamelet.Name, 
idx))
                if err != nil {
                        return err
                }
                sources = append(sources, intSource)
        }
+
+       kameletCounter := 0
+       for _, source := range sources {
+               if source.Type == v1.SourceTypeKamelet {
+                       kameletCounter++
+               }
+               replaced := false
+               for idx, existing := range 
e.Integration.Status.GeneratedSources {
+                       if existing.Name == source.Name {
+                               replaced = true
+                               e.Integration.Status.GeneratedSources[idx] = 
source
+                       }
+               }
+               if !replaced {
+                       e.Integration.Status.GeneratedSources = 
append(e.Integration.Status.GeneratedSources, source)
+               }
+       }
+
+       if kameletCounter > 1 {
+               return fmt.Errorf(`kamelet %s contains %d sources of type 
"kamelet": at most one is allowed`, kamelet.Name, kameletCounter)
+       }
+
        return nil
 }
 
@@ -128,9 +172,15 @@ func (t *kameletsTrait) getKamelets() []string {
        return answer
 }
 
-func integrationSourceFromKameletSource(e *Environment, kamelet 
*v1alpha1.Kamelet, source v1.SourceSpec, name string) (v1.SourceSpec, error) {
+func integrationSourceFromKameletSource(e *Environment, kamelet 
v1alpha1.Kamelet, source v1.SourceSpec, name string) (v1.SourceSpec, error) {
+       if source.Type == v1.SourceTypeKamelet {
+               // Kamelets must be named "<kamelet-name>.extension"
+               language := source.InferLanguage()
+               source.Name = fmt.Sprintf("%s.%s", kamelet.Name, 
string(language))
+       }
+
        if source.DataSpec.ContentRef != "" {
-               return renameSource(kamelet, source), nil
+               return source, nil
        }
 
        // Create configmaps to avoid storing kamelet definitions in the 
integration CR
@@ -169,19 +219,11 @@ func integrationSourceFromKameletSource(e *Environment, 
kamelet *v1alpha1.Kamele
 
        e.Resources.Add(&cm)
 
-       target := renameSource(kamelet, source)
+       target := source.DeepCopy()
        target.Content = ""
        target.ContentRef = name
        target.ContentKey = "content"
-       return target, nil
-}
-
-func renameSource(kamelet *v1alpha1.Kamelet, source v1.SourceSpec) 
v1.SourceSpec {
-       target := source.DeepCopy()
-       if !strings.HasPrefix(target.Name, fmt.Sprintf("kamelet-%s-", 
kamelet.Name)) {
-               target.Name = fmt.Sprintf("kamelet-%s-%s", kamelet.Name, 
target.Name)
-       }
-       return *target
+       return *target, nil
 }
 
 func extractKamelets(uris []string) (kamelets []string) {
diff --git a/pkg/trait/kamelets_test.go b/pkg/trait/kamelets_test.go
index b7d4fe7..30585c3 100644
--- a/pkg/trait/kamelets_test.go
+++ b/pkg/trait/kamelets_test.go
@@ -19,6 +19,10 @@ package trait
 
 import (
        "context"
+       "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+       corev1 "k8s.io/api/core/v1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/apimachinery/pkg/runtime"
        "testing"
 
        "github.com/apache/camel-k/pkg/util/camel"
@@ -30,7 +34,20 @@ import (
        "github.com/apache/camel-k/pkg/util/test"
 )
 
-func TestKameletsFinding(t *testing.T) {
+func TestConfigurationNoKameletsUsed(t *testing.T) {
+       trait, environment := createKameletsTestEnvironment(`
+- from:
+    uri: timer:tick
+    steps:
+    - to: log:info
+`)
+       enabled, err := trait.Configure(environment)
+       assert.NoError(t, err)
+       assert.False(t, enabled)
+       assert.Equal(t, "", trait.List)
+}
+
+func TestConfigurationWithKamelets(t *testing.T) {
        trait, environment := createKameletsTestEnvironment(`
 - from:
     uri: kamelet:c1
@@ -50,10 +67,180 @@ func TestKameletsFinding(t *testing.T) {
        assert.Equal(t, []string{"c0", "c1", "c2", "complex-.-.-1a", 
"complex-.-.-1b", "complex-.-.-1c"}, trait.getKamelets())
 }
 
-func createKameletsTestEnvironment(flow string) (*kameletsTrait, *Environment) 
{
+func TestKameletLookup(t *testing.T) {
+       trait, environment := createKameletsTestEnvironment(`
+- from:
+    uri: kamelet:timer
+    steps:
+    - to: log:info
+`, &v1alpha1.Kamelet{
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: "test",
+                       Name:      "timer",
+               },
+               Spec: v1alpha1.KameletSpec{
+                       Flow: &v1.Flow{
+                               "from": map[string]interface{}{
+                                       "uri": "timer:tick",
+                               },
+                       },
+               },
+       })
+       enabled, err := trait.Configure(environment)
+       assert.NoError(t, err)
+       assert.True(t, enabled)
+       assert.Equal(t, []string{"timer"}, trait.getKamelets())
+
+       err = trait.Apply(environment)
+       assert.NoError(t, err)
+       cm := environment.Resources.GetConfigMap(func(_ *corev1.ConfigMap) bool 
{ return true })
+       assert.NotNil(t, cm)
+       assert.Equal(t, "it-kamelet-timer-flow", cm.Name)
+       assert.Equal(t, "test", cm.Namespace)
+
+       assert.Len(t, environment.Integration.Status.GeneratedSources, 1)
+       source := environment.Integration.Status.GeneratedSources[0]
+       assert.Equal(t, "timer.yaml", source.Name)
+       assert.Equal(t, "kamelet", string(source.Type))
+}
+
+func TestKameletSecondarySourcesLookup(t *testing.T) {
+       trait, environment := createKameletsTestEnvironment(`
+- from:
+    uri: kamelet:timer
+    steps:
+    - to: log:info
+`, &v1alpha1.Kamelet{
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: "test",
+                       Name:      "timer",
+               },
+               Spec: v1alpha1.KameletSpec{
+                       Flow: &v1.Flow{
+                               "from": map[string]interface{}{
+                                       "uri": "timer:tick",
+                               },
+                       },
+                       Sources: []v1.SourceSpec{
+                               {
+                                       DataSpec: v1.DataSpec{
+                                               Name:    "support.groovy",
+                                               Content: 
"from('xxx:xxx').('to:log:info')",
+                                       },
+                                       Language: v1.LanguageGroovy,
+                               },
+                       },
+               },
+       })
+       enabled, err := trait.Configure(environment)
+       assert.NoError(t, err)
+       assert.True(t, enabled)
+       assert.Equal(t, []string{"timer"}, trait.getKamelets())
+
+       err = trait.Apply(environment)
+       assert.NoError(t, err)
+       cmFlow := environment.Resources.GetConfigMap(func(c *corev1.ConfigMap) 
bool { return c.Name == "it-kamelet-timer-flow" })
+       assert.NotNil(t, cmFlow)
+       cmRes := environment.Resources.GetConfigMap(func(c *corev1.ConfigMap) 
bool { return c.Name == "it-kamelet-timer-000" })
+       assert.NotNil(t, cmRes)
+
+       assert.Len(t, environment.Integration.Status.GeneratedSources, 2)
+
+       flowSource := environment.Integration.Status.GeneratedSources[0]
+       assert.Equal(t, "timer.yaml", flowSource.Name)
+       assert.Equal(t, "kamelet", string(flowSource.Type))
+       assert.Equal(t, "it-kamelet-timer-flow", flowSource.ContentRef)
+       assert.Equal(t, "content", flowSource.ContentKey)
+
+       supportSource := environment.Integration.Status.GeneratedSources[1]
+       assert.Equal(t, "support.groovy", supportSource.Name)
+       assert.Equal(t, "", string(supportSource.Type))
+       assert.Equal(t, "it-kamelet-timer-000", supportSource.ContentRef)
+       assert.Equal(t, "content", supportSource.ContentKey)
+}
+
+func TestNonYAMLKameletLookup(t *testing.T) {
+       trait, environment := createKameletsTestEnvironment(`
+- from:
+    uri: kamelet:timer
+    steps:
+    - to: log:info
+`, &v1alpha1.Kamelet{
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: "test",
+                       Name:      "timer",
+               },
+               Spec: v1alpha1.KameletSpec{
+                       Sources: []v1.SourceSpec{
+                               {
+                                       DataSpec: v1.DataSpec{
+                                               Name:    "mykamelet.groovy",
+                                               Content: 
`from("timer").to("log:info")`,
+                                       },
+                                       Type: v1.SourceTypeKamelet,
+                               },
+                       },
+               },
+       })
+       enabled, err := trait.Configure(environment)
+       assert.NoError(t, err)
+       assert.True(t, enabled)
+       assert.Equal(t, []string{"timer"}, trait.getKamelets())
+
+       err = trait.Apply(environment)
+       assert.NoError(t, err)
+       cm := environment.Resources.GetConfigMap(func(_ *corev1.ConfigMap) bool 
{ return true })
+       assert.NotNil(t, cm)
+       assert.Equal(t, "it-kamelet-timer-000", cm.Name)
+       assert.Equal(t, "test", cm.Namespace)
+
+       assert.Len(t, environment.Integration.Status.GeneratedSources, 1)
+       source := environment.Integration.Status.GeneratedSources[0]
+       assert.Equal(t, "timer.groovy", source.Name)
+       assert.Equal(t, "kamelet", string(source.Type))
+}
+
+func TestErrorMultipleKameletSources(t *testing.T) {
+       trait, environment := createKameletsTestEnvironment(`
+- from:
+    uri: kamelet:timer
+    steps:
+    - to: log:info
+`, &v1alpha1.Kamelet{
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: "test",
+                       Name:      "timer",
+               },
+               Spec: v1alpha1.KameletSpec{
+                       Sources: []v1.SourceSpec{
+                               {
+                                       DataSpec: v1.DataSpec{
+                                               Name:    "mykamelet.groovy",
+                                               Content: 
`from("timer").to("log:info")`,
+                                       },
+                                       Type: v1.SourceTypeKamelet,
+                               },
+                       },
+                       Flow: &v1.Flow{
+                               "from": map[string]interface{}{
+                                       "uri": "timer:tick",
+                               },
+                       },
+               },
+       })
+       enabled, err := trait.Configure(environment)
+       assert.NoError(t, err)
+       assert.True(t, enabled)
+       assert.Equal(t, []string{"timer"}, trait.getKamelets())
+
+       err = trait.Apply(environment)
+       assert.Error(t, err)
+}
+
+func createKameletsTestEnvironment(flow string, objects ...runtime.Object) 
(*kameletsTrait, *Environment) {
        catalog, _ := camel.DefaultCatalog()
 
-       client, _ := test.NewFakeClient()
+       client, _ := test.NewFakeClient(objects...)
        trait := newKameletsTrait().(*kameletsTrait)
        trait.Ctx = context.TODO()
        trait.Client = client
@@ -62,6 +249,10 @@ func createKameletsTestEnvironment(flow string) 
(*kameletsTrait, *Environment) {
                Catalog:      NewCatalog(context.TODO(), nil),
                CamelCatalog: catalog,
                Integration: &v1.Integration{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Namespace: "test",
+                               Name:      "it",
+                       },
                        Spec: v1.IntegrationSpec{
                                Sources: []v1.SourceSpec{
                                        {
diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go
index d50bd91..bcf3519 100644
--- a/pkg/trait/trait_test.go
+++ b/pkg/trait/trait_test.go
@@ -382,7 +382,7 @@ func TestOnlySomeTraitsInfluenceBuild(t *testing.T) {
 
 func TestOnlySomeTraitsArePlatform(t *testing.T) {
        c := NewTraitTestCatalog()
-       platformTraits := []string{"builder", "camel", "jvm", "container", 
"dependencies", "deployer", "deployment", "environment", "openapi", "owner", 
"platform"}
+       platformTraits := []string{"builder", "camel", "jvm", "container", 
"dependencies", "deployer", "deployment", "environment", "kamelets", "openapi", 
"owner", "platform"}
 
        for _, trait := range c.allTraits() {
                if trait.IsPlatformTrait() {
diff --git a/pkg/trait/trait_types.go b/pkg/trait/trait_types.go
index babe121..e815469 100644
--- a/pkg/trait/trait_types.go
+++ b/pkg/trait/trait_types.go
@@ -54,6 +54,9 @@ var (
        // SourcesMountPath --
        SourcesMountPath = path.Join(BasePath, "sources")
 
+       // KameletsMountPath --
+       KameletsMountPath = path.Join(BasePath, "kamelets.d")
+
        // ResourcesMountPath --
        ResourcesMountPath = path.Join(BasePath, "resources")
 
@@ -527,12 +530,20 @@ func (e *Environment) ConfigureVolumesAndMounts(vols 
*[]corev1.Volume, mnts *[]c
 
        for i, s := range e.Integration.Sources() {
                cmName := fmt.Sprintf("%s-source-%03d", e.Integration.Name, i)
+               if s.ContentRef != "" {
+                       cmName = s.ContentRef
+               }
+
                refName := fmt.Sprintf("i-source-%03d", i)
+               if s.Type == v1.SourceTypeKamelet {
+                       refName = fmt.Sprintf("i-kamelet-source-%03d", i)
+               }
+
                resName := strings.TrimPrefix(s.Name, "/")
-               resPath := path.Join(SourcesMountPath, refName)
 
-               if s.ContentRef != "" {
-                       cmName = s.ContentRef
+               resPath := path.Join(SourcesMountPath, refName)
+               if s.Type == v1.SourceTypeKamelet {
+                       resPath = KameletsMountPath
                }
 
                *vols = append(*vols, corev1.Volume{

Reply via email to