diff --git a/docs/Manual/Deployment/Kubernetes/DeploymentResource.md b/docs/Manual/Deployment/Kubernetes/DeploymentResource.md index 7d11b0304..7ccf56d91 100644 --- a/docs/Manual/Deployment/Kubernetes/DeploymentResource.md +++ b/docs/Manual/Deployment/Kubernetes/DeploymentResource.md @@ -352,6 +352,14 @@ for `spec.mode: Single` and `2` for `spec.mode: ActiveFailover`). For the `syncworkers` group, it is highly recommended to use the same number as for the `dbservers` group. +### `spec..minCount: number` + +Specifies a minimum for the count of servers. If set, a specification is invalid if `count < minCount`. + +### `spec..maxCount: number` + +Specifies a maximum for the count of servers. If set, a specification is invalid if `count > maxCount`. + ### `spec..args: []string` This setting specifies additional commandline arguments passed to all servers of this group. diff --git a/pkg/apis/deployment/v1alpha/server_group_spec.go b/pkg/apis/deployment/v1alpha/server_group_spec.go index 20d3ec0c7..7e0c657d0 100644 --- a/pkg/apis/deployment/v1alpha/server_group_spec.go +++ b/pkg/apis/deployment/v1alpha/server_group_spec.go @@ -23,6 +23,7 @@ package v1alpha import ( + "math" "strings" "github.com/pkg/errors" @@ -39,6 +40,10 @@ import ( type ServerGroupSpec struct { // Count holds the requested number of servers Count *int `json:"count,omitempty"` + // MinCount specifies a lower limit for count + MinCount *int `json:"minCount,omitempty"` + // MaxCount specifies a upper limit for count + MaxCount *int `json:"maxCount,omitempty"` // Args holds additional commandline arguments Args []string `json:"args,omitempty"` // StorageClassName specifies the classname for storage of the servers. @@ -58,6 +63,16 @@ func (s ServerGroupSpec) GetCount() int { return util.IntOrDefault(s.Count) } +// GetMinCount returns MinCount or 1 if not set +func (s ServerGroupSpec) GetMinCount() int { + return util.IntOrDefault(s.MinCount, 1) +} + +// GetMaxCount returns MaxCount or +func (s ServerGroupSpec) GetMaxCount() int { + return util.IntOrDefault(s.MaxCount, math.MaxInt32) +} + // GetNodeSelector returns the selectors for nodes of this group func (s ServerGroupSpec) GetNodeSelector() map[string]string { return s.NodeSelector @@ -110,8 +125,17 @@ func (s ServerGroupSpec) Validate(group ServerGroup, used bool, mode DeploymentM minCount = 2 } } + if s.GetMinCount() > s.GetMaxCount() { + return maskAny(errors.Wrapf(ValidationError, "Invalid min/maxCount. Min (%d) bigger than Max (%d)", s.GetMinCount(), s.GetMaxCount())) + } + if s.GetCount() < s.GetMinCount() { + return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected >= %d", s.GetCount(), s.GetMinCount())) + } + if s.GetCount() > s.GetMaxCount() { + return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected <= %d", s.GetCount(), s.GetMaxCount())) + } if s.GetCount() < minCount { - return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected >= %d", s.GetCount(), minCount)) + return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected >= %d (implicit minimum; by deployment mode)", s.GetCount(), minCount)) } if s.GetCount() > 1 && group == ServerGroupSingle && mode == DeploymentModeSingle { return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected 1", s.GetCount())) @@ -160,6 +184,8 @@ func (s *ServerGroupSpec) SetDefaults(group ServerGroup, used bool, mode Deploym } } else if s.GetCount() > 0 && !used { s.Count = nil + s.MinCount = nil + s.MaxCount = nil } if _, found := s.Resources.Requests[v1.ResourceStorage]; !found { switch group { @@ -189,6 +215,12 @@ func (s *ServerGroupSpec) SetDefaultsFrom(source ServerGroupSpec) { if s.Count == nil { s.Count = util.NewIntOrNil(source.Count) } + if s.MinCount == nil { + s.MinCount = util.NewIntOrNil(source.MinCount) + } + if s.MaxCount == nil { + s.MaxCount = util.NewIntOrNil(source.MaxCount) + } if s.Args == nil { s.Args = source.Args } diff --git a/pkg/apis/deployment/v1alpha/server_group_spec_test.go b/pkg/apis/deployment/v1alpha/server_group_spec_test.go index 1bcb04a94..5195e9fa5 100644 --- a/pkg/apis/deployment/v1alpha/server_group_spec_test.go +++ b/pkg/apis/deployment/v1alpha/server_group_spec_test.go @@ -48,6 +48,12 @@ func TestServerGroupSpecValidateCount(t *testing.T) { assert.Nil(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentProduction)) assert.Nil(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(2), MinCount: util.NewInt(2), MaxCount: util.NewInt(5)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(1), MaxCount: util.NewInt(5)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(6), MinCount: util.NewInt(2)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(5), MinCount: util.NewInt(5), MaxCount: util.NewInt(5)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + // Invalid assert.Error(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupSingle, false, DeploymentModeCluster, EnvironmentDevelopment)) assert.Error(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupSingle, true, DeploymentModeSingle, EnvironmentDevelopment)) @@ -70,6 +76,11 @@ func TestServerGroupSpecValidateCount(t *testing.T) { assert.Error(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentProduction)) assert.Error(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentProduction)) assert.Error(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentProduction)) + + assert.Error(t, ServerGroupSpec{Count: util.NewInt(2), MinCount: util.NewInt(5), MaxCount: util.NewInt(1)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(6), MaxCount: util.NewInt(5)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1), MinCount: util.NewInt(2)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + } func TestServerGroupSpecDefault(t *testing.T) { diff --git a/pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go b/pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go index 666fb48cc..7b7ef7234 100644 --- a/pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go +++ b/pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go @@ -630,6 +630,16 @@ func (in *ServerGroupSpec) DeepCopyInto(out *ServerGroupSpec) { *out = new(int) **out = **in } + if in.MinCount != nil { + in, out := &in.MinCount, &out.MinCount + *out = new(int) + **out = **in + } + if in.MaxCount != nil { + in, out := &in.MaxCount, &out.MaxCount + *out = new(int) + **out = **in + } if in.Args != nil { in, out := &in.Args, &out.Args *out = make([]string, len(*in)) diff --git a/pkg/deployment/cluster_scaling_integration.go b/pkg/deployment/cluster_scaling_integration.go index 6067a34b2..754680848 100644 --- a/pkg/deployment/cluster_scaling_integration.go +++ b/pkg/deployment/cluster_scaling_integration.go @@ -170,6 +170,8 @@ func (ci *clusterScalingIntegration) inspectCluster(ctx context.Context, expectS if dbserversChanged { newSpec.DBServers.Count = util.NewInt(req.GetDBServers()) } + // Validate will additionally check if + // min <= count <= max holds for the given server groups if err := newSpec.Validate(); err != nil { // Log failure & create event log.Warn().Err(err).Msg("Validation of updated spec has failed")