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

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

commit 8ba04e79a203d8fe382731ab3d1e5d2db9ef45da
Author: nicolaferraro <ni.ferr...@gmail.com>
AuthorDate: Wed Dec 15 10:36:40 2021 +0100

    Fix #1107: initial trait
---
 addons/keda/duck/v1alpha1/doc.go                   |  21 ++
 addons/keda/duck/v1alpha1/duck_types.go            | 108 ++++++++++
 .../v1alpha1/duck_types_support.go}                |  42 ++--
 addons/keda/duck/v1alpha1/register.go              |  57 +++++
 addons/keda/duck/v1alpha1/zz_generated.deepcopy.go | 235 +++++++++++++++++++++
 addons/keda/keda.go                                | 118 ++++++++++-
 docs/modules/ROOT/nav.adoc                         |   1 +
 docs/modules/traits/pages/keda.adoc                |  44 ++++
 go.sum                                             |   1 +
 pkg/cmd/run.go                                     |   7 +-
 pkg/resources/resources.go                         |   4 +-
 resources/traits.yaml                              |  23 ++
 script/Makefile                                    |   8 +-
 script/{gen_doc.sh => gen_client_keda.sh}          |  16 +-
 script/gen_doc.sh                                  |   2 +-
 15 files changed, 645 insertions(+), 42 deletions(-)

