github.com/gdevillele/moby@v1.13.0/integration-cli/docker_cli_swarm_test.go (about) 1 // +build !windows 2 3 package main 4 5 import ( 6 "bytes" 7 "encoding/json" 8 "fmt" 9 "io/ioutil" 10 "net/http" 11 "net/http/httptest" 12 "os" 13 "path/filepath" 14 "strings" 15 "time" 16 17 "github.com/docker/docker/api/types/swarm" 18 "github.com/docker/docker/pkg/integration/checker" 19 "github.com/docker/libnetwork/driverapi" 20 "github.com/docker/libnetwork/ipamapi" 21 remoteipam "github.com/docker/libnetwork/ipams/remote/api" 22 "github.com/go-check/check" 23 "github.com/vishvananda/netlink" 24 ) 25 26 func (s *DockerSwarmSuite) TestSwarmUpdate(c *check.C) { 27 d := s.AddDaemon(c, true, true) 28 29 getSpec := func() swarm.Spec { 30 sw := d.getSwarm(c) 31 return sw.Spec 32 } 33 34 out, err := d.Cmd("swarm", "update", "--cert-expiry", "30h", "--dispatcher-heartbeat", "11s") 35 c.Assert(err, checker.IsNil, check.Commentf("out: %v", out)) 36 37 spec := getSpec() 38 c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 30*time.Hour) 39 c.Assert(spec.Dispatcher.HeartbeatPeriod, checker.Equals, 11*time.Second) 40 41 // setting anything under 30m for cert-expiry is not allowed 42 out, err = d.Cmd("swarm", "update", "--cert-expiry", "15m") 43 c.Assert(err, checker.NotNil) 44 c.Assert(out, checker.Contains, "minimum certificate expiry time") 45 spec = getSpec() 46 c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 30*time.Hour) 47 } 48 49 func (s *DockerSwarmSuite) TestSwarmInit(c *check.C) { 50 d := s.AddDaemon(c, false, false) 51 52 getSpec := func() swarm.Spec { 53 sw := d.getSwarm(c) 54 return sw.Spec 55 } 56 57 out, err := d.Cmd("swarm", "init", "--cert-expiry", "30h", "--dispatcher-heartbeat", "11s") 58 c.Assert(err, checker.IsNil, check.Commentf("out: %v", out)) 59 60 spec := getSpec() 61 c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 30*time.Hour) 62 c.Assert(spec.Dispatcher.HeartbeatPeriod, checker.Equals, 11*time.Second) 63 64 c.Assert(d.Leave(true), checker.IsNil) 65 time.Sleep(500 * time.Millisecond) // https://github.com/docker/swarmkit/issues/1421 66 out, err = d.Cmd("swarm", "init") 67 c.Assert(err, checker.IsNil, check.Commentf("out: %v", out)) 68 69 spec = getSpec() 70 c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 90*24*time.Hour) 71 c.Assert(spec.Dispatcher.HeartbeatPeriod, checker.Equals, 5*time.Second) 72 } 73 74 func (s *DockerSwarmSuite) TestSwarmInitIPv6(c *check.C) { 75 testRequires(c, IPv6) 76 d1 := s.AddDaemon(c, false, false) 77 out, err := d1.Cmd("swarm", "init", "--listen-addr", "::1") 78 c.Assert(err, checker.IsNil, check.Commentf("out: %v", out)) 79 80 d2 := s.AddDaemon(c, false, false) 81 out, err = d2.Cmd("swarm", "join", "::1") 82 c.Assert(err, checker.IsNil, check.Commentf("out: %v", out)) 83 84 out, err = d2.Cmd("info") 85 c.Assert(err, checker.IsNil, check.Commentf("out: %v", out)) 86 c.Assert(out, checker.Contains, "Swarm: active") 87 } 88 89 func (s *DockerSwarmSuite) TestSwarmInitUnspecifiedAdvertiseAddr(c *check.C) { 90 d := s.AddDaemon(c, false, false) 91 out, err := d.Cmd("swarm", "init", "--advertise-addr", "0.0.0.0") 92 c.Assert(err, checker.NotNil) 93 c.Assert(out, checker.Contains, "advertise address must be a non-zero IP address") 94 } 95 96 func (s *DockerSwarmSuite) TestSwarmIncompatibleDaemon(c *check.C) { 97 // init swarm mode and stop a daemon 98 d := s.AddDaemon(c, true, true) 99 info, err := d.info() 100 c.Assert(err, checker.IsNil) 101 c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive) 102 c.Assert(d.Stop(), checker.IsNil) 103 104 // start a daemon with --cluster-store and --cluster-advertise 105 err = d.Start("--cluster-store=consul://consuladdr:consulport/some/path", "--cluster-advertise=1.1.1.1:2375") 106 c.Assert(err, checker.NotNil) 107 content, _ := ioutil.ReadFile(d.logFile.Name()) 108 c.Assert(string(content), checker.Contains, "--cluster-store and --cluster-advertise daemon configurations are incompatible with swarm mode") 109 110 // start a daemon with --live-restore 111 err = d.Start("--live-restore") 112 c.Assert(err, checker.NotNil) 113 content, _ = ioutil.ReadFile(d.logFile.Name()) 114 c.Assert(string(content), checker.Contains, "--live-restore daemon configuration is incompatible with swarm mode") 115 // restart for teardown 116 c.Assert(d.Start(), checker.IsNil) 117 } 118 119 // Test case for #24090 120 func (s *DockerSwarmSuite) TestSwarmNodeListHostname(c *check.C) { 121 d := s.AddDaemon(c, true, true) 122 123 // The first line should contain "HOSTNAME" 124 out, err := d.Cmd("node", "ls") 125 c.Assert(err, checker.IsNil) 126 c.Assert(strings.Split(out, "\n")[0], checker.Contains, "HOSTNAME") 127 } 128 129 func (s *DockerSwarmSuite) TestSwarmServiceTemplatingHostname(c *check.C) { 130 d := s.AddDaemon(c, true, true) 131 132 out, err := d.Cmd("service", "create", "--name", "test", "--hostname", "{{.Service.Name}}-{{.Task.Slot}}", "busybox", "top") 133 c.Assert(err, checker.IsNil, check.Commentf(out)) 134 135 // make sure task has been deployed. 136 waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1) 137 138 containers := d.activeContainers() 139 out, err = d.Cmd("inspect", "--type", "container", "--format", "{{.Config.Hostname}}", containers[0]) 140 c.Assert(err, checker.IsNil, check.Commentf(out)) 141 c.Assert(strings.Split(out, "\n")[0], checker.Equals, "test-1", check.Commentf("hostname with templating invalid")) 142 } 143 144 // Test case for #24270 145 func (s *DockerSwarmSuite) TestSwarmServiceListFilter(c *check.C) { 146 d := s.AddDaemon(c, true, true) 147 148 name1 := "redis-cluster-md5" 149 name2 := "redis-cluster" 150 name3 := "other-cluster" 151 out, err := d.Cmd("service", "create", "--name", name1, "busybox", "top") 152 c.Assert(err, checker.IsNil) 153 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 154 155 out, err = d.Cmd("service", "create", "--name", name2, "busybox", "top") 156 c.Assert(err, checker.IsNil) 157 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 158 159 out, err = d.Cmd("service", "create", "--name", name3, "busybox", "top") 160 c.Assert(err, checker.IsNil) 161 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 162 163 filter1 := "name=redis-cluster-md5" 164 filter2 := "name=redis-cluster" 165 166 // We search checker.Contains with `name+" "` to prevent prefix only. 167 out, err = d.Cmd("service", "ls", "--filter", filter1) 168 c.Assert(err, checker.IsNil) 169 c.Assert(out, checker.Contains, name1+" ") 170 c.Assert(out, checker.Not(checker.Contains), name2+" ") 171 c.Assert(out, checker.Not(checker.Contains), name3+" ") 172 173 out, err = d.Cmd("service", "ls", "--filter", filter2) 174 c.Assert(err, checker.IsNil) 175 c.Assert(out, checker.Contains, name1+" ") 176 c.Assert(out, checker.Contains, name2+" ") 177 c.Assert(out, checker.Not(checker.Contains), name3+" ") 178 179 out, err = d.Cmd("service", "ls") 180 c.Assert(err, checker.IsNil) 181 c.Assert(out, checker.Contains, name1+" ") 182 c.Assert(out, checker.Contains, name2+" ") 183 c.Assert(out, checker.Contains, name3+" ") 184 } 185 186 func (s *DockerSwarmSuite) TestSwarmNodeListFilter(c *check.C) { 187 d := s.AddDaemon(c, true, true) 188 189 out, err := d.Cmd("node", "inspect", "--format", "{{ .Description.Hostname }}", "self") 190 c.Assert(err, checker.IsNil) 191 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 192 name := strings.TrimSpace(out) 193 194 filter := "name=" + name[:4] 195 196 out, err = d.Cmd("node", "ls", "--filter", filter) 197 c.Assert(err, checker.IsNil) 198 c.Assert(out, checker.Contains, name) 199 200 out, err = d.Cmd("node", "ls", "--filter", "name=none") 201 c.Assert(err, checker.IsNil) 202 c.Assert(out, checker.Not(checker.Contains), name) 203 } 204 205 func (s *DockerSwarmSuite) TestSwarmNodeTaskListFilter(c *check.C) { 206 d := s.AddDaemon(c, true, true) 207 208 name := "redis-cluster-md5" 209 out, err := d.Cmd("service", "create", "--name", name, "--replicas=3", "busybox", "top") 210 c.Assert(err, checker.IsNil) 211 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 212 213 // make sure task has been deployed. 214 waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 3) 215 216 filter := "name=redis-cluster" 217 218 out, err = d.Cmd("node", "ps", "--filter", filter, "self") 219 c.Assert(err, checker.IsNil) 220 c.Assert(out, checker.Contains, name+".1") 221 c.Assert(out, checker.Contains, name+".2") 222 c.Assert(out, checker.Contains, name+".3") 223 224 out, err = d.Cmd("node", "ps", "--filter", "name=none", "self") 225 c.Assert(err, checker.IsNil) 226 c.Assert(out, checker.Not(checker.Contains), name+".1") 227 c.Assert(out, checker.Not(checker.Contains), name+".2") 228 c.Assert(out, checker.Not(checker.Contains), name+".3") 229 } 230 231 // Test case for #25375 232 func (s *DockerSwarmSuite) TestSwarmPublishAdd(c *check.C) { 233 d := s.AddDaemon(c, true, true) 234 235 testCases := []struct { 236 name string 237 publishAdd []string 238 ports string 239 }{ 240 { 241 name: "simple-syntax", 242 publishAdd: []string{ 243 "80:80", 244 "80:80", 245 "80:80", 246 "80:20", 247 }, 248 ports: "[{ tcp 80 80 ingress}]", 249 }, 250 { 251 name: "complex-syntax", 252 publishAdd: []string{ 253 "target=90,published=90,protocol=tcp,mode=ingress", 254 "target=90,published=90,protocol=tcp,mode=ingress", 255 "target=90,published=90,protocol=tcp,mode=ingress", 256 "target=30,published=90,protocol=tcp,mode=ingress", 257 }, 258 ports: "[{ tcp 90 90 ingress}]", 259 }, 260 } 261 262 for _, tc := range testCases { 263 out, err := d.Cmd("service", "create", "--name", tc.name, "--label", "x=y", "busybox", "top") 264 c.Assert(err, checker.IsNil, check.Commentf(out)) 265 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 266 267 out, err = d.cmdRetryOutOfSequence("service", "update", "--publish-add", tc.publishAdd[0], tc.name) 268 c.Assert(err, checker.IsNil, check.Commentf(out)) 269 270 out, err = d.cmdRetryOutOfSequence("service", "update", "--publish-add", tc.publishAdd[1], tc.name) 271 c.Assert(err, checker.IsNil, check.Commentf(out)) 272 273 out, err = d.cmdRetryOutOfSequence("service", "update", "--publish-add", tc.publishAdd[2], "--publish-add", tc.publishAdd[3], tc.name) 274 c.Assert(err, checker.NotNil, check.Commentf(out)) 275 276 out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.EndpointSpec.Ports }}", tc.name) 277 c.Assert(err, checker.IsNil) 278 c.Assert(strings.TrimSpace(out), checker.Equals, tc.ports) 279 } 280 } 281 282 func (s *DockerSwarmSuite) TestSwarmServiceWithGroup(c *check.C) { 283 d := s.AddDaemon(c, true, true) 284 285 name := "top" 286 out, err := d.Cmd("service", "create", "--name", name, "--user", "root:root", "--group", "wheel", "--group", "audio", "--group", "staff", "--group", "777", "busybox", "top") 287 c.Assert(err, checker.IsNil) 288 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 289 290 // make sure task has been deployed. 291 waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1) 292 293 out, err = d.Cmd("ps", "-q") 294 c.Assert(err, checker.IsNil) 295 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 296 297 container := strings.TrimSpace(out) 298 299 out, err = d.Cmd("exec", container, "id") 300 c.Assert(err, checker.IsNil) 301 c.Assert(strings.TrimSpace(out), checker.Equals, "uid=0(root) gid=0(root) groups=10(wheel),29(audio),50(staff),777") 302 } 303 304 func (s *DockerSwarmSuite) TestSwarmContainerAutoStart(c *check.C) { 305 d := s.AddDaemon(c, true, true) 306 307 out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo") 308 c.Assert(err, checker.IsNil) 309 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 310 311 out, err = d.Cmd("run", "-id", "--restart=always", "--net=foo", "--name=test", "busybox", "top") 312 c.Assert(err, checker.IsNil, check.Commentf(out)) 313 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 314 315 out, err = d.Cmd("ps", "-q") 316 c.Assert(err, checker.IsNil, check.Commentf(out)) 317 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 318 319 d.Restart() 320 321 out, err = d.Cmd("ps", "-q") 322 c.Assert(err, checker.IsNil, check.Commentf(out)) 323 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 324 } 325 326 func (s *DockerSwarmSuite) TestSwarmContainerEndpointOptions(c *check.C) { 327 d := s.AddDaemon(c, true, true) 328 329 out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo") 330 c.Assert(err, checker.IsNil, check.Commentf(out)) 331 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 332 333 _, err = d.Cmd("run", "-d", "--net=foo", "--name=first", "--net-alias=first-alias", "busybox", "top") 334 c.Assert(err, checker.IsNil, check.Commentf(out)) 335 336 _, err = d.Cmd("run", "-d", "--net=foo", "--name=second", "busybox", "top") 337 c.Assert(err, checker.IsNil, check.Commentf(out)) 338 339 // ping first container and its alias 340 _, err = d.Cmd("exec", "second", "ping", "-c", "1", "first") 341 c.Assert(err, check.IsNil, check.Commentf(out)) 342 _, err = d.Cmd("exec", "second", "ping", "-c", "1", "first-alias") 343 c.Assert(err, check.IsNil, check.Commentf(out)) 344 } 345 346 func (s *DockerSwarmSuite) TestSwarmContainerAttachByNetworkId(c *check.C) { 347 d := s.AddDaemon(c, true, true) 348 349 out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "testnet") 350 c.Assert(err, checker.IsNil) 351 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 352 networkID := strings.TrimSpace(out) 353 354 out, err = d.Cmd("run", "-d", "--net", networkID, "busybox", "top") 355 c.Assert(err, checker.IsNil) 356 cID := strings.TrimSpace(out) 357 d.waitRun(cID) 358 359 _, err = d.Cmd("rm", "-f", cID) 360 c.Assert(err, checker.IsNil) 361 362 out, err = d.Cmd("network", "rm", "testnet") 363 c.Assert(err, checker.IsNil) 364 365 checkNetwork := func(*check.C) (interface{}, check.CommentInterface) { 366 out, err := d.Cmd("network", "ls") 367 c.Assert(err, checker.IsNil) 368 return out, nil 369 } 370 371 waitAndAssert(c, 3*time.Second, checkNetwork, checker.Not(checker.Contains), "testnet") 372 } 373 374 func (s *DockerSwarmSuite) TestOverlayAttachable(c *check.C) { 375 d := s.AddDaemon(c, true, true) 376 377 out, err := d.Cmd("network", "create", "-d", "overlay", "--attachable", "ovnet") 378 c.Assert(err, checker.IsNil, check.Commentf(out)) 379 380 // validate attachable 381 out, err = d.Cmd("network", "inspect", "--format", "{{json .Attachable}}", "ovnet") 382 c.Assert(err, checker.IsNil, check.Commentf(out)) 383 c.Assert(strings.TrimSpace(out), checker.Equals, "true") 384 385 // validate containers can attache to this overlay network 386 out, err = d.Cmd("run", "-d", "--network", "ovnet", "--name", "c1", "busybox", "top") 387 c.Assert(err, checker.IsNil, check.Commentf(out)) 388 389 // redo validation, there was a bug that the value of attachable changes after 390 // containers attach to the network 391 out, err = d.Cmd("network", "inspect", "--format", "{{json .Attachable}}", "ovnet") 392 c.Assert(err, checker.IsNil, check.Commentf(out)) 393 c.Assert(strings.TrimSpace(out), checker.Equals, "true") 394 } 395 396 func (s *DockerSwarmSuite) TestSwarmRemoveInternalNetwork(c *check.C) { 397 d := s.AddDaemon(c, true, true) 398 399 name := "ingress" 400 out, err := d.Cmd("network", "rm", name) 401 c.Assert(err, checker.NotNil) 402 c.Assert(strings.TrimSpace(out), checker.Contains, name) 403 c.Assert(strings.TrimSpace(out), checker.Contains, "is a pre-defined network and cannot be removed") 404 } 405 406 // Test case for #24108, also the case from: 407 // https://github.com/docker/docker/pull/24620#issuecomment-233715656 408 func (s *DockerSwarmSuite) TestSwarmTaskListFilter(c *check.C) { 409 d := s.AddDaemon(c, true, true) 410 411 name := "redis-cluster-md5" 412 out, err := d.Cmd("service", "create", "--name", name, "--replicas=3", "busybox", "top") 413 c.Assert(err, checker.IsNil) 414 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 415 416 filter := "name=redis-cluster" 417 418 checkNumTasks := func(*check.C) (interface{}, check.CommentInterface) { 419 out, err := d.Cmd("service", "ps", "--filter", filter, name) 420 c.Assert(err, checker.IsNil) 421 return len(strings.Split(out, "\n")) - 2, nil // includes header and nl in last line 422 } 423 424 // wait until all tasks have been created 425 waitAndAssert(c, defaultReconciliationTimeout, checkNumTasks, checker.Equals, 3) 426 427 out, err = d.Cmd("service", "ps", "--filter", filter, name) 428 c.Assert(err, checker.IsNil) 429 c.Assert(out, checker.Contains, name+".1") 430 c.Assert(out, checker.Contains, name+".2") 431 c.Assert(out, checker.Contains, name+".3") 432 433 out, err = d.Cmd("service", "ps", "--filter", "name="+name+".1", name) 434 c.Assert(err, checker.IsNil) 435 c.Assert(out, checker.Contains, name+".1") 436 c.Assert(out, checker.Not(checker.Contains), name+".2") 437 c.Assert(out, checker.Not(checker.Contains), name+".3") 438 439 out, err = d.Cmd("service", "ps", "--filter", "name=none", name) 440 c.Assert(err, checker.IsNil) 441 c.Assert(out, checker.Not(checker.Contains), name+".1") 442 c.Assert(out, checker.Not(checker.Contains), name+".2") 443 c.Assert(out, checker.Not(checker.Contains), name+".3") 444 445 name = "redis-cluster-sha1" 446 out, err = d.Cmd("service", "create", "--name", name, "--mode=global", "busybox", "top") 447 c.Assert(err, checker.IsNil) 448 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 449 450 waitAndAssert(c, defaultReconciliationTimeout, checkNumTasks, checker.Equals, 1) 451 452 filter = "name=redis-cluster" 453 out, err = d.Cmd("service", "ps", "--filter", filter, name) 454 c.Assert(err, checker.IsNil) 455 c.Assert(out, checker.Contains, name) 456 457 out, err = d.Cmd("service", "ps", "--filter", "name="+name, name) 458 c.Assert(err, checker.IsNil) 459 c.Assert(out, checker.Contains, name) 460 461 out, err = d.Cmd("service", "ps", "--filter", "name=none", name) 462 c.Assert(err, checker.IsNil) 463 c.Assert(out, checker.Not(checker.Contains), name) 464 } 465 466 func (s *DockerSwarmSuite) TestPsListContainersFilterIsTask(c *check.C) { 467 d := s.AddDaemon(c, true, true) 468 469 // Create a bare container 470 out, err := d.Cmd("run", "-d", "--name=bare-container", "busybox", "top") 471 c.Assert(err, checker.IsNil) 472 bareID := strings.TrimSpace(out)[:12] 473 // Create a service 474 name := "busybox-top" 475 out, err = d.Cmd("service", "create", "--name", name, "busybox", "top") 476 c.Assert(err, checker.IsNil) 477 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 478 479 // make sure task has been deployed. 480 waitAndAssert(c, defaultReconciliationTimeout, d.checkServiceRunningTasks(name), checker.Equals, 1) 481 482 // Filter non-tasks 483 out, err = d.Cmd("ps", "-a", "-q", "--filter=is-task=false") 484 c.Assert(err, checker.IsNil) 485 psOut := strings.TrimSpace(out) 486 c.Assert(psOut, checker.Equals, bareID, check.Commentf("Expected id %s, got %s for is-task label, output %q", bareID, psOut, out)) 487 488 // Filter tasks 489 out, err = d.Cmd("ps", "-a", "-q", "--filter=is-task=true") 490 c.Assert(err, checker.IsNil) 491 lines := strings.Split(strings.Trim(out, "\n "), "\n") 492 c.Assert(lines, checker.HasLen, 1) 493 c.Assert(lines[0], checker.Not(checker.Equals), bareID, check.Commentf("Expected not %s, but got it for is-task label, output %q", bareID, out)) 494 } 495 496 const globalNetworkPlugin = "global-network-plugin" 497 const globalIPAMPlugin = "global-ipam-plugin" 498 499 func setupRemoteGlobalNetworkPlugin(c *check.C, mux *http.ServeMux, url, netDrv, ipamDrv string) { 500 501 mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) { 502 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 503 fmt.Fprintf(w, `{"Implements": ["%s", "%s"]}`, driverapi.NetworkPluginEndpointType, ipamapi.PluginEndpointType) 504 }) 505 506 // Network driver implementation 507 mux.HandleFunc(fmt.Sprintf("/%s.GetCapabilities", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 508 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 509 fmt.Fprintf(w, `{"Scope":"global"}`) 510 }) 511 512 mux.HandleFunc(fmt.Sprintf("/%s.AllocateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 513 err := json.NewDecoder(r.Body).Decode(&remoteDriverNetworkRequest) 514 if err != nil { 515 http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 516 return 517 } 518 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 519 fmt.Fprintf(w, "null") 520 }) 521 522 mux.HandleFunc(fmt.Sprintf("/%s.FreeNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 523 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 524 fmt.Fprintf(w, "null") 525 }) 526 527 mux.HandleFunc(fmt.Sprintf("/%s.CreateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 528 err := json.NewDecoder(r.Body).Decode(&remoteDriverNetworkRequest) 529 if err != nil { 530 http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 531 return 532 } 533 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 534 fmt.Fprintf(w, "null") 535 }) 536 537 mux.HandleFunc(fmt.Sprintf("/%s.DeleteNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 538 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 539 fmt.Fprintf(w, "null") 540 }) 541 542 mux.HandleFunc(fmt.Sprintf("/%s.CreateEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 543 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 544 fmt.Fprintf(w, `{"Interface":{"MacAddress":"a0:b1:c2:d3:e4:f5"}}`) 545 }) 546 547 mux.HandleFunc(fmt.Sprintf("/%s.Join", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 548 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 549 550 veth := &netlink.Veth{ 551 LinkAttrs: netlink.LinkAttrs{Name: "randomIfName", TxQLen: 0}, PeerName: "cnt0"} 552 if err := netlink.LinkAdd(veth); err != nil { 553 fmt.Fprintf(w, `{"Error":"failed to add veth pair: `+err.Error()+`"}`) 554 } else { 555 fmt.Fprintf(w, `{"InterfaceName":{ "SrcName":"cnt0", "DstPrefix":"veth"}}`) 556 } 557 }) 558 559 mux.HandleFunc(fmt.Sprintf("/%s.Leave", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 560 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 561 fmt.Fprintf(w, "null") 562 }) 563 564 mux.HandleFunc(fmt.Sprintf("/%s.DeleteEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 565 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 566 if link, err := netlink.LinkByName("cnt0"); err == nil { 567 netlink.LinkDel(link) 568 } 569 fmt.Fprintf(w, "null") 570 }) 571 572 // IPAM Driver implementation 573 var ( 574 poolRequest remoteipam.RequestPoolRequest 575 poolReleaseReq remoteipam.ReleasePoolRequest 576 addressRequest remoteipam.RequestAddressRequest 577 addressReleaseReq remoteipam.ReleaseAddressRequest 578 lAS = "localAS" 579 gAS = "globalAS" 580 pool = "172.28.0.0/16" 581 poolID = lAS + "/" + pool 582 gw = "172.28.255.254/16" 583 ) 584 585 mux.HandleFunc(fmt.Sprintf("/%s.GetDefaultAddressSpaces", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 586 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 587 fmt.Fprintf(w, `{"LocalDefaultAddressSpace":"`+lAS+`", "GlobalDefaultAddressSpace": "`+gAS+`"}`) 588 }) 589 590 mux.HandleFunc(fmt.Sprintf("/%s.RequestPool", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 591 err := json.NewDecoder(r.Body).Decode(&poolRequest) 592 if err != nil { 593 http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 594 return 595 } 596 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 597 if poolRequest.AddressSpace != lAS && poolRequest.AddressSpace != gAS { 598 fmt.Fprintf(w, `{"Error":"Unknown address space in pool request: `+poolRequest.AddressSpace+`"}`) 599 } else if poolRequest.Pool != "" && poolRequest.Pool != pool { 600 fmt.Fprintf(w, `{"Error":"Cannot handle explicit pool requests yet"}`) 601 } else { 602 fmt.Fprintf(w, `{"PoolID":"`+poolID+`", "Pool":"`+pool+`"}`) 603 } 604 }) 605 606 mux.HandleFunc(fmt.Sprintf("/%s.RequestAddress", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 607 err := json.NewDecoder(r.Body).Decode(&addressRequest) 608 if err != nil { 609 http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 610 return 611 } 612 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 613 // make sure libnetwork is now querying on the expected pool id 614 if addressRequest.PoolID != poolID { 615 fmt.Fprintf(w, `{"Error":"unknown pool id"}`) 616 } else if addressRequest.Address != "" { 617 fmt.Fprintf(w, `{"Error":"Cannot handle explicit address requests yet"}`) 618 } else { 619 fmt.Fprintf(w, `{"Address":"`+gw+`"}`) 620 } 621 }) 622 623 mux.HandleFunc(fmt.Sprintf("/%s.ReleaseAddress", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 624 err := json.NewDecoder(r.Body).Decode(&addressReleaseReq) 625 if err != nil { 626 http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 627 return 628 } 629 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 630 // make sure libnetwork is now asking to release the expected address from the expected poolid 631 if addressRequest.PoolID != poolID { 632 fmt.Fprintf(w, `{"Error":"unknown pool id"}`) 633 } else if addressReleaseReq.Address != gw { 634 fmt.Fprintf(w, `{"Error":"unknown address"}`) 635 } else { 636 fmt.Fprintf(w, "null") 637 } 638 }) 639 640 mux.HandleFunc(fmt.Sprintf("/%s.ReleasePool", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 641 err := json.NewDecoder(r.Body).Decode(&poolReleaseReq) 642 if err != nil { 643 http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 644 return 645 } 646 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 647 // make sure libnetwork is now asking to release the expected poolid 648 if addressRequest.PoolID != poolID { 649 fmt.Fprintf(w, `{"Error":"unknown pool id"}`) 650 } else { 651 fmt.Fprintf(w, "null") 652 } 653 }) 654 655 err := os.MkdirAll("/etc/docker/plugins", 0755) 656 c.Assert(err, checker.IsNil) 657 658 fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", netDrv) 659 err = ioutil.WriteFile(fileName, []byte(url), 0644) 660 c.Assert(err, checker.IsNil) 661 662 ipamFileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", ipamDrv) 663 err = ioutil.WriteFile(ipamFileName, []byte(url), 0644) 664 c.Assert(err, checker.IsNil) 665 } 666 667 func (s *DockerSwarmSuite) TestSwarmNetworkPlugin(c *check.C) { 668 mux := http.NewServeMux() 669 s.server = httptest.NewServer(mux) 670 c.Assert(s.server, check.NotNil, check.Commentf("Failed to start an HTTP Server")) 671 setupRemoteGlobalNetworkPlugin(c, mux, s.server.URL, globalNetworkPlugin, globalIPAMPlugin) 672 defer func() { 673 s.server.Close() 674 err := os.RemoveAll("/etc/docker/plugins") 675 c.Assert(err, checker.IsNil) 676 }() 677 678 d := s.AddDaemon(c, true, true) 679 680 _, err := d.Cmd("network", "create", "-d", globalNetworkPlugin, "foo") 681 c.Assert(err, checker.NotNil) 682 } 683 684 // Test case for #24712 685 func (s *DockerSwarmSuite) TestSwarmServiceEnvFile(c *check.C) { 686 d := s.AddDaemon(c, true, true) 687 688 path := filepath.Join(d.folder, "env.txt") 689 err := ioutil.WriteFile(path, []byte("VAR1=A\nVAR2=A\n"), 0644) 690 c.Assert(err, checker.IsNil) 691 692 name := "worker" 693 out, err := d.Cmd("service", "create", "--env-file", path, "--env", "VAR1=B", "--env", "VAR1=C", "--env", "VAR2=", "--env", "VAR2", "--name", name, "busybox", "top") 694 c.Assert(err, checker.IsNil) 695 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 696 697 // The complete env is [VAR1=A VAR2=A VAR1=B VAR1=C VAR2= VAR2] and duplicates will be removed => [VAR1=C VAR2] 698 out, err = d.Cmd("inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.Env }}", name) 699 c.Assert(err, checker.IsNil) 700 c.Assert(out, checker.Contains, "[VAR1=C VAR2]") 701 } 702 703 func (s *DockerSwarmSuite) TestSwarmServiceTTY(c *check.C) { 704 d := s.AddDaemon(c, true, true) 705 706 name := "top" 707 708 ttyCheck := "if [ -t 0 ]; then echo TTY > /status && top; else echo none > /status && top; fi" 709 710 // Without --tty 711 expectedOutput := "none" 712 out, err := d.Cmd("service", "create", "--name", name, "busybox", "sh", "-c", ttyCheck) 713 c.Assert(err, checker.IsNil) 714 715 // Make sure task has been deployed. 716 waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1) 717 718 // We need to get the container id. 719 out, err = d.Cmd("ps", "-a", "-q", "--no-trunc") 720 c.Assert(err, checker.IsNil) 721 id := strings.TrimSpace(out) 722 723 out, err = d.Cmd("exec", id, "cat", "/status") 724 c.Assert(err, checker.IsNil) 725 c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out)) 726 727 // Remove service 728 out, err = d.Cmd("service", "rm", name) 729 c.Assert(err, checker.IsNil) 730 // Make sure container has been destroyed. 731 waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 0) 732 733 // With --tty 734 expectedOutput = "TTY" 735 out, err = d.Cmd("service", "create", "--name", name, "--tty", "busybox", "sh", "-c", ttyCheck) 736 c.Assert(err, checker.IsNil) 737 738 // Make sure task has been deployed. 739 waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1) 740 741 // We need to get the container id. 742 out, err = d.Cmd("ps", "-a", "-q", "--no-trunc") 743 c.Assert(err, checker.IsNil) 744 id = strings.TrimSpace(out) 745 746 out, err = d.Cmd("exec", id, "cat", "/status") 747 c.Assert(err, checker.IsNil) 748 c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out)) 749 } 750 751 func (s *DockerSwarmSuite) TestSwarmServiceTTYUpdate(c *check.C) { 752 d := s.AddDaemon(c, true, true) 753 754 // Create a service 755 name := "top" 756 _, err := d.Cmd("service", "create", "--name", name, "busybox", "top") 757 c.Assert(err, checker.IsNil) 758 759 // Make sure task has been deployed. 760 waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1) 761 762 out, err := d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.TTY }}", name) 763 c.Assert(err, checker.IsNil) 764 c.Assert(strings.TrimSpace(out), checker.Equals, "false") 765 766 _, err = d.Cmd("service", "update", "--tty", name) 767 c.Assert(err, checker.IsNil) 768 769 out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.TTY }}", name) 770 c.Assert(err, checker.IsNil) 771 c.Assert(strings.TrimSpace(out), checker.Equals, "true") 772 } 773 774 func (s *DockerSwarmSuite) TestDNSConfig(c *check.C) { 775 d := s.AddDaemon(c, true, true) 776 777 // Create a service 778 name := "top" 779 _, err := d.Cmd("service", "create", "--name", name, "--dns=1.2.3.4", "--dns-search=example.com", "--dns-option=timeout:3", "busybox", "top") 780 c.Assert(err, checker.IsNil) 781 782 // Make sure task has been deployed. 783 waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1) 784 785 // We need to get the container id. 786 out, err := d.Cmd("ps", "-a", "-q", "--no-trunc") 787 c.Assert(err, checker.IsNil) 788 id := strings.TrimSpace(out) 789 790 // Compare against expected output. 791 expectedOutput1 := "nameserver 1.2.3.4" 792 expectedOutput2 := "search example.com" 793 expectedOutput3 := "options timeout:3" 794 out, err = d.Cmd("exec", id, "cat", "/etc/resolv.conf") 795 c.Assert(err, checker.IsNil) 796 c.Assert(out, checker.Contains, expectedOutput1, check.Commentf("Expected '%s', but got %q", expectedOutput1, out)) 797 c.Assert(out, checker.Contains, expectedOutput2, check.Commentf("Expected '%s', but got %q", expectedOutput2, out)) 798 c.Assert(out, checker.Contains, expectedOutput3, check.Commentf("Expected '%s', but got %q", expectedOutput3, out)) 799 } 800 801 func (s *DockerSwarmSuite) TestDNSConfigUpdate(c *check.C) { 802 d := s.AddDaemon(c, true, true) 803 804 // Create a service 805 name := "top" 806 _, err := d.Cmd("service", "create", "--name", name, "busybox", "top") 807 c.Assert(err, checker.IsNil) 808 809 // Make sure task has been deployed. 810 waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1) 811 812 _, err = d.Cmd("service", "update", "--dns-add=1.2.3.4", "--dns-search-add=example.com", "--dns-option-add=timeout:3", name) 813 c.Assert(err, checker.IsNil) 814 815 out, err := d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.DNSConfig }}", name) 816 c.Assert(err, checker.IsNil) 817 c.Assert(strings.TrimSpace(out), checker.Equals, "{[1.2.3.4] [example.com] [timeout:3]}") 818 } 819 820 func (s *DockerSwarmSuite) TestSwarmInitLocked(c *check.C) { 821 d := s.AddDaemon(c, false, false) 822 823 outs, err := d.Cmd("swarm", "init", "--autolock") 824 c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs)) 825 826 c.Assert(outs, checker.Contains, "docker swarm unlock") 827 828 var unlockKey string 829 for _, line := range strings.Split(outs, "\n") { 830 if strings.Contains(line, "SWMKEY") { 831 unlockKey = strings.TrimSpace(line) 832 break 833 } 834 } 835 836 c.Assert(unlockKey, checker.Not(checker.Equals), "") 837 838 outs, err = d.Cmd("swarm", "unlock-key", "-q") 839 c.Assert(outs, checker.Equals, unlockKey+"\n") 840 841 info, err := d.info() 842 c.Assert(err, checker.IsNil) 843 c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive) 844 845 c.Assert(d.Restart(), checker.IsNil) 846 847 info, err = d.info() 848 c.Assert(err, checker.IsNil) 849 c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateLocked) 850 851 cmd := d.command("swarm", "unlock") 852 cmd.Stdin = bytes.NewBufferString("wrong-secret-key") 853 out, err := cmd.CombinedOutput() 854 c.Assert(err, checker.NotNil, check.Commentf("out: %v", string(out))) 855 c.Assert(string(out), checker.Contains, "invalid key") 856 857 cmd = d.command("swarm", "unlock") 858 cmd.Stdin = bytes.NewBufferString(unlockKey) 859 out, err = cmd.CombinedOutput() 860 c.Assert(err, checker.IsNil, check.Commentf("out: %v", string(out))) 861 862 info, err = d.info() 863 c.Assert(err, checker.IsNil) 864 c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive) 865 866 outs, err = d.Cmd("node", "ls") 867 c.Assert(err, checker.IsNil) 868 c.Assert(outs, checker.Not(checker.Contains), "Swarm is encrypted and needs to be unlocked") 869 870 outs, err = d.Cmd("swarm", "update", "--autolock=false") 871 c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs)) 872 873 // Wait for autolock to be turned off 874 time.Sleep(time.Second) 875 876 c.Assert(d.Restart(), checker.IsNil) 877 878 info, err = d.info() 879 c.Assert(err, checker.IsNil) 880 c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive) 881 882 outs, err = d.Cmd("node", "ls") 883 c.Assert(err, checker.IsNil) 884 c.Assert(outs, checker.Not(checker.Contains), "Swarm is encrypted and needs to be unlocked") 885 } 886 887 func (s *DockerSwarmSuite) TestSwarmLeaveLocked(c *check.C) { 888 d := s.AddDaemon(c, false, false) 889 890 outs, err := d.Cmd("swarm", "init", "--autolock") 891 c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs)) 892 893 c.Assert(d.Restart("--swarm-default-advertise-addr=lo"), checker.IsNil) 894 895 info, err := d.info() 896 c.Assert(err, checker.IsNil) 897 c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateLocked) 898 899 outs, _ = d.Cmd("node", "ls") 900 c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked") 901 902 outs, err = d.Cmd("swarm", "leave", "--force") 903 c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs)) 904 905 info, err = d.info() 906 c.Assert(err, checker.IsNil) 907 c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive) 908 909 outs, err = d.Cmd("swarm", "init") 910 c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs)) 911 912 info, err = d.info() 913 c.Assert(err, checker.IsNil) 914 c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive) 915 } 916 917 func (s *DockerSwarmSuite) TestSwarmRotateUnlockKey(c *check.C) { 918 d := s.AddDaemon(c, true, true) 919 920 outs, err := d.Cmd("swarm", "update", "--autolock") 921 c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs)) 922 923 c.Assert(outs, checker.Contains, "docker swarm unlock") 924 925 var unlockKey string 926 for _, line := range strings.Split(outs, "\n") { 927 if strings.Contains(line, "SWMKEY") { 928 unlockKey = strings.TrimSpace(line) 929 break 930 } 931 } 932 933 c.Assert(unlockKey, checker.Not(checker.Equals), "") 934 935 outs, err = d.Cmd("swarm", "unlock-key", "-q") 936 c.Assert(outs, checker.Equals, unlockKey+"\n") 937 938 // Rotate multiple times 939 for i := 0; i != 3; i++ { 940 outs, err = d.Cmd("swarm", "unlock-key", "-q", "--rotate") 941 c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs)) 942 // Strip \n 943 newUnlockKey := outs[:len(outs)-1] 944 c.Assert(newUnlockKey, checker.Not(checker.Equals), "") 945 c.Assert(newUnlockKey, checker.Not(checker.Equals), unlockKey) 946 947 c.Assert(d.Restart(), checker.IsNil) 948 949 info, err := d.info() 950 c.Assert(err, checker.IsNil) 951 c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateLocked) 952 953 outs, _ = d.Cmd("node", "ls") 954 c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked") 955 956 cmd := d.command("swarm", "unlock") 957 cmd.Stdin = bytes.NewBufferString(unlockKey) 958 out, err := cmd.CombinedOutput() 959 960 if err == nil { 961 // On occasion, the daemon may not have finished 962 // rotating the KEK before restarting. The test is 963 // intentionally written to explore this behavior. 964 // When this happens, unlocking with the old key will 965 // succeed. If we wait for the rotation to happen and 966 // restart again, the new key should be required this 967 // time. 968 969 time.Sleep(3 * time.Second) 970 971 c.Assert(d.Restart(), checker.IsNil) 972 973 cmd = d.command("swarm", "unlock") 974 cmd.Stdin = bytes.NewBufferString(unlockKey) 975 out, err = cmd.CombinedOutput() 976 } 977 c.Assert(err, checker.NotNil, check.Commentf("out: %v", string(out))) 978 c.Assert(string(out), checker.Contains, "invalid key") 979 980 outs, _ = d.Cmd("node", "ls") 981 c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked") 982 983 cmd = d.command("swarm", "unlock") 984 cmd.Stdin = bytes.NewBufferString(newUnlockKey) 985 out, err = cmd.CombinedOutput() 986 c.Assert(err, checker.IsNil, check.Commentf("out: %v", string(out))) 987 988 info, err = d.info() 989 c.Assert(err, checker.IsNil) 990 c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive) 991 992 outs, err = d.Cmd("node", "ls") 993 c.Assert(err, checker.IsNil) 994 c.Assert(outs, checker.Not(checker.Contains), "Swarm is encrypted and needs to be unlocked") 995 996 unlockKey = newUnlockKey 997 } 998 } 999 1000 func (s *DockerSwarmSuite) TestExtraHosts(c *check.C) { 1001 d := s.AddDaemon(c, true, true) 1002 1003 // Create a service 1004 name := "top" 1005 _, err := d.Cmd("service", "create", "--name", name, "--host=example.com:1.2.3.4", "busybox", "top") 1006 c.Assert(err, checker.IsNil) 1007 1008 // Make sure task has been deployed. 1009 waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1) 1010 1011 // We need to get the container id. 1012 out, err := d.Cmd("ps", "-a", "-q", "--no-trunc") 1013 c.Assert(err, checker.IsNil) 1014 id := strings.TrimSpace(out) 1015 1016 // Compare against expected output. 1017 expectedOutput := "1.2.3.4\texample.com" 1018 out, err = d.Cmd("exec", id, "cat", "/etc/hosts") 1019 c.Assert(err, checker.IsNil) 1020 c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out)) 1021 } 1022 1023 func (s *DockerSwarmSuite) TestSwarmManagerAddress(c *check.C) { 1024 d1 := s.AddDaemon(c, true, true) 1025 d2 := s.AddDaemon(c, true, false) 1026 d3 := s.AddDaemon(c, true, false) 1027 1028 // Manager Addresses will always show Node 1's address 1029 expectedOutput := fmt.Sprintf("Manager Addresses:\n 127.0.0.1:%d\n", d1.port) 1030 1031 out, err := d1.Cmd("info") 1032 c.Assert(err, checker.IsNil) 1033 c.Assert(out, checker.Contains, expectedOutput) 1034 1035 out, err = d2.Cmd("info") 1036 c.Assert(err, checker.IsNil) 1037 c.Assert(out, checker.Contains, expectedOutput) 1038 1039 out, err = d3.Cmd("info") 1040 c.Assert(err, checker.IsNil) 1041 c.Assert(out, checker.Contains, expectedOutput) 1042 } 1043 1044 func (s *DockerSwarmSuite) TestSwarmServiceInspectPretty(c *check.C) { 1045 d := s.AddDaemon(c, true, true) 1046 1047 name := "top" 1048 out, err := d.Cmd("service", "create", "--name", name, "--limit-cpu=0.5", "busybox", "top") 1049 c.Assert(err, checker.IsNil, check.Commentf(out)) 1050 1051 expectedOutput := ` 1052 Resources: 1053 Limits: 1054 CPU: 0.5` 1055 out, err = d.Cmd("service", "inspect", "--pretty", name) 1056 c.Assert(err, checker.IsNil, check.Commentf(out)) 1057 c.Assert(out, checker.Contains, expectedOutput, check.Commentf(out)) 1058 } 1059 1060 func (s *DockerSwarmSuite) TestSwarmNetworkIPAMOptions(c *check.C) { 1061 d := s.AddDaemon(c, true, true) 1062 1063 out, err := d.Cmd("network", "create", "-d", "overlay", "--ipam-opt", "foo=bar", "foo") 1064 c.Assert(err, checker.IsNil, check.Commentf(out)) 1065 c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") 1066 1067 out, err = d.Cmd("network", "inspect", "--format", "{{.IPAM.Options}}", "foo") 1068 c.Assert(err, checker.IsNil, check.Commentf(out)) 1069 c.Assert(strings.TrimSpace(out), checker.Equals, "map[foo:bar]") 1070 1071 out, err = d.Cmd("service", "create", "--network=foo", "--name", "top", "busybox", "top") 1072 c.Assert(err, checker.IsNil, check.Commentf(out)) 1073 1074 // make sure task has been deployed. 1075 waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1) 1076 1077 out, err = d.Cmd("network", "inspect", "--format", "{{.IPAM.Options}}", "foo") 1078 c.Assert(err, checker.IsNil, check.Commentf(out)) 1079 c.Assert(strings.TrimSpace(out), checker.Equals, "map[foo:bar]") 1080 } 1081 1082 // TODO: migrate to a unit test 1083 // This test could be migrated to unit test and save costly integration test, 1084 // once PR #29143 is merged. 1085 func (s *DockerSwarmSuite) TestSwarmUpdateWithoutArgs(c *check.C) { 1086 d := s.AddDaemon(c, true, true) 1087 1088 expectedOutput := ` 1089 Usage: docker swarm update [OPTIONS] 1090 1091 Update the swarm 1092 1093 Options:` 1094 1095 out, err := d.Cmd("swarm", "update") 1096 c.Assert(err, checker.IsNil, check.Commentf("out: %v", out)) 1097 c.Assert(out, checker.Contains, expectedOutput, check.Commentf(out)) 1098 } 1099 1100 func (s *DockerTrustedSwarmSuite) TestTrustedServiceCreate(c *check.C) { 1101 d := s.swarmSuite.AddDaemon(c, true, true) 1102 1103 // Attempt creating a service from an image that is known to notary. 1104 repoName := s.trustSuite.setupTrustedImage(c, "trusted-pull") 1105 1106 name := "trusted" 1107 serviceCmd := d.command("-D", "service", "create", "--name", name, repoName, "top") 1108 s.trustSuite.trustedCmd(serviceCmd) 1109 out, _, err := runCommandWithOutput(serviceCmd) 1110 c.Assert(err, checker.IsNil, check.Commentf(out)) 1111 c.Assert(out, checker.Contains, "resolved image tag to", check.Commentf(out)) 1112 1113 out, err = d.Cmd("service", "inspect", "--pretty", name) 1114 c.Assert(err, checker.IsNil, check.Commentf(out)) 1115 c.Assert(out, checker.Contains, repoName+"@", check.Commentf(out)) 1116 1117 // Try trusted service create on an untrusted tag. 1118 1119 repoName = fmt.Sprintf("%v/untrustedservicecreate/createtest:latest", privateRegistryURL) 1120 // tag the image and upload it to the private registry 1121 dockerCmd(c, "tag", "busybox", repoName) 1122 dockerCmd(c, "push", repoName) 1123 dockerCmd(c, "rmi", repoName) 1124 1125 name = "untrusted" 1126 serviceCmd = d.command("service", "create", "--name", name, repoName, "top") 1127 s.trustSuite.trustedCmd(serviceCmd) 1128 out, _, err = runCommandWithOutput(serviceCmd) 1129 1130 c.Assert(err, check.NotNil, check.Commentf(out)) 1131 c.Assert(string(out), checker.Contains, "Error: remote trust data does not exist", check.Commentf(out)) 1132 1133 out, err = d.Cmd("service", "inspect", "--pretty", name) 1134 c.Assert(err, checker.NotNil, check.Commentf(out)) 1135 } 1136 1137 func (s *DockerTrustedSwarmSuite) TestTrustedServiceUpdate(c *check.C) { 1138 d := s.swarmSuite.AddDaemon(c, true, true) 1139 1140 // Attempt creating a service from an image that is known to notary. 1141 repoName := s.trustSuite.setupTrustedImage(c, "trusted-pull") 1142 1143 name := "myservice" 1144 1145 // Create a service without content trust 1146 _, err := d.Cmd("service", "create", "--name", name, repoName, "top") 1147 c.Assert(err, checker.IsNil) 1148 1149 out, err := d.Cmd("service", "inspect", "--pretty", name) 1150 c.Assert(err, checker.IsNil, check.Commentf(out)) 1151 // Daemon won't insert the digest because this is disabled by 1152 // DOCKER_SERVICE_PREFER_OFFLINE_IMAGE. 1153 c.Assert(out, check.Not(checker.Contains), repoName+"@", check.Commentf(out)) 1154 1155 serviceCmd := d.command("-D", "service", "update", "--image", repoName, name) 1156 s.trustSuite.trustedCmd(serviceCmd) 1157 out, _, err = runCommandWithOutput(serviceCmd) 1158 c.Assert(err, checker.IsNil, check.Commentf(out)) 1159 c.Assert(out, checker.Contains, "resolved image tag to", check.Commentf(out)) 1160 1161 out, err = d.Cmd("service", "inspect", "--pretty", name) 1162 c.Assert(err, checker.IsNil, check.Commentf(out)) 1163 c.Assert(out, checker.Contains, repoName+"@", check.Commentf(out)) 1164 1165 // Try trusted service update on an untrusted tag. 1166 1167 repoName = fmt.Sprintf("%v/untrustedservicecreate/createtest:latest", privateRegistryURL) 1168 // tag the image and upload it to the private registry 1169 dockerCmd(c, "tag", "busybox", repoName) 1170 dockerCmd(c, "push", repoName) 1171 dockerCmd(c, "rmi", repoName) 1172 1173 serviceCmd = d.command("service", "update", "--image", repoName, name) 1174 s.trustSuite.trustedCmd(serviceCmd) 1175 out, _, err = runCommandWithOutput(serviceCmd) 1176 1177 c.Assert(err, check.NotNil, check.Commentf(out)) 1178 c.Assert(string(out), checker.Contains, "Error: remote trust data does not exist", check.Commentf(out)) 1179 } 1180 1181 // Test case for issue #27866, which did not allow NW name that is the prefix of a swarm NW ID. 1182 // e.g. if the ingress ID starts with "n1", it was impossible to create a NW named "n1". 1183 func (s *DockerSwarmSuite) TestSwarmNetworkCreateIssue27866(c *check.C) { 1184 d := s.AddDaemon(c, true, true) 1185 out, err := d.Cmd("network", "inspect", "-f", "{{.Id}}", "ingress") 1186 c.Assert(err, checker.IsNil, check.Commentf("out: %v", out)) 1187 ingressID := strings.TrimSpace(out) 1188 c.Assert(ingressID, checker.Not(checker.Equals), "") 1189 1190 // create a network of which name is the prefix of the ID of an overlay network 1191 // (ingressID in this case) 1192 newNetName := ingressID[0:2] 1193 out, err = d.Cmd("network", "create", "--driver", "overlay", newNetName) 1194 // In #27866, it was failing because of "network with name %s already exists" 1195 c.Assert(err, checker.IsNil, check.Commentf("out: %v", out)) 1196 out, err = d.Cmd("network", "rm", newNetName) 1197 c.Assert(err, checker.IsNil, check.Commentf("out: %v", out)) 1198 } 1199 1200 // Test case for https://github.com/docker/docker/pull/27938#issuecomment-265768303 1201 // This test creates two networks with the same name sequentially, with various drivers. 1202 // Since the operations in this test are done sequentially, the 2nd call should fail with 1203 // "network with name FOO already exists". 1204 // Note that it is to ok have multiple networks with the same name if the operations are done 1205 // in parallel. (#18864) 1206 func (s *DockerSwarmSuite) TestSwarmNetworkCreateDup(c *check.C) { 1207 d := s.AddDaemon(c, true, true) 1208 drivers := []string{"bridge", "overlay"} 1209 for i, driver1 := range drivers { 1210 nwName := fmt.Sprintf("network-test-%d", i) 1211 for _, driver2 := range drivers { 1212 c.Logf("Creating a network named %q with %q, then %q", 1213 nwName, driver1, driver2) 1214 out, err := d.Cmd("network", "create", "--driver", driver1, nwName) 1215 c.Assert(err, checker.IsNil, check.Commentf("out: %v", out)) 1216 out, err = d.Cmd("network", "create", "--driver", driver2, nwName) 1217 c.Assert(out, checker.Contains, 1218 fmt.Sprintf("network with name %s already exists", nwName)) 1219 c.Assert(err, checker.NotNil) 1220 c.Logf("As expected, the attempt to network %q with %q failed: %s", 1221 nwName, driver2, out) 1222 out, err = d.Cmd("network", "rm", nwName) 1223 c.Assert(err, checker.IsNil, check.Commentf("out: %v", out)) 1224 } 1225 } 1226 }