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