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