github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/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  }