switch to metav1.Duration for timeout options (#3121)

* switch to metav1.Duration for timeout options
* fix custom config parsing and use safe coalesce
This commit is contained in:
Felix Kunde 2026-06-29 10:32:24 +02:00 committed by GitHub
parent 9030520e24
commit 1227fc2e1c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 214 additions and 218 deletions

View File

@ -447,8 +447,7 @@ spec:
default: 20m
description: timeout for successful migration of master pods from
unschedulable node
format: int64
type: integer
type: string
node_readiness_label:
additionalProperties:
type: string
@ -521,8 +520,7 @@ spec:
default: 5m
description: Postgres pods are terminated forcefully after this
timeout
format: int64
type: integer
type: string
secret_name_template:
default: '{username}.{cluster}.credentials.{tprkind}.{tprgroup}'
description: |-
@ -783,13 +781,11 @@ spec:
repair_period:
default: 5m
description: period between consecutive repair requests
format: int64
type: integer
type: string
resync_period:
default: 30m
description: period between consecutive sync requests
format: int64
type: integer
type: string
scalyr:
description: ScalyrConfiguration defines the configuration for ScalyrAPI
properties:
@ -885,48 +881,40 @@ spec:
default: 1s
description: interval between consecutive attempts of operator
calling the Patroni API
format: int64
type: integer
type: string
patroni_api_check_timeout:
default: 5s
description: timeout when waiting for successful response from
Patroni API
format: int64
type: integer
type: string
pod_deletion_wait_timeout:
default: 10m
description: timeout when waiting for the Postgres pods to be
deleted
format: int64
type: integer
type: string
pod_label_wait_timeout:
default: 10m
description: timeout when waiting for pod role and cluster labels
format: int64
type: integer
type: string
ready_wait_interval:
default: 4s
description: interval between consecutive attempts waiting for
postgresql CRD to be created
format: int64
type: integer
type: string
ready_wait_timeout:
default: 30s
description: timeout for the complete postgres CRD creation
format: int64
type: integer
type: string
resource_check_interval:
default: 3s
description: interval to wait between consecutive attempts to
check for some K8s resources
format: int64
type: integer
type: string
resource_check_timeout:
default: 10m
description: timeout when waiting for the presence of a certain
K8s resource
format: int64
type: integer
type: string
type: object
users:
description: PostgresUsersConfiguration defines the system users of

View File

@ -447,8 +447,7 @@ spec:
default: 20m
description: timeout for successful migration of master pods from
unschedulable node
format: int64
type: integer
type: string
node_readiness_label:
additionalProperties:
type: string
@ -521,8 +520,7 @@ spec:
default: 5m
description: Postgres pods are terminated forcefully after this
timeout
format: int64
type: integer
type: string
secret_name_template:
default: '{username}.{cluster}.credentials.{tprkind}.{tprgroup}'
description: |-
@ -783,13 +781,11 @@ spec:
repair_period:
default: 5m
description: period between consecutive repair requests
format: int64
type: integer
type: string
resync_period:
default: 30m
description: period between consecutive sync requests
format: int64
type: integer
type: string
scalyr:
description: ScalyrConfiguration defines the configuration for ScalyrAPI
properties:
@ -885,48 +881,40 @@ spec:
default: 1s
description: interval between consecutive attempts of operator
calling the Patroni API
format: int64
type: integer
type: string
patroni_api_check_timeout:
default: 5s
description: timeout when waiting for successful response from
Patroni API
format: int64
type: integer
type: string
pod_deletion_wait_timeout:
default: 10m
description: timeout when waiting for the Postgres pods to be
deleted
format: int64
type: integer
type: string
pod_label_wait_timeout:
default: 10m
description: timeout when waiting for pod role and cluster labels
format: int64
type: integer
type: string
ready_wait_interval:
default: 4s
description: interval between consecutive attempts waiting for
postgresql CRD to be created
format: int64
type: integer
type: string
ready_wait_timeout:
default: 30s
description: timeout for the complete postgres CRD creation
format: int64
type: integer
type: string
resource_check_interval:
default: 3s
description: interval to wait between consecutive attempts to
check for some K8s resources
format: int64
type: integer
type: string
resource_check_timeout:
default: 10m
description: timeout when waiting for the presence of a certain
K8s resource
format: int64
type: integer
type: string
type: object
users:
description: PostgresUsersConfiguration defines the system users of

View File

@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"strings"
"time"
)
type postgresqlCopy Postgresql
@ -120,29 +119,3 @@ func (p *Postgresql) UnmarshalJSON(data []byte) error {
return nil
}
// UnmarshalJSON convert to Duration from byte slice of json
func (d *Duration) UnmarshalJSON(b []byte) error {
var (
v interface{}
err error
)
if err = json.Unmarshal(b, &v); err != nil {
return err
}
switch val := v.(type) {
case string:
t, err := time.ParseDuration(val)
if err != nil {
return err
}
*d = Duration(t)
return nil
case float64:
t := time.Duration(val)
*d = Duration(t)
return nil
default:
return fmt.Errorf("could not recognize type %T as a valid type to unmarshal to Duration", val)
}
}

View File

