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 a000ec5c18b0e504ccd2e424a3ca417e0d50b11a
Author: John Poth <[email protected]>
AuthorDate: Fri Sep 25 14:31:30 2020 +0200

    feat: Add ServiceBinding trait (#1445)
---
 deploy/operator-role-binding-service-binding.yaml  |  30 +++
 deploy/operator-role-service-binding.yaml          |  37 +++
 go.mod                                             |   7 +-
 pkg/apis/addtoscheme_service_binding.go            |  25 ++
 pkg/apis/camel/v1/integration_types.go             |   4 +
 pkg/cmd/run.go                                     |   9 +-
 .../integration/integration_controller.go          |  10 +
 pkg/controller/integration/platform_setup.go       |   3 +-
 pkg/trait/deployer.go                              |   3 +-
 pkg/trait/service_binding.go                       | 266 +++++++++++++++++++++
 pkg/trait/trait_register.go                        |   1 +
 pkg/trait/trait_types.go                           |  33 ++-
 12 files changed, 418 insertions(+), 10 deletions(-)

diff --git a/deploy/operator-role-binding-service-binding.yaml 
b/deploy/operator-role-binding-service-binding.yaml
new file mode 100644
index 0000000..9a2621a
--- /dev/null
+++ b/deploy/operator-role-binding-service-binding.yaml
@@ -0,0 +1,30 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+
+kind: RoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+  name: camel-k-operator-service-binding
+  labels:
+    app: "camel-k"
+subjects:
+- kind: ServiceAccount
+  name: camel-k-operator
+roleRef:
+  kind: Role
+  name: camel-k-operator-service-binding
+  apiGroup: rbac.authorization.k8s.io
diff --git a/deploy/operator-role-service-binding.yaml 
b/deploy/operator-role-service-binding.yaml
new file mode 100644
index 0000000..68a2774
--- /dev/null
+++ b/deploy/operator-role-service-binding.yaml
@@ -0,0 +1,37 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+
+kind: Role
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+  name: camel-k-operator-service-binding
+  labels:
+    app: "camel-k"
+rules:
+- apiGroups:
+    - operators.coreos.com
+  resources:
+    - servicebindings
+  verbs:
+  - create
+  - delete
+  - deletecollection
+  - get
+  - list
+  - patch
+  - update
+  - watch
diff --git a/go.mod b/go.mod
index e744805..603cb68 100644
--- a/go.mod
+++ b/go.mod
@@ -28,6 +28,7 @@ require (
        github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring 
v0.42.1
        github.com/prometheus/client_golang v1.7.1
        github.com/radovskyb/watcher v1.0.6
+       github.com/redhat-developer/service-binding-operator v0.4.0
        github.com/rs/xid v1.2.1
        github.com/scylladb/go-set v1.0.2
        github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749
@@ -37,7 +38,7 @@ require (
        github.com/spf13/pflag v1.0.5
        github.com/spf13/viper v1.6.2
        github.com/stoewer/go-strcase v1.0.2
-       github.com/stretchr/testify v1.5.1
+       github.com/stretchr/testify v1.6.1
        go.uber.org/multierr v1.5.0
        golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
        gopkg.in/inf.v0 v0.9.1
@@ -57,6 +58,10 @@ replace (
        github.com/Azure/go-autorest => github.com/Azure/go-autorest 
v13.3.2+incompatible // Required by OLM
        k8s.io/client-go => k8s.io/client-go v0.18.9
        k8s.io/code-generator => k8s.io/code-generator v0.18.9
+       // Required by Service Binding Operator dependency, see 
https://github.com/redhat-developer/service-binding-operator/pull/798
+       github.com/operator-framework/operator-lifecycle-manager => 
github.com/operator-framework/operator-lifecycle-manager 
v0.0.0-20200321030439-57b580e57e88
+       github.com/operator-framework/operator-sdk => 
github.com/operator-framework/operator-sdk v0.17.1
+       k8s.io/api => k8s.io/api v0.18.9
 )
 
 replace github.com/docker/docker => github.com/moby/moby 
v0.7.3-0.20190826074503-38ab9da00309 // Required by Helm
diff --git a/pkg/apis/addtoscheme_service_binding.go 
b/pkg/apis/addtoscheme_service_binding.go
new file mode 100644
index 0000000..9fc4a7b
--- /dev/null
+++ b/pkg/apis/addtoscheme_service_binding.go
@@ -0,0 +1,25 @@
+/*
+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 apis
+
+import 
"github.com/redhat-developer/service-binding-operator/pkg/apis/operators/v1alpha1"
+
+func init() {
+       // Register the types with the Scheme so the components can map objects 
to GroupVersionKinds and back
+       AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme)
+}
diff --git a/pkg/apis/camel/v1/integration_types.go 
b/pkg/apis/camel/v1/integration_types.go
index 3d103d5..5a9e631 100644
--- a/pkg/apis/camel/v1/integration_types.go
+++ b/pkg/apis/camel/v1/integration_types.go
@@ -112,6 +112,8 @@ const (
        IntegrationPhaseBuildingKit IntegrationPhase = "Building Kit"
        // IntegrationPhaseResolvingKit --
        IntegrationPhaseResolvingKit IntegrationPhase = "Resolving Kit"
+       // IntegrationPhaseWaitingForServiceBindingCollectionReady --
+       IntegrationPhaseWaitingForServiceBindingCollectionReady 
IntegrationPhase = "Waiting For Service binding Collection Ready Status"
        // IntegrationPhaseDeploying --
        IntegrationPhaseDeploying IntegrationPhase = "Deploying"
        // IntegrationPhaseRunning --
@@ -133,6 +135,8 @@ const (
        IntegrationConditionKnativeServiceAvailable IntegrationConditionType = 
"KnativeServiceAvailable"
        // IntegrationConditionCronJobAvailable --
        IntegrationConditionCronJobAvailable IntegrationConditionType = 
"CronJobAvailable"
+       // IntegrationConditionServiceBindingCollectionReady --
+       IntegrationConditionServiceBindingCollectionReady 
IntegrationConditionType = "ServiceBindingCollectionReady"
        // IntegrationConditionExposureAvailable --
        IntegrationConditionExposureAvailable IntegrationConditionType = 
"ExposureAvailable"
        // IntegrationConditionPrometheusAvailable --
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 874c443..6f5f4ce 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -72,6 +72,7 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) 
(*cobra.Command, *runCmdOptions)
        }
 
        cmd.Flags().String("name", "", "The integration name")
+       cmd.Flags().StringArrayP("connect", "c", nil, "A ServiceBinding or 
Provisioned Service that the integration should bind to specified as 
KIND.VERSION.GROUP/NAME[/NAMESPACE]")
        cmd.Flags().StringArrayP("dependency", "d", nil, "An external library 
that should be included. E.g. for Maven dependencies \"mvn:org.my/app:1.0\"")
        cmd.Flags().BoolP("wait", "w", false, "Wait for the integration to be 
running")
        cmd.Flags().StringP("kit", "k", "", "The kit used to run the 
integration")
@@ -117,6 +118,7 @@ type runCmdOptions struct {
        IntegrationName string   `mapstructure:"name" yaml:",omitempty"`
        Profile         string   `mapstructure:"profile" yaml:",omitempty"`
        OutputFormat    string   `mapstructure:"output" yaml:",omitempty"`
+       Connects        []string `mapstructure:"connects" yaml:",omitempty"`
        Resources       []string `mapstructure:"resources" yaml:",omitempty"`
        OpenAPIs        []string `mapstructure:"open-apis" yaml:",omitempty"`
        Dependencies    []string `mapstructure:"dependencies" yaml:",omitempty"`
@@ -658,7 +660,12 @@ func (o *runCmdOptions) GetIntegrationName(sources 
[]string) string {
        return name
 }
 
-func (*runCmdOptions) configureTraits(integration *v1.Integration, options 
[]string, catalog *trait.Catalog) error {
+func (o *runCmdOptions) configureTraits(integration *v1.Integration, options 
[]string, catalog *trait.Catalog) error {
+       // configure ServiceBinding trait
+       for _, sb := range o.Connects {
+               bindings := fmt.Sprintf("service-binding.service-bindings=%s", 
sb)
+               options = append(options, bindings)
+       }
        traits, err := configureTraits(options, catalog)
        if err != nil {
                return err
diff --git a/pkg/controller/integration/integration_controller.go 
b/pkg/controller/integration/integration_controller.go
index 5a1b5fe..012a20c 100644
--- a/pkg/controller/integration/integration_controller.go
+++ b/pkg/controller/integration/integration_controller.go
@@ -44,6 +44,7 @@ import (
        "github.com/apache/camel-k/pkg/util/digest"
        "github.com/apache/camel-k/pkg/util/log"
        "github.com/apache/camel-k/pkg/util/monitoring"
+    sb 
"github.com/redhat-developer/service-binding-operator/pkg/apis/operators/v1alpha1"
 )
 
 // Add creates a new Integration Controller and adds it to the Manager. The 
Manager will set fields on the Controller
@@ -217,6 +218,15 @@ func add(mgr manager.Manager, r reconcile.Reconciler) 
error {
                return err
        }
 
+       // Watch ServiceBindings created
+       err = c.Watch(&source.Kind{Type: &sb.ServiceBinding{}}, 
&handler.EnqueueRequestForOwner{
+               OwnerType:    &v1.Integration{},
+               IsController: true,
+       })
+       if err != nil {
+               return err
+       }
+
        return nil
 }
 
diff --git a/pkg/controller/integration/platform_setup.go 
b/pkg/controller/integration/platform_setup.go
index 51c5a5b..0bf4127 100644
--- a/pkg/controller/integration/platform_setup.go
+++ b/pkg/controller/integration/platform_setup.go
@@ -46,7 +46,8 @@ func (action *platformSetupAction) Name() string {
 // CanHandle tells whether this action can handle the integration
 func (action *platformSetupAction) CanHandle(integration *v1.Integration) bool 
{
        return integration.Status.Phase == v1.IntegrationPhaseNone ||
-               integration.Status.Phase == 
v1.IntegrationPhaseWaitingForPlatform
+               integration.Status.Phase == 
v1.IntegrationPhaseWaitingForPlatform ||
+               integration.Status.Phase == 
v1.IntegrationPhaseWaitingForServiceBindingCollectionReady
 }
 
 // Handle handles the integrations
diff --git a/pkg/trait/deployer.go b/pkg/trait/deployer.go
index 81aaee8..ed11cf4 100644
--- a/pkg/trait/deployer.go
+++ b/pkg/trait/deployer.go
@@ -49,6 +49,7 @@ func newDeployerTrait() Trait {
 func (t *deployerTrait) Configure(e *Environment) (bool, error) {
        return e.IntegrationInPhase(
                v1.IntegrationPhaseNone,
+               v1.IntegrationPhaseWaitingForServiceBindingCollectionReady,
                v1.IntegrationPhaseWaitingForPlatform,
                v1.IntegrationPhaseInitialization,
                v1.IntegrationPhaseBuildingKit,
@@ -79,7 +80,7 @@ func (t *deployerTrait) Apply(e *Environment) error {
                        })
                }
 
-       case v1.IntegrationPhaseRunning:
+       case v1.IntegrationPhaseRunning, 
v1.IntegrationPhaseWaitingForServiceBindingCollectionReady:
                // Register a post action that patches the resources generated 
by the traits
                e.PostActions = append(e.PostActions, func(env *Environment) 
error {
                        for _, resource := range env.Resources.Items() {
diff --git a/pkg/trait/service_binding.go b/pkg/trait/service_binding.go
new file mode 100644
index 0000000..131d2c5
--- /dev/null
+++ b/pkg/trait/service_binding.go
@@ -0,0 +1,266 @@
+/*
+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 trait
+
+import (
+       "fmt"
+       "strings"
+
+       v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+       sb 
"github.com/redhat-developer/service-binding-operator/pkg/apis/operators/v1alpha1"
+       corev1 "k8s.io/api/core/v1"
+       k8serrors "k8s.io/apimachinery/pkg/api/errors"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+// The Service Binding trait allows users to connect to Provisioned Services 
and ServiceBindings in Kubernetes:
+// https://github.com/k8s-service-bindings/spec#service-binding
+// As the specification is still evolving this is subject to change
+// +camel-k:trait=service-binding
+type serviceBindingTrait struct {
+       BaseTrait `property:",squash"`
+       // List of Provisioned Services and ServiceBindings in the form 
KIND.VERSION.GROUP/NAME[/NAMESPACE]
+       ServiceBindings []string `property:"service-bindings" 
json:"serviceBindings,omitempty"`
+}
+
+func newServiceBindingTrait() Trait {
+       return &serviceBindingTrait{
+               BaseTrait: NewBaseTrait("service-binding", 250),
+       }
+}
+
+// IsAllowedInProfile overrides default
+func (t *serviceBindingTrait) IsAllowedInProfile(profile v1.TraitProfile) bool 
{
+       return profile == v1.TraitProfileKubernetes ||
+               profile == v1.TraitProfileOpenShift
+}
+
+func (t *serviceBindingTrait) Configure(e *Environment) (bool, error) {
+       if t.Enabled != nil && !*t.Enabled {
+               return false, nil
+       }
+
+       if len(t.ServiceBindings) == 0 {
+               return false, nil
+       }
+
+       return e.IntegrationInPhase(
+               v1.IntegrationPhaseInitialization,
+               v1.IntegrationPhaseWaitingForServiceBindingCollectionReady,
+               v1.IntegrationPhaseDeploying,
+               v1.IntegrationPhaseRunning,
+       ), nil
+}
+
+func (t *serviceBindingTrait) Apply(e *Environment) error {
+       integrationServiceBindingName := e.Integration.Name + 
"-service-binding-request"
+       services, err := t.parseProvisionedServices(e)
+       if err != nil {
+               return err
+       }
+       serviceBindings, err := t.parseServiceBindings(e)
+       if err != nil {
+               return err
+       }
+       if len(services) > 0 {
+               serviceBindings = append(serviceBindings, 
integrationServiceBindingName)
+       }
+       if e.IntegrationInPhase(v1.IntegrationPhaseInitialization) {
+               serviceBindingsCollectionReady := true
+               for _, name := range serviceBindings {
+                       isIntSB := name == integrationServiceBindingName
+                       serviceBinding, err := t.getServiceBinding(e, name)
+                       // Do not throw an error if the ServiceBinding is not 
found and if we are managing it: we will create it
+                       if (err != nil && !k8serrors.IsNotFound(err)) || (err 
!= nil && !isIntSB) {
+                               return err
+                       }
+                       if isIntSB {
+                               request := createServiceBinding(e, services, 
integrationServiceBindingName)
+                               e.Resources.Add(&request)
+                       }
+                       if isCollectionReady(serviceBinding) {
+                               setCollectionReady(e, name, 
corev1.ConditionTrue)
+                       } else {
+                               setCollectionReady(e, name, 
corev1.ConditionFalse)
+                               serviceBindingsCollectionReady = false
+                       }
+               }
+               if !serviceBindingsCollectionReady {
+                       e.PostProcessors = append(e.PostProcessors, 
func(environment *Environment) error {
+                               e.Integration.Status.Phase = 
v1.IntegrationPhaseWaitingForServiceBindingCollectionReady
+                               return nil
+                       })
+               }
+               return nil
+       } else if 
e.IntegrationInPhase(v1.IntegrationPhaseWaitingForServiceBindingCollectionReady)
 {
+               for _, name := range serviceBindings {
+                       serviceBinding, err := t.getServiceBinding(e, name)
+                       if err != nil {
+                               return err
+                       }
+                       if isCollectionReady(serviceBinding) {
+                               setCollectionReady(e, name, 
corev1.ConditionTrue)
+                       } else {
+                               setCollectionReady(e, name, 
corev1.ConditionFalse)
+                               return nil
+                       }
+               }
+               e.PostActions = append(e.PostActions, func(environment 
*Environment) error {
+                       e.Integration.Status.Phase = 
v1.IntegrationPhaseInitialization
+                       return nil
+               })
+       } else if e.IntegrationInPhase(v1.IntegrationPhaseDeploying, 
v1.IntegrationPhaseRunning) {
+               e.ServiceBindings = make(map[string]string)
+               for _, name := range serviceBindings {
+                       sb, err := t.getServiceBinding(e, name)
+                       if err != nil {
+                               return err
+                       }
+                       if !isCollectionReady(sb) {
+                               setCollectionReady(e, name, 
corev1.ConditionFalse)
+                               e.PostProcessors = append(e.PostProcessors, 
func(environment *Environment) error {
+                                       e.Integration.Status.Phase = 
v1.IntegrationPhaseWaitingForServiceBindingCollectionReady
+                                       return nil
+                               })
+                               return nil
+                       }
+                       e.ServiceBindings[name] = sb.Status.Secret
+                       if name == integrationServiceBindingName {
+                               request := createServiceBinding(e, services, 
integrationServiceBindingName)
+                               e.Resources.Add(&request)
+                       }
+               }
+               
e.ApplicationProperties["quarkus.kubernetes-service-binding.enabled"] = "true"
+               e.ApplicationProperties["SERVICE_BINDING_ROOT"] = 
ServiceBindingsMountPath
+       }
+       return nil
+}
+
+func setCollectionReady(e *Environment, serviceBinding string, status 
corev1.ConditionStatus) {
+       e.Integration.Status.SetCondition(
+               v1.IntegrationConditionServiceBindingCollectionReady,
+               status,
+               "",
+               fmt.Sprintf("Name=%s", serviceBinding),
+       )
+}
+
+func isCollectionReady(sb sb.ServiceBinding) bool {
+       for _, condition := range sb.Status.Conditions {
+               if condition.Type == "CollectionReady" {
+                       return condition.Status == corev1.ConditionTrue && 
sb.Status.Secret != ""
+               }
+       }
+       return false
+}
+
+func (t *serviceBindingTrait) getServiceBinding(e *Environment, name string) 
(sb.ServiceBinding, error) {
+       serviceBinding := sb.ServiceBinding{}
+       key := k8sclient.ObjectKey{
+               Namespace: e.Integration.Namespace,
+               Name:      name,
+       }
+       return serviceBinding, t.Client.Get(t.Ctx, key, &serviceBinding)
+}
+
+func (t *serviceBindingTrait) parseProvisionedServices(e *Environment) 
([]sb.Service, error) {
+       services := make([]sb.Service, 0)
+       for _, s := range t.ServiceBindings {
+               seg := strings.Split(s, "/")
+               if !(len(seg) == 3 || len(seg) == 2) {
+                       return nil, fmt.Errorf("ServiceBinding: %s should be 
specified in the form KIND.VERSION.GROUP/NAME[/NAMESPACE]", s)
+               }
+               gvk := seg[0]
+               index := strings.Index(gvk, ".")
+               kind := seg[0][0:index]
+               if kind == "ServiceBinding" {
+                       continue
+               }
+               vg := seg[0][index+1 : len(gvk)]
+               index = strings.Index(vg, ".")
+               version := vg[0:index]
+               group := vg[index+1 : len(vg)]
+               name := seg[1]
+               namespace := e.Integration.Namespace
+               if len(seg) == 3 {
+                       namespace = seg[2]
+               }
+               namePrefix := ""
+               service := sb.Service{
+                       GroupVersionKind: metav1.GroupVersionKind{
+                               Group:   group,
+                               Version: version,
+                               Kind:    kind,
+                       },
+                       EnvVarPrefix: &namePrefix,
+                       LocalObjectReference: corev1.LocalObjectReference{
+                               Name: name,
+                       },
+                       Namespace: &namespace,
+               }
+               services = append(services, service)
+       }
+       return services, nil
+}
+
+func (t *serviceBindingTrait) parseServiceBindings(e *Environment) ([]string, 
error) {
+       serviceBindings := make([]string, 0)
+       for _, s := range t.ServiceBindings {
+               seg := strings.Split(s, "/")
+               if !(len(seg) == 3 || len(seg) == 2) {
+                       return nil, fmt.Errorf("ServiceBinding: %s should be 
specified in the form KIND.VERSION.GROUP/NAME[/NAMESPACE]", s)
+               }
+               gvk := seg[0]
+               index := strings.Index(gvk, ".")
+               kind := seg[0][0:index]
+               if kind == "ServiceBinding" {
+                       vg := seg[0][index+1 : len(gvk)]
+                       if vg != "v1alpha1.operators.coreos.com" {
+                               return nil, fmt.Errorf("ServiceBinding: %s 
VERSION.GROUP should be v1alpha1.operators.coreos.com", s)
+                       }
+                       if len(seg) == 3 && seg[2] != e.Integration.Namespace {
+                               return nil, fmt.Errorf("ServiceBinding: %s 
should be in the same namespace %s as the integration", s, 
e.Integration.Namespace)
+                       }
+                       serviceBindings = append(serviceBindings, seg[1])
+               }
+       }
+       return serviceBindings, nil
+}
+
+func createServiceBinding(e *Environment, services []sb.Service, name string) 
sb.ServiceBinding {
+       spec := sb.ServiceBindingSpec{
+               Services: services,
+       }
+       labels := map[string]string{
+               v1.IntegrationLabel: e.Integration.Name,
+       }
+       serviceBinding := sb.ServiceBinding{
+               TypeMeta: metav1.TypeMeta{
+                       Kind:       "ServiceBinding",
+                       APIVersion: "operators.coreos.com/v1alpha1",
+               },
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: e.Integration.Namespace,
+                       Name:      name,
+                       Labels:    labels,
+               },
+               Spec: spec,
+       }
+       return serviceBinding
+}
diff --git a/pkg/trait/trait_register.go b/pkg/trait/trait_register.go
index ef171c7..1ec4e31 100644
--- a/pkg/trait/trait_register.go
+++ b/pkg/trait/trait_register.go
@@ -45,6 +45,7 @@ func init() {
        AddToTraits(newRouteTrait)
        AddToTraits(newIstioTrait)
        AddToTraits(newIngressTrait)
+       AddToTraits(newServiceBindingTrait)
        AddToTraits(newOwnerTrait)
        AddToTraits(newPdbTrait)
 }
diff --git a/pkg/trait/trait_types.go b/pkg/trait/trait_types.go
index 7b60392..ed893e0 100644
--- a/pkg/trait/trait_types.go
+++ b/pkg/trait/trait_types.go
@@ -42,12 +42,13 @@ import (
 const True = "true"
 
 var (
-       basePath            = "/etc/camel"
-       confDPath           = path.Join(basePath, "conf.d")
-       sourcesMountPath    = path.Join(basePath, "sources")
-       resourcesMountPath  = path.Join(basePath, "resources")
-       configMapsMountPath = path.Join(confDPath, "_configmaps")
-       secretsMountPath    = path.Join(confDPath, "_secrets")
+       basePath                 = "/etc/camel"
+       confDPath                = path.Join(basePath, "conf.d")
+       sourcesMountPath         = path.Join(basePath, "sources")
+       resourcesMountPath       = path.Join(basePath, "resources")
+       configMapsMountPath      = path.Join(confDPath, "_configmaps")
+       secretsMountPath         = path.Join(confDPath, "_secrets")
+       serviceBindingsMountPath = path.Join(ConfdPath, "_servicebindings")
 )
 
 // Identifiable represent an identifiable type
@@ -196,6 +197,7 @@ type Environment struct {
        EnvVars               []corev1.EnvVar
        ApplicationProperties map[string]string
        Interceptors          []string
+       ServiceBindings       map[string]string
 }
 
 // ControllerStrategy is used to determine the kind of controller that needs 
to be created for the integration
@@ -671,6 +673,25 @@ func (e *Environment) configureVolumesAndMounts(vols 
*[]corev1.Volume, mnts *[]c
        //
        // Volumes :: Additional Secrets
        //
+    // append Service Binding secrets
+    for _, name := range e.ServiceBindings {
+        refName := kubernetes.SanitizeLabel(name)
+
+        *vols = append(*vols, corev1.Volume{
+            Name: refName,
+            VolumeSource: corev1.VolumeSource{
+                Secret: &corev1.SecretVolumeSource{
+                    SecretName: name,
+                },
+            },
+        })
+
+        *mnts = append(*mnts, corev1.VolumeMount{
+            Name:      refName,
+            MountPath: path.Join(serviceBindingsMountPath, 
strings.ToLower(name)),
+        })
+    }
+    secrets := e.CollectConfigurationValues("secret")
        for _, secretName := range e.collectConfigurationValues("secret") {
                refName := kubernetes.SanitizeLabel(secretName)
 

Reply via email to