github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/client/dynamicplugins/registry_test.go (about)

     1  package dynamicplugins
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func TestPluginEventBroadcaster_SendsMessagesToAllClients(t *testing.T) {
    13  	t.Parallel()
    14  	b := newPluginEventBroadcaster()
    15  	defer close(b.stopCh)
    16  	var rcv1, rcv2 bool
    17  
    18  	ch1 := b.subscribe()
    19  	ch2 := b.subscribe()
    20  
    21  	listenFunc := func(ch chan *PluginUpdateEvent, updateBool *bool) {
    22  		select {
    23  		case <-ch:
    24  			*updateBool = true
    25  		}
    26  	}
    27  
    28  	go listenFunc(ch1, &rcv1)
    29  	go listenFunc(ch2, &rcv2)
    30  
    31  	b.broadcast(&PluginUpdateEvent{})
    32  
    33  	require.Eventually(t, func() bool {
    34  		return rcv1 == true && rcv2 == true
    35  	}, 1*time.Second, 200*time.Millisecond)
    36  }
    37  
    38  func TestPluginEventBroadcaster_UnsubscribeWorks(t *testing.T) {
    39  	t.Parallel()
    40  
    41  	b := newPluginEventBroadcaster()
    42  	defer close(b.stopCh)
    43  	var rcv1 bool
    44  
    45  	ch1 := b.subscribe()
    46  
    47  	listenFunc := func(ch chan *PluginUpdateEvent, updateBool *bool) {
    48  		select {
    49  		case e := <-ch:
    50  			if e == nil {
    51  				*updateBool = true
    52  			}
    53  		}
    54  	}
    55  
    56  	go listenFunc(ch1, &rcv1)
    57  
    58  	b.unsubscribe(ch1)
    59  
    60  	b.broadcast(&PluginUpdateEvent{})
    61  
    62  	require.Eventually(t, func() bool {
    63  		return rcv1 == true
    64  	}, 1*time.Second, 200*time.Millisecond)
    65  }
    66  
    67  func TestDynamicRegistry_RegisterPlugin_SendsUpdateEvents(t *testing.T) {
    68  	t.Parallel()
    69  	r := NewRegistry(nil, nil)
    70  
    71  	ctx, cancelFn := context.WithCancel(context.Background())
    72  	defer cancelFn()
    73  
    74  	ch := r.PluginsUpdatedCh(ctx, "csi")
    75  	receivedRegistrationEvent := false
    76  
    77  	listenFunc := func(ch <-chan *PluginUpdateEvent, updateBool *bool) {
    78  		select {
    79  		case e := <-ch:
    80  			if e == nil {
    81  				return
    82  			}
    83  
    84  			if e.EventType == EventTypeRegistered {
    85  				*updateBool = true
    86  			}
    87  		}
    88  	}
    89  
    90  	go listenFunc(ch, &receivedRegistrationEvent)
    91  
    92  	err := r.RegisterPlugin(&PluginInfo{
    93  		Type:           "csi",
    94  		Name:           "my-plugin",
    95  		ConnectionInfo: &PluginConnectionInfo{},
    96  	})
    97  
    98  	require.NoError(t, err)
    99  
   100  	require.Eventually(t, func() bool {
   101  		return receivedRegistrationEvent == true
   102  	}, 1*time.Second, 200*time.Millisecond)
   103  }
   104  
   105  func TestDynamicRegistry_DeregisterPlugin_SendsUpdateEvents(t *testing.T) {
   106  	t.Parallel()
   107  	r := NewRegistry(nil, nil)
   108  
   109  	ctx, cancelFn := context.WithCancel(context.Background())
   110  	defer cancelFn()
   111  
   112  	ch := r.PluginsUpdatedCh(ctx, "csi")
   113  	receivedDeregistrationEvent := false
   114  
   115  	listenFunc := func(ch <-chan *PluginUpdateEvent, updateBool *bool) {
   116  		for {
   117  			select {
   118  			case e := <-ch:
   119  				if e == nil {
   120  					return
   121  				}
   122  
   123  				if e.EventType == EventTypeDeregistered {
   124  					*updateBool = true
   125  				}
   126  			}
   127  		}
   128  	}
   129  
   130  	go listenFunc(ch, &receivedDeregistrationEvent)
   131  
   132  	err := r.RegisterPlugin(&PluginInfo{
   133  		Type:           "csi",
   134  		Name:           "my-plugin",
   135  		ConnectionInfo: &PluginConnectionInfo{},
   136  	})
   137  	require.NoError(t, err)
   138  
   139  	err = r.DeregisterPlugin("csi", "my-plugin")
   140  	require.NoError(t, err)
   141  
   142  	require.Eventually(t, func() bool {
   143  		return receivedDeregistrationEvent == true
   144  	}, 1*time.Second, 200*time.Millisecond)
   145  }
   146  
   147  func TestDynamicRegistry_DispensePlugin_Works(t *testing.T) {
   148  	dispenseFn := func(i *PluginInfo) (interface{}, error) {
   149  		return struct{}{}, nil
   150  	}
   151  
   152  	registry := NewRegistry(nil, map[string]PluginDispenser{"csi": dispenseFn})
   153  
   154  	err := registry.RegisterPlugin(&PluginInfo{
   155  		Type:           "csi",
   156  		Name:           "my-plugin",
   157  		ConnectionInfo: &PluginConnectionInfo{},
   158  	})
   159  	require.NoError(t, err)
   160  
   161  	result, err := registry.DispensePlugin("unknown-type", "unknown-name")
   162  	require.Nil(t, result)
   163  	require.EqualError(t, err, "no plugin dispenser found for type: unknown-type")
   164  
   165  	result, err = registry.DispensePlugin("csi", "unknown-name")
   166  	require.Nil(t, result)
   167  	require.EqualError(t, err, "plugin unknown-name for type csi not found")
   168  
   169  	result, err = registry.DispensePlugin("csi", "my-plugin")
   170  	require.NotNil(t, result)
   171  	require.NoError(t, err)
   172  }
   173  
   174  func TestDynamicRegistry_IsolatePluginTypes(t *testing.T) {
   175  	t.Parallel()
   176  	r := NewRegistry(nil, nil)
   177  
   178  	err := r.RegisterPlugin(&PluginInfo{
   179  		Type:           PluginTypeCSIController,
   180  		Name:           "my-plugin",
   181  		ConnectionInfo: &PluginConnectionInfo{},
   182  	})
   183  	require.NoError(t, err)
   184  
   185  	err = r.RegisterPlugin(&PluginInfo{
   186  		Type:           PluginTypeCSINode,
   187  		Name:           "my-plugin",
   188  		ConnectionInfo: &PluginConnectionInfo{},
   189  	})
   190  	require.NoError(t, err)
   191  
   192  	err = r.DeregisterPlugin(PluginTypeCSIController, "my-plugin")
   193  	require.NoError(t, err)
   194  	require.Equal(t, len(r.ListPlugins(PluginTypeCSINode)), 1)
   195  	require.Equal(t, len(r.ListPlugins(PluginTypeCSIController)), 0)
   196  }
   197  
   198  func TestDynamicRegistry_StateStore(t *testing.T) {
   199  	t.Parallel()
   200  	dispenseFn := func(i *PluginInfo) (interface{}, error) {
   201  		return i, nil
   202  	}
   203  
   204  	memdb := &MemDB{}
   205  	oldR := NewRegistry(memdb, map[string]PluginDispenser{"csi": dispenseFn})
   206  
   207  	err := oldR.RegisterPlugin(&PluginInfo{
   208  		Type:           "csi",
   209  		Name:           "my-plugin",
   210  		ConnectionInfo: &PluginConnectionInfo{},
   211  	})
   212  	require.NoError(t, err)
   213  	result, err := oldR.DispensePlugin("csi", "my-plugin")
   214  	require.NotNil(t, result)
   215  	require.NoError(t, err)
   216  
   217  	// recreate the registry from the state store and query again
   218  	newR := NewRegistry(memdb, map[string]PluginDispenser{"csi": dispenseFn})
   219  	result, err = newR.DispensePlugin("csi", "my-plugin")
   220  	require.NotNil(t, result)
   221  	require.NoError(t, err)
   222  }
   223  
   224  // MemDB implements a StateDB that stores data in memory and should only be
   225  // used for testing. All methods are safe for concurrent use. This is a
   226  // partial implementation of the MemDB in the client/state package, copied
   227  // here to avoid circular dependencies.
   228  type MemDB struct {
   229  	dynamicManagerPs *RegistryState
   230  	mu               sync.RWMutex
   231  }
   232  
   233  func (m *MemDB) GetDynamicPluginRegistryState() (*RegistryState, error) {
   234  	m.mu.Lock()
   235  	defer m.mu.Unlock()
   236  	return m.dynamicManagerPs, nil
   237  }
   238  
   239  func (m *MemDB) PutDynamicPluginRegistryState(ps *RegistryState) error {
   240  	m.mu.Lock()
   241  	defer m.mu.Unlock()
   242  	m.dynamicManagerPs = ps
   243  	return nil
   244  }