github.com/sams1990/dockerrepo@v17.12.1-ce-rc2+incompatible/integration-cli/daemon/daemon_swarm.go (about) 1 package daemon 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strings" 8 "time" 9 10 "github.com/docker/docker/api/types" 11 "github.com/docker/docker/api/types/filters" 12 "github.com/docker/docker/api/types/swarm" 13 "github.com/docker/docker/integration-cli/checker" 14 "github.com/go-check/check" 15 "github.com/pkg/errors" 16 "golang.org/x/net/context" 17 ) 18 19 // Swarm is a test daemon with helpers for participating in a swarm. 20 type Swarm struct { 21 *Daemon 22 swarm.Info 23 Port int 24 ListenAddr string 25 } 26 27 // Init initializes a new swarm cluster. 28 func (d *Swarm) Init(req swarm.InitRequest) error { 29 if req.ListenAddr == "" { 30 req.ListenAddr = d.ListenAddr 31 } 32 cli, err := d.NewClient() 33 if err != nil { 34 return fmt.Errorf("initializing swarm: failed to create client %v", err) 35 } 36 defer cli.Close() 37 _, err = cli.SwarmInit(context.Background(), req) 38 if err != nil { 39 return fmt.Errorf("initializing swarm: %v", err) 40 } 41 info, err := d.SwarmInfo() 42 if err != nil { 43 return err 44 } 45 d.Info = info 46 return nil 47 } 48 49 // Join joins a daemon to an existing cluster. 50 func (d *Swarm) Join(req swarm.JoinRequest) error { 51 if req.ListenAddr == "" { 52 req.ListenAddr = d.ListenAddr 53 } 54 cli, err := d.NewClient() 55 if err != nil { 56 return fmt.Errorf("joining swarm: failed to create client %v", err) 57 } 58 defer cli.Close() 59 err = cli.SwarmJoin(context.Background(), req) 60 if err != nil { 61 return fmt.Errorf("joining swarm: %v", err) 62 } 63 info, err := d.SwarmInfo() 64 if err != nil { 65 return err 66 } 67 d.Info = info 68 return nil 69 } 70 71 // Leave forces daemon to leave current cluster. 72 func (d *Swarm) Leave(force bool) error { 73 cli, err := d.NewClient() 74 if err != nil { 75 return fmt.Errorf("leaving swarm: failed to create client %v", err) 76 } 77 defer cli.Close() 78 err = cli.SwarmLeave(context.Background(), force) 79 if err != nil { 80 err = fmt.Errorf("leaving swarm: %v", err) 81 } 82 return err 83 } 84 85 // SwarmInfo returns the swarm information of the daemon 86 func (d *Swarm) SwarmInfo() (swarm.Info, error) { 87 cli, err := d.NewClient() 88 if err != nil { 89 return swarm.Info{}, fmt.Errorf("get swarm info: %v", err) 90 } 91 92 info, err := cli.Info(context.Background()) 93 if err != nil { 94 return swarm.Info{}, fmt.Errorf("get swarm info: %v", err) 95 } 96 97 return info.Swarm, nil 98 } 99 100 // Unlock tries to unlock a locked swarm 101 func (d *Swarm) Unlock(req swarm.UnlockRequest) error { 102 cli, err := d.NewClient() 103 if err != nil { 104 return fmt.Errorf("unlocking swarm: failed to create client %v", err) 105 } 106 defer cli.Close() 107 err = cli.SwarmUnlock(context.Background(), req) 108 if err != nil { 109 err = errors.Wrap(err, "unlocking swarm") 110 } 111 return err 112 } 113 114 // ServiceConstructor defines a swarm service constructor function 115 type ServiceConstructor func(*swarm.Service) 116 117 // NodeConstructor defines a swarm node constructor 118 type NodeConstructor func(*swarm.Node) 119 120 // SecretConstructor defines a swarm secret constructor 121 type SecretConstructor func(*swarm.Secret) 122 123 // ConfigConstructor defines a swarm config constructor 124 type ConfigConstructor func(*swarm.Config) 125 126 // SpecConstructor defines a swarm spec constructor 127 type SpecConstructor func(*swarm.Spec) 128 129 // CreateServiceWithOptions creates a swarm service given the specified service constructors 130 // and auth config 131 func (d *Swarm) CreateServiceWithOptions(c *check.C, opts types.ServiceCreateOptions, f ...ServiceConstructor) string { 132 var service swarm.Service 133 for _, fn := range f { 134 fn(&service) 135 } 136 137 cli, err := d.NewClient() 138 c.Assert(err, checker.IsNil) 139 defer cli.Close() 140 141 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 142 defer cancel() 143 144 res, err := cli.ServiceCreate(ctx, service.Spec, opts) 145 c.Assert(err, checker.IsNil) 146 return res.ID 147 } 148 149 // CreateService creates a swarm service given the specified service constructor 150 func (d *Swarm) CreateService(c *check.C, f ...ServiceConstructor) string { 151 return d.CreateServiceWithOptions(c, types.ServiceCreateOptions{}, f...) 152 } 153 154 // GetService returns the swarm service corresponding to the specified id 155 func (d *Swarm) GetService(c *check.C, id string) *swarm.Service { 156 cli, err := d.NewClient() 157 c.Assert(err, checker.IsNil) 158 defer cli.Close() 159 160 service, _, err := cli.ServiceInspectWithRaw(context.Background(), id, types.ServiceInspectOptions{}) 161 c.Assert(err, checker.IsNil) 162 return &service 163 } 164 165 // GetServiceTasks returns the swarm tasks for the specified service 166 func (d *Swarm) GetServiceTasks(c *check.C, service string) []swarm.Task { 167 cli, err := d.NewClient() 168 c.Assert(err, checker.IsNil) 169 defer cli.Close() 170 171 filterArgs := filters.NewArgs() 172 filterArgs.Add("desired-state", "running") 173 filterArgs.Add("service", service) 174 175 options := types.TaskListOptions{ 176 Filters: filterArgs, 177 } 178 179 tasks, err := cli.TaskList(context.Background(), options) 180 c.Assert(err, checker.IsNil) 181 return tasks 182 } 183 184 // CheckServiceTasksInState returns the number of tasks with a matching state, 185 // and optional message substring. 186 func (d *Swarm) CheckServiceTasksInState(service string, state swarm.TaskState, message string) func(*check.C) (interface{}, check.CommentInterface) { 187 return func(c *check.C) (interface{}, check.CommentInterface) { 188 tasks := d.GetServiceTasks(c, service) 189 var count int 190 for _, task := range tasks { 191 if task.Status.State == state { 192 if message == "" || strings.Contains(task.Status.Message, message) { 193 count++ 194 } 195 } 196 } 197 return count, nil 198 } 199 } 200 201 // CheckServiceTasksInStateWithError returns the number of tasks with a matching state, 202 // and optional message substring. 203 func (d *Swarm) CheckServiceTasksInStateWithError(service string, state swarm.TaskState, errorMessage string) func(*check.C) (interface{}, check.CommentInterface) { 204 return func(c *check.C) (interface{}, check.CommentInterface) { 205 tasks := d.GetServiceTasks(c, service) 206 var count int 207 for _, task := range tasks { 208 if task.Status.State == state { 209 if errorMessage == "" || strings.Contains(task.Status.Err, errorMessage) { 210 count++ 211 } 212 } 213 } 214 return count, nil 215 } 216 } 217 218 // CheckServiceRunningTasks returns the number of running tasks for the specified service 219 func (d *Swarm) CheckServiceRunningTasks(service string) func(*check.C) (interface{}, check.CommentInterface) { 220 return d.CheckServiceTasksInState(service, swarm.TaskStateRunning, "") 221 } 222 223 // CheckServiceUpdateState returns the current update state for the specified service 224 func (d *Swarm) CheckServiceUpdateState(service string) func(*check.C) (interface{}, check.CommentInterface) { 225 return func(c *check.C) (interface{}, check.CommentInterface) { 226 service := d.GetService(c, service) 227 if service.UpdateStatus == nil { 228 return "", nil 229 } 230 return service.UpdateStatus.State, nil 231 } 232 } 233 234 // CheckPluginRunning returns the runtime state of the plugin 235 func (d *Swarm) CheckPluginRunning(plugin string) func(c *check.C) (interface{}, check.CommentInterface) { 236 return func(c *check.C) (interface{}, check.CommentInterface) { 237 status, out, err := d.SockRequest("GET", "/plugins/"+plugin+"/json", nil) 238 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 239 if status != http.StatusOK { 240 return false, nil 241 } 242 243 var p types.Plugin 244 c.Assert(json.Unmarshal(out, &p), checker.IsNil, check.Commentf(string(out))) 245 246 return p.Enabled, check.Commentf("%+v", p) 247 } 248 } 249 250 // CheckPluginImage returns the runtime state of the plugin 251 func (d *Swarm) CheckPluginImage(plugin string) func(c *check.C) (interface{}, check.CommentInterface) { 252 return func(c *check.C) (interface{}, check.CommentInterface) { 253 status, out, err := d.SockRequest("GET", "/plugins/"+plugin+"/json", nil) 254 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 255 if status != http.StatusOK { 256 return false, nil 257 } 258 259 var p types.Plugin 260 c.Assert(json.Unmarshal(out, &p), checker.IsNil, check.Commentf(string(out))) 261 return p.PluginReference, check.Commentf("%+v", p) 262 } 263 } 264 265 // CheckServiceTasks returns the number of tasks for the specified service 266 func (d *Swarm) CheckServiceTasks(service string) func(*check.C) (interface{}, check.CommentInterface) { 267 return func(c *check.C) (interface{}, check.CommentInterface) { 268 tasks := d.GetServiceTasks(c, service) 269 return len(tasks), nil 270 } 271 } 272 273 // CheckRunningTaskNetworks returns the number of times each network is referenced from a task. 274 func (d *Swarm) CheckRunningTaskNetworks(c *check.C) (interface{}, check.CommentInterface) { 275 cli, err := d.NewClient() 276 c.Assert(err, checker.IsNil) 277 defer cli.Close() 278 279 filterArgs := filters.NewArgs() 280 filterArgs.Add("desired-state", "running") 281 282 options := types.TaskListOptions{ 283 Filters: filterArgs, 284 } 285 286 tasks, err := cli.TaskList(context.Background(), options) 287 c.Assert(err, checker.IsNil) 288 289 result := make(map[string]int) 290 for _, task := range tasks { 291 for _, network := range task.Spec.Networks { 292 result[network.Target]++ 293 } 294 } 295 return result, nil 296 } 297 298 // CheckRunningTaskImages returns the times each image is running as a task. 299 func (d *Swarm) CheckRunningTaskImages(c *check.C) (interface{}, check.CommentInterface) { 300 cli, err := d.NewClient() 301 c.Assert(err, checker.IsNil) 302 defer cli.Close() 303 304 filterArgs := filters.NewArgs() 305 filterArgs.Add("desired-state", "running") 306 307 options := types.TaskListOptions{ 308 Filters: filterArgs, 309 } 310 311 tasks, err := cli.TaskList(context.Background(), options) 312 c.Assert(err, checker.IsNil) 313 314 result := make(map[string]int) 315 for _, task := range tasks { 316 if task.Status.State == swarm.TaskStateRunning && task.Spec.ContainerSpec != nil { 317 result[task.Spec.ContainerSpec.Image]++ 318 } 319 } 320 return result, nil 321 } 322 323 // CheckNodeReadyCount returns the number of ready node on the swarm 324 func (d *Swarm) CheckNodeReadyCount(c *check.C) (interface{}, check.CommentInterface) { 325 nodes := d.ListNodes(c) 326 var readyCount int 327 for _, node := range nodes { 328 if node.Status.State == swarm.NodeStateReady { 329 readyCount++ 330 } 331 } 332 return readyCount, nil 333 } 334 335 // GetTask returns the swarm task identified by the specified id 336 func (d *Swarm) GetTask(c *check.C, id string) swarm.Task { 337 cli, err := d.NewClient() 338 c.Assert(err, checker.IsNil) 339 defer cli.Close() 340 341 task, _, err := cli.TaskInspectWithRaw(context.Background(), id) 342 c.Assert(err, checker.IsNil) 343 return task 344 } 345 346 // UpdateService updates a swarm service with the specified service constructor 347 func (d *Swarm) UpdateService(c *check.C, service *swarm.Service, f ...ServiceConstructor) { 348 cli, err := d.NewClient() 349 c.Assert(err, checker.IsNil) 350 defer cli.Close() 351 352 for _, fn := range f { 353 fn(service) 354 } 355 356 _, err = cli.ServiceUpdate(context.Background(), service.ID, service.Version, service.Spec, types.ServiceUpdateOptions{}) 357 c.Assert(err, checker.IsNil) 358 } 359 360 // RemoveService removes the specified service 361 func (d *Swarm) RemoveService(c *check.C, id string) { 362 cli, err := d.NewClient() 363 c.Assert(err, checker.IsNil) 364 defer cli.Close() 365 366 err = cli.ServiceRemove(context.Background(), id) 367 c.Assert(err, checker.IsNil) 368 } 369 370 // GetNode returns a swarm node identified by the specified id 371 func (d *Swarm) GetNode(c *check.C, id string) *swarm.Node { 372 cli, err := d.NewClient() 373 c.Assert(err, checker.IsNil) 374 defer cli.Close() 375 376 node, _, err := cli.NodeInspectWithRaw(context.Background(), id) 377 c.Assert(err, checker.IsNil) 378 c.Assert(node.ID, checker.Equals, id) 379 return &node 380 } 381 382 // RemoveNode removes the specified node 383 func (d *Swarm) RemoveNode(c *check.C, id string, force bool) { 384 cli, err := d.NewClient() 385 c.Assert(err, checker.IsNil) 386 defer cli.Close() 387 388 options := types.NodeRemoveOptions{ 389 Force: force, 390 } 391 err = cli.NodeRemove(context.Background(), id, options) 392 c.Assert(err, checker.IsNil) 393 } 394 395 // UpdateNode updates a swarm node with the specified node constructor 396 func (d *Swarm) UpdateNode(c *check.C, id string, f ...NodeConstructor) { 397 cli, err := d.NewClient() 398 c.Assert(err, checker.IsNil) 399 defer cli.Close() 400 401 for i := 0; ; i++ { 402 node := d.GetNode(c, id) 403 for _, fn := range f { 404 fn(node) 405 } 406 407 err = cli.NodeUpdate(context.Background(), node.ID, node.Version, node.Spec) 408 if i < 10 && err != nil && strings.Contains(err.Error(), "update out of sequence") { 409 time.Sleep(100 * time.Millisecond) 410 continue 411 } 412 c.Assert(err, checker.IsNil) 413 return 414 } 415 } 416 417 // ListNodes returns the list of the current swarm nodes 418 func (d *Swarm) ListNodes(c *check.C) []swarm.Node { 419 cli, err := d.NewClient() 420 c.Assert(err, checker.IsNil) 421 defer cli.Close() 422 423 nodes, err := cli.NodeList(context.Background(), types.NodeListOptions{}) 424 c.Assert(err, checker.IsNil) 425 426 return nodes 427 } 428 429 // ListServices returns the list of the current swarm services 430 func (d *Swarm) ListServices(c *check.C) []swarm.Service { 431 cli, err := d.NewClient() 432 c.Assert(err, checker.IsNil) 433 defer cli.Close() 434 435 services, err := cli.ServiceList(context.Background(), types.ServiceListOptions{}) 436 c.Assert(err, checker.IsNil) 437 return services 438 } 439 440 // CreateSecret creates a secret given the specified spec 441 func (d *Swarm) CreateSecret(c *check.C, secretSpec swarm.SecretSpec) string { 442 cli, err := d.NewClient() 443 c.Assert(err, checker.IsNil) 444 defer cli.Close() 445 446 scr, err := cli.SecretCreate(context.Background(), secretSpec) 447 c.Assert(err, checker.IsNil) 448 449 return scr.ID 450 } 451 452 // ListSecrets returns the list of the current swarm secrets 453 func (d *Swarm) ListSecrets(c *check.C) []swarm.Secret { 454 cli, err := d.NewClient() 455 c.Assert(err, checker.IsNil) 456 defer cli.Close() 457 458 secrets, err := cli.SecretList(context.Background(), types.SecretListOptions{}) 459 c.Assert(err, checker.IsNil) 460 return secrets 461 } 462 463 // GetSecret returns a swarm secret identified by the specified id 464 func (d *Swarm) GetSecret(c *check.C, id string) *swarm.Secret { 465 cli, err := d.NewClient() 466 c.Assert(err, checker.IsNil) 467 defer cli.Close() 468 469 secret, _, err := cli.SecretInspectWithRaw(context.Background(), id) 470 c.Assert(err, checker.IsNil) 471 return &secret 472 } 473 474 // DeleteSecret removes the swarm secret identified by the specified id 475 func (d *Swarm) DeleteSecret(c *check.C, id string) { 476 cli, err := d.NewClient() 477 c.Assert(err, checker.IsNil) 478 defer cli.Close() 479 480 err = cli.SecretRemove(context.Background(), id) 481 c.Assert(err, checker.IsNil) 482 } 483 484 // UpdateSecret updates the swarm secret identified by the specified id 485 // Currently, only label update is supported. 486 func (d *Swarm) UpdateSecret(c *check.C, id string, f ...SecretConstructor) { 487 cli, err := d.NewClient() 488 c.Assert(err, checker.IsNil) 489 defer cli.Close() 490 491 secret := d.GetSecret(c, id) 492 for _, fn := range f { 493 fn(secret) 494 } 495 496 err = cli.SecretUpdate(context.Background(), secret.ID, secret.Version, secret.Spec) 497 498 c.Assert(err, checker.IsNil) 499 } 500 501 // CreateConfig creates a config given the specified spec 502 func (d *Swarm) CreateConfig(c *check.C, configSpec swarm.ConfigSpec) string { 503 cli, err := d.NewClient() 504 c.Assert(err, checker.IsNil) 505 defer cli.Close() 506 507 scr, err := cli.ConfigCreate(context.Background(), configSpec) 508 c.Assert(err, checker.IsNil) 509 return scr.ID 510 } 511 512 // ListConfigs returns the list of the current swarm configs 513 func (d *Swarm) ListConfigs(c *check.C) []swarm.Config { 514 cli, err := d.NewClient() 515 c.Assert(err, checker.IsNil) 516 defer cli.Close() 517 518 configs, err := cli.ConfigList(context.Background(), types.ConfigListOptions{}) 519 c.Assert(err, checker.IsNil) 520 return configs 521 } 522 523 // GetConfig returns a swarm config identified by the specified id 524 func (d *Swarm) GetConfig(c *check.C, id string) *swarm.Config { 525 cli, err := d.NewClient() 526 c.Assert(err, checker.IsNil) 527 defer cli.Close() 528 529 config, _, err := cli.ConfigInspectWithRaw(context.Background(), id) 530 c.Assert(err, checker.IsNil) 531 return &config 532 } 533 534 // DeleteConfig removes the swarm config identified by the specified id 535 func (d *Swarm) DeleteConfig(c *check.C, id string) { 536 cli, err := d.NewClient() 537 c.Assert(err, checker.IsNil) 538 defer cli.Close() 539 540 err = cli.ConfigRemove(context.Background(), id) 541 c.Assert(err, checker.IsNil) 542 } 543 544 // UpdateConfig updates the swarm config identified by the specified id 545 // Currently, only label update is supported. 546 func (d *Swarm) UpdateConfig(c *check.C, id string, f ...ConfigConstructor) { 547 cli, err := d.NewClient() 548 c.Assert(err, checker.IsNil) 549 defer cli.Close() 550 551 config := d.GetConfig(c, id) 552 for _, fn := range f { 553 fn(config) 554 } 555 556 err = cli.ConfigUpdate(context.Background(), config.ID, config.Version, config.Spec) 557 c.Assert(err, checker.IsNil) 558 } 559 560 // GetSwarm returns the current swarm object 561 func (d *Swarm) GetSwarm(c *check.C) swarm.Swarm { 562 cli, err := d.NewClient() 563 c.Assert(err, checker.IsNil) 564 defer cli.Close() 565 566 sw, err := cli.SwarmInspect(context.Background()) 567 c.Assert(err, checker.IsNil) 568 return sw 569 } 570 571 // UpdateSwarm updates the current swarm object with the specified spec constructors 572 func (d *Swarm) UpdateSwarm(c *check.C, f ...SpecConstructor) { 573 cli, err := d.NewClient() 574 c.Assert(err, checker.IsNil) 575 defer cli.Close() 576 577 sw := d.GetSwarm(c) 578 for _, fn := range f { 579 fn(&sw.Spec) 580 } 581 582 err = cli.SwarmUpdate(context.Background(), sw.Version, sw.Spec, swarm.UpdateFlags{}) 583 c.Assert(err, checker.IsNil) 584 } 585 586 // RotateTokens update the swarm to rotate tokens 587 func (d *Swarm) RotateTokens(c *check.C) { 588 cli, err := d.NewClient() 589 c.Assert(err, checker.IsNil) 590 defer cli.Close() 591 592 sw, err := cli.SwarmInspect(context.Background()) 593 c.Assert(err, checker.IsNil) 594 595 flags := swarm.UpdateFlags{ 596 RotateManagerToken: true, 597 RotateWorkerToken: true, 598 } 599 600 err = cli.SwarmUpdate(context.Background(), sw.Version, sw.Spec, flags) 601 c.Assert(err, checker.IsNil) 602 } 603 604 // JoinTokens returns the current swarm join tokens 605 func (d *Swarm) JoinTokens(c *check.C) swarm.JoinTokens { 606 cli, err := d.NewClient() 607 c.Assert(err, checker.IsNil) 608 defer cli.Close() 609 610 sw, err := cli.SwarmInspect(context.Background()) 611 c.Assert(err, checker.IsNil) 612 return sw.JoinTokens 613 } 614 615 // CheckLocalNodeState returns the current swarm node state 616 func (d *Swarm) CheckLocalNodeState(c *check.C) (interface{}, check.CommentInterface) { 617 info, err := d.SwarmInfo() 618 c.Assert(err, checker.IsNil) 619 return info.LocalNodeState, nil 620 } 621 622 // CheckControlAvailable returns the current swarm control available 623 func (d *Swarm) CheckControlAvailable(c *check.C) (interface{}, check.CommentInterface) { 624 info, err := d.SwarmInfo() 625 c.Assert(err, checker.IsNil) 626 c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive) 627 return info.ControlAvailable, nil 628 } 629 630 // CheckLeader returns whether there is a leader on the swarm or not 631 func (d *Swarm) CheckLeader(c *check.C) (interface{}, check.CommentInterface) { 632 cli, err := d.NewClient() 633 c.Assert(err, checker.IsNil) 634 defer cli.Close() 635 636 errList := check.Commentf("could not get node list") 637 638 ls, err := cli.NodeList(context.Background(), types.NodeListOptions{}) 639 if err != nil { 640 return err, errList 641 } 642 643 for _, node := range ls { 644 if node.ManagerStatus != nil && node.ManagerStatus.Leader { 645 return nil, nil 646 } 647 } 648 return fmt.Errorf("no leader"), check.Commentf("could not find leader") 649 } 650 651 // CmdRetryOutOfSequence tries the specified command against the current daemon for 10 times 652 func (d *Swarm) CmdRetryOutOfSequence(args ...string) (string, error) { 653 for i := 0; ; i++ { 654 out, err := d.Cmd(args...) 655 if err != nil { 656 if strings.Contains(out, "update out of sequence") { 657 if i < 10 { 658 continue 659 } 660 } 661 } 662 return out, err 663 } 664 }