@@ -19,7 +19,9 @@ import (
1919
2020appsv1 "k8s.io/api/apps/v1"
2121corev1 "k8s.io/api/core/v1"
22+ apierrors "k8s.io/apimachinery/pkg/api/errors"
2223metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+ "sigs.k8s.io/controller-runtime/pkg/client"
2325
2426"github.com/pkg/errors"
2527
@@ -34,6 +36,30 @@ func (r *PGAdminReconciler) reconcilePGAdminStatefulSet(
3436configmap * corev1.ConfigMap , dataVolume * corev1.PersistentVolumeClaim ,
3537) error {
3638sts := statefulset (r , pgadmin , configmap , dataVolume )
39+
40+ // Previous versions of PGO used a StatefulSet Pod Management Policy that could leave the Pod
41+ // in a failed state. When we see that it has the wrong policy, we will delete the StatefulSet
42+ // and then recreate it with the correct policy, as this is not a property that can be patched.
43+ // When we delete the StatefulSet, we will leave its Pods in place. They will be claimed by
44+ // the StatefulSet that gets created in the next reconcile.
45+ existing := & appsv1.StatefulSet {}
46+ if err := errors .WithStack (r .Client .Get (ctx , client .ObjectKeyFromObject (sts ), existing )); err != nil {
47+ if ! apierrors .IsNotFound (err ) {
48+ return err
49+ }
50+ } else {
51+ if existing .Spec .PodManagementPolicy != sts .Spec .PodManagementPolicy {
52+ // We want to delete the STS without affecting the Pods, so we set the PropagationPolicy to Orphan.
53+ // The orphaned Pods will be claimed by the StatefulSet that will be created in the next reconcile.
54+ uid := existing .GetUID ()
55+ version := existing .GetResourceVersion ()
56+ exactly := client.Preconditions {UID : & uid , ResourceVersion : & version }
57+ propagate := client .PropagationPolicy (metav1 .DeletePropagationOrphan )
58+
59+ return errors .WithStack (client .IgnoreNotFound (r .Client .Delete (ctx , existing , exactly , propagate )))
60+ }
61+ }
62+
3763if err := errors .WithStack (r .setControllerReference (pgadmin , sts )); err != nil {
3864return err
3965}
@@ -70,15 +96,13 @@ func statefulset(
7096// Don't clutter the namespace with extra ControllerRevisions.
7197sts .Spec .RevisionHistoryLimit = initialize .Int32 (0 )
7298
73- // Set the StatefulSet update strategy to "RollingUpdate", and the Partition size for the
74- // update strategy to 0 (note that these are the defaults for a StatefulSet). This means
75- // every pod of the StatefulSet will be deleted and recreated when the Pod template changes.
76- // - https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#rolling-updates
77- // - https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#forced-rollback
99+ // Use StatefulSet's "RollingUpdate" strategy and "Parallel" policy to roll
100+ // out changes to pods even when not Running or not Ready.
101+ // - https://docs.k8s.io/concepts/workloads/controllers/statefulset/#rolling-updates
102+ // - https://docs.k8s.io/concepts/workloads/controllers/statefulset/#forced-rollback
103+ // - https://kep.k8s.io/3541
104+ sts .Spec .PodManagementPolicy = appsv1 .ParallelPodManagement
78105sts .Spec .UpdateStrategy .Type = appsv1 .RollingUpdateStatefulSetStrategyType
79- sts .Spec .UpdateStrategy .RollingUpdate = & appsv1.RollingUpdateStatefulSetStrategy {
80- Partition : initialize .Int32 (0 ),
81- }
82106
83107// Use scheduling constraints from the cluster spec.
84108sts .Spec .Template .Spec .Affinity = pgadmin .Spec .Affinity
0 commit comments