From ce861bf7e0418cd2038ca97dd0df0b69ec7e2f49 Mon Sep 17 00:00:00 2001 From: Jonathan Knight Date: Wed, 10 Sep 2025 14:45:13 +0300 Subject: [PATCH 1/6] Operator should not overwrite the replica field when it is not set in the yaml --- controllers/coherence_controller.go | 23 ------ docs/scaling/010_overview.adoc | 107 +++++++++++++++++++++++++++- examples/200_autoscaler/README.adoc | 8 ++- test/e2e/remote/scaling_test.go | 67 ++++++++++++++--- 4 files changed, 170 insertions(+), 35 deletions(-) diff --git a/controllers/coherence_controller.go b/controllers/coherence_controller.go index 9882db00c..0135c886e 100644 --- a/controllers/coherence_controller.go +++ b/controllers/coherence_controller.go @@ -9,7 +9,6 @@ package controllers import ( "context" "fmt" - "strconv" "time" "github.com/go-logr/logr" @@ -198,28 +197,6 @@ func (in *CoherenceReconciler) Reconcile(ctx context.Context, request ctrl.Reque } } - // Check whether the deployment has a replica count specified - // Ideally we'd do this with a validating/defaulting web-hook but maybe in a later version. - if deployment.Spec.Replicas == nil { - // No replica count, so we patch the deployment to have the default replicas value. - // The reason we do this, is because the kubectl scale command will fail if the replicas - // field is not set to a non-nil value. - patch := &coh.Coherence{} - deployment.DeepCopyInto(patch) - replicas := deployment.GetReplicas() - patch.Spec.Replicas = &replicas - _, err = in.ThreeWayPatch(ctx, deployment.Name, deployment, deployment, patch) - if err != nil { - in.GetEventRecorder().Event(deployment, coreV1.EventTypeWarning, reconciler.EventReasonFailed, - fmt.Sprintf("failed to add default replicas to Coherence resource, %s", err.Error())) - return reconcile.Result{}, errors.Wrap(err, "failed to add default replicas to Coherence resource") - } - msg := "Added default replicas to Coherence resource, re-queuing request" - log.Info(msg, "Replicas", strconv.Itoa(int(replicas))) - in.GetEventRecorder().Event(deployment, coreV1.EventTypeNormal, reconciler.EventReasonUpdated, msg) - return reconcile.Result{}, err - } - // ensure that the Operator configuration Secret exists if err = in.resourcesManager.EnsureOperatorSecret(ctx, deployment); err != nil { err = errorhandling.NewResourceError("ensure", "operator_secret", deployment.GetNamespace(), err) diff --git a/docs/scaling/010_overview.adoc b/docs/scaling/010_overview.adoc index 4ed10e86d..4c2f7deb6 100644 --- a/docs/scaling/010_overview.adoc +++ b/docs/scaling/010_overview.adoc @@ -41,9 +41,114 @@ NOTE: The Operator will only apply safe scaling functionality to deployments tha If a deployment is storage disabled then it can be scaled up or down by the required number of members in one step as there is no fear of data loss in a storage disabled member. +[#problem] +== Kubectl Scale & Autoscaling + +When using the `kubectl scale` command or the Kubernetes autoscaler to scale Coherence clusters it is important +to be aware of how replicas are controlled when later applying updates to a cluster. + +=== The Problem + +If the replica count that has been changed via one of the scaling commands, when later updating it using an edited +version of the original YAML the replicas may be reverted and the cluster inadvertently resized. + +For example, a Coherence cluster can be defined with the `storage-cluster.yaml` file shown below. + +[source,yaml] +.storage-cluster.yaml +---- +apiVersion: coherence.oracle.com/v1 +kind: Coherence +metadata: + name: storage +spec: + replicas: 6 +---- + +Applying the YAML file will create a cluster with six Pods. +[source,bash] +---- +kubectl apply -f storage-cluster.yaml +---- + +The cluster can then be scaled using kubectl scale +[source,bash] +---- +kubectl scale coh/storage --replicas=9 +---- + +The cluster now has nine Pods. Later an update is applied to add a new system property to the JVM by +editing the original YAML file: + +[source,yaml] +.storage-cluster.yaml +---- +apiVersion: coherence.oracle.com/v1 +kind: Coherence +metadata: + name: storage +spec: + replicas: 6 + jvm: + args: + - "-Dfoo=bar" +---- + +The update can be applied using kubectl apply: +[source,bash] +---- +kubectl apply -f storage-cluster.yaml +---- + +But, the YAML file contains the original replica count, so the cluster will be scaled back from nine to six Pods. +This is probably not what was desired. + +One solution is to always make sure the replicas in the yaml is set to the "correct" value before applying it. +This can be awkward in some environments, and especially if using the Kubernetes HPA to control scaling. + +=== The Solution + +If you intend to use `kubectl scale` or the Kubernetes HPA to control scaling then it is best to not set the +`replicas` field in the YAML file used to create and update the Coherence cluster. + +For example, the initial YAML above could have been created like this: + +[source,yaml] +.storage-cluster.yaml +---- +apiVersion: coherence.oracle.com/v1 +kind: Coherence +metadata: + name: storage +---- + +After applying the YAML, the cluster size would start with the default three Pods. +After creation, the cluster can easily be scaled up to its required size using `kubectl scale` + +Later when applying the system property update, again the `replicas` field is unset in the YAML file: +[source,yaml] +.storage-cluster.yaml +---- +apiVersion: coherence.oracle.com/v1 +kind: Coherence +metadata: + name: storage +spec: + jvm: + args: + - "-Dfoo=bar" +---- + +Now, when the above YAML is applied using `kubectl apply` the replicas value for the cluster will be unchanged and +stay at whatever value it was scaled to. + +Another solution would be to create the initial YAML with the `replicas` field set to the desired initial size. +Then later when applying updates, ensure that the `replicas` field has been deleted from the YAML file. + + == Controlling Safe Scaling -The `Coherence` CRD has a number of fields that control the behaviour of scaling. +The `Coherence` CRD has a number of fields that control the behavior of scaling. === Scaling Policy diff --git a/examples/200_autoscaler/README.adoc b/examples/200_autoscaler/README.adoc index 6ca412658..78c0cd007 100644 --- a/examples/200_autoscaler/README.adoc +++ b/examples/200_autoscaler/README.adoc @@ -32,12 +32,18 @@ The diagram below shows, at a high level, how this works. image::images/autoscaler.png[] -Prometheus will obtain metrics from the Coherence Pod's metrics endpoints. +Prometheus will get metrics from the Coherence Pod's metrics endpoints. The Prometheus Adapter exposes certain configured metrics polled from Prometheus as custom Kubernetes metrics. The HPA is configured to poll the custom metrics and use those to scale the `Coherence` resource (which will in turn cause the Coherence Operator to scale the `StatefulSet`). +[IMPORTANT] +==== +When using the HPA it is important to understand how the replica field in the Coherence resource +is applied to avoid inadvertently resizing an autoscaled cluster when applying updates to the YAML. +See the explanation in <> +==== == Autoscaling Coherence Clusters diff --git a/test/e2e/remote/scaling_test.go b/test/e2e/remote/scaling_test.go index 5353d697e..5a6072c52 100644 --- a/test/e2e/remote/scaling_test.go +++ b/test/e2e/remote/scaling_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -9,20 +9,24 @@ package remote import ( goctx "context" "fmt" + "io" + "strings" + "testing" + "time" + cohv1 "github.com/oracle/coherence-operator/api/v1" "github.com/oracle/coherence-operator/test/e2e/helper" "golang.org/x/net/context" - "io" appsv1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" "sigs.k8s.io/testing_frameworks/integration" - "strings" - "testing" - "time" . "github.com/onsi/gomega" ) +var scalingTestCounter = 0 + // Test scaling up and down with different policies. // This test is an example of using sub-tests to run the test with different test cases. func TestScaling(t *testing.T) { @@ -98,6 +102,37 @@ func TestScaleDownToZeroUsingKubectl(t *testing.T) { assertScaleDownToZero(t, "DownToZeroUsingKubectl", kubeCtlScaler, nil) } +// Test that a cluster that has been scaled and is later updated with +// new yaml does not get resized +func TestScaleWithUpdates(t *testing.T) { + g := NewGomegaWithT(t) + + // Ensure that everything is cleaned up after the test! + testContext.CleanupAfterTest(t) + // Create an initial cluster that will have no replica field set, so will default to 3, then be scaled down to 2 + names := assertScale(t, "DownWithKubectl", cohv1.ParallelUpSafeDownScaling, -1, 2, kubeCtlScaler) + + // Now apply an update to the cluster, without setting replicas + deployment, err := helper.NewSingleCoherenceFromYaml(names.Namespace, "scaling-test.yaml") + g.Expect(err).NotTo(HaveOccurred()) + deployment.Spec.Replicas = nil + labels := make(map[string]string) + labels["one"] = "test1" + deployment.Spec.Labels = labels + + err = testContext.Client.Update(context.TODO(), &deployment) + g.Expect(err).NotTo(HaveOccurred()) + _, err = helper.WaitForPodsWithLabel(testContext, names.Namespace, "one=test1", 2, time.Second*10, time.Minute*5) + g.Expect(err).NotTo(HaveOccurred()) + // assert the StatefulSet still has a replica count of 2 + sts := appsv1.StatefulSet{} + err = testContext.Client.Get(context.TODO(), names, &sts) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(sts.Spec.Replicas).NotTo(BeNil()) + var replicas int32 = 2 + g.Expect(*sts.Spec.Replicas).To(Equal(replicas)) +} + // ----- helper methods ------------------------------------------------ // ScaleFunction is a function that can scale a deployment up or down @@ -138,7 +173,7 @@ var kubeCtlScaler = func(t *testing.T, d *cohv1.Coherence, replicas int32) error } // Assert that a deployment can be created and scaled using the specified policy. -func assertScale(t *testing.T, id string, policy cohv1.ScalingPolicy, replicasStart, replicasScale int32, scaler ScaleFunction) { +func assertScale(t *testing.T, id string, policy cohv1.ScalingPolicy, replicasStart, replicasScale int32, scaler ScaleFunction) types.NamespacedName { g := NewGomegaWithT(t) testContext.CleanupAfterTest(t) @@ -147,22 +182,32 @@ func assertScale(t *testing.T, id string, policy cohv1.ScalingPolicy, replicasSt namespace := helper.GetTestNamespace() - deployment, err := helper.NewSingleCoherenceFromYaml(namespace, "scaling-test.yaml") + scalingTestCounter++ + suffix := fmt.Sprintf("-%d", scalingTestCounter) + deployment, err := helper.NewSingleCoherenceFromYamlWithSuffix(namespace, "scaling-test.yaml", suffix) g.Expect(err).NotTo(HaveOccurred()) // Give the deployment a unique name based on the test name deployment.SetName(fmt.Sprintf("%s-%s", deployment.GetName(), strings.ToLower(id))) - // update the replica count and scaling policy - deployment.SetReplicas(replicasStart) + // update the replica count if greater than or equal zero, otherwise do not set the replica count field + var initialReplicas int32 + if replicasStart >= 0 { + deployment.SetReplicas(replicasStart) + initialReplicas = replicasStart + } else { + deployment.Spec.Replicas = nil + initialReplicas = cohv1.DefaultReplicas + } + // update the scaling policy if deployment.Spec.Scaling == nil { deployment.Spec.Scaling = &cohv1.ScalingSpec{} } deployment.Spec.Scaling.Policy = &policy // Do the canary test unless parallel scaling down - doCanary := replicasStart < replicasScale || policy != cohv1.ParallelScaling + doCanary := initialReplicas < replicasScale || policy != cohv1.ParallelScaling t.Logf("assertScale() - doCanary=%t", doCanary) t.Log("assertScale() - Installing Coherence deployment...") @@ -186,6 +231,8 @@ func assertScale(t *testing.T, id string, policy cohv1.ScalingPolicy, replicasSt err = helper.CheckCanary(testContext, namespace, deployment.Name) g.Expect(err).NotTo(HaveOccurred()) } + + return types.NamespacedName{Namespace: deployment.Namespace, Name: deployment.Name} } func assertScaleDownToZero(t *testing.T, id string, scaler ScaleFunction, suspend *bool) { From 1573e8bafe1f92d21799981f4fa9679a085c7553 Mon Sep 17 00:00:00 2001 From: Jonathan Knight Date: Wed, 10 Sep 2025 18:26:17 +0300 Subject: [PATCH 2/6] Revert to set default replicas --- controllers/coherence_controller.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/controllers/coherence_controller.go b/controllers/coherence_controller.go index 0135c886e..9882db00c 100644 --- a/controllers/coherence_controller.go +++ b/controllers/coherence_controller.go @@ -9,6 +9,7 @@ package controllers import ( "context" "fmt" + "strconv" "time" "github.com/go-logr/logr" @@ -197,6 +198,28 @@ func (in *CoherenceReconciler) Reconcile(ctx context.Context, request ctrl.Reque } } + // Check whether the deployment has a replica count specified + // Ideally we'd do this with a validating/defaulting web-hook but maybe in a later version. + if deployment.Spec.Replicas == nil { + // No replica count, so we patch the deployment to have the default replicas value. + // The reason we do this, is because the kubectl scale command will fail if the replicas + // field is not set to a non-nil value. + patch := &coh.Coherence{} + deployment.DeepCopyInto(patch) + replicas := deployment.GetReplicas() + patch.Spec.Replicas = &replicas + _, err = in.ThreeWayPatch(ctx, deployment.Name, deployment, deployment, patch) + if err != nil { + in.GetEventRecorder().Event(deployment, coreV1.EventTypeWarning, reconciler.EventReasonFailed, + fmt.Sprintf("failed to add default replicas to Coherence resource, %s", err.Error())) + return reconcile.Result{}, errors.Wrap(err, "failed to add default replicas to Coherence resource") + } + msg := "Added default replicas to Coherence resource, re-queuing request" + log.Info(msg, "Replicas", strconv.Itoa(int(replicas))) + in.GetEventRecorder().Event(deployment, coreV1.EventTypeNormal, reconciler.EventReasonUpdated, msg) + return reconcile.Result{}, err + } + // ensure that the Operator configuration Secret exists if err = in.resourcesManager.EnsureOperatorSecret(ctx, deployment); err != nil { err = errorhandling.NewResourceError("ensure", "operator_secret", deployment.GetNamespace(), err) From 84e9cad174f49c6fbe9426f6ef1b05b2eea47df7 Mon Sep 17 00:00:00 2001 From: Jonathan Knight Date: Thu, 11 Sep 2025 10:17:50 +0300 Subject: [PATCH 3/6] Add scale with update compatibility test --- .../certification/certifiy_deployment_test.go | 50 +++++++++++++++++++ test/e2e/remote/scaling_test.go | 12 ++--- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/test/certification/certifiy_deployment_test.go b/test/certification/certifiy_deployment_test.go index 62b08b7c5..52c3dffed 100644 --- a/test/certification/certifiy_deployment_test.go +++ b/test/certification/certifiy_deployment_test.go @@ -84,6 +84,56 @@ func TestCertifyScaling(t *testing.T) { g.Expect(err).NotTo(HaveOccurred()) } +// Test the scenario where we create a Coherence cluster without a replicas field, which will default to three Pods. +// Then scale up the cluster to four. +// The apply an update using the same Coherence resource with no replicas field. +// After the update is applied, the cluster should still be four and not revert to three. +func TestCertifyScalingWithUpdate(t *testing.T) { + // Ensure that everything is cleaned up after the test! + testContext.CleanupAfterTest(t) + g := NewGomegaWithT(t) + + ns := helper.GetTestClusterNamespace() + + // This Coherence resource has no replicas field so it will default to three + d := &v1.Coherence{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: "certify-scale", + }, + Spec: v1.CoherenceStatefulSetResourceSpec{ + CoherenceResourceSpec: v1.CoherenceResourceSpec{ + ReadinessProbe: &v1.ReadinessProbeSpec{ + InitialDelaySeconds: ptr.To(int32(10)), + PeriodSeconds: ptr.To(int32(10)), + }, + }, + }, + } + + // Start with the default three replicas + err := testContext.Client.Create(context.TODO(), d) + g.Expect(err).NotTo(HaveOccurred()) + _, err = helper.WaitForStatefulSetForDeployment(testContext, ns, d, time.Second*10, time.Minute*5) + g.Expect(err).NotTo(HaveOccurred()) + + // Scale Up to four + err = scale(t, ns, d.Name, 4) + g.Expect(err).NotTo(HaveOccurred()) + _, err = helper.WaitForStatefulSet(testContext, ns, d.Name, 4, time.Second*10, time.Minute*5) + g.Expect(err).NotTo(HaveOccurred()) + + // Add a label to the deployment + d.Spec.Labels = make(map[string]string) + d.Spec.Labels["one"] = "testOne" + + // apply the update + err = testContext.Client.Update(context.TODO(), d) + g.Expect(err).NotTo(HaveOccurred()) + // There should eventually be four Pods with the new label + _, err = helper.WaitForPodsWithLabel(testContext, ns, "one=testOne", 4, time.Second*10, time.Minute*10) +} + func scale(t *testing.T, namespace, name string, replicas int32) error { cmd := exec.Command("kubectl", "-n", namespace, "scale", fmt.Sprintf("--replicas=%d", replicas), "coherence/"+name) cmd.Stdout = os.Stdout diff --git a/test/e2e/remote/scaling_test.go b/test/e2e/remote/scaling_test.go index 5a6072c52..3d33f5c1d 100644 --- a/test/e2e/remote/scaling_test.go +++ b/test/e2e/remote/scaling_test.go @@ -25,10 +25,8 @@ import ( . "github.com/onsi/gomega" ) -var scalingTestCounter = 0 - // Test scaling up and down with different policies. -// This test is an example of using sub-tests to run the test with different test cases. +// This test is an example of using subtests to run the test with different test cases. func TestScaling(t *testing.T) { // Ensure that everything is cleaned up after the test! testContext.CleanupAfterTest(t) @@ -95,7 +93,7 @@ func TestScaleDownToZeroWithSuspendFalse(t *testing.T) { } // If a deployment is scaled down to zero it should be deleted and just its parent Coherence resource should remain. -// This test scales down using the "kubectl scale --relicas=0" command +// This test scales down using the "kubectl scale --replicas=0" command func TestScaleDownToZeroUsingKubectl(t *testing.T) { // Ensure that everything is cleaned up after the test! testContext.CleanupAfterTest(t) @@ -122,7 +120,7 @@ func TestScaleWithUpdates(t *testing.T) { err = testContext.Client.Update(context.TODO(), &deployment) g.Expect(err).NotTo(HaveOccurred()) - _, err = helper.WaitForPodsWithLabel(testContext, names.Namespace, "one=test1", 2, time.Second*10, time.Minute*5) + _, err = helper.WaitForPodsWithLabel(testContext, names.Namespace, "one=test1", 2, time.Second*10, time.Minute*10) g.Expect(err).NotTo(HaveOccurred()) // assert the StatefulSet still has a replica count of 2 sts := appsv1.StatefulSet{} @@ -182,9 +180,7 @@ func assertScale(t *testing.T, id string, policy cohv1.ScalingPolicy, replicasSt namespace := helper.GetTestNamespace() - scalingTestCounter++ - suffix := fmt.Sprintf("-%d", scalingTestCounter) - deployment, err := helper.NewSingleCoherenceFromYamlWithSuffix(namespace, "scaling-test.yaml", suffix) + deployment, err := helper.NewSingleCoherenceFromYaml(namespace, "scaling-test.yaml") g.Expect(err).NotTo(HaveOccurred()) // Give the deployment a unique name based on the test name From 7ee6747607bf782193ba7621eb2a42eb361916d5 Mon Sep 17 00:00:00 2001 From: Jonathan Knight Date: Thu, 11 Sep 2025 10:34:40 +0300 Subject: [PATCH 4/6] Fix scale with update compatibility test --- test/certification/certifiy_deployment_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/test/certification/certifiy_deployment_test.go b/test/certification/certifiy_deployment_test.go index 52c3dffed..383b77edf 100644 --- a/test/certification/certifiy_deployment_test.go +++ b/test/certification/certifiy_deployment_test.go @@ -132,6 +132,7 @@ func TestCertifyScalingWithUpdate(t *testing.T) { g.Expect(err).NotTo(HaveOccurred()) // There should eventually be four Pods with the new label _, err = helper.WaitForPodsWithLabel(testContext, ns, "one=testOne", 4, time.Second*10, time.Minute*10) + g.Expect(err).NotTo(HaveOccurred()) } func scale(t *testing.T, namespace, name string, replicas int32) error { From a8738b6faba25954fe8a516eb4237f782a5e375c Mon Sep 17 00:00:00 2001 From: Jonathan Knight Date: Thu, 11 Sep 2025 11:19:45 +0300 Subject: [PATCH 5/6] Update scale with update compatibility test --- .../certification/certifiy_deployment_test.go | 47 +++++++++---------- test/certification/scale-with-update-one.yaml | 10 ++++ test/certification/scale-with-update-two.yaml | 8 ++++ 3 files changed, 39 insertions(+), 26 deletions(-) create mode 100644 test/certification/scale-with-update-one.yaml create mode 100644 test/certification/scale-with-update-two.yaml diff --git a/test/certification/certifiy_deployment_test.go b/test/certification/certifiy_deployment_test.go index 383b77edf..8d81e2f11 100644 --- a/test/certification/certifiy_deployment_test.go +++ b/test/certification/certifiy_deployment_test.go @@ -95,43 +95,26 @@ func TestCertifyScalingWithUpdate(t *testing.T) { ns := helper.GetTestClusterNamespace() - // This Coherence resource has no replicas field so it will default to three - d := &v1.Coherence{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: ns, - Name: "certify-scale", - }, - Spec: v1.CoherenceStatefulSetResourceSpec{ - CoherenceResourceSpec: v1.CoherenceResourceSpec{ - ReadinessProbe: &v1.ReadinessProbeSpec{ - InitialDelaySeconds: ptr.To(int32(10)), - PeriodSeconds: ptr.To(int32(10)), - }, - }, - }, - } + // the name of the cluster from scale-with-update-one.yaml and scale-with-update-two.yaml + name := "certify-scale-update" // Start with the default three replicas - err := testContext.Client.Create(context.TODO(), d) + err := apply(t, ns, "scale-with-update-one.yaml") g.Expect(err).NotTo(HaveOccurred()) - _, err = helper.WaitForStatefulSetForDeployment(testContext, ns, d, time.Second*10, time.Minute*5) + _, err = helper.WaitForPodsWithLabel(testContext, ns, "one=testOne", 3, time.Second*10, time.Minute*10) g.Expect(err).NotTo(HaveOccurred()) // Scale Up to four - err = scale(t, ns, d.Name, 4) + err = scale(t, ns, name, 4) g.Expect(err).NotTo(HaveOccurred()) - _, err = helper.WaitForStatefulSet(testContext, ns, d.Name, 4, time.Second*10, time.Minute*5) + _, err = helper.WaitForStatefulSet(testContext, ns, name, 4, time.Second*10, time.Minute*5) g.Expect(err).NotTo(HaveOccurred()) - // Add a label to the deployment - d.Spec.Labels = make(map[string]string) - d.Spec.Labels["one"] = "testOne" - // apply the update - err = testContext.Client.Update(context.TODO(), d) + err = apply(t, ns, "scale-with-update-two.yaml") g.Expect(err).NotTo(HaveOccurred()) - // There should eventually be four Pods with the new label - _, err = helper.WaitForPodsWithLabel(testContext, ns, "one=testOne", 4, time.Second*10, time.Minute*10) + // There should eventually be four Pods with the additional label + _, err = helper.WaitForPodsWithLabel(testContext, ns, "two=testTwo", 4, time.Second*10, time.Minute*10) g.Expect(err).NotTo(HaveOccurred()) } @@ -142,3 +125,15 @@ func scale(t *testing.T, namespace, name string, replicas int32) error { t.Log("Executing Scale Command: " + strings.Join(cmd.Args, " ")) return cmd.Run() } + +func apply(t *testing.T, namespace, fileName string) error { + actualFile, err := helper.FindActualFile(fileName) + if err != nil { + return err + } + cmd := exec.Command("kubectl", "-n", namespace, "apply", "-f", actualFile) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + t.Log("Executing Kubectl Command: " + strings.Join(cmd.Args, " ")) + return cmd.Run() +} diff --git a/test/certification/scale-with-update-one.yaml b/test/certification/scale-with-update-one.yaml new file mode 100644 index 000000000..e6f0b8c28 --- /dev/null +++ b/test/certification/scale-with-update-one.yaml @@ -0,0 +1,10 @@ +apiVersion: coherence.oracle.com/v1 +kind: Coherence +metadata: + name: certify-scale-update +spec: + labels: + one: testOne + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 \ No newline at end of file diff --git a/test/certification/scale-with-update-two.yaml b/test/certification/scale-with-update-two.yaml new file mode 100644 index 000000000..3c9d3c11f --- /dev/null +++ b/test/certification/scale-with-update-two.yaml @@ -0,0 +1,8 @@ +apiVersion: coherence.oracle.com/v1 +kind: Coherence +metadata: + name: certify-scale-update +spec: + labels: + one: testOne + two: testTwo From d5794b4a49e18866586107ed6b975a0bf96476c5 Mon Sep 17 00:00:00 2001 From: Jonathan Knight Date: Thu, 11 Sep 2025 13:20:14 +0300 Subject: [PATCH 6/6] Fix tests --- test/e2e/remote/scaling_test.go | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/test/e2e/remote/scaling_test.go b/test/e2e/remote/scaling_test.go index 3d33f5c1d..d03b50b25 100644 --- a/test/e2e/remote/scaling_test.go +++ b/test/e2e/remote/scaling_test.go @@ -100,37 +100,6 @@ func TestScaleDownToZeroUsingKubectl(t *testing.T) { assertScaleDownToZero(t, "DownToZeroUsingKubectl", kubeCtlScaler, nil) } -// Test that a cluster that has been scaled and is later updated with -// new yaml does not get resized -func TestScaleWithUpdates(t *testing.T) { - g := NewGomegaWithT(t) - - // Ensure that everything is cleaned up after the test! - testContext.CleanupAfterTest(t) - // Create an initial cluster that will have no replica field set, so will default to 3, then be scaled down to 2 - names := assertScale(t, "DownWithKubectl", cohv1.ParallelUpSafeDownScaling, -1, 2, kubeCtlScaler) - - // Now apply an update to the cluster, without setting replicas - deployment, err := helper.NewSingleCoherenceFromYaml(names.Namespace, "scaling-test.yaml") - g.Expect(err).NotTo(HaveOccurred()) - deployment.Spec.Replicas = nil - labels := make(map[string]string) - labels["one"] = "test1" - deployment.Spec.Labels = labels - - err = testContext.Client.Update(context.TODO(), &deployment) - g.Expect(err).NotTo(HaveOccurred()) - _, err = helper.WaitForPodsWithLabel(testContext, names.Namespace, "one=test1", 2, time.Second*10, time.Minute*10) - g.Expect(err).NotTo(HaveOccurred()) - // assert the StatefulSet still has a replica count of 2 - sts := appsv1.StatefulSet{} - err = testContext.Client.Get(context.TODO(), names, &sts) - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(sts.Spec.Replicas).NotTo(BeNil()) - var replicas int32 = 2 - g.Expect(*sts.Spec.Replicas).To(Equal(replicas)) -} - // ----- helper methods ------------------------------------------------ // ScaleFunction is a function that can scale a deployment up or down