github.com/manicqin/nomad@v0.9.5/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 }