github.com/ferranbt/nomad@v0.9.3-0.20190607002617-85c449b7667c/plugins/drivers/testutils/testing_test.go (about) 1 package testutils 2 3 import ( 4 "bytes" 5 "context" 6 "sync" 7 "testing" 8 "time" 9 10 "github.com/hashicorp/nomad/nomad/structs" 11 "github.com/hashicorp/nomad/plugins/drivers" 12 pstructs "github.com/hashicorp/nomad/plugins/shared/structs" 13 "github.com/stretchr/testify/require" 14 "github.com/ugorji/go/codec" 15 ) 16 17 var _ drivers.DriverPlugin = (*MockDriver)(nil) 18 19 // Very simple test to ensure the test harness works as expected 20 func TestDriverHarness(t *testing.T) { 21 handle := &drivers.TaskHandle{Config: &drivers.TaskConfig{Name: "mock"}} 22 d := &MockDriver{ 23 StartTaskF: func(task *drivers.TaskConfig) (*drivers.TaskHandle, *drivers.DriverNetwork, error) { 24 return handle, nil, nil 25 }, 26 } 27 harness := NewDriverHarness(t, d) 28 defer harness.Kill() 29 actual, _, err := harness.StartTask(&drivers.TaskConfig{}) 30 require.NoError(t, err) 31 require.Equal(t, handle.Config.Name, actual.Config.Name) 32 } 33 34 type testDriverState struct { 35 Pid int 36 Log string 37 } 38 39 func TestBaseDriver_Fingerprint(t *testing.T) { 40 t.Parallel() 41 require := require.New(t) 42 43 fingerprints := []*drivers.Fingerprint{ 44 { 45 Attributes: map[string]*pstructs.Attribute{"foo": pstructs.NewStringAttribute("bar")}, 46 Health: drivers.HealthStateUnhealthy, 47 HealthDescription: "starting up", 48 }, 49 { 50 Attributes: map[string]*pstructs.Attribute{"foo": pstructs.NewStringAttribute("bar")}, 51 Health: drivers.HealthStateHealthy, 52 HealthDescription: "running", 53 }, 54 } 55 56 var complete bool 57 impl := &MockDriver{ 58 FingerprintF: func(ctx context.Context) (<-chan *drivers.Fingerprint, error) { 59 ch := make(chan *drivers.Fingerprint) 60 go func() { 61 defer close(ch) 62 ch <- fingerprints[0] 63 time.Sleep(500 * time.Millisecond) 64 ch <- fingerprints[1] 65 complete = true 66 }() 67 return ch, nil 68 }, 69 } 70 71 harness := NewDriverHarness(t, impl) 72 defer harness.Kill() 73 74 ch, err := harness.Fingerprint(context.Background()) 75 require.NoError(err) 76 77 var wg sync.WaitGroup 78 wg.Add(1) 79 go func() { 80 defer wg.Done() 81 select { 82 case f := <-ch: 83 require.Exactly(f, fingerprints[0]) 84 case <-time.After(1 * time.Second): 85 require.Fail("did not receive fingerprint[0]") 86 } 87 select { 88 case f := <-ch: 89 require.Exactly(f, fingerprints[1]) 90 case <-time.After(1 * time.Second): 91 require.Fail("did not receive fingerprint[1]") 92 } 93 }() 94 require.False(complete) 95 wg.Wait() 96 require.True(complete) 97 98 } 99 100 func TestBaseDriver_RecoverTask(t *testing.T) { 101 t.Parallel() 102 require := require.New(t) 103 104 // build driver state and encode it into proto msg 105 state := testDriverState{Pid: 1, Log: "foo"} 106 var buf bytes.Buffer 107 enc := codec.NewEncoder(&buf, structs.MsgpackHandle) 108 enc.Encode(state) 109 110 // mock the RecoverTask driver call 111 impl := &MockDriver{ 112 RecoverTaskF: func(h *drivers.TaskHandle) error { 113 var actual testDriverState 114 require.NoError(h.GetDriverState(&actual)) 115 require.Equal(state, actual) 116 return nil 117 }, 118 } 119 120 harness := NewDriverHarness(t, impl) 121 defer harness.Kill() 122 123 handle := &drivers.TaskHandle{ 124 DriverState: buf.Bytes(), 125 } 126 err := harness.RecoverTask(handle) 127 require.NoError(err) 128 } 129 130 func TestBaseDriver_StartTask(t *testing.T) { 131 t.Parallel() 132 require := require.New(t) 133 134 cfg := &drivers.TaskConfig{ 135 ID: "foo", 136 } 137 state := &testDriverState{Pid: 1, Log: "log"} 138 var handle *drivers.TaskHandle 139 impl := &MockDriver{ 140 StartTaskF: func(c *drivers.TaskConfig) (*drivers.TaskHandle, *drivers.DriverNetwork, error) { 141 handle = drivers.NewTaskHandle(1) 142 handle.Config = c 143 handle.State = drivers.TaskStateRunning 144 handle.SetDriverState(state) 145 return handle, nil, nil 146 }, 147 } 148 149 harness := NewDriverHarness(t, impl) 150 defer harness.Kill() 151 resp, _, err := harness.StartTask(cfg) 152 require.NoError(err) 153 require.Equal(cfg.ID, resp.Config.ID) 154 require.Equal(handle.State, resp.State) 155 156 var actualState testDriverState 157 require.NoError(resp.GetDriverState(&actualState)) 158 require.Equal(*state, actualState) 159 160 } 161 162 func TestBaseDriver_WaitTask(t *testing.T) { 163 t.Parallel() 164 require := require.New(t) 165 166 result := &drivers.ExitResult{ExitCode: 1, Signal: 9} 167 168 signalTask := make(chan struct{}) 169 170 impl := &MockDriver{ 171 WaitTaskF: func(_ context.Context, id string) (<-chan *drivers.ExitResult, error) { 172 ch := make(chan *drivers.ExitResult) 173 go func() { 174 <-signalTask 175 ch <- result 176 }() 177 return ch, nil 178 }, 179 } 180 181 harness := NewDriverHarness(t, impl) 182 defer harness.Kill() 183 var wg sync.WaitGroup 184 wg.Add(1) 185 var finished bool 186 go func() { 187 defer wg.Done() 188 ch, err := harness.WaitTask(context.TODO(), "foo") 189 require.NoError(err) 190 actualResult := <-ch 191 finished = true 192 require.Exactly(result, actualResult) 193 }() 194 require.False(finished) 195 close(signalTask) 196 wg.Wait() 197 require.True(finished) 198 } 199 200 func TestBaseDriver_TaskEvents(t *testing.T) { 201 t.Parallel() 202 require := require.New(t) 203 204 now := time.Now().UTC().Truncate(time.Millisecond) 205 events := []*drivers.TaskEvent{ 206 { 207 TaskID: "abc", 208 Timestamp: now, 209 Annotations: map[string]string{"foo": "bar"}, 210 Message: "starting", 211 }, 212 { 213 TaskID: "xyz", 214 Timestamp: now.Add(2 * time.Second), 215 Annotations: map[string]string{"foo": "bar"}, 216 Message: "starting", 217 }, 218 { 219 TaskID: "xyz", 220 Timestamp: now.Add(3 * time.Second), 221 Annotations: map[string]string{"foo": "bar"}, 222 Message: "running", 223 }, 224 { 225 TaskID: "abc", 226 Timestamp: now.Add(4 * time.Second), 227 Annotations: map[string]string{"foo": "bar"}, 228 Message: "running", 229 }, 230 } 231 232 impl := &MockDriver{ 233 TaskEventsF: func(ctx context.Context) (<-chan *drivers.TaskEvent, error) { 234 ch := make(chan *drivers.TaskEvent) 235 go func() { 236 defer close(ch) 237 for _, event := range events { 238 ch <- event 239 } 240 }() 241 return ch, nil 242 }, 243 } 244 245 harness := NewDriverHarness(t, impl) 246 defer harness.Kill() 247 248 ch, err := harness.TaskEvents(context.Background()) 249 require.NoError(err) 250 251 for _, event := range events { 252 select { 253 case actual := <-ch: 254 require.Exactly(actual, event) 255 case <-time.After(500 * time.Millisecond): 256 require.Fail("failed to receive event") 257 258 } 259 } 260 261 }