You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by nf...@apache.org on 2020/09/17 09:10:00 UTC
[camel-k] 05/21: kamelets: adding mount paths and implement
kamelets trait
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 <ni...@gmail.com>
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{