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