github.com/diptanu/nomad@v0.5.7-0.20170516172507-d72e86cbe3d9/client/restarts_test.go (about) 1 package client 2 3 import ( 4 "fmt" 5 "testing" 6 "time" 7 8 cstructs "github.com/hashicorp/nomad/client/driver/structs" 9 "github.com/hashicorp/nomad/nomad/structs" 10 ) 11 12 func testPolicy(success bool, mode string) *structs.RestartPolicy { 13 return &structs.RestartPolicy{ 14 Interval: 2 * time.Minute, 15 Delay: 1 * time.Second, 16 Attempts: 3, 17 Mode: mode, 18 } 19 } 20 21 // withinJitter is a helper that returns whether the returned delay is within 22 // the jitter. 23 func withinJitter(expected, actual time.Duration) bool { 24 return float64((actual.Nanoseconds()-expected.Nanoseconds())/ 25 expected.Nanoseconds()) <= jitter 26 } 27 28 func testWaitResult(exit int) *cstructs.WaitResult { 29 return cstructs.NewWaitResult(exit, 0, nil) 30 } 31 32 func TestClient_RestartTracker_ModeDelay(t *testing.T) { 33 t.Parallel() 34 p := testPolicy(true, structs.RestartPolicyModeDelay) 35 rt := newRestartTracker(p, structs.JobTypeService) 36 for i := 0; i < p.Attempts; i++ { 37 state, when := rt.SetWaitResult(testWaitResult(127)).GetState() 38 if state != structs.TaskRestarting { 39 t.Fatalf("NextRestart() returned %v, want %v", state, structs.TaskRestarting) 40 } 41 if !withinJitter(p.Delay, when) { 42 t.Fatalf("NextRestart() returned %v; want %v+jitter", when, p.Delay) 43 } 44 } 45 46 // Follow up restarts should cause delay. 47 for i := 0; i < 3; i++ { 48 state, when := rt.SetWaitResult(testWaitResult(127)).GetState() 49 if state != structs.TaskRestarting { 50 t.Fail() 51 } 52 if !(when > p.Delay && when <= p.Interval) { 53 t.Fatalf("NextRestart() returned %v; want > %v and <= %v", when, p.Delay, p.Interval) 54 } 55 } 56 } 57 58 func TestClient_RestartTracker_ModeFail(t *testing.T) { 59 t.Parallel() 60 p := testPolicy(true, structs.RestartPolicyModeFail) 61 rt := newRestartTracker(p, structs.JobTypeSystem) 62 for i := 0; i < p.Attempts; i++ { 63 state, when := rt.SetWaitResult(testWaitResult(127)).GetState() 64 if state != structs.TaskRestarting { 65 t.Fatalf("NextRestart() returned %v, want %v", state, structs.TaskRestarting) 66 } 67 if !withinJitter(p.Delay, when) { 68 t.Fatalf("NextRestart() returned %v; want %v+jitter", when, p.Delay) 69 } 70 } 71 72 // Next restart should cause fail 73 if state, _ := rt.SetWaitResult(testWaitResult(127)).GetState(); state != structs.TaskNotRestarting { 74 t.Fatalf("NextRestart() returned %v; want %v", state, structs.TaskNotRestarting) 75 } 76 } 77 78 func TestClient_RestartTracker_NoRestartOnSuccess(t *testing.T) { 79 t.Parallel() 80 p := testPolicy(false, structs.RestartPolicyModeDelay) 81 rt := newRestartTracker(p, structs.JobTypeBatch) 82 if state, _ := rt.SetWaitResult(testWaitResult(0)).GetState(); state != structs.TaskTerminated { 83 t.Fatalf("NextRestart() returned %v, expected: %v", state, structs.TaskTerminated) 84 } 85 } 86 87 func TestClient_RestartTracker_ZeroAttempts(t *testing.T) { 88 t.Parallel() 89 p := testPolicy(true, structs.RestartPolicyModeFail) 90 p.Attempts = 0 91 rt := newRestartTracker(p, structs.JobTypeService) 92 if state, when := rt.SetWaitResult(testWaitResult(1)).GetState(); state != structs.TaskNotRestarting { 93 t.Fatalf("expect no restart, got restart/delay: %v", when) 94 } 95 } 96 97 func TestClient_RestartTracker_RestartTriggered(t *testing.T) { 98 t.Parallel() 99 p := testPolicy(true, structs.RestartPolicyModeFail) 100 p.Attempts = 0 101 rt := newRestartTracker(p, structs.JobTypeService) 102 if state, when := rt.SetRestartTriggered().GetState(); state != structs.TaskRestarting && when != 0 { 103 t.Fatalf("expect restart immediately, got %v %v", state, when) 104 } 105 } 106 107 func TestClient_RestartTracker_StartError_Recoverable_Fail(t *testing.T) { 108 t.Parallel() 109 p := testPolicy(true, structs.RestartPolicyModeFail) 110 rt := newRestartTracker(p, structs.JobTypeSystem) 111 recErr := structs.NewRecoverableError(fmt.Errorf("foo"), true) 112 for i := 0; i < p.Attempts; i++ { 113 state, when := rt.SetStartError(recErr).GetState() 114 if state != structs.TaskRestarting { 115 t.Fatalf("NextRestart() returned %v, want %v", state, structs.TaskRestarting) 116 } 117 if !withinJitter(p.Delay, when) { 118 t.Fatalf("NextRestart() returned %v; want %v+jitter", when, p.Delay) 119 } 120 } 121 122 // Next restart should cause fail 123 if state, _ := rt.SetStartError(recErr).GetState(); state != structs.TaskNotRestarting { 124 t.Fatalf("NextRestart() returned %v; want %v", state, structs.TaskNotRestarting) 125 } 126 } 127 128 func TestClient_RestartTracker_StartError_Recoverable_Delay(t *testing.T) { 129 t.Parallel() 130 p := testPolicy(true, structs.RestartPolicyModeDelay) 131 rt := newRestartTracker(p, structs.JobTypeSystem) 132 recErr := structs.NewRecoverableError(fmt.Errorf("foo"), true) 133 for i := 0; i < p.Attempts; i++ { 134 state, when := rt.SetStartError(recErr).GetState() 135 if state != structs.TaskRestarting { 136 t.Fatalf("NextRestart() returned %v, want %v", state, structs.TaskRestarting) 137 } 138 if !withinJitter(p.Delay, when) { 139 t.Fatalf("NextRestart() returned %v; want %v+jitter", when, p.Delay) 140 } 141 } 142 143 // Next restart should cause delay 144 state, when := rt.SetStartError(recErr).GetState() 145 if state != structs.TaskRestarting { 146 t.Fatalf("NextRestart() returned %v; want %v", state, structs.TaskRestarting) 147 } 148 if !(when > p.Delay && when <= p.Interval) { 149 t.Fatalf("NextRestart() returned %v; want > %v and <= %v", when, p.Delay, p.Interval) 150 } 151 }