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  }