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
}