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