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