@@ -23,7 +23,6 @@ import (
23
23
"time"
24
24
25
25
"github.com/go-logr/logr"
26
- "github.com/go-test/deep"
27
26
"github.com/iancoleman/strcase"
28
27
"github.com/imdario/mergo"
29
28
"github.com/presslabs/controller-util/mergo/transformers"
@@ -34,7 +33,9 @@ import (
34
33
"k8s.io/apimachinery/pkg/api/equality"
35
34
k8serrors "k8s.io/apimachinery/pkg/api/errors"
36
35
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
36
+ "k8s.io/apimachinery/pkg/labels"
37
37
"k8s.io/apimachinery/pkg/runtime"
38
+ "k8s.io/apimachinery/pkg/selection"
38
39
"k8s.io/apimachinery/pkg/types"
39
40
"k8s.io/apimachinery/pkg/util/wait"
40
41
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -277,17 +278,18 @@ func (s *StatefulSetSyncer) createOrUpdate(ctx context.Context) (controllerutil.
277
278
}
278
279
// Check if statefulset changed.
279
280
if ! s .sfsUpdated (existing ) {
280
- return controllerutil .OperationResultNone , nil
281
- }
282
-
283
- // If changed, update statefulset.
284
- if err := s .cli .Update (ctx , s .sfs ); err != nil {
285
- return controllerutil .OperationResultNone , err
286
- }
287
- // Need roll update.
288
- if ! equality .Semantic .DeepEqual (existing .Spec .Template , s .sfs .Spec .Template ) {
289
- s .log .Info ("update statefulset pods" , "name" , s .Name , "diff" , deep .Equal (existing .Spec .Template , s .sfs .Spec .Template ))
290
- // Update every pods of statefulset.
281
+ if s .podsAllUpdated (ctx ) {
282
+ return controllerutil .OperationResultNone , nil
283
+ } else {
284
+ if err := s .updatePod (ctx ); err != nil {
285
+ return controllerutil .OperationResultNone , err
286
+ }
287
+ }
288
+ } else {
289
+ // If changed, update statefulset.
290
+ if err := s .cli .Update (ctx , s .sfs ); err != nil {
291
+ return controllerutil .OperationResultNone , err
292
+ }
291
293
if err := s .updatePod (ctx ); err != nil {
292
294
return controllerutil .OperationResultNone , err
293
295
}
@@ -302,7 +304,7 @@ func (s *StatefulSetSyncer) createOrUpdate(ctx context.Context) (controllerutil.
302
304
// updatePod update the pods, update follower nodes first.
303
305
// This can reduce the number of master-slave switching during the update process.
304
306
func (s * StatefulSetSyncer ) updatePod (ctx context.Context ) error {
305
- // updatedRevision will not update with the currentRevision when using `onDelete`.
307
+ // currentRevision will not update with the updatedRevision when using `onDelete`.
306
308
// https://github.com/kubernetes/kubernetes/pull/106059
307
309
if s .sfs .Status .UpdatedReplicas == * s .sfs .Spec .Replicas {
308
310
return nil
@@ -333,7 +335,7 @@ func (s *StatefulSetSyncer) updatePod(ctx context.Context) error {
333
335
var leaderPod corev1.Pod
334
336
for _ , pod := range pods .Items {
335
337
// Check if the pod is healthy.
336
- err := wait .PollImmediate (time .Second * 2 , time .Minute , func () (bool , error ) {
338
+ err := wait .PollImmediate (time .Second * 2 , time .Second * 30 , func () (bool , error ) {
337
339
s .cli .Get (ctx , client .ObjectKeyFromObject (& pod ), & pod )
338
340
if pod .ObjectMeta .Labels ["healthy" ] == "yes" {
339
341
return true , nil
@@ -489,14 +491,17 @@ func (s *StatefulSetSyncer) updatePVC(ctx context.Context) error {
489
491
}
490
492
491
493
func (s * StatefulSetSyncer ) applyNWait (ctx context.Context , pod * corev1.Pod ) error {
494
+ if s .sfs .Status .UpdateRevision == "" {
495
+ return fmt .Errorf ("update revision is empty" )
496
+ }
492
497
// Check version, if not latest, delete node.
493
498
if pod .ObjectMeta .Labels ["controller-revision-hash" ] == s .sfs .Status .UpdateRevision {
494
499
s .log .Info ("pod is already updated" , "pod name" , pod .Name )
495
500
} else {
496
501
s .Status .State = apiv1alpha1 .ClusterUpdateState
497
- s .log .Info ("updating pod" , "pod" , pod .Name , "key" , s . Unwrap () )
502
+ s .log .Info ("updating pod" , "pod" , pod .Name )
498
503
// Try to delete pod and wait for pod restart.
499
- err := wait .PollImmediate (time .Second * 5 , time .Minute * 5 , func () (bool , error ) {
504
+ err := wait .PollImmediate (time .Second * 5 , time .Minute * 2 , func () (bool , error ) {
500
505
if err := s .cli .Get (ctx , types.NamespacedName {Name : pod .Name , Namespace : pod .Namespace }, pod ); err != nil {
501
506
return false , nil
502
507
}
@@ -585,7 +590,30 @@ func (s *StatefulSetSyncer) backupIsRunning(ctx context.Context) (bool, error) {
585
590
586
591
// Updates to statefulset spec for fields other than 'replicas', 'template', and 'updateStrategy' are forbidden.
587
592
func (s * StatefulSetSyncer ) sfsUpdated (existing * appsv1.StatefulSet ) bool {
588
- return existing .Spec .Replicas != s .sfs .Spec .Replicas ||
593
+ return * existing .Spec .Replicas != * s .sfs .Spec .Replicas ||
589
594
! equality .Semantic .DeepEqual (existing .Spec .Template , s .sfs .Spec .Template ) ||
590
595
existing .Spec .UpdateStrategy != s .sfs .Spec .UpdateStrategy
591
596
}
597
+
598
+ func (s * StatefulSetSyncer ) podsAllUpdated (ctx context.Context ) bool {
599
+ podlist := corev1.PodList {}
600
+ labelSelector := s .GetLabels ().AsSelector ()
601
+ // Find the pods that revision is old.
602
+ r , err := labels .NewRequirement ("controller-revision-hash" , selection .NotEquals , []string {s .sfs .Status .UpdateRevision })
603
+ if err != nil {
604
+ s .log .V (1 ).Info ("failed to create label requirement" , "error" , err )
605
+ return false
606
+ }
607
+ labelSelector = labelSelector .Add (* r )
608
+ if err := s .cli .List (ctx ,
609
+ & podlist ,
610
+ & client.ListOptions {
611
+ Namespace : s .sfs .Namespace ,
612
+ LabelSelector : labelSelector ,
613
+ },
614
+ ); err != nil {
615
+ s .log .V (1 ).Info ("failed to list pods" , "error" , err )
616
+ return false
617
+ }
618
+ return len (podlist .Items ) == 0
619
+ }
0 commit comments