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 9d89922951da6dcfc258d7fad097e7e47b78a4dc
Author: nicolaferraro <ni.ferr...@gmail.com>
AuthorDate: Wed Dec 22 00:54:16 2021 +0100

    Fix #1107: fix findings
---
 addons/keda/keda.go                    |  43 ++++++-------
 addons/keda/keda_test.go               |  61 ++++++++++++++++---
 docs/modules/traits/pages/keda.adoc    |   4 --
 e2e/common/scale_binding_test.go       |  11 +---
 e2e/common/scale_integration_test.go   |  11 +---
 pkg/client/{serverside.go => apply.go} |   0
 pkg/client/client.go                   |   2 +
 pkg/client/scale.go                    |  35 +++++++++++
 pkg/cmd/run.go                         |   2 +-
 pkg/resources/resources.go             |   4 +-
 pkg/trait/deployer.go                  | 108 +--------------------------------
 pkg/util/test/client.go                |  45 +++++++++++++-
 resources/traits.yaml                  |   4 --
 13 files changed, 157 insertions(+), 173 deletions(-)

diff --git a/addons/keda/keda.go b/addons/keda/keda.go
index ad9f71d..e6e1d5e 100644
--- a/addons/keda/keda.go
+++ b/addons/keda/keda.go
@@ -38,7 +38,7 @@ import (
        "github.com/apache/camel-k/pkg/util/source"
        "github.com/apache/camel-k/pkg/util/uri"
        "github.com/pkg/errors"
-       scase "github.com/stoewer/go-strcase"
+       autoscalingv1 "k8s.io/api/autoscaling/v1"
        v1 "k8s.io/api/core/v1"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        ctrl "sigs.k8s.io/controller-runtime/pkg/client"
@@ -76,8 +76,6 @@ type kedaTrait struct {
        trait.BaseTrait `property:",squash"`
        // Enables automatic configuration of the trait. Allows the trait to 
infer KEDA triggers from the Kamelets.
        Auto *bool `property:"auto" json:"auto,omitempty"`
-       // Convert metadata properties to camelCase (needed because Camel K 
trait properties use kebab-case from command line). Disabled 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"`
        // Interval (seconds) to check each trigger on.
@@ -170,11 +168,7 @@ func (t *kedaTrait) addScalingResources(e 
*trait.Environment) error {
        for idx, 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
+                       meta[k] = v
                }
                var authenticationRef *kedav1alpha1.ScaledObjectAuthRef
                if len(trigger.authentication) > 0 && 
trigger.AuthenticationSecret != "" {
@@ -269,28 +263,25 @@ func (t *kedaTrait) addScalingResources(e 
*trait.Environment) error {
 
 func (t *kedaTrait) hackControllerReplicas(e *trait.Environment) error {
        ctrlRef := t.getTopControllerReference(e)
+       scale := autoscalingv1.Scale{
+               Spec: autoscalingv1.ScaleSpec{
+                       Replicas: int32(1),
+               },
+       }
+       scalesClient, err := e.Client.ScalesClient()
+       if err != nil {
+               return err
+       }
        if ctrlRef.Kind == camelv1alpha1.KameletBindingKind {
-               // Update the KameletBinding directly (do not add it to env 
resources, it's the integration parent)
-               key := ctrl.ObjectKey{
-                       Namespace: e.Integration.Namespace,
-                       Name:      ctrlRef.Name,
-               }
-               klb := camelv1alpha1.KameletBinding{}
-               if err := e.Client.Get(e.Ctx, key, &klb); err != nil {
+               scale.ObjectMeta.Name = ctrlRef.Name
+               _, err = 
scalesClient.Scales(e.Integration.Namespace).Update(e.Ctx, 
camelv1alpha1.SchemeGroupVersion.WithResource("kameletbindings").GroupResource(),
 &scale, metav1.UpdateOptions{})
+               if 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
-               // Update the Integration directly as the spec section is not 
merged by default
-               if err := e.Client.Update(e.Ctx, e.Integration); err != nil {
+               scale.ObjectMeta.Name = e.Integration.Name
+               _, err = 
scalesClient.Scales(e.Integration.Namespace).Update(e.Ctx, 
camelv1.SchemeGroupVersion.WithResource("integrations").GroupResource(), 
&scale, metav1.UpdateOptions{})
+               if err != nil {
                        return err
                }
        }
diff --git a/addons/keda/keda_test.go b/addons/keda/keda_test.go
index 08a627e..4783653 100644
--- a/addons/keda/keda_test.go
+++ b/addons/keda/keda_test.go
@@ -35,7 +35,6 @@ import (
        corev1 "k8s.io/api/core/v1"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "k8s.io/apimachinery/pkg/runtime"
-       "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
 var (
@@ -348,14 +347,58 @@ func TestHackReplicas(t *testing.T) {
        assert.NoError(t, err)
        assert.True(t, res)
        assert.NoError(t, keda.Apply(env))
-       it := camelv1.Integration{}
-       key := client.ObjectKey{
-               Namespace: "test",
-               Name:      "my-it",
-       }
-       assert.NoError(t, env.Client.Get(env.Ctx, key, &it))
-       assert.NotNil(t, it.Spec.Replicas)
-       assert.Equal(t, int32(1), *it.Spec.Replicas)
+       scalesClient, err := env.Client.ScalesClient()
+       assert.NoError(t, err)
+       sc, err := scalesClient.Scales("test").Get(env.Ctx, 
camelv1.SchemeGroupVersion.WithResource("integrations").GroupResource(), 
"my-it", metav1.GetOptions{})
+       assert.NoError(t, err)
+       assert.Equal(t, int32(1), sc.Spec.Replicas)
+}
+
+func TestHackKLBReplicas(t *testing.T) {
+       keda, _ := NewKedaTrait().(*kedaTrait)
+       keda.Enabled = &testingTrue
+       keda.Auto = &testingFalse
+       keda.Triggers = append(keda.Triggers, kedaTrigger{
+               Type: "custom",
+               Metadata: map[string]string{
+                       "a": "b",
+               },
+       })
+       keda.HackControllerReplicas = &testingTrue
+       env := createBasicTestEnvironment(
+               &camelv1alpha1.KameletBinding{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Namespace: "test",
+                               Name:      "my-klb",
+                       },
+               },
+               &camelv1.Integration{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Namespace: "test",
+                               Name:      "my-it",
+                               OwnerReferences: []metav1.OwnerReference{
+                                       {
+                                               APIVersion: 
camelv1alpha1.SchemeGroupVersion.String(),
+                                               Kind:       "KameletBinding",
+                                               Name:       "my-klb",
+                                       },
+                               },
+                       },
+                       Status: camelv1.IntegrationStatus{
+                               Phase: camelv1.IntegrationPhaseInitialization,
+                       },
+               },
+       )
+
+       res, err := keda.Configure(env)
+       assert.NoError(t, err)
+       assert.True(t, res)
+       assert.NoError(t, keda.Apply(env))
+       scalesClient, err := env.Client.ScalesClient()
+       assert.NoError(t, err)
+       sc, err := scalesClient.Scales("test").Get(env.Ctx, 
camelv1alpha1.SchemeGroupVersion.WithResource("kameletbindings").GroupResource(),
 "my-klb", metav1.GetOptions{})
+       assert.NoError(t, err)
+       assert.Equal(t, int32(1), sc.Spec.Replicas)
 }
 
 func getScaledObject(e *trait.Environment) *v1alpha1.ScaledObject {
diff --git a/docs/modules/traits/pages/keda.adoc 
b/docs/modules/traits/pages/keda.adoc
index df6c8d9..b1a5827 100644
--- a/docs/modules/traits/pages/keda.adoc
+++ b/docs/modules/traits/pages/keda.adoc
@@ -38,10 +38,6 @@ The following configuration options are available:
 | bool
 | Enables automatic configuration of the trait. Allows the trait to infer KEDA 
triggers from the Kamelets.
 
-| keda.camel-case-conversion
-| bool
-| Convert metadata properties to camelCase (needed because Camel K trait 
properties use kebab-case from command line). Disabled by default.
-
 | keda.hack-controller-replicas
 | bool
 | 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.
diff --git a/e2e/common/scale_binding_test.go b/e2e/common/scale_binding_test.go
index 49d9668..3bbc2ee 100644
--- a/e2e/common/scale_binding_test.go
+++ b/e2e/common/scale_binding_test.go
@@ -33,10 +33,6 @@ import (
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "k8s.io/apimachinery/pkg/types"
 
-       "k8s.io/client-go/dynamic"
-       "k8s.io/client-go/restmapper"
-       "k8s.io/client-go/scale"
-
        . "github.com/apache/camel-k/e2e/support"
        v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
        "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
@@ -80,12 +76,7 @@ func TestKameletBindingScale(t *testing.T) {
 
                t.Run("Scale kamelet binding with polymorphic client", func(t 
*testing.T) {
                        RegisterTestingT(t)
-                       // Polymorphic scale client
-                       groupResources, err := 
restmapper.GetAPIGroupResources(TestClient().Discovery())
-                       Expect(err).To(BeNil())
-                       mapper := 
restmapper.NewDiscoveryRESTMapper(groupResources)
-                       resolver := 
scale.NewDiscoveryScaleKindResolver(TestClient().Discovery())
-                       scaleClient, err := 
scale.NewForConfig(TestClient().GetConfig(), mapper, 
dynamic.LegacyAPIPathResolverFunc, resolver)
+                       scaleClient, err := TestClient().ScalesClient()
                        Expect(err).To(BeNil())
 
                        // Patch the integration scale subresource
diff --git a/e2e/common/scale_integration_test.go 
b/e2e/common/scale_integration_test.go
index 5c70901..1c01d1c 100644
--- a/e2e/common/scale_integration_test.go
+++ b/e2e/common/scale_integration_test.go
@@ -33,10 +33,6 @@ import (
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "k8s.io/apimachinery/pkg/types"
 
-       "k8s.io/client-go/dynamic"
-       "k8s.io/client-go/restmapper"
-       "k8s.io/client-go/scale"
-
        . "github.com/apache/camel-k/e2e/support"
        v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
        "github.com/apache/camel-k/pkg/client/camel/clientset/versioned"
@@ -67,12 +63,7 @@ func TestIntegrationScale(t *testing.T) {
 
                t.Run("Scale integration with polymorphic client", func(t 
*testing.T) {
                        RegisterTestingT(t)
-                       // Polymorphic scale client
-                       groupResources, err := 
restmapper.GetAPIGroupResources(TestClient().Discovery())
-                       Expect(err).To(BeNil())
-                       mapper := 
restmapper.NewDiscoveryRESTMapper(groupResources)
-                       resolver := 
scale.NewDiscoveryScaleKindResolver(TestClient().Discovery())
-                       scaleClient, err := 
scale.NewForConfig(TestClient().GetConfig(), mapper, 
dynamic.LegacyAPIPathResolverFunc, resolver)
+                       scaleClient, err := TestClient().ScalesClient()
                        Expect(err).To(BeNil())
 
                        // Patch the integration scale subresource
diff --git a/pkg/client/serverside.go b/pkg/client/apply.go
similarity index 100%
rename from pkg/client/serverside.go
rename to pkg/client/apply.go
diff --git a/pkg/client/client.go b/pkg/client/client.go
index 2cf73c2..967a5fc 100644
--- a/pkg/client/client.go
+++ b/pkg/client/client.go
@@ -26,6 +26,7 @@ import (
        user "github.com/mitchellh/go-homedir"
        "github.com/pkg/errors"
        "github.com/sirupsen/logrus"
+       "k8s.io/client-go/scale"
 
        "k8s.io/apimachinery/pkg/api/meta"
        "k8s.io/apimachinery/pkg/runtime"
@@ -64,6 +65,7 @@ type Client interface {
        GetConfig() *rest.Config
        GetCurrentNamespace(kubeConfig string) (string, error)
        ServerOrClientSideApplier() ServerOrClientSideApplier
+       ScalesClient() (scale.ScalesGetter, error)
 }
 
 // Injectable identifies objects that can receive a Client.
diff --git a/pkg/client/scale.go b/pkg/client/scale.go
new file mode 100644
index 0000000..7bcf7d7
--- /dev/null
+++ b/pkg/client/scale.go
@@ -0,0 +1,35 @@
+/*
+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 client
+
+import (
+       "k8s.io/client-go/dynamic"
+       "k8s.io/client-go/restmapper"
+       "k8s.io/client-go/scale"
+)
+
+func (c *defaultClient) ScalesClient() (scale.ScalesGetter, error) {
+       // Polymorphic scale client
+       groupResources, err := restmapper.GetAPIGroupResources(c.Discovery())
+       if err != nil {
+               return nil, err
+       }
+       mapper := restmapper.NewDiscoveryRESTMapper(groupResources)
+       resolver := scale.NewDiscoveryScaleKindResolver(c.Discovery())
+       return scale.NewForConfig(c.GetConfig(), mapper, 
dynamic.LegacyAPIPathResolverFunc, resolver)
+}
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index d2d260f..4721fef 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -54,7 +54,7 @@ import (
        "github.com/apache/camel-k/pkg/util/watch"
 )
 
-var traitConfigRegexp = 
regexp.MustCompile(`^([a-z0-9-]+)((?:\[[0-9]+\]|\.[a-z0-9-]+)+)=(.*)$`)
+var traitConfigRegexp = 
regexp.MustCompile(`^([a-z0-9-]+)((?:\.[a-z0-9-]+)(?:\[[0-9]+\]|\.[A-Za-z0-9-_]+)*)=(.*)$`)
 
 func newCmdRun(rootCmdOptions *RootCmdOptions) (*cobra.Command, 
*runCmdOptions) {
        options := runCmdOptions{
diff --git a/pkg/resources/resources.go b/pkg/resources/resources.go
index 80a8c6d..5d8301d 100644
--- a/pkg/resources/resources.go
+++ b/pkg/resources/resources.go
@@ -555,9 +555,9 @@ var assets = func() http.FileSystem {
                "/traits.yaml": &vfsgen۰CompressedFileInfo{
                        name:             "traits.yaml",
                        modTime:          time.Time{},
-                       uncompressedSize: 49570,
+                       uncompressedSize: 49341,
 
-                       compressedContent: 
[]byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\xfd\x73\x5b\xb9\x91\xe0\xef\xf3\x57\xa0\xb4\x57\x65\x49\x45\x52\x9e\xc9\x26\x3b\xa7\xbb\xd9\x94\xc6\x76\x12\xcd\xf8\x43\x67\x3b\xb3\x97\x9a\x9b\x0a\xc1\xf7\x9a\x24\xcc\x47\xe0\x05\xc0\x93\xcc\xdc\xde\xff\x7e\x85\xee\xc6\xc7\x7b\x24\x25\xca\xb6\x66\xa3\xad\xdd\x54\xed\x58\xd2\x03\xd0\x68\x34\xfa\xbb\x1b\xde\x4a\xe5\xdd\xf9\x57\x63\xa1\xe5\x1a\xce\x85\x9c\xcf\x95\x56\x7e\xf3\x95\x10\x6d\x23\xfd\xdc\xd8\xf5\xb9\x
 [...]
+                       compressedContent: 
[]byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\xfd\x73\x5b\xb9\x91\xe0\xef\xf3\x57\xa0\xb4\x57\x65\x49\x45\x52\x9e\xc9\x26\x3b\xa7\xbb\xd9\x94\xc6\x76\x12\xcd\xf8\x43\x67\x3b\xb3\x97\x9a\x9b\x0a\xc1\xf7\x9a\x24\xcc\x47\xe0\x05\xc0\x93\xcc\xdc\xde\xff\x7e\x85\xee\xc6\xc7\x7b\x24\x25\xca\xb6\x66\xa3\xad\xdd\x54\xed\x58\xd2\x03\xd0\x68\x34\xfa\xbb\x1b\xde\x4a\xe5\xdd\xf9\x57\x63\xa1\xe5\x1a\xce\x85\x9c\xcf\x95\x56\x7e\xf3\x95\x10\x6d\x23\xfd\xdc\xd8\xf5\xb9\x
 [...]
                },
        }
        fs["/"].(*vfsgen۰DirInfo).entries = []os.FileInfo{
diff --git a/pkg/trait/deployer.go b/pkg/trait/deployer.go
index 67cdb79..7735a37 100644
--- a/pkg/trait/deployer.go
+++ b/pkg/trait/deployer.go
@@ -17,22 +17,6 @@ limitations under the License.
 
 package trait
 
-import (
-       "encoding/json"
-       "errors"
-       "fmt"
-       "net/http"
-       "strings"
-
-       k8serrors "k8s.io/apimachinery/pkg/api/errors"
-       "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
-       "k8s.io/apimachinery/pkg/types"
-
-       ctrl "sigs.k8s.io/controller-runtime/pkg/client"
-
-       "github.com/apache/camel-k/pkg/util/patch"
-)
-
 // The deployer trait is responsible for deploying the resources owned by the 
integration, and can be used
 // to explicitly select the underlying controller that will manage the 
integration pods.
 //
@@ -45,8 +29,6 @@ type deployerTrait struct {
 
 var _ ControllerStrategySelector = &deployerTrait{}
 
-var hasServerSideApply = true
-
 func newDeployerTrait() Trait {
        return &deployerTrait{
                BaseTrait: NewBaseTrait("deployer", 900),
@@ -60,28 +42,9 @@ func (t *deployerTrait) Configure(e *Environment) (bool, 
error) {
 func (t *deployerTrait) Apply(e *Environment) error {
        // Register a post action that patches the resources generated by the 
traits
        e.PostActions = append(e.PostActions, func(env *Environment) error {
+               applier := e.Client.ServerOrClientSideApplier()
                for _, resource := range env.Resources.Items() {
-                       // We assume that server-side apply is enabled by 
default.
-                       // It is currently convoluted to check pro-actively 
whether server-side apply
-                       // is enabled. This is possible to fetch the OpenAPI 
endpoint, which returns
-                       // the entire server API document, then lookup the 
resource PATCH endpoint, and
-                       // check its list of accepted MIME types.
-                       // As a simpler solution, we fall back to client-side 
apply at the first
-                       // 415 error, and assume server-side apply is not 
available globally.
-                       if hasServerSideApply {
-                               err := t.serverSideApply(env, resource)
-                               switch {
-                               case err == nil:
-                                       continue
-                               case isIncompatibleServerError(err):
-                                       t.L.Info("Fallback to client-side apply 
to patch resources")
-                                       hasServerSideApply = false
-                               default:
-                                       // Keep server-side apply unless server 
is incompatible with it
-                                       return err
-                               }
-                       }
-                       if err := t.clientSideApply(env, resource); err != nil {
+                       if err := applier.Apply(e.Ctx, resource); err != nil {
                                return err
                        }
                }
@@ -91,73 +54,6 @@ func (t *deployerTrait) Apply(e *Environment) error {
        return nil
 }
 
-func (t *deployerTrait) serverSideApply(env *Environment, resource 
ctrl.Object) error {
-       target, err := patch.PositiveApplyPatch(resource)
-       if err != nil {
-               return err
-       }
-       err = env.Client.Patch(env.Ctx, target, ctrl.Apply, 
ctrl.ForceOwnership, ctrl.FieldOwner("camel-k-operator"))
-       if err != nil {
-               return fmt.Errorf("error during apply resource: %s/%s: %w", 
resource.GetNamespace(), resource.GetName(), err)
-       }
-       // Update the resource with the response returned from the API server
-       return t.unstructuredToRuntimeObject(target, resource)
-}
-
-func (t *deployerTrait) clientSideApply(env *Environment, resource 
ctrl.Object) error {
-       err := env.Client.Create(env.Ctx, resource)
-       if err == nil {
-               return nil
-       } else if !k8serrors.IsAlreadyExists(err) {
-               return fmt.Errorf("error during create resource: %s/%s: %w", 
resource.GetNamespace(), resource.GetName(), err)
-       }
-       object := &unstructured.Unstructured{}
-       object.SetNamespace(resource.GetNamespace())
-       object.SetName(resource.GetName())
-       object.SetGroupVersionKind(resource.GetObjectKind().GroupVersionKind())
-       err = env.Client.Get(env.Ctx, ctrl.ObjectKeyFromObject(object), object)
-       if err != nil {
-               return err
-       }
-       p, err := patch.PositiveMergePatch(object, resource)
-       if err != nil {
-               return err
-       } else if len(p) == 0 {
-               // Update the resource with the object returned from the API 
server
-               return t.unstructuredToRuntimeObject(object, resource)
-       }
-       err = env.Client.Patch(env.Ctx, resource, 
ctrl.RawPatch(types.MergePatchType, p))
-       if err != nil {
-               return fmt.Errorf("error during patch %s/%s: %w", 
resource.GetNamespace(), resource.GetName(), err)
-       }
-       return nil
-}
-
-func (t *deployerTrait) unstructuredToRuntimeObject(u 
*unstructured.Unstructured, obj ctrl.Object) error {
-       data, err := json.Marshal(u)
-       if err != nil {
-               return err
-       }
-       return json.Unmarshal(data, obj)
-}
-
-func isIncompatibleServerError(err error) bool {
-       // First simpler check for older servers (i.e. OpenShift 3.11)
-       if strings.Contains(err.Error(), "415: Unsupported Media Type") {
-               return true
-       }
-
-       // 415: Unsupported media type means we're talking to a server which 
doesn't
-       // support server-side apply.
-       var serr *k8serrors.StatusError
-       if errors.As(err, &serr) {
-               return serr.Status().Code == http.StatusUnsupportedMediaType
-       }
-
-       // Non-StatusError means the error isn't because the server is 
incompatible.
-       return false
-}
-
 func (t *deployerTrait) SelectControllerStrategy(e *Environment) 
(*ControllerStrategy, error) {
        if IsFalse(t.Enabled) {
                return nil, nil
diff --git a/pkg/util/test/client.go b/pkg/util/test/client.go
index b4f6db4..7086e05 100644
--- a/pkg/util/test/client.go
+++ b/pkg/util/test/client.go
@@ -19,6 +19,7 @@ package test
 
 import (
        "context"
+       "fmt"
        "strings"
 
        "github.com/apache/camel-k/pkg/apis"
@@ -27,6 +28,7 @@ import (
        fakecamelclientset 
"github.com/apache/camel-k/pkg/client/camel/clientset/versioned/fake"
        camelv1 
"github.com/apache/camel-k/pkg/client/camel/clientset/versioned/typed/camel/v1"
        camelv1alpha1 
"github.com/apache/camel-k/pkg/client/camel/clientset/versioned/typed/camel/v1alpha1"
+       autoscalingv1 "k8s.io/api/autoscaling/v1"
        k8serrors "k8s.io/apimachinery/pkg/api/errors"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "k8s.io/apimachinery/pkg/runtime"
@@ -36,6 +38,9 @@ import (
        fakeclientset "k8s.io/client-go/kubernetes/fake"
        clientscheme "k8s.io/client-go/kubernetes/scheme"
        "k8s.io/client-go/rest"
+       "k8s.io/client-go/scale"
+       fakescale "k8s.io/client-go/scale/fake"
+       "k8s.io/client-go/testing"
        controller "sigs.k8s.io/controller-runtime/pkg/client"
        "sigs.k8s.io/controller-runtime/pkg/client/fake"
 )
@@ -57,11 +62,44 @@ func NewFakeClient(initObjs ...runtime.Object) 
(client.Client, error) {
        clientset := fakeclientset.NewSimpleClientset(filterObjects(scheme, 
initObjs, func(gvk schema.GroupVersionKind) bool {
                return !strings.Contains(gvk.Group, "camel") && 
!strings.Contains(gvk.Group, "knative")
        })...)
+       replicasCount := make(map[string]int32)
+       fakescaleclient := fakescale.FakeScaleClient{}
+       fakescaleclient.AddReactor("update", "*", func(rawAction 
testing.Action) (handled bool, ret runtime.Object, err error) {
+               action := rawAction.(testing.UpdateAction)       // nolint: 
forcetypeassert
+               obj := action.GetObject().(*autoscalingv1.Scale) // nolint: 
forcetypeassert
+               replicas := obj.Spec.Replicas
+               key := fmt.Sprintf("%s:%s:%s/%s", action.GetResource().Group, 
action.GetResource().Resource, action.GetNamespace(), obj.GetName())
+               replicasCount[key] = replicas
+               return true, &autoscalingv1.Scale{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Name:      obj.Name,
+                               Namespace: action.GetNamespace(),
+                       },
+                       Spec: autoscalingv1.ScaleSpec{
+                               Replicas: replicas,
+                       },
+               }, nil
+       })
+       fakescaleclient.AddReactor("get", "*", func(rawAction testing.Action) 
(handled bool, ret runtime.Object, err error) {
+               action := rawAction.(testing.GetAction) // nolint: 
forcetypeassert
+               key := fmt.Sprintf("%s:%s:%s/%s", action.GetResource().Group, 
action.GetResource().Resource, action.GetNamespace(), action.GetName())
+               obj := &autoscalingv1.Scale{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Name:      action.GetName(),
+                               Namespace: action.GetNamespace(),
+                       },
+                       Spec: autoscalingv1.ScaleSpec{
+                               Replicas: replicasCount[key],
+                       },
+               }
+               return true, obj, nil
+       })
 
        return &FakeClient{
                Client:    c,
                Interface: clientset,
                camel:     camelClientset,
+               scales:    &fakescaleclient,
        }, nil
 }
 
@@ -82,7 +120,8 @@ func filterObjects(scheme *runtime.Scheme, input 
[]runtime.Object, filter func(g
 type FakeClient struct {
        controller.Client
        kubernetes.Interface
-       camel camel.Interface
+       camel  camel.Interface
+       scales *fakescale.FakeScaleClient
 }
 
 func (c *FakeClient) CamelV1() camelv1.CamelV1Interface {
@@ -123,6 +162,10 @@ func (c *FakeClient) ServerOrClientSideApplier() 
client.ServerOrClientSideApplie
        }
 }
 
+func (c *FakeClient) ScalesClient() (scale.ScalesGetter, error) {
+       return c.scales, nil
+}
+
 type FakeDiscovery struct {
        discovery.DiscoveryInterface
 }
diff --git a/resources/traits.yaml b/resources/traits.yaml
index 638418d..fd24ebe 100755
--- a/resources/traits.yaml
+++ b/resources/traits.yaml
@@ -593,10 +593,6 @@ traits:
     type: bool
     description: Enables automatic configuration of the trait. Allows the 
trait to
       infer KEDA triggers from the Kamelets.
-  - name: camel-case-conversion
-    type: bool
-    description: Convert metadata properties to camelCase (needed because 
Camel K
-      trait properties use kebab-case from command line). Disabled by default.
   - name: hack-controller-replicas
     type: bool
     description: Set the spec->replicas field on the top level controller to 
an explicit

Reply via email to