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  }