github.com/ojongerius/docker@v1.11.2/container/container.go (about) 1 package container 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "net" 8 "os" 9 "path/filepath" 10 "strconv" 11 "strings" 12 "sync" 13 "syscall" 14 "time" 15 16 "golang.org/x/net/context" 17 18 "github.com/Sirupsen/logrus" 19 "github.com/docker/docker/daemon/exec" 20 "github.com/docker/docker/daemon/logger" 21 "github.com/docker/docker/daemon/logger/jsonfilelog" 22 "github.com/docker/docker/daemon/network" 23 "github.com/docker/docker/image" 24 "github.com/docker/docker/layer" 25 "github.com/docker/docker/pkg/idtools" 26 "github.com/docker/docker/pkg/promise" 27 "github.com/docker/docker/pkg/signal" 28 "github.com/docker/docker/pkg/symlink" 29 "github.com/docker/docker/restartmanager" 30 "github.com/docker/docker/runconfig" 31 runconfigopts "github.com/docker/docker/runconfig/opts" 32 "github.com/docker/docker/volume" 33 containertypes "github.com/docker/engine-api/types/container" 34 networktypes "github.com/docker/engine-api/types/network" 35 "github.com/docker/go-connections/nat" 36 "github.com/docker/libnetwork" 37 "github.com/docker/libnetwork/netlabel" 38 "github.com/docker/libnetwork/options" 39 "github.com/docker/libnetwork/types" 40 "github.com/opencontainers/runc/libcontainer/label" 41 ) 42 43 const configFileName = "config.v2.json" 44 45 var ( 46 errInvalidEndpoint = fmt.Errorf("invalid endpoint while building port map info") 47 errInvalidNetwork = fmt.Errorf("invalid network settings while building port map info") 48 ) 49 50 // CommonContainer holds the fields for a container which are 51 // applicable across all platforms supported by the daemon. 52 type CommonContainer struct { 53 *runconfig.StreamConfig 54 // embed for Container to support states directly. 55 *State `json:"State"` // Needed for remote api version <= 1.11 56 Root string `json:"-"` // Path to the "home" of the container, including metadata. 57 BaseFS string `json:"-"` // Path to the graphdriver mountpoint 58 RWLayer layer.RWLayer `json:"-"` 59 ID string 60 Created time.Time 61 Path string 62 Args []string 63 Config *containertypes.Config 64 ImageID image.ID `json:"Image"` 65 NetworkSettings *network.Settings 66 LogPath string 67 Name string 68 Driver string 69 // MountLabel contains the options for the 'mount' command 70 MountLabel string 71 ProcessLabel string 72 RestartCount int 73 HasBeenStartedBefore bool 74 HasBeenManuallyStopped bool // used for unless-stopped restart policy 75 MountPoints map[string]*volume.MountPoint 76 HostConfig *containertypes.HostConfig `json:"-"` // do not serialize the host config in the json, otherwise we'll make the container unportable 77 ExecCommands *exec.Store `json:"-"` 78 // logDriver for closing 79 LogDriver logger.Logger `json:"-"` 80 LogCopier *logger.Copier `json:"-"` 81 restartManager restartmanager.RestartManager 82 attachContext *attachContext 83 } 84 85 // NewBaseContainer creates a new container with its 86 // basic configuration. 87 func NewBaseContainer(id, root string) *Container { 88 return &Container{ 89 CommonContainer: CommonContainer{ 90 ID: id, 91 State: NewState(), 92 ExecCommands: exec.NewStore(), 93 Root: root, 94 MountPoints: make(map[string]*volume.MountPoint), 95 StreamConfig: runconfig.NewStreamConfig(), 96 attachContext: &attachContext{}, 97 }, 98 } 99 } 100 101 // FromDisk loads the container configuration stored in the host. 102 func (container *Container) FromDisk() error { 103 pth, err := container.ConfigPath() 104 if err != nil { 105 return err 106 } 107 108 jsonSource, err := os.Open(pth) 109 if err != nil { 110 return err 111 } 112 defer jsonSource.Close() 113 114 dec := json.NewDecoder(jsonSource) 115 116 // Load container settings 117 if err := dec.Decode(container); err != nil { 118 return err 119 } 120 121 if err := label.ReserveLabel(container.ProcessLabel); err != nil { 122 return err 123 } 124 return container.readHostConfig() 125 } 126 127 // ToDisk saves the container configuration on disk. 128 func (container *Container) ToDisk() error { 129 pth, err := container.ConfigPath() 130 if err != nil { 131 return err 132 } 133 134 jsonSource, err := os.Create(pth) 135 if err != nil { 136 return err 137 } 138 defer jsonSource.Close() 139 140 enc := json.NewEncoder(jsonSource) 141 142 // Save container settings 143 if err := enc.Encode(container); err != nil { 144 return err 145 } 146 147 return container.WriteHostConfig() 148 } 149 150 // ToDiskLocking saves the container configuration on disk in a thread safe way. 151 func (container *Container) ToDiskLocking() error { 152 container.Lock() 153 err := container.ToDisk() 154 container.Unlock() 155 return err 156 } 157 158 // readHostConfig reads the host configuration from disk for the container. 159 func (container *Container) readHostConfig() error { 160 container.HostConfig = &containertypes.HostConfig{} 161 // If the hostconfig file does not exist, do not read it. 162 // (We still have to initialize container.HostConfig, 163 // but that's OK, since we just did that above.) 164 pth, err := container.HostConfigPath() 165 if err != nil { 166 return err 167 } 168 169 f, err := os.Open(pth) 170 if err != nil { 171 if os.IsNotExist(err) { 172 return nil 173 } 174 return err 175 } 176 defer f.Close() 177 178 if err := json.NewDecoder(f).Decode(&container.HostConfig); err != nil { 179 return err 180 } 181 182 container.InitDNSHostConfig() 183 184 return nil 185 } 186 187 // WriteHostConfig saves the host configuration on disk for the container. 188 func (container *Container) WriteHostConfig() error { 189 pth, err := container.HostConfigPath() 190 if err != nil { 191 return err 192 } 193 194 f, err := os.Create(pth) 195 if err != nil { 196 return err 197 } 198 defer f.Close() 199 200 return json.NewEncoder(f).Encode(&container.HostConfig) 201 } 202 203 // SetupWorkingDirectory sets up the container's working directory as set in container.Config.WorkingDir 204 func (container *Container) SetupWorkingDirectory(rootUID, rootGID int) error { 205 if container.Config.WorkingDir == "" { 206 return nil 207 } 208 209 // If can't mount container FS at this point (eg Hyper-V Containers on 210 // Windows) bail out now with no action. 211 if !container.canMountFS() { 212 return nil 213 } 214 215 container.Config.WorkingDir = filepath.Clean(container.Config.WorkingDir) 216 217 pth, err := container.GetResourcePath(container.Config.WorkingDir) 218 if err != nil { 219 return err 220 } 221 222 if err := idtools.MkdirAllNewAs(pth, 0755, rootUID, rootGID); err != nil { 223 pthInfo, err2 := os.Stat(pth) 224 if err2 == nil && pthInfo != nil && !pthInfo.IsDir() { 225 return fmt.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir) 226 } 227 228 return err 229 } 230 231 return nil 232 } 233 234 // GetResourcePath evaluates `path` in the scope of the container's BaseFS, with proper path 235 // sanitisation. Symlinks are all scoped to the BaseFS of the container, as 236 // though the container's BaseFS was `/`. 237 // 238 // The BaseFS of a container is the host-facing path which is bind-mounted as 239 // `/` inside the container. This method is essentially used to access a 240 // particular path inside the container as though you were a process in that 241 // container. 242 // 243 // NOTE: The returned path is *only* safely scoped inside the container's BaseFS 244 // if no component of the returned path changes (such as a component 245 // symlinking to a different path) between using this method and using the 246 // path. See symlink.FollowSymlinkInScope for more details. 247 func (container *Container) GetResourcePath(path string) (string, error) { 248 // IMPORTANT - These are paths on the OS where the daemon is running, hence 249 // any filepath operations must be done in an OS agnostic way. 250 251 cleanPath := cleanResourcePath(path) 252 r, e := symlink.FollowSymlinkInScope(filepath.Join(container.BaseFS, cleanPath), container.BaseFS) 253 return r, e 254 } 255 256 // GetRootResourcePath evaluates `path` in the scope of the container's root, with proper path 257 // sanitisation. Symlinks are all scoped to the root of the container, as 258 // though the container's root was `/`. 259 // 260 // The root of a container is the host-facing configuration metadata directory. 261 // Only use this method to safely access the container's `container.json` or 262 // other metadata files. If in doubt, use container.GetResourcePath. 263 // 264 // NOTE: The returned path is *only* safely scoped inside the container's root 265 // if no component of the returned path changes (such as a component 266 // symlinking to a different path) between using this method and using the 267 // path. See symlink.FollowSymlinkInScope for more details. 268 func (container *Container) GetRootResourcePath(path string) (string, error) { 269 // IMPORTANT - These are paths on the OS where the daemon is running, hence 270 // any filepath operations must be done in an OS agnostic way. 271 cleanPath := filepath.Join(string(os.PathSeparator), path) 272 return symlink.FollowSymlinkInScope(filepath.Join(container.Root, cleanPath), container.Root) 273 } 274 275 // ExitOnNext signals to the monitor that it should not restart the container 276 // after we send the kill signal. 277 func (container *Container) ExitOnNext() { 278 if container.restartManager != nil { 279 container.restartManager.Cancel() 280 } 281 } 282 283 // HostConfigPath returns the path to the container's JSON hostconfig 284 func (container *Container) HostConfigPath() (string, error) { 285 return container.GetRootResourcePath("hostconfig.json") 286 } 287 288 // ConfigPath returns the path to the container's JSON config 289 func (container *Container) ConfigPath() (string, error) { 290 return container.GetRootResourcePath(configFileName) 291 } 292 293 // StartLogger starts a new logger driver for the container. 294 func (container *Container) StartLogger(cfg containertypes.LogConfig) (logger.Logger, error) { 295 c, err := logger.GetLogDriver(cfg.Type) 296 if err != nil { 297 return nil, fmt.Errorf("Failed to get logging factory: %v", err) 298 } 299 ctx := logger.Context{ 300 Config: cfg.Config, 301 ContainerID: container.ID, 302 ContainerName: container.Name, 303 ContainerEntrypoint: container.Path, 304 ContainerArgs: container.Args, 305 ContainerImageID: container.ImageID.String(), 306 ContainerImageName: container.Config.Image, 307 ContainerCreated: container.Created, 308 ContainerEnv: container.Config.Env, 309 ContainerLabels: container.Config.Labels, 310 } 311 312 // Set logging file for "json-logger" 313 if cfg.Type == jsonfilelog.Name { 314 ctx.LogPath, err = container.GetRootResourcePath(fmt.Sprintf("%s-json.log", container.ID)) 315 if err != nil { 316 return nil, err 317 } 318 } 319 return c(ctx) 320 } 321 322 // GetProcessLabel returns the process label for the container. 323 func (container *Container) GetProcessLabel() string { 324 // even if we have a process label return "" if we are running 325 // in privileged mode 326 if container.HostConfig.Privileged { 327 return "" 328 } 329 return container.ProcessLabel 330 } 331 332 // GetMountLabel returns the mounting label for the container. 333 // This label is empty if the container is privileged. 334 func (container *Container) GetMountLabel() string { 335 if container.HostConfig.Privileged { 336 return "" 337 } 338 return container.MountLabel 339 } 340 341 // GetExecIDs returns the list of exec commands running on the container. 342 func (container *Container) GetExecIDs() []string { 343 return container.ExecCommands.List() 344 } 345 346 // Attach connects to the container's TTY, delegating to standard 347 // streams or websockets depending on the configuration. 348 func (container *Container) Attach(stdin io.ReadCloser, stdout io.Writer, stderr io.Writer, keys []byte) chan error { 349 ctx := container.InitAttachContext() 350 return AttachStreams(ctx, container.StreamConfig, container.Config.OpenStdin, container.Config.StdinOnce, container.Config.Tty, stdin, stdout, stderr, keys) 351 } 352 353 // AttachStreams connects streams to a TTY. 354 // Used by exec too. Should this move somewhere else? 355 func AttachStreams(ctx context.Context, streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer, keys []byte) chan error { 356 var ( 357 cStdout, cStderr io.ReadCloser 358 cStdin io.WriteCloser 359 wg sync.WaitGroup 360 errors = make(chan error, 3) 361 ) 362 363 if stdin != nil && openStdin { 364 cStdin = streamConfig.StdinPipe() 365 wg.Add(1) 366 } 367 368 if stdout != nil { 369 cStdout = streamConfig.StdoutPipe() 370 wg.Add(1) 371 } 372 373 if stderr != nil { 374 cStderr = streamConfig.StderrPipe() 375 wg.Add(1) 376 } 377 378 // Connect stdin of container to the http conn. 379 go func() { 380 if stdin == nil || !openStdin { 381 return 382 } 383 logrus.Debugf("attach: stdin: begin") 384 385 var err error 386 if tty { 387 _, err = copyEscapable(cStdin, stdin, keys) 388 } else { 389 _, err = io.Copy(cStdin, stdin) 390 391 } 392 if err == io.ErrClosedPipe { 393 err = nil 394 } 395 if err != nil { 396 logrus.Errorf("attach: stdin: %s", err) 397 errors <- err 398 } 399 if stdinOnce && !tty { 400 cStdin.Close() 401 } else { 402 // No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr 403 if cStdout != nil { 404 cStdout.Close() 405 } 406 if cStderr != nil { 407 cStderr.Close() 408 } 409 } 410 logrus.Debugf("attach: stdin: end") 411 wg.Done() 412 }() 413 414 attachStream := func(name string, stream io.Writer, streamPipe io.ReadCloser) { 415 if stream == nil { 416 return 417 } 418 419 logrus.Debugf("attach: %s: begin", name) 420 _, err := io.Copy(stream, streamPipe) 421 if err == io.ErrClosedPipe { 422 err = nil 423 } 424 if err != nil { 425 logrus.Errorf("attach: %s: %v", name, err) 426 errors <- err 427 } 428 // Make sure stdin gets closed 429 if stdin != nil { 430 stdin.Close() 431 } 432 streamPipe.Close() 433 logrus.Debugf("attach: %s: end", name) 434 wg.Done() 435 } 436 437 go attachStream("stdout", stdout, cStdout) 438 go attachStream("stderr", stderr, cStderr) 439 440 return promise.Go(func() error { 441 done := make(chan struct{}) 442 go func() { 443 wg.Wait() 444 close(done) 445 }() 446 select { 447 case <-done: 448 case <-ctx.Done(): 449 // close all pipes 450 if cStdin != nil { 451 cStdin.Close() 452 } 453 if cStdout != nil { 454 cStdout.Close() 455 } 456 if cStderr != nil { 457 cStderr.Close() 458 } 459 <-done 460 } 461 close(errors) 462 for err := range errors { 463 if err != nil { 464 return err 465 } 466 } 467 return nil 468 }) 469 } 470 471 // Code c/c from io.Copy() modified to handle escape sequence 472 func copyEscapable(dst io.Writer, src io.ReadCloser, keys []byte) (written int64, err error) { 473 if len(keys) == 0 { 474 // Default keys : ctrl-p ctrl-q 475 keys = []byte{16, 17} 476 } 477 buf := make([]byte, 32*1024) 478 for { 479 nr, er := src.Read(buf) 480 if nr > 0 { 481 // ---- Docker addition 482 for i, key := range keys { 483 if nr != 1 || buf[0] != key { 484 break 485 } 486 if i == len(keys)-1 { 487 if err := src.Close(); err != nil { 488 return 0, err 489 } 490 return 0, nil 491 } 492 nr, er = src.Read(buf) 493 } 494 // ---- End of docker 495 nw, ew := dst.Write(buf[0:nr]) 496 if nw > 0 { 497 written += int64(nw) 498 } 499 if ew != nil { 500 err = ew 501 break 502 } 503 if nr != nw { 504 err = io.ErrShortWrite 505 break 506 } 507 } 508 if er == io.EOF { 509 break 510 } 511 if er != nil { 512 err = er 513 break 514 } 515 } 516 return written, err 517 } 518 519 // ShouldRestartOnBoot decides whether the daemon should restart the container or not. 520 // This is based on the container's restart policy. 521 func (container *Container) ShouldRestartOnBoot() bool { 522 return container.HostConfig.RestartPolicy.Name == "always" || 523 (container.HostConfig.RestartPolicy.Name == "unless-stopped" && !container.HasBeenManuallyStopped) || 524 (container.HostConfig.RestartPolicy.Name == "on-failure" && container.ExitCode != 0) 525 } 526 527 // AddBindMountPoint adds a new bind mount point configuration to the container. 528 func (container *Container) AddBindMountPoint(name, source, destination string, rw bool) { 529 container.MountPoints[destination] = &volume.MountPoint{ 530 Name: name, 531 Source: source, 532 Destination: destination, 533 RW: rw, 534 } 535 } 536 537 // AddLocalMountPoint adds a new local mount point configuration to the container. 538 func (container *Container) AddLocalMountPoint(name, destination string, rw bool) { 539 container.MountPoints[destination] = &volume.MountPoint{ 540 Name: name, 541 Driver: volume.DefaultDriverName, 542 Destination: destination, 543 RW: rw, 544 } 545 } 546 547 // AddMountPointWithVolume adds a new mount point configured with a volume to the container. 548 func (container *Container) AddMountPointWithVolume(destination string, vol volume.Volume, rw bool) { 549 container.MountPoints[destination] = &volume.MountPoint{ 550 Name: vol.Name(), 551 Driver: vol.DriverName(), 552 Destination: destination, 553 RW: rw, 554 Volume: vol, 555 CopyData: volume.DefaultCopyMode, 556 } 557 } 558 559 // IsDestinationMounted checks whether a path is mounted on the container or not. 560 func (container *Container) IsDestinationMounted(destination string) bool { 561 return container.MountPoints[destination] != nil 562 } 563 564 // StopSignal returns the signal used to stop the container. 565 func (container *Container) StopSignal() int { 566 var stopSignal syscall.Signal 567 if container.Config.StopSignal != "" { 568 stopSignal, _ = signal.ParseSignal(container.Config.StopSignal) 569 } 570 571 if int(stopSignal) == 0 { 572 stopSignal, _ = signal.ParseSignal(signal.DefaultStopSignal) 573 } 574 return int(stopSignal) 575 } 576 577 // InitDNSHostConfig ensures that the dns fields are never nil. 578 // New containers don't ever have those fields nil, 579 // but pre created containers can still have those nil values. 580 // The non-recommended host configuration in the start api can 581 // make these fields nil again, this corrects that issue until 582 // we remove that behavior for good. 583 // See https://github.com/docker/docker/pull/17779 584 // for a more detailed explanation on why we don't want that. 585 func (container *Container) InitDNSHostConfig() { 586 container.Lock() 587 defer container.Unlock() 588 if container.HostConfig.DNS == nil { 589 container.HostConfig.DNS = make([]string, 0) 590 } 591 592 if container.HostConfig.DNSSearch == nil { 593 container.HostConfig.DNSSearch = make([]string, 0) 594 } 595 596 if container.HostConfig.DNSOptions == nil { 597 container.HostConfig.DNSOptions = make([]string, 0) 598 } 599 } 600 601 // GetEndpointInNetwork returns the container's endpoint to the provided network. 602 func (container *Container) GetEndpointInNetwork(n libnetwork.Network) (libnetwork.Endpoint, error) { 603 endpointName := strings.TrimPrefix(container.Name, "/") 604 return n.EndpointByName(endpointName) 605 } 606 607 func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint) error { 608 if ep == nil { 609 return errInvalidEndpoint 610 } 611 612 networkSettings := container.NetworkSettings 613 if networkSettings == nil { 614 return errInvalidNetwork 615 } 616 617 if len(networkSettings.Ports) == 0 { 618 pm, err := getEndpointPortMapInfo(ep) 619 if err != nil { 620 return err 621 } 622 networkSettings.Ports = pm 623 } 624 return nil 625 } 626 627 func getEndpointPortMapInfo(ep libnetwork.Endpoint) (nat.PortMap, error) { 628 pm := nat.PortMap{} 629 driverInfo, err := ep.DriverInfo() 630 if err != nil { 631 return pm, err 632 } 633 634 if driverInfo == nil { 635 // It is not an error for epInfo to be nil 636 return pm, nil 637 } 638 639 if expData, ok := driverInfo[netlabel.ExposedPorts]; ok { 640 if exposedPorts, ok := expData.([]types.TransportPort); ok { 641 for _, tp := range exposedPorts { 642 natPort, err := nat.NewPort(tp.Proto.String(), strconv.Itoa(int(tp.Port))) 643 if err != nil { 644 return pm, fmt.Errorf("Error parsing Port value(%v):%v", tp.Port, err) 645 } 646 pm[natPort] = nil 647 } 648 } 649 } 650 651 mapData, ok := driverInfo[netlabel.PortMap] 652 if !ok { 653 return pm, nil 654 } 655 656 if portMapping, ok := mapData.([]types.PortBinding); ok { 657 for _, pp := range portMapping { 658 natPort, err := nat.NewPort(pp.Proto.String(), strconv.Itoa(int(pp.Port))) 659 if err != nil { 660 return pm, err 661 } 662 natBndg := nat.PortBinding{HostIP: pp.HostIP.String(), HostPort: strconv.Itoa(int(pp.HostPort))} 663 pm[natPort] = append(pm[natPort], natBndg) 664 } 665 } 666 667 return pm, nil 668 } 669 670 // GetSandboxPortMapInfo retrieves the current port-mapping programmed for the given sandbox 671 func GetSandboxPortMapInfo(sb libnetwork.Sandbox) nat.PortMap { 672 pm := nat.PortMap{} 673 if sb == nil { 674 return pm 675 } 676 677 for _, ep := range sb.Endpoints() { 678 pm, _ = getEndpointPortMapInfo(ep) 679 if len(pm) > 0 { 680 break 681 } 682 } 683 return pm 684 } 685 686 // BuildEndpointInfo sets endpoint-related fields on container.NetworkSettings based on the provided network and endpoint. 687 func (container *Container) BuildEndpointInfo(n libnetwork.Network, ep libnetwork.Endpoint) error { 688 if ep == nil { 689 return errInvalidEndpoint 690 } 691 692 networkSettings := container.NetworkSettings 693 if networkSettings == nil { 694 return errInvalidNetwork 695 } 696 697 epInfo := ep.Info() 698 if epInfo == nil { 699 // It is not an error to get an empty endpoint info 700 return nil 701 } 702 703 if _, ok := networkSettings.Networks[n.Name()]; !ok { 704 networkSettings.Networks[n.Name()] = new(networktypes.EndpointSettings) 705 } 706 networkSettings.Networks[n.Name()].NetworkID = n.ID() 707 networkSettings.Networks[n.Name()].EndpointID = ep.ID() 708 709 iface := epInfo.Iface() 710 if iface == nil { 711 return nil 712 } 713 714 if iface.MacAddress() != nil { 715 networkSettings.Networks[n.Name()].MacAddress = iface.MacAddress().String() 716 } 717 718 if iface.Address() != nil { 719 ones, _ := iface.Address().Mask.Size() 720 networkSettings.Networks[n.Name()].IPAddress = iface.Address().IP.String() 721 networkSettings.Networks[n.Name()].IPPrefixLen = ones 722 } 723 724 if iface.AddressIPv6() != nil && iface.AddressIPv6().IP.To16() != nil { 725 onesv6, _ := iface.AddressIPv6().Mask.Size() 726 networkSettings.Networks[n.Name()].GlobalIPv6Address = iface.AddressIPv6().IP.String() 727 networkSettings.Networks[n.Name()].GlobalIPv6PrefixLen = onesv6 728 } 729 730 return nil 731 } 732 733 // UpdateJoinInfo updates network settings when container joins network n with endpoint ep. 734 func (container *Container) UpdateJoinInfo(n libnetwork.Network, ep libnetwork.Endpoint) error { 735 if err := container.buildPortMapInfo(ep); err != nil { 736 return err 737 } 738 739 epInfo := ep.Info() 740 if epInfo == nil { 741 // It is not an error to get an empty endpoint info 742 return nil 743 } 744 if epInfo.Gateway() != nil { 745 container.NetworkSettings.Networks[n.Name()].Gateway = epInfo.Gateway().String() 746 } 747 if epInfo.GatewayIPv6().To16() != nil { 748 container.NetworkSettings.Networks[n.Name()].IPv6Gateway = epInfo.GatewayIPv6().String() 749 } 750 751 return nil 752 } 753 754 // UpdateSandboxNetworkSettings updates the sandbox ID and Key. 755 func (container *Container) UpdateSandboxNetworkSettings(sb libnetwork.Sandbox) error { 756 container.NetworkSettings.SandboxID = sb.ID() 757 container.NetworkSettings.SandboxKey = sb.Key() 758 return nil 759 } 760 761 // BuildJoinOptions builds endpoint Join options from a given network. 762 func (container *Container) BuildJoinOptions(n libnetwork.Network) ([]libnetwork.EndpointOption, error) { 763 var joinOptions []libnetwork.EndpointOption 764 if epConfig, ok := container.NetworkSettings.Networks[n.Name()]; ok { 765 for _, str := range epConfig.Links { 766 name, alias, err := runconfigopts.ParseLink(str) 767 if err != nil { 768 return nil, err 769 } 770 joinOptions = append(joinOptions, libnetwork.CreateOptionAlias(name, alias)) 771 } 772 } 773 return joinOptions, nil 774 } 775 776 // BuildCreateEndpointOptions builds endpoint options from a given network. 777 func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network, epConfig *networktypes.EndpointSettings, sb libnetwork.Sandbox) ([]libnetwork.EndpointOption, error) { 778 var ( 779 bindings = make(nat.PortMap) 780 pbList []types.PortBinding 781 exposeList []types.TransportPort 782 createOptions []libnetwork.EndpointOption 783 ) 784 785 defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName() 786 787 if n.Name() == defaultNetName || container.NetworkSettings.IsAnonymousEndpoint { 788 createOptions = append(createOptions, libnetwork.CreateOptionAnonymous()) 789 } 790 791 if epConfig != nil { 792 ipam := epConfig.IPAMConfig 793 if ipam != nil && (ipam.IPv4Address != "" || ipam.IPv6Address != "") { 794 createOptions = append(createOptions, 795 libnetwork.CreateOptionIpam(net.ParseIP(ipam.IPv4Address), net.ParseIP(ipam.IPv6Address), nil)) 796 } 797 798 for _, alias := range epConfig.Aliases { 799 createOptions = append(createOptions, libnetwork.CreateOptionMyAlias(alias)) 800 } 801 } 802 803 if !containertypes.NetworkMode(n.Name()).IsUserDefined() { 804 createOptions = append(createOptions, libnetwork.CreateOptionDisableResolution()) 805 } 806 807 // configs that are applicable only for the endpoint in the network 808 // to which container was connected to on docker run. 809 // Ideally all these network-specific endpoint configurations must be moved under 810 // container.NetworkSettings.Networks[n.Name()] 811 if n.Name() == container.HostConfig.NetworkMode.NetworkName() || 812 (n.Name() == defaultNetName && container.HostConfig.NetworkMode.IsDefault()) { 813 if container.Config.MacAddress != "" { 814 mac, err := net.ParseMAC(container.Config.MacAddress) 815 if err != nil { 816 return nil, err 817 } 818 819 genericOption := options.Generic{ 820 netlabel.MacAddress: mac, 821 } 822 823 createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(genericOption)) 824 } 825 } 826 827 // Port-mapping rules belong to the container & applicable only to non-internal networks 828 portmaps := GetSandboxPortMapInfo(sb) 829 if n.Info().Internal() || len(portmaps) > 0 { 830 return createOptions, nil 831 } 832 833 if container.HostConfig.PortBindings != nil { 834 for p, b := range container.HostConfig.PortBindings { 835 bindings[p] = []nat.PortBinding{} 836 for _, bb := range b { 837 bindings[p] = append(bindings[p], nat.PortBinding{ 838 HostIP: bb.HostIP, 839 HostPort: bb.HostPort, 840 }) 841 } 842 } 843 } 844 845 portSpecs := container.Config.ExposedPorts 846 ports := make([]nat.Port, len(portSpecs)) 847 var i int 848 for p := range portSpecs { 849 ports[i] = p 850 i++ 851 } 852 nat.SortPortMap(ports, bindings) 853 for _, port := range ports { 854 expose := types.TransportPort{} 855 expose.Proto = types.ParseProtocol(port.Proto()) 856 expose.Port = uint16(port.Int()) 857 exposeList = append(exposeList, expose) 858 859 pb := types.PortBinding{Port: expose.Port, Proto: expose.Proto} 860 binding := bindings[port] 861 for i := 0; i < len(binding); i++ { 862 pbCopy := pb.GetCopy() 863 newP, err := nat.NewPort(nat.SplitProtoPort(binding[i].HostPort)) 864 var portStart, portEnd int 865 if err == nil { 866 portStart, portEnd, err = newP.Range() 867 } 868 if err != nil { 869 return nil, fmt.Errorf("Error parsing HostPort value(%s):%v", binding[i].HostPort, err) 870 } 871 pbCopy.HostPort = uint16(portStart) 872 pbCopy.HostPortEnd = uint16(portEnd) 873 pbCopy.HostIP = net.ParseIP(binding[i].HostIP) 874 pbList = append(pbList, pbCopy) 875 } 876 877 if container.HostConfig.PublishAllPorts && len(binding) == 0 { 878 pbList = append(pbList, pb) 879 } 880 } 881 882 createOptions = append(createOptions, 883 libnetwork.CreateOptionPortMapping(pbList), 884 libnetwork.CreateOptionExposedPorts(exposeList)) 885 886 return createOptions, nil 887 } 888 889 // UpdateMonitor updates monitor configure for running container 890 func (container *Container) UpdateMonitor(restartPolicy containertypes.RestartPolicy) { 891 type policySetter interface { 892 SetPolicy(containertypes.RestartPolicy) 893 } 894 895 if rm, ok := container.RestartManager(false).(policySetter); ok { 896 rm.SetPolicy(restartPolicy) 897 } 898 } 899 900 // FullHostname returns hostname and optional domain appended to it. 901 func (container *Container) FullHostname() string { 902 fullHostname := container.Config.Hostname 903 if container.Config.Domainname != "" { 904 fullHostname = fmt.Sprintf("%s.%s", fullHostname, container.Config.Domainname) 905 } 906 return fullHostname 907 } 908 909 // RestartManager returns the current restartmanager instace connected to container. 910 func (container *Container) RestartManager(reset bool) restartmanager.RestartManager { 911 if reset { 912 container.RestartCount = 0 913 container.restartManager = nil 914 } 915 if container.restartManager == nil { 916 container.restartManager = restartmanager.New(container.HostConfig.RestartPolicy) 917 } 918 return container.restartManager 919 } 920 921 type attachContext struct { 922 ctx context.Context 923 cancel context.CancelFunc 924 mu sync.Mutex 925 } 926 927 // InitAttachContext initialize or returns existing context for attach calls to 928 // track container liveness. 929 func (container *Container) InitAttachContext() context.Context { 930 container.attachContext.mu.Lock() 931 defer container.attachContext.mu.Unlock() 932 if container.attachContext.ctx == nil { 933 container.attachContext.ctx, container.attachContext.cancel = context.WithCancel(context.Background()) 934 } 935 return container.attachContext.ctx 936 } 937 938 // CancelAttachContext cancel attach context. All attach calls should detach 939 // after this call. 940 func (container *Container) CancelAttachContext() { 941 container.attachContext.mu.Lock() 942 if container.attachContext.ctx != nil { 943 container.attachContext.cancel() 944 container.attachContext.ctx = nil 945 } 946 container.attachContext.mu.Unlock() 947 }