github.com/kim0/docker@v0.6.2-0.20161130212042-4addda3f07e7/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) checkServiceRunningTasks(service string) func(*check.C) (interface{}, check.CommentInterface) { 143 return func(c *check.C) (interface{}, check.CommentInterface) { 144 tasks := d.getServiceTasks(c, service) 145 var runningCount int 146 for _, task := range tasks { 147 if task.Status.State == swarm.TaskStateRunning { 148 runningCount++ 149 } 150 } 151 return runningCount, nil 152 } 153 } 154 155 func (d *SwarmDaemon) checkServiceUpdateState(service string) func(*check.C) (interface{}, check.CommentInterface) { 156 return func(c *check.C) (interface{}, check.CommentInterface) { 157 service := d.getService(c, service) 158 return service.UpdateStatus.State, nil 159 } 160 } 161 162 func (d *SwarmDaemon) checkServiceTasks(service string) func(*check.C) (interface{}, check.CommentInterface) { 163 return func(c *check.C) (interface{}, check.CommentInterface) { 164 tasks := d.getServiceTasks(c, service) 165 return len(tasks), nil 166 } 167 } 168 169 func (d *SwarmDaemon) checkRunningTaskImages(c *check.C) (interface{}, check.CommentInterface) { 170 var tasks []swarm.Task 171 172 filterArgs := filters.NewArgs() 173 filterArgs.Add("desired-state", "running") 174 filters, err := filters.ToParam(filterArgs) 175 c.Assert(err, checker.IsNil) 176 177 status, out, err := d.SockRequest("GET", "/tasks?filters="+filters, nil) 178 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 179 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 180 c.Assert(json.Unmarshal(out, &tasks), checker.IsNil) 181 182 result := make(map[string]int) 183 for _, task := range tasks { 184 if task.Status.State == swarm.TaskStateRunning { 185 result[task.Spec.ContainerSpec.Image]++ 186 } 187 } 188 return result, nil 189 } 190 191 func (d *SwarmDaemon) checkNodeReadyCount(c *check.C) (interface{}, check.CommentInterface) { 192 nodes := d.listNodes(c) 193 var readyCount int 194 for _, node := range nodes { 195 if node.Status.State == swarm.NodeStateReady { 196 readyCount++ 197 } 198 } 199 return readyCount, nil 200 } 201 202 func (d *SwarmDaemon) getTask(c *check.C, id string) swarm.Task { 203 var task swarm.Task 204 205 status, out, err := d.SockRequest("GET", "/tasks/"+id, nil) 206 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 207 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 208 c.Assert(json.Unmarshal(out, &task), checker.IsNil) 209 return task 210 } 211 212 func (d *SwarmDaemon) updateService(c *check.C, service *swarm.Service, f ...serviceConstructor) { 213 for _, fn := range f { 214 fn(service) 215 } 216 url := fmt.Sprintf("/services/%s/update?version=%d", service.ID, service.Version.Index) 217 status, out, err := d.SockRequest("POST", url, service.Spec) 218 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 219 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 220 } 221 222 func (d *SwarmDaemon) removeService(c *check.C, id string) { 223 status, out, err := d.SockRequest("DELETE", "/services/"+id, nil) 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) getNode(c *check.C, id string) *swarm.Node { 229 var node swarm.Node 230 status, out, err := d.SockRequest("GET", "/nodes/"+id, nil) 231 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 232 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 233 c.Assert(json.Unmarshal(out, &node), checker.IsNil) 234 c.Assert(node.ID, checker.Equals, id) 235 return &node 236 } 237 238 func (d *SwarmDaemon) removeNode(c *check.C, id string, force bool) { 239 url := "/nodes/" + id 240 if force { 241 url += "?force=1" 242 } 243 244 status, out, err := d.SockRequest("DELETE", url, 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 } 248 249 func (d *SwarmDaemon) updateNode(c *check.C, id string, f ...nodeConstructor) { 250 for i := 0; ; i++ { 251 node := d.getNode(c, id) 252 for _, fn := range f { 253 fn(node) 254 } 255 url := fmt.Sprintf("/nodes/%s/update?version=%d", node.ID, node.Version.Index) 256 status, out, err := d.SockRequest("POST", url, node.Spec) 257 if i < 10 && strings.Contains(string(out), "update out of sequence") { 258 time.Sleep(100 * time.Millisecond) 259 continue 260 } 261 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 262 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 263 return 264 } 265 } 266 267 func (d *SwarmDaemon) listNodes(c *check.C) []swarm.Node { 268 status, out, err := d.SockRequest("GET", "/nodes", nil) 269 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 270 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 271 272 nodes := []swarm.Node{} 273 c.Assert(json.Unmarshal(out, &nodes), checker.IsNil) 274 return nodes 275 } 276 277 func (d *SwarmDaemon) listServices(c *check.C) []swarm.Service { 278 status, out, err := d.SockRequest("GET", "/services", nil) 279 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 280 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 281 282 services := []swarm.Service{} 283 c.Assert(json.Unmarshal(out, &services), checker.IsNil) 284 return services 285 } 286 287 func (d *SwarmDaemon) getSwarm(c *check.C) swarm.Swarm { 288 var sw swarm.Swarm 289 status, out, err := d.SockRequest("GET", "/swarm", nil) 290 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 291 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 292 c.Assert(json.Unmarshal(out, &sw), checker.IsNil) 293 return sw 294 } 295 296 func (d *SwarmDaemon) updateSwarm(c *check.C, f ...specConstructor) { 297 sw := d.getSwarm(c) 298 for _, fn := range f { 299 fn(&sw.Spec) 300 } 301 url := fmt.Sprintf("/swarm/update?version=%d", sw.Version.Index) 302 status, out, err := d.SockRequest("POST", url, sw.Spec) 303 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 304 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 305 } 306 307 func (d *SwarmDaemon) rotateTokens(c *check.C) { 308 var sw swarm.Swarm 309 status, out, err := d.SockRequest("GET", "/swarm", nil) 310 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 311 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 312 c.Assert(json.Unmarshal(out, &sw), checker.IsNil) 313 314 url := fmt.Sprintf("/swarm/update?version=%d&rotateWorkerToken=true&rotateManagerToken=true", sw.Version.Index) 315 status, out, err = d.SockRequest("POST", url, sw.Spec) 316 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 317 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 318 } 319 320 func (d *SwarmDaemon) joinTokens(c *check.C) swarm.JoinTokens { 321 var sw swarm.Swarm 322 status, out, err := d.SockRequest("GET", "/swarm", nil) 323 c.Assert(err, checker.IsNil, check.Commentf(string(out))) 324 c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) 325 c.Assert(json.Unmarshal(out, &sw), checker.IsNil) 326 return sw.JoinTokens 327 } 328 329 func (d *SwarmDaemon) checkLocalNodeState(c *check.C) (interface{}, check.CommentInterface) { 330 info, err := d.info() 331 c.Assert(err, checker.IsNil) 332 return info.LocalNodeState, nil 333 } 334 335 func (d *SwarmDaemon) checkControlAvailable(c *check.C) (interface{}, check.CommentInterface) { 336 info, err := d.info() 337 c.Assert(err, checker.IsNil) 338 c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive) 339 return info.ControlAvailable, nil 340 } 341 342 func (d *SwarmDaemon) checkLeader(c *check.C) (interface{}, check.CommentInterface) { 343 errList := check.Commentf("could not get node list") 344 status, out, err := d.SockRequest("GET", "/nodes", nil) 345 if err != nil { 346 return err, errList 347 } 348 if status != http.StatusOK { 349 return fmt.Errorf("expected http status OK, got: %d", status), errList 350 } 351 352 var ls []swarm.Node 353 if err := json.Unmarshal(out, &ls); err != nil { 354 return err, errList 355 } 356 357 for _, node := range ls { 358 if node.ManagerStatus != nil && node.ManagerStatus.Leader { 359 return nil, nil 360 } 361 } 362 return fmt.Errorf("no leader"), check.Commentf("could not find leader") 363 } 364 365 func (d *SwarmDaemon) cmdRetryOutOfSequence(args ...string) (string, error) { 366 for i := 0; ; i++ { 367 out, err := d.Cmd(args...) 368 if err != nil { 369 if strings.Contains(out, "update out of sequence") { 370 if i < 10 { 371 continue 372 } 373 } 374 } 375 return out, err 376 } 377 }