func PTestUpdateRecovery(t *testing.T) { // Test recovery from interruption rc := oldRc(2) rcExisting := newRc(1, 3) output := `Continuing update with existing controller foo-v2. Scaling up foo-v2 from 1 to 3, scaling down foo-v1 from 2 to 0 (scale up first by 1 each interval) Scaling foo-v2 to 2 Scaling foo-v1 to 1 Scaling foo-v2 to 3 Scaling foo-v2 to 0 Update succeeded. Deleting foo-v1 ` responses := []fakeResponse{ // Existing newRc {rcExisting, nil}, // scaling iteration {newRc(2, 2), nil}, {oldRc(1), nil}, // scaling iteration {newRc(3, 3), nil}, {oldRc(0), nil}, // cleanup annotations {newRc(3, 3), nil}, {newRc(3, 3), nil}, {newRc(3, 3), nil}, } client := NewRollingUpdaterClient(fakeClientFor("default", responses)) updater := RollingUpdater{ c: client, ns: "default", scaleAndWait: func(rc *api.ReplicationController, retry *RetryParams, wait *RetryParams) (*api.ReplicationController, error) { return client.GetReplicationController(rc.Namespace, rc.Name) }, } var buffer bytes.Buffer config := &RollingUpdaterConfig{ Out: &buffer, OldRc: rc, NewRc: rcExisting, UpdatePeriod: 0, Interval: time.Millisecond, Timeout: time.Millisecond, CleanupPolicy: DeleteRollingUpdateCleanupPolicy, UpdateAcceptor: DefaultUpdateAcceptor, } if err := updater.Update(config); err != nil { t.Errorf("Update failed: %v", err) } if buffer.String() != output { t.Errorf("Output was not as expected. Expected:\n%s\nGot:\n%s", output, buffer.String()) } }
func TestUpdate(t *testing.T) { // Helpers Percent := func(p int) *int { return &p } var NilPercent *int // Scenarios tests := []struct { oldRc, newRc *api.ReplicationController accepted bool percent *int responses []fakeResponse output string }{ { oldRc: oldRc(1), newRc: newRc(1, 1), accepted: true, percent: NilPercent, responses: []fakeResponse{ // no existing newRc {nil, fmt.Errorf("not found")}, // scaling iteration {newRc(1, 1), nil}, {oldRc(0), nil}, // cleanup annotations {newRc(1, 1), nil}, {newRc(1, 1), nil}, {newRc(1, 1), nil}, }, output: `Creating foo-v2 Scaling up foo-v2 from 0 to 1, scaling down foo-v1 from 1 to 0 (scale up first by 1 each interval) Scaling foo-v2 up to 1 Scaling foo-v1 down to 0 Update succeeded. Deleting foo-v1 `, }, { oldRc: oldRc(1), newRc: newRc(1, 1), accepted: true, percent: NilPercent, responses: []fakeResponse{ // no existing newRc {nil, fmt.Errorf("not found")}, // scaling iteration {newRc(1, 1), nil}, {oldRc(0), nil}, // cleanup annotations {newRc(1, 1), nil}, {newRc(1, 1), nil}, {newRc(1, 1), nil}, }, output: `Creating foo-v2 Scaling up foo-v2 from 0 to 1, scaling down foo-v1 from 1 to 0 (scale up first by 1 each interval) Scaling foo-v2 up to 1 Scaling foo-v1 down to 0 Update succeeded. Deleting foo-v1 `, }, { oldRc: oldRc(2), newRc: newRc(2, 2), accepted: true, percent: NilPercent, responses: []fakeResponse{ // no existing newRc {nil, fmt.Errorf("not found")}, // scaling iteration {newRc(1, 2), nil}, {oldRc(1), nil}, // scaling iteration {newRc(2, 2), nil}, {oldRc(0), nil}, // cleanup annotations {newRc(2, 2), nil}, {newRc(2, 2), nil}, {newRc(1, 1), nil}, }, output: `Creating foo-v2 Scaling up foo-v2 from 0 to 2, scaling down foo-v1 from 2 to 0 (scale up first by 1 each interval) Scaling foo-v2 up to 1 Scaling foo-v1 down to 1 Scaling foo-v2 up to 2 Scaling foo-v1 down to 0 Update succeeded. Deleting foo-v1 `, }, { oldRc: oldRc(2), newRc: newRc(7, 7), accepted: true, percent: NilPercent, responses: []fakeResponse{ // no existing newRc {nil, fmt.Errorf("not found")}, // scaling iteration {newRc(1, 7), nil}, {oldRc(1), nil}, // scaling iteration {newRc(2, 7), nil}, {oldRc(0), nil}, // final scale on newRc {newRc(7, 7), nil}, // cleanup annotations {newRc(7, 7), nil}, {newRc(7, 7), nil}, {newRc(7, 7), nil}, }, output: `Creating foo-v2 Scaling up foo-v2 from 0 to 7, scaling down foo-v1 from 2 to 0 (scale up first by 1 each interval) Scaling foo-v2 up to 1 Scaling foo-v1 down to 1 Scaling foo-v2 up to 2 Scaling foo-v1 down to 0 Scaling foo-v2 up to 7 Update succeeded. Deleting foo-v1 `, }, { oldRc: oldRc(7), newRc: newRc(2, 2), accepted: true, percent: NilPercent, responses: []fakeResponse{ // no existing newRc {nil, fmt.Errorf("not found")}, // scaling iteration {newRc(1, 2), nil}, {oldRc(6), nil}, // scaling iteration {newRc(2, 2), nil}, {oldRc(0), nil}, // cleanup annotations {newRc(2, 2), nil}, {newRc(2, 2), nil}, {newRc(2, 2), nil}, }, output: `Creating foo-v2 Scaling up foo-v2 from 0 to 2, scaling down foo-v1 from 7 to 0 (scale up first by 1 each interval) Scaling foo-v2 up to 1 Scaling foo-v1 down to 6 Scaling foo-v2 up to 2 Scaling foo-v1 down to 0 Update succeeded. Deleting foo-v1 `, }, { oldRc: oldRc(7), newRc: newRc(2, 2), accepted: false, percent: NilPercent, responses: []fakeResponse{ // no existing newRc {nil, fmt.Errorf("not found")}, // scaling iteration (only up occurs since the update is rejected) {newRc(1, 2), nil}, }, output: `Creating foo-v2 Scaling up foo-v2 from 0 to 2, scaling down foo-v1 from 7 to 0 (scale up first by 1 each interval) Scaling foo-v2 up to 1 `, }, { oldRc: oldRc(10), newRc: newRc(10, 10), accepted: true, percent: Percent(20), responses: []fakeResponse{ // no existing newRc {nil, fmt.Errorf("not found")}, // scaling iteration {newRc(2, 10), nil}, {oldRc(8), nil}, // scaling iteration {newRc(4, 10), nil}, {oldRc(6), nil}, // scaling iteration {newRc(6, 10), nil}, {oldRc(4), nil}, // scaling iteration {newRc(8, 10), nil}, {oldRc(2), nil}, // scaling iteration {newRc(10, 10), nil}, {oldRc(0), nil}, // cleanup annotations {newRc(10, 10), nil}, {newRc(10, 10), nil}, {newRc(10, 10), nil}, }, output: `Creating foo-v2 Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (scale up first by 2 each interval) Scaling foo-v2 up to 2 Scaling foo-v1 down to 8 Scaling foo-v2 up to 4 Scaling foo-v1 down to 6 Scaling foo-v2 up to 6 Scaling foo-v1 down to 4 Scaling foo-v2 up to 8 Scaling foo-v1 down to 2 Scaling foo-v2 up to 10 Scaling foo-v1 down to 0 Update succeeded. Deleting foo-v1 `, }, { oldRc: oldRc(2), newRc: newRc(6, 6), accepted: true, percent: Percent(50), responses: []fakeResponse{ // no existing newRc {nil, fmt.Errorf("not found")}, // scaling iteration {newRc(3, 6), nil}, {oldRc(0), nil}, // scaling iteration {newRc(6, 6), nil}, // cleanup annotations {newRc(6, 6), nil}, {newRc(6, 6), nil}, {newRc(6, 6), nil}, }, output: `Creating foo-v2 Scaling up foo-v2 from 0 to 6, scaling down foo-v1 from 2 to 0 (scale up first by 3 each interval) Scaling foo-v2 up to 3 Scaling foo-v1 down to 0 Scaling foo-v2 up to 6 Update succeeded. Deleting foo-v1 `, }, { oldRc: oldRc(10), newRc: newRc(3, 3), accepted: true, percent: Percent(50), responses: []fakeResponse{ // no existing newRc {nil, fmt.Errorf("not found")}, // scaling iteration {newRc(2, 3), nil}, {oldRc(8), nil}, // scaling iteration {newRc(3, 3), nil}, {oldRc(0), nil}, // cleanup annotations {newRc(3, 3), nil}, {newRc(3, 3), nil}, {newRc(3, 3), nil}, }, output: `Creating foo-v2 Scaling up foo-v2 from 0 to 3, scaling down foo-v1 from 10 to 0 (scale up first by 2 each interval) Scaling foo-v2 up to 2 Scaling foo-v1 down to 8 Scaling foo-v2 up to 3 Scaling foo-v1 down to 0 Update succeeded. Deleting foo-v1 `, }, { oldRc: oldRc(4), newRc: newRc(4, 4), accepted: true, percent: Percent(-50), responses: []fakeResponse{ // no existing newRc {nil, fmt.Errorf("not found")}, // scaling iteration {oldRc(2), nil}, {newRc(2, 4), nil}, // scaling iteration {oldRc(0), nil}, {newRc(4, 4), nil}, // cleanup annotations {newRc(4, 4), nil}, {newRc(4, 4), nil}, {newRc(4, 4), nil}, }, output: `Creating foo-v2 Scaling up foo-v2 from 0 to 4, scaling down foo-v1 from 4 to 0 (scale down first by 2 each interval) Scaling foo-v1 down to 2 Scaling foo-v2 up to 2 Scaling foo-v1 down to 0 Scaling foo-v2 up to 4 Update succeeded. Deleting foo-v1 `, }, { oldRc: oldRc(2), newRc: newRc(4, 4), accepted: true, percent: Percent(-50), responses: []fakeResponse{ // no existing newRc {nil, fmt.Errorf("not found")}, // scaling iteration {oldRc(0), nil}, {newRc(4, 4), nil}, // cleanup annotations {newRc(4, 4), nil}, {newRc(4, 4), nil}, {newRc(4, 4), nil}, }, output: `Creating foo-v2 Scaling up foo-v2 from 0 to 4, scaling down foo-v1 from 2 to 0 (scale down first by 2 each interval) Scaling foo-v1 down to 0 Scaling foo-v2 up to 4 Update succeeded. Deleting foo-v1 `, }, { oldRc: oldRc(4), newRc: newRc(2, 2), accepted: true, percent: Percent(-50), responses: []fakeResponse{ // no existing newRc {nil, fmt.Errorf("not found")}, // scaling iteration {oldRc(3), nil}, {newRc(1, 2), nil}, // scaling iteration {oldRc(2), nil}, {newRc(2, 2), nil}, // scaling iteration {oldRc(0), nil}, // cleanup annotations {newRc(2, 2), nil}, {newRc(2, 2), nil}, {newRc(2, 2), nil}, }, output: `Creating foo-v2 Scaling up foo-v2 from 0 to 2, scaling down foo-v1 from 4 to 0 (scale down first by 1 each interval) Scaling foo-v1 down to 3 Scaling foo-v2 up to 1 Scaling foo-v1 down to 2 Scaling foo-v2 up to 2 Scaling foo-v1 down to 0 Update succeeded. Deleting foo-v1 `, }, { oldRc: oldRc(4), newRc: newRc(4, 4), accepted: true, percent: Percent(-100), responses: []fakeResponse{ // no existing newRc {nil, fmt.Errorf("not found")}, // scaling iteration {oldRc(0), nil}, {newRc(4, 4), nil}, // cleanup annotations {newRc(4, 4), nil}, {newRc(4, 4), nil}, {newRc(4, 4), nil}, }, output: `Creating foo-v2 Scaling up foo-v2 from 0 to 4, scaling down foo-v1 from 4 to 0 (scale down first by 4 each interval) Scaling foo-v1 down to 0 Scaling foo-v2 up to 4 Update succeeded. Deleting foo-v1 `, }, } for _, test := range tests { client := NewRollingUpdaterClient(fakeClientFor("default", test.responses)) updater := RollingUpdater{ c: client, ns: "default", scaleAndWait: func(rc *api.ReplicationController, retry *RetryParams, wait *RetryParams) (*api.ReplicationController, error) { return client.GetReplicationController(rc.Namespace, rc.Name) }, } var buffer bytes.Buffer acceptor := &testAcceptor{ accept: func(rc *api.ReplicationController) error { if test.accepted { return nil } return fmt.Errorf("rejecting controller %s", rc.Name) }, } config := &RollingUpdaterConfig{ Out: &buffer, OldRc: test.oldRc, NewRc: test.newRc, UpdatePeriod: 0, Interval: time.Millisecond, Timeout: time.Millisecond, CleanupPolicy: DeleteRollingUpdateCleanupPolicy, UpdateAcceptor: acceptor, UpdatePercent: test.percent, } err := updater.Update(config) if test.accepted && err != nil { t.Errorf("Update failed: %v", err) } if !test.accepted && err == nil { t.Errorf("Expected update to fail") } if buffer.String() != test.output { t.Errorf("Bad output. expected:\n%s\ngot:\n%s", test.output, buffer.String()) } } }