github.com/containerd/nerdctl@v1.7.7/pkg/cmd/container/create.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package container 18 19 import ( 20 "context" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "net/url" 25 "os" 26 "os/exec" 27 "path" 28 "path/filepath" 29 "runtime" 30 "strconv" 31 "strings" 32 33 "github.com/containerd/containerd" 34 "github.com/containerd/containerd/cio" 35 "github.com/containerd/containerd/containers" 36 "github.com/containerd/containerd/oci" 37 gocni "github.com/containerd/go-cni" 38 "github.com/containerd/log" 39 "github.com/containerd/nerdctl/pkg/api/types" 40 "github.com/containerd/nerdctl/pkg/clientutil" 41 "github.com/containerd/nerdctl/pkg/cmd/image" 42 "github.com/containerd/nerdctl/pkg/containerutil" 43 "github.com/containerd/nerdctl/pkg/flagutil" 44 "github.com/containerd/nerdctl/pkg/idgen" 45 "github.com/containerd/nerdctl/pkg/imgutil" 46 "github.com/containerd/nerdctl/pkg/inspecttypes/dockercompat" 47 "github.com/containerd/nerdctl/pkg/labels" 48 "github.com/containerd/nerdctl/pkg/logging" 49 "github.com/containerd/nerdctl/pkg/mountutil" 50 "github.com/containerd/nerdctl/pkg/namestore" 51 "github.com/containerd/nerdctl/pkg/platformutil" 52 "github.com/containerd/nerdctl/pkg/referenceutil" 53 "github.com/containerd/nerdctl/pkg/strutil" 54 dockercliopts "github.com/docker/cli/opts" 55 dockeropts "github.com/docker/docker/opts" 56 "github.com/opencontainers/runtime-spec/specs-go" 57 ) 58 59 // Create will create a container. 60 func Create(ctx context.Context, client *containerd.Client, args []string, netManager containerutil.NetworkOptionsManager, options types.ContainerCreateOptions) (containerd.Container, func(), error) { 61 // simulate the behavior of double dash 62 newArg := []string{} 63 if len(args) >= 2 && args[1] == "--" { 64 newArg = append(newArg, args[:1]...) 65 newArg = append(newArg, args[2:]...) 66 args = newArg 67 } 68 var internalLabels internalLabels 69 internalLabels.platform = options.Platform 70 internalLabels.namespace = options.GOptions.Namespace 71 72 var ( 73 id = idgen.GenerateID() 74 opts []oci.SpecOpts 75 cOpts []containerd.NewContainerOpts 76 ) 77 78 if options.CidFile != "" { 79 if err := writeCIDFile(options.CidFile, id); err != nil { 80 return nil, nil, err 81 } 82 } 83 dataStore, err := clientutil.DataStore(options.GOptions.DataRoot, options.GOptions.Address) 84 if err != nil { 85 return nil, nil, err 86 } 87 88 internalLabels.stateDir, err = containerutil.ContainerStateDirPath(options.GOptions.Namespace, dataStore, id) 89 if err != nil { 90 return nil, nil, err 91 } 92 if err := os.MkdirAll(internalLabels.stateDir, 0700); err != nil { 93 return nil, nil, err 94 } 95 96 opts = append(opts, 97 oci.WithDefaultSpec(), 98 ) 99 100 platformOpts, err := setPlatformOptions(ctx, client, id, netManager.NetworkOptions().UTSNamespace, &internalLabels, options) 101 if err != nil { 102 return nil, nil, err 103 } 104 opts = append(opts, platformOpts...) 105 106 var ensuredImage *imgutil.EnsuredImage 107 if !options.Rootfs { 108 var platformSS []string // len: 0 or 1 109 if options.Platform != "" { 110 platformSS = append(platformSS, options.Platform) 111 } 112 ocispecPlatforms, err := platformutil.NewOCISpecPlatformSlice(false, platformSS) 113 if err != nil { 114 return nil, nil, err 115 } 116 rawRef := args[0] 117 118 ensuredImage, err = image.EnsureImage(ctx, client, rawRef, ocispecPlatforms, options.Pull, nil, false, options.ImagePullOpt) 119 if err != nil { 120 return nil, nil, err 121 } 122 } 123 124 rootfsOpts, rootfsCOpts, err := generateRootfsOpts(args, id, ensuredImage, options) 125 if err != nil { 126 return nil, nil, err 127 } 128 opts = append(opts, rootfsOpts...) 129 cOpts = append(cOpts, rootfsCOpts...) 130 131 if options.Workdir != "" { 132 opts = append(opts, oci.WithProcessCwd(options.Workdir)) 133 } 134 135 envs, err := flagutil.MergeEnvFileAndOSEnv(options.EnvFile, options.Env) 136 if err != nil { 137 return nil, nil, err 138 } 139 opts = append(opts, oci.WithEnv(envs)) 140 141 if options.Interactive { 142 if options.Detach { 143 return nil, nil, errors.New("currently flag -i and -d cannot be specified together (FIXME)") 144 } 145 } 146 147 if options.TTY { 148 opts = append(opts, oci.WithTTY) 149 } 150 151 var mountOpts []oci.SpecOpts 152 mountOpts, internalLabels.anonVolumes, internalLabels.mountPoints, err = generateMountOpts(ctx, client, ensuredImage, options) 153 if err != nil { 154 return nil, nil, err 155 } 156 opts = append(opts, mountOpts...) 157 158 // Always set internalLabels.logURI 159 // to support restart the container that run with "-it", like 160 // 161 // 1, nerdctl run --name demo -it imagename 162 // 2, ctrl + c to stop demo container 163 // 3, nerdctl start/restart demo 164 logConfig, err := generateLogConfig(dataStore, id, options.LogDriver, options.LogOpt, options.GOptions.Namespace) 165 if err != nil { 166 return nil, nil, err 167 } 168 internalLabels.logURI = logConfig.LogURI 169 170 restartOpts, err := generateRestartOpts(ctx, client, options.Restart, logConfig.LogURI, options.InRun) 171 if err != nil { 172 return nil, nil, err 173 } 174 cOpts = append(cOpts, restartOpts...) 175 cOpts = append(cOpts, withStop(options.StopSignal, options.StopTimeout, ensuredImage)) 176 177 if err = netManager.VerifyNetworkOptions(ctx); err != nil { 178 return nil, nil, fmt.Errorf("failed to verify networking settings: %s", err) 179 } 180 181 netOpts, netNewContainerOpts, err := netManager.ContainerNetworkingOpts(ctx, id) 182 if err != nil { 183 return nil, nil, fmt.Errorf("failed to generate networking spec options: %s", err) 184 } 185 opts = append(opts, netOpts...) 186 cOpts = append(cOpts, netNewContainerOpts...) 187 188 netLabelOpts, err := netManager.InternalNetworkingOptionLabels(ctx) 189 if err != nil { 190 return nil, nil, fmt.Errorf("failed to generate internal networking labels: %s", err) 191 } 192 // TODO(aznashwan): more formal way to load net opts into internalLabels: 193 internalLabels.hostname = netLabelOpts.Hostname 194 internalLabels.ports = netLabelOpts.PortMappings 195 internalLabels.ipAddress = netLabelOpts.IPAddress 196 internalLabels.ip6Address = netLabelOpts.IP6Address 197 internalLabels.networks = netLabelOpts.NetworkSlice 198 internalLabels.macAddress = netLabelOpts.MACAddress 199 200 // NOTE: OCI hooks are currently not supported on Windows so we skip setting them altogether. 201 // The OCI hooks we define (whose logic can be found in pkg/ocihook) primarily 202 // perform network setup and teardown when using CNI networking. 203 // On Windows, we are forced to set up and tear down the networking from within nerdctl. 204 if runtime.GOOS != "windows" { 205 hookOpt, err := withNerdctlOCIHook(options.NerdctlCmd, options.NerdctlArgs) 206 if err != nil { 207 return nil, nil, err 208 } 209 opts = append(opts, hookOpt) 210 } 211 212 uOpts, err := generateUserOpts(options.User) 213 if err != nil { 214 return nil, nil, err 215 } 216 opts = append(opts, uOpts...) 217 gOpts, err := generateGroupsOpts(options.GroupAdd) 218 if err != nil { 219 return nil, nil, err 220 } 221 opts = append(opts, gOpts...) 222 223 umaskOpts, err := generateUmaskOpts(options.Umask) 224 if err != nil { 225 return nil, nil, err 226 } 227 opts = append(opts, umaskOpts...) 228 229 rtCOpts, err := generateRuntimeCOpts(options.GOptions.CgroupManager, options.Runtime) 230 if err != nil { 231 return nil, nil, err 232 } 233 cOpts = append(cOpts, rtCOpts...) 234 235 lCOpts, err := withContainerLabels(options.Label, options.LabelFile) 236 if err != nil { 237 return nil, nil, err 238 } 239 cOpts = append(cOpts, lCOpts...) 240 241 var containerNameStore namestore.NameStore 242 if options.Name == "" && !options.NameChanged { 243 // Automatically set the container name, unless `--name=""` was explicitly specified. 244 var imageRef string 245 if ensuredImage != nil { 246 imageRef = ensuredImage.Ref 247 } 248 options.Name = referenceutil.SuggestContainerName(imageRef, id) 249 } 250 if options.Name != "" { 251 containerNameStore, err = namestore.New(dataStore, options.GOptions.Namespace) 252 if err != nil { 253 return nil, nil, err 254 } 255 if err := containerNameStore.Acquire(options.Name, id); err != nil { 256 return nil, nil, err 257 } 258 } 259 internalLabels.name = options.Name 260 internalLabels.pidFile = options.PidFile 261 internalLabels.extraHosts = strutil.DedupeStrSlice(netManager.NetworkOptions().AddHost) 262 for i, host := range internalLabels.extraHosts { 263 if _, err := dockercliopts.ValidateExtraHost(host); err != nil { 264 return nil, nil, err 265 } 266 parts := strings.SplitN(host, ":", 2) 267 // If the IP Address is a string called "host-gateway", replace this value with the IP address stored 268 // in the daemon level HostGateway IP config variable. 269 if parts[1] == dockeropts.HostGatewayName { 270 if options.GOptions.HostGatewayIP == "" { 271 return nil, nil, fmt.Errorf("unable to derive the IP value for host-gateway") 272 } 273 parts[1] = options.GOptions.HostGatewayIP 274 internalLabels.extraHosts[i] = fmt.Sprintf(`%s:%s`, parts[0], parts[1]) 275 } 276 } 277 278 ilOpt, err := withInternalLabels(internalLabels) 279 if err != nil { 280 return nil, nil, err 281 } 282 cOpts = append(cOpts, ilOpt) 283 284 opts = append(opts, propagateContainerdLabelsToOCIAnnotations()) 285 286 var s specs.Spec 287 spec := containerd.WithSpec(&s, opts...) 288 289 cOpts = append(cOpts, spec) 290 291 c, containerErr := client.NewContainer(ctx, id, cOpts...) 292 var netSetupErr error 293 // NOTE: on non-Windows platforms, network setup is performed by OCI hooks. 294 // Seeing as though Windows does not currently support OCI hooks, we must explicitly 295 // perform network setup/teardown in the main nerdctl executable. 296 if containerErr == nil && runtime.GOOS == "windows" { 297 netSetupErr = netManager.SetupNetworking(ctx, id) 298 if netSetupErr != nil { 299 log.G(ctx).WithError(netSetupErr).Warnf("networking setup error has occurred") 300 } 301 } 302 303 if containerErr != nil || netSetupErr != nil { 304 returnedError := containerErr 305 if netSetupErr != nil { 306 returnedError = netSetupErr // mutually exclusive 307 } 308 return nil, generateGcFunc(ctx, c, options.GOptions.Namespace, id, options.Name, dataStore, containerErr, containerNameStore, netManager, internalLabels), returnedError 309 } 310 311 return c, nil, nil 312 } 313 314 func generateRootfsOpts(args []string, id string, ensured *imgutil.EnsuredImage, options types.ContainerCreateOptions) (opts []oci.SpecOpts, cOpts []containerd.NewContainerOpts, err error) { 315 if !options.Rootfs { 316 cOpts = append(cOpts, 317 containerd.WithImage(ensured.Image), 318 containerd.WithSnapshotter(ensured.Snapshotter), 319 containerd.WithNewSnapshot(id, ensured.Image), 320 containerd.WithImageStopSignal(ensured.Image, "SIGTERM"), 321 ) 322 323 if len(ensured.ImageConfig.Env) == 0 { 324 opts = append(opts, oci.WithDefaultPathEnv) 325 } 326 for ind, env := range ensured.ImageConfig.Env { 327 if strings.HasPrefix(env, "PATH=") { 328 break 329 } 330 if ind == len(ensured.ImageConfig.Env)-1 { 331 opts = append(opts, oci.WithDefaultPathEnv) 332 } 333 } 334 } else { 335 absRootfs, err := filepath.Abs(args[0]) 336 if err != nil { 337 return nil, nil, err 338 } 339 opts = append(opts, oci.WithRootFSPath(absRootfs), oci.WithDefaultPathEnv) 340 } 341 342 if !options.Rootfs && !options.EntrypointChanged { 343 opts = append(opts, oci.WithImageConfigArgs(ensured.Image, args[1:])) 344 } else { 345 if !options.Rootfs { 346 opts = append(opts, oci.WithImageConfig(ensured.Image)) 347 } 348 var processArgs []string 349 if len(options.Entrypoint) != 0 { 350 processArgs = append(processArgs, options.Entrypoint...) 351 } 352 if len(args) > 1 { 353 processArgs = append(processArgs, args[1:]...) 354 } 355 if len(processArgs) == 0 { 356 // error message is from Podman 357 return nil, nil, errors.New("no command or entrypoint provided, and no CMD or ENTRYPOINT from image") 358 } 359 opts = append(opts, oci.WithProcessArgs(processArgs...)) 360 } 361 if options.InitBinary != nil { 362 options.InitProcessFlag = true 363 } 364 if options.InitProcessFlag { 365 binaryPath, err := exec.LookPath(*options.InitBinary) 366 if err != nil { 367 if errors.Is(err, exec.ErrNotFound) { 368 return nil, nil, fmt.Errorf(`init binary %q not found`, *options.InitBinary) 369 } 370 return nil, nil, err 371 } 372 inContainerPath := filepath.Join("/sbin", filepath.Base(*options.InitBinary)) 373 opts = append(opts, func(_ context.Context, _ oci.Client, _ *containers.Container, spec *oci.Spec) error { 374 spec.Process.Args = append([]string{inContainerPath, "--"}, spec.Process.Args...) 375 spec.Mounts = append([]specs.Mount{{ 376 Destination: inContainerPath, 377 Type: "bind", 378 Source: binaryPath, 379 Options: []string{"bind", "ro"}, 380 }}, spec.Mounts...) 381 return nil 382 }) 383 } 384 if options.ReadOnly { 385 opts = append(opts, oci.WithRootFSReadonly()) 386 } 387 return opts, cOpts, nil 388 } 389 390 // withBindMountHostIPC replaces /dev/shm and /dev/mqueue mount with rbind. 391 // Required for --ipc=host on rootless. 392 func withBindMountHostIPC(_ context.Context, _ oci.Client, _ *containers.Container, s *oci.Spec) error { 393 for i, m := range s.Mounts { 394 switch p := path.Clean(m.Destination); p { 395 case "/dev/shm", "/dev/mqueue": 396 s.Mounts[i] = specs.Mount{ 397 Destination: p, 398 Type: "bind", 399 Source: p, 400 Options: []string{"rbind", "nosuid", "noexec", "nodev"}, 401 } 402 } 403 } 404 return nil 405 } 406 407 // GenerateLogURI generates a log URI for the current container store 408 func GenerateLogURI(dataStore string) (*url.URL, error) { 409 selfExe, err := os.Executable() 410 if err != nil { 411 return nil, err 412 } 413 args := map[string]string{ 414 logging.MagicArgv1: dataStore, 415 } 416 417 return cio.LogURIGenerator("binary", selfExe, args) 418 } 419 420 func withNerdctlOCIHook(cmd string, args []string) (oci.SpecOpts, error) { 421 args = append([]string{cmd}, append(args, "internal", "oci-hook")...) 422 return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error { 423 if s.Hooks == nil { 424 s.Hooks = &specs.Hooks{} 425 } 426 crArgs := append(args, "createRuntime") 427 s.Hooks.CreateRuntime = append(s.Hooks.CreateRuntime, specs.Hook{ 428 Path: cmd, 429 Args: crArgs, 430 Env: os.Environ(), 431 }) 432 argsCopy := append([]string(nil), args...) 433 psArgs := append(argsCopy, "postStop") 434 s.Hooks.Poststop = append(s.Hooks.Poststop, specs.Hook{ 435 Path: cmd, 436 Args: psArgs, 437 Env: os.Environ(), 438 }) 439 return nil 440 }, nil 441 } 442 443 func withContainerLabels(label, labelFile []string) ([]containerd.NewContainerOpts, error) { 444 labelMap, err := readKVStringsMapfFromLabel(label, labelFile) 445 if err != nil { 446 return nil, err 447 } 448 o := containerd.WithAdditionalContainerLabels(labelMap) 449 return []containerd.NewContainerOpts{o}, nil 450 } 451 452 func readKVStringsMapfFromLabel(label, labelFile []string) (map[string]string, error) { 453 labelsMap := strutil.DedupeStrSlice(label) 454 labelsFilePath := strutil.DedupeStrSlice(labelFile) 455 kvStrings, err := dockercliopts.ReadKVStrings(labelsFilePath, labelsMap) 456 if err != nil { 457 return nil, err 458 } 459 return strutil.ConvertKVStringsToMap(kvStrings), nil 460 } 461 462 // parseKVStringsMapFromLogOpt parse log options KV entries and convert to Map 463 func parseKVStringsMapFromLogOpt(logOpt []string, logDriver string) (map[string]string, error) { 464 logOptArray := strutil.DedupeStrSlice(logOpt) 465 logOptMap := strutil.ConvertKVStringsToMap(logOptArray) 466 if logDriver == "json-file" { 467 if _, ok := logOptMap[logging.MaxSize]; !ok { 468 delete(logOptMap, logging.MaxFile) 469 } 470 } 471 if err := logging.ValidateLogOpts(logDriver, logOptMap); err != nil { 472 return nil, err 473 } 474 return logOptMap, nil 475 } 476 477 func withStop(stopSignal string, stopTimeout int, ensuredImage *imgutil.EnsuredImage) containerd.NewContainerOpts { 478 return func(ctx context.Context, _ *containerd.Client, c *containers.Container) error { 479 if c.Labels == nil { 480 c.Labels = make(map[string]string) 481 } 482 var err error 483 if ensuredImage != nil { 484 stopSignal, err = containerd.GetOCIStopSignal(ctx, ensuredImage.Image, stopSignal) 485 if err != nil { 486 return err 487 } 488 } 489 c.Labels[containerd.StopSignalLabel] = stopSignal 490 if stopTimeout != 0 { 491 c.Labels[labels.StopTimeout] = strconv.Itoa(stopTimeout) 492 } 493 return nil 494 } 495 } 496 497 type internalLabels struct { 498 // labels from cmd options 499 namespace string 500 platform string 501 extraHosts []string 502 pidFile string 503 // labels from cmd options or automatically set 504 name string 505 hostname string 506 // automatically generated 507 stateDir string 508 // network 509 networks []string 510 ipAddress string 511 ip6Address string 512 ports []gocni.PortMapping 513 macAddress string 514 // volume 515 mountPoints []*mountutil.Processed 516 anonVolumes []string 517 // pid namespace 518 pidContainer string 519 // log 520 logURI string 521 } 522 523 // WithInternalLabels sets the internal labels for a container. 524 func withInternalLabels(internalLabels internalLabels) (containerd.NewContainerOpts, error) { 525 m := make(map[string]string) 526 m[labels.Namespace] = internalLabels.namespace 527 if internalLabels.name != "" { 528 m[labels.Name] = internalLabels.name 529 } 530 m[labels.Hostname] = internalLabels.hostname 531 extraHostsJSON, err := json.Marshal(internalLabels.extraHosts) 532 if err != nil { 533 return nil, err 534 } 535 m[labels.ExtraHosts] = string(extraHostsJSON) 536 m[labels.StateDir] = internalLabels.stateDir 537 networksJSON, err := json.Marshal(internalLabels.networks) 538 if err != nil { 539 return nil, err 540 } 541 m[labels.Networks] = string(networksJSON) 542 if len(internalLabels.ports) > 0 { 543 portsJSON, err := json.Marshal(internalLabels.ports) 544 if err != nil { 545 return nil, err 546 } 547 m[labels.Ports] = string(portsJSON) 548 } 549 if internalLabels.logURI != "" { 550 m[labels.LogURI] = internalLabels.logURI 551 } 552 if len(internalLabels.anonVolumes) > 0 { 553 anonVolumeJSON, err := json.Marshal(internalLabels.anonVolumes) 554 if err != nil { 555 return nil, err 556 } 557 m[labels.AnonymousVolumes] = string(anonVolumeJSON) 558 } 559 560 if internalLabels.pidFile != "" { 561 m[labels.PIDFile] = internalLabels.pidFile 562 } 563 564 if internalLabels.ipAddress != "" { 565 m[labels.IPAddress] = internalLabels.ipAddress 566 } 567 568 if internalLabels.ip6Address != "" { 569 m[labels.IP6Address] = internalLabels.ip6Address 570 } 571 572 m[labels.Platform], err = platformutil.NormalizeString(internalLabels.platform) 573 if err != nil { 574 return nil, err 575 } 576 577 if len(internalLabels.mountPoints) > 0 { 578 mounts := dockercompatMounts(internalLabels.mountPoints) 579 mountPointsJSON, err := json.Marshal(mounts) 580 if err != nil { 581 return nil, err 582 } 583 m[labels.Mounts] = string(mountPointsJSON) 584 } 585 586 if internalLabels.macAddress != "" { 587 m[labels.MACAddress] = internalLabels.macAddress 588 } 589 590 if internalLabels.pidContainer != "" { 591 m[labels.PIDContainer] = internalLabels.pidContainer 592 } 593 594 return containerd.WithAdditionalContainerLabels(m), nil 595 } 596 597 func dockercompatMounts(mountPoints []*mountutil.Processed) []dockercompat.MountPoint { 598 result := make([]dockercompat.MountPoint, len(mountPoints)) 599 for i := range mountPoints { 600 mp := mountPoints[i] 601 result[i] = dockercompat.MountPoint{ 602 Type: mp.Type, 603 Name: mp.Name, 604 Source: mp.Mount.Source, 605 Destination: mp.Mount.Destination, 606 Driver: "", 607 Mode: mp.Mode, 608 } 609 610 // it's an anonymous volume 611 if mp.AnonymousVolume != "" { 612 result[i].Name = mp.AnonymousVolume 613 } 614 615 // volume only support local driver 616 if mp.Type == "volume" { 617 result[i].Driver = "local" 618 } 619 } 620 return result 621 } 622 623 func processeds(mountPoints []dockercompat.MountPoint) []*mountutil.Processed { 624 result := make([]*mountutil.Processed, len(mountPoints)) 625 for i := range mountPoints { 626 mp := mountPoints[i] 627 result[i] = &mountutil.Processed{ 628 Type: mp.Type, 629 Name: mp.Name, 630 Mount: specs.Mount{ 631 Source: mp.Source, 632 Destination: mp.Destination, 633 }, 634 Mode: mp.Mode, 635 } 636 } 637 return result 638 } 639 640 func propagateContainerdLabelsToOCIAnnotations() oci.SpecOpts { 641 return func(ctx context.Context, oc oci.Client, c *containers.Container, s *oci.Spec) error { 642 return oci.WithAnnotations(c.Labels)(ctx, oc, c, s) 643 } 644 } 645 646 func writeCIDFile(path, id string) error { 647 _, err := os.Stat(path) 648 if err == nil { 649 return fmt.Errorf("container ID file found, make sure the other container isn't running or delete %s", path) 650 } else if errors.Is(err, os.ErrNotExist) { 651 f, err := os.Create(path) 652 if err != nil { 653 return fmt.Errorf("failed to create the container ID file: %s for %s, err: %s", path, id, err) 654 } 655 defer f.Close() 656 657 if _, err := f.WriteString(id); err != nil { 658 return err 659 } 660 return nil 661 } 662 return err 663 } 664 665 // generateLogConfig creates a LogConfig for the current container store 666 func generateLogConfig(dataStore string, id string, logDriver string, logOpt []string, ns string) (logConfig logging.LogConfig, err error) { 667 var u *url.URL 668 if u, err = url.Parse(logDriver); err == nil && u.Scheme != "" { 669 logConfig.LogURI = logDriver 670 } else { 671 logConfig.Driver = logDriver 672 logConfig.Opts, err = parseKVStringsMapFromLogOpt(logOpt, logDriver) 673 if err != nil { 674 return 675 } 676 var ( 677 logDriverInst logging.Driver 678 logConfigB []byte 679 lu *url.URL 680 ) 681 logDriverInst, err = logging.GetDriver(logDriver, logConfig.Opts) 682 if err != nil { 683 return 684 } 685 if err = logDriverInst.Init(dataStore, ns, id); err != nil { 686 return 687 } 688 689 logConfigB, err = json.Marshal(logConfig) 690 if err != nil { 691 return 692 } 693 694 logConfigFilePath := logging.LogConfigFilePath(dataStore, ns, id) 695 if err = os.WriteFile(logConfigFilePath, logConfigB, 0600); err != nil { 696 return 697 } 698 699 lu, err = GenerateLogURI(dataStore) 700 if err != nil { 701 return 702 } 703 if lu != nil { 704 log.L.Debugf("generated log driver: %s", lu.String()) 705 logConfig.LogURI = lu.String() 706 } 707 } 708 return logConfig, nil 709 } 710 711 func generateGcFunc(ctx context.Context, container containerd.Container, ns, id, name, dataStore string, containerErr error, containerNameStore namestore.NameStore, netManager containerutil.NetworkOptionsManager, internalLabels internalLabels) func() { 712 return func() { 713 if containerErr == nil { 714 netGcErr := netManager.CleanupNetworking(ctx, container) 715 if netGcErr != nil { 716 log.G(ctx).WithError(netGcErr).Warnf("failed to revert container %q networking settings", id) 717 } 718 } 719 720 if rmErr := os.RemoveAll(internalLabels.stateDir); rmErr != nil { 721 log.G(ctx).WithError(rmErr).Warnf("failed to remove container %q state dir %q", id, internalLabels.stateDir) 722 } 723 724 if name != "" { 725 var errE error 726 if containerNameStore, errE = namestore.New(dataStore, ns); errE != nil { 727 log.G(ctx).WithError(errE).Warnf("failed to instantiate container name store during cleanup for container %q", id) 728 } 729 if errE = containerNameStore.Release(name, id); errE != nil { 730 log.G(ctx).WithError(errE).Warnf("failed to release container name store for container %q (%s)", name, id) 731 } 732 } 733 } 734 }