github.com/akerouanton/docker@v1.11.0-rc3/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 // ShouldRestart decides whether the daemon should restart the container or not. 520 // This is based on the container's restart policy. 521 func (container *Container) ShouldRestart() 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 func getSandboxPortMapInfo(sb libnetwork.Sandbox) nat.PortMap { 671 pm := nat.PortMap{} 672 if sb == nil { 673 return pm 674 } 675 676 for _, ep := range sb.Endpoints() { 677 pm, _ = getEndpointPortMapInfo(ep) 678 if len(pm) > 0 { 679 break 680 } 681 } 682 return pm 683 } 684 685 // BuildEndpointInfo sets endpoint-related fields on container.NetworkSettings based on the provided network and endpoint. 686 func (container *Container) BuildEndpointInfo(n libnetwork.Network, ep libnetwork.Endpoint) error { 687 if ep == nil { 688 return errInvalidEndpoint 689 } 690 691 networkSettings := container.NetworkSettings 692 if networkSettings == nil { 693 return errInvalidNetwork 694 } 695 696 epInfo := ep.Info() 697 if epInfo == nil { 698 // It is not an error to get an empty endpoint info 699 return nil 700 } 701 702 if _, ok := networkSettings.Networks[n.Name()]; !ok { 703 networkSettings.Networks[n.Name()] = new(networktypes.EndpointSettings) 704 } 705 networkSettings.Networks[n.Name()].NetworkID = n.ID() 706 networkSettings.Networks[n.Name()].EndpointID = ep.ID() 707 708 iface := epInfo.Iface() 709 if iface == nil { 710 return nil 711 } 712 713 if iface.MacAddress() != nil { 714 networkSettings.Networks[n.Name()].MacAddress = iface.MacAddress().String() 715 } 716 717 if iface.Address() != nil { 718 ones, _ := iface.Address().Mask.Size() 719 networkSettings.Networks[n.Name()].IPAddress = iface.Address().IP.String() 720 networkSettings.Networks[n.Name()].IPPrefixLen = ones 721 } 722 723 if iface.AddressIPv6() != nil && iface.AddressIPv6().IP.To16() != nil { 724 onesv6, _ := iface.AddressIPv6().Mask.Size() 725 networkSettings.Networks[n.Name()].GlobalIPv6Address = iface.AddressIPv6().IP.String() 726 networkSettings.Networks[n.Name()].GlobalIPv6PrefixLen = onesv6 727 } 728 729 return nil 730 } 731 732 // UpdateJoinInfo updates network settings when container joins network n with endpoint ep. 733 func (container *Container) UpdateJoinInfo(n libnetwork.Network, ep libnetwork.Endpoint) error { 734 if err := container.buildPortMapInfo(ep); err != nil { 735 return err 736 } 737 738 epInfo := ep.Info() 739 if epInfo == nil { 740 // It is not an error to get an empty endpoint info 741 return nil 742 } 743 if epInfo.Gateway() != nil { 744 container.NetworkSettings.Networks[n.Name()].Gateway = epInfo.Gateway().String() 745 } 746 if epInfo.GatewayIPv6().To16() != nil { 747 container.NetworkSettings.Networks[n.Name()].IPv6Gateway = epInfo.GatewayIPv6().String() 748 } 749 750 return nil 751 } 752 753 // UpdateSandboxNetworkSettings updates the sandbox ID and Key. 754 func (container *Container) UpdateSandboxNetworkSettings(sb libnetwork.Sandbox) error { 755 container.NetworkSettings.SandboxID = sb.ID() 756 container.NetworkSettings.SandboxKey = sb.Key() 757 return nil 758 } 759 760 // BuildJoinOptions builds endpoint Join options from a given network. 761 func (container *Container) BuildJoinOptions(n libnetwork.Network) ([]libnetwork.EndpointOption, error) { 762 var joinOptions []libnetwork.EndpointOption 763 if epConfig, ok := container.NetworkSettings.Networks[n.Name()]; ok { 764 for _, str := range epConfig.Links { 765 name, alias, err := runconfigopts.ParseLink(str) 766 if err != nil { 767 return nil, err 768 } 769 joinOptions = append(joinOptions, libnetwork.CreateOptionAlias(name, alias)) 770 } 771 } 772 return joinOptions, nil 773 } 774 775 // BuildCreateEndpointOptions builds endpoint options from a given network. 776 func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network, epConfig *networktypes.EndpointSettings, sb libnetwork.Sandbox) ([]libnetwork.EndpointOption, error) { 777 var ( 778 bindings = make(nat.PortMap) 779 pbList []types.PortBinding 780 exposeList []types.TransportPort 781 createOptions []libnetwork.EndpointOption 782 ) 783 784 defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName() 785 786 if n.Name() == defaultNetName || container.NetworkSettings.IsAnonymousEndpoint { 787 createOptions = append(createOptions, libnetwork.CreateOptionAnonymous()) 788 } 789 790 if epConfig != nil { 791 ipam := epConfig.IPAMConfig 792 if ipam != nil && (ipam.IPv4Address != "" || ipam.IPv6Address != "") { 793 createOptions = append(createOptions, 794 libnetwork.CreateOptionIpam(net.ParseIP(ipam.IPv4Address), net.ParseIP(ipam.IPv6Address), nil)) 795 } 796 797 for _, alias := range epConfig.Aliases { 798 createOptions = append(createOptions, libnetwork.CreateOptionMyAlias(alias)) 799 } 800 } 801 802 if !containertypes.NetworkMode(n.Name()).IsUserDefined() { 803 createOptions = append(createOptions, libnetwork.CreateOptionDisableResolution()) 804 } 805 806 // configs that are applicable only for the endpoint in the network 807 // to which container was connected to on docker run. 808 // Ideally all these network-specific endpoint configurations must be moved under 809 // container.NetworkSettings.Networks[n.Name()] 810 if n.Name() == container.HostConfig.NetworkMode.NetworkName() || 811 (n.Name() == defaultNetName && container.HostConfig.NetworkMode.IsDefault()) { 812 if container.Config.MacAddress != "" { 813 mac, err := net.ParseMAC(container.Config.MacAddress) 814 if err != nil { 815 return nil, err 816 } 817 818 genericOption := options.Generic{ 819 netlabel.MacAddress: mac, 820 } 821 822 createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(genericOption)) 823 } 824 } 825 826 // Port-mapping rules belong to the container & applicable only to non-internal networks 827 portmaps := getSandboxPortMapInfo(sb) 828 if n.Info().Internal() || len(portmaps) > 0 { 829 return createOptions, nil 830 } 831 832 if container.HostConfig.PortBindings != nil { 833 for p, b := range container.HostConfig.PortBindings { 834 bindings[p] = []nat.PortBinding{} 835 for _, bb := range b { 836 bindings[p] = append(bindings[p], nat.PortBinding{ 837 HostIP: bb.HostIP, 838 HostPort: bb.HostPort, 839 }) 840 } 841 } 842 } 843 844 portSpecs := container.Config.ExposedPorts 845 ports := make([]nat.Port, len(portSpecs)) 846 var i int 847 for p := range portSpecs { 848 ports[i] = p 849 i++ 850 } 851 nat.SortPortMap(ports, bindings) 852 for _, port := range ports { 853 expose := types.TransportPort{} 854 expose.Proto = types.ParseProtocol(port.Proto()) 855 expose.Port = uint16(port.Int()) 856 exposeList = append(exposeList, expose) 857 858 pb := types.PortBinding{Port: expose.Port, Proto: expose.Proto} 859 binding := bindings[port] 860 for i := 0; i < len(binding); i++ { 861 pbCopy := pb.GetCopy() 862 newP, err := nat.NewPort(nat.SplitProtoPort(binding[i].HostPort)) 863 var portStart, portEnd int 864 if err == nil { 865 portStart, portEnd, err = newP.Range() 866 } 867 if err != nil { 868 return nil, fmt.Errorf("Error parsing HostPort value(%s):%v", binding[i].HostPort, err) 869 } 870 pbCopy.HostPort = uint16(portStart) 871 pbCopy.HostPortEnd = uint16(portEnd) 872 pbCopy.HostIP = net.ParseIP(binding[i].HostIP) 873 pbList = append(pbList, pbCopy) 874 } 875 876 if container.HostConfig.PublishAllPorts && len(binding) == 0 { 877 pbList = append(pbList, pb) 878 } 879 } 880 881 createOptions = append(createOptions, 882 libnetwork.CreateOptionPortMapping(pbList), 883 libnetwork.CreateOptionExposedPorts(exposeList)) 884 885 return createOptions, nil 886 } 887 888 // UpdateMonitor updates monitor configure for running container 889 func (container *Container) UpdateMonitor(restartPolicy containertypes.RestartPolicy) { 890 type policySetter interface { 891 SetPolicy(containertypes.RestartPolicy) 892 } 893 894 if rm, ok := container.RestartManager(false).(policySetter); ok { 895 rm.SetPolicy(restartPolicy) 896 } 897 } 898 899 // FullHostname returns hostname and optional domain appended to it. 900 func (container *Container) FullHostname() string { 901 fullHostname := container.Config.Hostname 902 if container.Config.Domainname != "" { 903 fullHostname = fmt.Sprintf("%s.%s", fullHostname, container.Config.Domainname) 904 } 905 return fullHostname 906 } 907 908 // RestartManager returns the current restartmanager instace connected to container. 909 func (container *Container) RestartManager(reset bool) restartmanager.RestartManager { 910 if reset { 911 container.RestartCount = 0 912 } 913 if container.restartManager == nil { 914 container.restartManager = restartmanager.New(container.HostConfig.RestartPolicy) 915 } 916 return container.restartManager 917 } 918 919 type attachContext struct { 920 ctx context.Context 921 cancel context.CancelFunc 922 mu sync.Mutex 923 } 924 925 // InitAttachContext initialize or returns existing context for attach calls to 926 // track container liveness. 927 func (container *Container) InitAttachContext() context.Context { 928 container.attachContext.mu.Lock() 929 defer container.attachContext.mu.Unlock() 930 if container.attachContext.ctx == nil { 931 container.attachContext.ctx, container.attachContext.cancel = context.WithCancel(context.Background()) 932 } 933 return container.attachContext.ctx 934 } 935 936 // CancelAttachContext cancel attach context. All attach calls should detach 937 // after this call. 938 func (container *Container) CancelAttachContext() { 939 container.attachContext.mu.Lock() 940 if container.attachContext.ctx != nil { 941 container.attachContext.cancel() 942 container.attachContext.ctx = nil 943 } 944 container.attachContext.mu.Unlock() 945 }