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:03 UTC

[camel-k] 08/21: kamelets: support automatic configuration

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 69a6947eb28c8c24425f347df8006ef626acd43b
Author: Nicola Ferraro <ni...@gmail.com>
AuthorDate: Fri Jul 3 11:45:05 2020 +0200

    kamelets: support automatic configuration
---
 pkg/apis/camel/v1/integration_types_support.go |  16 +++
 pkg/trait/kamelets.go                          |  94 ++++++++++++++--
 pkg/trait/kamelets_test.go                     | 150 ++++++++++++++++++++++++-
 pkg/util/test/client.go                        |  19 +++-
 4 files changed, 264 insertions(+), 15 deletions(-)

diff --git a/pkg/apis/camel/v1/integration_types_support.go b/pkg/apis/camel/v1/integration_types_support.go
index dd848f0..2e807d3 100644
--- a/pkg/apis/camel/v1/integration_types_support.go
+++ b/pkg/apis/camel/v1/integration_types_support.go
@@ -156,6 +156,22 @@ func (in *IntegrationStatus) AddOrReplaceGeneratedSources(sources ...SourceSpec)
 	in.GeneratedSources = append(in.GeneratedSources, newSources...)
 }
 
+// AddConfigurationsIfMissing --
+func (in *IntegrationStatus) AddConfigurationsIfMissing(configurations ...ConfigurationSpec) {
+	for _, config := range configurations {
+		alreadyPresent := false
+		for _, r := range in.Configuration {
+			if r.Type == config.Type && r.Value == config.Value {
+				alreadyPresent = true
+				break
+			}
+		}
+		if !alreadyPresent {
+			in.Configuration = append(in.Configuration, config)
+		}
+	}
+}
+
 // Configurations --
 func (in *IntegrationSpec) Configurations() []ConfigurationSpec {
 	if in == nil {
diff --git a/pkg/trait/kamelets.go b/pkg/trait/kamelets.go
index 55b61a2..2a98a3a 100644
--- a/pkg/trait/kamelets.go
+++ b/pkg/trait/kamelets.go
@@ -45,8 +45,25 @@ type kameletsTrait struct {
 	List string `property:"list"`
 }
 
+type configurationKey struct {
+	kamelet         string
+	configurationID string
+}
+
+func newConfigurationKey(kamelet, configurationID string) configurationKey {
+	return configurationKey{
+		kamelet:         kamelet,
+		configurationID: configurationID,
+	}
+}
+
+const (
+	kameletLabel              = "camel.apache.org/kamelet"
+	kameletConfigurationLabel = "camel.apache.org/kamelet.configuration"
+)
+
 var (
-	kameletNameRegexp = regexp.MustCompile("kamelet:(?://)?([a-z0-9-.]+)(?:$|[^a-z0-9-.].*)")
+	kameletNameRegexp = regexp.MustCompile("kamelet:(?://)?([a-z0-9-.]+(/[a-z0-9-.]+)?)(?:$|[^a-z0-9-.].*)")
 )
 
 func newKameletsTrait() Trait {
@@ -79,19 +96,21 @@ func (t *kameletsTrait) Configure(e *Environment) (bool, error) {
 
 	}
 
-	return len(t.getKamelets()) > 0, nil
+	return len(t.getKameletKeys()) > 0, nil
 }
 
 func (t *kameletsTrait) Apply(e *Environment) error {
 	if err := t.addKamelets(e); err != nil {
 		return err
 	}
-
+	if err := t.addConfigurationSecrets(e); err != nil {
+		return err
+	}
 	return nil
 }
 
 func (t *kameletsTrait) addKamelets(e *Environment) error {
-	for _, k := range t.getKamelets() {
+	for _, k := range t.getKameletKeys() {
 		var kamelet v1alpha1.Kamelet
 		key := client.ObjectKey{
 			Namespace: e.Integration.Namespace,
@@ -168,14 +187,75 @@ func (t *kameletsTrait) addKameletAsSource(e *Environment, kamelet v1alpha1.Kame
 	return nil
 }
 
-func (t *kameletsTrait) getKamelets() []string {
+func (t *kameletsTrait) addConfigurationSecrets(e *Environment) error {
+	for _, k := range t.getConfigurationKeys() {
+		var options = metav1.ListOptions{
+			LabelSelector: fmt.Sprintf("%s=%s", kameletLabel, k.kamelet),
+		}
+		if k.configurationID != "" {
+			options.LabelSelector = fmt.Sprintf("%s=%s,%s=%s", kameletLabel, k.kamelet, kameletConfigurationLabel, k.configurationID)
+		}
+		secrets, err := t.Client.CoreV1().Secrets(e.Integration.Namespace).List(options)
+		if err != nil {
+			return err
+		}
+
+		for _, item := range secrets.Items {
+			if item.Labels != nil && item.Labels[kameletConfigurationLabel] != k.configurationID {
+				continue
+			}
+
+			e.Integration.Status.AddConfigurationsIfMissing(v1.ConfigurationSpec{
+				Type:  "secret",
+				Value: item.Name,
+			})
+		}
+	}
+	return nil
+}
+
+func (t *kameletsTrait) getKameletKeys() []string {
 	answer := make([]string, 0)
 	for _, item := range strings.Split(t.List, ",") {
 		i := strings.Trim(item, " \t\"")
+		if strings.Contains(i, "/") {
+			i = strings.SplitN(i, "/", 2)[0]
+		}
 		if i != "" {
-			answer = append(answer, i)
+			util.StringSliceUniqueAdd(&answer, i)
+		}
+	}
+	sort.Strings(answer)
+	return answer
+}
+
+func (t *kameletsTrait) getConfigurationKeys() []configurationKey {
+	answer := make([]configurationKey, 0)
+	for _, item := range t.getKameletKeys() {
+		answer = append(answer, newConfigurationKey(item, ""))
+	}
+	for _, item := range strings.Split(t.List, ",") {
+		i := strings.Trim(item, " \t\"")
+		if strings.Contains(i, "/") {
+			parts := strings.SplitN(i, "/", 2)
+			newKey := newConfigurationKey(parts[0], parts[1])
+			alreadyPresent := false
+			for _, existing := range answer {
+				if existing == newKey {
+					alreadyPresent = true
+					break
+				}
+			}
+			if !alreadyPresent {
+				answer = append(answer, newKey)
+			}
 		}
 	}
+	sort.Slice(answer, func(i, j int) bool {
+		o1 := answer[i]
+		o2 := answer[j]
+		return o1.kamelet < o2.kamelet || (o1.kamelet == o2.kamelet && o1.configurationID < o2.configurationID)
+	})
 	return answer
 }
 
@@ -236,7 +316,7 @@ func integrationSourceFromKameletSource(e *Environment, kamelet v1alpha1.Kamelet
 func extractKamelets(uris []string) (kamelets []string) {
 	for _, uri := range uris {
 		matches := kameletNameRegexp.FindStringSubmatch(uri)
-		if len(matches) == 2 {
+		if len(matches) > 1 {
 			kamelets = append(kamelets, matches[1])
 		}
 	}
diff --git a/pkg/trait/kamelets_test.go b/pkg/trait/kamelets_test.go
index 500baba..4916046 100644
--- a/pkg/trait/kamelets_test.go
+++ b/pkg/trait/kamelets_test.go
@@ -64,7 +64,17 @@ func TestConfigurationWithKamelets(t *testing.T) {
 	enabled, err := trait.Configure(environment)
 	assert.NoError(t, err)
 	assert.True(t, enabled)
-	assert.Equal(t, []string{"c0", "c1", "c2", "complex-.-.-1a", "complex-.-.-1b", "complex-.-.-1c"}, trait.getKamelets())
+	assert.Equal(t, []string{"c0", "c1", "c2", "complex-.-.-1a", "complex-.-.-1b", "complex-.-.-1c"}, trait.getKameletKeys())
+	assert.Equal(t, []configurationKey{
+		newConfigurationKey("c0", ""),
+		newConfigurationKey("c1", ""),
+		newConfigurationKey("c2", ""),
+		newConfigurationKey("complex-.-.-1a", ""),
+		newConfigurationKey("complex-.-.-1b", ""),
+		newConfigurationKey("complex-.-.-1b", "a"),
+		newConfigurationKey("complex-.-.-1c", ""),
+		newConfigurationKey("complex-.-.-1c", "b"),
+	}, trait.getConfigurationKeys())
 }
 
 func TestKameletLookup(t *testing.T) {
@@ -93,7 +103,7 @@ func TestKameletLookup(t *testing.T) {
 	enabled, err := trait.Configure(environment)
 	assert.NoError(t, err)
 	assert.True(t, enabled)
-	assert.Equal(t, []string{"timer"}, trait.getKamelets())
+	assert.Equal(t, []string{"timer"}, trait.getKameletKeys())
 
 	err = trait.Apply(environment)
 	assert.NoError(t, err)
@@ -141,7 +151,7 @@ func TestKameletSecondarySourcesLookup(t *testing.T) {
 	enabled, err := trait.Configure(environment)
 	assert.NoError(t, err)
 	assert.True(t, enabled)
-	assert.Equal(t, []string{"timer"}, trait.getKamelets())
+	assert.Equal(t, []string{"timer"}, trait.getKameletKeys())
 
 	err = trait.Apply(environment)
 	assert.NoError(t, err)
@@ -191,7 +201,7 @@ func TestNonYAMLKameletLookup(t *testing.T) {
 	enabled, err := trait.Configure(environment)
 	assert.NoError(t, err)
 	assert.True(t, enabled)
-	assert.Equal(t, []string{"timer"}, trait.getKamelets())
+	assert.Equal(t, []string{"timer"}, trait.getKameletKeys())
 
 	err = trait.Apply(environment)
 	assert.NoError(t, err)
@@ -237,7 +247,7 @@ func TestErrorMultipleKameletSources(t *testing.T) {
 	enabled, err := trait.Configure(environment)
 	assert.NoError(t, err)
 	assert.True(t, enabled)
-	assert.Equal(t, []string{"timer"}, trait.getKamelets())
+	assert.Equal(t, []string{"timer"}, trait.getKameletKeys())
 
 	err = trait.Apply(environment)
 	assert.Error(t, err)
@@ -302,7 +312,7 @@ func TestMultipleKamelets(t *testing.T) {
 	enabled, err := trait.Configure(environment)
 	assert.NoError(t, err)
 	assert.True(t, enabled)
-	assert.Equal(t, []string{"logger", "timer"}, trait.getKamelets())
+	assert.Equal(t, []string{"logger", "timer"}, trait.getKameletKeys())
 
 	err = trait.Apply(environment)
 	assert.NoError(t, err)
@@ -337,6 +347,134 @@ func TestMultipleKamelets(t *testing.T) {
 	assert.Equal(t, []string{"camel:log", "camel:tbd", "camel:timer", "camel:xxx"}, environment.Integration.Status.Dependencies)
 }
 
+func TestKameletConfigLookup(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",
+				},
+			},
+			Dependencies: []string{
+				"camel:timer",
+				"camel:log",
+			},
+		},
+	}, &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "test",
+			Name:      "my-secret",
+			Labels: map[string]string{
+				"camel.apache.org/kamelet": "timer",
+			},
+		},
+	}, &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "test",
+			Name:      "my-secret2",
+			Labels: map[string]string{
+				"camel.apache.org/kamelet":               "timer",
+				"camel.apache.org/kamelet.configuration": "id2",
+			},
+		},
+	}, &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "test",
+			Name:      "my-secret3",
+			Labels: map[string]string{
+				"camel.apache.org/kamelet": "timer",
+			},
+		},
+	})
+	enabled, err := trait.Configure(environment)
+	assert.NoError(t, err)
+	assert.True(t, enabled)
+	assert.Equal(t, []string{"timer"}, trait.getKameletKeys())
+	assert.Equal(t, []configurationKey{newConfigurationKey("timer", "")}, trait.getConfigurationKeys())
+
+	err = trait.Apply(environment)
+	assert.NoError(t, err)
+	assert.Len(t, environment.Integration.Status.Configuration, 2)
+	assert.Contains(t, environment.Integration.Status.Configuration, v1.ConfigurationSpec{Type: "secret", Value: "my-secret"})
+	assert.NotContains(t, environment.Integration.Status.Configuration, v1.ConfigurationSpec{Type: "secret", Value: "my-secret2"})
+	assert.Contains(t, environment.Integration.Status.Configuration, v1.ConfigurationSpec{Type: "secret", Value: "my-secret3"})
+}
+
+func TestKameletNamedConfigLookup(t *testing.T) {
+	trait, environment := createKameletsTestEnvironment(`
+- from:
+    uri: kamelet:timer/id2
+    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",
+				},
+			},
+			Dependencies: []string{
+				"camel:timer",
+				"camel:log",
+			},
+		},
+	}, &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "test",
+			Name:      "my-secret",
+			Labels: map[string]string{
+				"camel.apache.org/kamelet": "timer",
+			},
+		},
+	}, &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "test",
+			Name:      "my-secret2",
+			Labels: map[string]string{
+				"camel.apache.org/kamelet":               "timer",
+				"camel.apache.org/kamelet.configuration": "id2",
+			},
+		},
+	}, &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "test",
+			Name:      "my-secret3",
+			Labels: map[string]string{
+				"camel.apache.org/kamelet":               "timer",
+				"camel.apache.org/kamelet.configuration": "id3",
+			},
+		},
+	})
+	enabled, err := trait.Configure(environment)
+	assert.NoError(t, err)
+	assert.True(t, enabled)
+	assert.Equal(t, []string{"timer"}, trait.getKameletKeys())
+	assert.Equal(t, []configurationKey{
+		newConfigurationKey("timer", ""),
+		newConfigurationKey("timer", "id2"),
+	}, trait.getConfigurationKeys())
+
+	err = trait.Apply(environment)
+	assert.NoError(t, err)
+	assert.Len(t, environment.Integration.Status.Configuration, 2)
+	assert.Contains(t, environment.Integration.Status.Configuration, v1.ConfigurationSpec{Type: "secret", Value: "my-secret"})
+	assert.Contains(t, environment.Integration.Status.Configuration, v1.ConfigurationSpec{Type: "secret", Value: "my-secret2"})
+	assert.NotContains(t, environment.Integration.Status.Configuration, v1.ConfigurationSpec{Type: "secret", Value: "my-secret3"})
+}
+
 func createKameletsTestEnvironment(flow string, objects ...runtime.Object) (*kameletsTrait, *Environment) {
 	catalog, _ := camel.DefaultCatalog()
 
diff --git a/pkg/util/test/client.go b/pkg/util/test/client.go
index d3df07b..b223f95 100644
--- a/pkg/util/test/client.go
+++ b/pkg/util/test/client.go
@@ -20,9 +20,9 @@ package test
 import (
 	"github.com/apache/camel-k/pkg/apis"
 	"github.com/apache/camel-k/pkg/client"
-
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/client-go/kubernetes"
+	fakeclientset "k8s.io/client-go/kubernetes/fake"
 	clientscheme "k8s.io/client-go/kubernetes/scheme"
 	"k8s.io/client-go/rest"
 
@@ -40,10 +40,25 @@ func NewFakeClient(initObjs ...runtime.Object) (client.Client, error) {
 	}
 
 	c := fake.NewFakeClientWithScheme(scheme, initObjs...)
+	filtered := make([]runtime.Object, 0, len(initObjs))
+	for _, o := range initObjs {
+		kinds, _, _ := scheme.ObjectKinds(o)
+		allow := true
+		for _, k := range kinds {
+			if k.Group == "camel.apache.org" {
+				allow = false
+				break
+			}
+		}
+		if allow {
+			filtered = append(filtered, o)
+		}
+	}
+	clientset := fakeclientset.NewSimpleClientset(filtered...)
 
 	return &FakeClient{
 		Client:    c,
-		Interface: nil,
+		Interface: clientset,
 	}, nil
 }