github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/container/container.go (about) 1 package container // import "github.com/demonoid81/moby/container" 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "fmt" 8 "io" 9 "os" 10 "path/filepath" 11 "runtime" 12 "strings" 13 "sync" 14 "syscall" 15 "time" 16 17 "github.com/containerd/containerd/cio" 18 containertypes "github.com/demonoid81/moby/api/types/container" 19 mounttypes "github.com/demonoid81/moby/api/types/mount" 20 swarmtypes "github.com/demonoid81/moby/api/types/swarm" 21 "github.com/demonoid81/moby/container/stream" 22 "github.com/demonoid81/moby/daemon/exec" 23 "github.com/demonoid81/moby/daemon/logger" 24 "github.com/demonoid81/moby/daemon/logger/jsonfilelog" 25 "github.com/demonoid81/moby/daemon/logger/local" 26 "github.com/demonoid81/moby/daemon/logger/loggerutils/cache" 27 "github.com/demonoid81/moby/daemon/network" 28 "github.com/demonoid81/moby/errdefs" 29 "github.com/demonoid81/moby/image" 30 "github.com/demonoid81/moby/layer" 31 "github.com/demonoid81/moby/pkg/containerfs" 32 "github.com/demonoid81/moby/pkg/idtools" 33 "github.com/demonoid81/moby/pkg/ioutils" 34 "github.com/demonoid81/moby/pkg/signal" 35 "github.com/demonoid81/moby/pkg/symlink" 36 "github.com/demonoid81/moby/pkg/system" 37 "github.com/demonoid81/moby/restartmanager" 38 "github.com/demonoid81/moby/volume" 39 volumemounts "github.com/demonoid81/moby/volume/mounts" 40 units "github.com/docker/go-units" 41 agentexec "github.com/docker/swarmkit/agent/exec" 42 "github.com/pkg/errors" 43 "github.com/sirupsen/logrus" 44 ) 45 46 const configFileName = "config.v2.json" 47 48 // ExitStatus provides exit reasons for a container. 49 type ExitStatus struct { 50 // The exit code with which the container exited. 51 ExitCode int 52 53 // Whether the container encountered an OOM. 54 OOMKilled bool 55 56 // Time at which the container died 57 ExitedAt time.Time 58 } 59 60 // Container holds the structure defining a container object. 61 type Container struct { 62 StreamConfig *stream.Config 63 // embed for Container to support states directly. 64 *State `json:"State"` // Needed for Engine API version <= 1.11 65 Root string `json:"-"` // Path to the "home" of the container, including metadata. 66 BaseFS containerfs.ContainerFS `json:"-"` // interface containing graphdriver mount 67 RWLayer layer.RWLayer `json:"-"` 68 ID string 69 Created time.Time 70 Managed bool 71 Path string 72 Args []string 73 Config *containertypes.Config 74 ImageID image.ID `json:"Image"` 75 NetworkSettings *network.Settings 76 LogPath string 77 Name string 78 Driver string 79 OS string 80 // MountLabel contains the options for the 'mount' command 81 MountLabel string 82 ProcessLabel string 83 RestartCount int 84 HasBeenStartedBefore bool 85 HasBeenManuallyStopped bool // used for unless-stopped restart policy 86 MountPoints map[string]*volumemounts.MountPoint 87 HostConfig *containertypes.HostConfig `json:"-"` // do not serialize the host config in the json, otherwise we'll make the container unportable 88 ExecCommands *exec.Store `json:"-"` 89 DependencyStore agentexec.DependencyGetter `json:"-"` 90 SecretReferences []*swarmtypes.SecretReference 91 ConfigReferences []*swarmtypes.ConfigReference 92 // logDriver for closing 93 LogDriver logger.Logger `json:"-"` 94 LogCopier *logger.Copier `json:"-"` 95 restartManager restartmanager.RestartManager 96 attachContext *attachContext 97 98 // Fields here are specific to Unix platforms 99 AppArmorProfile string 100 HostnamePath string 101 HostsPath string 102 ShmPath string 103 ResolvConfPath string 104 SeccompProfile string 105 NoNewPrivileges bool 106 107 // Fields here are specific to Windows 108 NetworkSharedContainerID string `json:"-"` 109 SharedEndpointList []string `json:"-"` 110 LocalLogCacheMeta localLogCacheMeta `json:",omitempty"` 111 } 112 113 type localLogCacheMeta struct { 114 HaveNotifyEnabled bool 115 } 116 117 // NewBaseContainer creates a new container with its 118 // basic configuration. 119 func NewBaseContainer(id, root string) *Container { 120 return &Container{ 121 ID: id, 122 State: NewState(), 123 ExecCommands: exec.NewStore(), 124 Root: root, 125 MountPoints: make(map[string]*volumemounts.MountPoint), 126 StreamConfig: stream.NewConfig(), 127 attachContext: &attachContext{}, 128 } 129 } 130 131 // FromDisk loads the container configuration stored in the host. 132 func (container *Container) FromDisk() error { 133 pth, err := container.ConfigPath() 134 if err != nil { 135 return err 136 } 137 138 jsonSource, err := os.Open(pth) 139 if err != nil { 140 return err 141 } 142 defer jsonSource.Close() 143 144 dec := json.NewDecoder(jsonSource) 145 146 // Load container settings 147 if err := dec.Decode(container); err != nil { 148 return err 149 } 150 151 // Ensure the operating system is set if blank. Assume it is the OS of the 152 // host OS if not, to ensure containers created before multiple-OS 153 // support are migrated 154 if container.OS == "" { 155 container.OS = runtime.GOOS 156 } 157 158 return container.readHostConfig() 159 } 160 161 // toDisk saves the container configuration on disk and returns a deep copy. 162 func (container *Container) toDisk() (*Container, error) { 163 var ( 164 buf bytes.Buffer 165 deepCopy Container 166 ) 167 pth, err := container.ConfigPath() 168 if err != nil { 169 return nil, err 170 } 171 172 // Save container settings 173 f, err := ioutils.NewAtomicFileWriter(pth, 0600) 174 if err != nil { 175 return nil, err 176 } 177 defer f.Close() 178 179 w := io.MultiWriter(&buf, f) 180 if err := json.NewEncoder(w).Encode(container); err != nil { 181 return nil, err 182 } 183 184 if err := json.NewDecoder(&buf).Decode(&deepCopy); err != nil { 185 return nil, err 186 } 187 deepCopy.HostConfig, err = container.WriteHostConfig() 188 if err != nil { 189 return nil, err 190 } 191 192 return &deepCopy, nil 193 } 194 195 // CheckpointTo makes the Container's current state visible to queries, and persists state. 196 // Callers must hold a Container lock. 197 func (container *Container) CheckpointTo(store ViewDB) error { 198 deepCopy, err := container.toDisk() 199 if err != nil { 200 return err 201 } 202 return store.Save(deepCopy) 203 } 204 205 // readHostConfig reads the host configuration from disk for the container. 206 func (container *Container) readHostConfig() error { 207 container.HostConfig = &containertypes.HostConfig{} 208 // If the hostconfig file does not exist, do not read it. 209 // (We still have to initialize container.HostConfig, 210 // but that's OK, since we just did that above.) 211 pth, err := container.HostConfigPath() 212 if err != nil { 213 return err 214 } 215 216 f, err := os.Open(pth) 217 if err != nil { 218 if os.IsNotExist(err) { 219 return nil 220 } 221 return err 222 } 223 defer f.Close() 224 225 if err := json.NewDecoder(f).Decode(&container.HostConfig); err != nil { 226 return err 227 } 228 229 container.InitDNSHostConfig() 230 231 return nil 232 } 233 234 // WriteHostConfig saves the host configuration on disk for the container, 235 // and returns a deep copy of the saved object. Callers must hold a Container lock. 236 func (container *Container) WriteHostConfig() (*containertypes.HostConfig, error) { 237 var ( 238 buf bytes.Buffer 239 deepCopy containertypes.HostConfig 240 ) 241 242 pth, err := container.HostConfigPath() 243 if err != nil { 244 return nil, err 245 } 246 247 f, err := ioutils.NewAtomicFileWriter(pth, 0644) 248 if err != nil { 249 return nil, err 250 } 251 defer f.Close() 252 253 w := io.MultiWriter(&buf, f) 254 if err := json.NewEncoder(w).Encode(&container.HostConfig); err != nil { 255 return nil, err 256 } 257 258 if err := json.NewDecoder(&buf).Decode(&deepCopy); err != nil { 259 return nil, err 260 } 261 return &deepCopy, nil 262 } 263 264 // SetupWorkingDirectory sets up the container's working directory as set in container.Config.WorkingDir 265 func (container *Container) SetupWorkingDirectory(rootIdentity idtools.Identity) error { 266 // TODO: LCOW Support. This will need revisiting. 267 // We will need to do remote filesystem operations here. 268 if container.OS != runtime.GOOS { 269 return nil 270 } 271 272 if container.Config.WorkingDir == "" { 273 return nil 274 } 275 276 container.Config.WorkingDir = filepath.Clean(container.Config.WorkingDir) 277 pth, err := container.GetResourcePath(container.Config.WorkingDir) 278 if err != nil { 279 return err 280 } 281 282 if err := idtools.MkdirAllAndChownNew(pth, 0755, rootIdentity); err != nil { 283 pthInfo, err2 := os.Stat(pth) 284 if err2 == nil && pthInfo != nil && !pthInfo.IsDir() { 285 return errors.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir) 286 } 287 288 return err 289 } 290 291 return nil 292 } 293 294 // GetResourcePath evaluates `path` in the scope of the container's BaseFS, with proper path 295 // sanitisation. Symlinks are all scoped to the BaseFS of the container, as 296 // though the container's BaseFS was `/`. 297 // 298 // The BaseFS of a container is the host-facing path which is bind-mounted as 299 // `/` inside the container. This method is essentially used to access a 300 // particular path inside the container as though you were a process in that 301 // container. 302 // 303 // NOTE: The returned path is *only* safely scoped inside the container's BaseFS 304 // if no component of the returned path changes (such as a component 305 // symlinking to a different path) between using this method and using the 306 // path. See symlink.FollowSymlinkInScope for more details. 307 func (container *Container) GetResourcePath(path string) (string, error) { 308 if container.BaseFS == nil { 309 return "", errors.New("GetResourcePath: BaseFS of container " + container.ID + " is unexpectedly nil") 310 } 311 // IMPORTANT - These are paths on the OS where the daemon is running, hence 312 // any filepath operations must be done in an OS agnostic way. 313 r, e := container.BaseFS.ResolveScopedPath(path, false) 314 315 // Log this here on the daemon side as there's otherwise no indication apart 316 // from the error being propagated all the way back to the client. This makes 317 // debugging significantly easier and clearly indicates the error comes from the daemon. 318 if e != nil { 319 logrus.Errorf("Failed to ResolveScopedPath BaseFS %s path %s %s\n", container.BaseFS.Path(), path, e) 320 } 321 return r, e 322 } 323 324 // GetRootResourcePath evaluates `path` in the scope of the container's root, with proper path 325 // sanitisation. Symlinks are all scoped to the root of the container, as 326 // though the container's root was `/`. 327 // 328 // The root of a container is the host-facing configuration metadata directory. 329 // Only use this method to safely access the container's `container.json` or 330 // other metadata files. If in doubt, use container.GetResourcePath. 331 // 332 // NOTE: The returned path is *only* safely scoped inside the container's root 333 // if no component of the returned path changes (such as a component 334 // symlinking to a different path) between using this method and using the 335 // path. See symlink.FollowSymlinkInScope for more details. 336 func (container *Container) GetRootResourcePath(path string) (string, error) { 337 // IMPORTANT - These are paths on the OS where the daemon is running, hence 338 // any filepath operations must be done in an OS agnostic way. 339 cleanPath := filepath.Join(string(os.PathSeparator), path) 340 return symlink.FollowSymlinkInScope(filepath.Join(container.Root, cleanPath), container.Root) 341 } 342 343 // ExitOnNext signals to the monitor that it should not restart the container 344 // after we send the kill signal. 345 func (container *Container) ExitOnNext() { 346 container.RestartManager().Cancel() 347 } 348 349 // HostConfigPath returns the path to the container's JSON hostconfig 350 func (container *Container) HostConfigPath() (string, error) { 351 return container.GetRootResourcePath("hostconfig.json") 352 } 353 354 // ConfigPath returns the path to the container's JSON config 355 func (container *Container) ConfigPath() (string, error) { 356 return container.GetRootResourcePath(configFileName) 357 } 358 359 // CheckpointDir returns the directory checkpoints are stored in 360 func (container *Container) CheckpointDir() string { 361 return filepath.Join(container.Root, "checkpoints") 362 } 363 364 // StartLogger starts a new logger driver for the container. 365 func (container *Container) StartLogger() (logger.Logger, error) { 366 cfg := container.HostConfig.LogConfig 367 initDriver, err := logger.GetLogDriver(cfg.Type) 368 if err != nil { 369 return nil, errors.Wrap(err, "failed to get logging factory") 370 } 371 info := logger.Info{ 372 Config: cfg.Config, 373 ContainerID: container.ID, 374 ContainerName: container.Name, 375 ContainerEntrypoint: container.Path, 376 ContainerArgs: container.Args, 377 ContainerImageID: container.ImageID.String(), 378 ContainerImageName: container.Config.Image, 379 ContainerCreated: container.Created, 380 ContainerEnv: container.Config.Env, 381 ContainerLabels: container.Config.Labels, 382 DaemonName: "docker", 383 } 384 385 // Set logging file for "json-logger" 386 // TODO(@cpuguy83): Setup here based on log driver is a little weird. 387 switch cfg.Type { 388 case jsonfilelog.Name: 389 info.LogPath, err = container.GetRootResourcePath(fmt.Sprintf("%s-json.log", container.ID)) 390 if err != nil { 391 return nil, err 392 } 393 394 container.LogPath = info.LogPath 395 case local.Name: 396 // Do not set container.LogPath for the local driver 397 // This would expose the value to the API, which should not be done as it means 398 // that the log file implementation would become a stable API that cannot change. 399 logDir, err := container.GetRootResourcePath("local-logs") 400 if err != nil { 401 return nil, err 402 } 403 if err := os.MkdirAll(logDir, 0700); err != nil { 404 return nil, errdefs.System(errors.Wrap(err, "error creating local logs dir")) 405 } 406 info.LogPath = filepath.Join(logDir, "container.log") 407 } 408 409 l, err := initDriver(info) 410 if err != nil { 411 return nil, err 412 } 413 414 if containertypes.LogMode(cfg.Config["mode"]) == containertypes.LogModeNonBlock { 415 bufferSize := int64(-1) 416 if s, exists := cfg.Config["max-buffer-size"]; exists { 417 bufferSize, err = units.RAMInBytes(s) 418 if err != nil { 419 return nil, err 420 } 421 } 422 l = logger.NewRingLogger(l, info, bufferSize) 423 } 424 425 if _, ok := l.(logger.LogReader); !ok { 426 if cache.ShouldUseCache(cfg.Config) { 427 logPath, err := container.GetRootResourcePath("container-cached.log") 428 if err != nil { 429 return nil, err 430 } 431 432 if !container.LocalLogCacheMeta.HaveNotifyEnabled { 433 logrus.WithField("container", container.ID).WithField("driver", container.HostConfig.LogConfig.Type).Info("Configured log driver does not support reads, enabling local file cache for container logs") 434 container.LocalLogCacheMeta.HaveNotifyEnabled = true 435 } 436 info.LogPath = logPath 437 l, err = cache.WithLocalCache(l, info) 438 if err != nil { 439 return nil, errors.Wrap(err, "error setting up local container log cache") 440 } 441 } 442 } 443 return l, nil 444 } 445 446 // GetProcessLabel returns the process label for the container. 447 func (container *Container) GetProcessLabel() string { 448 // even if we have a process label return "" if we are running 449 // in privileged mode 450 if container.HostConfig.Privileged { 451 return "" 452 } 453 return container.ProcessLabel 454 } 455 456 // GetMountLabel returns the mounting label for the container. 457 // This label is empty if the container is privileged. 458 func (container *Container) GetMountLabel() string { 459 return container.MountLabel 460 } 461 462 // GetExecIDs returns the list of exec commands running on the container. 463 func (container *Container) GetExecIDs() []string { 464 return container.ExecCommands.List() 465 } 466 467 // ShouldRestart decides whether the daemon should restart the container or not. 468 // This is based on the container's restart policy. 469 func (container *Container) ShouldRestart() bool { 470 shouldRestart, _, _ := container.RestartManager().ShouldRestart(uint32(container.ExitCode()), container.HasBeenManuallyStopped, container.FinishedAt.Sub(container.StartedAt)) 471 return shouldRestart 472 } 473 474 // AddMountPointWithVolume adds a new mount point configured with a volume to the container. 475 func (container *Container) AddMountPointWithVolume(destination string, vol volume.Volume, rw bool) { 476 operatingSystem := container.OS 477 if operatingSystem == "" { 478 operatingSystem = runtime.GOOS 479 } 480 volumeParser := volumemounts.NewParser(operatingSystem) 481 container.MountPoints[destination] = &volumemounts.MountPoint{ 482 Type: mounttypes.TypeVolume, 483 Name: vol.Name(), 484 Driver: vol.DriverName(), 485 Destination: destination, 486 RW: rw, 487 Volume: vol, 488 CopyData: volumeParser.DefaultCopyMode(), 489 } 490 } 491 492 // UnmountVolumes unmounts all volumes 493 func (container *Container) UnmountVolumes(volumeEventLog func(name, action string, attributes map[string]string)) error { 494 var errors []string 495 for _, volumeMount := range container.MountPoints { 496 if volumeMount.Volume == nil { 497 continue 498 } 499 500 if err := volumeMount.Cleanup(); err != nil { 501 errors = append(errors, err.Error()) 502 continue 503 } 504 505 attributes := map[string]string{ 506 "driver": volumeMount.Volume.DriverName(), 507 "container": container.ID, 508 } 509 volumeEventLog(volumeMount.Volume.Name(), "unmount", attributes) 510 } 511 if len(errors) > 0 { 512 return fmt.Errorf("error while unmounting volumes for container %s: %s", container.ID, strings.Join(errors, "; ")) 513 } 514 return nil 515 } 516 517 // IsDestinationMounted checks whether a path is mounted on the container or not. 518 func (container *Container) IsDestinationMounted(destination string) bool { 519 return container.MountPoints[destination] != nil 520 } 521 522 // StopSignal returns the signal used to stop the container. 523 func (container *Container) StopSignal() int { 524 var stopSignal syscall.Signal 525 if container.Config.StopSignal != "" { 526 stopSignal, _ = signal.ParseSignal(container.Config.StopSignal) 527 } 528 529 if int(stopSignal) == 0 { 530 stopSignal, _ = signal.ParseSignal(signal.DefaultStopSignal) 531 } 532 return int(stopSignal) 533 } 534 535 // StopTimeout returns the timeout (in seconds) used to stop the container. 536 func (container *Container) StopTimeout() int { 537 if container.Config.StopTimeout != nil { 538 return *container.Config.StopTimeout 539 } 540 return DefaultStopTimeout 541 } 542 543 // InitDNSHostConfig ensures that the dns fields are never nil. 544 // New containers don't ever have those fields nil, 545 // but pre created containers can still have those nil values. 546 // The non-recommended host configuration in the start api can 547 // make these fields nil again, this corrects that issue until 548 // we remove that behavior for good. 549 // See https://github.com/demonoid81/moby/pull/17779 550 // for a more detailed explanation on why we don't want that. 551 func (container *Container) InitDNSHostConfig() { 552 container.Lock() 553 defer container.Unlock() 554 if container.HostConfig.DNS == nil { 555 container.HostConfig.DNS = make([]string, 0) 556 } 557 558 if container.HostConfig.DNSSearch == nil { 559 container.HostConfig.DNSSearch = make([]string, 0) 560 } 561 562 if container.HostConfig.DNSOptions == nil { 563 container.HostConfig.DNSOptions = make([]string, 0) 564 } 565 } 566 567 // UpdateMonitor updates monitor configure for running container 568 func (container *Container) UpdateMonitor(restartPolicy containertypes.RestartPolicy) { 569 type policySetter interface { 570 SetPolicy(containertypes.RestartPolicy) 571 } 572 573 if rm, ok := container.RestartManager().(policySetter); ok { 574 rm.SetPolicy(restartPolicy) 575 } 576 } 577 578 // FullHostname returns hostname and optional domain appended to it. 579 func (container *Container) FullHostname() string { 580 fullHostname := container.Config.Hostname 581 if container.Config.Domainname != "" { 582 fullHostname = fmt.Sprintf("%s.%s", fullHostname, container.Config.Domainname) 583 } 584 return fullHostname 585 } 586 587 // RestartManager returns the current restartmanager instance connected to container. 588 func (container *Container) RestartManager() restartmanager.RestartManager { 589 if container.restartManager == nil { 590 container.restartManager = restartmanager.New(container.HostConfig.RestartPolicy, container.RestartCount) 591 } 592 return container.restartManager 593 } 594 595 // ResetRestartManager initializes new restartmanager based on container config 596 func (container *Container) ResetRestartManager(resetCount bool) { 597 if container.restartManager != nil { 598 container.restartManager.Cancel() 599 } 600 if resetCount { 601 container.RestartCount = 0 602 } 603 container.restartManager = nil 604 } 605 606 type attachContext struct { 607 ctx context.Context 608 cancel context.CancelFunc 609 mu sync.Mutex 610 } 611 612 // InitAttachContext initializes or returns existing context for attach calls to 613 // track container liveness. 614 func (container *Container) InitAttachContext() context.Context { 615 container.attachContext.mu.Lock() 616 defer container.attachContext.mu.Unlock() 617 if container.attachContext.ctx == nil { 618 container.attachContext.ctx, container.attachContext.cancel = context.WithCancel(context.Background()) 619 } 620 return container.attachContext.ctx 621 } 622 623 // CancelAttachContext cancels attach context. All attach calls should detach 624 // after this call. 625 func (container *Container) CancelAttachContext() { 626 container.attachContext.mu.Lock() 627 if container.attachContext.ctx != nil { 628 container.attachContext.cancel() 629 container.attachContext.ctx = nil 630 } 631 container.attachContext.mu.Unlock() 632 } 633 634 func (container *Container) startLogging() error { 635 if container.HostConfig.LogConfig.Type == "none" { 636 return nil // do not start logging routines 637 } 638 639 l, err := container.StartLogger() 640 if err != nil { 641 return fmt.Errorf("failed to initialize logging driver: %v", err) 642 } 643 644 copier := logger.NewCopier(map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l) 645 container.LogCopier = copier 646 copier.Run() 647 container.LogDriver = l 648 649 return nil 650 } 651 652 // StdinPipe gets the stdin stream of the container 653 func (container *Container) StdinPipe() io.WriteCloser { 654 return container.StreamConfig.StdinPipe() 655 } 656 657 // StdoutPipe gets the stdout stream of the container 658 func (container *Container) StdoutPipe() io.ReadCloser { 659 return container.StreamConfig.StdoutPipe() 660 } 661 662 // StderrPipe gets the stderr stream of the container 663 func (container *Container) StderrPipe() io.ReadCloser { 664 return container.StreamConfig.StderrPipe() 665 } 666 667 // CloseStreams closes the container's stdio streams 668 func (container *Container) CloseStreams() error { 669 return container.StreamConfig.CloseStreams() 670 } 671 672 // InitializeStdio is called by libcontainerd to connect the stdio. 673 func (container *Container) InitializeStdio(iop *cio.DirectIO) (cio.IO, error) { 674 if err := container.startLogging(); err != nil { 675 container.Reset(false) 676 return nil, err 677 } 678 679 container.StreamConfig.CopyToPipe(iop) 680 681 if container.StreamConfig.Stdin() == nil && !container.Config.Tty { 682 if iop.Stdin != nil { 683 if err := iop.Stdin.Close(); err != nil { 684 logrus.Warnf("error closing stdin: %+v", err) 685 } 686 } 687 } 688 689 return &rio{IO: iop, sc: container.StreamConfig}, nil 690 } 691 692 // MountsResourcePath returns the path where mounts are stored for the given mount 693 func (container *Container) MountsResourcePath(mount string) (string, error) { 694 return container.GetRootResourcePath(filepath.Join("mounts", mount)) 695 } 696 697 // SecretMountPath returns the path of the secret mount for the container 698 func (container *Container) SecretMountPath() (string, error) { 699 return container.MountsResourcePath("secrets") 700 } 701 702 // SecretFilePath returns the path to the location of a secret on the host. 703 func (container *Container) SecretFilePath(secretRef swarmtypes.SecretReference) (string, error) { 704 secrets, err := container.SecretMountPath() 705 if err != nil { 706 return "", err 707 } 708 return filepath.Join(secrets, secretRef.SecretID), nil 709 } 710 711 func getSecretTargetPath(r *swarmtypes.SecretReference) string { 712 if filepath.IsAbs(r.File.Name) { 713 return r.File.Name 714 } 715 716 return filepath.Join(containerSecretMountPath, r.File.Name) 717 } 718 719 // CreateDaemonEnvironment creates a new environment variable slice for this container. 720 func (container *Container) CreateDaemonEnvironment(tty bool, linkedEnv []string) []string { 721 // Setup environment 722 os := container.OS 723 if os == "" { 724 os = runtime.GOOS 725 } 726 727 // Figure out what size slice we need so we can allocate this all at once. 728 envSize := len(container.Config.Env) 729 if runtime.GOOS != "windows" || (runtime.GOOS == "windows" && os == "linux") { 730 envSize += 2 + len(linkedEnv) 731 } 732 if tty { 733 envSize++ 734 } 735 736 env := make([]string, 0, envSize) 737 if runtime.GOOS != "windows" || (runtime.GOOS == "windows" && os == "linux") { 738 env = append(env, "PATH="+system.DefaultPathEnv(os)) 739 env = append(env, "HOSTNAME="+container.Config.Hostname) 740 if tty { 741 env = append(env, "TERM=xterm") 742 } 743 env = append(env, linkedEnv...) 744 } 745 746 // because the env on the container can override certain default values 747 // we need to replace the 'env' keys where they match and append anything 748 // else. 749 env = ReplaceOrAppendEnvValues(env, container.Config.Env) 750 return env 751 } 752 753 type rio struct { 754 cio.IO 755 756 sc *stream.Config 757 } 758 759 func (i *rio) Close() error { 760 i.IO.Close() 761 762 return i.sc.CloseStreams() 763 } 764 765 func (i *rio) Wait() { 766 i.sc.Wait(context.Background()) 767 768 i.IO.Wait() 769 }