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  }