diff --git a/api/v1/coherenceresource_types.go b/api/v1/coherenceresource_types.go index 526d6407..b6c17955 100644 --- a/api/v1/coherenceresource_types.go +++ b/api/v1/coherenceresource_types.go @@ -181,11 +181,10 @@ func (in *Coherence) GetReplicas() int32 { if in == nil { return DefaultReplicas } - if in.Spec.Replicas == nil { + if in.Spec.Replicas == nil && in.Spec.InitialReplicas == nil { if in.Status.Replicas > 0 { return in.Status.Replicas } - return DefaultReplicas } return in.Spec.GetReplicas() } diff --git a/api/v1/coherenceresourcespec_types.go b/api/v1/coherenceresourcespec_types.go index 6e9001f2..9c1ee2b0 100644 --- a/api/v1/coherenceresourcespec_types.go +++ b/api/v1/coherenceresourcespec_types.go @@ -10,14 +10,15 @@ import ( "bytes" "encoding/base64" "fmt" + "strconv" + "strings" + "github.com/oracle/coherence-operator/pkg/operator" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" - "strconv" - "strings" ) // NOTE: This file is used to generate the CRDs use by the Operator. The CRD files should not be manually edited @@ -46,12 +47,30 @@ type CoherenceResourceSpec struct { // +optional ImagePullSecrets []LocalObjectReference `json:"imagePullSecrets,omitempty"` // The desired number of cluster members of this deployment. + // + // If the cluster will be scaled using the Horizontal Pod Autoscaler or the kubectl scale command + // then this field should be left unset and the initial cluster size should be specified using the + // `initialReplicas` field. + // // This is a pointer to distinguish between explicit zero and not specified. // If not specified a default value of 3 will be used. // This field cannot be negative. // +kubebuilder:validation:Minimum:=0 // +optional Replicas *int32 `json:"replicas,omitempty"` + // The initial number of cluster members of this deployment when first created. + // + // If the `replicas` field is set this field is ignored. + // + // This field is to set an initial size for a cluster that is then resized only using the + // Horizontal Pod Autoscaler or the kubectl scale command. + // + // This is a pointer to distinguish between explicit zero and not specified. + // If not specified a default value of 3 will be used. + // This field cannot be negative. + // +kubebuilder:validation:Minimum:=0 + // +optional + InitialReplicas *int32 `json:"initialReplicas,omitempty"` // The name of the role that this deployment represents in a Coherence cluster. // This value will be used to set the Coherence role property for all members of this role // +optional @@ -327,9 +346,15 @@ type ActionJob struct { // return either the actual Replica value or the default (DefaultReplicas const) // if the Replicas field is nil. func (in *CoherenceResourceSpec) GetReplicas() int32 { - if in == nil || in.Replicas == nil { + if in == nil { return DefaultReplicas } + if in.Replicas == nil { + if in.InitialReplicas == nil { + return DefaultReplicas + } + return *in.InitialReplicas + } return *in.Replicas } diff --git a/api/v1/create_statefulset_test.go b/api/v1/create_statefulset_test.go index bb17e02f..0af16273 100644 --- a/api/v1/create_statefulset_test.go +++ b/api/v1/create_statefulset_test.go @@ -8,13 +8,14 @@ package v1_test import ( "fmt" + "testing" + coh "github.com/oracle/coherence-operator/api/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" - "testing" ) func TestCreateStatefulSetFromMinimalRoleSpec(t *testing.T) { @@ -56,6 +57,40 @@ func TestCreateStatefulSetWithReplicas(t *testing.T) { deployment := createTestDeployment(spec) // Create expected StatefulSet stsExpected := createMinimalExpectedStatefulSet(deployment) + stsExpected.Spec.Replicas = ptr.To(int32(50)) + + // assert that the StatefulSet is as expected + assertStatefulSetCreation(t, deployment, stsExpected) +} + +func TestCreateStatefulSetWithInitialReplicas(t *testing.T) { + // create a spec with a name + spec := coh.CoherenceResourceSpec{ + InitialReplicas: ptr.To(int32(50)), + } + + // Create the test deployment + deployment := createTestDeployment(spec) + // Create expected StatefulSet + stsExpected := createMinimalExpectedStatefulSet(deployment) + stsExpected.Spec.Replicas = ptr.To(int32(50)) + + // assert that the StatefulSet is as expected + assertStatefulSetCreation(t, deployment, stsExpected) +} + +func TestCreateStatefulSetWhenReplicasAndInitialReplicasSet(t *testing.T) { + // create a spec with a name + spec := coh.CoherenceResourceSpec{ + Replicas: ptr.To(int32(100)), + InitialReplicas: ptr.To(int32(50)), + } + + // Create the test deployment + deployment := createTestDeployment(spec) + // Create expected StatefulSet + stsExpected := createMinimalExpectedStatefulSet(deployment) + stsExpected.Spec.Replicas = ptr.To(int32(100)) // assert that the StatefulSet is as expected assertStatefulSetCreation(t, deployment, stsExpected) diff --git a/docs/about/04_coherence_spec.adoc b/docs/about/04_coherence_spec.adoc index 7dde6460..7efa9bc4 100644 --- a/docs/about/04_coherence_spec.adoc +++ b/docs/about/04_coherence_spec.adoc @@ -281,7 +281,18 @@ CoherenceResourceSpec defines the specification of a Coherence resource. A Coher m| image | The name of the image. More info: https://kubernetes.io/docs/concepts/containers/images m| *string | false m| imagePullPolicy | Image pull policy. One of Always, Never, IfNotPresent. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images m| *https://pkg.go.dev/k8s.io/api/core/v1#PullPolicy | false m| imagePullSecrets | ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod m| []<> | false -m| replicas | The desired number of cluster members of this deployment. This is a pointer to distinguish between explicit zero and not specified. If not specified a default value of 3 will be used. This field cannot be negative. m| *int32 | false +m| replicas | The desired number of cluster members of this deployment. + + + +If the cluster will be scaled using the Horizontal Pod Autoscaler or the kubectl scale command then this field should be left unset and the initial cluster size should be specified using the `initialReplicas` field. + + + +This is a pointer to distinguish between explicit zero and not specified. If not specified a default value of 3 will be used. This field cannot be negative. m| *int32 | false +m| initialReplicas | The initial number of cluster members of this deployment when first created. + + + +If the `replicas` field is set this field is ignored. + + + +This field is to set an initial size for a cluster that is then resized only using the Horizontal Pod Autoscaler or the kubectl scale command. + + + +This is a pointer to distinguish between explicit zero and not specified. If not specified a default value of 3 will be used. This field cannot be negative. m| *int32 | false m| role | The name of the role that this deployment represents in a Coherence cluster. This value will be used to set the Coherence role property for all members of this role m| string | false m| appLabel | An optional app label to apply to resources created for this deployment. This is useful for example to apply an app label for use by Istio. This field follows standard Kubernetes label syntax. m| *string | false m| versionLabel | An optional version label to apply to resources created for this deployment. This is useful for example to apply a version label for use by Istio. This field follows standard Kubernetes label syntax. m| *string | false diff --git a/docs/scaling/010_overview.adoc b/docs/scaling/010_overview.adoc index 4c2f7deb..496b9635 100644 --- a/docs/scaling/010_overview.adoc +++ b/docs/scaling/010_overview.adoc @@ -106,10 +106,21 @@ 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 +=== The Solutions -If you intend to use `kubectl scale` or the Kubernetes HPA to control scaling then it is best to not set the +If you intend to use `kubectl scale` or the Kubernetes HPA to control scaling there are two options to fix the +issue described above. + +1: <> + +2: <> + +[#replicas_not_set] +==== 1: Do Not Set Replicas + +If you intend to use `kubectl scale` or the Kubernetes HPA to control scaling then you can choose not to set the `replicas` field in the YAML file used to create and update the Coherence cluster. +The operator will then create a cluster with the default three replicas. For example, the initial YAML above could have been created like this: @@ -145,6 +156,34 @@ 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. +[#initial_replicas_set] +==== 2: Use the `initialReplicas` Field + +The `Coherence` resource spec contains an `initialReplicas` field which can be used to specify the initial size of the +cluster. This field can be used when a cluster needs an initial size greater or smaller than the operator's default +size of three replicas. + +For example, the yaml below will create a StatefulSet with a replica count of six. +[source,yaml] +.storage-cluster.yaml +---- +apiVersion: coherence.oracle.com/v1 +kind: Coherence +metadata: + name: storage +spec: + initialReplicas: 6 +---- + +[NOTE] +==== +If both the `initialReplicas` field and the `replicas` field are set then the `replicas` field willl be used to set the cluster size. +==== + +When the Coherence cluster is scaled using either the HPA or the `kubectl scale` command then they will set the `replicas` +field in the Coherence spec which will scale up or down the cluster. +When an upgrade is performed by editing the original yaml that contains no `replicas` field and only the `initialReplicas` +field the resulting patch applied will not change whatever the current `replicas` value is that was set by the HPA or scale command. == Controlling Safe Scaling