github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/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 ) 17 18 // Swarm is a test daemon with helpers for participating in a swarm. 19 type Swarm struct { 20 *Daemon 21 swarm.Info 22 Port int 23 ListenAddr string 24 } 25 26 // Init initializes a new swarm cluster. 27 func (d *Swarm) Init(req swarm.InitRequest) error { 28 if req.ListenAddr == "" { 29 req.ListenAddr = d.ListenAddr 30 } 31 status, out, err := d.SockRequest("POST", "/swarm/init", req) 32 if status != http.StatusOK { 33 return fmt.Errorf("initializing swarm: invalid statuscode %v, %q", status, out) 34 } 35 if err != nil { 36 return fmt.Errorf("initializing swarm: %v", err) 37 } 38 info, err := d.SwarmInfo() 39 if err != nil { 40 return err 41 } 42 d.Info = info 43 return nil 44 } 45 46 // Join joins a daemon to an existing cluster. 47 func (d *Swarm) Join(req swarm.JoinRequest) error { 48 if req.ListenAddr == "" { 49 req.ListenAddr = d.ListenAddr 50 } 51 status, out, err := d.SockRequest("POST", "/swarm/join", req) 52 if status != http.StatusOK { 53 return fmt.Errorf("joining swarm: invalid statuscode %v, %q", status, out) 54 } 55 if err != nil { 56 return fmt.Errorf("joining swarm: %v", err) 57 } 58 info, err := d.SwarmInfo() 59 if err != nil { 60 return err 61 } 62 d.Info = info 63 return nil 64 } 65 66 // Leave forces daemon to leave current cluster. 67 func (d *Swarm) Leave(force bool) error { 68 url := "/swarm/leave" 69 if force { 70 url += "?force=1" 71 } 72 status, out, err := d.SockRequest("POST", url, nil) 73 if status != http.StatusOK { 74 return fmt.Errorf("leaving swarm: invalid statuscode %v, %q", status, out) 75 } 76 if err != nil { 77 err = fmt.Errorf("leaving swarm: %v", err) 78 } 79 return err 80 } 81 82 // SwarmInfo returns the swarm information of the daemon 83 func (d *Swarm) SwarmInfo() (swarm.Info, error) { 84 var info struct { 85 Swarm swarm.Info 86 } 87 status, dt, err := d.SockRequest("GET", "/info", nil) 88 if status != http.StatusOK { 89 return info.Swarm, fmt.Errorf("get swarm info: invalid statuscode %v", status) 90 } 91 if err != nil { 92 return info.Swarm, fmt.Errorf("get swarm info: %v", err) 93 } 94 if err := json.Unmarshal(dt, &info); err != nil { 95 return info.Swarm, err 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 status, out, err := d.SockRequest("POST", "/swarm/unlock", req) 103 if status != http.StatusOK { 104 return fmt.Errorf("unlocking swarm: invalid statuscode %v, %q", status, out) 105 } 106 if err != nil { 107 err = errors.Wrap(err, "unlocking swarm") 108 } 109 return err 110 } 111 112 // ServiceConstructor defines a swarm service constructor function 113 type ServiceConstructor func(*swarm.Service) 114 115 // NodeConstructor defines a swarm node constructor 116 type NodeConstructor func(*swarm.Node) 117 118 // SpecConstructor defines a swarm spec constructor 119 type SpecConstructor func(*swarm.Spec) 120 121 // CreateService creates a swarm service given the specified service constructor 122 func (d *Swarm) CreateService(c *check.C, f ...ServiceConstructor) string { 123 var service swarm.Service 124 for _, fn := range f { 125 fn(&service) 126 } 127 status, out, err := d.SockRequest("POST", "/services/create", service.Spec) 128 129 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 130 c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf("output: %q", string(out))) 131 132 var scr types.ServiceCreateResponse 133 c.Assert(json.Unmarshal(out, &scr), checker.IsNil) 134 return scr.ID 135 } 136 137 // GetService returns the swarm service corresponding to the specified id 138 func (d *Swarm) GetService(c *check.C, id string) *swarm.Service { 139 var service swarm.Service 140 status, out, err := d.SockRequest("GET", "/services/"+id, nil) 141 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 142 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 143 c.Assert(json.Unmarshal(out, &service), checker.IsNil) 144 return &service 145 } 146 147 // GetServiceTasks returns the swarm tasks for the specified service 148 func (d *Swarm) GetServiceTasks(c *check.C, service string) []swarm.Task { 149 var tasks []swarm.Task 150 151 filterArgs := filters.NewArgs() 152 filterArgs.Add("desired-state", "running") 153 filterArgs.Add("service", service) 154 filters, err := filters.ToParam(filterArgs) 155 c.Assert(err, checker.IsNil) 156 157 status, out, err := d.SockRequest("GET", "/tasks?filters="+filters, nil) 158 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 159 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 160 c.Assert(json.Unmarshal(out, &tasks), checker.IsNil) 161 return tasks 162 } 163 164 // CheckServiceTasksInState returns the number of tasks with a matching state, 165 // and optional message substring. 166 func (d *Swarm) CheckServiceTasksInState(service string, state swarm.TaskState, message string) func(*check.C) (interface{}, check.CommentInterface) { 167 return func(c *check.C) (interface{}, check.CommentInterface) { 168 tasks := d.GetServiceTasks(c, service) 169 var count int 170 for _, task := range tasks { 171 if task.Status.State == state { 172 if message == "" || strings.Contains(task.Status.Message, message) { 173 count++ 174 } 175 } 176 } 177 return count, nil 178 } 179 } 180 181 // CheckServiceRunningTasks returns the number of running tasks for the specified service 182 func (d *Swarm) CheckServiceRunningTasks(service string) func(*check.C) (interface{}, check.CommentInterface) { 183 return d.CheckServiceTasksInState(service, swarm.TaskStateRunning, "") 184 } 185 186 // CheckServiceUpdateState returns the current update state for the specified service 187 func (d *Swarm) CheckServiceUpdateState(service string) func(*check.C) (interface{}, check.CommentInterface) { 188 return func(c *check.C) (interface{}, check.CommentInterface) { 189 service := d.GetService(c, service) 190 if service.UpdateStatus == nil { 191 return "", nil 192 } 193 return service.UpdateStatus.State, nil 194 } 195 } 196 197 // CheckServiceTasks returns the number of tasks for the specified service 198 func (d *Swarm) CheckServiceTasks(service string) func(*check.C) (interface{}, check.CommentInterface) { 199 return func(c *check.C) (interface{}, check.CommentInterface) { 200 tasks := d.GetServiceTasks(c, service) 201 return len(tasks), nil 202 } 203 } 204 205 // CheckRunningTaskImages returns the number of different images attached to a running task 206 func (d *Swarm) CheckRunningTaskImages(c *check.C) (interface{}, check.CommentInterface) { 207 var tasks []swarm.Task 208 209 filterArgs := filters.NewArgs() 210 filterArgs.Add("desired-state", "running") 211 filters, err := filters.ToParam(filterArgs) 212 c.Assert(err, checker.IsNil) 213 214 status, out, err := d.SockRequest("GET", "/tasks?filters="+filters, nil) 215 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 216 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 217 c.Assert(json.Unmarshal(out, &tasks), checker.IsNil) 218 219 result := make(map[string]int) 220 for _, task := range tasks { 221 if task.Status.State == swarm.TaskStateRunning { 222 result[task.Spec.ContainerSpec.Image]++ 223 } 224 } 225 return result, nil 226 } 227 228 // CheckNodeReadyCount returns the number of ready node on the swarm 229 func (d *Swarm) CheckNodeReadyCount(c *check.C) (interface{}, check.CommentInterface) { 230 nodes := d.ListNodes(c) 231 var readyCount int 232 for _, node := range nodes { 233 if node.Status.State == swarm.NodeStateReady { 234 readyCount++ 235 } 236 } 237 return readyCount, nil 238 } 239 240 // GetTask returns the swarm task identified by the specified id 241 func (d *Swarm) GetTask(c *check.C, id string) swarm.Task { 242 var task swarm.Task 243 244 status, out, err := d.SockRequest("GET", "/tasks/"+id, nil) 245 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 246 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 247 c.Assert(json.Unmarshal(out, &task), checker.IsNil) 248 return task 249 } 250 251 // UpdateService updates a swarm service with the specified service constructor 252 func (d *Swarm) UpdateService(c *check.C, service *swarm.Service, f ...ServiceConstructor) { 253 for _, fn := range f { 254 fn(service) 255 } 256 url := fmt.Sprintf("/services/%s/update?version=%d", service.ID, service.Version.Index) 257 status, out, err := d.SockRequest("POST", url, service.Spec) 258 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 259 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 260 } 261 262 // RemoveService removes the specified service 263 func (d *Swarm) RemoveService(c *check.C, id string) { 264 status, out, err := d.SockRequest("DELETE", "/services/"+id, nil) 265 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 266 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 267 } 268 269 // GetNode returns a swarm node identified by the specified id 270 func (d *Swarm) GetNode(c *check.C, id string) *swarm.Node { 271 var node swarm.Node 272 status, out, err := d.SockRequest("GET", "/nodes/"+id, nil) 273 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 274 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 275 c.Assert(json.Unmarshal(out, &node), checker.IsNil) 276 c.Assert(node.ID, checker.Equals, id) 277 return &node 278 } 279 280 // RemoveNode removes the specified node 281 func (d *Swarm) RemoveNode(c *check.C, id string, force bool) { 282 url := "/nodes/" + id 283 if force { 284 url += "?force=1" 285 } 286 287 status, out, err := d.SockRequest("DELETE", url, nil) 288 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 289 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 290 } 291 292 // UpdateNode updates a swarm node with the specified node constructor 293 func (d *Swarm) UpdateNode(c *check.C, id string, f ...NodeConstructor) { 294 for i := 0; ; i++ { 295 node := d.GetNode(c, id) 296 for _, fn := range f { 297 fn(node) 298 } 299 url := fmt.Sprintf("/nodes/%s/update?version=%d", node.ID, node.Version.Index) 300 status, out, err := d.SockRequest("POST", url, node.Spec) 301 if i < 10 && strings.Contains(string(out), "update out of sequence") { 302 time.Sleep(100 * time.Millisecond) 303 continue 304 } 305 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 306 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 307 return 308 } 309 } 310 311 // ListNodes returns the list of the current swarm nodes 312 func (d *Swarm) ListNodes(c *check.C) []swarm.Node { 313 status, out, err := d.SockRequest("GET", "/nodes", nil) 314 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 315 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 316 317 nodes := []swarm.Node{} 318 c.Assert(json.Unmarshal(out, &nodes), checker.IsNil) 319 return nodes 320 } 321 322 // ListServices return the list of the current swarm services 323 func (d *Swarm) ListServices(c *check.C) []swarm.Service { 324 status, out, err := d.SockRequest("GET", "/services", nil) 325 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 326 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 327 328 services := []swarm.Service{} 329 c.Assert(json.Unmarshal(out, &services), checker.IsNil) 330 return services 331 } 332 333 // CreateSecret creates a secret given the specified spec 334 func (d *Swarm) CreateSecret(c *check.C, secretSpec swarm.SecretSpec) string { 335 status, out, err := d.SockRequest("POST", "/secrets/create", secretSpec) 336 337 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 338 c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf("output: %q", string(out))) 339 340 var scr types.SecretCreateResponse 341 c.Assert(json.Unmarshal(out, &scr), checker.IsNil) 342 return scr.ID 343 } 344 345 // ListSecrets returns the list of the current swarm secrets 346 func (d *Swarm) ListSecrets(c *check.C) []swarm.Secret { 347 status, out, err := d.SockRequest("GET", "/secrets", nil) 348 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 349 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 350 351 secrets := []swarm.Secret{} 352 c.Assert(json.Unmarshal(out, &secrets), checker.IsNil) 353 return secrets 354 } 355 356 // GetSecret returns a swarm secret identified by the specified id 357 func (d *Swarm) GetSecret(c *check.C, id string) *swarm.Secret { 358 var secret swarm.Secret 359 status, out, err := d.SockRequest("GET", "/secrets/"+id, nil) 360 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 361 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 362 c.Assert(json.Unmarshal(out, &secret), checker.IsNil) 363 return &secret 364 } 365 366 // DeleteSecret removes the swarm secret identified by the specified id 367 func (d *Swarm) DeleteSecret(c *check.C, id string) { 368 status, out, err := d.SockRequest("DELETE", "/secrets/"+id, nil) 369 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 370 c.Assert(status, checker.Equals, http.StatusNoContent, check.Commentf("output: %q", string(out))) 371 } 372 373 // GetSwarm return the current swarm object 374 func (d *Swarm) GetSwarm(c *check.C) swarm.Swarm { 375 var sw swarm.Swarm 376 status, out, err := d.SockRequest("GET", "/swarm", nil) 377 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 378 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 379 c.Assert(json.Unmarshal(out, &sw), checker.IsNil) 380 return sw 381 } 382 383 // UpdateSwarm updates the current swarm object with the specified spec constructors 384 func (d *Swarm) UpdateSwarm(c *check.C, f ...SpecConstructor) { 385 sw := d.GetSwarm(c) 386 for _, fn := range f { 387 fn(&sw.Spec) 388 } 389 url := fmt.Sprintf("/swarm/update?version=%d", sw.Version.Index) 390 status, out, err := d.SockRequest("POST", url, sw.Spec) 391 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 392 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 393 } 394 395 // RotateTokens update the swarm to rotate tokens 396 func (d *Swarm) RotateTokens(c *check.C) { 397 var sw swarm.Swarm 398 status, out, err := d.SockRequest("GET", "/swarm", nil) 399 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 400 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 401 c.Assert(json.Unmarshal(out, &sw), checker.IsNil) 402 403 url := fmt.Sprintf("/swarm/update?version=%d&rotateWorkerToken=true&rotateManagerToken=true", sw.Version.Index) 404 status, out, err = d.SockRequest("POST", url, sw.Spec) 405 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 406 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 407 } 408 409 // JoinTokens returns the current swarm join tokens 410 func (d *Swarm) JoinTokens(c *check.C) swarm.JoinTokens { 411 var sw swarm.Swarm 412 status, out, err := d.SockRequest("GET", "/swarm", nil) 413 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 414 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 415 c.Assert(json.Unmarshal(out, &sw), checker.IsNil) 416 return sw.JoinTokens 417 } 418 419 // CheckLocalNodeState returns the current swarm node state 420 func (d *Swarm) CheckLocalNodeState(c *check.C) (interface{}, check.CommentInterface) { 421 info, err := d.SwarmInfo() 422 c.Assert(err, checker.IsNil) 423 return info.LocalNodeState, nil 424 } 425 426 // CheckControlAvailable returns the current swarm control available 427 func (d *Swarm) CheckControlAvailable(c *check.C) (interface{}, check.CommentInterface) { 428 info, err := d.SwarmInfo() 429 c.Assert(err, checker.IsNil) 430 c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive) 431 return info.ControlAvailable, nil 432 } 433 434 // CheckLeader returns whether there is a leader on the swarm or not 435 func (d *Swarm) CheckLeader(c *check.C) (interface{}, check.CommentInterface) { 436 errList := check.Commentf("could not get node list") 437 status, out, err := d.SockRequest("GET", "/nodes", nil) 438 if err != nil { 439 return err, errList 440 } 441 if status != http.StatusOK { 442 return fmt.Errorf("expected http status OK, got: %d", status), errList 443 } 444 445 var ls []swarm.Node 446 if err := json.Unmarshal(out, &ls); err != nil { 447 return err, errList 448 } 449 450 for _, node := range ls { 451 if node.ManagerStatus != nil && node.ManagerStatus.Leader { 452 return nil, nil 453 } 454 } 455 return fmt.Errorf("no leader"), check.Commentf("could not find leader") 456 } 457 458 // CmdRetryOutOfSequence tries the specified command against the current daemon for 10 times 459 func (d *Swarm) CmdRetryOutOfSequence(args ...string) (string, error) { 460 for i := 0; ; i++ { 461 out, err := d.Cmd(args...) 462 if err != nil { 463 if strings.Contains(out, "update out of sequence") { 464 if i < 10 { 465 continue 466 } 467 } 468 } 469 return out, err 470 } 471 }