feature toggle for using maintenance windows (#3074)
* feature toggle for using maintenance windows
This commit is contained in:
parent
e9478894a8
commit
39cc09ccaa
|
|
@ -79,6 +79,9 @@ spec:
|
||||||
enable_lazy_spilo_upgrade:
|
enable_lazy_spilo_upgrade:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
|
enable_maintenance_windows:
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
enable_pgversion_env_var:
|
enable_pgversion_env_var:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: true
|
default: true
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ configGeneral:
|
||||||
- "all"
|
- "all"
|
||||||
# update only the statefulsets without immediately doing the rolling update
|
# update only the statefulsets without immediately doing the rolling update
|
||||||
enable_lazy_spilo_upgrade: false
|
enable_lazy_spilo_upgrade: false
|
||||||
|
# toogle to use maintenance windows feature
|
||||||
|
enable_maintenance_windows: true
|
||||||
# set the PGVERSION env var instead of providing the version via postgresql.bin_dir in SPILO_CONFIGURATION
|
# set the PGVERSION env var instead of providing the version via postgresql.bin_dir in SPILO_CONFIGURATION
|
||||||
enable_pgversion_env_var: true
|
enable_pgversion_env_var: true
|
||||||
# start any new database pod without limitations on shm memory
|
# start any new database pod without limitations on shm memory
|
||||||
|
|
|
||||||
|
|
@ -95,11 +95,11 @@ Thus, the `full` mode can create drift between desired and actual state.
|
||||||
|
|
||||||
### Upgrade during maintenance windows
|
### Upgrade during maintenance windows
|
||||||
|
|
||||||
When `maintenanceWindows` are defined in the Postgres manifest the operator
|
When `maintenanceWindows` are defined in the Postgres manifest or in the global
|
||||||
will trigger major-version-related pod rotation and the major version upgrade
|
config the operator will trigger major-version-related pod rotation and the
|
||||||
only during these periods. Make sure they are at least twice as long as your
|
major version upgrade only during these periods. Make sure they are at least
|
||||||
configured `resync_period` to guarantee
|
twice as long as your configured `resync_period` to guarantee that operator
|
||||||
that operator actions can be triggered.
|
actions can be triggered.
|
||||||
|
|
||||||
### Upgrade annotations
|
### Upgrade annotations
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,9 @@ These parameters are grouped directly under the `spec` key in the manifest.
|
||||||
a list which defines specific time frames when certain maintenance operations
|
a list which defines specific time frames when certain maintenance operations
|
||||||
such as automatic major upgrades or master pod migration are allowed to happen.
|
such as automatic major upgrades or master pod migration are allowed to happen.
|
||||||
Accepted formats are "01:00-06:00" for daily maintenance windows or
|
Accepted formats are "01:00-06:00" for daily maintenance windows or
|
||||||
"Sat:00:00-04:00" for specific days, with all times in UTC.
|
"Sat:00:00-04:00" for specific days, with all times in UTC. Note, when the
|
||||||
|
global config option `enable_maintenance_windows` is false, the specified
|
||||||
|
windows will be ignored.
|
||||||
|
|
||||||
* **users**
|
* **users**
|
||||||
a map of usernames to user flags for the users that should be created in the
|
a map of usernames to user flags for the users that should be created in the
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,9 @@ Those are top-level keys, containing both leaf keys and groups.
|
||||||
the thresholds. The value must be `"true"` to be effective. The default is empty
|
the thresholds. The value must be `"true"` to be effective. The default is empty
|
||||||
which means the feature is disabled.
|
which means the feature is disabled.
|
||||||
|
|
||||||
|
* **enable_maintenance_windows**
|
||||||
|
toggle for using the maintenance windows feature. Default is `"true"`.
|
||||||
|
|
||||||
* **maintenance_windows**
|
* **maintenance_windows**
|
||||||
a list which defines specific time frames when certain maintenance
|
a list which defines specific time frames when certain maintenance
|
||||||
operations such as automatic major upgrades or master pod migration are
|
operations such as automatic major upgrades or master pod migration are
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ data:
|
||||||
enable_ebs_gp3_migration_max_size: "1000"
|
enable_ebs_gp3_migration_max_size: "1000"
|
||||||
enable_init_containers: "true"
|
enable_init_containers: "true"
|
||||||
enable_lazy_spilo_upgrade: "false"
|
enable_lazy_spilo_upgrade: "false"
|
||||||
|
enable_maintenance_windows: "true"
|
||||||
enable_master_load_balancer: "false"
|
enable_master_load_balancer: "false"
|
||||||
enable_master_pooler_load_balancer: "false"
|
enable_master_pooler_load_balancer: "false"
|
||||||
enable_password_rotation: "false"
|
enable_password_rotation: "false"
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,9 @@ spec:
|
||||||
enable_lazy_spilo_upgrade:
|
enable_lazy_spilo_upgrade:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
|
enable_maintenance_windows:
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
enable_pgversion_env_var:
|
enable_pgversion_env_var:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: true
|
default: true
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ configuration:
|
||||||
# crd_categories:
|
# crd_categories:
|
||||||
# - all
|
# - all
|
||||||
# enable_lazy_spilo_upgrade: false
|
# enable_lazy_spilo_upgrade: false
|
||||||
|
enable_maintenance_windows: true
|
||||||
enable_pgversion_env_var: true
|
enable_pgversion_env_var: true
|
||||||
# enable_shm_volume: true
|
# enable_shm_volume: true
|
||||||
enable_spilo_wal_path_compat: false
|
enable_spilo_wal_path_compat: false
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{
|
||||||
"enable_lazy_spilo_upgrade": {
|
"enable_lazy_spilo_upgrade": {
|
||||||
Type: "boolean",
|
Type: "boolean",
|
||||||
},
|
},
|
||||||
|
"enable_maintenance_windows": {
|
||||||
|
Type: "boolean",
|
||||||
|
},
|
||||||
"enable_shm_volume": {
|
"enable_shm_volume": {
|
||||||
Type: "boolean",
|
Type: "boolean",
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,7 @@ type OperatorConfigurationData struct {
|
||||||
Workers uint32 `json:"workers,omitempty"`
|
Workers uint32 `json:"workers,omitempty"`
|
||||||
ResyncPeriod Duration `json:"resync_period,omitempty"`
|
ResyncPeriod Duration `json:"resync_period,omitempty"`
|
||||||
RepairPeriod Duration `json:"repair_period,omitempty"`
|
RepairPeriod Duration `json:"repair_period,omitempty"`
|
||||||
|
EnableMaintenanceWindows *bool `json:"enable_maintenance_windows,omitempty"`
|
||||||
MaintenanceWindows []MaintenanceWindow `json:"maintenance_windows,omitempty"`
|
MaintenanceWindows []MaintenanceWindow `json:"maintenance_windows,omitempty"`
|
||||||
SetMemoryRequestToLimit bool `json:"set_memory_request_to_limit,omitempty"`
|
SetMemoryRequestToLimit bool `json:"set_memory_request_to_limit,omitempty"`
|
||||||
ShmVolume *bool `json:"enable_shm_volume,omitempty"`
|
ShmVolume *bool `json:"enable_shm_volume,omitempty"`
|
||||||
|
|
|
||||||
|
|
@ -433,6 +433,11 @@ func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData
|
||||||
*out = make([]string, len(*in))
|
*out = make([]string, len(*in))
|
||||||
copy(*out, *in)
|
copy(*out, *in)
|
||||||
}
|
}
|
||||||
|
if in.EnableMaintenanceWindows != nil {
|
||||||
|
in, out := &in.EnableMaintenanceWindows, &out.EnableMaintenanceWindows
|
||||||
|
*out = new(bool)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
if in.MaintenanceWindows != nil {
|
if in.MaintenanceWindows != nil {
|
||||||
in, out := &in.MaintenanceWindows, &out.MaintenanceWindows
|
in, out := &in.MaintenanceWindows, &out.MaintenanceWindows
|
||||||
*out = make([]MaintenanceWindow, len(*in))
|
*out = make([]MaintenanceWindow, len(*in))
|
||||||
|
|
|
||||||
|
|
@ -675,7 +675,9 @@ func isStandbyCluster(spec *acidv1.PostgresSpec) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) isInMaintenanceWindow(specMaintenanceWindows []acidv1.MaintenanceWindow) bool {
|
func (c *Cluster) isInMaintenanceWindow(specMaintenanceWindows []acidv1.MaintenanceWindow) bool {
|
||||||
if len(specMaintenanceWindows) == 0 && len(c.OpConfig.MaintenanceWindows) == 0 {
|
ignoreMaintenanceWindows := c.OpConfig.EnableMaintenanceWindows != nil && !*c.OpConfig.EnableMaintenanceWindows
|
||||||
|
noWindowsDefined := len(specMaintenanceWindows) == 0 && len(c.OpConfig.MaintenanceWindows) == 0
|
||||||
|
if noWindowsDefined || ignoreMaintenanceWindows {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
|
||||||
|
|
@ -660,6 +660,7 @@ func TestIsInMaintenanceWindow(t *testing.T) {
|
||||||
cluster := New(
|
cluster := New(
|
||||||
Config{
|
Config{
|
||||||
OpConfig: config.Config{
|
OpConfig: config.Config{
|
||||||
|
EnableMaintenanceWindows: util.True(),
|
||||||
Resources: config.Resources{
|
Resources: config.Resources{
|
||||||
ClusterLabels: map[string]string{"application": "spilo"},
|
ClusterLabels: map[string]string{"application": "spilo"},
|
||||||
ClusterNameLabel: "cluster-name",
|
ClusterNameLabel: "cluster-name",
|
||||||
|
|
@ -683,12 +684,27 @@ func TestIsInMaintenanceWindow(t *testing.T) {
|
||||||
name string
|
name string
|
||||||
windows []acidv1.MaintenanceWindow
|
windows []acidv1.MaintenanceWindow
|
||||||
configWindows []string
|
configWindows []string
|
||||||
|
windowsFlag bool
|
||||||
expected bool
|
expected bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no maintenance windows",
|
name: "no maintenance windows",
|
||||||
windows: nil,
|
windows: nil,
|
||||||
configWindows: nil,
|
configWindows: nil,
|
||||||
|
windowsFlag: true,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "maintenance windows diabled",
|
||||||
|
windows: []acidv1.MaintenanceWindow{
|
||||||
|
{
|
||||||
|
Everyday: true,
|
||||||
|
StartTime: mustParseTime("00:00"),
|
||||||
|
EndTime: mustParseTime("23:59"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
configWindows: nil,
|
||||||
|
windowsFlag: false,
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -701,6 +717,7 @@ func TestIsInMaintenanceWindow(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
configWindows: nil,
|
configWindows: nil,
|
||||||
|
windowsFlag: true,
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -713,6 +730,7 @@ func TestIsInMaintenanceWindow(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
configWindows: nil,
|
configWindows: nil,
|
||||||
|
windowsFlag: true,
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -724,24 +742,35 @@ func TestIsInMaintenanceWindow(t *testing.T) {
|
||||||
EndTime: mustParseTime(futureTimeEndFormatted),
|
EndTime: mustParseTime(futureTimeEndFormatted),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: false,
|
windowsFlag: true,
|
||||||
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "global maintenance windows with future interval time",
|
name: "global maintenance windows with future interval time",
|
||||||
windows: nil,
|
windows: nil,
|
||||||
configWindows: []string{fmt.Sprintf("%s-%s", futureTimeStartFormatted, futureTimeEndFormatted)},
|
configWindows: []string{fmt.Sprintf("%s-%s", futureTimeStartFormatted, futureTimeEndFormatted)},
|
||||||
|
windowsFlag: true,
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "global maintenance windows all day",
|
name: "global maintenance windows all day",
|
||||||
windows: nil,
|
windows: nil,
|
||||||
configWindows: []string{"00:00-02:00", "02:00-23:59"},
|
configWindows: []string{"00:00-02:00", "02:00-23:59"},
|
||||||
|
windowsFlag: true,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "global maintenance windows ignored",
|
||||||
|
windows: nil,
|
||||||
|
configWindows: []string{"00:00-02:00", "02:00-23:59"},
|
||||||
|
windowsFlag: false,
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cluster.OpConfig.EnableMaintenanceWindows = &tt.windowsFlag
|
||||||
cluster.OpConfig.MaintenanceWindows = tt.configWindows
|
cluster.OpConfig.MaintenanceWindows = tt.configWindows
|
||||||
cluster.Spec.MaintenanceWindows = tt.windows
|
cluster.Spec.MaintenanceWindows = tt.windows
|
||||||
if cluster.isInMaintenanceWindow(cluster.Spec.MaintenanceWindows) != tt.expected {
|
if cluster.isInMaintenanceWindow(cluster.Spec.MaintenanceWindows) != tt.expected {
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
|
||||||
result.ShmVolume = util.CoalesceBool(fromCRD.ShmVolume, util.True())
|
result.ShmVolume = util.CoalesceBool(fromCRD.ShmVolume, util.True())
|
||||||
result.SidecarImages = fromCRD.SidecarImages
|
result.SidecarImages = fromCRD.SidecarImages
|
||||||
result.SidecarContainers = fromCRD.SidecarContainers
|
result.SidecarContainers = fromCRD.SidecarContainers
|
||||||
|
result.EnableMaintenanceWindows = util.CoalesceBool(fromCRD.EnableMaintenanceWindows, util.True())
|
||||||
if len(fromCRD.MaintenanceWindows) > 0 {
|
if len(fromCRD.MaintenanceWindows) > 0 {
|
||||||
result.MaintenanceWindows = make([]string, 0, len(fromCRD.MaintenanceWindows))
|
result.MaintenanceWindows = make([]string, 0, len(fromCRD.MaintenanceWindows))
|
||||||
for _, window := range fromCRD.MaintenanceWindows {
|
for _, window := range fromCRD.MaintenanceWindows {
|
||||||
|
|
|
||||||
|
|
@ -173,14 +173,15 @@ type Config struct {
|
||||||
LogicalBackup
|
LogicalBackup
|
||||||
ConnectionPooler
|
ConnectionPooler
|
||||||
|
|
||||||
WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to'
|
WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to'
|
||||||
KubernetesUseConfigMaps bool `name:"kubernetes_use_configmaps" default:"false"`
|
KubernetesUseConfigMaps bool `name:"kubernetes_use_configmaps" default:"false"`
|
||||||
EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS
|
EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS
|
||||||
MaintenanceWindows []string `name:"maintenance_windows"`
|
EnableMaintenanceWindows *bool `name:"enable_maintenance_windows" default:"true"`
|
||||||
DockerImage string `name:"docker_image" default:"ghcr.io/zalando/spilo-18:4.1-p1"`
|
MaintenanceWindows []string `name:"maintenance_windows"`
|
||||||
SidecarImages map[string]string `name:"sidecar_docker_images"` // deprecated in favour of SidecarContainers
|
DockerImage string `name:"docker_image" default:"ghcr.io/zalando/spilo-18:4.1-p1"`
|
||||||
SidecarContainers []v1.Container `name:"sidecars"`
|
SidecarImages map[string]string `name:"sidecar_docker_images"` // deprecated in favour of SidecarContainers
|
||||||
PodServiceAccountName string `name:"pod_service_account_name" default:"postgres-pod"`
|
SidecarContainers []v1.Container `name:"sidecars"`
|
||||||
|
PodServiceAccountName string `name:"pod_service_account_name" default:"postgres-pod"`
|
||||||
// value of this string must be valid JSON or YAML; see initPodServiceAccount
|
// value of this string must be valid JSON or YAML; see initPodServiceAccount
|
||||||
PodServiceAccountDefinition string `name:"pod_service_account_definition" default:""`
|
PodServiceAccountDefinition string `name:"pod_service_account_definition" default:""`
|
||||||
PodServiceAccountRoleBindingDefinition string `name:"pod_service_account_role_binding_definition" default:""`
|
PodServiceAccountRoleBindingDefinition string `name:"pod_service_account_role_binding_definition" default:""`
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue