mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 19:01:49 +00:00
Merge pull request #86801 from likakuli/fix_syncsts_orphanrevision
fix a bug that orphan revision cannot be adopted and statefulset cannot be synced
This commit is contained in:
commit
073da1588a
@ -304,7 +304,7 @@ type objectMetaForPatch struct {
|
||||
func (rh *realHistory) AdoptControllerRevision(parent metav1.Object, parentKind schema.GroupVersionKind, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) {
|
||||
blockOwnerDeletion := true
|
||||
isController := true
|
||||
// Return an error if the parent does not own the revision
|
||||
// Return an error if the revision is not orphan
|
||||
if owner := metav1.GetControllerOfNoCopy(revision); owner != nil {
|
||||
return nil, fmt.Errorf("attempt to adopt revision owned by %v", owner)
|
||||
}
|
||||
|
@ -311,14 +311,13 @@ func (ssc *StatefulSetController) adoptOrphanRevisions(set *apps.StatefulSet) er
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hasOrphans := false
|
||||
orphanRevisions := make([]*apps.ControllerRevision, 0)
|
||||
for i := range revisions {
|
||||
if metav1.GetControllerOf(revisions[i]) == nil {
|
||||
hasOrphans = true
|
||||
break
|
||||
orphanRevisions = append(orphanRevisions, revisions[i])
|
||||
}
|
||||
}
|
||||
if hasOrphans {
|
||||
if len(orphanRevisions) > 0 {
|
||||
fresh, err := ssc.kubeClient.AppsV1().StatefulSets(set.Namespace).Get(set.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
@ -326,7 +325,7 @@ func (ssc *StatefulSetController) adoptOrphanRevisions(set *apps.StatefulSet) er
|
||||
if fresh.UID != set.UID {
|
||||
return fmt.Errorf("original StatefulSet %v/%v is gone: got uid %v, wanted %v", set.Namespace, set.Name, fresh.UID, set.UID)
|
||||
}
|
||||
return ssc.control.AdoptOrphanRevisions(set, revisions)
|
||||
return ssc.control.AdoptOrphanRevisions(set, orphanRevisions)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ type invariantFunc func(set *apps.StatefulSet, spc *fakeStatefulPodControl) erro
|
||||
|
||||
func setupController(client clientset.Interface) (*fakeStatefulPodControl, *fakeStatefulSetStatusUpdater, StatefulSetControlInterface, chan struct{}) {
|
||||
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
|
||||
spc := newFakeStatefulPodControl(informerFactory.Core().V1().Pods(), informerFactory.Apps().V1().StatefulSets())
|
||||
spc := newFakeStatefulPodControl(informerFactory.Core().V1().Pods(), informerFactory.Apps().V1().StatefulSets(), informerFactory.Apps().V1().ControllerRevisions())
|
||||
ssu := newFakeStatefulSetStatusUpdater(informerFactory.Apps().V1().StatefulSets())
|
||||
recorder := record.NewFakeRecorder(10)
|
||||
ssc := NewDefaultStatefulSetControl(spc, ssu, history.NewFakeHistory(informerFactory.Apps().V1().ControllerRevisions()), recorder)
|
||||
@ -494,7 +494,7 @@ func TestStatefulSetControl_getSetRevisions(t *testing.T) {
|
||||
testFn := func(test *testcase, t *testing.T) {
|
||||
client := fake.NewSimpleClientset()
|
||||
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
|
||||
spc := newFakeStatefulPodControl(informerFactory.Core().V1().Pods(), informerFactory.Apps().V1().StatefulSets())
|
||||
spc := newFakeStatefulPodControl(informerFactory.Core().V1().Pods(), informerFactory.Apps().V1().StatefulSets(), informerFactory.Apps().V1().ControllerRevisions())
|
||||
ssu := newFakeStatefulSetStatusUpdater(informerFactory.Apps().V1().StatefulSets())
|
||||
recorder := record.NewFakeRecorder(10)
|
||||
ssc := defaultStatefulSetControl{spc, ssu, history.NewFakeHistory(informerFactory.Apps().V1().ControllerRevisions()), recorder}
|
||||
@ -1554,12 +1554,13 @@ type fakeStatefulPodControl struct {
|
||||
podsIndexer cache.Indexer
|
||||
claimsIndexer cache.Indexer
|
||||
setsIndexer cache.Indexer
|
||||
revisionsIndexer cache.Indexer
|
||||
createPodTracker requestTracker
|
||||
updatePodTracker requestTracker
|
||||
deletePodTracker requestTracker
|
||||
}
|
||||
|
||||
func newFakeStatefulPodControl(podInformer coreinformers.PodInformer, setInformer appsinformers.StatefulSetInformer) *fakeStatefulPodControl {
|
||||
func newFakeStatefulPodControl(podInformer coreinformers.PodInformer, setInformer appsinformers.StatefulSetInformer, revisionInformer appsinformers.ControllerRevisionInformer) *fakeStatefulPodControl {
|
||||
claimsIndexer := cache.NewIndexer(controller.KeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
||||
return &fakeStatefulPodControl{
|
||||
podInformer.Lister(),
|
||||
@ -1568,6 +1569,7 @@ func newFakeStatefulPodControl(podInformer coreinformers.PodInformer, setInforme
|
||||
podInformer.Informer().GetIndexer(),
|
||||
claimsIndexer,
|
||||
setInformer.Informer().GetIndexer(),
|
||||
revisionInformer.Informer().GetIndexer(),
|
||||
requestTracker{0, nil, 0},
|
||||
requestTracker{0, nil, 0},
|
||||
requestTracker{0, nil, 0}}
|
||||
|
@ -17,6 +17,8 @@ limitations under the License.
|
||||
package statefulset
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
@ -24,6 +26,7 @@ import (
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
@ -33,6 +36,8 @@ import (
|
||||
"k8s.io/kubernetes/pkg/controller/history"
|
||||
)
|
||||
|
||||
var parentKind = apps.SchemeGroupVersion.WithKind("StatefulSet")
|
||||
|
||||
func alwaysReady() bool { return true }
|
||||
|
||||
func TestStatefulSetControllerCreates(t *testing.T) {
|
||||
@ -534,6 +539,48 @@ func TestGetPodsForStatefulSetAdopt(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdoptOrphanRevisions(t *testing.T) {
|
||||
ss1 := newStatefulSetWithLabels(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"})
|
||||
ss1.Status.CollisionCount = new(int32)
|
||||
ss1Rev1, err := history.NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 1, ss1.Status.CollisionCount)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ss1Rev1.Namespace = ss1.Namespace
|
||||
ss1.Spec.Template.Annotations = make(map[string]string)
|
||||
ss1.Spec.Template.Annotations["ss1"] = "ss1"
|
||||
ss1Rev2, err := history.NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 2, ss1.Status.CollisionCount)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ss1Rev2.Namespace = ss1.Namespace
|
||||
ss1Rev2.OwnerReferences = []metav1.OwnerReference{}
|
||||
|
||||
ssc, spc := newFakeStatefulSetController(ss1, ss1Rev1, ss1Rev2)
|
||||
|
||||
spc.revisionsIndexer.Add(ss1Rev1)
|
||||
spc.revisionsIndexer.Add(ss1Rev2)
|
||||
|
||||
err = ssc.adoptOrphanRevisions(ss1)
|
||||
if err != nil {
|
||||
t.Errorf("adoptOrphanRevisions() error: %v", err)
|
||||
}
|
||||
|
||||
if revisions, err := ssc.control.ListRevisions(ss1); err != nil {
|
||||
t.Errorf("ListRevisions() error: %v", err)
|
||||
} else {
|
||||
var adopted bool
|
||||
for i := range revisions {
|
||||
if revisions[i].Name == ss1Rev2.Name && metav1.GetControllerOf(revisions[i]) != nil {
|
||||
adopted = true
|
||||
}
|
||||
}
|
||||
if !adopted {
|
||||
t.Error("adoptOrphanRevisions() not adopt orphan revisions")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPodsForStatefulSetRelease(t *testing.T) {
|
||||
set := newStatefulSet(3)
|
||||
ssc, spc := newFakeStatefulSetController(set)
|
||||
@ -575,7 +622,7 @@ func TestGetPodsForStatefulSetRelease(t *testing.T) {
|
||||
func newFakeStatefulSetController(initialObjects ...runtime.Object) (*StatefulSetController, *fakeStatefulPodControl) {
|
||||
client := fake.NewSimpleClientset(initialObjects...)
|
||||
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
|
||||
fpc := newFakeStatefulPodControl(informerFactory.Core().V1().Pods(), informerFactory.Apps().V1().StatefulSets())
|
||||
fpc := newFakeStatefulPodControl(informerFactory.Core().V1().Pods(), informerFactory.Apps().V1().StatefulSets(), informerFactory.Apps().V1().ControllerRevisions())
|
||||
ssu := newFakeStatefulSetStatusUpdater(informerFactory.Apps().V1().StatefulSets())
|
||||
ssc := NewStatefulSetController(
|
||||
informerFactory.Core().V1().Pods(),
|
||||
@ -710,3 +757,12 @@ func scaleDownStatefulSetController(set *apps.StatefulSet, ssc *StatefulSetContr
|
||||
}
|
||||
return assertMonotonicInvariants(set, spc)
|
||||
}
|
||||
|
||||
func rawTemplate(template *v1.PodTemplateSpec) runtime.RawExtension {
|
||||
buf := new(bytes.Buffer)
|
||||
enc := json.NewEncoder(buf)
|
||||
if err := enc.Encode(template); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return runtime.RawExtension{Raw: buf.Bytes()}
|
||||
}
|
||||
|
@ -469,3 +469,73 @@ func newStatefulSet(replicas int) *apps.StatefulSet {
|
||||
}
|
||||
return newStatefulSetWithVolumes(replicas, "foo", petMounts, podMounts)
|
||||
}
|
||||
|
||||
func newStatefulSetWithLabels(replicas int, name string, uid types.UID, labels map[string]string) *apps.StatefulSet {
|
||||
// Converting all the map-only selectors to set-based selectors.
|
||||
var testMatchExpressions []metav1.LabelSelectorRequirement
|
||||
for key, value := range labels {
|
||||
sel := metav1.LabelSelectorRequirement{
|
||||
Key: key,
|
||||
Operator: metav1.LabelSelectorOpIn,
|
||||
Values: []string{value},
|
||||
}
|
||||
testMatchExpressions = append(testMatchExpressions, sel)
|
||||
}
|
||||
return &apps.StatefulSet{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "StatefulSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: v1.NamespaceDefault,
|
||||
UID: uid,
|
||||
},
|
||||
Spec: apps.StatefulSetSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
// Purposely leaving MatchLabels nil, so to ensure it will break if any link
|
||||
// in the chain ignores the set-based MatchExpressions.
|
||||
MatchLabels: nil,
|
||||
MatchExpressions: testMatchExpressions,
|
||||
},
|
||||
Replicas: func() *int32 { i := int32(replicas); return &i }(),
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "nginx",
|
||||
Image: "nginx",
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{Name: "datadir", MountPath: "/tmp/"},
|
||||
{Name: "home", MountPath: "/home"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Volumes: []v1.Volume{{
|
||||
Name: "home",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: fmt.Sprintf("/tmp/%v", "home"),
|
||||
},
|
||||
}}},
|
||||
},
|
||||
},
|
||||
VolumeClaimTemplates: []v1.PersistentVolumeClaim{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "datadir"},
|
||||
Spec: v1.PersistentVolumeClaimSpec{
|
||||
Resources: v1.ResourceRequirements{
|
||||
Requests: v1.ResourceList{
|
||||
v1.ResourceStorage: *resource.NewQuantity(1, resource.BinarySI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ServiceName: "governingsvc",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user