diff --git a/addons/keda/duck/v1alpha1/doc.go b/addons/keda/duck/v1alpha1/doc.go
new file mode 100644
index 0000000..56d897a
--- /dev/null
+++ b/addons/keda/duck/v1alpha1/doc.go
@@ -0,0 +1,21 @@
+/*
+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 duck contains a partial schema of the Keda APIs
+// +kubebuilder:object:generate=true
+// +groupName=keda.sh
+package v1alpha1
diff --git a/addons/keda/duck/v1alpha1/duck_types.go 
b/addons/keda/duck/v1alpha1/duck_types.go
new file mode 100644
index 0000000..8504b6c
--- /dev/null
+++ b/addons/keda/duck/v1alpha1/duck_types.go
@@ -0,0 +1,108 @@
+/*
+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 v1alpha1
+
+import (
+       v1 "k8s.io/api/core/v1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// +genclient
+// +genclient:onlyVerbs=get,list,watch
+// +genclient:noStatus
+// +kubebuilder:object:root=true
+
+// ScaledObject is a specification for a ScaledObject resource
+type ScaledObject struct {
+       metav1.TypeMeta   `json:",inline"`
+       metav1.ObjectMeta `json:"metadata,omitempty"`
+
+       Spec ScaledObjectSpec `json:"spec"`
+}
+
+// ScaledObjectSpec is the spec for a ScaledObject resource
+type ScaledObjectSpec struct {
+       ScaleTargetRef *v1.ObjectReference `json:"scaleTargetRef"`
+
+       Triggers []ScaleTriggers `json:"triggers"`
+}
+
+// ScaleTriggers reference the scaler that will be used
+type ScaleTriggers struct {
+       Type string `json:"type"`
+       // +optional
+       Name     string            `json:"name,omitempty"`
+       Metadata map[string]string `json:"metadata"`
+       // +optional
+       AuthenticationRef *ScaledObjectAuthRef 
`json:"authenticationRef,omitempty"`
+       // +optional
+       FallbackReplicas *int32 `json:"fallback,omitempty"`
+}
+
+// ScaledObjectAuthRef points to the TriggerAuthentication or 
ClusterTriggerAuthentication object that
+// is used to authenticate the scaler with the environment
+type ScaledObjectAuthRef struct {
+       Name string `json:"name"`
+       // Kind of the resource being referred to. Defaults to 
TriggerAuthentication.
+       // +optional
+       Kind string `json:"kind,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+
+// ScaledObjectList contains a list of ScaledObject.
+type ScaledObjectList struct {
+       metav1.TypeMeta `json:",inline"`
+       metav1.ListMeta `json:"metadata,omitempty"`
+       Items           []ScaledObject `json:"items"`
+}
+
+// +genclient
+// +genclient:onlyVerbs=get,list,watch
+// +genclient:noStatus
+// +kubebuilder:object:root=true
+
+// TriggerAuthentication defines how a trigger can authenticate
+type TriggerAuthentication struct {
+       metav1.TypeMeta   `json:",inline"`
+       metav1.ObjectMeta `json:"metadata,omitempty"`
+
+       Spec TriggerAuthenticationSpec `json:"spec"`
+}
+
+// TriggerAuthenticationSpec defines the various ways to authenticate
+type TriggerAuthenticationSpec struct {
+       // +optional
+       SecretTargetRef []AuthSecretTargetRef `json:"secretTargetRef,omitempty"`
+}
+
+// AuthSecretTargetRef is used to authenticate using a reference to a secret
+type AuthSecretTargetRef struct {
+       Parameter string `json:"parameter"`
+       Name      string `json:"name"`
+       Key       string `json:"key"`
+}
+
+// +kubebuilder:object:root=true
+
+// TriggerAuthenticationList contains a list of TriggerAuthentication
+type TriggerAuthenticationList struct {
+       metav1.TypeMeta `json:",inline"`
+       metav1.ListMeta `json:"metadata"`
+       Items           []TriggerAuthentication `json:"items"`
+}
diff --git a/addons/keda/keda.go 
b/addons/keda/duck/v1alpha1/duck_types_support.go
similarity index 50%
copy from addons/keda/keda.go
copy to addons/keda/duck/v1alpha1/duck_types_support.go
index c794249..c8c2f23 100644
--- a/addons/keda/keda.go
+++ b/addons/keda/duck/v1alpha1/duck_types_support.go
@@ -15,37 +15,25 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package keda
+package v1alpha1
 
 import (
-       "github.com/apache/camel-k/pkg/trait"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
-// The Keda trait can be used for automatic integration with Keda autoscalers.
-//
-// The Keda trait is disabled by default.
-//
-// +camel-k:trait=keda.
-type kedaTrait struct {
-       trait.BaseTrait `property:",squash"`
-       // Enables automatic configuration of the trait.
-       Auto *bool `property:"auto" json:"auto,omitempty"`
-       // Metadata
-       Metadata map[string]string `property:"metadata" 
json:"metadata,omitempty"`
-}
+const (
+       ScaledObjectKind = "ScaledObject"
+)
 
-// NewKedaTrait --.
-func NewKedaTrait() trait.Trait {
-       return &kedaTrait{
-               BaseTrait: trait.NewBaseTrait("keda", 
trait.TraitOrderPostProcessResources),
+func NewScaledObject(namespace string, name string) ScaledObject {
+       return ScaledObject{
+               TypeMeta: metav1.TypeMeta{
+                       APIVersion: SchemeGroupVersion.String(),
+                       Kind:       ScaledObjectKind,
+               },
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: namespace,
+                       Name:      name,
+               },
        }
 }
-
-func (t *kedaTrait) Configure(e *trait.Environment) (bool, error) {
-
-       return false, nil
-}
-
-func (t *kedaTrait) Apply(e *trait.Environment) error {
-       return nil
-}
diff --git a/addons/keda/duck/v1alpha1/register.go 
b/addons/keda/duck/v1alpha1/register.go
new file mode 100644
index 0000000..a3814da
--- /dev/null
+++ b/addons/keda/duck/v1alpha1/register.go
@@ -0,0 +1,57 @@
+/*
+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 v1alpha1
+
+import (
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/apimachinery/pkg/runtime"
+       "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+const (
+       KedaGroup   = "keda.sh"
+       KedaVersion = "v1alpha1"
+)
+
+var (
+       // SchemeGroupVersion is group version used to register these objects.
+       SchemeGroupVersion = schema.GroupVersion{Group: KedaGroup, Version: 
KedaVersion}
+
+       // SchemeBuilder is used to add go types to the GroupVersionKind scheme.
+       SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
+
+       // AddToScheme is a shortcut to SchemeBuilder.AddToScheme.
+       AddToScheme = SchemeBuilder.AddToScheme
+)
+
+// Resource takes an unqualified resource and returns a Group qualified 
GroupResource.
+func Resource(resource string) schema.GroupResource {
+       return SchemeGroupVersion.WithResource(resource).GroupResource()
+}
+
+// Adds the list of known types to Scheme.
+func addKnownTypes(scheme *runtime.Scheme) error {
+       scheme.AddKnownTypes(SchemeGroupVersion,
+               &ScaledObject{},
+               &ScaledObjectList{},
+               &TriggerAuthentication{},
+               &TriggerAuthenticationList{},
+       )
+       metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
+       return nil
+}
diff --git a/addons/keda/duck/v1alpha1/zz_generated.deepcopy.go 
b/addons/keda/duck/v1alpha1/zz_generated.deepcopy.go
new file mode 100644
index 0000000..b551c7f
--- /dev/null
+++ b/addons/keda/duck/v1alpha1/zz_generated.deepcopy.go
@@ -0,0 +1,235 @@
+// +build !ignore_autogenerated
+
+// Code generated by controller-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+       "k8s.io/api/core/v1"
+       "k8s.io/apimachinery/pkg/runtime"
+)
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *AuthSecretTargetRef) DeepCopyInto(out *AuthSecretTargetRef) {
+       *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new AuthSecretTargetRef.
+func (in *AuthSecretTargetRef) DeepCopy() *AuthSecretTargetRef {
+       if in == nil {
+               return nil
+       }
+       out := new(AuthSecretTargetRef)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *ScaleTriggers) DeepCopyInto(out *ScaleTriggers) {
+       *out = *in
+       if in.Metadata != nil {
+               in, out := &in.Metadata, &out.Metadata
+               *out = make(map[string]string, len(*in))
+               for key, val := range *in {
+                       (*out)[key] = val
+               }
+       }
+       if in.AuthenticationRef != nil {
+               in, out := &in.AuthenticationRef, &out.AuthenticationRef
+               *out = new(ScaledObjectAuthRef)
+               **out = **in
+       }
+       if in.FallbackReplicas != nil {
+               in, out := &in.FallbackReplicas, &out.FallbackReplicas
+               *out = new(int32)
+               **out = **in
+       }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new ScaleTriggers.
+func (in *ScaleTriggers) DeepCopy() *ScaleTriggers {
+       if in == nil {
+               return nil
+       }
+       out := new(ScaleTriggers)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *ScaledObject) DeepCopyInto(out *ScaledObject) {
+       *out = *in
+       out.TypeMeta = in.TypeMeta
+       in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+       in.Spec.DeepCopyInto(&out.Spec)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new ScaledObject.
+func (in *ScaledObject) DeepCopy() *ScaledObject {
+       if in == nil {
+               return nil
+       }
+       out := new(ScaledObject)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, 
creating a new runtime.Object.
+func (in *ScaledObject) DeepCopyObject() runtime.Object {
+       if c := in.DeepCopy(); c != nil {
+               return c
+       }
+       return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *ScaledObjectAuthRef) DeepCopyInto(out *ScaledObjectAuthRef) {
+       *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new ScaledObjectAuthRef.
+func (in *ScaledObjectAuthRef) DeepCopy() *ScaledObjectAuthRef {
+       if in == nil {
+               return nil
+       }
+       out := new(ScaledObjectAuthRef)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *ScaledObjectList) DeepCopyInto(out *ScaledObjectList) {
+       *out = *in
+       out.TypeMeta = in.TypeMeta
+       in.ListMeta.DeepCopyInto(&out.ListMeta)
+       if in.Items != nil {
+               in, out := &in.Items, &out.Items
+               *out = make([]ScaledObject, len(*in))
+               for i := range *in {
+                       (*in)[i].DeepCopyInto(&(*out)[i])
+               }
+       }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new ScaledObjectList.
+func (in *ScaledObjectList) DeepCopy() *ScaledObjectList {
+       if in == nil {
+               return nil
+       }
+       out := new(ScaledObjectList)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, 
creating a new runtime.Object.
+func (in *ScaledObjectList) DeepCopyObject() runtime.Object {
+       if c := in.DeepCopy(); c != nil {
+               return c
+       }
+       return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *ScaledObjectSpec) DeepCopyInto(out *ScaledObjectSpec) {
+       *out = *in
+       if in.ScaleTargetRef != nil {
+               in, out := &in.ScaleTargetRef, &out.ScaleTargetRef
+               *out = new(v1.ObjectReference)
+               **out = **in
+       }
+       if in.Triggers != nil {
+               in, out := &in.Triggers, &out.Triggers
+               *out = make([]ScaleTriggers, len(*in))
+               for i := range *in {
+                       (*in)[i].DeepCopyInto(&(*out)[i])
+               }
+       }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new ScaledObjectSpec.
+func (in *ScaledObjectSpec) DeepCopy() *ScaledObjectSpec {
+       if in == nil {
+               return nil
+       }
+       out := new(ScaledObjectSpec)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *TriggerAuthentication) DeepCopyInto(out *TriggerAuthentication) {
+       *out = *in
+       out.TypeMeta = in.TypeMeta
+       in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+       in.Spec.DeepCopyInto(&out.Spec)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new TriggerAuthentication.
+func (in *TriggerAuthentication) DeepCopy() *TriggerAuthentication {
+       if in == nil {
+               return nil
+       }
+       out := new(TriggerAuthentication)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, 
creating a new runtime.Object.
+func (in *TriggerAuthentication) DeepCopyObject() runtime.Object {
+       if c := in.DeepCopy(); c != nil {
+               return c
+       }
+       return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *TriggerAuthenticationList) DeepCopyInto(out 
*TriggerAuthenticationList) {
+       *out = *in
+       out.TypeMeta = in.TypeMeta
+       in.ListMeta.DeepCopyInto(&out.ListMeta)
+       if in.Items != nil {
+               in, out := &in.Items, &out.Items
+               *out = make([]TriggerAuthentication, len(*in))
+               for i := range *in {
+                       (*in)[i].DeepCopyInto(&(*out)[i])
+               }
+       }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new TriggerAuthenticationList.
+func (in *TriggerAuthenticationList) DeepCopy() *TriggerAuthenticationList {
+       if in == nil {
+               return nil
+       }
+       out := new(TriggerAuthenticationList)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, 
creating a new runtime.Object.
+func (in *TriggerAuthenticationList) DeepCopyObject() runtime.Object {
+       if c := in.DeepCopy(); c != nil {
+               return c
+       }
+       return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *TriggerAuthenticationSpec) DeepCopyInto(out 
*TriggerAuthenticationSpec) {
+       *out = *in
+       if in.SecretTargetRef != nil {
+               in, out := &in.SecretTargetRef, &out.SecretTargetRef
+               *out = make([]AuthSecretTargetRef, len(*in))
+               copy(*out, *in)
+       }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new TriggerAuthenticationSpec.
+func (in *TriggerAuthenticationSpec) DeepCopy() *TriggerAuthenticationSpec {
+       if in == nil {
+               return nil
+       }
+       out := new(TriggerAuthenticationSpec)
+       in.DeepCopyInto(out)
+       return out
+}
diff --git a/addons/keda/keda.go b/addons/keda/keda.go
index c794249..65a8bd4 100644
--- a/addons/keda/keda.go
+++ b/addons/keda/keda.go
@@ -18,7 +18,16 @@ limitations under the License.
 package keda
 
 import (
+       "strings"
+
+       kedav1alpha1 "github.com/apache/camel-k/addons/keda/duck/v1alpha1"
+       camelv1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+       "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+       camelv1alpha1 "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
        "github.com/apache/camel-k/pkg/trait"
+       scase "github.com/stoewer/go-strcase"
+       v1 "k8s.io/api/core/v1"
+       "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
 // The Keda trait can be used for automatic integration with Keda autoscalers.
@@ -30,7 +39,16 @@ type kedaTrait struct {
        trait.BaseTrait `property:",squash"`
        // Enables automatic configuration of the trait.
        Auto *bool `property:"auto" json:"auto,omitempty"`
-       // Metadata
+       // Convert metadata properties to camelCase (needed because trait 
properties use kebab-case). Enabled by default.
+       CamelCaseConversion *bool `property:"camel-case-conversion" 
json:"camelCaseConversion,omitempty"`
+       // Set the spec->replicas field on the top level controller to an 
explicit value if missing, to allow Keda to recognize it as a scalable resource
+       HackControllerReplicas *bool `property:"hack-controller-replicas" 
json:"hackControllerReplicas,omitempty"`
+       // Triggers
+       Triggers []kedaTrigger `property:"triggers" json:"triggers,omitempty"`
+}
+
+type kedaTrigger struct {
+       Type     string            `property:"type" json:"type,omitempty"`
        Metadata map[string]string `property:"metadata" 
json:"metadata,omitempty"`
 }
 
@@ -42,10 +60,106 @@ func NewKedaTrait() trait.Trait {
 }
 
 func (t *kedaTrait) Configure(e *trait.Environment) (bool, error) {
+       if t.Enabled == nil || !*t.Enabled {
+               return false, nil
+       }
+
+       if !e.IntegrationInPhase(camelv1.IntegrationPhaseInitialization) && 
!e.IntegrationInRunningPhases() {
+               return false, nil
+       }
 
-       return false, nil
+       return true, nil
 }
 
 func (t *kedaTrait) Apply(e *trait.Environment) error {
+       if e.IntegrationInPhase(camelv1.IntegrationPhaseInitialization) {
+               if t.HackControllerReplicas == nil || *t.HackControllerReplicas 
{
+                       if err := t.hackControllerReplicas(e); err != nil {
+                               return err
+                       }
+               }
+       } else if e.IntegrationInRunningPhases() {
+               if so, err := t.getScaledObject(e); err != nil {
+                       return err
+               } else if so != nil {
+                       e.Resources.Add(so)
+               }
+       }
+
        return nil
 }
+
+func (t *kedaTrait) getScaledObject(e *trait.Environment) 
(*kedav1alpha1.ScaledObject, error) {
+       if len(t.Triggers) == 0 {
+               return nil, nil
+       }
+       obj := kedav1alpha1.NewScaledObject(e.Integration.Namespace, 
e.Integration.Name)
+       obj.Spec.ScaleTargetRef = t.getTopControllerReference(e)
+
+       for _, trigger := range t.Triggers {
+               meta := make(map[string]string)
+               for k, v := range trigger.Metadata {
+                       kk := k
+                       if t.CamelCaseConversion == nil || 
*t.CamelCaseConversion {
+                               kk = scase.LowerCamelCase(k)
+                       }
+                       meta[kk] = v
+               }
+               st := kedav1alpha1.ScaleTriggers{
+                       Type:     trigger.Type,
+                       Metadata: meta,
+               }
+               obj.Spec.Triggers = append(obj.Spec.Triggers, st)
+       }
+
+       return &obj, nil
+}
+
+func (t *kedaTrait) hackControllerReplicas(e *trait.Environment) error {
+       ctrlRef := t.getTopControllerReference(e)
+
+       if ctrlRef.Kind == camelv1alpha1.KameletBindingKind {
+               // Update the KameletBinding directly (do not add it to env 
resources, it's the integration parent)
+               key := client.ObjectKey{
+                       Namespace: e.Integration.Namespace,
+                       Name:      ctrlRef.Name,
+               }
+               klb := camelv1alpha1.KameletBinding{}
+               if err := e.Client.Get(e.Ctx, key, &klb); err != nil {
+                       return err
+               }
+               if klb.Spec.Replicas == nil {
+                       one := int32(1)
+                       klb.Spec.Replicas = &one
+                       if err := e.Client.Update(e.Ctx, &klb); err != nil {
+                               return err
+                       }
+               }
+       } else {
+               if e.Integration.Spec.Replicas == nil {
+                       one := int32(1)
+                       e.Integration.Spec.Replicas = &one
+                       if err := e.Client.Update(e.Ctx, e.Integration); err != 
nil {
+                               return err
+                       }
+               }
+       }
+       return nil
+}
+
+func (t *kedaTrait) getTopControllerReference(e *trait.Environment) 
*v1.ObjectReference {
+       for _, o := range e.Integration.OwnerReferences {
+               if o.Kind == v1alpha1.KameletBindingKind && 
strings.HasPrefix(o.APIVersion, v1alpha1.SchemeGroupVersion.Group) {
+                       return &v1.ObjectReference{
+                               APIVersion: o.APIVersion,
+                               Kind:       o.Kind,
+                               Name:       o.Name,
+                       }
+               }
+       }
+       return &v1.ObjectReference{
+               APIVersion: e.Integration.APIVersion,
+               Kind:       e.Integration.Kind,
+               Name:       e.Integration.Name,
+       }
+}
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index 593ab84..890e733 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -63,6 +63,7 @@
 ** xref:traits:jolokia.adoc[Jolokia]
 ** xref:traits:jvm.adoc[Jvm]
 ** xref:traits:kamelets.adoc[Kamelets]
+** xref:traits:keda.adoc[Keda]
 ** xref:traits:knative-service.adoc[Knative Service]
 ** xref:traits:knative.adoc[Knative]
 ** xref:traits:logging.adoc[Logging]
diff --git a/docs/modules/traits/pages/keda.adoc 
b/docs/modules/traits/pages/keda.adoc
new file mode 100644
index 0000000..a73dabd
--- /dev/null
+++ b/docs/modules/traits/pages/keda.adoc
@@ -0,0 +1,44 @@
+= Keda Trait
+
+// Start of autogenerated code - DO NOT EDIT! (description)
+The Keda trait can be used for automatic integration with Keda autoscalers.
+
+The Keda trait is disabled by default.
+
+
+This trait is available in the following profiles: **Kubernetes, Knative, 
OpenShift**.
+
+// End of autogenerated code - DO NOT EDIT! (description)
+// Start of autogenerated code - DO NOT EDIT! (configuration)
+== Configuration
+
+Trait properties can be specified when running any integration with the CLI:
+[source,console]
+----
+$ kamel run --trait keda.[key]=[value] --trait keda.[key2]=[value2] 
integration.groovy
+----
+The following configuration options are available:
+
+[cols="2m,1m,5a"]
+|===
+|Property | Type | Description
+
+| keda.enabled
+| bool
+| Can be used to enable or disable a trait. All traits share this common 
property.
+
+| keda.auto
+| bool
+| Enables automatic configuration of the trait.
+
+| keda.camel-case-conversion
+| bool
+| Convert metadata properties to camelCase (needed because trait properties 
use kebab-case). Enabled by default.
+
+| keda.triggers
+| []github.com/apache/camel-k/addons/keda.kedaTrigger
+| Triggers
+
+|===
+
+// End of autogenerated code - DO NOT EDIT! (configuration)
diff --git a/go.sum b/go.sum
index a7e63c9..9aaf745 100644
--- a/go.sum
+++ b/go.sum
@@ -1803,6 +1803,7 @@ k8s.io/code-generator v0.18.2/go.mod 
h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRV
 k8s.io/code-generator v0.18.6/go.mod 
h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
 k8s.io/code-generator v0.19.2/go.mod 
h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk=
 k8s.io/code-generator v0.21.1/go.mod 
h1:hUlps5+9QaTrKx+jiM4rmq7YmH8wPOIko64uZCHDh6Q=
+k8s.io/code-generator v0.21.4 h1:vO8jVuEGV4UF+/2s/88Qg05MokE/1QUFi/Q2YDgz++A=
 k8s.io/code-generator v0.21.4/go.mod 
h1:K3y0Bv9Cz2cOW2vXUrNZlFbflhuPvuadW6JdnN6gGKo=
 k8s.io/component-base v0.18.2/go.mod 
h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM=
 k8s.io/component-base v0.18.6/go.mod 
h1:knSVsibPR5K6EW2XOjEHik6sdU5nCvKMrzMt2D4In14=
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 5dfca09..1819405 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -292,8 +292,11 @@ func (o *runCmdOptions) run(cmd *cobra.Command, args 
[]string) error {
        tp := catalog.ComputeTraitsProperties()
        for _, t := range o.Traits {
                kv := strings.SplitN(t, "=", 2)
-
-               if !util.StringSliceExists(tp, kv[0]) {
+               prefix := kv[0]
+               if strings.Contains(prefix, "[") {
+                       prefix = prefix[0:strings.Index(prefix, "[")]
+               }
+               if !util.StringSliceExists(tp, prefix) {
                        fmt.Printf("Error: %s is not a valid trait property\n", 
t)
                        return nil
                }
diff --git a/pkg/resources/resources.go b/pkg/resources/resources.go
index b798a6a..a979664 100644
--- a/pkg/resources/resources.go
+++ b/pkg/resources/resources.go
@@ -541,9 +541,9 @@ var assets = func() http.FileSystem {
                "/traits.yaml": &vfsgen۰CompressedFileInfo{
                        name:             "traits.yaml",
                        modTime:          time.Time{},
-                       uncompressedSize: 46896,
+                       uncompressedSize: 47743,
 
-                       compressedContent: 
[]byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\xfd\x73\x1c\xb7\xb1\xe0\xef\xfe\x2b\x50\x7c\x57\x25\x92\xb5\xbb\x94\x9d\x97\x3c\x1f\xef\x74\x29\x5a\x92\x13\xda\xfa\xe0\x49\xb2\x73\x29\x9f\x2b\x8b\x9d\xe9\xdd\x85\x38\x0b\x4c\x00\x0c\xa9\xcd\xbd\xfb\xdf\xaf\xd0\xdd\xf8\x98\xd9\x5d\x72\x29\x91\x7e\xe1\xd5\x4b\x7e\xb0\x48\x0e\x80\x46\xa3\xd1\xdf\xdd\xf0\x56\x2a\xef\x4e\xbf\x1a\x0b\x2d\x57\x70\x2a\xe4\x7c\xae\xb4\xf2\xeb\xaf\x84\x68\x1b\xe9\xe7\xc6\xae\x4e\xc5\x
 [...]
+                       compressedContent: 
[]byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\xfd\x73\x1c\xb7\xb1\xe0\xef\xfe\x2b\x50\x7c\x57\x25\x92\xb5\xbb\x94\x9d\x97\xc4\xc7\x3b\x5d\x8a\x96\xe4\x98\xb6\x3e\x78\x92\xec\x5c\x4a\xe7\xca\x62\x67\x7a\x77\x21\x62\x80\x09\x80\x21\xb5\xb9\x77\xff\xfb\x2b\x74\xe3\x6b\x66\x77\xc9\xa1\x24\xfa\x85\x55\x79\xa9\x7a\x16\xc9\x01\xd0\xdd\x68\x34\xfa\x1b\xce\x70\xe1\xec\xe9\x57\x53\xa6\x78\x03\xa7\x8c\x2f\x97\x42\x09\xb7\xf9\x8a\xb1\x56\x72\xb7\xd4\xa6\x39\x65\x4b\x
 [...]
                },
        }
        fs["/"].(*vfsgen۰DirInfo).entries = []os.FileInfo{
diff --git a/resources/traits.yaml b/resources/traits.yaml
index 2b66ec4..7bd54bf 100755
--- a/resources/traits.yaml
+++ b/resources/traits.yaml
@@ -570,6 +570,29 @@ traits:
   - name: list
     type: string
     description: Comma separated list of Kamelet names to load into the 
current integration
+- name: keda
+  platform: false
+  profiles:
+  - Kubernetes
+  - Knative
+  - OpenShift
+  description: The Keda trait can be used for automatic integration with Keda 
autoscalers.
+    The Keda trait is disabled by default.
+  properties:
+  - name: enabled
+    type: bool
+    description: Can be used to enable or disable a trait. All traits share 
this common
+      property.
+  - name: auto
+    type: bool
+    description: Enables automatic configuration of the trait.
+  - name: camel-case-conversion
+    type: bool
+    description: Convert metadata properties to camelCase (needed because 
trait properties
+      use kebab-case). Enabled by default.
+  - name: triggers
+    type: '[]github.com/apache/camel-k/addons/keda.kedaTrigger'
+    description: Triggers
 - name: knative-service
   platform: false
   profiles:
diff --git a/script/Makefile b/script/Makefile
index be57f3b..af03077 100644
--- a/script/Makefile
+++ b/script/Makefile
@@ -155,7 +155,7 @@ codegen:
 
        gofmt -w pkg/util/defaults/defaults.go
 
-generate: generate-deepcopy generate-crd generate-client generate-doc 
generate-json-schema generate-strimzi
+generate: generate-deepcopy generate-crd generate-client generate-doc 
generate-json-schema generate-keda generate-strimzi
 
 generate-client:
        ./script/gen_client.sh
@@ -173,6 +173,10 @@ generate-json-schema:
        # Skip since the YAML DSL schema has been moved to apache/camel
        #./script/gen_json_schema.sh $(RUNTIME_VERSION) $(STAGING_RUNTIME_REPO)
 
+generate-keda:
+       cd addons/keda/duck && $(CONTROLLER_GEN) paths="./..." object
+       ./script/gen_client_keda.sh
+
 generate-strimzi:
        cd addons/strimzi/duck && $(CONTROLLER_GEN) paths="./..." object
        ./script/gen_client_strimzi.sh
@@ -359,7 +363,7 @@ install-minikube:
 get-staging-repo:
        @echo $(or 
${STAGING_RUNTIME_REPO},https://repository.apache.org/content/repositories/snapshots@id=apache-snapshots@snapshots)
 
-.PHONY: build build-kamel build-resources dep codegen images images-dev 
images-push images-push-staging test check test-integration clean release 
cross-compile package-examples set-version git-tag release-notes check-licenses 
generate-deepcopy generate-client generate-doc build-resources release-helm 
release-staging release-nightly get-staging-repo get-version build-submodules 
set-module-version bundle-kamelets generate-strimzi
+.PHONY: build build-kamel build-resources dep codegen images images-dev 
images-push images-push-staging test check test-integration clean release 
cross-compile package-examples set-version git-tag release-notes check-licenses 
generate-deepcopy generate-client generate-doc build-resources release-helm 
release-staging release-nightly get-staging-repo get-version build-submodules 
set-module-version bundle-kamelets generate-keda generate-strimzi
 
 # find or download controller-gen if necessary
 controller-gen:
diff --git a/script/gen_doc.sh b/script/gen_client_keda.sh
similarity index 70%
copy from script/gen_doc.sh
copy to script/gen_client_keda.sh
index d4d6aab..e5dd2ca 100755
--- a/script/gen_doc.sh
+++ b/script/gen_client_keda.sh
@@ -15,14 +15,18 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+set -e
+
 location=$(dirname $0)
 rootdir=$location/..
 
-echo "Generating API documentation..."
-$location/gen_crd/gen_crd_api.sh
-echo "Generating API documentation... done!"
+unset GOPATH
+GO111MODULE=on
+
+echo "Generating boilerplate code for Keda addon..."
 
-echo "Generating traits documentation..."
 cd $rootdir
-go run ./cmd/util/doc-gen --input-dirs ./pkg/trait --input-dirs 
./addons/master --input-dirs ./addons/threescale --input-dirs ./addons/tracing
-echo "Generating traits documentation... done!"
+
+go run k8s.io/code-generator/cmd/deepcopy-gen \
+  -h ./script/headers/default.txt \
+  --input-dirs=github.com/apache/camel-k/addons/keda
diff --git a/script/gen_doc.sh b/script/gen_doc.sh
index d4d6aab..028ec44 100755
--- a/script/gen_doc.sh
+++ b/script/gen_doc.sh
@@ -24,5 +24,5 @@ echo "Generating API documentation... done!"
 
 echo "Generating traits documentation..."
 cd $rootdir
-go run ./cmd/util/doc-gen --input-dirs ./pkg/trait --input-dirs 
./addons/master --input-dirs ./addons/threescale --input-dirs ./addons/tracing
+go run ./cmd/util/doc-gen --input-dirs github.com/apache/camel-k/pkg/trait 
--input-dirs github.com/apache/camel-k/addons/keda --input-dirs 
github.com/apache/camel-k/addons/master --input-dirs 
github.com/apache/camel-k/addons/threescale --input-dirs 
github.com/apache/camel-k/addons/tracing
 echo "Generating traits documentation... done!"

Reply via email to