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