@ -5,8 +5,6 @@ package v1
import (
"github.com/zalando/postgres-operator/pkg/util/config"
"time"
"github.com/zalando/postgres-operator/pkg/spec"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -78,7 +76,7 @@ type KubernetesMetaConfiguration struct {
PodServiceAccountRoleBindingDefinition string `json:"pod_service_account_role_binding_definition,omitempty"`
// +kubebuilder:default="5m"
// Postgres pods are terminated forcefully after this timeout
PodTerminateGracePeriod Duration `json:"pod_terminate_grace_period,omitempty"`
PodTerminateGracePeriod *metav1.Duration `json:"pod_terminate_grace_period,omitempty"`
// +optional
LivenessProbe *v1.Probe `json:"liveness_probe"`
SpiloPrivileged bool `json:"spilo_privileged,omitempty"`
@ -143,9 +141,9 @@ type KubernetesMetaConfiguration struct {
PodPriorityClassName string `json:"pod_priority_class_name,omitempty"`
// +kubebuilder:default="20m"
// timeout for successful migration of master pods from unschedulable node
MasterPodMoveTimeout Duration `json:"master_pod_move_timeout,omitempty"`
EnablePodAntiAffinity bool `json:"enable_pod_antiaffinity,omitempty"`
PodAntiAffinityPreferredDuringScheduling bool `json:"pod_antiaffinity_preferred_during_scheduling,omitempty"`
MasterPodMoveTimeout *metav1.Duration `json:"master_pod_move_timeout,omitempty"`
EnablePodAntiAffinity bool `json:"enable_pod_antiaffinity,omitempty"`
PodAntiAffinityPreferredDuringScheduling bool `json:"pod_antiaffinity_preferred_during_scheduling,omitempty"`
// +kubebuilder:default="kubernetes.io/hostname"
PodAntiAffinityTopologyKey string `json:"pod_antiaffinity_topology_key,omitempty"`
// +kubebuilder:validation:Enum=ordered_ready;parallel
@ -186,28 +184,28 @@ type PostgresPodResourcesDefaults struct {
type OperatorTimeouts struct {
// +kubebuilder:default="3s"
// interval to wait between consecutive attempts to check for some K8s resources
ResourceCheckInterval Duration `json:"resource_check_interval,omitempty"`
ResourceCheckInterval *metav1.Duration `json:"resource_check_interval,omitempty"`
// +kubebuilder:default="10m"
// timeout when waiting for the presence of a certain K8s resource
ResourceCheckTimeout Duration `json:"resource_check_timeout,omitempty"`
ResourceCheckTimeout *metav1.Duration `json:"resource_check_timeout,omitempty"`
// +kubebuilder:default="10m"
// timeout when waiting for pod role and cluster labels
PodLabelWaitTimeout Duration `json:"pod_label_wait_timeout,omitempty"`
PodLabelWaitTimeout *metav1.Duration `json:"pod_label_wait_timeout,omitempty"`
// +kubebuilder:default="10m"
// timeout when waiting for the Postgres pods to be deleted
PodDeletionWaitTimeout Duration `json:"pod_deletion_wait_timeout,omitempty"`
PodDeletionWaitTimeout *metav1.Duration `json:"pod_deletion_wait_timeout,omitempty"`
// +kubebuilder:default="4s"
// interval between consecutive attempts waiting for postgresql CRD to be created
ReadyWaitInterval Duration `json:"ready_wait_interval,omitempty"`
ReadyWaitInterval *metav1.Duration `json:"ready_wait_interval,omitempty"`
// +kubebuilder:default="30s"
// timeout for the complete postgres CRD creation
ReadyWaitTimeout Duration `json:"ready_wait_timeout,omitempty"`
ReadyWaitTimeout *metav1.Duration `json:"ready_wait_timeout,omitempty"`
// +kubebuilder:default="1s"
// interval between consecutive attempts of operator calling the Patroni API
PatroniAPICheckInterval Duration `json:"patroni_api_check_interval,omitempty"`
PatroniAPICheckInterval *metav1.Duration `json:"patroni_api_check_interval,omitempty"`
// +kubebuilder:default="5s"
// timeout when waiting for successful response from Patroni API
PatroniAPICheckTimeout Duration `json:"patroni_api_check_timeout,omitempty"`
PatroniAPICheckTimeout *metav1.Duration `json:"patroni_api_check_timeout,omitempty"`
}
// LoadBalancerConfiguration defines the LB configuration
@ -420,10 +418,10 @@ type OperatorConfigurationData struct {
Workers uint32 `json:"workers,omitempty"`
// +kubebuilder:default="30m"
// period between consecutive sync requests
ResyncPeriod Duration `json:"resync_period,omitempty"`
ResyncPeriod *metav1.Duration `json:"resync_period,omitempty"`
// +kubebuilder:default="5m"
// period between consecutive repair requests
RepairPeriod Duration `json:"repair_period,omitempty"`
RepairPeriod *metav1.Duration `json:"repair_period,omitempty"`
// +kubebuilder:default=true
EnableMaintenanceWindows *bool `json:"enable_maintenance_windows,omitempty"`
// +kubebuilder:validation:Type=array
@ -478,6 +476,3 @@ type OperatorConfigurationData struct {
IgnoreInstanceLimitsAnnotationKey string `json:"ignore_instance_limits_annotation_key,omitempty"`
IgnoreResourcesLimitsAnnotationKey string `json:"ignore_resources_limits_annotation_key,omitempty"`
}
// Duration shortens this frequently used name
type Duration time.Duration

View File

@ -447,8 +447,7 @@ spec:
default: 20m
description: timeout for successful migration of master pods from
unschedulable node
format: int64
type: integer
type: string
node_readiness_label:
additionalProperties:
type: string
@ -521,8 +520,7 @@ spec:
default: 5m
description: Postgres pods are terminated forcefully after this
timeout
format: int64
type: integer
type: string
secret_name_template:
default: '{username}.{cluster}.credentials.{tprkind}.{tprgroup}'
description: |-
@ -783,13 +781,11 @@ spec:
repair_period:
default: 5m
description: period between consecutive repair requests
format: int64
type: integer
type: string
resync_period:
default: 30m
description: period between consecutive sync requests
format: int64
type: integer
type: string
scalyr:
description: ScalyrConfiguration defines the configuration for ScalyrAPI
properties:
@ -885,48 +881,40 @@ spec:
default: 1s
description: interval between consecutive attempts of operator
calling the Patroni API
format: int64
type: integer
type: string
patroni_api_check_timeout:
default: 5s
description: timeout when waiting for successful response from
Patroni API
format: int64
type: integer
type: string
pod_deletion_wait_timeout:
default: 10m
description: timeout when waiting for the Postgres pods to be
deleted
format: int64
type: integer
type: string
pod_label_wait_timeout:
default: 10m
description: timeout when waiting for pod role and cluster labels
format: int64
type: integer
type: string
ready_wait_interval:
default: 4s
description: interval between consecutive attempts waiting for
postgresql CRD to be created
format: int64
type: integer
type: string
ready_wait_timeout:
default: 30s
description: timeout for the complete postgres CRD creation
format: int64
type: integer
type: string
resource_check_interval:
default: 3s
description: interval to wait between consecutive attempts to
check for some K8s resources
format: int64
type: integer
type: string
resource_check_timeout:
default: 10m
description: timeout when waiting for the presence of a certain
K8s resource
format: int64
type: integer
type: string
type: object
users:
description: PostgresUsersConfiguration defines the system users of

View File

@ -163,6 +163,11 @@ func (in *KubernetesMetaConfiguration) DeepCopyInto(out *KubernetesMetaConfigura
*out = new(bool)
**out = **in
}
if in.PodTerminateGracePeriod != nil {
in, out := &in.PodTerminateGracePeriod, &out.PodTerminateGracePeriod
*out = new(metav1.Duration)
**out = **in
}
if in.LivenessProbe != nil {
in, out := &in.LivenessProbe, &out.LivenessProbe
*out = new(corev1.Probe)
@ -280,6 +285,11 @@ func (in *KubernetesMetaConfiguration) DeepCopyInto(out *KubernetesMetaConfigura
}
}
out.PodEnvironmentConfigMap = in.PodEnvironmentConfigMap
if in.MasterPodMoveTimeout != nil {
in, out := &in.MasterPodMoveTimeout, &out.MasterPodMoveTimeout
*out = new(metav1.Duration)
**out = **in
}
if in.PersistentVolumeClaimRetentionPolicy != nil {
in, out := &in.PersistentVolumeClaimRetentionPolicy, &out.PersistentVolumeClaimRetentionPolicy
*out = make(map[string]string, len(*in))
@ -433,6 +443,16 @@ func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.ResyncPeriod != nil {
in, out := &in.ResyncPeriod, &out.ResyncPeriod
*out = new(metav1.Duration)
**out = **in
}
if in.RepairPeriod != nil {
in, out := &in.RepairPeriod, &out.RepairPeriod
*out = new(metav1.Duration)
**out = **in
}
if in.EnableMaintenanceWindows != nil {
in, out := &in.EnableMaintenanceWindows, &out.EnableMaintenanceWindows
*out = new(bool)
@ -466,7 +486,7 @@ func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData
in.MajorVersionUpgrade.DeepCopyInto(&out.MajorVersionUpgrade)
in.Kubernetes.DeepCopyInto(&out.Kubernetes)
out.PostgresPodResources = in.PostgresPodResources
out.Timeouts = in.Timeouts
in.Timeouts.DeepCopyInto(&out.Timeouts)
in.LoadBalancer.DeepCopyInto(&out.LoadBalancer)
out.AWSGCP = in.AWSGCP
in.OperatorDebug.DeepCopyInto(&out.OperatorDebug)
@ -582,6 +602,46 @@ func (in *OperatorLogicalBackupConfiguration) DeepCopy() *OperatorLogicalBackupC
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OperatorTimeouts) DeepCopyInto(out *OperatorTimeouts) {
*out = *in
if in.ResourceCheckInterval != nil {
in, out := &in.ResourceCheckInterval, &out.ResourceCheckInterval
*out = new(metav1.Duration)
**out = **in
}
if in.ResourceCheckTimeout != nil {
in, out := &in.ResourceCheckTimeout, &out.ResourceCheckTimeout
*out = new(metav1.Duration)
**out = **in
}
if in.PodLabelWaitTimeout != nil {
in, out := &in.PodLabelWaitTimeout, &out.PodLabelWaitTimeout
*out = new(metav1.Duration)
**out = **in
}
if in.PodDeletionWaitTimeout != nil {
in, out := &in.PodDeletionWaitTimeout, &out.PodDeletionWaitTimeout
*out = new(metav1.Duration)
**out = **in
}
if in.ReadyWaitInterval != nil {
in, out := &in.ReadyWaitInterval, &out.ReadyWaitInterval
*out = new(metav1.Duration)
**out = **in
}
if in.ReadyWaitTimeout != nil {
in, out := &in.ReadyWaitTimeout, &out.ReadyWaitTimeout
*out = new(metav1.Duration)
**out = **in
}
if in.PatroniAPICheckInterval != nil {
in, out := &in.PatroniAPICheckInterval, &out.PatroniAPICheckInterval
*out = new(metav1.Duration)
**out = **in
}
if in.PatroniAPICheckTimeout != nil {
in, out := &in.PatroniAPICheckTimeout, &out.PatroniAPICheckTimeout
*out = new(metav1.Duration)
**out = **in
}
return
}

View File

@ -138,7 +138,8 @@ func TestCreate(t *testing.T) {
var cluster = New(
Config{
OpConfig: config.Config{
PodManagementPolicy: "ordered_ready",
PodManagementPolicy: "ordered_ready",
PodTerminateGracePeriod: &metav1.Duration{Duration: 600 * time.Second},
Resources: config.Resources{
ClusterLabels: map[string]string{"application": "spilo"},
ClusterNameLabel: "cluster-name",
@ -147,8 +148,8 @@ func TestCreate(t *testing.T) {
DefaultMemoryRequest: "300Mi",
DefaultMemoryLimit: "300Mi",
PodRoleLabel: "spilo-role",
ResourceCheckInterval: time.Duration(3),
ResourceCheckTimeout: time.Duration(10),
ResourceCheckInterval: &metav1.Duration{Duration: 3 * time.Second},
ResourceCheckTimeout: &metav1.Duration{Duration: 10 * time.Minute},
},
EnableFinalizers: util.True(),
},

View File

@ -292,7 +292,7 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) (
if connectionPoolerSpec == nil {
connectionPoolerSpec = &acidv1.ConnectionPooler{}
}
gracePeriod := int64(c.OpConfig.PodTerminateGracePeriod.Seconds())
gracePeriod := int64(util.CoalesceDuration(c.OpConfig.PodTerminateGracePeriod, "5m").Seconds())
resources, err := c.generateResourceRequirements(
connectionPoolerSpec.Resources,
makeDefaultConnectionPoolerResources(&c.OpConfig),

View File

@ -1165,7 +1165,7 @@ func (c *Cluster) getPodEnvironmentSecretVariables() ([]v1.EnvVar, error) {
secret := &v1.Secret{}
var notFoundErr error
err := retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
err := retryutil.Retry(c.OpConfig.ResourceCheckInterval.Duration, c.OpConfig.ResourceCheckTimeout.Duration,
func() (bool, error) {
var err error
secret, err = c.KubeClient.Secrets(c.Namespace).Get(
@ -1498,7 +1498,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
&tolerationSpec,
c.nodeAffinity(c.OpConfig.NodeReadinessLabel, spec.NodeAffinity),
spec.SchedulerName,
int64(c.OpConfig.PodTerminateGracePeriod.Seconds()),
int64(util.CoalesceDuration(c.OpConfig.PodTerminateGracePeriod, "5m").Seconds()),
c.OpConfig.PodServiceAccountName,
c.OpConfig.KubeIAMRole,
effectivePodPriorityClassName,
@ -2431,7 +2431,7 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) {
&tolerationsSpec,
c.nodeAffinity(c.OpConfig.NodeReadinessLabel, nil),
nil,
int64(c.OpConfig.PodTerminateGracePeriod.Seconds()),
int64(util.CoalesceDuration(c.OpConfig.PodTerminateGracePeriod, "5m").Seconds()),
c.OpConfig.PodServiceAccountName,
c.OpConfig.KubeIAMRole,
"",

View File

@ -6,7 +6,6 @@ import (
"reflect"
"sort"
"testing"
"time"
"github.com/stretchr/testify/assert"
@ -361,8 +360,8 @@ func TestPodEnvironmentSecretVariables(t *testing.T) {
opConfig: config.Config{
Resources: config.Resources{
PodEnvironmentSecret: testPodEnvironmentObjectNotExists,
ResourceCheckInterval: time.Duration(testResourceCheckInterval),
ResourceCheckTimeout: time.Duration(testResourceCheckTimeout),
ResourceCheckInterval: &metav1.Duration{Duration: testResourceCheckInterval},
ResourceCheckTimeout: &metav1.Duration{Duration: testResourceCheckTimeout},
},
},
err: fmt.Errorf("could not read Secret PodEnvironmentSecretName: still failing after %d retries: secret.core %q not found", maxRetries, testPodEnvironmentObjectNotExists),
@ -372,8 +371,8 @@ func TestPodEnvironmentSecretVariables(t *testing.T) {
opConfig: config.Config{
Resources: config.Resources{
PodEnvironmentSecret: testPodEnvironmentSecretNameAPIError,
ResourceCheckInterval: time.Duration(testResourceCheckInterval),
ResourceCheckTimeout: time.Duration(testResourceCheckTimeout),
ResourceCheckInterval: &metav1.Duration{Duration: testResourceCheckInterval},
ResourceCheckTimeout: &metav1.Duration{Duration: testResourceCheckTimeout},
},
},
err: fmt.Errorf("could not read Secret PodEnvironmentSecretName: Secret PodEnvironmentSecret API error"),
@ -383,8 +382,8 @@ func TestPodEnvironmentSecretVariables(t *testing.T) {
opConfig: config.Config{
Resources: config.Resources{
PodEnvironmentSecret: testPodEnvironmentSecretName,
ResourceCheckInterval: time.Duration(testResourceCheckInterval),
ResourceCheckTimeout: time.Duration(testResourceCheckTimeout),
ResourceCheckInterval: &metav1.Duration{Duration: testResourceCheckInterval},
ResourceCheckTimeout: &metav1.Duration{Duration: testResourceCheckTimeout},
},
},
envVars: []v1.EnvVar{
@ -848,8 +847,8 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) {
Name: testPodEnvironmentConfigMapName,
},
PodEnvironmentSecret: testPodEnvironmentSecretName,
ResourceCheckInterval: time.Duration(testResourceCheckInterval),
ResourceCheckTimeout: time.Duration(testResourceCheckTimeout),
ResourceCheckInterval: &metav1.Duration{Duration: testResourceCheckInterval},
ResourceCheckTimeout: &metav1.Duration{Duration: testResourceCheckTimeout},
},
},
cloneDescription: &acidv1.CloneDescription{},
@ -877,8 +876,8 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) {
Name: testPodEnvironmentConfigMapName,
},
PodEnvironmentSecret: testPodEnvironmentSecretName,
ResourceCheckInterval: time.Duration(testResourceCheckInterval),
ResourceCheckTimeout: time.Duration(testResourceCheckTimeout),
ResourceCheckInterval: &metav1.Duration{Duration: testResourceCheckInterval},
ResourceCheckTimeout: &metav1.Duration{Duration: testResourceCheckTimeout},
},
},
cloneDescription: &acidv1.CloneDescription{},
@ -968,8 +967,8 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) {
opConfig: config.Config{
Resources: config.Resources{
PodEnvironmentSecret: testPodEnvironmentSecretName,
ResourceCheckInterval: time.Duration(testResourceCheckInterval),
ResourceCheckTimeout: time.Duration(testResourceCheckTimeout),
ResourceCheckInterval: &metav1.Duration{Duration: testResourceCheckInterval},
ResourceCheckTimeout: &metav1.Duration{Duration: testResourceCheckTimeout},
},
WALES3Bucket: "global-s3-bucket",
},
@ -986,8 +985,8 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) {
opConfig: config.Config{
Resources: config.Resources{
PodEnvironmentSecret: testPodEnvironmentSecretName,
ResourceCheckInterval: time.Duration(testResourceCheckInterval),
ResourceCheckTimeout: time.Duration(testResourceCheckTimeout),
ResourceCheckInterval: &metav1.Duration{Duration: testResourceCheckInterval},
ResourceCheckTimeout: &metav1.Duration{Duration: testResourceCheckTimeout},
},
WALES3Bucket: "global-s3-bucket",
},

View File

@ -334,7 +334,7 @@ func (c *Cluster) getPatroniConfig(pod *v1.Pod) (acidv1.Patroni, map[string]stri
pgParameters map[string]string
)
podName := util.NameFromMeta(pod.ObjectMeta)
err := retryutil.Retry(c.OpConfig.PatroniAPICheckInterval, c.OpConfig.PatroniAPICheckTimeout,
err := retryutil.Retry(c.OpConfig.PatroniAPICheckInterval.Duration, c.OpConfig.PatroniAPICheckTimeout.Duration,
func() (bool, error) {
var err error
patroniConfig, pgParameters, err = c.patroni.GetConfig(pod)
@ -355,7 +355,7 @@ func (c *Cluster) getPatroniConfig(pod *v1.Pod) (acidv1.Patroni, map[string]stri
func (c *Cluster) getPatroniMemberData(pod *v1.Pod) (patroni.MemberData, error) {
var memberData patroni.MemberData
err := retryutil.Retry(c.OpConfig.PatroniAPICheckInterval, c.OpConfig.PatroniAPICheckTimeout,
err := retryutil.Retry(c.OpConfig.PatroniAPICheckInterval.Duration, c.OpConfig.PatroniAPICheckTimeout.Duration,
func() (bool, error) {
var err error
memberData, err = c.patroni.GetMemberData(pod)
@ -506,7 +506,7 @@ func (c *Cluster) getSwitchoverCandidate(master *v1.Pod) (spec.NamespacedName, e
candidates := make([]patroni.ClusterMember, 0)
syncCandidates := make([]patroni.ClusterMember, 0)
err := retryutil.Retry(c.OpConfig.PatroniAPICheckInterval, c.OpConfig.PatroniAPICheckTimeout,
err := retryutil.Retry(c.OpConfig.PatroniAPICheckInterval.Duration, c.OpConfig.PatroniAPICheckTimeout.Duration,
func() (bool, error) {
var err error
members, err = c.patroni.GetClusterMembers(master)

View File

@ -16,6 +16,7 @@ import (
"github.com/zalando/postgres-operator/pkg/util/k8sutil"
"github.com/zalando/postgres-operator/pkg/util/patroni"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestGetSwitchoverCandidate(t *testing.T) {
@ -28,8 +29,8 @@ func TestGetSwitchoverCandidate(t *testing.T) {
var cluster = New(
Config{
OpConfig: config.Config{
PatroniAPICheckInterval: time.Duration(1),
PatroniAPICheckTimeout: time.Duration(5),
PatroniAPICheckInterval: &metav1.Duration{Duration: 1 * time.Second},
PatroniAPICheckTimeout: &metav1.Duration{Duration: 5 * time.Second},
},
}, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder)

View File

@ -234,7 +234,7 @@ func (c *Cluster) replaceStatefulSet(newStatefulSet *appsv1.StatefulSet) error {
// wait until the statefulset is truly deleted
c.logger.Debug("waiting for the statefulset to be deleted")
err = retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
err = retryutil.Retry(c.OpConfig.ResourceCheckInterval.Duration, c.OpConfig.ResourceCheckTimeout.Duration,
func() (bool, error) {
_, err2 := c.KubeClient.StatefulSets(oldStatefulset.Namespace).Get(context.TODO(), oldStatefulset.Name, metav1.GetOptions{})
if err2 == nil {
@ -550,7 +550,7 @@ func (c *Cluster) deletePrimaryPodDisruptionBudget() error {
c.logger.Infof("pod disruption budget %q has been deleted", util.NameFromMeta(c.PrimaryPodDisruptionBudget.ObjectMeta))
c.PrimaryPodDisruptionBudget = nil
err = retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
err = retryutil.Retry(c.OpConfig.ResourceCheckInterval.Duration, c.OpConfig.ResourceCheckTimeout.Duration,
func() (bool, error) {
_, err2 := c.KubeClient.PodDisruptionBudgets(pdbName.Namespace).Get(context.TODO(), pdbName.Name, metav1.GetOptions{})
if err2 == nil {
@ -588,7 +588,7 @@ func (c *Cluster) deleteCriticalOpPodDisruptionBudget() error {
c.logger.Infof("pod disruption budget %q has been deleted", util.NameFromMeta(c.CriticalOpPodDisruptionBudget.ObjectMeta))
c.CriticalOpPodDisruptionBudget = nil
err = retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
err = retryutil.Retry(c.OpConfig.ResourceCheckInterval.Duration, c.OpConfig.ResourceCheckTimeout.Duration,
func() (bool, error) {
_, err2 := c.KubeClient.PodDisruptionBudgets(pdbName.Namespace).Get(context.TODO(), pdbName.Name, metav1.GetOptions{})
if err2 == nil {

View File

@ -101,8 +101,8 @@ func TestSyncStatefulSetsAnnotations(t *testing.T) {
DefaultMemoryLimit: "300Mi",
InheritedAnnotations: []string{inheritedAnnotation},
PodRoleLabel: "spilo-role",
ResourceCheckInterval: time.Duration(3),
ResourceCheckTimeout: time.Duration(10),
ResourceCheckInterval: &metav1.Duration{Duration: 3 * time.Second},
ResourceCheckTimeout: &metav1.Duration{Duration: 10 * time.Minute},
},
},
}, client, pg, logger, eventRecorder)
@ -187,8 +187,8 @@ func TestPodAnnotationsSync(t *testing.T) {
var cluster = New(
Config{
OpConfig: config.Config{
PatroniAPICheckInterval: time.Duration(1),
PatroniAPICheckTimeout: time.Duration(5),
PatroniAPICheckInterval: &metav1.Duration{Duration: 1 * time.Second},
PatroniAPICheckTimeout: &metav1.Duration{Duration: 5 * time.Second},
PodManagementPolicy: "ordered_ready",
CustomPodAnnotations: customPodAnnotations,
ConnectionPooler: config.ConnectionPooler{
@ -207,8 +207,8 @@ func TestPodAnnotationsSync(t *testing.T) {
DefaultMemoryLimit: "300Mi",
MaxInstances: -1,
PodRoleLabel: "spilo-role",
ResourceCheckInterval: time.Duration(3),
ResourceCheckTimeout: time.Duration(10),
ResourceCheckInterval: &metav1.Duration{Duration: 3 * time.Second},
ResourceCheckTimeout: &metav1.Duration{Duration: 10 * time.Minute},
},
},
}, client, pg, logger, eventRecorder)
@ -381,8 +381,8 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) {
DefaultMemoryRequest: "300Mi",
DefaultMemoryLimit: "300Mi",
PodRoleLabel: "spilo-role",
ResourceCheckInterval: time.Duration(3),
ResourceCheckTimeout: time.Duration(10),
ResourceCheckInterval: &metav1.Duration{Duration: 3 * time.Second},
ResourceCheckTimeout: &metav1.Duration{Duration: 10 * time.Minute},
},
},
}, client, pg, logger, eventRecorder)
@ -694,8 +694,8 @@ func TestSyncStandbyClusterConfiguration(t *testing.T) {
var cluster = New(
Config{
OpConfig: config.Config{
PatroniAPICheckInterval: time.Duration(1),
PatroniAPICheckTimeout: time.Duration(5),
PatroniAPICheckInterval: &metav1.Duration{Duration: 1 * time.Second},
PatroniAPICheckTimeout: &metav1.Duration{Duration: 5 * time.Second},
PodManagementPolicy: "ordered_ready",
Resources: config.Resources{
ClusterLabels: map[string]string{"application": applicationLabel},
@ -707,8 +707,8 @@ func TestSyncStandbyClusterConfiguration(t *testing.T) {
MinInstances: int32(-1),
MaxInstances: int32(-1),
PodRoleLabel: "spilo-role",
ResourceCheckInterval: time.Duration(3),
ResourceCheckTimeout: time.Duration(10),
ResourceCheckInterval: &metav1.Duration{Duration: 3 * time.Second},
ResourceCheckTimeout: &metav1.Duration{Duration: 10 * time.Minute},
},
},
}, client, pg, logger, eventRecorder)

View File

@ -342,7 +342,7 @@ func (c *Cluster) annotationsSet(annotations map[string]string) map[string]strin
}
func (c *Cluster) waitForPodLabel(podEvents chan PodEvent, stopCh chan struct{}, role *PostgresRole) (*v1.Pod, error) {
timeout := time.After(c.OpConfig.PodLabelWaitTimeout)
timeout := time.After(c.OpConfig.PodLabelWaitTimeout.Duration)
for {
select {
case podEvent := <-podEvents:
@ -364,7 +364,7 @@ func (c *Cluster) waitForPodLabel(podEvents chan PodEvent, stopCh chan struct{},
}
func (c *Cluster) waitForPodDeletion(podEvents chan PodEvent) error {
timeout := time.After(c.OpConfig.PodDeletionWaitTimeout)
timeout := time.After(c.OpConfig.PodDeletionWaitTimeout.Duration)
for {
select {
case podEvent := <-podEvents:
@ -378,7 +378,7 @@ func (c *Cluster) waitForPodDeletion(podEvents chan PodEvent) error {
}
func (c *Cluster) waitStatefulsetReady() error {
return retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
return retryutil.Retry(c.OpConfig.ResourceCheckInterval.Duration, c.OpConfig.ResourceCheckTimeout.Duration,
func() (bool, error) {
listOptions := metav1.ListOptions{
LabelSelector: c.labelsSet(false).String(),
@ -428,7 +428,7 @@ func (c *Cluster) _waitPodLabelsReady(anyReplica bool) error {
c.logger.Debug("Waiting for any replica pod to become ready")
}
err := retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
err := retryutil.Retry(c.OpConfig.ResourceCheckInterval.Duration, c.OpConfig.ResourceCheckTimeout.Duration,
func() (bool, error) {
masterCount := 0
if !anyReplica {

View File

@ -297,8 +297,8 @@ func newInheritedAnnotationsCluster(client k8sutil.KubernetesClient) (*Cluster,
cluster := New(
Config{
OpConfig: config.Config{
PatroniAPICheckInterval: time.Duration(1),
PatroniAPICheckTimeout: time.Duration(5),
PatroniAPICheckInterval: &metav1.Duration{Duration: 1 * time.Second},
PatroniAPICheckTimeout: &metav1.Duration{Duration: 5 * time.Second},
KubernetesUseConfigMaps: true,
ConnectionPooler: config.ConnectionPooler{
ConnectionPoolerDefaultCPURequest: "100m",
@ -318,8 +318,8 @@ func newInheritedAnnotationsCluster(client k8sutil.KubernetesClient) (*Cluster,
DefaultMemoryLimit: "300Mi",
InheritedAnnotations: []string{"owned-by"},
PodRoleLabel: "spilo-role",
ResourceCheckInterval: time.Duration(testResourceCheckInterval),
ResourceCheckTimeout: time.Duration(testResourceCheckTimeout),
ResourceCheckInterval: &metav1.Duration{Duration: testResourceCheckInterval},
ResourceCheckTimeout: &metav1.Duration{Duration: testResourceCheckTimeout},
MinInstances: -1,
MaxInstances: -1,
},

View File

@ -152,7 +152,7 @@ func (c *Controller) nodeDelete(obj interface{}) {
func (c *Controller) moveMasterPodsOffNode(node *v1.Node) {
// retry to move master until configured timeout is reached
err := retryutil.Retry(1*time.Minute, c.opConfig.MasterPodMoveTimeout,
err := retryutil.Retry(1*time.Minute, c.opConfig.MasterPodMoveTimeout.Duration,
func() (bool, error) {
err := c.attemptToMoveMasterPodsOffNode(node)
if err != nil {

View File

@ -4,8 +4,6 @@ import (
"context"
"fmt"
"time"
acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
"github.com/zalando/postgres-operator/pkg/util"
"github.com/zalando/postgres-operator/pkg/util/config"
@ -44,8 +42,8 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result.MaxInstances = fromCRD.MaxInstances
result.IgnoreInstanceLimitsAnnotationKey = fromCRD.IgnoreInstanceLimitsAnnotationKey
result.IgnoreResourcesLimitsAnnotationKey = fromCRD.IgnoreResourcesLimitsAnnotationKey
result.ResyncPeriod = util.CoalesceDuration(time.Duration(fromCRD.ResyncPeriod), "30m")
result.RepairPeriod = util.CoalesceDuration(time.Duration(fromCRD.RepairPeriod), "5m")
result.ResyncPeriod = util.CoalesceDuration(fromCRD.ResyncPeriod, "30m")
result.RepairPeriod = util.CoalesceDuration(fromCRD.RepairPeriod, "5m")
result.SetMemoryRequestToLimit = fromCRD.SetMemoryRequestToLimit
result.ShmVolume = util.CoalesceBool(fromCRD.ShmVolume, util.True())
result.SidecarImages = fromCRD.SidecarImages
@ -75,7 +73,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result.PodServiceAccountRoleBindingDefinition = fromCRD.Kubernetes.PodServiceAccountRoleBindingDefinition
result.PodEnvironmentConfigMap = fromCRD.Kubernetes.PodEnvironmentConfigMap
result.PodEnvironmentSecret = fromCRD.Kubernetes.PodEnvironmentSecret
result.PodTerminateGracePeriod = util.CoalesceDuration(time.Duration(fromCRD.Kubernetes.PodTerminateGracePeriod), "5m")
result.PodTerminateGracePeriod = util.CoalesceDuration(fromCRD.Kubernetes.PodTerminateGracePeriod, "5m")
result.LivenessProbe = fromCRD.Kubernetes.LivenessProbe
result.SpiloPrivileged = fromCRD.Kubernetes.SpiloPrivileged
result.SpiloAllowPrivilegeEscalation = util.CoalesceBool(fromCRD.Kubernetes.SpiloAllowPrivilegeEscalation, util.True())
@ -129,7 +127,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result.EnableSecretsDeletion = util.CoalesceBool(fromCRD.Kubernetes.EnableSecretsDeletion, util.True())
result.EnablePersistentVolumeClaimDeletion = util.CoalesceBool(fromCRD.Kubernetes.EnablePersistentVolumeClaimDeletion, util.True())
result.EnableReadinessProbe = fromCRD.Kubernetes.EnableReadinessProbe
result.MasterPodMoveTimeout = util.CoalesceDuration(time.Duration(fromCRD.Kubernetes.MasterPodMoveTimeout), "10m")
result.MasterPodMoveTimeout = util.CoalesceDuration(fromCRD.Kubernetes.MasterPodMoveTimeout, "10m")
result.EnablePodAntiAffinity = fromCRD.Kubernetes.EnablePodAntiAffinity
result.PodAntiAffinityTopologyKey = util.Coalesce(fromCRD.Kubernetes.PodAntiAffinityTopologyKey, "kubernetes.io/hostname")
result.PodAntiAffinityPreferredDuringScheduling = fromCRD.Kubernetes.PodAntiAffinityPreferredDuringScheduling
@ -146,14 +144,14 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result.MaxMemoryRequest = fromCRD.PostgresPodResources.MaxMemoryRequest
// timeout config
result.ResourceCheckInterval = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.ResourceCheckInterval), "3s")
result.ResourceCheckTimeout = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.ResourceCheckTimeout), "10m")
result.PodLabelWaitTimeout = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.PodLabelWaitTimeout), "10m")
result.PodDeletionWaitTimeout = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.PodDeletionWaitTimeout), "10m")
result.ReadyWaitInterval = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.ReadyWaitInterval), "4s")
result.ReadyWaitTimeout = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.ReadyWaitTimeout), "30s")
result.PatroniAPICheckInterval = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.PatroniAPICheckInterval), "1s")
result.PatroniAPICheckTimeout = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.PatroniAPICheckTimeout), "5s")
result.ResourceCheckInterval = util.CoalesceDuration(fromCRD.Timeouts.ResourceCheckInterval, "3s")
result.ResourceCheckTimeout = util.CoalesceDuration(fromCRD.Timeouts.ResourceCheckTimeout, "10m")
result.PodLabelWaitTimeout = util.CoalesceDuration(fromCRD.Timeouts.PodLabelWaitTimeout, "10m")
result.PodDeletionWaitTimeout = util.CoalesceDuration(fromCRD.Timeouts.PodDeletionWaitTimeout, "10m")
result.ReadyWaitInterval = util.CoalesceDuration(fromCRD.Timeouts.ReadyWaitInterval, "4s")
result.ReadyWaitTimeout = util.CoalesceDuration(fromCRD.Timeouts.ReadyWaitTimeout, "30s")
result.PatroniAPICheckInterval = util.CoalesceDuration(fromCRD.Timeouts.PatroniAPICheckInterval, "1s")
result.PatroniAPICheckTimeout = util.CoalesceDuration(fromCRD.Timeouts.PatroniAPICheckTimeout, "5s")
// load balancer config
result.DbHostedZone = util.Coalesce(fromCRD.LoadBalancer.DbHostedZone, "db.example.com")

View File

@ -27,7 +27,7 @@ import (
func (c *Controller) clusterResync(stopCh <-chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
ticker := time.NewTicker(c.opConfig.ResyncPeriod)
ticker := time.NewTicker(c.opConfig.ResyncPeriod.Duration)
for {
select {

View File

@ -3,33 +3,33 @@ package config
import (
"encoding/json"
"strings"
"time"
"fmt"
"github.com/zalando/postgres-operator/pkg/spec"
"github.com/zalando/postgres-operator/pkg/util/constants"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// CRD describes CustomResourceDefinition specific configuration parameters
type CRD struct {
ReadyWaitInterval time.Duration `name:"ready_wait_interval" default:"4s"`
ReadyWaitTimeout time.Duration `name:"ready_wait_timeout" default:"30s"`
ResyncPeriod time.Duration `name:"resync_period" default:"30m"`
RepairPeriod time.Duration `name:"repair_period" default:"5m"`
EnableCRDRegistration *bool `name:"enable_crd_registration" default:"true"`
CRDCategories []string `name:"crd_categories" default:"all"`
ReadyWaitInterval *metav1.Duration `name:"ready_wait_interval" default:"4s"`
ReadyWaitTimeout *metav1.Duration `name:"ready_wait_timeout" default:"30s"`
ResyncPeriod *metav1.Duration `name:"resync_period" default:"30m"`
RepairPeriod *metav1.Duration `name:"repair_period" default:"5m"`
EnableCRDRegistration *bool `name:"enable_crd_registration" default:"true"`
CRDCategories []string `name:"crd_categories" default:"all"`
}
// Resources describes kubernetes resource specific configuration parameters
type Resources struct {
EnableOwnerReferences *bool `name:"enable_owner_references" default:"false"`
ResourceCheckInterval time.Duration `name:"resource_check_interval" default:"3s"`
ResourceCheckTimeout time.Duration `name:"resource_check_timeout" default:"10m"`
PodLabelWaitTimeout time.Duration `name:"pod_label_wait_timeout" default:"10m"`
PodDeletionWaitTimeout time.Duration `name:"pod_deletion_wait_timeout" default:"10m"`
PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"`
ResourceCheckInterval *metav1.Duration `name:"resource_check_interval" default:"3s"`
ResourceCheckTimeout *metav1.Duration `name:"resource_check_timeout" default:"10m"`
PodLabelWaitTimeout *metav1.Duration `name:"pod_label_wait_timeout" default:"10m"`
PodDeletionWaitTimeout *metav1.Duration `name:"pod_deletion_wait_timeout" default:"10m"`
PodTerminateGracePeriod *metav1.Duration `name:"pod_terminate_grace_period" default:"5m"`
LivenessProbe *v1.Probe `name:"-"`
SpiloRunAsUser *int64 `name:"spilo_runasuser"`
SpiloRunAsGroup *int64 `name:"spilo_runasgroup"`
@ -188,7 +188,7 @@ type Config struct {
// value of this string must be valid JSON or YAML; see initPodServiceAccount
PodServiceAccountDefinition string `name:"pod_service_account_definition" default:""`
PodServiceAccountRoleBindingDefinition string `name:"pod_service_account_role_binding_definition" default:""`
MasterPodMoveTimeout time.Duration `name:"master_pod_move_timeout" default:"20m"`
MasterPodMoveTimeout *metav1.Duration `name:"master_pod_move_timeout" default:"20m"`
DbHostedZone string `name:"db_hosted_zone" default:"db.example.com"`
AWSRegion string `name:"aws_region" default:"eu-central-1"`
WALES3Bucket string `name:"wal_s3_bucket"`
@ -242,7 +242,7 @@ type Config struct {
RingLogLines int `name:"ring_log_lines" default:"100"`
ClusterHistoryEntries int `name:"cluster_history_entries" default:"1000"`
TeamAPIRoleConfiguration map[string]string `name:"team_api_role_configuration" default:"log_statement:all"`
PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"`
PodTerminateGracePeriod *metav1.Duration `name:"pod_terminate_grace_period" default:"5m"`
PodManagementPolicy string `name:"pod_management_policy" default:"ordered_ready"`
EnableReadinessProbe bool `name:"enable_readiness_probe" default:"false"`
ProtectedRoles []string `name:"protected_role_names" default:"admin,cron_admin"`
@ -258,8 +258,8 @@ type Config struct {
MajorVersionUpgradeTeamAllowList []string `name:"major_version_upgrade_team_allow_list" default:""`
MinimalMajorVersion string `name:"minimal_major_version" default:"14"`
TargetMajorVersion string `name:"target_major_version" default:"18"`
PatroniAPICheckInterval time.Duration `name:"patroni_api_check_interval" default:"1s"`
PatroniAPICheckTimeout time.Duration `name:"patroni_api_check_timeout" default:"5s"`
PatroniAPICheckInterval *metav1.Duration `name:"patroni_api_check_interval" default:"1s"`
PatroniAPICheckTimeout *metav1.Duration `name:"patroni_api_check_timeout" default:"5s"`
EnablePatroniFailsafeMode *bool `name:"enable_patroni_failsafe_mode" default:"false"`
EnableSecretsDeletion *bool `name:"enable_secrets_deletion" default:"true"`
EnablePersistentVolumeClaimDeletion *bool `name:"enable_persistent_volume_claim_deletion" default:"true"`

View File

@ -230,16 +230,16 @@ var newFromMapTests = []struct {
{
description: "duration parsing",
input: map[string]string{
"ready_wait_interval": "10s",
"ready_wait_timeout": "1m",
"patroni_api_check_interval": "1s",
"patroni_api_check_timeout": "5s",
},
expectPanic: false,
validateFunc: func(t *testing.T, cfg *Config) {
if cfg.ReadyWaitInterval.Seconds() != 10 {
t.Errorf("expected ReadyWaitInterval=10s, got %v", cfg.ReadyWaitInterval)
if cfg.PatroniAPICheckInterval.Seconds() != 1 {
t.Errorf("expected check interval of 1s, got %.0fs", cfg.PatroniAPICheckInterval.Seconds())
}
if cfg.ReadyWaitTimeout.Minutes() != 1 {
t.Errorf("expected ReadyWaitTimeout=1m, got %v", cfg.ReadyWaitTimeout)
if cfg.PatroniAPICheckTimeout.Seconds() != 5 {
t.Errorf("expected check timeout of 5s, got %.0fs", cfg.PatroniAPICheckTimeout.Seconds())
}
},
},

View File

@ -7,6 +7,8 @@ import (
"strconv"
"strings"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type decoder interface {
@ -101,13 +103,7 @@ func processField(value string, field reflect.Value) error {
val int64
err error
)
if field.Kind() == reflect.Int64 && typ.PkgPath() == "time" && typ.Name() == "Duration" {
var d time.Duration
d, err = time.ParseDuration(value)
val = int64(d)
} else {
val, err = strconv.ParseInt(value, 0, typ.Bits())
}
val, err = strconv.ParseInt(value, 0, typ.Bits())
if err != nil {
return err
}
@ -165,6 +161,15 @@ func processField(value string, field reflect.Value) error {
mp.SetMapIndex(k, v)
}
field.Set(mp)
case reflect.Struct:
if typ.Name() == "Duration" {
var d time.Duration
d, err := time.ParseDuration(value)
if err != nil {
return err
}
field.Set(reflect.ValueOf(metav1.Duration{Duration: d}))
}
}
return nil

View File

@ -311,14 +311,14 @@ func CoalesceBool(val, defaultVal *bool) *bool {
return val
}
// CoalesceDuration works like coalesce but for time.Duration
func CoalesceDuration(val time.Duration, defaultVal string) time.Duration {
if val == 0 {
// CoalesceDuration works like coalesce but for metav1.Duration
func CoalesceDuration(val *metav1.Duration, defaultVal string) *metav1.Duration {
if val == nil || val.Duration == 0 {
duration, err := time.ParseDuration(defaultVal)
if err != nil {
panic(err)
}
return duration
return &metav1.Duration{Duration: duration}
}
return val
}