You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ts...@apache.org on 2022/05/18 08:12:08 UTC
[camel-k] branch main updated: chore(cmd/trait): refactor cmd & trait packages
This is an automated email from the ASF dual-hosted git repository.
tsato pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git
The following commit(s) were added to refs/heads/main by this push:
new d9f10e963 chore(cmd/trait): refactor cmd & trait packages
d9f10e963 is described below
commit d9f10e963213b6dec74abe594e77270145c0b40e
Author: Tadayoshi Sato <sa...@gmail.com>
AuthorDate: Tue May 17 15:18:18 2022 +0900
chore(cmd/trait): refactor cmd & trait packages
---
pkg/cmd/trait_help.go | 107 ----------------
pkg/cmd/trait_support.go | 136 +++++++++++++++++++++
pkg/trait/{trait_types_test.go => test_support.go} | 16 ---
pkg/trait/trait_types_test.go | 133 +++++++++-----------
pkg/trait/util_test.go | 79 ------------
5 files changed, 194 insertions(+), 277 deletions(-)
diff --git a/pkg/cmd/trait_help.go b/pkg/cmd/trait_help.go
index b56e8c69e..0ef7bc9af 100644
--- a/pkg/cmd/trait_help.go
+++ b/pkg/cmd/trait_help.go
@@ -26,14 +26,12 @@ import (
"strings"
"github.com/fatih/structs"
- "github.com/mitchellh/mapstructure"
"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
"github.com/apache/camel-k/pkg/resources"
"github.com/apache/camel-k/pkg/trait"
- "github.com/apache/camel-k/pkg/util"
"github.com/apache/camel-k/pkg/util/indentedwriter"
)
@@ -263,108 +261,3 @@ func outputTraits(descriptions []*traitDescription) (string, error) {
return nil
})
}
-
-func validateTraits(catalog *trait.Catalog, traits []string) error {
- tp := catalog.ComputeTraitsProperties()
- for _, t := range traits {
- kv := strings.SplitN(t, "=", 2)
- prefix := kv[0]
- if strings.Contains(prefix, "[") {
- prefix = prefix[0:strings.Index(prefix, "[")]
- }
- if !util.StringSliceExists(tp, prefix) {
- return fmt.Errorf("%s is not a valid trait property", t)
- }
- }
-
- return nil
-}
-
-func configureTraits(options []string, catalog trait.Finder) (map[string]v1.TraitSpec, error) {
- traits := make(map[string]map[string]interface{})
-
- for _, option := range options {
- parts := traitConfigRegexp.FindStringSubmatch(option)
- if len(parts) < 4 {
- return nil, errors.New("unrecognized config format (expected \"<trait>.<prop>=<value>\"): " + option)
- }
- id := parts[1]
- fullProp := parts[2][1:]
- value := parts[3]
- if _, ok := traits[id]; !ok {
- traits[id] = make(map[string]interface{})
- }
-
- propParts := util.ConfigTreePropertySplit(fullProp)
- var current = traits[id]
- if len(propParts) > 1 {
- c, err := util.NavigateConfigTree(current, propParts[0:len(propParts)-1])
- if err != nil {
- return nil, err
- }
- if cc, ok := c.(map[string]interface{}); ok {
- current = cc
- } else {
- return nil, errors.New("trait configuration cannot end with a slice")
- }
- }
-
- prop := propParts[len(propParts)-1]
- switch v := current[prop].(type) {
- case []string:
- current[prop] = append(v, value)
- case string:
- // Aggregate multiple occurrences of the same option into a string array, to emulate POSIX conventions.
- // This enables executing:
- // $ kamel run -t <trait>.<property>=<value_1> ... -t <trait>.<property>=<value_N>
- // Or:
- // $ kamel run --trait <trait>.<property>=<value_1>,...,<trait>.<property>=<value_N>
- current[prop] = []string{v, value}
- case nil:
- current[prop] = value
- }
- }
-
- specs := make(map[string]v1.TraitSpec)
- for id, config := range traits {
- t := catalog.GetTrait(id)
- if t != nil {
- // let's take a clone to prevent default values set at runtime from being serialized
- zero := reflect.New(reflect.TypeOf(t)).Interface()
- err := configureTrait(config, zero)
- if err != nil {
- return nil, err
- }
- data, err := json.Marshal(zero)
- if err != nil {
- return nil, err
- }
- var spec v1.TraitSpec
- err = json.Unmarshal(data, &spec.Configuration)
- if err != nil {
- return nil, err
- }
- specs[id] = spec
- }
- }
-
- return specs, nil
-}
-
-func configureTrait(config map[string]interface{}, trait interface{}) error {
- md := mapstructure.Metadata{}
-
- decoder, err := mapstructure.NewDecoder(
- &mapstructure.DecoderConfig{
- Metadata: &md,
- WeaklyTypedInput: true,
- TagName: "property",
- Result: &trait,
- },
- )
- if err != nil {
- return err
- }
-
- return decoder.Decode(config)
-}
diff --git a/pkg/cmd/trait_support.go b/pkg/cmd/trait_support.go
new file mode 100644
index 000000000..63fa23b9c
--- /dev/null
+++ b/pkg/cmd/trait_support.go
@@ -0,0 +1,136 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cmd
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "reflect"
+ "strings"
+
+ v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+ "github.com/apache/camel-k/pkg/trait"
+ "github.com/apache/camel-k/pkg/util"
+ "github.com/mitchellh/mapstructure"
+)
+
+func validateTraits(catalog *trait.Catalog, traits []string) error {
+ tp := catalog.ComputeTraitsProperties()
+ for _, t := range traits {
+ kv := strings.SplitN(t, "=", 2)
+ prefix := kv[0]
+ if strings.Contains(prefix, "[") {
+ prefix = prefix[0:strings.Index(prefix, "[")]
+ }
+ if !util.StringSliceExists(tp, prefix) {
+ return fmt.Errorf("%s is not a valid trait property", t)
+ }
+ }
+
+ return nil
+}
+
+func configureTraits(options []string, catalog trait.Finder) (map[string]v1.TraitSpec, error) {
+ traits := make(map[string]map[string]interface{})
+
+ for _, option := range options {
+ parts := traitConfigRegexp.FindStringSubmatch(option)
+ if len(parts) < 4 {
+ return nil, errors.New("unrecognized config format (expected \"<trait>.<prop>=<value>\"): " + option)
+ }
+ id := parts[1]
+ fullProp := parts[2][1:]
+ value := parts[3]
+ if _, ok := traits[id]; !ok {
+ traits[id] = make(map[string]interface{})
+ }
+
+ propParts := util.ConfigTreePropertySplit(fullProp)
+ var current = traits[id]
+ if len(propParts) > 1 {
+ c, err := util.NavigateConfigTree(current, propParts[0:len(propParts)-1])
+ if err != nil {
+ return nil, err
+ }
+ if cc, ok := c.(map[string]interface{}); ok {
+ current = cc
+ } else {
+ return nil, errors.New("trait configuration cannot end with a slice")
+ }
+ }
+
+ prop := propParts[len(propParts)-1]
+ switch v := current[prop].(type) {
+ case []string:
+ current[prop] = append(v, value)
+ case string:
+ // Aggregate multiple occurrences of the same option into a string array, to emulate POSIX conventions.
+ // This enables executing:
+ // $ kamel run -t <trait>.<property>=<value_1> ... -t <trait>.<property>=<value_N>
+ // Or:
+ // $ kamel run --trait <trait>.<property>=<value_1>,...,<trait>.<property>=<value_N>
+ current[prop] = []string{v, value}
+ case nil:
+ current[prop] = value
+ }
+ }
+
+ specs := make(map[string]v1.TraitSpec)
+ for id, config := range traits {
+ t := catalog.GetTrait(id)
+ if t != nil {
+ // let's take a clone to prevent default values set at runtime from being serialized
+ zero := reflect.New(reflect.TypeOf(t)).Interface()
+ err := configureTrait(config, zero)
+ if err != nil {
+ return nil, err
+ }
+ data, err := json.Marshal(zero)
+ if err != nil {
+ return nil, err
+ }
+ var spec v1.TraitSpec
+ err = json.Unmarshal(data, &spec.Configuration)
+ if err != nil {
+ return nil, err
+ }
+ specs[id] = spec
+ }
+ }
+
+ return specs, nil
+}
+
+func configureTrait(config map[string]interface{}, trait interface{}) error {
+ md := mapstructure.Metadata{}
+
+ decoder, err := mapstructure.NewDecoder(
+ &mapstructure.DecoderConfig{
+ Metadata: &md,
+ WeaklyTypedInput: true,
+ TagName: "property",
+ Result: &trait,
+ },
+ )
+ if err != nil {
+ return err
+ }
+
+ return decoder.Decode(config)
+}
diff --git a/pkg/trait/trait_types_test.go b/pkg/trait/test_support.go
similarity index 88%
copy from pkg/trait/trait_types_test.go
copy to pkg/trait/test_support.go
index 2a68853ae..137f0f91d 100644
--- a/pkg/trait/trait_types_test.go
+++ b/pkg/trait/test_support.go
@@ -18,9 +18,6 @@ limitations under the License.
package trait
import (
- "testing"
-
- "github.com/stretchr/testify/assert"
serving "knative.dev/serving/pkg/apis/serving/v1"
appsv1 "k8s.io/api/apps/v1"
@@ -32,19 +29,6 @@ import (
"github.com/apache/camel-k/pkg/util/kubernetes"
)
-func TestMultilinePropertiesHandled(t *testing.T) {
- e := Environment{
- ApplicationProperties: map[string]string{
- "prop": "multi\nline",
- },
- Integration: &v1.Integration{},
- }
- cm, err := e.computeApplicationProperties()
- assert.NoError(t, err)
- assert.NotNil(t, cm)
- assert.Equal(t, "prop = multi\\nline\n", cm.Data["application.properties"])
-}
-
func createNominalDeploymentTraitTest() (*Environment, *appsv1.Deployment) {
deployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
diff --git a/pkg/trait/trait_types_test.go b/pkg/trait/trait_types_test.go
index 2a68853ae..9d3509de9 100644
--- a/pkg/trait/trait_types_test.go
+++ b/pkg/trait/trait_types_test.go
@@ -21,15 +21,8 @@ import (
"testing"
"github.com/stretchr/testify/assert"
- serving "knative.dev/serving/pkg/apis/serving/v1"
-
- appsv1 "k8s.io/api/apps/v1"
- "k8s.io/api/batch/v1beta1"
- corev1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
- "github.com/apache/camel-k/pkg/util/kubernetes"
)
func TestMultilinePropertiesHandled(t *testing.T) {
@@ -45,89 +38,79 @@ func TestMultilinePropertiesHandled(t *testing.T) {
assert.Equal(t, "prop = multi\\nline\n", cm.Data["application.properties"])
}
-func createNominalDeploymentTraitTest() (*Environment, *appsv1.Deployment) {
- deployment := &appsv1.Deployment{
- ObjectMeta: metav1.ObjectMeta{
- Name: "integration-name",
- },
- Spec: appsv1.DeploymentSpec{
- Template: corev1.PodTemplateSpec{},
- },
- }
-
- environment := &Environment{
+func TestCollectConfigurationValues(t *testing.T) {
+ e := Environment{
Integration: &v1.Integration{
- ObjectMeta: metav1.ObjectMeta{
- Name: "integration-name",
- },
- Status: v1.IntegrationStatus{
- Phase: v1.IntegrationPhaseDeploying,
+ Spec: v1.IntegrationSpec{
+ Configuration: []v1.ConfigurationSpec{
+ {Type: "configmap", Value: "my-cm-integration"},
+ {Type: "env", Value: "my-env-integration"},
+ },
},
},
- Resources: kubernetes.NewCollection(deployment),
- }
-
- return environment, deployment
-}
-
-func createNominalMissingDeploymentTraitTest() *Environment {
- environment := &Environment{
- Integration: &v1.Integration{
- ObjectMeta: metav1.ObjectMeta{
- Name: "integration-name",
+ IntegrationKit: &v1.IntegrationKit{
+ Spec: v1.IntegrationKitSpec{
+ Configuration: []v1.ConfigurationSpec{
+ {Type: "configmap", Value: "my-cm-kit"},
+ {Type: "property", Value: "my-p-kit"},
+ },
},
- Status: v1.IntegrationStatus{
- Phase: v1.IntegrationPhaseDeploying,
+ },
+ Platform: &v1.IntegrationPlatform{
+ Spec: v1.IntegrationPlatformSpec{
+ Configuration: []v1.ConfigurationSpec{
+ {Type: "configmap", Value: "my-cm-platform"},
+ {Type: "secret", Value: "my-secret-platform"},
+ {Type: "property", Value: "my-p-platform"},
+ {Type: "env", Value: "my-env-platform"},
+ },
},
},
- Resources: kubernetes.NewCollection(),
}
+ e.Platform.ResyncStatusFullConfig()
- return environment
+ assert.Contains(t, e.collectConfigurationValues("configmap"), "my-cm-integration")
+ assert.Contains(t, e.collectConfigurationValues("secret"), "my-secret-platform")
+ assert.Contains(t, e.collectConfigurationValues("property"), "my-p-kit")
+ assert.Contains(t, e.collectConfigurationValues("env"), "my-env-integration")
}
-func createNominalKnativeServiceTraitTest() (*Environment, *serving.Service) {
- knativeService := &serving.Service{
- ObjectMeta: metav1.ObjectMeta{
- Name: "integration-name",
- },
- Spec: serving.ServiceSpec{},
- }
-
- environment := &Environment{
+func TestCollectConfigurationPairs(t *testing.T) {
+ e := Environment{
Integration: &v1.Integration{
- ObjectMeta: metav1.ObjectMeta{
- Name: "integration-name",
- },
- Status: v1.IntegrationStatus{
- Phase: v1.IntegrationPhaseDeploying,
+ Spec: v1.IntegrationSpec{
+ Configuration: []v1.ConfigurationSpec{
+ {Type: "property", Value: "p1=integration"},
+ {Type: "property", Value: "p4=integration"},
+ },
},
},
- Resources: kubernetes.NewCollection(knativeService),
- }
-
- return environment, knativeService
-}
-
-func createNominalCronJobTraitTest() (*Environment, *v1beta1.CronJob) {
- cronJob := &v1beta1.CronJob{
- ObjectMeta: metav1.ObjectMeta{
- Name: "integration-name",
- },
- Spec: v1beta1.CronJobSpec{},
- }
-
- environment := &Environment{
- Integration: &v1.Integration{
- ObjectMeta: metav1.ObjectMeta{
- Name: "integration-name",
+ IntegrationKit: &v1.IntegrationKit{
+ Spec: v1.IntegrationKitSpec{
+ Configuration: []v1.ConfigurationSpec{
+ {Type: "property", Value: "p1=kit"},
+ {Type: "property", Value: "p2=kit"},
+ },
},
- Status: v1.IntegrationStatus{
- Phase: v1.IntegrationPhaseDeploying,
+ },
+ Platform: &v1.IntegrationPlatform{
+ Spec: v1.IntegrationPlatformSpec{
+ Configuration: []v1.ConfigurationSpec{
+ {Type: "property", Value: "p1=platform"},
+ {Type: "property", Value: "p2=platform"},
+ {Type: "property", Value: "p3=platform"},
+ {Type: "property", Value: "p4=platform"},
+ },
},
},
- Resources: kubernetes.NewCollection(cronJob),
}
-
- return environment, cronJob
+ e.Platform.ResyncStatusFullConfig()
+
+ pairs := e.collectConfigurationPairs("property")
+ assert.Equal(t, pairs, []variable{
+ {Name: "p1", Value: "integration"},
+ {Name: "p2", Value: "kit"},
+ {Name: "p3", Value: "platform"},
+ {Name: "p4", Value: "integration"},
+ })
}
diff --git a/pkg/trait/util_test.go b/pkg/trait/util_test.go
index d1488ad41..da4cd9589 100644
--- a/pkg/trait/util_test.go
+++ b/pkg/trait/util_test.go
@@ -21,87 +21,8 @@ import (
"testing"
"github.com/stretchr/testify/assert"
-
- v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
)
-func TestCollectConfigurationValues(t *testing.T) {
- e := Environment{
- Integration: &v1.Integration{
- Spec: v1.IntegrationSpec{
- Configuration: []v1.ConfigurationSpec{
- {Type: "configmap", Value: "my-cm-integration"},
- {Type: "env", Value: "my-env-integration"},
- },
- },
- },
- IntegrationKit: &v1.IntegrationKit{
- Spec: v1.IntegrationKitSpec{
- Configuration: []v1.ConfigurationSpec{
- {Type: "configmap", Value: "my-cm-kit"},
- {Type: "property", Value: "my-p-kit"},
- },
- },
- },
- Platform: &v1.IntegrationPlatform{
- Spec: v1.IntegrationPlatformSpec{
- Configuration: []v1.ConfigurationSpec{
- {Type: "configmap", Value: "my-cm-platform"},
- {Type: "secret", Value: "my-secret-platform"},
- {Type: "property", Value: "my-p-platform"},
- {Type: "env", Value: "my-env-platform"},
- },
- },
- },
- }
- e.Platform.ResyncStatusFullConfig()
-
- assert.Contains(t, e.collectConfigurationValues("configmap"), "my-cm-integration")
- assert.Contains(t, e.collectConfigurationValues("secret"), "my-secret-platform")
- assert.Contains(t, e.collectConfigurationValues("property"), "my-p-kit")
- assert.Contains(t, e.collectConfigurationValues("env"), "my-env-integration")
-}
-
-func TestCollectConfigurationPairs(t *testing.T) {
- e := Environment{
- Integration: &v1.Integration{
- Spec: v1.IntegrationSpec{
- Configuration: []v1.ConfigurationSpec{
- {Type: "property", Value: "p1=integration"},
- {Type: "property", Value: "p4=integration"},
- },
- },
- },
- IntegrationKit: &v1.IntegrationKit{
- Spec: v1.IntegrationKitSpec{
- Configuration: []v1.ConfigurationSpec{
- {Type: "property", Value: "p1=kit"},
- {Type: "property", Value: "p2=kit"},
- },
- },
- },
- Platform: &v1.IntegrationPlatform{
- Spec: v1.IntegrationPlatformSpec{
- Configuration: []v1.ConfigurationSpec{
- {Type: "property", Value: "p1=platform"},
- {Type: "property", Value: "p2=platform"},
- {Type: "property", Value: "p3=platform"},
- {Type: "property", Value: "p4=platform"},
- },
- },
- },
- }
- e.Platform.ResyncStatusFullConfig()
-
- pairs := e.collectConfigurationPairs("property")
- assert.Equal(t, pairs, []variable{
- {Name: "p1", Value: "integration"},
- {Name: "p2", Value: "kit"},
- {Name: "p3", Value: "platform"},
- {Name: "p4", Value: "integration"},
- })
-}
-
func TestBoolPointerFunctions(t *testing.T) {
trueP := BoolP(true)
falseP := BoolP(false)