mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-01-04 15:05:20 +00:00
Merge pull request #29971 from caesarxuchao/fix-kubectl-rolling-update-with-gc
Automatic merge from submit-queue [GarbageCollector] Fix kubectl rolling-update to work with GC This changes the order of the [Rename()](https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/rolling_updater.go#L532) function. After the change, Rename() first deletes the old RC and orphans its pods, then creates the new RC, which will then have a chance to adopt the orphaned pods. This also fixes the "should support rolling-update to same image" [test](https://github.com/kubernetes/kubernetes/blob/master/test/e2e/kubectl.go#L915) when the garbage collector is on. Here is the detailed explanation on why the test would have failed: `kubectl rolling-update` will [rename](https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/rolling_updater.go#L532-L546) the RC. It first creates the an identical RC (including spec.selectors) with the new name, then it deletes the existing RC. When GC is turned on, the newly created RC cannot adopt the existing pod, because it has a controllerRef pointing to the exising RC, so the new RC will create new pods and expect to see the creation. However, the new RC and the old RC have the same selector, so sometimes the old RC, instead of the new RC, has its [expectation lowered](https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/replication/replication_controller.go#L346-L362), the new RC's expectation will stuck forever. The e2e test then times out when executing `kubectl delete newRC`, because there is the new RC will not scale down as its expectation is not fulfilled. A side-note, we should fix [rm.getPodController()](https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/replication/replication_controller.go#L346) to respect pod's controllerref, that will prevent similar bugs. Also note that an old version `kubectl rolling-update` will not work with the GC. We cannot fix that.
This commit is contained in:
@@ -27,6 +27,7 @@ import (
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
apitesting "k8s.io/kubernetes/pkg/api/testing"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
@@ -1143,20 +1144,27 @@ func TestRollingUpdater_cleanupWithClients(t *testing.T) {
|
||||
"delete",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rename",
|
||||
policy: RenameRollingUpdateCleanupPolicy,
|
||||
responses: []runtime.Object{rcExisting},
|
||||
expected: []string{
|
||||
"get",
|
||||
"update",
|
||||
"get",
|
||||
"get",
|
||||
"delete",
|
||||
"create",
|
||||
"delete",
|
||||
},
|
||||
},
|
||||
//{
|
||||
// This cases is separated to a standalone
|
||||
// TestRollingUpdater_cleanupWithClients_Rename. We have to do this
|
||||
// because the unversioned fake client is unable to delete objects.
|
||||
// TODO: uncomment this case when the unversioned fake client uses
|
||||
// pkg/client/testing/core.
|
||||
// {
|
||||
// name: "rename",
|
||||
// policy: RenameRollingUpdateCleanupPolicy,
|
||||
// responses: []runtime.Object{rcExisting},
|
||||
// expected: []string{
|
||||
// "get",
|
||||
// "update",
|
||||
// "get",
|
||||
// "get",
|
||||
// "delete",
|
||||
// "create",
|
||||
// "delete",
|
||||
// },
|
||||
// },
|
||||
//},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -1189,6 +1197,39 @@ func TestRollingUpdater_cleanupWithClients(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestRollingUpdater_cleanupWithClients_Rename tests the rename cleanup policy. It's separated to
|
||||
// a standalone test because the unversioned fake client is unable to delete
|
||||
// objects.
|
||||
// TODO: move this test back to TestRollingUpdater_cleanupWithClients
|
||||
// when the fake client uses pkg/client/testing/core in the future.
|
||||
func TestRollingUpdater_cleanupWithClients_Rename(t *testing.T) {
|
||||
rc := oldRc(2, 2)
|
||||
rcExisting := newRc(1, 3)
|
||||
expectedActions := []string{"delete", "get", "create"}
|
||||
fake := &testclient.Fake{}
|
||||
fake.AddReactor("*", "*", func(action testclient.Action) (handled bool, ret runtime.Object, err error) {
|
||||
switch action.(type) {
|
||||
case testclient.CreateAction:
|
||||
return true, nil, nil
|
||||
case testclient.GetAction:
|
||||
return true, nil, errors.NewNotFound(unversioned.GroupResource{}, "")
|
||||
case testclient.DeleteAction:
|
||||
return true, nil, nil
|
||||
}
|
||||
return false, nil, nil
|
||||
})
|
||||
|
||||
err := Rename(fake, rcExisting, rc.Name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for j, action := range fake.Actions() {
|
||||
if e, a := expectedActions[j], action.GetVerb(); e != a {
|
||||
t.Errorf("unexpected action: expected %s, got %s", e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindSourceController(t *testing.T) {
|
||||
ctrl1 := api.ReplicationController{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
|
||||
Reference in New Issue
Block a user