github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/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 shouldRestart, _, _ := container.restartManager.ShouldRestart(uint32(container.ExitCode), container.HasBeenManuallyStopped) 523 return shouldRestart 524 } 525 526 // AddBindMountPoint adds a new bind mount point configuration to the container. 527 func (container *Container) AddBindMountPoint(name, source, destination string, rw bool) { 528 container.MountPoints[destination] = &volume.MountPoint{ 529 Name: name, 530 Source: source, 531 Destination: destination, 532 RW: rw, 533 } 534 } 535 536 // AddLocalMountPoint adds a new local mount point configuration to the container. 537 func (container *Container) AddLocalMountPoint(name, destination string, rw bool) { 538 container.MountPoints[destination] = &volume.MountPoint{ 539 Name: name, 540 Driver: volume.DefaultDriverName, 541 Destination: destination, 542 RW: rw, 543 } 544 } 545 546 // AddMountPointWithVolume adds a new mount point configured with a volume to the container. 547 func (container *Container) AddMountPointWithVolume(destination string, vol volume.Volume, rw bool) { 548 container.MountPoints[destination] = &volume.MountPoint{ 549 Name: vol.Name(), 550 Driver: vol.DriverName(), 551 Destination: destination, 552 RW: rw, 553 Volume: vol, 554 CopyData: volume.DefaultCopyMode, 555 } 556 } 557 558 // IsDestinationMounted checks whether a path is mounted on the container or not. 559 func (container *Container) IsDestinationMounted(destination string) bool { 560 return container.MountPoints[destination] != nil 561 } 562 563 // StopSignal returns the signal used to stop the container. 564 func (container *Container) StopSignal() int { 565 var stopSignal syscall.Signal 566 if container.Config.StopSignal != "" { 567 stopSignal, _ = signal.ParseSignal(container.Config.StopSignal) 568 } 569 570 if int(stopSignal) == 0 { 571 stopSignal, _ = signal.ParseSignal(signal.DefaultStopSignal) 572 } 573 return int(stopSignal) 574 } 575 576 // InitDNSHostConfig ensures that the dns fields are never nil. 577 // New containers don't ever have those fields nil, 578 // but pre created containers can still have those nil values. 579 // The non-recommended host configuration in the start api can 580 // make these fields nil again, this corrects that issue until 581 // we remove that behavior for good. 582 // See https://github.com/docker/docker/pull/17779 583 // for a more detailed explanation on why we don't want that. 584 func (container *Container) InitDNSHostConfig() { 585 container.Lock() 586 defer container.Unlock() 587 if container.HostConfig.DNS == nil { 588 container.HostConfig.DNS = make([]string, 0) 589 } 590 591 if container.HostConfig.DNSSearch == nil { 592 container.HostConfig.DNSSearch = make([]string, 0) 593 } 594 595 if container.HostConfig.DNSOptions == nil { 596 container.HostConfig.DNSOptions = make([]string, 0) 597 } 598 } 599 600 // GetEndpointInNetwork returns the container's endpoint to the provided network. 601 func (container *Container) GetEndpointInNetwork(n libnetwork.Network) (libnetwork.Endpoint, error) { 602 endpointName := strings.TrimPrefix(container.Name, "/") 603 return n.EndpointByName(endpointName) 604 } 605 606 func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint) error { 607 if ep == nil { 608 return errInvalidEndpoint 609 } 610 611 networkSettings := container.NetworkSettings 612 if networkSettings == nil { 613 return errInvalidNetwork 614 } 615 616 if len(networkSettings.Ports) == 0 { 617 pm, err := getEndpointPortMapInfo(ep) 618 if err != nil { 619 return err 620 } 621 networkSettings.Ports = pm 622 } 623 return nil 624 } 625 626 func getEndpointPortMapInfo(ep libnetwork.Endpoint) (nat.PortMap, error) { 627 pm := nat.PortMap{} 628 driverInfo, err := ep.DriverInfo() 629 if err != nil { 630 return pm, err 631 } 632 633 if driverInfo == nil { 634 // It is not an error for epInfo to be nil 635 return pm, nil 636 } 637 638 if expData, ok := driverInfo[netlabel.ExposedPorts]; ok { 639 if exposedPorts, ok := expData.([]types.TransportPort); ok { 640 for _, tp := range exposedPorts { 641 natPort, err := nat.NewPort(tp.Proto.String(), strconv.Itoa(int(tp.Port))) 642 if err != nil { 643 return pm, fmt.Errorf("Error parsing Port value(%v):%v", tp.Port, err) 644 } 645 pm[natPort] = nil 646 } 647 } 648 } 649 650 mapData, ok := driverInfo[netlabel.PortMap] 651 if !ok { 652 return pm, nil 653 } 654 655 if portMapping, ok := mapData.([]types.PortBinding); ok { 656 for _, pp := range portMapping { 657 natPort, err := nat.NewPort(pp.Proto.String(), strconv.Itoa(int(pp.Port))) 658 if err != nil { 659 return pm, err 660 } 661 natBndg := nat.PortBinding{HostIP: pp.HostIP.String(), HostPort: strconv.Itoa(int(pp.HostPort))} 662 pm[natPort] = append(pm[natPort], natBndg) 663 } 664 } 665 666 return pm, nil 667 } 668 669 func getSandboxPortMapInfo(sb libnetwork.Sandbox) nat.PortMap { 670 pm := nat.PortMap{} 671 if sb == nil { 672 return pm 673 } 674 675 for _, ep := range sb.Endpoints() { 676 pm, _ = getEndpointPortMapInfo(ep) 677 if len(pm) > 0 { 678 break 679 } 680 } 681 return pm 682 } 683 684 // BuildEndpointInfo sets endpoint-related fields on container.NetworkSettings based on the provided network and endpoint. 685 func (container *Container) BuildEndpointInfo(n libnetwork.Network, ep libnetwork.Endpoint) error { 686 if ep == nil { 687 return errInvalidEndpoint 688 } 689 690 networkSettings := container.NetworkSettings 691 if networkSettings == nil { 692 return errInvalidNetwork 693 } 694 695 epInfo := ep.Info() 696 if epInfo == nil { 697 // It is not an error to get an empty endpoint info 698 return nil 699 } 700 701 if _, ok := networkSettings.Networks[n.Name()]; !ok { 702 networkSettings.Networks[n.Name()] = new(networktypes.EndpointSettings) 703 } 704 networkSettings.Networks[n.Name()].NetworkID = n.ID() 705 networkSettings.Networks[n.Name()].EndpointID = ep.ID() 706 707 iface := epInfo.Iface() 708 if iface == nil { 709 return nil 710 } 711 712 if iface.MacAddress() != nil { 713 networkSettings.Networks[n.Name()].MacAddress = iface.MacAddress().String() 714 } 715 716 if iface.Address() != nil { 717 ones, _ := iface.Address().Mask.Size() 718 networkSettings.Networks[n.Name()].IPAddress = iface.Address().IP.String() 719 networkSettings.Networks[n.Name()].IPPrefixLen = ones 720 } 721 722 if iface.AddressIPv6() != nil && iface.AddressIPv6().IP.To16() != nil { 723 onesv6, _ := iface.AddressIPv6().Mask.Size() 724 networkSettings.Networks[n.Name()].GlobalIPv6Address = iface.AddressIPv6().IP.String() 725 networkSettings.Networks[n.Name()].GlobalIPv6PrefixLen = onesv6 726 } 727 728 return nil 729 } 730 731 // UpdateJoinInfo updates network settings when container joins network n with endpoint ep. 732 func (container *Container) UpdateJoinInfo(n libnetwork.Network, ep libnetwork.Endpoint) error { 733 if err := container.buildPortMapInfo(ep); err != nil { 734 return err 735 } 736 737 epInfo := ep.Info() 738 if epInfo == nil { 739 // It is not an error to get an empty endpoint info 740 return nil 741 } 742 if epInfo.Gateway() != nil { 743 container.NetworkSettings.Networks[n.Name()].Gateway = epInfo.Gateway().String() 744 } 745 if epInfo.GatewayIPv6().To16() != nil { 746 container.NetworkSettings.Networks[n.Name()].IPv6Gateway = epInfo.GatewayIPv6().String() 747 } 748 749 return nil 750 } 751 752 // UpdateSandboxNetworkSettings updates the sandbox ID and Key. 753 func (container *Container) UpdateSandboxNetworkSettings(sb libnetwork.Sandbox) error { 754 container.NetworkSettings.SandboxID = sb.ID() 755 container.NetworkSettings.SandboxKey = sb.Key() 756 return nil 757 } 758 759 // BuildJoinOptions builds endpoint Join options from a given network. 760 func (container *Container) BuildJoinOptions(n libnetwork.Network) ([]libnetwork.EndpointOption, error) { 761 var joinOptions []libnetwork.EndpointOption 762 if epConfig, ok := container.NetworkSettings.Networks[n.Name()]; ok { 763 for _, str := range epConfig.Links { 764 name, alias, err := runconfigopts.ParseLink(str) 765 if err != nil { 766 return nil, err 767 } 768 joinOptions = append(joinOptions, libnetwork.CreateOptionAlias(name, alias)) 769 } 770 } 771 return joinOptions, nil 772 } 773 774 // BuildCreateEndpointOptions builds endpoint options from a given network. 775 func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network, epConfig *networktypes.EndpointSettings, sb libnetwork.Sandbox) ([]libnetwork.EndpointOption, error) { 776 var ( 777 bindings = make(nat.PortMap) 778 pbList []types.PortBinding 779 exposeList []types.TransportPort 780 createOptions []libnetwork.EndpointOption 781 ) 782 783 defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName() 784 785 if n.Name() == defaultNetName || container.NetworkSettings.IsAnonymousEndpoint { 786 createOptions = append(createOptions, libnetwork.CreateOptionAnonymous()) 787 } 788 789 if epConfig != nil { 790 ipam := epConfig.IPAMConfig 791 if ipam != nil && (ipam.IPv4Address != "" || ipam.IPv6Address != "") { 792 createOptions = append(createOptions, 793 libnetwork.CreateOptionIpam(net.ParseIP(ipam.IPv4Address), net.ParseIP(ipam.IPv6Address), nil)) 794 } 795 796 for _, alias := range epConfig.Aliases { 797 createOptions = append(createOptions, libnetwork.CreateOptionMyAlias(alias)) 798 } 799 } 800 801 if !containertypes.NetworkMode(n.Name()).IsUserDefined() { 802 createOptions = append(createOptions, libnetwork.CreateOptionDisableResolution()) 803 } 804 805 // configs that are applicable only for the endpoint in the network 806 // to which container was connected to on docker run. 807 // Ideally all these network-specific endpoint configurations must be moved under 808 // container.NetworkSettings.Networks[n.Name()] 809 if n.Name() == container.HostConfig.NetworkMode.NetworkName() || 810 (n.Name() == defaultNetName && container.HostConfig.NetworkMode.IsDefault()) { 811 if container.Config.MacAddress != "" { 812 mac, err := net.ParseMAC(container.Config.MacAddress) 813 if err != nil { 814 return nil, err 815 } 816 817 genericOption := options.Generic{ 818 netlabel.MacAddress: mac, 819 } 820 821 createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(genericOption)) 822 } 823 } 824 825 // Port-mapping rules belong to the container & applicable only to non-internal networks 826 portmaps := getSandboxPortMapInfo(sb) 827 if n.Info().Internal() || len(portmaps) > 0 { 828 return createOptions, nil 829 } 830 831 if container.HostConfig.PortBindings != nil { 832 for p, b := range container.HostConfig.PortBindings { 833 bindings[p] = []nat.PortBinding{} 834 for _, bb := range b { 835 bindings[p] = append(bindings[p], nat.PortBinding{ 836 HostIP: bb.HostIP, 837 HostPort: bb.HostPort, 838 }) 839 } 840 } 841 } 842 843 portSpecs := container.Config.ExposedPorts 844 ports := make([]nat.Port, len(portSpecs)) 845 var i int 846 for p := range portSpecs { 847 ports[i] = p 848 i++ 849 } 850 nat.SortPortMap(ports, bindings) 851 for _, port := range ports { 852 expose := types.TransportPort{} 853 expose.Proto = types.ParseProtocol(port.Proto()) 854 expose.Port = uint16(port.Int()) 855 exposeList = append(exposeList, expose) 856 857 pb := types.PortBinding{Port: expose.Port, Proto: expose.Proto} 858 binding := bindings[port] 859 for i := 0; i < len(binding); i++ { 860 pbCopy := pb.GetCopy() 861 newP, err := nat.NewPort(nat.SplitProtoPort(binding[i].HostPort)) 862 var portStart, portEnd int 863 if err == nil { 864 portStart, portEnd, err = newP.Range() 865 } 866 if err != nil { 867 return nil, fmt.Errorf("Error parsing HostPort value(%s):%v", binding[i].HostPort, err) 868 } 869 pbCopy.HostPort = uint16(portStart) 870 pbCopy.HostPortEnd = uint16(portEnd) 871 pbCopy.HostIP = net.ParseIP(binding[i].HostIP) 872 pbList = append(pbList, pbCopy) 873 } 874 875 if container.HostConfig.PublishAllPorts && len(binding) == 0 { 876 pbList = append(pbList, pb) 877 } 878 } 879 880 createOptions = append(createOptions, 881 libnetwork.CreateOptionPortMapping(pbList), 882 libnetwork.CreateOptionExposedPorts(exposeList)) 883 884 return createOptions, nil 885 } 886 887 // UpdateMonitor updates monitor configure for running container 888 func (container *Container) UpdateMonitor(restartPolicy containertypes.RestartPolicy) { 889 type policySetter interface { 890 SetPolicy(containertypes.RestartPolicy) 891 } 892 893 if rm, ok := container.RestartManager(false).(policySetter); ok { 894 rm.SetPolicy(restartPolicy) 895 } 896 } 897 898 // FullHostname returns hostname and optional domain appended to it. 899 func (container *Container) FullHostname() string { 900 fullHostname := container.Config.Hostname 901 if container.Config.Domainname != "" { 902 fullHostname = fmt.Sprintf("%s.%s", fullHostname, container.Config.Domainname) 903 } 904 return fullHostname 905 } 906 907 // RestartManager returns the current restartmanager instance connected to container. 908 func (container *Container) RestartManager(reset bool) restartmanager.RestartManager { 909 if reset { 910 container.RestartCount = 0 911 container.restartManager = nil 912 } 913 if container.restartManager == nil { 914 container.restartManager = restartmanager.New(container.HostConfig.RestartPolicy, container.RestartCount) 915 } 916 917 return container.restartManager 918 } 919 920 type attachContext struct { 921 ctx context.Context 922 cancel context.CancelFunc 923 mu sync.Mutex 924 } 925 926 // InitAttachContext initialize or returns existing context for attach calls to 927 // track container liveness. 928 func (container *Container) InitAttachContext() context.Context { 929 container.attachContext.mu.Lock() 930 defer container.attachContext.mu.Unlock() 931 if container.attachContext.ctx == nil { 932 container.attachContext.ctx, container.attachContext.cancel = context.WithCancel(context.Background()) 933 } 934 return container.attachContext.ctx 935 } 936 937 // CancelAttachContext cancel attach context. All attach calls should detach 938 // after this call. 939 func (container *Container) CancelAttachContext() { 940 container.attachContext.mu.Lock() 941 if container.attachContext.ctx != nil { 942 container.attachContext.cancel() 943 container.attachContext.ctx = nil 944 } 945 container.attachContext.mu.Unlock() 946 }