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