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