Skip to content

Commit 073da15

Browse files
authored
Merge pull request kubernetes#86801 from likakuli/fix_syncsts_orphanrevision
fix a bug that orphan revision cannot be adopted and statefulset cannot be synced
2 parents f4b6b75 + 10864d3 commit 073da15

File tree

5 files changed

+137
-10
lines changed

5 files changed

+137
-10
lines changed

pkg/controller/history/controller_history.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ type objectMetaForPatch struct {
304304
func (rh *realHistory) AdoptControllerRevision(parent metav1.Object, parentKind schema.GroupVersionKind, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) {
305305
blockOwnerDeletion := true
306306
isController := true
307-
// Return an error if the parent does not own the revision
307+
// Return an error if the revision is not orphan
308308
if owner := metav1.GetControllerOfNoCopy(revision); owner != nil {
309309
return nil, fmt.Errorf("attempt to adopt revision owned by %v", owner)
310310
}

pkg/controller/statefulset/stateful_set.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -311,22 +311,21 @@ func (ssc *StatefulSetController) adoptOrphanRevisions(set *apps.StatefulSet) er
311311
if err != nil {
312312
return err
313313
}
314-
hasOrphans := false
314+
orphanRevisions := make([]*apps.ControllerRevision, 0)
315315
for i := range revisions {
316316
if metav1.GetControllerOf(revisions[i]) == nil {
317-
hasOrphans = true
318-
break
317+
orphanRevisions = append(orphanRevisions, revisions[i])
319318
}
320319
}
321-
if hasOrphans {
320+
if len(orphanRevisions) > 0 {
322321
fresh, err := ssc.kubeClient.AppsV1().StatefulSets(set.Namespace).Get(set.Name, metav1.GetOptions{})
323322
if err != nil {
324323
return err
325324
}
326325
if fresh.UID != set.UID {
327326
return fmt.Errorf("original StatefulSet %v/%v is gone: got uid %v, wanted %v", set.Namespace, set.Name, fresh.UID, set.UID)
328327
}
329-
return ssc.control.AdoptOrphanRevisions(set, revisions)
328+
return ssc.control.AdoptOrphanRevisions(set, orphanRevisions)
330329
}
331330
return nil
332331
}

pkg/controller/statefulset/stateful_set_control_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ type invariantFunc func(set *apps.StatefulSet, spc *fakeStatefulPodControl) erro
5151

5252
func setupController(client clientset.Interface) (*fakeStatefulPodControl, *fakeStatefulSetStatusUpdater, StatefulSetControlInterface, chan struct{}) {
5353
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
54-
spc := newFakeStatefulPodControl(informerFactory.Core().V1().Pods(), informerFactory.Apps().V1().StatefulSets())
54+
spc := newFakeStatefulPodControl(informerFactory.Core().V1().Pods(), informerFactory.Apps().V1().StatefulSets(), informerFactory.Apps().V1().ControllerRevisions())
5555
ssu := newFakeStatefulSetStatusUpdater(informerFactory.Apps().V1().StatefulSets())
5656
recorder := record.NewFakeRecorder(10)
5757
ssc := NewDefaultStatefulSetControl(spc, ssu, history.NewFakeHistory(informerFactory.Apps().V1().ControllerRevisions()), recorder)
@@ -494,7 +494,7 @@ func TestStatefulSetControl_getSetRevisions(t *testing.T) {
494494
testFn := func(test *testcase, t *testing.T) {
495495
client := fake.NewSimpleClientset()
496496
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
497-
spc := newFakeStatefulPodControl(informerFactory.Core().V1().Pods(), informerFactory.Apps().V1().StatefulSets())
497+
spc := newFakeStatefulPodControl(informerFactory.Core().V1().Pods(), informerFactory.Apps().V1().StatefulSets(), informerFactory.Apps().V1().ControllerRevisions())
498498
ssu := newFakeStatefulSetStatusUpdater(informerFactory.Apps().V1().StatefulSets())
499499
recorder := record.NewFakeRecorder(10)
500500
ssc := defaultStatefulSetControl{spc, ssu, history.NewFakeHistory(informerFactory.Apps().V1().ControllerRevisions()), recorder}
@@ -1554,12 +1554,13 @@ type fakeStatefulPodControl struct {
15541554
podsIndexer cache.Indexer
15551555
claimsIndexer cache.Indexer
15561556
setsIndexer cache.Indexer
1557+
revisionsIndexer cache.Indexer
15571558
createPodTracker requestTracker
15581559
updatePodTracker requestTracker
15591560
deletePodTracker requestTracker
15601561
}
15611562

1562-
func newFakeStatefulPodControl(podInformer coreinformers.PodInformer, setInformer appsinformers.StatefulSetInformer) *fakeStatefulPodControl {
1563+
func newFakeStatefulPodControl(podInformer coreinformers.PodInformer, setInformer appsinformers.StatefulSetInformer, revisionInformer appsinformers.ControllerRevisionInformer) *fakeStatefulPodControl {
15631564
claimsIndexer := cache.NewIndexer(controller.KeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
15641565
return &fakeStatefulPodControl{
15651566
podInformer.Lister(),
@@ -1568,6 +1569,7 @@ func newFakeStatefulPodControl(podInformer coreinformers.PodInformer, setInforme
15681569
podInformer.Informer().GetIndexer(),
15691570
claimsIndexer,
15701571
setInformer.Informer().GetIndexer(),
1572+
revisionInformer.Informer().GetIndexer(),
15711573
requestTracker{0, nil, 0},
15721574
requestTracker{0, nil, 0},
15731575
requestTracker{0, nil, 0}}

pkg/controller/statefulset/stateful_set_test.go

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@ limitations under the License.
1717
package statefulset
1818

1919
import (
20+
"bytes"
21+
"encoding/json"
2022
"sort"
2123
"testing"
2224

2325
apps "k8s.io/api/apps/v1"
2426
"k8s.io/api/core/v1"
2527
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2628
"k8s.io/apimachinery/pkg/runtime"
29+
"k8s.io/apimachinery/pkg/types"
2730
"k8s.io/apimachinery/pkg/util/sets"
2831
"k8s.io/client-go/informers"
2932
"k8s.io/client-go/kubernetes/fake"
@@ -33,6 +36,8 @@ import (
3336
"k8s.io/kubernetes/pkg/controller/history"
3437
)
3538

39+
var parentKind = apps.SchemeGroupVersion.WithKind("StatefulSet")
40+
3641
func alwaysReady() bool { return true }
3742

3843
func TestStatefulSetControllerCreates(t *testing.T) {
@@ -534,6 +539,48 @@ func TestGetPodsForStatefulSetAdopt(t *testing.T) {
534539
}
535540
}
536541

542+
func TestAdoptOrphanRevisions(t *testing.T) {
543+
ss1 := newStatefulSetWithLabels(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"})
544+
ss1.Status.CollisionCount = new(int32)
545+
ss1Rev1, err := history.NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 1, ss1.Status.CollisionCount)
546+
if err != nil {
547+
t.Fatal(err)
548+
}
549+
ss1Rev1.Namespace = ss1.Namespace
550+
ss1.Spec.Template.Annotations = make(map[string]string)
551+
ss1.Spec.Template.Annotations["ss1"] = "ss1"
552+
ss1Rev2, err := history.NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 2, ss1.Status.CollisionCount)
553+
if err != nil {
554+
t.Fatal(err)
555+
}
556+
ss1Rev2.Namespace = ss1.Namespace
557+
ss1Rev2.OwnerReferences = []metav1.OwnerReference{}
558+
559+
ssc, spc := newFakeStatefulSetController(ss1, ss1Rev1, ss1Rev2)
560+
561+
spc.revisionsIndexer.Add(ss1Rev1)
562+
spc.revisionsIndexer.Add(ss1Rev2)
563+
564+
err = ssc.adoptOrphanRevisions(ss1)
565+
if err != nil {
566+
t.Errorf("adoptOrphanRevisions() error: %v", err)
567+
}
568+
569+
if revisions, err := ssc.control.ListRevisions(ss1); err != nil {
570+
t.Errorf("ListRevisions() error: %v", err)
571+
} else {
572+
var adopted bool
573+
for i := range revisions {
574+
if revisions[i].Name == ss1Rev2.Name && metav1.GetControllerOf(revisions[i]) != nil {
575+
adopted = true
576+
}
577+
}
578+
if !adopted {
579+
t.Error("adoptOrphanRevisions() not adopt orphan revisions")
580+
}
581+
}
582+
}
583+
537584
func TestGetPodsForStatefulSetRelease(t *testing.T) {
538585
set := newStatefulSet(3)
539586
ssc, spc := newFakeStatefulSetController(set)
@@ -575,7 +622,7 @@ func TestGetPodsForStatefulSetRelease(t *testing.T) {
575622
func newFakeStatefulSetController(initialObjects ...runtime.Object) (*StatefulSetController, *fakeStatefulPodControl) {
576623
client := fake.NewSimpleClientset(initialObjects...)
577624
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
578-
fpc := newFakeStatefulPodControl(informerFactory.Core().V1().Pods(), informerFactory.Apps().V1().StatefulSets())
625+
fpc := newFakeStatefulPodControl(informerFactory.Core().V1().Pods(), informerFactory.Apps().V1().StatefulSets(), informerFactory.Apps().V1().ControllerRevisions())
579626
ssu := newFakeStatefulSetStatusUpdater(informerFactory.Apps().V1().StatefulSets())
580627
ssc := NewStatefulSetController(
581628
informerFactory.Core().V1().Pods(),
@@ -710,3 +757,12 @@ func scaleDownStatefulSetController(set *apps.StatefulSet, ssc *StatefulSetContr
710757
}
711758
return assertMonotonicInvariants(set, spc)
712759
}
760+
761+
func rawTemplate(template *v1.PodTemplateSpec) runtime.RawExtension {
762+
buf := new(bytes.Buffer)
763+
enc := json.NewEncoder(buf)
764+
if err := enc.Encode(template); err != nil {
765+
panic(err)
766+
}
767+
return runtime.RawExtension{Raw: buf.Bytes()}
768+
}

pkg/controller/statefulset/stateful_set_utils_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,3 +469,73 @@ func newStatefulSet(replicas int) *apps.StatefulSet {
469469
}
470470
return newStatefulSetWithVolumes(replicas, "foo", petMounts, podMounts)
471471
}
472+
473+
func newStatefulSetWithLabels(replicas int, name string, uid types.UID, labels map[string]string) *apps.StatefulSet {
474+
// Converting all the map-only selectors to set-based selectors.
475+
var testMatchExpressions []metav1.LabelSelectorRequirement
476+
for key, value := range labels {
477+
sel := metav1.LabelSelectorRequirement{
478+
Key: key,
479+
Operator: metav1.LabelSelectorOpIn,
480+
Values: []string{value},
481+
}
482+
testMatchExpressions = append(testMatchExpressions, sel)
483+
}
484+
return &apps.StatefulSet{
485+
TypeMeta: metav1.TypeMeta{
486+
Kind: "StatefulSet",
487+
APIVersion: "apps/v1",
488+
},
489+
ObjectMeta: metav1.ObjectMeta{
490+
Name: name,
491+
Namespace: v1.NamespaceDefault,
492+
UID: uid,
493+
},
494+
Spec: apps.StatefulSetSpec{
495+
Selector: &metav1.LabelSelector{
496+
// Purposely leaving MatchLabels nil, so to ensure it will break if any link
497+
// in the chain ignores the set-based MatchExpressions.
498+
MatchLabels: nil,
499+
MatchExpressions: testMatchExpressions,
500+
},
501+
Replicas: func() *int32 { i := int32(replicas); return &i }(),
502+
Template: v1.PodTemplateSpec{
503+
ObjectMeta: metav1.ObjectMeta{
504+
Labels: labels,
505+
},
506+
Spec: v1.PodSpec{
507+
Containers: []v1.Container{
508+
{
509+
Name: "nginx",
510+
Image: "nginx",
511+
VolumeMounts: []v1.VolumeMount{
512+
{Name: "datadir", MountPath: "/tmp/"},
513+
{Name: "home", MountPath: "/home"},
514+
},
515+
},
516+
},
517+
Volumes: []v1.Volume{{
518+
Name: "home",
519+
VolumeSource: v1.VolumeSource{
520+
HostPath: &v1.HostPathVolumeSource{
521+
Path: fmt.Sprintf("/tmp/%v", "home"),
522+
},
523+
}}},
524+
},
525+
},
526+
VolumeClaimTemplates: []v1.PersistentVolumeClaim{
527+
{
528+
ObjectMeta: metav1.ObjectMeta{Name: "datadir"},
529+
Spec: v1.PersistentVolumeClaimSpec{
530+
Resources: v1.ResourceRequirements{
531+
Requests: v1.ResourceList{
532+
v1.ResourceStorage: *resource.NewQuantity(1, resource.BinarySI),
533+
},
534+
},
535+
},
536+
},
537+
},
538+
ServiceName: "governingsvc",
539+
},
540+
}
541+
}

0 commit comments

Comments
 (0)