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 }