You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by lb...@apache.org on 2019/03/21 05:32:08 UTC

[camel-k] branch master updated: Add support for traits defaults #566

This is an automated email from the ASF dual-hosted git repository.

lburgazzoli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git


The following commit(s) were added to refs/heads/master by this push:
     new eb4aecb  Add support for traits defaults #566
eb4aecb is described below

commit eb4aecb11e343a4fa983df51228756d3ac14fcb9
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Wed Mar 20 15:55:35 2019 +0100

    Add support for traits defaults #566
---
 pkg/apis/camel/v1alpha1/common_types.go            |  5 ++
 pkg/apis/camel/v1alpha1/common_types_support.go    | 21 ++++++
 pkg/apis/camel/v1alpha1/integration_types.go       | 25 +++-----
 .../camel/v1alpha1/integration_types_support.go    | 21 ------
 .../camel/v1alpha1/integrationcontext_types.go     | 12 ++--
 .../camel/v1alpha1/integrationplatform_types.go    |  1 +
 pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go   | 75 ++++++++++++++--------
 pkg/apis/camel/v1alpha1/zz_generated.defaults.go   | 32 +++++++++
 pkg/cmd/context_create.go                          |  4 +-
 pkg/cmd/run.go                                     |  4 +-
 pkg/trait/debug_test.go                            |  4 +-
 pkg/trait/environment_test.go                      |  4 +-
 pkg/trait/knative_service_vol_test.go              |  4 +-
 pkg/trait/owner_test.go                            |  2 +-
 pkg/trait/trait_catalog.go                         | 34 ++++++----
 pkg/trait/trait_test.go                            | 65 +++++++++++++++++--
 16 files changed, 216 insertions(+), 97 deletions(-)

diff --git a/pkg/apis/camel/v1alpha1/common_types.go b/pkg/apis/camel/v1alpha1/common_types.go
index be2fdf0..57bdac7 100644
--- a/pkg/apis/camel/v1alpha1/common_types.go
+++ b/pkg/apis/camel/v1alpha1/common_types.go
@@ -61,3 +61,8 @@ type FailureRecovery struct {
 	AttemptMax  int         `json:"attemptMax"`
 	AttemptTime metav1.Time `json:"attemptTime"`
 }
+
+// A TraitSpec contains the configuration of a trait
+type TraitSpec struct {
+	Configuration map[string]string `json:"configuration,omitempty"`
+}
diff --git a/pkg/apis/camel/v1alpha1/common_types_support.go b/pkg/apis/camel/v1alpha1/common_types_support.go
index 364150e..29c6ef1 100644
--- a/pkg/apis/camel/v1alpha1/common_types_support.go
+++ b/pkg/apis/camel/v1alpha1/common_types_support.go
@@ -20,6 +20,7 @@ package v1alpha1
 import (
 	"fmt"
 
+	"github.com/mitchellh/mapstructure"
 	yaml2 "gopkg.in/yaml.v2"
 )
 
@@ -39,3 +40,23 @@ func (flows Flows) Serialize() (string, error) {
 	}
 	return string(res), nil
 }
