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