From bf80f5225efb272783e371e0f50514f3582bedcc Mon Sep 17 00:00:00 2001 From: Oleksii Kliukin Date: Fri, 15 Dec 2017 16:02:50 +0100 Subject: [PATCH] Introduce higher and lower bounds for the number of instances (#178) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Introduce higher and lower bounds for the number of instances Reduce the number of instances to the min_instances if it is lower and to the max_instances if it is higher. -1 for either of those means there is no lower or upper bound. In addition, terminate the operator when there is a nonsense in the configuration (i.e. max_instances < min_instances). Reviewed by Jan Mußler and Sergey Dudoladov. --- README.md | 6 ++++++ pkg/cluster/k8sres.go | 23 ++++++++++++++++++++++- pkg/cluster/sync.go | 2 +- pkg/util/config/config.go | 17 +++++++++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b50d2a176..769640069 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,12 @@ This ConfigMap is then added as a source of environment variables to the Postgre :exclamation: Note that there are environment variables defined by the operator itself in order to pass parameters to the Spilo image. The values from the operator for those variables will take precedence over those defined in the `pod_environment_configmap`. +### Limiting the number of instances in clusters with `min_instances` and `max_instances` + +As a preventive measure, one can restrict the minimum and the maximum number of instances permitted by each Postgres cluster managed by the operator. +If either `min_instances` or `max_instances` is set to a non-zero value, the operator may adjust the number of instances specified in the cluster manifest to match either the min or the max boundary. +For instance, of a cluster manifest has 1 instance and the min_instances is set to 3, the cluster will be created with 3 instances. By default, both parameters are set to -1. + # Setup development environment The following steps guide you through the setup to work on the operator itself. diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index d8ed0435d..6c68b8827 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -464,6 +464,8 @@ func (c *Cluster) generateStatefulSet(spec *spec.PostgresSpec) (*v1beta1.Statefu return nil, fmt.Errorf("could not generate volume claim template: %v", err) } + numberOfInstances := c.getNumberOfInstances(spec) + statefulSet := &v1beta1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ Name: c.statefulSetName(), @@ -471,7 +473,7 @@ func (c *Cluster) generateStatefulSet(spec *spec.PostgresSpec) (*v1beta1.Statefu Labels: c.labelsSet(), }, Spec: v1beta1.StatefulSetSpec{ - Replicas: &spec.NumberOfInstances, + Replicas: &numberOfInstances, ServiceName: c.serviceName(Master), Template: *podTemplate, VolumeClaimTemplates: []v1.PersistentVolumeClaim{*volumeClaimTemplate}, @@ -481,6 +483,25 @@ func (c *Cluster) generateStatefulSet(spec *spec.PostgresSpec) (*v1beta1.Statefu return statefulSet, nil } +func (c *Cluster) getNumberOfInstances(spec *spec.PostgresSpec) (newcur int32) { + min := c.OpConfig.MinInstances + max := c.OpConfig.MaxInstances + cur := spec.NumberOfInstances + newcur = cur + + if max >= 0 && newcur > max { + newcur = max + } + if min >= 0 && newcur < min { + newcur = min + } + if newcur != cur { + c.logger.Infof("adjusted number of instances from %d to %d (min: %d, max: %d)", cur, newcur, min, max) + } + + return +} + func generatePersistentVolumeClaimTemplate(volumeSize, volumeStorageClass string) (*v1.PersistentVolumeClaim, error) { metadata := metav1.ObjectMeta{ Name: constants.DataVolumeName, diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index afbe9708b..9b8baa97b 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -267,7 +267,7 @@ func (c *Cluster) syncStatefulSet() error { c.logger.Infof("found pods without the statefulset: trigger rolling update") } else { - // statefulset is alrady there, make sure we use its definition in order to compare with the spec. + // statefulset is already there, make sure we use its definition in order to compare with the spec. c.Statefulset = sset desiredSS, err := c.generateStatefulSet(&c.Spec) diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 772503ab5..3a2d9abf4 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -5,6 +5,7 @@ import ( "strings" "time" + "fmt" "github.com/zalando-incubator/postgres-operator/pkg/spec" ) @@ -33,6 +34,8 @@ type Resources struct { PodEnvironmentConfigMap string `name:"pod_environment_configmap" default:""` NodeEOLLabel map[string]string `name:"node_eol_label" default:"lifecycle-status:pending-decommission"` NodeReadinessLabel map[string]string `name:"node_readiness_label" default:"lifecycle-status:ready"` + MaxInstances int32 `name:"max_instances" default:"-1"` + MinInstances int32 `name:"min_instances" default:"-1"` } // Auth describes authentication specific configuration parameters @@ -108,6 +111,9 @@ func NewFromMap(m map[string]string) *Config { panic(err) } } + if err := validate(&cfg); err != nil { + panic(err) + } return &cfg } @@ -123,3 +129,14 @@ func Copy(c *Config) Config { return cfg } + +func validate(cfg *Config) (err error) { + if cfg.MinInstances > 0 && cfg.MaxInstances > 0 && cfg.MinInstances > cfg.MaxInstances { + err = fmt.Errorf("minimum number of instances %d is set higher than the maximum number %d", + cfg.MinInstances, cfg.MaxInstances) + } + if cfg.Workers == 0 { + err = fmt.Errorf("number of workers should be higher than 0") + } + return +}