github.com/smithx10/nomad@v0.9.1-rc1/client/pluginmanager/drivermanager/manager_test.go (about)

     1  package drivermanager
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	log "github.com/hashicorp/go-hclog"
    11  	plugin "github.com/hashicorp/go-plugin"
    12  	"github.com/hashicorp/nomad/client/pluginmanager"
    13  	"github.com/hashicorp/nomad/client/state"
    14  	"github.com/hashicorp/nomad/helper/pluginutils/loader"
    15  	"github.com/hashicorp/nomad/helper/testlog"
    16  	"github.com/hashicorp/nomad/nomad/structs"
    17  	"github.com/hashicorp/nomad/plugins/base"
    18  	"github.com/hashicorp/nomad/plugins/drivers"
    19  	dtu "github.com/hashicorp/nomad/plugins/drivers/testutils"
    20  	"github.com/hashicorp/nomad/testutil"
    21  	"github.com/stretchr/testify/assert"
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  var _ Manager = (*manager)(nil)
    26  var _ pluginmanager.PluginManager = (*manager)(nil)
    27  
    28  func testSetup(t *testing.T) (chan *drivers.Fingerprint, chan *drivers.TaskEvent, *manager) {
    29  	fpChan := make(chan *drivers.Fingerprint)
    30  	evChan := make(chan *drivers.TaskEvent)
    31  	drv := mockDriver(fpChan, evChan)
    32  	cat := mockCatalog(map[string]drivers.DriverPlugin{"mock": drv})
    33  	cfg := &Config{
    34  		Logger:              testlog.HCLogger(t),
    35  		Loader:              cat,
    36  		PluginConfig:        &base.AgentConfig{},
    37  		Updater:             noopUpdater,
    38  		EventHandlerFactory: noopEventHandlerFactory,
    39  		State:               state.NoopDB{},
    40  		AllowedDrivers:      make(map[string]struct{}),
    41  		BlockedDrivers:      make(map[string]struct{}),
    42  	}
    43  
    44  	mgr := New(cfg)
    45  	return fpChan, evChan, mgr
    46  }
    47  
    48  func mockDriver(fpChan chan *drivers.Fingerprint, evChan chan *drivers.TaskEvent) drivers.DriverPlugin {
    49  	return &dtu.MockDriver{
    50  		FingerprintF: func(ctx context.Context) (<-chan *drivers.Fingerprint, error) {
    51  			return fpChan, nil
    52  		},
    53  		TaskEventsF: func(ctx context.Context) (<-chan *drivers.TaskEvent, error) {
    54  			return evChan, nil
    55  		},
    56  	}
    57  }
    58  
    59  func mockCatalog(drivers map[string]drivers.DriverPlugin) *loader.MockCatalog {
    60  	cat := map[string][]*base.PluginInfoResponse{
    61  		base.PluginTypeDriver: {},
    62  	}
    63  	for d := range drivers {
    64  		cat[base.PluginTypeDriver] = append(cat[base.PluginTypeDriver], &base.PluginInfoResponse{
    65  			Name: d,
    66  			Type: base.PluginTypeDriver,
    67  		})
    68  	}
    69  
    70  	return &loader.MockCatalog{
    71  		DispenseF: func(name, pluginType string, cfg *base.AgentConfig, logger log.Logger) (loader.PluginInstance, error) {
    72  			d, ok := drivers[name]
    73  			if !ok {
    74  				return nil, fmt.Errorf("driver not found")
    75  			}
    76  			return loader.MockBasicExternalPlugin(d, "0.1.0"), nil
    77  		},
    78  		ReattachF: func(name, pluginType string, config *plugin.ReattachConfig) (loader.PluginInstance, error) {
    79  			d, ok := drivers[name]
    80  			if !ok {
    81  				return nil, fmt.Errorf("driver not found")
    82  			}
    83  			return loader.MockBasicExternalPlugin(d, "0.1.0"), nil
    84  		},
    85  		CatalogF: func() map[string][]*base.PluginInfoResponse {
    86  			return cat
    87  		},
    88  	}
    89  }
    90  
    91  func mockTaskEvent(taskID string) *drivers.TaskEvent {
    92  	return &drivers.TaskEvent{
    93  		TaskID:      taskID,
    94  		Timestamp:   time.Now(),
    95  		Annotations: map[string]string{},
    96  		Message:     "event from " + taskID,
    97  	}
    98  }
    99  
   100  func noopUpdater(string, *structs.DriverInfo)             {}
   101  func noopEventHandlerFactory(string, string) EventHandler { return nil }
   102  
   103  func TestManager_Fingerprint(t *testing.T) {
   104  	t.Parallel()
   105  	require := require.New(t)
   106  	fpChan, _, mgr := testSetup(t)
   107  	var infos []*structs.DriverInfo
   108  	mgr.updater = func(d string, i *structs.DriverInfo) {
   109  		infos = append(infos, i)
   110  	}
   111  	go mgr.Run()
   112  	defer mgr.Shutdown()
   113  	fpChan <- &drivers.Fingerprint{Health: drivers.HealthStateHealthy}
   114  	testutil.WaitForResult(func() (bool, error) {
   115  		mgr.instancesMu.Lock()
   116  		defer mgr.instancesMu.Unlock()
   117  		return len(mgr.instances) == 1, fmt.Errorf("manager should have registered 1 instance")
   118  	}, func(err error) {
   119  		require.NoError(err)
   120  	})
   121  
   122  	testutil.WaitForResult(func() (bool, error) {
   123  		mgr.instancesMu.Lock()
   124  		defer mgr.instancesMu.Unlock()
   125  		if mgr.instances["mock"].getLastHealth() != drivers.HealthStateHealthy {
   126  			return false, fmt.Errorf("mock instance should be healthy")
   127  		}
   128  		return true, nil
   129  	}, func(err error) {
   130  		require.NoError(err)
   131  	})
   132  
   133  	fpChan <- &drivers.Fingerprint{
   134  		Health: drivers.HealthStateUnhealthy,
   135  	}
   136  	testutil.WaitForResult(func() (bool, error) {
   137  		mgr.instancesMu.Lock()
   138  		defer mgr.instancesMu.Unlock()
   139  		if mgr.instances["mock"].getLastHealth() == drivers.HealthStateHealthy {
   140  			return false, fmt.Errorf("mock instance should be unhealthy")
   141  		}
   142  		return true, nil
   143  	}, func(err error) {
   144  		require.NoError(err)
   145  	})
   146  
   147  	fpChan <- &drivers.Fingerprint{
   148  		Health: drivers.HealthStateUndetected,
   149  	}
   150  	testutil.WaitForResult(func() (bool, error) {
   151  		mgr.instancesMu.Lock()
   152  		defer mgr.instancesMu.Unlock()
   153  		if mgr.instances["mock"].getLastHealth() != drivers.HealthStateUndetected {
   154  			return false, fmt.Errorf("mock instance should be undetected")
   155  		}
   156  		return true, nil
   157  	}, func(err error) {
   158  		require.NoError(err)
   159  	})
   160  
   161  	require.Len(infos, 3)
   162  	require.True(infos[0].Healthy)
   163  	require.True(infos[0].Detected)
   164  	require.False(infos[1].Healthy)
   165  	require.True(infos[1].Detected)
   166  	require.False(infos[2].Healthy)
   167  	require.False(infos[2].Detected)
   168  }
   169  
   170  func TestManager_TaskEvents(t *testing.T) {
   171  	t.Parallel()
   172  	require := require.New(t)
   173  	fpChan, evChan, mgr := testSetup(t)
   174  	go mgr.Run()
   175  	defer mgr.Shutdown()
   176  	fpChan <- &drivers.Fingerprint{Health: drivers.HealthStateHealthy}
   177  	testutil.WaitForResult(func() (bool, error) {
   178  		mgr.instancesMu.Lock()
   179  		defer mgr.instancesMu.Unlock()
   180  		return len(mgr.instances) == 1, fmt.Errorf("manager should have registered 1 instance")
   181  	}, func(err error) {
   182  		require.NoError(err)
   183  	})
   184  
   185  	event1 := mockTaskEvent("abc1")
   186  	var wg sync.WaitGroup
   187  	wg.Add(1)
   188  	mgr.instancesMu.Lock()
   189  	mgr.instances["mock"].eventHandlerFactory = func(string, string) EventHandler {
   190  		return func(ev *drivers.TaskEvent) {
   191  			defer wg.Done()
   192  			assert.Exactly(t, event1, ev)
   193  		}
   194  	}
   195  	mgr.instancesMu.Unlock()
   196  
   197  	evChan <- event1
   198  	wg.Wait()
   199  }
   200  
   201  func TestManager_Run_AllowedDrivers(t *testing.T) {
   202  	t.Parallel()
   203  	require := require.New(t)
   204  	fpChan, _, mgr := testSetup(t)
   205  	mgr.allowedDrivers = map[string]struct{}{"foo": {}}
   206  	go mgr.Run()
   207  	defer mgr.Shutdown()
   208  	select {
   209  	case fpChan <- &drivers.Fingerprint{Health: drivers.HealthStateHealthy}:
   210  	default:
   211  	}
   212  	testutil.AssertUntil(200*time.Millisecond, func() (bool, error) {
   213  		mgr.instancesMu.Lock()
   214  		defer mgr.instancesMu.Unlock()
   215  		return len(mgr.instances) == 0, fmt.Errorf("manager should have no registered instances")
   216  	}, func(err error) {
   217  		require.NoError(err)
   218  	})
   219  }
   220  
   221  func TestManager_Run_BlockedDrivers(t *testing.T) {
   222  	t.Parallel()
   223  	require := require.New(t)
   224  	fpChan, _, mgr := testSetup(t)
   225  	mgr.blockedDrivers = map[string]struct{}{"mock": {}}
   226  	go mgr.Run()
   227  	defer mgr.Shutdown()
   228  	select {
   229  	case fpChan <- &drivers.Fingerprint{Health: drivers.HealthStateHealthy}:
   230  	default:
   231  	}
   232  	testutil.AssertUntil(200*time.Millisecond, func() (bool, error) {
   233  		mgr.instancesMu.Lock()
   234  		defer mgr.instancesMu.Unlock()
   235  		return len(mgr.instances) == 0, fmt.Errorf("manager should have no registered instances")
   236  	}, func(err error) {
   237  		require.NoError(err)
   238  	})
   239  }
   240  
   241  func TestManager_Run_AllowedBlockedDrivers_Combined(t *testing.T) {
   242  	t.Parallel()
   243  	require := require.New(t)
   244  	drvs := map[string]drivers.DriverPlugin{}
   245  	fpChs := map[string]chan *drivers.Fingerprint{}
   246  	names := []string{"mock1", "mock2", "mock3", "mock4", "mock5"}
   247  	for _, d := range names {
   248  		ch := make(chan *drivers.Fingerprint)
   249  		drv := mockDriver(ch, nil)
   250  		drvs[d] = drv
   251  		fpChs[d] = ch
   252  	}
   253  	cat := mockCatalog(drvs)
   254  	cfg := &Config{
   255  		Logger:       testlog.HCLogger(t),
   256  		Loader:       cat,
   257  		PluginConfig: nil,
   258  		Updater:      noopUpdater,
   259  		State:        state.NoopDB{},
   260  		AllowedDrivers: map[string]struct{}{
   261  			"mock2": {},
   262  			"mock3": {},
   263  			"mock4": {},
   264  			"foo":   {},
   265  		},
   266  		BlockedDrivers: map[string]struct{}{
   267  			"mock2": {},
   268  			"mock4": {},
   269  			"bar":   {},
   270  		},
   271  	}
   272  	mgr := New(cfg)
   273  
   274  	go mgr.Run()
   275  	defer mgr.Shutdown()
   276  	for _, d := range names {
   277  		go func(drv string) {
   278  			select {
   279  			case fpChs[drv] <- &drivers.Fingerprint{Health: drivers.HealthStateHealthy}:
   280  			case <-time.After(200 * time.Millisecond):
   281  			}
   282  		}(d)
   283  	}
   284  
   285  	testutil.AssertUntil(250*time.Millisecond, func() (bool, error) {
   286  		mgr.instancesMu.Lock()
   287  		defer mgr.instancesMu.Unlock()
   288  		return len(mgr.instances) < 2, fmt.Errorf("manager should have 1 registered instance, %v", len(mgr.instances))
   289  	}, func(err error) {
   290  		require.NoError(err)
   291  	})
   292  	mgr.instancesMu.Lock()
   293  	require.Len(mgr.instances, 1)
   294  	_, ok := mgr.instances["mock3"]
   295  	mgr.instancesMu.Unlock()
   296  	require.True(ok)
   297  }