+
+// Decode the trait configuration to a type safe struct
+func (in *TraitSpec) Decode(target interface{}) error {
+	md := mapstructure.Metadata{}
+
+	decoder, err := mapstructure.NewDecoder(
+		&mapstructure.DecoderConfig{
+			Metadata:         &md,
+			WeaklyTypedInput: true,
+			TagName:          "property",
+			Result:           &target,
+		},
+	)
+
+	if err != nil {
+		return err
+	}
+
+	return decoder.Decode(in.Configuration)
+}
diff --git a/pkg/apis/camel/v1alpha1/integration_types.go b/pkg/apis/camel/v1alpha1/integration_types.go
index 69c29e0..5c515d8 100644
--- a/pkg/apis/camel/v1alpha1/integration_types.go
+++ b/pkg/apis/camel/v1alpha1/integration_types.go
@@ -8,16 +8,16 @@ import (
 
 // IntegrationSpec defines the desired state of Integration
 type IntegrationSpec struct {
-	Replicas           *int32                          `json:"replicas,omitempty"`
-	Sources            []SourceSpec                    `json:"sources,omitempty"`
-	Resources          []ResourceSpec                  `json:"resources,omitempty"`
-	Context            string                          `json:"context,omitempty"`
-	Dependencies       []string                        `json:"dependencies,omitempty"`
-	Profile            TraitProfile                    `json:"profile,omitempty"`
-	Traits             map[string]IntegrationTraitSpec `json:"traits,omitempty"`
-	Configuration      []ConfigurationSpec             `json:"configuration,omitempty"`
-	Repositories       []string                        `json:"repositories,omitempty"`
-	ServiceAccountName string                          `json:"serviceAccountName,omitempty"`
+	Replicas           *int32               `json:"replicas,omitempty"`
+	Sources            []SourceSpec         `json:"sources,omitempty"`
+	Resources          []ResourceSpec       `json:"resources,omitempty"`
+	Context            string               `json:"context,omitempty"`
+	Dependencies       []string             `json:"dependencies,omitempty"`
+	Profile            TraitProfile         `json:"profile,omitempty"`
+	Traits             map[string]TraitSpec `json:"traits,omitempty"`
+	Configuration      []ConfigurationSpec  `json:"configuration,omitempty"`
+	Repositories       []string             `json:"repositories,omitempty"`
+	ServiceAccountName string               `json:"serviceAccountName,omitempty"`
 }
 
 // IntegrationStatus defines the observed state of Integration
@@ -117,11 +117,6 @@ var Languages = []Language{
 	LanguageYamlFlow,
 }
 
-// A IntegrationTraitSpec contains the configuration of a trait
-type IntegrationTraitSpec struct {
-	Configuration map[string]string `json:"configuration,omitempty"`
-}
-
 // IntegrationPhase --
 type IntegrationPhase string
 
diff --git a/pkg/apis/camel/v1alpha1/integration_types_support.go b/pkg/apis/camel/v1alpha1/integration_types_support.go
index 07e0e50..531cf88 100644
--- a/pkg/apis/camel/v1alpha1/integration_types_support.go
+++ b/pkg/apis/camel/v1alpha1/integration_types_support.go
@@ -21,7 +21,6 @@ import (
 	"strings"
 
 	"github.com/apache/camel-k/pkg/util"
-	"github.com/mitchellh/mapstructure"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
@@ -91,26 +90,6 @@ func (is *IntegrationSpec) AddDependency(dependency string) {
 	}
 }
 
-// Decode the trait configuration to a type safe struct
-func (in *IntegrationTraitSpec) Decode(target interface{}) error {
-	md := mapstructure.Metadata{}
-
-	decoder, err := mapstructure.NewDecoder(
-		&mapstructure.DecoderConfig{
-			Metadata:         &md,
-			WeaklyTypedInput: true,
-			TagName:          "property",
-			Result:           &target,
-		},
-	)
-
-	if err != nil {
-		return err
-	}
-
-	return decoder.Decode(in.Configuration)
-}
-
 // NewSourceSpec --
 func NewSourceSpec(name string, content string, language Language) SourceSpec {
 	return SourceSpec{
diff --git a/pkg/apis/camel/v1alpha1/integrationcontext_types.go b/pkg/apis/camel/v1alpha1/integrationcontext_types.go
index c0b733c..e6a567b 100644
--- a/pkg/apis/camel/v1alpha1/integrationcontext_types.go
+++ b/pkg/apis/camel/v1alpha1/integrationcontext_types.go
@@ -8,12 +8,12 @@ import (
 
 // IntegrationContextSpec defines the desired state of IntegrationContext
 type IntegrationContextSpec struct {
-	Image         string                          `json:"image,omitempty"`
-	Dependencies  []string                        `json:"dependencies,omitempty"`
-	Profile       TraitProfile                    `json:"profile,omitempty"`
-	Traits        map[string]IntegrationTraitSpec `json:"traits,omitempty"`
-	Configuration []ConfigurationSpec             `json:"configuration,omitempty"`
-	Repositories  []string                        `json:"repositories,omitempty"`
+	Image         string               `json:"image,omitempty"`
+	Dependencies  []string             `json:"dependencies,omitempty"`
+	Profile       TraitProfile         `json:"profile,omitempty"`
+	Traits        map[string]TraitSpec `json:"traits,omitempty"`
+	Configuration []ConfigurationSpec  `json:"configuration,omitempty"`
+	Repositories  []string             `json:"repositories,omitempty"`
 }
 
 // IntegrationContextStatus defines the observed state of IntegrationContext
diff --git a/pkg/apis/camel/v1alpha1/integrationplatform_types.go b/pkg/apis/camel/v1alpha1/integrationplatform_types.go
index 134017f..a0fd232 100644
--- a/pkg/apis/camel/v1alpha1/integrationplatform_types.go
+++ b/pkg/apis/camel/v1alpha1/integrationplatform_types.go
@@ -12,6 +12,7 @@ type IntegrationPlatformSpec struct {
 	Profile   TraitProfile                     `json:"profile,omitempty"`
 	Build     IntegrationPlatformBuildSpec     `json:"build,omitempty"`
 	Resources IntegrationPlatformResourcesSpec `json:"resources,omitempty"`
+	Traits    map[string]TraitSpec             `json:"traits,omitempty"`
 }
 
 // IntegrationPlatformResourcesSpec contains platform related resources
diff --git a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
index e9a0f03..ff43010 100644
--- a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
@@ -441,7 +441,7 @@ func (in *IntegrationContextSpec) DeepCopyInto(out *IntegrationContextSpec) {
 	}
 	if in.Traits != nil {
 		in, out := &in.Traits, &out.Traits
-		*out = make(map[string]IntegrationTraitSpec, len(*in))
+		*out = make(map[string]TraitSpec, len(*in))
 		for key, val := range *in {
 			(*out)[key] = *val.DeepCopy()
 		}
@@ -571,6 +571,8 @@ func (in *IntegrationPlatformBuildSpec) DeepCopyInto(out *IntegrationPlatformBui
 		*out = make([]string, len(*in))
 		copy(*out, *in)
 	}
+	out.Registry = in.Registry
+	out.Timeout = in.Timeout
 	return
 }
 
@@ -618,6 +620,22 @@ func (in *IntegrationPlatformList) DeepCopyObject() runtime.Object {
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *IntegrationPlatformRegistrySpec) DeepCopyInto(out *IntegrationPlatformRegistrySpec) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntegrationPlatformRegistrySpec.
+func (in *IntegrationPlatformRegistrySpec) DeepCopy() *IntegrationPlatformRegistrySpec {
+	if in == nil {
+		return nil
+	}
+	out := new(IntegrationPlatformRegistrySpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *IntegrationPlatformResourcesSpec) DeepCopyInto(out *IntegrationPlatformResourcesSpec) {
 	*out = *in
 	if in.Contexts != nil {
@@ -643,6 +661,13 @@ func (in *IntegrationPlatformSpec) DeepCopyInto(out *IntegrationPlatformSpec) {
 	*out = *in
 	in.Build.DeepCopyInto(&out.Build)
 	in.Resources.DeepCopyInto(&out.Resources)
+	if in.Traits != nil {
+		in, out := &in.Traits, &out.Traits
+		*out = make(map[string]TraitSpec, len(*in))
+		for key, val := range *in {
+			(*out)[key] = *val.DeepCopy()
+		}
+	}
 	return
 }
 
@@ -697,7 +722,7 @@ func (in *IntegrationSpec) DeepCopyInto(out *IntegrationSpec) {
 	}
 	if in.Traits != nil {
 		in, out := &in.Traits, &out.Traits
-		*out = make(map[string]IntegrationTraitSpec, len(*in))
+		*out = make(map[string]TraitSpec, len(*in))
 		for key, val := range *in {
 			(*out)[key] = *val.DeepCopy()
 		}
@@ -757,29 +782,6 @@ func (in *IntegrationStatus) DeepCopy() *IntegrationStatus {
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *IntegrationTraitSpec) DeepCopyInto(out *IntegrationTraitSpec) {
-	*out = *in
-	if in.Configuration != nil {
-		in, out := &in.Configuration, &out.Configuration
-		*out = make(map[string]string, len(*in))
-		for key, val := range *in {
-			(*out)[key] = val
-		}
-	}
-	return
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntegrationTraitSpec.
-func (in *IntegrationTraitSpec) DeepCopy() *IntegrationTraitSpec {
-	if in == nil {
-		return nil
-	}
-	out := new(IntegrationTraitSpec)
-	in.DeepCopyInto(out)
-	return out
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ResourceSpec) DeepCopyInto(out *ResourceSpec) {
 	*out = *in
 	out.DataSpec = in.DataSpec
@@ -828,3 +830,26 @@ func (in *Step) DeepCopy() *Step {
 	in.DeepCopyInto(out)
 	return out
 }
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *TraitSpec) DeepCopyInto(out *TraitSpec) {
+	*out = *in
+	if in.Configuration != nil {
+		in, out := &in.Configuration, &out.Configuration
+		*out = make(map[string]string, len(*in))
+		for key, val := range *in {
+			(*out)[key] = val
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TraitSpec.
+func (in *TraitSpec) DeepCopy() *TraitSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(TraitSpec)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/pkg/apis/camel/v1alpha1/zz_generated.defaults.go b/pkg/apis/camel/v1alpha1/zz_generated.defaults.go
new file mode 100644
index 0000000..dd621a3
--- /dev/null
+++ b/pkg/apis/camel/v1alpha1/zz_generated.defaults.go
@@ -0,0 +1,32 @@
+// +build !ignore_autogenerated
+
+/*
+Copyright The Kubernetes Authors.
+
+Licensed 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.
+*/
+
+// Code generated by defaulter-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	runtime "k8s.io/apimachinery/pkg/runtime"
+)
+
+// RegisterDefaults adds defaulters functions to the given scheme.
+// Public to allow building arbitrary schemes.
+// All generated defaulters are covering - they call all nested defaulters.
+func RegisterDefaults(scheme *runtime.Scheme) error {
+	return nil
+}
diff --git a/pkg/cmd/context_create.go b/pkg/cmd/context_create.go
index bf9a440..059497b 100644
--- a/pkg/cmd/context_create.go
+++ b/pkg/cmd/context_create.go
@@ -212,7 +212,7 @@ func (command *contextCreateCommand) run(_ *cobra.Command, args []string) error
 
 func (*contextCreateCommand) configureTrait(ctx *v1alpha1.IntegrationContext, config string) error {
 	if ctx.Spec.Traits == nil {
-		ctx.Spec.Traits = make(map[string]v1alpha1.IntegrationTraitSpec)
+		ctx.Spec.Traits = make(map[string]v1alpha1.TraitSpec)
 	}
 
 	parts := traitConfigRegexp.FindStringSubmatch(config)
@@ -225,7 +225,7 @@ func (*contextCreateCommand) configureTrait(ctx *v1alpha1.IntegrationContext, co
 
 	spec, ok := ctx.Spec.Traits[traitID]
 	if !ok {
-		spec = v1alpha1.IntegrationTraitSpec{
+		spec = v1alpha1.TraitSpec{
 			Configuration: make(map[string]string),
 		}
 	}
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 9d72789..5e807cd 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -466,7 +466,7 @@ func (*runCmdOptions) loadData(fileName string, compress bool) (string, error) {
 
 func (*runCmdOptions) configureTrait(integration *v1alpha1.Integration, config string) error {
 	if integration.Spec.Traits == nil {
-		integration.Spec.Traits = make(map[string]v1alpha1.IntegrationTraitSpec)
+		integration.Spec.Traits = make(map[string]v1alpha1.TraitSpec)
 	}
 
 	parts := traitConfigRegexp.FindStringSubmatch(config)
@@ -479,7 +479,7 @@ func (*runCmdOptions) configureTrait(integration *v1alpha1.Integration, config s
 
 	spec, ok := integration.Spec.Traits[traitID]
 	if !ok {
-		spec = v1alpha1.IntegrationTraitSpec{
+		spec = v1alpha1.TraitSpec{
 			Configuration: make(map[string]string),
 		}
 	}
diff --git a/pkg/trait/debug_test.go b/pkg/trait/debug_test.go
index 8e4e2de..b01fdd0 100644
--- a/pkg/trait/debug_test.go
+++ b/pkg/trait/debug_test.go
@@ -40,7 +40,7 @@ func TestDebugTraitApplicability(t *testing.T) {
 				Phase: v1alpha1.IntegrationPhaseDeploying,
 			},
 			Spec: v1alpha1.IntegrationSpec{
-				Traits: map[string]v1alpha1.IntegrationTraitSpec{
+				Traits: map[string]v1alpha1.TraitSpec{
 					"debug": {
 						Configuration: map[string]string{
 							"enabled": "true",
@@ -72,7 +72,7 @@ func TestApplyDebugTrait(t *testing.T) {
 				Phase: v1alpha1.IntegrationPhaseDeploying,
 			},
 			Spec: v1alpha1.IntegrationSpec{
-				Traits: map[string]v1alpha1.IntegrationTraitSpec{
+				Traits: map[string]v1alpha1.TraitSpec{
 					"debug": {
 						Configuration: map[string]string{
 							"enabled": "true",
diff --git a/pkg/trait/environment_test.go b/pkg/trait/environment_test.go
index c210105..e6a1bde 100644
--- a/pkg/trait/environment_test.go
+++ b/pkg/trait/environment_test.go
@@ -104,7 +104,7 @@ func TestEnabledContainerMetaDataEnvVars(t *testing.T) {
 			},
 			Spec: v1alpha1.IntegrationSpec{
 				Profile: v1alpha1.TraitProfileOpenShift,
-				Traits: map[string]v1alpha1.IntegrationTraitSpec{
+				Traits: map[string]v1alpha1.TraitSpec{
 					"environment": {
 						Configuration: map[string]string{
 							"container-meta": "true",
@@ -169,7 +169,7 @@ func TestDisabledContainerMetaDataEnvVars(t *testing.T) {
 			},
 			Spec: v1alpha1.IntegrationSpec{
 				Profile: v1alpha1.TraitProfileOpenShift,
-				Traits: map[string]v1alpha1.IntegrationTraitSpec{
+				Traits: map[string]v1alpha1.TraitSpec{
 					"environment": {
 						Configuration: map[string]string{
 							"container-meta": "false",
diff --git a/pkg/trait/knative_service_vol_test.go b/pkg/trait/knative_service_vol_test.go
index 87c8600..86ff6c1 100644
--- a/pkg/trait/knative_service_vol_test.go
+++ b/pkg/trait/knative_service_vol_test.go
@@ -79,7 +79,7 @@ func TestKnativeWithVolumeBinding(t *testing.T) {
 					{Type: "secret", Value: "my-secret"},
 					{Type: "property", Value: "my-property=my-property-value"},
 				},
-				Traits: map[string]v1alpha1.IntegrationTraitSpec{
+				Traits: map[string]v1alpha1.TraitSpec{
 					"knative-service": {
 						Configuration: map[string]string{
 							"configuration-type": "volume",
@@ -217,7 +217,7 @@ func TestKnativeWithVolumeBindingAndContainerImage(t *testing.T) {
 					{Type: "configmap", Value: "my-cm"},
 					{Type: "secret", Value: "my-secret"},
 				},
-				Traits: map[string]v1alpha1.IntegrationTraitSpec{
+				Traits: map[string]v1alpha1.TraitSpec{
 					"deployer": {
 						Configuration: map[string]string{
 							"container-image": "true",
diff --git a/pkg/trait/owner_test.go b/pkg/trait/owner_test.go
index 2c08f0e..16410b4 100644
--- a/pkg/trait/owner_test.go
+++ b/pkg/trait/owner_test.go
@@ -54,7 +54,7 @@ func TestOwnerWithoutFinalizer(t *testing.T) {
 
 func SetUpOwnerEnvironment(t *testing.T) *Environment {
 	env := createTestEnv(t, v1alpha1.IntegrationPlatformClusterOpenShift, "camel:core")
-	env.Integration.Spec.Traits = map[string]v1alpha1.IntegrationTraitSpec{
+	env.Integration.Spec.Traits = map[string]v1alpha1.TraitSpec{
 		"owner": {
 			Configuration: map[string]string{
 				"target-labels":      "com.mycompany/mylabel1",
diff --git a/pkg/trait/trait_catalog.go b/pkg/trait/trait_catalog.go
index 757af57..3935c86 100644
--- a/pkg/trait/trait_catalog.go
+++ b/pkg/trait/trait_catalog.go
@@ -243,23 +243,31 @@ func (c *Catalog) GetTrait(id string) Trait {
 }
 
 func (c *Catalog) configure(env *Environment) error {
+	if env.Platform != nil && env.Platform.Spec.Traits != nil {
+		if err := c.configureTraits(env.Platform.Spec.Traits); err != nil {
+			return err
+		}
+	}
 	if env.IntegrationContext != nil && env.IntegrationContext.Spec.Traits != nil {
-		for id, traitSpec := range env.IntegrationContext.Spec.Traits {
-			catTrait := c.GetTrait(id)
-			if catTrait != nil {
-				if err := traitSpec.Decode(catTrait); err != nil {
-					return err
-				}
-			}
+		if err := c.configureTraits(env.IntegrationContext.Spec.Traits); err != nil {
+			return err
 		}
 	}
 	if env.Integration != nil && env.Integration.Spec.Traits != nil {
-		for id, traitSpec := range env.Integration.Spec.Traits {
-			catTrait := c.GetTrait(id)
-			if catTrait != nil {
-				if err := traitSpec.Decode(catTrait); err != nil {
-					return err
-				}
+		if err := c.configureTraits(env.Integration.Spec.Traits); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (c *Catalog) configureTraits(traits map[string]v1alpha1.TraitSpec) error {
+	for id, traitSpec := range traits {
+		catTrait := c.GetTrait(id)
+		if catTrait != nil {
+			if err := traitSpec.Decode(catTrait); err != nil {
+				return err
 			}
 		}
 	}
diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go
index ed02a94..bae8930 100644
--- a/pkg/trait/trait_test.go
+++ b/pkg/trait/trait_test.go
@@ -80,8 +80,8 @@ func TestOpenShiftTraitsWithWeb(t *testing.T) {
 
 func TestOpenShiftTraitsWithWebAndConfig(t *testing.T) {
 	env := createTestEnv(t, v1alpha1.IntegrationPlatformClusterOpenShift, "from('undertow:http').to('log:info')")
-	env.Integration.Spec.Traits = make(map[string]v1alpha1.IntegrationTraitSpec)
-	env.Integration.Spec.Traits["service"] = v1alpha1.IntegrationTraitSpec{
+	env.Integration.Spec.Traits = make(map[string]v1alpha1.TraitSpec)
+	env.Integration.Spec.Traits["service"] = v1alpha1.TraitSpec{
 		Configuration: map[string]string{
 			"port": "7071",
 		},
@@ -96,8 +96,8 @@ func TestOpenShiftTraitsWithWebAndConfig(t *testing.T) {
 
 func TestOpenShiftTraitsWithWebAndDisabledTrait(t *testing.T) {
 	env := createTestEnv(t, v1alpha1.IntegrationPlatformClusterOpenShift, "from('undertow:http').to('log:info')")
-	env.Integration.Spec.Traits = make(map[string]v1alpha1.IntegrationTraitSpec)
-	env.Integration.Spec.Traits["service"] = v1alpha1.IntegrationTraitSpec{
+	env.Integration.Spec.Traits = make(map[string]v1alpha1.TraitSpec)
+	env.Integration.Spec.Traits["service"] = v1alpha1.TraitSpec{
 		Configuration: map[string]string{
 			"enabled": "false",
 			"port":    "7071",
@@ -146,8 +146,8 @@ func TestKubernetesTraitsWithWeb(t *testing.T) {
 
 func TestTraitDecode(t *testing.T) {
 	env := createTestEnv(t, v1alpha1.IntegrationPlatformClusterOpenShift, "")
-	env.Integration.Spec.Traits = make(map[string]v1alpha1.IntegrationTraitSpec)
-	svcTrait := v1alpha1.IntegrationTraitSpec{
+	env.Integration.Spec.Traits = make(map[string]v1alpha1.TraitSpec)
+	svcTrait := v1alpha1.TraitSpec{
 		Configuration: map[string]string{
 			"enabled": "false",
 			"port":    "7071",
@@ -165,6 +165,59 @@ func TestTraitDecode(t *testing.T) {
 	assert.Equal(t, false, *svc.Enabled)
 }
 
+func TestTraitHierarchyDecode(t *testing.T) {
+	env := createTestEnv(t, v1alpha1.IntegrationPlatformClusterOpenShift, "")
+
+	env.Platform.Spec.Traits = make(map[string]v1alpha1.TraitSpec)
+	env.Platform.Spec.Traits["knative-service"] = v1alpha1.TraitSpec{
+		Configuration: map[string]string{
+			"enabled":            "false",
+			"min-scale":          "1",
+			"max-scale":          "10",
+			"autoscaling-target": "15",
+		},
+	}
+
+	env.IntegrationContext.Spec.Traits = make(map[string]v1alpha1.TraitSpec)
+	env.IntegrationContext.Spec.Traits["knative-service"] = v1alpha1.TraitSpec{
+		Configuration: map[string]string{
+			"enabled":   "true",
+			"min-scale": "5",
+		},
+	}
+
+	env.Integration.Spec.Traits = make(map[string]v1alpha1.TraitSpec)
+	env.Integration.Spec.Traits["knative-service"] = v1alpha1.TraitSpec{
+		Configuration: map[string]string{
+			"max-scale": "20",
+		},
+	}
+
+	c := NewTraitTestCatalog()
+	err := c.configure(env)
+
+	assert.Nil(t, err)
+
+	knt := c.GetTrait("knative-service")
+	assert.NotNil(t, knt)
+
+	kns, ok := knt.(*knativeServiceTrait)
+	assert.True(t, ok)
+	assert.NotNil(t, kns)
+
+	assert.NotNil(t, kns.Enabled)
+	assert.True(t, *kns.Enabled)
+
+	assert.NotNil(t, kns.MinScale)
+	assert.Equal(t, 5, *kns.MinScale)
+
+	assert.NotNil(t, kns.MaxScale)
+	assert.Equal(t, 20, *kns.MaxScale)
+
+	assert.NotNil(t, kns.Target)
+	assert.Equal(t, 15, *kns.Target)
+}
+
 func TestConfigureVolumesAndMounts(t *testing.T) {
 	env := Environment{
 		Integration: &v1alpha1.Integration{