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