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
commit 9ad69746c80092d6c767acbf9889fa81d8247201 Author: lburgazzoli <lburgazz...@gmail.com> AuthorDate: Sat Dec 8 00:54:25 2018 +0100 Refactor traits lifecycle #268 --- pkg/trait/builder.go | 29 +++++++++++++----------- pkg/trait/builder_test.go | 10 ++++----- pkg/trait/catalog.go | 19 +++++++--------- pkg/trait/debug.go | 20 ++++++++--------- pkg/trait/debug_test.go | 49 +++++++++++++++++++++++++++++++++-------- pkg/trait/dependencies.go | 14 ++++++++---- pkg/trait/deployment.go | 48 ++++++++++++++++++++++------------------ pkg/trait/ingress.go | 55 +++++++++++++++++++++++++++++----------------- pkg/trait/istio.go | 16 +++++++++----- pkg/trait/knative.go | 50 +++++++++++++++++++++++++----------------- pkg/trait/knative_test.go | 4 ++-- pkg/trait/owner.go | 14 ++++++++---- pkg/trait/route.go | 48 ++++++++++++++++++++++++---------------- pkg/trait/service.go | 39 ++++++++++++++++++++------------- pkg/trait/springboot.go | 35 ++++++++++++----------------- pkg/trait/trait.go | 3 ++- pkg/trait/trait_test.go | 46 +++++++++++++++++++------------------- pkg/trait/types.go | 56 +++++++++++++++-------------------------------- 18 files changed, 314 insertions(+), 241 deletions(-) diff --git a/pkg/trait/builder.go b/pkg/trait/builder.go index 2132a1b..e495510 100644 --- a/pkg/trait/builder.go +++ b/pkg/trait/builder.go @@ -32,25 +32,30 @@ type builderTrait struct { func newBuilderTrait() *builderTrait { return &builderTrait{ - BaseTrait: newBaseTrait("builder"), + BaseTrait: BaseTrait{ + id: ID("builder"), + }, } } -func (*builderTrait) appliesTo(e *Environment) bool { - if e.Context != nil && e.Context.Status.Phase == v1alpha1.IntegrationContextPhaseBuilding { - return true +func (t *builderTrait) Configure(e *Environment) (bool, error) { + if t.Enabled != nil && !*t.Enabled { + return false, nil } - if e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseBuildingImage && - e.Context != nil && e.Context.Status.Phase == v1alpha1.IntegrationContextPhaseReady { - return true + if e.IntegrationContextInPhase(v1alpha1.IntegrationContextPhaseBuilding) { + return true, nil } - return false + if e.InPhase(v1alpha1.IntegrationContextPhaseReady, v1alpha1.IntegrationPhaseBuildingImage) { + return true, nil + } + + return false, nil } -func (*builderTrait) apply(e *Environment) error { - if e.Context != nil && e.Context.Status.Phase == v1alpha1.IntegrationContextPhaseBuilding { +func (t *builderTrait) Apply(e *Environment) error { + if e.IntegrationContextInPhase(v1alpha1.IntegrationContextPhaseBuilding) { if platform.SupportsS2iPublishStrategy(e.Platform) { e.Steps = s2i.DefaultSteps } else if platform.SupportsKanikoPublishStrategy(e.Platform) { @@ -59,9 +64,7 @@ func (*builderTrait) apply(e *Environment) error { } } - if e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseBuildingImage && - e.Context != nil && e.Context.Status.Phase == v1alpha1.IntegrationContextPhaseReady { - + if e.InPhase(v1alpha1.IntegrationContextPhaseReady, v1alpha1.IntegrationPhaseBuildingImage) { if platform.SupportsS2iPublishStrategy(e.Platform) { e.Steps = []builder.Step{ builder.NewStep("packager", builder.ApplicationPackagePhase, builder.StandardPackager), diff --git a/pkg/trait/builder_test.go b/pkg/trait/builder_test.go index cf6c064..c4bb0ee 100644 --- a/pkg/trait/builder_test.go +++ b/pkg/trait/builder_test.go @@ -44,7 +44,7 @@ func TestBuilderTraitNotAppliedBecauseOfNilContext(t *testing.T) { assert.Nil(t, err) assert.NotEmpty(t, e.ExecutedTraits) - assert.NotContains(t, e.ExecutedTraits, ID("builder")) + assert.Nil(t, e.GetTrait(ID("builder"))) assert.Empty(t, e.Steps) }) } @@ -65,7 +65,7 @@ func TestBuilderTraitNotAppliedBecauseOfNilPhase(t *testing.T) { assert.Nil(t, err) assert.NotEmpty(t, e.ExecutedTraits) - assert.NotContains(t, e.ExecutedTraits, ID("builder")) + assert.Nil(t, e.GetTrait(ID("builder"))) assert.Empty(t, e.Steps) }) } @@ -77,7 +77,7 @@ func TestS2IBuilderTrait(t *testing.T) { assert.Nil(t, err) assert.NotEmpty(t, env.ExecutedTraits) - assert.Contains(t, env.ExecutedTraits, ID("builder")) + assert.NotNil(t, env.GetTrait(ID("builder"))) assert.NotEmpty(t, env.Steps) assert.Len(t, env.Steps, 5) assert.Condition(t, func() bool { @@ -97,7 +97,7 @@ func TestKanikoBuilderTrait(t *testing.T) { assert.Nil(t, err) assert.NotEmpty(t, env.ExecutedTraits) - assert.Contains(t, env.ExecutedTraits, ID("builder")) + assert.NotNil(t, env.GetTrait(ID("builder"))) assert.NotEmpty(t, env.Steps) assert.Len(t, env.Steps, 5) assert.Condition(t, func() bool { @@ -137,7 +137,7 @@ func createBuilderTestEnv(cluster v1alpha1.IntegrationPlatformCluster, strategy }, }, EnvVars: make(map[string]string), - ExecutedTraits: make([]ID, 0), + ExecutedTraits: make([]Trait, 0), Resources: kubernetes.NewCollection(), } } diff --git a/pkg/trait/catalog.go b/pkg/trait/catalog.go index 39ebdfd..ef0b9b3 100644 --- a/pkg/trait/catalog.go +++ b/pkg/trait/catalog.go @@ -122,23 +122,20 @@ func (c *Catalog) apply(environment *Environment) error { traits := c.traitsFor(environment) for _, trait := range traits { - if !trait.appliesTo(environment) { - continue + enabled, err := trait.Configure(environment) + if err != nil { + return err } - if trait.IsAuto() { - if err := trait.autoconfigure(environment); err != nil { - return err - } - } + if enabled { + logrus.Infof("Apply trait: %s", trait.ID()) - if trait.IsEnabled() { - logrus.Infof("apply trait: %s", trait.ID()) - if err := trait.apply(environment); err != nil { + err = trait.Apply(environment) + if err != nil { return err } - environment.ExecutedTraits = append(environment.ExecutedTraits, trait.ID()) + environment.ExecutedTraits = append(environment.ExecutedTraits, trait) } } diff --git a/pkg/trait/debug.go b/pkg/trait/debug.go index e5e40d9..b78bcc6 100644 --- a/pkg/trait/debug.go +++ b/pkg/trait/debug.go @@ -27,23 +27,21 @@ type debugTrait struct { func newDebugTrait() *debugTrait { return &debugTrait{ - BaseTrait: newBaseTrait("debug"), + BaseTrait: BaseTrait{ + id: ID("debug"), + }, } } -func (r *debugTrait) appliesTo(e *Environment) bool { - return e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying -} - -func (r *debugTrait) autoconfigure(e *Environment) error { - if r.Enabled == nil { - enabled := false - r.Enabled = &enabled +func (t *debugTrait) Configure(e *Environment) (bool, error) { + if t.Enabled != nil && *t.Enabled { + return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying), nil } - return nil + + return false, nil } -func (r *debugTrait) apply(e *Environment) error { +func (t *debugTrait) Apply(e *Environment) error { // this is all that's needed as long as the base image is `fabric8/s2i-java` look into builder/builder.go e.EnvVars["JAVA_DEBUG"] = True diff --git a/pkg/trait/debug_test.go b/pkg/trait/debug_test.go index 3989009..6c24d82 100644 --- a/pkg/trait/debug_test.go +++ b/pkg/trait/debug_test.go @@ -24,26 +24,57 @@ import ( "github.com/stretchr/testify/assert" ) -var ( - env = &Environment{ +func TestDebugTraitApplicability(t *testing.T) { + env := Environment{ Integration: &v1alpha1.Integration{ Status: v1alpha1.IntegrationStatus{ Phase: v1alpha1.IntegrationPhaseDeploying, }, + Spec: v1alpha1.IntegrationSpec{ + Traits: map[string]v1alpha1.IntegrationTraitSpec{ + "debug": { + Configuration: map[string]string{ + "enabled": "true", + }, + }, + }, + }, }, EnvVars: make(map[string]string)} - trait = newDebugTrait() -) + trait := newDebugTrait() -func TestApplicability(t *testing.T) { - assert.True(t, trait.appliesTo(env)) + enabled, err := trait.Configure(&env) + assert.Nil(t, err) + assert.False(t, enabled) env.Integration.Status.Phase = v1alpha1.IntegrationPhaseRunning - assert.False(t, trait.appliesTo(env)) + + enabled, err = trait.Configure(&env) + assert.Nil(t, err) + assert.False(t, enabled) } -func TestApply(t *testing.T) { - assert.Nil(t, trait.apply(env)) +func TestApplyDebugTrait(t *testing.T) { + env := Environment{ + Integration: &v1alpha1.Integration{ + Status: v1alpha1.IntegrationStatus{ + Phase: v1alpha1.IntegrationPhaseDeploying, + }, + Spec: v1alpha1.IntegrationSpec{ + Traits: map[string]v1alpha1.IntegrationTraitSpec{ + "debug": { + Configuration: map[string]string{ + "enabled": "true", + }, + }, + }, + }, + }, + EnvVars: make(map[string]string)} + + trait := newDebugTrait() + + assert.Nil(t, trait.Apply(&env)) assert.Equal(t, True, env.EnvVars["JAVA_DEBUG"]) } diff --git a/pkg/trait/dependencies.go b/pkg/trait/dependencies.go index b57d097..ccbc41b 100644 --- a/pkg/trait/dependencies.go +++ b/pkg/trait/dependencies.go @@ -31,15 +31,21 @@ type dependenciesTrait struct { func newDependenciesTrait() *dependenciesTrait { return &dependenciesTrait{ - BaseTrait: newBaseTrait("dependencies"), + BaseTrait: BaseTrait{ + id: ID("dependencies"), + }, } } -func (*dependenciesTrait) appliesTo(e *Environment) bool { - return e.Integration != nil && e.Integration.Status.Phase == "" +func (t *dependenciesTrait) Configure(e *Environment) (bool, error) { + if t.Enabled != nil && !*t.Enabled { + return false, nil + } + + return e.IntegrationInPhase(""), nil } -func (*dependenciesTrait) apply(e *Environment) error { +func (t *dependenciesTrait) Apply(e *Environment) error { for _, s := range e.Integration.Spec.Sources { meta := metadata.Extract(s) diff --git a/pkg/trait/deployment.go b/pkg/trait/deployment.go index 801f849..7e8d01f 100644 --- a/pkg/trait/deployment.go +++ b/pkg/trait/deployment.go @@ -37,43 +37,49 @@ type deploymentTrait struct { func newDeploymentTrait() *deploymentTrait { return &deploymentTrait{ - BaseTrait: newBaseTrait("deployment"), + BaseTrait: BaseTrait{ + id: ID("deployment"), + }, } } -func (d *deploymentTrait) appliesTo(e *Environment) bool { - if e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying { +func (t *deploymentTrait) Configure(e *Environment) (bool, error) { + if t.Enabled != nil && !*t.Enabled { + return false, nil + } + + if e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) { // // Don't deploy on knative // - return e.DetermineProfile() != v1alpha1.TraitProfileKnative + return e.DetermineProfile() != v1alpha1.TraitProfileKnative, nil } - if d.ContainerImage && e.InPhase(v1alpha1.IntegrationContextPhaseReady, v1alpha1.IntegrationPhaseBuildingContext) { - return true + if t.ContainerImage && e.InPhase(v1alpha1.IntegrationContextPhaseReady, v1alpha1.IntegrationPhaseBuildingContext) { + return true, nil } - if !d.ContainerImage && e.InPhase(v1alpha1.IntegrationContextPhaseReady, v1alpha1.IntegrationPhaseBuildingContext) { - return true + if !t.ContainerImage && e.InPhase(v1alpha1.IntegrationContextPhaseReady, v1alpha1.IntegrationPhaseBuildingContext) { + return true, nil } - return false + return false, nil } -func (d *deploymentTrait) apply(e *Environment) error { - if d.ContainerImage && e.InPhase(v1alpha1.IntegrationContextPhaseReady, v1alpha1.IntegrationPhaseBuildingContext) { +func (t *deploymentTrait) Apply(e *Environment) error { + if t.ContainerImage && e.InPhase(v1alpha1.IntegrationContextPhaseReady, v1alpha1.IntegrationPhaseBuildingContext) { // trigger container image build e.Integration.Status.Phase = v1alpha1.IntegrationPhaseBuildingImage } - if !d.ContainerImage && e.InPhase(v1alpha1.IntegrationContextPhaseReady, v1alpha1.IntegrationPhaseBuildingContext) { + if !t.ContainerImage && e.InPhase(v1alpha1.IntegrationContextPhaseReady, v1alpha1.IntegrationPhaseBuildingContext) { // trigger integration deploy e.Integration.Status.Phase = v1alpha1.IntegrationPhaseDeploying } if e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying { - e.Resources.AddAll(d.getConfigMapsFor(e)) - e.Resources.Add(d.getDeploymentFor(e)) + e.Resources.AddAll(t.getConfigMapsFor(e)) + e.Resources.Add(t.getDeploymentFor(e)) } return nil @@ -85,7 +91,7 @@ func (d *deploymentTrait) apply(e *Environment) error { // // ********************************** -func (d *deploymentTrait) getConfigMapsFor(e *Environment) []runtime.Object { +func (t *deploymentTrait) getConfigMapsFor(e *Environment) []runtime.Object { maps := make([]runtime.Object, 0, len(e.Integration.Spec.Sources)+1) // combine properties of integration with context, integration @@ -112,7 +118,7 @@ func (d *deploymentTrait) getConfigMapsFor(e *Environment) []runtime.Object { }, ) - if !d.ContainerImage { + if !t.ContainerImage { // do not create 'source' ConfigMap if a docker images for deployment // is required @@ -152,13 +158,13 @@ func (d *deploymentTrait) getConfigMapsFor(e *Environment) []runtime.Object { // // ********************************** -func (d *deploymentTrait) getSources(e *Environment) []string { +func (t *deploymentTrait) getSources(e *Environment) []string { sources := make([]string, 0, len(e.Integration.Spec.Sources)) for i, s := range e.Integration.Spec.Sources { root := fmt.Sprintf("/etc/camel/integrations/%03d", i) - if d.ContainerImage { + if t.ContainerImage { // assume sources are copied over the standard deployments folder root = "/deployments/sources" @@ -185,8 +191,8 @@ func (d *deploymentTrait) getSources(e *Environment) []string { return sources } -func (d *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment { - sources := d.getSources(e) +func (t *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment { + sources := t.getSources(e) // combine Environment of integration with context, integration // Environment has the priority @@ -288,7 +294,7 @@ func (d *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment { // Volumes :: Sources // - if !d.ContainerImage { + if !t.ContainerImage { // We can configure the operator to generate a container images that include // integration sources instead of mounting it at runtime and in such case we diff --git a/pkg/trait/ingress.go b/pkg/trait/ingress.go index 1688e23..f8bd7ec 100644 --- a/pkg/trait/ingress.go +++ b/pkg/trait/ingress.go @@ -30,43 +30,58 @@ import ( type ingressTrait struct { BaseTrait `property:",squash"` Host string `property:"host"` + Auto *bool `property:"auto"` } func newIngressTrait() *ingressTrait { return &ingressTrait{ - BaseTrait: newBaseTrait("ingress"), - Host: "", + BaseTrait: BaseTrait{ + id: ID("ingress"), + }, + Host: "", } } -func (*ingressTrait) appliesTo(e *Environment) bool { - return e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying -} +func (t *ingressTrait) Configure(e *Environment) (bool, error) { + if t.Enabled != nil && !*t.Enabled { + return false, nil + } -func (i *ingressTrait) autoconfigure(e *Environment) error { - if i.Enabled == nil { - hasService := i.getTargetService(e) != nil - hasHost := i.Host != "" + if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) { + return false, nil + } + + if t.Auto == nil || *t.Auto { + hasService := t.getTargetService(e) != nil + hasHost := t.Host != "" enabled := hasService && hasHost - i.Enabled = &enabled + + if !enabled { + return false, nil + } } - return nil + + if t.Host == "" { + return false, errors.New("cannot Apply ingress trait: no host defined") + } + + return true, nil } -func (i *ingressTrait) apply(e *Environment) error { - if i.Host == "" { - return errors.New("cannot apply ingress trait: no host defined") +func (t *ingressTrait) Apply(e *Environment) error { + if t.Host == "" { + return errors.New("cannot Apply ingress trait: no host defined") } - service := i.getTargetService(e) + service := t.getTargetService(e) if service == nil { - return errors.New("cannot apply ingress trait: no target service") + return errors.New("cannot Apply ingress trait: no target service") } - e.Resources.Add(i.getIngressFor(service)) + e.Resources.Add(t.getIngressFor(service)) return nil } -func (*ingressTrait) getTargetService(e *Environment) (service *corev1.Service) { +func (t *ingressTrait) getTargetService(e *Environment) (service *corev1.Service) { e.Resources.VisitService(func(s *corev1.Service) { if s.ObjectMeta.Labels != nil { if intName, ok := s.ObjectMeta.Labels["camel.apache.org/integration"]; ok && intName == e.Integration.Name { @@ -77,7 +92,7 @@ func (*ingressTrait) getTargetService(e *Environment) (service *corev1.Service) return } -func (i *ingressTrait) getIngressFor(service *corev1.Service) *v1beta1.Ingress { +func (t *ingressTrait) getIngressFor(service *corev1.Service) *v1beta1.Ingress { ingress := v1beta1.Ingress{ TypeMeta: metav1.TypeMeta{ Kind: "Ingress", @@ -94,7 +109,7 @@ func (i *ingressTrait) getIngressFor(service *corev1.Service) *v1beta1.Ingress { }, Rules: []v1beta1.IngressRule{ { - Host: i.Host, + Host: t.Host, }, }, }, diff --git a/pkg/trait/istio.go b/pkg/trait/istio.go index 765cbe8..2073220 100644 --- a/pkg/trait/istio.go +++ b/pkg/trait/istio.go @@ -34,16 +34,22 @@ const ( func newIstioTrait() *istioTrait { return &istioTrait{ - BaseTrait: newBaseTrait("istio"), - Allow: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16", + BaseTrait: BaseTrait{ + id: ID("istio"), + }, + Allow: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16", } } -func (t *istioTrait) appliesTo(e *Environment) bool { - return e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying +func (t *istioTrait) Configure(e *Environment) (bool, error) { + if t.Enabled != nil && !*t.Enabled { + return false, nil + } + + return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying), nil } -func (t *istioTrait) apply(e *Environment) error { +func (t *istioTrait) Apply(e *Environment) error { if t.Allow != "" { e.Resources.VisitDeployment(func(d *appsv1.Deployment) { d.Spec.Template.Annotations = t.injectIstioAnnotation(d.Spec.Template.Annotations) diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go index 17293a7..a40abb3 100644 --- a/pkg/trait/knative.go +++ b/pkg/trait/knative.go @@ -48,39 +48,49 @@ type knativeTrait struct { Sinks string `property:"sinks"` MinScale *int `property:"minScale"` MaxScale *int `property:"maxScale"` + Auto *bool `property:"auto"` } func newKnativeTrait() *knativeTrait { return &knativeTrait{ - BaseTrait: newBaseTrait("knative"), + BaseTrait: BaseTrait{ + id: ID("knative"), + }, } } -func (t *knativeTrait) appliesTo(e *Environment) bool { - return e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying -} - -func (t *knativeTrait) autoconfigure(e *Environment) error { - if t.Sources == "" { - channels := t.getSourceChannels(e) - t.Sources = strings.Join(channels, ",") +func (t *knativeTrait) Configure(e *Environment) (bool, error) { + if t.Enabled != nil && !*t.Enabled { + return false, nil } - if t.Sinks == "" { - channels := t.getSinkChannels(e) - t.Sinks = strings.Join(channels, ",") + + if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) { + return false, nil } - // Check the right value for minScale, as not all services are allowed to scale down to 0 - if t.MinScale == nil { - meta := metadata.ExtractAll(e.Integration.Spec.Sources) - if !meta.RequiresHTTPService || !meta.PassiveEndpoints { - single := 1 - t.MinScale = &single + + if t.Auto == nil || *t.Auto { + if t.Sources == "" { + channels := t.getSourceChannels(e) + t.Sources = strings.Join(channels, ",") + } + if t.Sinks == "" { + channels := t.getSinkChannels(e) + t.Sinks = strings.Join(channels, ",") + } + // Check the right value for minScale, as not all services are allowed to scale down to 0 + if t.MinScale == nil { + meta := metadata.ExtractAll(e.Integration.Spec.Sources) + if !meta.RequiresHTTPService || !meta.PassiveEndpoints { + single := 1 + t.MinScale = &single + } } } - return nil + + return true, nil } -func (t *knativeTrait) apply(e *Environment) error { +func (t *knativeTrait) Apply(e *Environment) error { if err := t.prepareEnvVars(e); err != nil { return err } diff --git a/pkg/trait/knative_test.go b/pkg/trait/knative_test.go index 325e0f9..f701c88 100644 --- a/pkg/trait/knative_test.go +++ b/pkg/trait/knative_test.go @@ -64,7 +64,7 @@ func TestKnativeTraitWithCompressedSources(t *testing.T) { }, }, EnvVars: make(map[string]string), - ExecutedTraits: make([]ID, 0), + ExecutedTraits: make([]Trait, 0), Resources: kubernetes.NewCollection(), } @@ -72,7 +72,7 @@ func TestKnativeTraitWithCompressedSources(t *testing.T) { assert.Nil(t, err) assert.NotEmpty(t, env.ExecutedTraits) - assert.Contains(t, env.ExecutedTraits, ID("knative")) + assert.NotNil(t, env.GetTrait(ID("knative"))) assert.NotNil(t, env.EnvVars["KAMEL_KNATIVE_CONFIGURATION"]) services := 0 diff --git a/pkg/trait/owner.go b/pkg/trait/owner.go index 51e491a..26a7906 100644 --- a/pkg/trait/owner.go +++ b/pkg/trait/owner.go @@ -29,15 +29,21 @@ type ownerTrait struct { func newOwnerTrait() *ownerTrait { return &ownerTrait{ - BaseTrait: newBaseTrait("owner"), + BaseTrait: BaseTrait{ + id: ID("owner"), + }, } } -func (t *ownerTrait) appliesTo(e *Environment) bool { - return e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying +func (t *ownerTrait) Configure(e *Environment) (bool, error) { + if t.Enabled != nil && !*t.Enabled { + return false, nil + } + + return e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying), nil } -func (*ownerTrait) apply(e *Environment) error { +func (*ownerTrait) Apply(e *Environment) error { controller := true blockOwnerDeletion := true e.Resources.VisitMetaObject(func(res metav1.Object) { diff --git a/pkg/trait/route.go b/pkg/trait/route.go index e65b0cb..1e6746c 100644 --- a/pkg/trait/route.go +++ b/pkg/trait/route.go @@ -30,42 +30,52 @@ import ( type routeTrait struct { BaseTrait `property:",squash"` + Auto *bool `property:"auto"` Host string `property:"host"` + service *corev1.Service } func newRouteTrait() *routeTrait { return &routeTrait{ - BaseTrait: newBaseTrait("route"), + BaseTrait: BaseTrait{ + id: ID("route"), + }, } } -func (r *routeTrait) appliesTo(e *Environment) bool { - return e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying -} +func (t *routeTrait) Configure(e *Environment) (bool, error) { + if t.Enabled != nil && !*t.Enabled { + return false, nil + } -func (r *routeTrait) autoconfigure(e *Environment) error { - if r.Enabled == nil { - hasService := r.getTargetService(e) != nil - r.Enabled = &hasService + if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) { + return false, nil } - return nil + + if t.Auto == nil || *t.Auto { + t.service = t.getTargetService(e) + if t.service == nil { + return false, nil + } + } + + if t.service == nil { + return false, errors.New("cannot apply route trait: no target service") + } + + return true, nil } -func (r *routeTrait) apply(e *Environment) error { +func (t *routeTrait) Apply(e *Environment) error { if e.Integration == nil || e.Integration.Status.Phase != v1alpha1.IntegrationPhaseDeploying { return nil } - service := r.getTargetService(e) - if service == nil { - return errors.New("cannot apply route trait: no target service") - } - - e.Resources.Add(r.getRouteFor(service)) + e.Resources.Add(t.getRouteFor(t.service)) return nil } -func (*routeTrait) getTargetService(e *Environment) (service *corev1.Service) { +func (t *routeTrait) getTargetService(e *Environment) (service *corev1.Service) { e.Resources.VisitService(func(s *corev1.Service) { if s.ObjectMeta.Labels != nil { if intName, ok := s.ObjectMeta.Labels["camel.apache.org/integration"]; ok && intName == e.Integration.Name { @@ -76,7 +86,7 @@ func (*routeTrait) getTargetService(e *Environment) (service *corev1.Service) { return } -func (r *routeTrait) getRouteFor(service *corev1.Service) *routev1.Route { +func (t *routeTrait) getRouteFor(service *corev1.Service) *routev1.Route { route := routev1.Route{ TypeMeta: metav1.TypeMeta{ Kind: "Route", @@ -94,7 +104,7 @@ func (r *routeTrait) getRouteFor(service *corev1.Service) *routev1.Route { Kind: "Service", Name: service.Name, }, - Host: r.Host, + Host: t.Host, }, } return &route diff --git a/pkg/trait/service.go b/pkg/trait/service.go index 0efaf45..007c0fe 100644 --- a/pkg/trait/service.go +++ b/pkg/trait/service.go @@ -28,36 +28,45 @@ import ( type serviceTrait struct { BaseTrait `property:",squash"` - Port int `property:"port"` + Auto *bool `property:"auto"` + Port int `property:"port"` } func newServiceTrait() *serviceTrait { return &serviceTrait{ - BaseTrait: newBaseTrait("service"), - Port: 8080, + BaseTrait: BaseTrait{ + id: ID("service"), + }, + Port: 8080, } } -func (s *serviceTrait) appliesTo(e *Environment) bool { - return e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying -} +func (t *serviceTrait) Configure(e *Environment) (bool, error) { + if t.Enabled != nil && !*t.Enabled { + return false, nil + } -func (s *serviceTrait) autoconfigure(e *Environment) error { - if s.Enabled == nil { + if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) { + return false, nil + } + + if t.Auto == nil || *t.Auto { meta := metadata.ExtractAll(e.Integration.Spec.Sources) - required := meta.RequiresHTTPService - s.Enabled = &required + if !meta.RequiresHTTPService { + return false, nil + } } - return nil + + return true, nil } -func (s *serviceTrait) apply(e *Environment) (err error) { - svc := s.getServiceFor(e) +func (t *serviceTrait) Apply(e *Environment) (err error) { + svc := t.getServiceFor(e) e.Resources.Add(svc) return nil } -func (s *serviceTrait) getServiceFor(e *Environment) *corev1.Service { +func (t *serviceTrait) getServiceFor(e *Environment) *corev1.Service { svc := corev1.Service{ TypeMeta: metav1.TypeMeta{ Kind: "Service", @@ -76,7 +85,7 @@ func (s *serviceTrait) getServiceFor(e *Environment) *corev1.Service { Name: "http", Port: 80, Protocol: corev1.ProtocolTCP, - TargetPort: intstr.FromInt(s.Port), + TargetPort: intstr.FromInt(t.Port), }, }, Selector: map[string]string{ diff --git a/pkg/trait/springboot.go b/pkg/trait/springboot.go index 1314bfb..522a526 100644 --- a/pkg/trait/springboot.go +++ b/pkg/trait/springboot.go @@ -36,38 +36,31 @@ type springBootTrait struct { func newSpringBootTrait() *springBootTrait { return &springBootTrait{ - BaseTrait: newBaseTrait("springboot"), + BaseTrait: BaseTrait{ + id: ID("springboot"), + }, } } -// IsAuto determines if we should apply automatic configuration -func (trait *springBootTrait) IsAuto() bool { - return false -} - -// IsEnabled is used to determine if the trait needs to be executed -func (trait *springBootTrait) IsEnabled() bool { - if trait.Enabled == nil { - return false +func (t *springBootTrait) Configure(e *Environment) (bool, error) { + if t.Enabled == nil || !*t.Enabled { + return false, nil } - return *trait.Enabled -} -func (trait *springBootTrait) appliesTo(e *Environment) bool { - if e.Context != nil && e.Context.Status.Phase == v1alpha1.IntegrationContextPhaseBuilding { - return true + if e.IntegrationContextInPhase(v1alpha1.IntegrationContextPhaseBuilding) { + return true, nil } - if e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying { - return true + if e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) { + return true, nil } - if e.Integration != nil && e.Integration.Status.Phase == "" { - return true + if e.IntegrationInPhase("") { + return true, nil } - return false + return false, nil } -func (trait *springBootTrait) apply(e *Environment) error { +func (t *springBootTrait) Apply(e *Environment) error { // // Integration diff --git a/pkg/trait/trait.go b/pkg/trait/trait.go index 592796d..6684319 100644 --- a/pkg/trait/trait.go +++ b/pkg/trait/trait.go @@ -24,6 +24,7 @@ import ( "github.com/pkg/errors" ) +// True -- const True = "true" // Apply -- @@ -72,7 +73,7 @@ func newEnvironment(integration *v1alpha1.Integration, ctx *v1alpha1.Integration Platform: pl, Context: ctx, Integration: integration, - ExecutedTraits: make([]ID, 0), + ExecutedTraits: make([]Trait, 0), Resources: kubernetes.NewCollection(), EnvVars: make(map[string]string), }, nil diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go index c6df05d..b48bd37 100644 --- a/pkg/trait/trait_test.go +++ b/pkg/trait/trait_test.go @@ -37,11 +37,12 @@ const ( func TestOpenShiftTraits(t *testing.T) { env := createTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, "camel:core") res := processTestEnv(t, env) + assert.NotEmpty(t, env.ExecutedTraits) - assert.Contains(t, env.ExecutedTraits, ID("deployment")) - assert.NotContains(t, env.ExecutedTraits, ID("service")) - assert.NotContains(t, env.ExecutedTraits, ID("route")) - assert.Contains(t, env.ExecutedTraits, ID("owner")) + assert.NotNil(t, env.GetTrait(ID("deployment"))) + assert.Nil(t, env.GetTrait(ID("service"))) + assert.Nil(t, env.GetTrait(ID("route"))) + assert.NotNil(t, env.GetTrait(ID("owner"))) assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool { return cm.Name == TestProperties })) @@ -53,10 +54,10 @@ func TestOpenShiftTraits(t *testing.T) { func TestOpenShiftTraitsWithWeb(t *testing.T) { env := createTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, "from('undertow:http').to('log:info')") res := processTestEnv(t, env) - assert.Contains(t, env.ExecutedTraits, ID("deployment")) - assert.Contains(t, env.ExecutedTraits, ID("service")) - assert.Contains(t, env.ExecutedTraits, ID("route")) - assert.Contains(t, env.ExecutedTraits, ID("owner")) + assert.NotNil(t, env.GetTrait(ID("deployment"))) + assert.NotNil(t, env.GetTrait(ID("service"))) + assert.NotNil(t, env.GetTrait(ID("route"))) + assert.NotNil(t, env.GetTrait(ID("owner"))) assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool { return cm.Name == TestProperties })) @@ -80,8 +81,8 @@ func TestOpenShiftTraitsWithWebAndConfig(t *testing.T) { }, } res := processTestEnv(t, env) - assert.Contains(t, env.ExecutedTraits, ID("service")) - assert.Contains(t, env.ExecutedTraits, ID("route")) + assert.NotNil(t, env.GetTrait(ID("service"))) + assert.NotNil(t, env.GetTrait(ID("route"))) assert.NotNil(t, res.GetService(func(svc *corev1.Service) bool { return svc.Name == TestDeployment && svc.Spec.Ports[0].TargetPort.IntVal == int32(7071) })) @@ -97,8 +98,8 @@ func TestOpenShiftTraitsWithWebAndDisabledTrait(t *testing.T) { }, } res := processTestEnv(t, env) - assert.NotContains(t, env.ExecutedTraits, ID("service")) - assert.NotContains(t, env.ExecutedTraits, ID("route")) // No route without service + assert.Nil(t, env.GetTrait(ID("service"))) + assert.Nil(t, env.GetTrait(ID("route"))) // No route without service assert.Nil(t, res.GetService(func(svc *corev1.Service) bool { return true })) @@ -107,10 +108,10 @@ func TestOpenShiftTraitsWithWebAndDisabledTrait(t *testing.T) { func TestKubernetesTraits(t *testing.T) { env := createTestEnv(v1alpha1.IntegrationPlatformClusterKubernetes, "from('timer:tick').to('log:info')") res := processTestEnv(t, env) - assert.Contains(t, env.ExecutedTraits, ID("deployment")) - assert.NotContains(t, env.ExecutedTraits, ID("service")) - assert.NotContains(t, env.ExecutedTraits, ID("route")) - assert.Contains(t, env.ExecutedTraits, ID("owner")) + assert.NotNil(t, env.GetTrait(ID("deployment"))) + assert.Nil(t, env.GetTrait(ID("service"))) + assert.Nil(t, env.GetTrait(ID("route"))) + assert.NotNil(t, env.GetTrait(ID("owner"))) assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool { return cm.Name == TestProperties })) @@ -122,10 +123,10 @@ func TestKubernetesTraits(t *testing.T) { func TestKubernetesTraitsWithWeb(t *testing.T) { env := createTestEnv(v1alpha1.IntegrationPlatformClusterKubernetes, "from('servlet:http').to('log:info')") res := processTestEnv(t, env) - assert.Contains(t, env.ExecutedTraits, ID("deployment")) - assert.Contains(t, env.ExecutedTraits, ID("service")) - assert.NotContains(t, env.ExecutedTraits, ID("route")) - assert.Contains(t, env.ExecutedTraits, ID("owner")) + assert.NotNil(t, env.GetTrait(ID("deployment"))) + assert.NotNil(t, env.GetTrait(ID("service"))) + assert.Nil(t, env.GetTrait(ID("route"))) + assert.NotNil(t, env.GetTrait(ID("owner"))) assert.NotNil(t, res.GetConfigMap(func(cm *corev1.ConfigMap) bool { return cm.Name == TestProperties })) @@ -154,7 +155,8 @@ func TestTraitDecode(t *testing.T) { assert.Nil(t, err) assert.Equal(t, 7071, svc.Port) - assert.Equal(t, false, svc.IsEnabled()) + assert.NotNil(t, svc.Enabled) + assert.Equal(t, false, *svc.Enabled) } func processTestEnv(t *testing.T, env *Environment) *kubernetes.Collection { @@ -191,7 +193,7 @@ func createTestEnv(cluster v1alpha1.IntegrationPlatformCluster, script string) * }, }, EnvVars: make(map[string]string), - ExecutedTraits: make([]ID, 0), + ExecutedTraits: make([]Trait, 0), Resources: kubernetes.NewCollection(), } } diff --git a/pkg/trait/types.go b/pkg/trait/types.go index d61f352..a81f2f4 100644 --- a/pkg/trait/types.go +++ b/pkg/trait/types.go @@ -35,16 +35,12 @@ type ID string // Trait is the interface of all traits type Trait interface { Identifiable - // IsEnabled tells if the trait is enabled - IsEnabled() bool - // IsAuto determine if the trait should be configured automatically - IsAuto() bool - // appliesTo tells if the trait supports the given environment - appliesTo(environment *Environment) bool - // autoconfigure is called before any customization to ensure the trait is fully configured - autoconfigure(environment *Environment) error - // apply executes a customization of the Environment - apply(environment *Environment) error + + // Configure the trait + Configure(environment *Environment) (bool, error) + + // Apply executes a customization of the Environment + Apply(environment *Environment) error } /* Base trait */ @@ -53,13 +49,6 @@ type Trait interface { type BaseTrait struct { id ID Enabled *bool `property:"enabled"` - Auto *bool `property:"auto"` -} - -func newBaseTrait(id string) BaseTrait { - return BaseTrait{ - id: ID(id), - } } // ID returns the identifier of the trait @@ -67,26 +56,6 @@ func (trait *BaseTrait) ID() ID { return trait.id } -// IsAuto determines if we should apply automatic configuration -func (trait *BaseTrait) IsAuto() bool { - if trait.Auto == nil { - return true - } - return *trait.Auto -} - -// IsEnabled is used to determine if the trait needs to be executed -func (trait *BaseTrait) IsEnabled() bool { - if trait.Enabled == nil { - return true - } - return *trait.Enabled -} - -func (trait *BaseTrait) autoconfigure(environment *Environment) error { - return nil -} - /* Environment */ // A Environment provides the context where the trait is executed @@ -97,10 +66,21 @@ type Environment struct { Resources *kubernetes.Collection Steps []builder.Step BuildDir string - ExecutedTraits []ID + ExecutedTraits []Trait EnvVars map[string]string } +// GetTrait -- +func (e *Environment) GetTrait(id ID) Trait { + for _, t := range e.ExecutedTraits { + if t.ID() == id { + return t + } + } + + return nil +} + // IntegrationInPhase -- func (e *Environment) IntegrationInPhase(phase v1alpha1.IntegrationPhase) bool { return e.Integration != nil && e.Integration.Status.Phase == phase