github.com/niusmallnan/moby@v1.13.1/integration-cli/daemon_swarm.go (about) 1 package main 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/pkg/integration/checker" 14 "github.com/go-check/check" 15 ) 16 17 // SwarmDaemon is a test daemon with helpers for participating in a swarm. 18 type SwarmDaemon struct { 19 *Daemon 20 swarm.Info 21 port int 22 listenAddr string 23 } 24 25 // Init initializes a new swarm cluster. 26 func (d *SwarmDaemon) Init(req swarm.InitRequest) error { 27 if req.ListenAddr == "" { 28 req.ListenAddr = d.listenAddr 29 } 30 status, out, err := d.SockRequest("POST", "/swarm/init", req) 31 if status != http.StatusOK { 32 return fmt.Errorf("initializing swarm: invalid statuscode %v, %q", status, out) 33 } 34 if err != nil { 35 return fmt.Errorf("initializing swarm: %v", err) 36 } 37 info, err := d.info() 38 if err != nil { 39 return err 40 } 41 d.Info = info 42 return nil 43 } 44 45 // Join joins a daemon to an existing cluster. 46 func (d *SwarmDaemon) Join(req swarm.JoinRequest) error { 47 if req.ListenAddr == "" { 48 req.ListenAddr = d.listenAddr 49 } 50 status, out, err := d.SockRequest("POST", "/swarm/join", req) 51 if status != http.StatusOK { 52 return fmt.Errorf("joining swarm: invalid statuscode %v, %q", status, out) 53 } 54 if err != nil { 55 return fmt.Errorf("joining swarm: %v", err) 56 } 57 info, err := d.info() 58 if err != nil { 59 return err 60 } 61 d.Info = info 62 return nil 63 } 64 65 // Leave forces daemon to leave current cluster. 66 func (d *SwarmDaemon) Leave(force bool) error { 67 url := "/swarm/leave" 68 if force { 69 url += "?force=1" 70 } 71 status, out, err := d.SockRequest("POST", url, nil) 72 if status != http.StatusOK { 73 return fmt.Errorf("leaving swarm: invalid statuscode %v, %q", status, out) 74 } 75 if err != nil { 76 err = fmt.Errorf("leaving swarm: %v", err) 77 } 78 return err 79 } 80 81 func (d *SwarmDaemon) info() (swarm.Info, error) { 82 var info struct { 83 Swarm swarm.Info 84 } 85 status, dt, err := d.SockRequest("GET", "/info", nil) 86 if status != http.StatusOK { 87 return info.Swarm, fmt.Errorf("get swarm info: invalid statuscode %v", status) 88 } 89 if err != nil { 90 return info.Swarm, fmt.Errorf("get swarm info: %v", err) 91 } 92 if err := json.Unmarshal(dt, &info); err != nil { 93 return info.Swarm, err 94 } 95 return info.Swarm, nil 96 } 97 98 type serviceConstructor func(*swarm.Service) 99 type nodeConstructor func(*swarm.Node) 100 type specConstructor func(*swarm.Spec) 101 102 func (d *SwarmDaemon) createService(c *check.C, f ...serviceConstructor) string { 103 var service swarm.Service 104 for _, fn := range f { 105 fn(&service) 106 } 107 status, out, err := d.SockRequest("POST", "/services/create", service.Spec) 108 109 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 110 c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf("output: %q", string(out))) 111 112 var scr types.ServiceCreateResponse 113 c.Assert(json.Unmarshal(out, &scr), checker.IsNil) 114 return scr.ID 115 } 116 117 func (d *SwarmDaemon) getService(c *check.C, id string) *swarm.Service { 118 var service swarm.Service 119 status, out, err := d.SockRequest("GET", "/services/"+id, nil) 120 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 121 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 122 c.Assert(json.Unmarshal(out, &service), checker.IsNil) 123 return &service 124 } 125 126 func (d *SwarmDaemon) getServiceTasks(c *check.C, service string) []swarm.Task { 127 var tasks []swarm.Task 128 129 filterArgs := filters.NewArgs() 130 filterArgs.Add("desired-state", "running") 131 filterArgs.Add("service", service) 132 filters, err := filters.ToParam(filterArgs) 133 c.Assert(err, checker.IsNil) 134 135 status, out, err := d.SockRequest("GET", "/tasks?filters="+filters, nil) 136 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 137 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 138 c.Assert(json.Unmarshal(out, &tasks), checker.IsNil) 139 return tasks 140 } 141 142 func (d *SwarmDaemon) checkServiceTasksInState(service string, state swarm.TaskState, message string) func(*check.C) (interface{}, check.CommentInterface) { 143 return func(c *check.C) (interface{}, check.CommentInterface) { 144 tasks := d.getServiceTasks(c, service) 145 var count int 146 for _, task := range tasks { 147 if task.Status.State == state { 148 if message == "" || strings.Contains(task.Status.Message, message) { 149 count++ 150 } 151 } 152 } 153 return count, nil 154 } 155 } 156 157 func (d *SwarmDaemon) checkServiceRunningTasks(service string) func(*check.C) (interface{}, check.CommentInterface) { 158 return d.checkServiceTasksInState(service, swarm.TaskStateRunning, "") 159 } 160 161 func (d *SwarmDaemon) checkServiceUpdateState(service string) func(*check.C) (interface{}, check.CommentInterface) { 162 return func(c *check.C) (interface{}, check.CommentInterface) { 163 service := d.getService(c, service) 164 return service.UpdateStatus.State, nil 165 } 166 } 167 168 func (d *SwarmDaemon) checkServiceTasks(service string) func(*check.C) (interface{}, check.CommentInterface) { 169 return func(c *check.C) (interface{}, check.CommentInterface) { 170 tasks := d.getServiceTasks(c, service) 171 return len(tasks), nil 172 } 173 } 174 175 func (d *SwarmDaemon) checkRunningTaskImages(c *check.C) (interface{}, check.CommentInterface) { 176 var tasks []swarm.Task 177 178 filterArgs := filters.NewArgs() 179 filterArgs.Add("desired-state", "running") 180 filters, err := filters.ToParam(filterArgs) 181 c.Assert(err, checker.IsNil) 182 183 status, out, err := d.SockRequest("GET", "/tasks?filters="+filters, nil) 184 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 185 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 186 c.Assert(json.Unmarshal(out, &tasks), checker.IsNil) 187 188 result := make(map[string]int) 189 for _, task := range tasks { 190 if task.Status.State == swarm.TaskStateRunning { 191 result[task.Spec.ContainerSpec.Image]++ 192 } 193 } 194 return result, nil 195 } 196 197 func (d *SwarmDaemon) checkNodeReadyCount(c *check.C) (interface{}, check.CommentInterface) { 198 nodes := d.listNodes(c) 199 var readyCount int 200 for _, node := range nodes { 201 if node.Status.State == swarm.NodeStateReady { 202 readyCount++ 203 } 204 } 205 return readyCount, nil 206 } 207 208 func (d *SwarmDaemon) getTask(c *check.C, id string) swarm.Task { 209 var task swarm.Task 210 211 status, out, err := d.SockRequest("GET", "/tasks/"+id, nil) 212 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 213 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 214 c.Assert(json.Unmarshal(out, &task), checker.IsNil) 215 return task 216 } 217 218 func (d *SwarmDaemon) updateService(c *check.C, service *swarm.Service, f ...serviceConstructor) { 219 for _, fn := range f { 220 fn(service) 221 } 222 url := fmt.Sprintf("/services/%s/update?version=%d", service.ID, service.Version.Index) 223 status, out, err := d.SockRequest("POST", url, service.Spec) 224 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 225 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 226 } 227 228 func (d *SwarmDaemon) removeService(c *check.C, id string) { 229 status, out, err := d.SockRequest("DELETE", "/services/"+id, nil) 230 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 231 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 232 } 233 234 func (d *SwarmDaemon) getNode(c *check.C, id string) *swarm.Node { 235 var node swarm.Node 236 status, out, err := d.SockRequest("GET", "/nodes/"+id, nil) 237 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 238 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 239 c.Assert(json.Unmarshal(out, &node), checker.IsNil) 240 c.Assert(node.ID, checker.Equals, id) 241 return &node 242 } 243 244 func (d *SwarmDaemon) removeNode(c *check.C, id string, force bool) { 245 url := "/nodes/" + id 246 if force { 247 url += "?force=1" 248 } 249 250 status, out, err := d.SockRequest("DELETE", url, nil) 251 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 252 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 253 } 254 255 func (d *SwarmDaemon) updateNode(c *check.C, id string, f ...nodeConstructor) { 256 for i := 0; ; i++ { 257 node := d.getNode(c, id) 258 for _, fn := range f { 259 fn(node) 260 } 261 url := fmt.Sprintf("/nodes/%s/update?version=%d", node.ID, node.Version.Index) 262 status, out, err := d.SockRequest("POST", url, node.Spec) 263 if i < 10 && strings.Contains(string(out), "update out of sequence") { 264 time.Sleep(100 * time.Millisecond) 265 continue 266 } 267 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 268 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 269 return 270 } 271 } 272 273 func (d *SwarmDaemon) listNodes(c *check.C) []swarm.Node { 274 status, out, err := d.SockRequest("GET", "/nodes", nil) 275 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 276 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 277 278 nodes := []swarm.Node{} 279 c.Assert(json.Unmarshal(out, &nodes), checker.IsNil) 280 return nodes 281 } 282 283 func (d *SwarmDaemon) listServices(c *check.C) []swarm.Service { 284 status, out, err := d.SockRequest("GET", "/services", nil) 285 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 286 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 287 288 services := []swarm.Service{} 289 c.Assert(json.Unmarshal(out, &services), checker.IsNil) 290 return services 291 } 292 293 func (d *SwarmDaemon) createSecret(c *check.C, secretSpec swarm.SecretSpec) string { 294 status, out, err := d.SockRequest("POST", "/secrets/create", secretSpec) 295 296 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 297 c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf("output: %q", string(out))) 298 299 var scr types.SecretCreateResponse 300 c.Assert(json.Unmarshal(out, &scr), checker.IsNil) 301 return scr.ID 302 } 303 304 func (d *SwarmDaemon) listSecrets(c *check.C) []swarm.Secret { 305 status, out, err := d.SockRequest("GET", "/secrets", nil) 306 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 307 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 308 309 secrets := []swarm.Secret{} 310 c.Assert(json.Unmarshal(out, &secrets), checker.IsNil) 311 return secrets 312 } 313 314 func (d *SwarmDaemon) getSecret(c *check.C, id string) *swarm.Secret { 315 var secret swarm.Secret 316 status, out, err := d.SockRequest("GET", "/secrets/"+id, nil) 317 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 318 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 319 c.Assert(json.Unmarshal(out, &secret), checker.IsNil) 320 return &secret 321 } 322 323 func (d *SwarmDaemon) deleteSecret(c *check.C, id string) { 324 status, out, err := d.SockRequest("DELETE", "/secrets/"+id, nil) 325 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 326 c.Assert(status, checker.Equals, http.StatusNoContent, check.Commentf("output: %q", string(out))) 327 } 328 329 func (d *SwarmDaemon) getSwarm(c *check.C) swarm.Swarm { 330 var sw swarm.Swarm 331 status, out, err := d.SockRequest("GET", "/swarm", nil) 332 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 333 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 334 c.Assert(json.Unmarshal(out, &sw), checker.IsNil) 335 return sw 336 } 337 338 func (d *SwarmDaemon) updateSwarm(c *check.C, f ...specConstructor) { 339 sw := d.getSwarm(c) 340 for _, fn := range f { 341 fn(&sw.Spec) 342 } 343 url := fmt.Sprintf("/swarm/update?version=%d", sw.Version.Index) 344 status, out, err := d.SockRequest("POST", url, sw.Spec) 345 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 346 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 347 } 348 349 func (d *SwarmDaemon) rotateTokens(c *check.C) { 350 var sw swarm.Swarm 351 status, out, err := d.SockRequest("GET", "/swarm", nil) 352 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 353 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 354 c.Assert(json.Unmarshal(out, &sw), checker.IsNil) 355 356 url := fmt.Sprintf("/swarm/update?version=%d&rotateWorkerToken=true&rotateManagerToken=true", sw.Version.Index) 357 status, out, err = d.SockRequest("POST", url, sw.Spec) 358 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 359 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 360 } 361 362 func (d *SwarmDaemon) joinTokens(c *check.C) swarm.JoinTokens { 363 var sw swarm.Swarm 364 status, out, err := d.SockRequest("GET", "/swarm", nil) 365 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 366 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 367 c.Assert(json.Unmarshal(out, &sw), checker.IsNil) 368 return sw.JoinTokens 369 } 370 371 func (d *SwarmDaemon) checkLocalNodeState(c *check.C) (interface{}, check.CommentInterface) { 372 info, err := d.info() 373 c.Assert(err, checker.IsNil) 374 return info.LocalNodeState, nil 375 } 376 377 func (d *SwarmDaemon) checkControlAvailable(c *check.C) (interface{}, check.CommentInterface) { 378 info, err := d.info() 379 c.Assert(err, checker.IsNil) 380 c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive) 381 return info.ControlAvailable, nil 382 } 383 384 func (d *SwarmDaemon) checkLeader(c *check.C) (interface{}, check.CommentInterface) { 385 errList := check.Commentf("could not get node list") 386 status, out, err := d.SockRequest("GET", "/nodes", nil) 387 if err != nil { 388 return err, errList 389 } 390 if status != http.StatusOK { 391 return fmt.Errorf("expected http status OK, got: %d", status), errList 392 } 393 394 var ls []swarm.Node 395 if err := json.Unmarshal(out, &ls); err != nil { 396 return err, errList 397 } 398 399 for _, node := range ls { 400 if node.ManagerStatus != nil && node.ManagerStatus.Leader { 401 return nil, nil 402 } 403 } 404 return fmt.Errorf("no leader"), check.Commentf("could not find leader") 405 } 406 407 func (d *SwarmDaemon) cmdRetryOutOfSequence(args ...string) (string, error) { 408 for i := 0; ; i++ { 409 out, err := d.Cmd(args...) 410 if err != nil { 411 if strings.Contains(out, "update out of sequence") { 412 if i < 10 { 413 continue 414 } 415 } 416 } 417 return out, err 418 } 419 }