github.com/mforkel/docker-ce-i386@v17.12.1-ce-rc2+incompatible/components/engine/daemon/container_operations.go (about) 1 package daemon 2 3 import ( 4 "errors" 5 "fmt" 6 "net" 7 "os" 8 "path" 9 "runtime" 10 "strings" 11 "time" 12 13 containertypes "github.com/docker/docker/api/types/container" 14 networktypes "github.com/docker/docker/api/types/network" 15 "github.com/docker/docker/container" 16 "github.com/docker/docker/daemon/network" 17 "github.com/docker/docker/opts" 18 "github.com/docker/docker/pkg/stringid" 19 "github.com/docker/docker/runconfig" 20 "github.com/docker/go-connections/nat" 21 "github.com/docker/libnetwork" 22 "github.com/docker/libnetwork/netlabel" 23 "github.com/docker/libnetwork/options" 24 "github.com/docker/libnetwork/types" 25 "github.com/sirupsen/logrus" 26 ) 27 28 var ( 29 // ErrRootFSReadOnly is returned when a container 30 // rootfs is marked readonly. 31 ErrRootFSReadOnly = errors.New("container rootfs is marked read-only") 32 getPortMapInfo = container.GetSandboxPortMapInfo 33 ) 34 35 func (daemon *Daemon) getDNSSearchSettings(container *container.Container) []string { 36 if len(container.HostConfig.DNSSearch) > 0 { 37 return container.HostConfig.DNSSearch 38 } 39 40 if len(daemon.configStore.DNSSearch) > 0 { 41 return daemon.configStore.DNSSearch 42 } 43 44 return nil 45 } 46 47 func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]libnetwork.SandboxOption, error) { 48 var ( 49 sboxOptions []libnetwork.SandboxOption 50 err error 51 dns []string 52 dnsOptions []string 53 bindings = make(nat.PortMap) 54 pbList []types.PortBinding 55 exposeList []types.TransportPort 56 ) 57 58 defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName() 59 sboxOptions = append(sboxOptions, libnetwork.OptionHostname(container.Config.Hostname), 60 libnetwork.OptionDomainname(container.Config.Domainname)) 61 62 if container.HostConfig.NetworkMode.IsHost() { 63 sboxOptions = append(sboxOptions, libnetwork.OptionUseDefaultSandbox()) 64 if len(container.HostConfig.ExtraHosts) == 0 { 65 sboxOptions = append(sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts")) 66 } 67 if len(container.HostConfig.DNS) == 0 && len(daemon.configStore.DNS) == 0 && 68 len(container.HostConfig.DNSSearch) == 0 && len(daemon.configStore.DNSSearch) == 0 && 69 len(container.HostConfig.DNSOptions) == 0 && len(daemon.configStore.DNSOptions) == 0 { 70 sboxOptions = append(sboxOptions, libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf")) 71 } 72 } else { 73 // OptionUseExternalKey is mandatory for userns support. 74 // But optional for non-userns support 75 sboxOptions = append(sboxOptions, libnetwork.OptionUseExternalKey()) 76 } 77 78 if err = setupPathsAndSandboxOptions(container, &sboxOptions); err != nil { 79 return nil, err 80 } 81 82 if len(container.HostConfig.DNS) > 0 { 83 dns = container.HostConfig.DNS 84 } else if len(daemon.configStore.DNS) > 0 { 85 dns = daemon.configStore.DNS 86 } 87 88 for _, d := range dns { 89 sboxOptions = append(sboxOptions, libnetwork.OptionDNS(d)) 90 } 91 92 dnsSearch := daemon.getDNSSearchSettings(container) 93 94 for _, ds := range dnsSearch { 95 sboxOptions = append(sboxOptions, libnetwork.OptionDNSSearch(ds)) 96 } 97 98 if len(container.HostConfig.DNSOptions) > 0 { 99 dnsOptions = container.HostConfig.DNSOptions 100 } else if len(daemon.configStore.DNSOptions) > 0 { 101 dnsOptions = daemon.configStore.DNSOptions 102 } 103 104 for _, ds := range dnsOptions { 105 sboxOptions = append(sboxOptions, libnetwork.OptionDNSOptions(ds)) 106 } 107 108 if container.NetworkSettings.SecondaryIPAddresses != nil { 109 name := container.Config.Hostname 110 if container.Config.Domainname != "" { 111 name = name + "." + container.Config.Domainname 112 } 113 114 for _, a := range container.NetworkSettings.SecondaryIPAddresses { 115 sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(name, a.Addr)) 116 } 117 } 118 119 for _, extraHost := range container.HostConfig.ExtraHosts { 120 // allow IPv6 addresses in extra hosts; only split on first ":" 121 if _, err := opts.ValidateExtraHost(extraHost); err != nil { 122 return nil, err 123 } 124 parts := strings.SplitN(extraHost, ":", 2) 125 sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(parts[0], parts[1])) 126 } 127 128 if container.HostConfig.PortBindings != nil { 129 for p, b := range container.HostConfig.PortBindings { 130 bindings[p] = []nat.PortBinding{} 131 for _, bb := range b { 132 bindings[p] = append(bindings[p], nat.PortBinding{ 133 HostIP: bb.HostIP, 134 HostPort: bb.HostPort, 135 }) 136 } 137 } 138 } 139 140 portSpecs := container.Config.ExposedPorts 141 ports := make([]nat.Port, len(portSpecs)) 142 var i int 143 for p := range portSpecs { 144 ports[i] = p 145 i++ 146 } 147 nat.SortPortMap(ports, bindings) 148 for _, port := range ports { 149 expose := types.TransportPort{} 150 expose.Proto = types.ParseProtocol(port.Proto()) 151 expose.Port = uint16(port.Int()) 152 exposeList = append(exposeList, expose) 153 154 pb := types.PortBinding{Port: expose.Port, Proto: expose.Proto} 155 binding := bindings[port] 156 for i := 0; i < len(binding); i++ { 157 pbCopy := pb.GetCopy() 158 newP, err := nat.NewPort(nat.SplitProtoPort(binding[i].HostPort)) 159 var portStart, portEnd int 160 if err == nil { 161 portStart, portEnd, err = newP.Range() 162 } 163 if err != nil { 164 return nil, fmt.Errorf("Error parsing HostPort value(%s):%v", binding[i].HostPort, err) 165 } 166 pbCopy.HostPort = uint16(portStart) 167 pbCopy.HostPortEnd = uint16(portEnd) 168 pbCopy.HostIP = net.ParseIP(binding[i].HostIP) 169 pbList = append(pbList, pbCopy) 170 } 171 172 if container.HostConfig.PublishAllPorts && len(binding) == 0 { 173 pbList = append(pbList, pb) 174 } 175 } 176 177 sboxOptions = append(sboxOptions, 178 libnetwork.OptionPortMapping(pbList), 179 libnetwork.OptionExposedPorts(exposeList)) 180 181 // Legacy Link feature is supported only for the default bridge network. 182 // return if this call to build join options is not for default bridge network 183 // Legacy Link is only supported by docker run --link 184 bridgeSettings, ok := container.NetworkSettings.Networks[defaultNetName] 185 if !ok || bridgeSettings.EndpointSettings == nil { 186 return sboxOptions, nil 187 } 188 189 if bridgeSettings.EndpointID == "" { 190 return sboxOptions, nil 191 } 192 193 var ( 194 childEndpoints, parentEndpoints []string 195 cEndpointID string 196 ) 197 198 children := daemon.children(container) 199 for linkAlias, child := range children { 200 if !isLinkable(child) { 201 return nil, fmt.Errorf("Cannot link to %s, as it does not belong to the default network", child.Name) 202 } 203 _, alias := path.Split(linkAlias) 204 // allow access to the linked container via the alias, real name, and container hostname 205 aliasList := alias + " " + child.Config.Hostname 206 // only add the name if alias isn't equal to the name 207 if alias != child.Name[1:] { 208 aliasList = aliasList + " " + child.Name[1:] 209 } 210 sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(aliasList, child.NetworkSettings.Networks[defaultNetName].IPAddress)) 211 cEndpointID = child.NetworkSettings.Networks[defaultNetName].EndpointID 212 if cEndpointID != "" { 213 childEndpoints = append(childEndpoints, cEndpointID) 214 } 215 } 216 217 for alias, parent := range daemon.parents(container) { 218 if daemon.configStore.DisableBridge || !container.HostConfig.NetworkMode.IsPrivate() { 219 continue 220 } 221 222 _, alias = path.Split(alias) 223 logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", parent.ID, alias, bridgeSettings.IPAddress) 224 sboxOptions = append(sboxOptions, libnetwork.OptionParentUpdate( 225 parent.ID, 226 alias, 227 bridgeSettings.IPAddress, 228 )) 229 if cEndpointID != "" { 230 parentEndpoints = append(parentEndpoints, cEndpointID) 231 } 232 } 233 234 linkOptions := options.Generic{ 235 netlabel.GenericData: options.Generic{ 236 "ParentEndpoints": parentEndpoints, 237 "ChildEndpoints": childEndpoints, 238 }, 239 } 240 241 sboxOptions = append(sboxOptions, libnetwork.OptionGeneric(linkOptions)) 242 return sboxOptions, nil 243 } 244 245 func (daemon *Daemon) updateNetworkSettings(container *container.Container, n libnetwork.Network, endpointConfig *networktypes.EndpointSettings) error { 246 if container.NetworkSettings == nil { 247 container.NetworkSettings = &network.Settings{Networks: make(map[string]*network.EndpointSettings)} 248 } 249 250 if !container.HostConfig.NetworkMode.IsHost() && containertypes.NetworkMode(n.Type()).IsHost() { 251 return runconfig.ErrConflictHostNetwork 252 } 253 254 for s := range container.NetworkSettings.Networks { 255 sn, err := daemon.FindNetwork(s) 256 if err != nil { 257 continue 258 } 259 260 if sn.Name() == n.Name() { 261 // Avoid duplicate config 262 return nil 263 } 264 if !containertypes.NetworkMode(sn.Type()).IsPrivate() || 265 !containertypes.NetworkMode(n.Type()).IsPrivate() { 266 return runconfig.ErrConflictSharedNetwork 267 } 268 if containertypes.NetworkMode(sn.Name()).IsNone() || 269 containertypes.NetworkMode(n.Name()).IsNone() { 270 return runconfig.ErrConflictNoNetwork 271 } 272 } 273 274 if _, ok := container.NetworkSettings.Networks[n.Name()]; !ok { 275 container.NetworkSettings.Networks[n.Name()] = &network.EndpointSettings{ 276 EndpointSettings: endpointConfig, 277 } 278 } 279 280 return nil 281 } 282 283 func (daemon *Daemon) updateEndpointNetworkSettings(container *container.Container, n libnetwork.Network, ep libnetwork.Endpoint) error { 284 if err := container.BuildEndpointInfo(n, ep); err != nil { 285 return err 286 } 287 288 if container.HostConfig.NetworkMode == runconfig.DefaultDaemonNetworkMode() { 289 container.NetworkSettings.Bridge = daemon.configStore.BridgeConfig.Iface 290 } 291 292 return nil 293 } 294 295 // UpdateNetwork is used to update the container's network (e.g. when linked containers 296 // get removed/unlinked). 297 func (daemon *Daemon) updateNetwork(container *container.Container) error { 298 var ( 299 start = time.Now() 300 ctrl = daemon.netController 301 sid = container.NetworkSettings.SandboxID 302 ) 303 304 sb, err := ctrl.SandboxByID(sid) 305 if err != nil { 306 return fmt.Errorf("error locating sandbox id %s: %v", sid, err) 307 } 308 309 // Find if container is connected to the default bridge network 310 var n libnetwork.Network 311 for name := range container.NetworkSettings.Networks { 312 sn, err := daemon.FindNetwork(name) 313 if err != nil { 314 continue 315 } 316 if sn.Name() == runconfig.DefaultDaemonNetworkMode().NetworkName() { 317 n = sn 318 break 319 } 320 } 321 322 if n == nil { 323 // Not connected to the default bridge network; Nothing to do 324 return nil 325 } 326 327 options, err := daemon.buildSandboxOptions(container) 328 if err != nil { 329 return fmt.Errorf("Update network failed: %v", err) 330 } 331 332 if err := sb.Refresh(options...); err != nil { 333 return fmt.Errorf("Update network failed: Failure in refresh sandbox %s: %v", sid, err) 334 } 335 336 networkActions.WithValues("update").UpdateSince(start) 337 338 return nil 339 } 340 341 func (daemon *Daemon) findAndAttachNetwork(container *container.Container, idOrName string, epConfig *networktypes.EndpointSettings) (libnetwork.Network, *networktypes.NetworkingConfig, error) { 342 n, err := daemon.FindNetwork(idOrName) 343 if err != nil { 344 // We should always be able to find the network for a 345 // managed container. 346 if container.Managed { 347 return nil, nil, err 348 } 349 } 350 351 // If we found a network and if it is not dynamically created 352 // we should never attempt to attach to that network here. 353 if n != nil { 354 if container.Managed || !n.Info().Dynamic() { 355 return n, nil, nil 356 } 357 } 358 359 var addresses []string 360 if epConfig != nil && epConfig.IPAMConfig != nil { 361 if epConfig.IPAMConfig.IPv4Address != "" { 362 addresses = append(addresses, epConfig.IPAMConfig.IPv4Address) 363 } 364 365 if epConfig.IPAMConfig.IPv6Address != "" { 366 addresses = append(addresses, epConfig.IPAMConfig.IPv6Address) 367 } 368 } 369 370 var ( 371 config *networktypes.NetworkingConfig 372 retryCount int 373 ) 374 375 for { 376 // In all other cases, attempt to attach to the network to 377 // trigger attachment in the swarm cluster manager. 378 if daemon.clusterProvider != nil { 379 var err error 380 config, err = daemon.clusterProvider.AttachNetwork(idOrName, container.ID, addresses) 381 if err != nil { 382 return nil, nil, err 383 } 384 } 385 386 n, err = daemon.FindNetwork(idOrName) 387 if err != nil { 388 if daemon.clusterProvider != nil { 389 if err := daemon.clusterProvider.DetachNetwork(idOrName, container.ID); err != nil { 390 logrus.Warnf("Could not rollback attachment for container %s to network %s: %v", container.ID, idOrName, err) 391 } 392 } 393 394 // Retry network attach again if we failed to 395 // find the network after successful 396 // attachment because the only reason that 397 // would happen is if some other container 398 // attached to the swarm scope network went down 399 // and removed the network while we were in 400 // the process of attaching. 401 if config != nil { 402 if _, ok := err.(libnetwork.ErrNoSuchNetwork); ok { 403 if retryCount >= 5 { 404 return nil, nil, fmt.Errorf("could not find network %s after successful attachment", idOrName) 405 } 406 retryCount++ 407 continue 408 } 409 } 410 411 return nil, nil, err 412 } 413 414 break 415 } 416 417 // This container has attachment to a swarm scope 418 // network. Update the container network settings accordingly. 419 container.NetworkSettings.HasSwarmEndpoint = true 420 return n, config, nil 421 } 422 423 // updateContainerNetworkSettings updates the network settings 424 func (daemon *Daemon) updateContainerNetworkSettings(container *container.Container, endpointsConfig map[string]*networktypes.EndpointSettings) { 425 var n libnetwork.Network 426 427 mode := container.HostConfig.NetworkMode 428 if container.Config.NetworkDisabled || mode.IsContainer() { 429 return 430 } 431 432 networkName := mode.NetworkName() 433 if mode.IsDefault() { 434 networkName = daemon.netController.Config().Daemon.DefaultNetwork 435 } 436 437 if mode.IsUserDefined() { 438 var err error 439 440 n, err = daemon.FindNetwork(networkName) 441 if err == nil { 442 networkName = n.Name() 443 } 444 } 445 446 if container.NetworkSettings == nil { 447 container.NetworkSettings = &network.Settings{} 448 } 449 450 if len(endpointsConfig) > 0 { 451 if container.NetworkSettings.Networks == nil { 452 container.NetworkSettings.Networks = make(map[string]*network.EndpointSettings) 453 } 454 455 for name, epConfig := range endpointsConfig { 456 container.NetworkSettings.Networks[name] = &network.EndpointSettings{ 457 EndpointSettings: epConfig, 458 } 459 } 460 } 461 462 if container.NetworkSettings.Networks == nil { 463 container.NetworkSettings.Networks = make(map[string]*network.EndpointSettings) 464 container.NetworkSettings.Networks[networkName] = &network.EndpointSettings{ 465 EndpointSettings: &networktypes.EndpointSettings{}, 466 } 467 } 468 469 // Convert any settings added by client in default name to 470 // engine's default network name key 471 if mode.IsDefault() { 472 if nConf, ok := container.NetworkSettings.Networks[mode.NetworkName()]; ok { 473 container.NetworkSettings.Networks[networkName] = nConf 474 delete(container.NetworkSettings.Networks, mode.NetworkName()) 475 } 476 } 477 478 if !mode.IsUserDefined() { 479 return 480 } 481 // Make sure to internally store the per network endpoint config by network name 482 if _, ok := container.NetworkSettings.Networks[networkName]; ok { 483 return 484 } 485 486 if n != nil { 487 if nwConfig, ok := container.NetworkSettings.Networks[n.ID()]; ok { 488 container.NetworkSettings.Networks[networkName] = nwConfig 489 delete(container.NetworkSettings.Networks, n.ID()) 490 return 491 } 492 } 493 } 494 495 func (daemon *Daemon) allocateNetwork(container *container.Container) error { 496 start := time.Now() 497 controller := daemon.netController 498 499 if daemon.netController == nil { 500 return nil 501 } 502 503 // Cleanup any stale sandbox left over due to ungraceful daemon shutdown 504 if err := controller.SandboxDestroy(container.ID); err != nil { 505 logrus.Errorf("failed to cleanup up stale network sandbox for container %s", container.ID) 506 } 507 508 if container.Config.NetworkDisabled || container.HostConfig.NetworkMode.IsContainer() { 509 return nil 510 } 511 512 updateSettings := false 513 514 if len(container.NetworkSettings.Networks) == 0 { 515 daemon.updateContainerNetworkSettings(container, nil) 516 updateSettings = true 517 } 518 519 // always connect default network first since only default 520 // network mode support link and we need do some setting 521 // on sandbox initialize for link, but the sandbox only be initialized 522 // on first network connecting. 523 defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName() 524 if nConf, ok := container.NetworkSettings.Networks[defaultNetName]; ok { 525 cleanOperationalData(nConf) 526 if err := daemon.connectToNetwork(container, defaultNetName, nConf.EndpointSettings, updateSettings); err != nil { 527 return err 528 } 529 530 } 531 532 // the intermediate map is necessary because "connectToNetwork" modifies "container.NetworkSettings.Networks" 533 networks := make(map[string]*network.EndpointSettings) 534 for n, epConf := range container.NetworkSettings.Networks { 535 if n == defaultNetName { 536 continue 537 } 538 539 networks[n] = epConf 540 } 541 542 for netName, epConf := range networks { 543 cleanOperationalData(epConf) 544 if err := daemon.connectToNetwork(container, netName, epConf.EndpointSettings, updateSettings); err != nil { 545 return err 546 } 547 } 548 549 // If the container is not to be connected to any network, 550 // create its network sandbox now if not present 551 if len(networks) == 0 { 552 if nil == daemon.getNetworkSandbox(container) { 553 options, err := daemon.buildSandboxOptions(container) 554 if err != nil { 555 return err 556 } 557 sb, err := daemon.netController.NewSandbox(container.ID, options...) 558 if err != nil { 559 return err 560 } 561 container.UpdateSandboxNetworkSettings(sb) 562 defer func() { 563 if err != nil { 564 sb.Delete() 565 } 566 }() 567 } 568 569 } 570 571 if _, err := container.WriteHostConfig(); err != nil { 572 return err 573 } 574 networkActions.WithValues("allocate").UpdateSince(start) 575 return nil 576 } 577 578 func (daemon *Daemon) getNetworkSandbox(container *container.Container) libnetwork.Sandbox { 579 var sb libnetwork.Sandbox 580 daemon.netController.WalkSandboxes(func(s libnetwork.Sandbox) bool { 581 if s.ContainerID() == container.ID { 582 sb = s 583 return true 584 } 585 return false 586 }) 587 return sb 588 } 589 590 // hasUserDefinedIPAddress returns whether the passed endpoint configuration contains IP address configuration 591 func hasUserDefinedIPAddress(epConfig *networktypes.EndpointSettings) bool { 592 return epConfig != nil && epConfig.IPAMConfig != nil && (len(epConfig.IPAMConfig.IPv4Address) > 0 || len(epConfig.IPAMConfig.IPv6Address) > 0) 593 } 594 595 // User specified ip address is acceptable only for networks with user specified subnets. 596 func validateNetworkingConfig(n libnetwork.Network, epConfig *networktypes.EndpointSettings) error { 597 if n == nil || epConfig == nil { 598 return nil 599 } 600 if !hasUserDefinedIPAddress(epConfig) { 601 return nil 602 } 603 _, _, nwIPv4Configs, nwIPv6Configs := n.Info().IpamConfig() 604 for _, s := range []struct { 605 ipConfigured bool 606 subnetConfigs []*libnetwork.IpamConf 607 }{ 608 { 609 ipConfigured: len(epConfig.IPAMConfig.IPv4Address) > 0, 610 subnetConfigs: nwIPv4Configs, 611 }, 612 { 613 ipConfigured: len(epConfig.IPAMConfig.IPv6Address) > 0, 614 subnetConfigs: nwIPv6Configs, 615 }, 616 } { 617 if s.ipConfigured { 618 foundSubnet := false 619 for _, cfg := range s.subnetConfigs { 620 if len(cfg.PreferredPool) > 0 { 621 foundSubnet = true 622 break 623 } 624 } 625 if !foundSubnet { 626 return runconfig.ErrUnsupportedNetworkNoSubnetAndIP 627 } 628 } 629 } 630 631 return nil 632 } 633 634 // cleanOperationalData resets the operational data from the passed endpoint settings 635 func cleanOperationalData(es *network.EndpointSettings) { 636 es.EndpointID = "" 637 es.Gateway = "" 638 es.IPAddress = "" 639 es.IPPrefixLen = 0 640 es.IPv6Gateway = "" 641 es.GlobalIPv6Address = "" 642 es.GlobalIPv6PrefixLen = 0 643 es.MacAddress = "" 644 if es.IPAMOperational { 645 es.IPAMConfig = nil 646 } 647 } 648 649 func (daemon *Daemon) updateNetworkConfig(container *container.Container, n libnetwork.Network, endpointConfig *networktypes.EndpointSettings, updateSettings bool) error { 650 651 if !containertypes.NetworkMode(n.Name()).IsUserDefined() { 652 if hasUserDefinedIPAddress(endpointConfig) && !enableIPOnPredefinedNetwork() { 653 return runconfig.ErrUnsupportedNetworkAndIP 654 } 655 if endpointConfig != nil && len(endpointConfig.Aliases) > 0 && !container.EnableServiceDiscoveryOnDefaultNetwork() { 656 return runconfig.ErrUnsupportedNetworkAndAlias 657 } 658 } else { 659 addShortID := true 660 shortID := stringid.TruncateID(container.ID) 661 for _, alias := range endpointConfig.Aliases { 662 if alias == shortID { 663 addShortID = false 664 break 665 } 666 } 667 if addShortID { 668 endpointConfig.Aliases = append(endpointConfig.Aliases, shortID) 669 } 670 } 671 672 if err := validateNetworkingConfig(n, endpointConfig); err != nil { 673 return err 674 } 675 676 if updateSettings { 677 if err := daemon.updateNetworkSettings(container, n, endpointConfig); err != nil { 678 return err 679 } 680 } 681 return nil 682 } 683 684 func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings, updateSettings bool) (err error) { 685 start := time.Now() 686 if container.HostConfig.NetworkMode.IsContainer() { 687 return runconfig.ErrConflictSharedNetwork 688 } 689 if containertypes.NetworkMode(idOrName).IsBridge() && 690 daemon.configStore.DisableBridge { 691 container.Config.NetworkDisabled = true 692 return nil 693 } 694 if endpointConfig == nil { 695 endpointConfig = &networktypes.EndpointSettings{} 696 } 697 698 n, config, err := daemon.findAndAttachNetwork(container, idOrName, endpointConfig) 699 if err != nil { 700 return err 701 } 702 if n == nil { 703 return nil 704 } 705 706 var operIPAM bool 707 if config != nil { 708 if epConfig, ok := config.EndpointsConfig[n.Name()]; ok { 709 if endpointConfig.IPAMConfig == nil || 710 (endpointConfig.IPAMConfig.IPv4Address == "" && 711 endpointConfig.IPAMConfig.IPv6Address == "" && 712 len(endpointConfig.IPAMConfig.LinkLocalIPs) == 0) { 713 operIPAM = true 714 } 715 716 // copy IPAMConfig and NetworkID from epConfig via AttachNetwork 717 endpointConfig.IPAMConfig = epConfig.IPAMConfig 718 endpointConfig.NetworkID = epConfig.NetworkID 719 } 720 } 721 722 err = daemon.updateNetworkConfig(container, n, endpointConfig, updateSettings) 723 if err != nil { 724 return err 725 } 726 727 controller := daemon.netController 728 sb := daemon.getNetworkSandbox(container) 729 createOptions, err := container.BuildCreateEndpointOptions(n, endpointConfig, sb, daemon.configStore.DNS) 730 if err != nil { 731 return err 732 } 733 734 endpointName := strings.TrimPrefix(container.Name, "/") 735 ep, err := n.CreateEndpoint(endpointName, createOptions...) 736 if err != nil { 737 return err 738 } 739 defer func() { 740 if err != nil { 741 if e := ep.Delete(false); e != nil { 742 logrus.Warnf("Could not rollback container connection to network %s", idOrName) 743 } 744 } 745 }() 746 container.NetworkSettings.Networks[n.Name()] = &network.EndpointSettings{ 747 EndpointSettings: endpointConfig, 748 IPAMOperational: operIPAM, 749 } 750 if _, ok := container.NetworkSettings.Networks[n.ID()]; ok { 751 delete(container.NetworkSettings.Networks, n.ID()) 752 } 753 754 if err := daemon.updateEndpointNetworkSettings(container, n, ep); err != nil { 755 return err 756 } 757 758 if sb == nil { 759 options, err := daemon.buildSandboxOptions(container) 760 if err != nil { 761 return err 762 } 763 sb, err = controller.NewSandbox(container.ID, options...) 764 if err != nil { 765 return err 766 } 767 768 container.UpdateSandboxNetworkSettings(sb) 769 } 770 771 joinOptions, err := container.BuildJoinOptions(n) 772 if err != nil { 773 return err 774 } 775 776 if err := ep.Join(sb, joinOptions...); err != nil { 777 return err 778 } 779 780 if !container.Managed { 781 // add container name/alias to DNS 782 if err := daemon.ActivateContainerServiceBinding(container.Name); err != nil { 783 return fmt.Errorf("Activate container service binding for %s failed: %v", container.Name, err) 784 } 785 } 786 787 if err := container.UpdateJoinInfo(n, ep); err != nil { 788 return fmt.Errorf("Updating join info failed: %v", err) 789 } 790 791 container.NetworkSettings.Ports = getPortMapInfo(sb) 792 793 daemon.LogNetworkEventWithAttributes(n, "connect", map[string]string{"container": container.ID}) 794 networkActions.WithValues("connect").UpdateSince(start) 795 return nil 796 } 797 798 // ForceEndpointDelete deletes an endpoint from a network forcefully 799 func (daemon *Daemon) ForceEndpointDelete(name string, networkName string) error { 800 n, err := daemon.FindNetwork(networkName) 801 if err != nil { 802 return err 803 } 804 805 ep, err := n.EndpointByName(name) 806 if err != nil { 807 return err 808 } 809 return ep.Delete(true) 810 } 811 812 func (daemon *Daemon) disconnectFromNetwork(container *container.Container, n libnetwork.Network, force bool) error { 813 var ( 814 ep libnetwork.Endpoint 815 sbox libnetwork.Sandbox 816 ) 817 818 s := func(current libnetwork.Endpoint) bool { 819 epInfo := current.Info() 820 if epInfo == nil { 821 return false 822 } 823 if sb := epInfo.Sandbox(); sb != nil { 824 if sb.ContainerID() == container.ID { 825 ep = current 826 sbox = sb 827 return true 828 } 829 } 830 return false 831 } 832 n.WalkEndpoints(s) 833 834 if ep == nil && force { 835 epName := strings.TrimPrefix(container.Name, "/") 836 ep, err := n.EndpointByName(epName) 837 if err != nil { 838 return err 839 } 840 return ep.Delete(force) 841 } 842 843 if ep == nil { 844 return fmt.Errorf("container %s is not connected to network %s", container.ID, n.Name()) 845 } 846 847 if err := ep.Leave(sbox); err != nil { 848 return fmt.Errorf("container %s failed to leave network %s: %v", container.ID, n.Name(), err) 849 } 850 851 container.NetworkSettings.Ports = getPortMapInfo(sbox) 852 853 if err := ep.Delete(false); err != nil { 854 return fmt.Errorf("endpoint delete failed for container %s on network %s: %v", container.ID, n.Name(), err) 855 } 856 857 delete(container.NetworkSettings.Networks, n.Name()) 858 859 daemon.tryDetachContainerFromClusterNetwork(n, container) 860 861 return nil 862 } 863 864 func (daemon *Daemon) tryDetachContainerFromClusterNetwork(network libnetwork.Network, container *container.Container) { 865 if daemon.clusterProvider != nil && network.Info().Dynamic() && !container.Managed { 866 if err := daemon.clusterProvider.DetachNetwork(network.Name(), container.ID); err != nil { 867 logrus.Warnf("error detaching from network %s: %v", network.Name(), err) 868 if err := daemon.clusterProvider.DetachNetwork(network.ID(), container.ID); err != nil { 869 logrus.Warnf("error detaching from network %s: %v", network.ID(), err) 870 } 871 } 872 } 873 attributes := map[string]string{ 874 "container": container.ID, 875 } 876 daemon.LogNetworkEventWithAttributes(network, "disconnect", attributes) 877 } 878 879 func (daemon *Daemon) initializeNetworking(container *container.Container) error { 880 var err error 881 882 if container.HostConfig.NetworkMode.IsContainer() { 883 // we need to get the hosts files from the container to join 884 nc, err := daemon.getNetworkedContainer(container.ID, container.HostConfig.NetworkMode.ConnectedContainer()) 885 if err != nil { 886 return err 887 } 888 889 err = daemon.initializeNetworkingPaths(container, nc) 890 if err != nil { 891 return err 892 } 893 894 container.Config.Hostname = nc.Config.Hostname 895 container.Config.Domainname = nc.Config.Domainname 896 return nil 897 } 898 899 if container.HostConfig.NetworkMode.IsHost() { 900 if container.Config.Hostname == "" { 901 container.Config.Hostname, err = os.Hostname() 902 if err != nil { 903 return err 904 } 905 } 906 } 907 908 if err := daemon.allocateNetwork(container); err != nil { 909 return err 910 } 911 912 return container.BuildHostnameFile() 913 } 914 915 func (daemon *Daemon) getNetworkedContainer(containerID, connectedContainerID string) (*container.Container, error) { 916 nc, err := daemon.GetContainer(connectedContainerID) 917 if err != nil { 918 return nil, err 919 } 920 if containerID == nc.ID { 921 return nil, fmt.Errorf("cannot join own network") 922 } 923 if !nc.IsRunning() { 924 err := fmt.Errorf("cannot join network of a non running container: %s", connectedContainerID) 925 return nil, stateConflictError{err} 926 } 927 if nc.IsRestarting() { 928 return nil, errContainerIsRestarting(connectedContainerID) 929 } 930 return nc, nil 931 } 932 933 func (daemon *Daemon) releaseNetwork(container *container.Container) { 934 start := time.Now() 935 if daemon.netController == nil { 936 return 937 } 938 if container.HostConfig.NetworkMode.IsContainer() || container.Config.NetworkDisabled { 939 return 940 } 941 942 sid := container.NetworkSettings.SandboxID 943 settings := container.NetworkSettings.Networks 944 container.NetworkSettings.Ports = nil 945 946 if sid == "" { 947 return 948 } 949 950 var networks []libnetwork.Network 951 for n, epSettings := range settings { 952 if nw, err := daemon.FindNetwork(n); err == nil { 953 networks = append(networks, nw) 954 } 955 956 if epSettings.EndpointSettings == nil { 957 continue 958 } 959 960 cleanOperationalData(epSettings) 961 } 962 963 sb, err := daemon.netController.SandboxByID(sid) 964 if err != nil { 965 logrus.Warnf("error locating sandbox id %s: %v", sid, err) 966 return 967 } 968 969 if err := sb.Delete(); err != nil { 970 logrus.Errorf("Error deleting sandbox id %s for container %s: %v", sid, container.ID, err) 971 } 972 973 for _, nw := range networks { 974 daemon.tryDetachContainerFromClusterNetwork(nw, container) 975 } 976 networkActions.WithValues("release").UpdateSince(start) 977 } 978 979 func errRemovalContainer(containerID string) error { 980 return fmt.Errorf("Container %s is marked for removal and cannot be connected or disconnected to the network", containerID) 981 } 982 983 // ConnectToNetwork connects a container to a network 984 func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings) error { 985 if endpointConfig == nil { 986 endpointConfig = &networktypes.EndpointSettings{} 987 } 988 container.Lock() 989 defer container.Unlock() 990 991 if !container.Running { 992 if container.RemovalInProgress || container.Dead { 993 return errRemovalContainer(container.ID) 994 } 995 996 n, err := daemon.FindNetwork(idOrName) 997 if err == nil && n != nil { 998 if err := daemon.updateNetworkConfig(container, n, endpointConfig, true); err != nil { 999 return err 1000 } 1001 } else { 1002 container.NetworkSettings.Networks[idOrName] = &network.EndpointSettings{ 1003 EndpointSettings: endpointConfig, 1004 } 1005 } 1006 } else if !daemon.isNetworkHotPluggable() { 1007 return fmt.Errorf(runtime.GOOS + " does not support connecting a running container to a network") 1008 } else { 1009 if err := daemon.connectToNetwork(container, idOrName, endpointConfig, true); err != nil { 1010 return err 1011 } 1012 } 1013 1014 return container.CheckpointTo(daemon.containersReplica) 1015 } 1016 1017 // DisconnectFromNetwork disconnects container from network n. 1018 func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, networkName string, force bool) error { 1019 n, err := daemon.FindNetwork(networkName) 1020 container.Lock() 1021 defer container.Unlock() 1022 1023 if !container.Running || (err != nil && force) { 1024 if container.RemovalInProgress || container.Dead { 1025 return errRemovalContainer(container.ID) 1026 } 1027 // In case networkName is resolved we will use n.Name() 1028 // this will cover the case where network id is passed. 1029 if n != nil { 1030 networkName = n.Name() 1031 } 1032 if _, ok := container.NetworkSettings.Networks[networkName]; !ok { 1033 return fmt.Errorf("container %s is not connected to the network %s", container.ID, networkName) 1034 } 1035 delete(container.NetworkSettings.Networks, networkName) 1036 } else if err == nil && !daemon.isNetworkHotPluggable() { 1037 return fmt.Errorf(runtime.GOOS + " does not support connecting a running container to a network") 1038 } else if err == nil { 1039 if container.HostConfig.NetworkMode.IsHost() && containertypes.NetworkMode(n.Type()).IsHost() { 1040 return runconfig.ErrConflictHostNetwork 1041 } 1042 1043 if err := daemon.disconnectFromNetwork(container, n, false); err != nil { 1044 return err 1045 } 1046 } else { 1047 return err 1048 } 1049 1050 if err := container.CheckpointTo(daemon.containersReplica); err != nil { 1051 return err 1052 } 1053 1054 if n != nil { 1055 daemon.LogNetworkEventWithAttributes(n, "disconnect", map[string]string{ 1056 "container": container.ID, 1057 }) 1058 } 1059 1060 return nil 1061 } 1062 1063 // ActivateContainerServiceBinding puts this container into load balancer active rotation and DNS response 1064 func (daemon *Daemon) ActivateContainerServiceBinding(containerName string) error { 1065 container, err := daemon.GetContainer(containerName) 1066 if err != nil { 1067 return err 1068 } 1069 sb := daemon.getNetworkSandbox(container) 1070 if sb == nil { 1071 return fmt.Errorf("network sandbox does not exist for container %s", containerName) 1072 } 1073 return sb.EnableService() 1074 } 1075 1076 // DeactivateContainerServiceBinding removes this container from load balancer active rotation, and DNS response 1077 func (daemon *Daemon) DeactivateContainerServiceBinding(containerName string) error { 1078 container, err := daemon.GetContainer(containerName) 1079 if err != nil { 1080 return err 1081 } 1082 sb := daemon.getNetworkSandbox(container) 1083 if sb == nil { 1084 // If the network sandbox is not found, then there is nothing to deactivate 1085 logrus.Debugf("Could not find network sandbox for container %s on service binding deactivation request", containerName) 1086 return nil 1087 } 1088 return sb.DisableService() 1089 }