github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/daemon/cluster/controllers/plugin/controller_test.go (about) 1 package plugin // import "github.com/Prakhar-Agarwal-byte/moby/daemon/cluster/controllers/plugin" 2 3 import ( 4 "context" 5 "errors" 6 "io" 7 "net/http" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/Prakhar-Agarwal-byte/moby/api/types" 13 "github.com/Prakhar-Agarwal-byte/moby/api/types/registry" 14 "github.com/Prakhar-Agarwal-byte/moby/api/types/swarm/runtime" 15 "github.com/Prakhar-Agarwal-byte/moby/plugin" 16 v2 "github.com/Prakhar-Agarwal-byte/moby/plugin/v2" 17 "github.com/containerd/log" 18 "github.com/distribution/reference" 19 "github.com/moby/pubsub" 20 "github.com/sirupsen/logrus" 21 ) 22 23 const ( 24 pluginTestName = "test" 25 pluginTestRemote = "testremote" 26 pluginTestRemoteUpgrade = "testremote2" 27 ) 28 29 func TestPrepare(t *testing.T) { 30 b := newMockBackend() 31 c := newTestController(b, false) 32 ctx := context.Background() 33 34 if err := c.Prepare(ctx); err != nil { 35 t.Fatal(err) 36 } 37 38 if b.p == nil { 39 t.Fatal("pull not performed") 40 } 41 42 c = newTestController(b, false) 43 if err := c.Prepare(ctx); err != nil { 44 t.Fatal(err) 45 } 46 if b.p == nil { 47 t.Fatal("unexpected nil") 48 } 49 if b.p.PluginObj.PluginReference != pluginTestRemoteUpgrade { 50 t.Fatal("upgrade not performed") 51 } 52 53 c = newTestController(b, false) 54 c.serviceID = "1" 55 if err := c.Prepare(ctx); err == nil { 56 t.Fatal("expected error on prepare") 57 } 58 } 59 60 func TestStart(t *testing.T) { 61 b := newMockBackend() 62 c := newTestController(b, false) 63 ctx := context.Background() 64 65 if err := c.Prepare(ctx); err != nil { 66 t.Fatal(err) 67 } 68 69 if err := c.Start(ctx); err != nil { 70 t.Fatal(err) 71 } 72 73 if !b.p.IsEnabled() { 74 t.Fatal("expected plugin to be enabled") 75 } 76 77 c = newTestController(b, true) 78 if err := c.Prepare(ctx); err != nil { 79 t.Fatal(err) 80 } 81 if err := c.Start(ctx); err != nil { 82 t.Fatal(err) 83 } 84 if b.p.IsEnabled() { 85 t.Fatal("expected plugin to be disabled") 86 } 87 88 c = newTestController(b, false) 89 if err := c.Prepare(ctx); err != nil { 90 t.Fatal(err) 91 } 92 if err := c.Start(ctx); err != nil { 93 t.Fatal(err) 94 } 95 if !b.p.IsEnabled() { 96 t.Fatal("expected plugin to be enabled") 97 } 98 } 99 100 func TestWaitCancel(t *testing.T) { 101 b := newMockBackend() 102 c := newTestController(b, true) 103 ctx := context.Background() 104 if err := c.Prepare(ctx); err != nil { 105 t.Fatal(err) 106 } 107 if err := c.Start(ctx); err != nil { 108 t.Fatal(err) 109 } 110 111 ctxCancel, cancel := context.WithCancel(ctx) 112 chErr := make(chan error, 1) 113 go func() { 114 chErr <- c.Wait(ctxCancel) 115 }() 116 cancel() 117 select { 118 case err := <-chErr: 119 if err != context.Canceled { 120 t.Fatal(err) 121 } 122 case <-time.After(10 * time.Second): 123 t.Fatal("timeout waiting for cancelation") 124 } 125 } 126 127 func TestWaitDisabled(t *testing.T) { 128 b := newMockBackend() 129 c := newTestController(b, true) 130 ctx := context.Background() 131 if err := c.Prepare(ctx); err != nil { 132 t.Fatal(err) 133 } 134 if err := c.Start(ctx); err != nil { 135 t.Fatal(err) 136 } 137 138 chErr := make(chan error, 1) 139 go func() { 140 chErr <- c.Wait(ctx) 141 }() 142 143 if err := b.Enable("test", nil); err != nil { 144 t.Fatal(err) 145 } 146 select { 147 case err := <-chErr: 148 if err == nil { 149 t.Fatal("expected error") 150 } 151 case <-time.After(10 * time.Second): 152 t.Fatal("timeout waiting for event") 153 } 154 155 if err := c.Start(ctx); err != nil { 156 t.Fatal(err) 157 } 158 159 ctxWaitReady, cancelCtxWaitReady := context.WithTimeout(ctx, 30*time.Second) 160 c.signalWaitReady = cancelCtxWaitReady 161 defer cancelCtxWaitReady() 162 163 go func() { 164 chErr <- c.Wait(ctx) 165 }() 166 167 chEvent, cancel := b.SubscribeEvents(1) 168 defer cancel() 169 170 if err := b.Disable("test", nil); err != nil { 171 t.Fatal(err) 172 } 173 174 select { 175 case <-chEvent: 176 <-ctxWaitReady.Done() 177 if err := ctxWaitReady.Err(); err == context.DeadlineExceeded { 178 t.Fatal(err) 179 } 180 select { 181 case <-chErr: 182 t.Fatal("wait returned unexpectedly") 183 default: 184 // all good 185 } 186 case <-chErr: 187 t.Fatal("wait returned unexpectedly") 188 case <-time.After(10 * time.Second): 189 t.Fatal("timeout waiting for event") 190 } 191 192 if err := b.Remove("test", nil); err != nil { 193 t.Fatal(err) 194 } 195 select { 196 case err := <-chErr: 197 if err == nil { 198 t.Fatal("expected error") 199 } 200 if !strings.Contains(err.Error(), "removed") { 201 t.Fatal(err) 202 } 203 case <-time.After(10 * time.Second): 204 t.Fatal("timeout waiting for event") 205 } 206 } 207 208 func TestWaitEnabled(t *testing.T) { 209 b := newMockBackend() 210 c := newTestController(b, false) 211 ctx := context.Background() 212 if err := c.Prepare(ctx); err != nil { 213 t.Fatal(err) 214 } 215 if err := c.Start(ctx); err != nil { 216 t.Fatal(err) 217 } 218 219 chErr := make(chan error, 1) 220 go func() { 221 chErr <- c.Wait(ctx) 222 }() 223 224 if err := b.Disable("test", nil); err != nil { 225 t.Fatal(err) 226 } 227 select { 228 case err := <-chErr: 229 if err == nil { 230 t.Fatal("expected error") 231 } 232 case <-time.After(10 * time.Second): 233 t.Fatal("timeout waiting for event") 234 } 235 236 if err := c.Start(ctx); err != nil { 237 t.Fatal(err) 238 } 239 240 ctxWaitReady, ctxWaitCancel := context.WithCancel(ctx) 241 c.signalWaitReady = ctxWaitCancel 242 defer ctxWaitCancel() 243 244 go func() { 245 chErr <- c.Wait(ctx) 246 }() 247 248 chEvent, cancel := b.SubscribeEvents(1) 249 defer cancel() 250 251 if err := b.Enable("test", nil); err != nil { 252 t.Fatal(err) 253 } 254 255 select { 256 case <-chEvent: 257 <-ctxWaitReady.Done() 258 if err := ctxWaitReady.Err(); err == context.DeadlineExceeded { 259 t.Fatal(err) 260 } 261 select { 262 case <-chErr: 263 t.Fatal("wait returned unexpectedly") 264 default: 265 // all good 266 } 267 case <-chErr: 268 t.Fatal("wait returned unexpectedly") 269 case <-time.After(10 * time.Second): 270 t.Fatal("timeout waiting for event") 271 } 272 273 if err := b.Remove("test", nil); err != nil { 274 t.Fatal(err) 275 } 276 select { 277 case err := <-chErr: 278 if err == nil { 279 t.Fatal("expected error") 280 } 281 if !strings.Contains(err.Error(), "removed") { 282 t.Fatal(err) 283 } 284 case <-time.After(10 * time.Second): 285 t.Fatal("timeout waiting for event") 286 } 287 } 288 289 func TestRemove(t *testing.T) { 290 b := newMockBackend() 291 c := newTestController(b, false) 292 ctx := context.Background() 293 294 if err := c.Prepare(ctx); err != nil { 295 t.Fatal(err) 296 } 297 if err := c.Shutdown(ctx); err != nil { 298 t.Fatal(err) 299 } 300 301 c2 := newTestController(b, false) 302 if err := c2.Prepare(ctx); err != nil { 303 t.Fatal(err) 304 } 305 306 if err := c.Remove(ctx); err != nil { 307 t.Fatal(err) 308 } 309 if b.p == nil { 310 t.Fatal("plugin removed unexpectedly") 311 } 312 if err := c2.Shutdown(ctx); err != nil { 313 t.Fatal(err) 314 } 315 if err := c2.Remove(ctx); err != nil { 316 t.Fatal(err) 317 } 318 if b.p != nil { 319 t.Fatal("expected plugin to be removed") 320 } 321 } 322 323 func newTestController(b Backend, disabled bool) *Controller { 324 return &Controller{ 325 logger: &log.Entry{Logger: &logrus.Logger{Out: io.Discard}}, 326 backend: b, 327 spec: runtime.PluginSpec{ 328 Name: pluginTestName, 329 Remote: pluginTestRemote, 330 Disabled: disabled, 331 }, 332 } 333 } 334 335 func newMockBackend() *mockBackend { 336 return &mockBackend{ 337 pub: pubsub.NewPublisher(0, 0), 338 } 339 } 340 341 type mockBackend struct { 342 p *v2.Plugin 343 pub *pubsub.Publisher 344 } 345 346 func (m *mockBackend) Disable(name string, config *types.PluginDisableConfig) error { 347 m.p.PluginObj.Enabled = false 348 m.pub.Publish(plugin.EventDisable{}) 349 return nil 350 } 351 352 func (m *mockBackend) Enable(name string, config *types.PluginEnableConfig) error { 353 m.p.PluginObj.Enabled = true 354 m.pub.Publish(plugin.EventEnable{}) 355 return nil 356 } 357 358 func (m *mockBackend) Remove(name string, config *types.PluginRmConfig) error { 359 m.p = nil 360 m.pub.Publish(plugin.EventRemove{}) 361 return nil 362 } 363 364 func (m *mockBackend) Pull(ctx context.Context, ref reference.Named, name string, metaHeaders http.Header, authConfig *registry.AuthConfig, privileges types.PluginPrivileges, outStream io.Writer, opts ...plugin.CreateOpt) error { 365 m.p = &v2.Plugin{ 366 PluginObj: types.Plugin{ 367 ID: "1234", 368 Name: name, 369 PluginReference: ref.String(), 370 }, 371 } 372 return nil 373 } 374 375 func (m *mockBackend) Upgrade(ctx context.Context, ref reference.Named, name string, metaHeaders http.Header, authConfig *registry.AuthConfig, privileges types.PluginPrivileges, outStream io.Writer) error { 376 m.p.PluginObj.PluginReference = pluginTestRemoteUpgrade 377 return nil 378 } 379 380 func (m *mockBackend) Get(name string) (*v2.Plugin, error) { 381 if m.p == nil { 382 return nil, errors.New("not found") 383 } 384 return m.p, nil 385 } 386 387 func (m *mockBackend) SubscribeEvents(buffer int, events ...plugin.Event) (eventCh <-chan interface{}, cancel func()) { 388 ch := m.pub.SubscribeTopicWithBuffer(nil, buffer) 389 cancel = func() { m.pub.Evict(ch) } 390 return ch, cancel 391 }