diff --git a/pkg/controller/replication_controller.go b/pkg/controller/replication_controller.go index 39691cfbdc0..35f6499b9c9 100644 --- a/pkg/controller/replication_controller.go +++ b/pkg/controller/replication_controller.go @@ -148,7 +148,21 @@ func (rm *ReplicationManager) handleWatchResponse(response *etcd.Response) (*api } else { return nil, fmt.Errorf("response node is null %#v", response) } + } else if response.Action == "delete" { + // Ensure that the final state of a replication controller is applied before it is deleted. + // Otherwise, a replication controller could be modified and then deleted (for example, from 3 to 0 + // replicas), and it would be non-deterministic which of its pods continued to exist. + if response.PrevNode != nil { + var controllerSpec api.ReplicationController + if err := json.Unmarshal([]byte(response.PrevNode.Value), &controllerSpec); err != nil { + return nil, err + } + return &controllerSpec, nil + } else { + return nil, fmt.Errorf("previous node is null %#v", response) + } } + return nil, nil } diff --git a/pkg/controller/replication_controller_test.go b/pkg/controller/replication_controller_test.go index a7e6f621b80..b9be3e1ad12 100644 --- a/pkg/controller/replication_controller_test.go +++ b/pkg/controller/replication_controller_test.go @@ -231,7 +231,7 @@ func TestHandleWatchResponseNotSet(t *testing.T) { manager := MakeReplicationManager(nil, &client) manager.podControl = &fakePodControl _, err := manager.handleWatchResponse(&etcd.Response{ - Action: "delete", + Action: "update", }) expectNoError(t, err) } @@ -319,6 +319,40 @@ func TestHandleWatchResponse(t *testing.T) { } } +func TestHandleWatchResponseDelete(t *testing.T) { + body, _ := json.Marshal(makePodList(2)) + fakeHandler := util.FakeHandler{ + StatusCode: 200, + ResponseBody: string(body), + } + testServer := httptest.NewTLSServer(&fakeHandler) + client := client.Client{ + Host: testServer.URL, + } + + fakePodControl := FakePodControl{} + + manager := MakeReplicationManager(nil, &client) + manager.podControl = &fakePodControl + + controller := makeReplicationController(2) + + data, err := json.Marshal(controller) + expectNoError(t, err) + controllerOut, err := manager.handleWatchResponse(&etcd.Response{ + Action: "delete", + PrevNode: &etcd.Node{ + Value: string(data), + }, + }) + if err != nil { + t.Errorf("Unexpected error: %#v", err) + } + if !reflect.DeepEqual(controller, *controllerOut) { + t.Errorf("Unexpected mismatch. Expected %#v, Saw: %#v", controller, controllerOut) + } +} + func TestSyncronize(t *testing.T) { controllerSpec1 := api.ReplicationController{ DesiredState: api.ReplicationControllerState{