github.com/containerd/nerdctl@v1.7.7/cmd/nerdctl/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 main
    18  
    19  import (
    20  	"fmt"
    21  	"runtime"
    22  
    23  	"github.com/containerd/nerdctl/pkg/api/types"
    24  	"github.com/containerd/nerdctl/pkg/clientutil"
    25  	"github.com/containerd/nerdctl/pkg/cmd/container"
    26  	"github.com/containerd/nerdctl/pkg/containerutil"
    27  	"github.com/spf13/cobra"
    28  )
    29  
    30  func newCreateCommand() *cobra.Command {
    31  	shortHelp := "Create a new container. Optionally specify \"ipfs://\" or \"ipns://\" scheme to pull image from IPFS."
    32  	longHelp := shortHelp
    33  	switch runtime.GOOS {
    34  	case "windows":
    35  		longHelp += "\n"
    36  		longHelp += "WARNING: `nerdctl create` is experimental on Windows and currently broken (https://github.com/containerd/nerdctl/issues/28)"
    37  	case "freebsd":
    38  		longHelp += "\n"
    39  		longHelp += "WARNING: `nerdctl create` is experimental on FreeBSD and currently requires `--net=none` (https://github.com/containerd/nerdctl/blob/main/docs/freebsd.md)"
    40  	}
    41  	var createCommand = &cobra.Command{
    42  		Use:               "create [flags] IMAGE [COMMAND] [ARG...]",
    43  		Args:              cobra.MinimumNArgs(1),
    44  		Short:             shortHelp,
    45  		Long:              longHelp,
    46  		RunE:              createAction,
    47  		ValidArgsFunction: runShellComplete,
    48  		SilenceUsage:      true,
    49  		SilenceErrors:     true,
    50  	}
    51  	createCommand.Flags().SetInterspersed(false)
    52  	setCreateFlags(createCommand)
    53  	return createCommand
    54  }
    55  
    56  func processContainerCreateOptions(cmd *cobra.Command) (opt types.ContainerCreateOptions, err error) {
    57  	opt.Stdout = cmd.OutOrStdout()
    58  	opt.Stderr = cmd.ErrOrStderr()
    59  	opt.GOptions, err = processRootCmdFlags(cmd)
    60  	if err != nil {
    61  		return
    62  	}
    63  
    64  	opt.NerdctlCmd, opt.NerdctlArgs = globalFlags(cmd)
    65  
    66  	// #region for basic flags
    67  	// The command `container start` doesn't support the flag `--interactive`. Set the default value of `opt.Interactive` false.
    68  	opt.Interactive = false
    69  	opt.TTY, err = cmd.Flags().GetBool("tty")
    70  	if err != nil {
    71  		return
    72  	}
    73  	// The nerdctl create command similar to nerdctl run -d except the container is never started.
    74  	// So we keep the default value of `opt.Detach` true.
    75  	opt.Detach = true
    76  	opt.Restart, err = cmd.Flags().GetString("restart")
    77  	if err != nil {
    78  		return
    79  	}
    80  	opt.Rm, err = cmd.Flags().GetBool("rm")
    81  	if err != nil {
    82  		return
    83  	}
    84  	opt.Pull, err = cmd.Flags().GetString("pull")
    85  	if err != nil {
    86  		return
    87  	}
    88  	opt.Pid, err = cmd.Flags().GetString("pid")
    89  	if err != nil {
    90  		return
    91  	}
    92  	opt.StopSignal, err = cmd.Flags().GetString("stop-signal")
    93  	if err != nil {
    94  		return
    95  	}
    96  	opt.StopTimeout, err = cmd.Flags().GetInt("stop-timeout")
    97  	if err != nil {
    98  		return
    99  	}
   100  	// #endregion
   101  
   102  	// #region for platform flags
   103  	opt.Platform, err = cmd.Flags().GetString("platform")
   104  	if err != nil {
   105  		return
   106  	}
   107  	// #endregion
   108  
   109  	// #region for init process flags
   110  	opt.InitProcessFlag, err = cmd.Flags().GetBool("init")
   111  	if err != nil {
   112  		return
   113  	}
   114  	if opt.InitProcessFlag || cmd.Flags().Changed("init-binary") {
   115  		var initBinary string
   116  		initBinary, err = cmd.Flags().GetString("init-binary")
   117  		if err != nil {
   118  			return
   119  		}
   120  		opt.InitBinary = &initBinary
   121  	}
   122  	// #endregion
   123  
   124  	// #region for isolation flags
   125  	opt.Isolation, err = cmd.Flags().GetString("isolation")
   126  	if err != nil {
   127  		return
   128  	}
   129  	// #endregion
   130  
   131  	// #region for resource flags
   132  	opt.CPUs, err = cmd.Flags().GetFloat64("cpus")
   133  	if err != nil {
   134  		return
   135  	}
   136  	opt.CPUQuota, err = cmd.Flags().GetInt64("cpu-quota")
   137  	if err != nil {
   138  		return
   139  	}
   140  	opt.CPUPeriod, err = cmd.Flags().GetUint64("cpu-period")
   141  	if err != nil {
   142  		return
   143  	}
   144  	opt.CPUShares, err = cmd.Flags().GetUint64("cpu-shares")
   145  	if err != nil {
   146  		return
   147  	}
   148  	opt.CPUSetCPUs, err = cmd.Flags().GetString("cpuset-cpus")
   149  	if err != nil {
   150  		return
   151  	}
   152  	opt.CPUSetMems, err = cmd.Flags().GetString("cpuset-mems")
   153  	if err != nil {
   154  		return
   155  	}
   156  	opt.Memory, err = cmd.Flags().GetString("memory")
   157  	if err != nil {
   158  		return
   159  	}
   160  	opt.MemoryReservationChanged = cmd.Flags().Changed("memory-reservation")
   161  	opt.MemoryReservation, err = cmd.Flags().GetString("memory-reservation")
   162  	if err != nil {
   163  		return
   164  	}
   165  	opt.MemorySwap, err = cmd.Flags().GetString("memory-swap")
   166  	if err != nil {
   167  		return
   168  	}
   169  	opt.MemorySwappiness64Changed = cmd.Flags().Changed("memory-swappiness")
   170  	opt.MemorySwappiness64, err = cmd.Flags().GetInt64("memory-swappiness")
   171  	if err != nil {
   172  		return
   173  	}
   174  	opt.KernelMemoryChanged = cmd.Flag("kernel-memory").Changed
   175  	opt.KernelMemory, err = cmd.Flags().GetString("kernel-memory")
   176  	if err != nil {
   177  		return
   178  	}
   179  	opt.OomKillDisable, err = cmd.Flags().GetBool("oom-kill-disable")
   180  	if err != nil {
   181  		return
   182  	}
   183  	opt.OomScoreAdjChanged = cmd.Flags().Changed("oom-score-adj")
   184  	opt.OomScoreAdj, err = cmd.Flags().GetInt("oom-score-adj")
   185  	if err != nil {
   186  		return
   187  	}
   188  	opt.PidsLimit, err = cmd.Flags().GetInt64("pids-limit")
   189  	if err != nil {
   190  		return
   191  	}
   192  	opt.CgroupConf, err = cmd.Flags().GetStringSlice("cgroup-conf")
   193  	if err != nil {
   194  		return
   195  	}
   196  	opt.BlkioWeight, err = cmd.Flags().GetUint16("blkio-weight")
   197  	if err != nil {
   198  		return
   199  	}
   200  	opt.Cgroupns, err = cmd.Flags().GetString("cgroupns")
   201  	if err != nil {
   202  		return
   203  	}
   204  	opt.CgroupParent, err = cmd.Flags().GetString("cgroup-parent")
   205  	if err != nil {
   206  		return
   207  	}
   208  	opt.Device, err = cmd.Flags().GetStringSlice("device")
   209  	if err != nil {
   210  		return
   211  	}
   212  	// #endregion
   213  
   214  	// #region for intel RDT flags
   215  	opt.RDTClass, err = cmd.Flags().GetString("rdt-class")
   216  	if err != nil {
   217  		return
   218  	}
   219  	// #endregion
   220  
   221  	// #region for user flags
   222  	// If user is set we will attempt to start container with that user (must be present on the host)
   223  	// Otherwise we will inherit permissions from the user that the containerd process is running as
   224  	opt.User, err = cmd.Flags().GetString("user")
   225  	if err != nil {
   226  		return
   227  	}
   228  	opt.Umask = ""
   229  	if cmd.Flags().Changed("umask") {
   230  		opt.Umask, err = cmd.Flags().GetString("umask")
   231  		if err != nil {
   232  			return
   233  		}
   234  	}
   235  	opt.GroupAdd, err = cmd.Flags().GetStringSlice("group-add")
   236  	if err != nil {
   237  		return
   238  	}
   239  	// #endregion
   240  
   241  	// #region for security flags
   242  	opt.SecurityOpt, err = cmd.Flags().GetStringArray("security-opt")
   243  	if err != nil {
   244  		return
   245  	}
   246  	opt.CapAdd, err = cmd.Flags().GetStringSlice("cap-add")
   247  	if err != nil {
   248  		return
   249  	}
   250  	opt.CapDrop, err = cmd.Flags().GetStringSlice("cap-drop")
   251  	if err != nil {
   252  		return
   253  	}
   254  	opt.Privileged, err = cmd.Flags().GetBool("privileged")
   255  	if err != nil {
   256  		return
   257  	}
   258  	// #endregion
   259  
   260  	// #region for runtime flags
   261  	opt.Runtime, err = cmd.Flags().GetString("runtime")
   262  	if err != nil {
   263  		return
   264  	}
   265  	opt.Sysctl, err = cmd.Flags().GetStringArray("sysctl")
   266  	if err != nil {
   267  		return
   268  	}
   269  	// #endregion
   270  
   271  	// #region for volume flags
   272  	opt.Volume, err = cmd.Flags().GetStringArray("volume")
   273  	if err != nil {
   274  		return
   275  	}
   276  	// tmpfs needs to be StringArray, not StringSlice, to prevent "/foo:size=64m,exec" from being split to {"/foo:size=64m", "exec"}
   277  	opt.Tmpfs, err = cmd.Flags().GetStringArray("tmpfs")
   278  	if err != nil {
   279  		return
   280  	}
   281  	opt.Mount, err = cmd.Flags().GetStringArray("mount")
   282  	if err != nil {
   283  		return
   284  	}
   285  	opt.VolumesFrom, err = cmd.Flags().GetStringArray("volumes-from")
   286  	if err != nil {
   287  		return
   288  	}
   289  	// #endregion
   290  
   291  	// #region for rootfs flags
   292  	opt.ReadOnly, err = cmd.Flags().GetBool("read-only")
   293  	if err != nil {
   294  		return
   295  	}
   296  	opt.Rootfs, err = cmd.Flags().GetBool("rootfs")
   297  	if err != nil {
   298  		return
   299  	}
   300  	// #endregion
   301  
   302  	// #region for env flags
   303  	opt.EntrypointChanged = cmd.Flags().Changed("entrypoint")
   304  	opt.Entrypoint, err = cmd.Flags().GetStringArray("entrypoint")
   305  	if err != nil {
   306  		return
   307  	}
   308  	opt.Workdir, err = cmd.Flags().GetString("workdir")
   309  	if err != nil {
   310  		return
   311  	}
   312  	opt.Env, err = cmd.Flags().GetStringArray("env")
   313  	if err != nil {
   314  		return
   315  	}
   316  	opt.EnvFile, err = cmd.Flags().GetStringSlice("env-file")
   317  	if err != nil {
   318  		return
   319  	}
   320  	// #endregion
   321  
   322  	// #region for metadata flags
   323  	opt.NameChanged = cmd.Flags().Changed("name")
   324  	opt.Name, err = cmd.Flags().GetString("name")
   325  	if err != nil {
   326  		return
   327  	}
   328  	opt.Label, err = cmd.Flags().GetStringArray("label")
   329  	if err != nil {
   330  		return
   331  	}
   332  	opt.LabelFile, err = cmd.Flags().GetStringSlice("label-file")
   333  	if err != nil {
   334  		return
   335  	}
   336  	opt.CidFile, err = cmd.Flags().GetString("cidfile")
   337  	if err != nil {
   338  		return
   339  	}
   340  	opt.PidFile = ""
   341  	if cmd.Flags().Changed("pidfile") {
   342  		opt.PidFile, err = cmd.Flags().GetString("pidfile")
   343  		if err != nil {
   344  			return
   345  		}
   346  	}
   347  	// #endregion
   348  
   349  	// #region for logging flags
   350  	// json-file is the built-in and default log driver for nerdctl
   351  	opt.LogDriver, err = cmd.Flags().GetString("log-driver")
   352  	if err != nil {
   353  		return
   354  	}
   355  	opt.LogOpt, err = cmd.Flags().GetStringArray("log-opt")
   356  	if err != nil {
   357  		return
   358  	}
   359  	// #endregion
   360  
   361  	// #region for shared memory flags
   362  	opt.IPC, err = cmd.Flags().GetString("ipc")
   363  	if err != nil {
   364  		return
   365  	}
   366  	opt.ShmSize, err = cmd.Flags().GetString("shm-size")
   367  	if err != nil {
   368  		return
   369  	}
   370  	// #endregion
   371  
   372  	// #region for gpu flags
   373  	opt.GPUs, err = cmd.Flags().GetStringArray("gpus")
   374  	if err != nil {
   375  		return
   376  	}
   377  	// #endregion
   378  
   379  	// #region for ulimit flags
   380  	opt.Ulimit, err = cmd.Flags().GetStringSlice("ulimit")
   381  	if err != nil {
   382  		return
   383  	}
   384  	// #endregion
   385  
   386  	// #region for ipfs flags
   387  	opt.IPFSAddress, err = cmd.Flags().GetString("ipfs-address")
   388  	if err != nil {
   389  		return
   390  	}
   391  	// #endregion
   392  
   393  	// #region for image pull and verify options
   394  	imageVerifyOpt, err := processImageVerifyOptions(cmd)
   395  	if err != nil {
   396  		return
   397  	}
   398  	opt.ImagePullOpt = types.ImagePullOptions{
   399  		GOptions:      opt.GOptions,
   400  		VerifyOptions: imageVerifyOpt,
   401  		IPFSAddress:   opt.IPFSAddress,
   402  		Stdout:        opt.Stdout,
   403  		Stderr:        opt.Stderr,
   404  	}
   405  	// #endregion
   406  
   407  	return opt, nil
   408  }
   409  
   410  func createAction(cmd *cobra.Command, args []string) error {
   411  	createOpt, err := processContainerCreateOptions(cmd)
   412  	if err != nil {
   413  		return err
   414  	}
   415  
   416  	if (createOpt.Platform == "windows" || createOpt.Platform == "freebsd") && !createOpt.GOptions.Experimental {
   417  		return fmt.Errorf("%s requires experimental mode to be enabled", createOpt.Platform)
   418  	}
   419  	client, ctx, cancel, err := clientutil.NewClientWithPlatform(cmd.Context(), createOpt.GOptions.Namespace, createOpt.GOptions.Address, createOpt.Platform)
   420  	if err != nil {
   421  		return err
   422  	}
   423  	defer cancel()
   424  
   425  	netFlags, err := loadNetworkFlags(cmd)
   426  	if err != nil {
   427  		return fmt.Errorf("failed to load networking flags: %s", err)
   428  	}
   429  
   430  	netManager, err := containerutil.NewNetworkingOptionsManager(createOpt.GOptions, netFlags, client)
   431  	if err != nil {
   432  		return err
   433  	}
   434  
   435  	c, gc, err := container.Create(ctx, client, args, netManager, createOpt)
   436  	if err != nil {
   437  		if gc != nil {
   438  			gc()
   439  		}
   440  		return err
   441  	}
   442  	// defer setting `nerdctl/error` label in case of error
   443  	defer func() {
   444  		if err != nil {
   445  			containerutil.UpdateErrorLabel(ctx, c, err)
   446  		}
   447  	}()
   448  
   449  	fmt.Fprintln(createOpt.Stdout, c.ID())
   450  	return nil
   451  }