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