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