github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/cmd/podman/shared/create.go (about)

     1  package shared
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"path/filepath"
    10  	goruntime "runtime"
    11  	"strconv"
    12  	"strings"
    13  	"syscall"
    14  	"time"
    15  
    16  	"github.com/containers/image/v5/manifest"
    17  	"github.com/containers/libpod/cmd/podman/shared/parse"
    18  	"github.com/containers/libpod/libpod"
    19  	"github.com/containers/libpod/libpod/image"
    20  	ann "github.com/containers/libpod/pkg/annotations"
    21  	"github.com/containers/libpod/pkg/autoupdate"
    22  	envLib "github.com/containers/libpod/pkg/env"
    23  	"github.com/containers/libpod/pkg/errorhandling"
    24  	"github.com/containers/libpod/pkg/inspect"
    25  	ns "github.com/containers/libpod/pkg/namespaces"
    26  	"github.com/containers/libpod/pkg/rootless"
    27  	"github.com/containers/libpod/pkg/seccomp"
    28  	cc "github.com/containers/libpod/pkg/spec"
    29  	systemdGen "github.com/containers/libpod/pkg/systemd/generate"
    30  	"github.com/containers/libpod/pkg/util"
    31  	"github.com/docker/go-connections/nat"
    32  	"github.com/docker/go-units"
    33  	"github.com/opentracing/opentracing-go"
    34  	"github.com/pkg/errors"
    35  	"github.com/sirupsen/logrus"
    36  )
    37  
    38  func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.Runtime) (*libpod.Container, *cc.CreateConfig, error) {
    39  	var (
    40  		healthCheck *manifest.Schema2HealthConfig
    41  		err         error
    42  		cidFile     *os.File
    43  	)
    44  	if c.Bool("trace") {
    45  		span, _ := opentracing.StartSpanFromContext(ctx, "createContainer")
    46  		defer span.Finish()
    47  	}
    48  	if c.Bool("rm") && c.String("restart") != "" && c.String("restart") != "no" {
    49  		return nil, nil, errors.Errorf("the --rm option conflicts with --restart")
    50  	}
    51  
    52  	rtc, err := runtime.GetConfig()
    53  	if err != nil {
    54  		return nil, nil, err
    55  	}
    56  	rootfs := ""
    57  	if c.Bool("rootfs") {
    58  		rootfs = c.InputArgs[0]
    59  	}
    60  
    61  	if c.IsSet("cidfile") {
    62  		cidFile, err = util.OpenExclusiveFile(c.String("cidfile"))
    63  		if err != nil && os.IsExist(err) {
    64  			return nil, nil, errors.Errorf("container id file exists. Ensure another container is not using it or delete %s", c.String("cidfile"))
    65  		}
    66  		if err != nil {
    67  			return nil, nil, errors.Errorf("error opening cidfile %s", c.String("cidfile"))
    68  		}
    69  		defer errorhandling.CloseQuiet(cidFile)
    70  		defer errorhandling.SyncQuiet(cidFile)
    71  	}
    72  
    73  	imageName := ""
    74  	rawImageName := ""
    75  	var imageData *inspect.ImageData = nil
    76  
    77  	// Set the storage if there is no rootfs specified
    78  	if rootfs == "" {
    79  		var writer io.Writer
    80  		if !c.Bool("quiet") {
    81  			writer = os.Stderr
    82  		}
    83  
    84  		if len(c.InputArgs) != 0 {
    85  			rawImageName = c.InputArgs[0]
    86  		} else {
    87  			return nil, nil, errors.Errorf("error, image name not provided")
    88  		}
    89  
    90  		pullType, err := util.ValidatePullType(c.String("pull"))
    91  		if err != nil {
    92  			return nil, nil, err
    93  		}
    94  
    95  		overrideOS := c.String("override-os")
    96  		overrideArch := c.String("override-arch")
    97  		dockerRegistryOptions := image.DockerRegistryOptions{
    98  			OSChoice:           overrideOS,
    99  			ArchitectureChoice: overrideArch,
   100  		}
   101  
   102  		newImage, err := runtime.ImageRuntime().New(ctx, rawImageName, rtc.Engine.SignaturePolicyPath, c.String("authfile"), writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullType)
   103  		if err != nil {
   104  			return nil, nil, err
   105  		}
   106  		imageData, err = newImage.InspectNoSize(ctx)
   107  		if err != nil {
   108  			return nil, nil, err
   109  		}
   110  
   111  		if overrideOS == "" && imageData.Os != goruntime.GOOS {
   112  			logrus.Infof("Using %q (OS) image on %q host", imageData.Os, goruntime.GOOS)
   113  		}
   114  
   115  		if overrideArch == "" && imageData.Architecture != goruntime.GOARCH {
   116  			logrus.Infof("Using %q (architecture) on %q host", imageData.Architecture, goruntime.GOARCH)
   117  		}
   118  
   119  		names := newImage.Names()
   120  		if len(names) > 0 {
   121  			imageName = names[0]
   122  		} else {
   123  			imageName = newImage.ID()
   124  		}
   125  
   126  		// if the user disabled the healthcheck with "none" or the no-healthcheck
   127  		// options is provided, we skip adding it
   128  		healthCheckCommandInput := c.String("healthcheck-command")
   129  
   130  		// the user didn't disable the healthcheck but did pass in a healthcheck command
   131  		// now we need to make a healthcheck from the commandline input
   132  		if healthCheckCommandInput != "none" && !c.Bool("no-healthcheck") {
   133  			if len(healthCheckCommandInput) > 0 {
   134  				healthCheck, err = makeHealthCheckFromCli(c)
   135  				if err != nil {
   136  					return nil, nil, errors.Wrapf(err, "unable to create healthcheck")
   137  				}
   138  			} else {
   139  				// the user did not disable the health check and did not pass in a healthcheck
   140  				// command as input.  so now we add healthcheck if it exists AND is correct mediatype
   141  				_, mediaType, err := newImage.Manifest(ctx)
   142  				if err != nil {
   143  					return nil, nil, errors.Wrapf(err, "unable to determine mediatype of image %s", newImage.ID())
   144  				}
   145  				if mediaType == manifest.DockerV2Schema2MediaType {
   146  					healthCheck, err = newImage.GetHealthCheck(ctx)
   147  					if err != nil {
   148  						return nil, nil, errors.Wrapf(err, "unable to get healthcheck for %s", c.InputArgs[0])
   149  					}
   150  
   151  					if healthCheck != nil {
   152  						hcCommand := healthCheck.Test
   153  						if len(hcCommand) < 1 || hcCommand[0] == "" || hcCommand[0] == "NONE" {
   154  							// disable health check
   155  							healthCheck = nil
   156  						} else {
   157  							// apply defaults if image doesn't override them
   158  							if healthCheck.Interval == 0 {
   159  								healthCheck.Interval = 30 * time.Second
   160  							}
   161  							if healthCheck.Timeout == 0 {
   162  								healthCheck.Timeout = 30 * time.Second
   163  							}
   164  							/* Docker default is 0s, so the following would be a no-op
   165  							if healthCheck.StartPeriod == 0 {
   166  								healthCheck.StartPeriod = 0 * time.Second
   167  							}
   168  							*/
   169  							if healthCheck.Retries == 0 {
   170  								healthCheck.Retries = 3
   171  							}
   172  						}
   173  					}
   174  				}
   175  			}
   176  		}
   177  	}
   178  
   179  	createConfig, err := ParseCreateOpts(ctx, c, runtime, imageName, rawImageName, imageData)
   180  	if err != nil {
   181  		return nil, nil, err
   182  	}
   183  
   184  	// (VR): Ideally we perform the checks _before_ pulling the image but that
   185  	// would require some bigger code refactoring of `ParseCreateOpts` and the
   186  	// logic here.  But as the creation code will be consolidated in the future
   187  	// and given auto updates are experimental, we can live with that for now.
   188  	// In the end, the user may only need to correct the policy or the raw image
   189  	// name.
   190  	autoUpdatePolicy, autoUpdatePolicySpecified := createConfig.Labels[autoupdate.Label]
   191  	if autoUpdatePolicySpecified {
   192  		if _, err := autoupdate.LookupPolicy(autoUpdatePolicy); err != nil {
   193  			return nil, nil, err
   194  		}
   195  		// Now we need to make sure we're having a fully-qualified image reference.
   196  		if rootfs != "" {
   197  			return nil, nil, errors.Errorf("auto updates do not work with --rootfs")
   198  		}
   199  		// Make sure the input image is a docker.
   200  		if err := autoupdate.ValidateImageReference(rawImageName); err != nil {
   201  			return nil, nil, err
   202  		}
   203  	}
   204  
   205  	// Because parseCreateOpts does derive anything from the image, we add health check
   206  	// at this point. The rest is done by WithOptions.
   207  	createConfig.HealthCheck = healthCheck
   208  
   209  	// TODO: Should be able to return this from ParseCreateOpts
   210  	var pod *libpod.Pod
   211  	if createConfig.Pod != "" {
   212  		pod, err = runtime.LookupPod(createConfig.Pod)
   213  		if err != nil {
   214  			return nil, nil, errors.Wrapf(err, "error looking up pod to join")
   215  		}
   216  	}
   217  
   218  	ctr, err := CreateContainerFromCreateConfig(runtime, createConfig, ctx, pod)
   219  	if err != nil {
   220  		return nil, nil, err
   221  	}
   222  	if cidFile != nil {
   223  		_, err = cidFile.WriteString(ctr.ID())
   224  		if err != nil {
   225  			logrus.Error(err)
   226  		}
   227  
   228  	}
   229  
   230  	logrus.Debugf("New container created %q", ctr.ID())
   231  	return ctr, createConfig, nil
   232  }
   233  
   234  func configureEntrypoint(c *GenericCLIResults, data *inspect.ImageData) []string {
   235  	entrypoint := []string{}
   236  	if c.IsSet("entrypoint") {
   237  		// Force entrypoint to ""
   238  		if c.String("entrypoint") == "" {
   239  			return entrypoint
   240  		}
   241  		// Check if entrypoint specified is json
   242  		if err := json.Unmarshal([]byte(c.String("entrypoint")), &entrypoint); err == nil {
   243  			return entrypoint
   244  		}
   245  		// Return entrypoint as a single command
   246  		return []string{c.String("entrypoint")}
   247  	}
   248  	if data != nil {
   249  		return data.Config.Entrypoint
   250  	}
   251  	return entrypoint
   252  }
   253  
   254  func configurePod(c *GenericCLIResults, runtime *libpod.Runtime, namespaces map[string]string, podName string) (map[string]string, string, error) {
   255  	pod, err := runtime.LookupPod(podName)
   256  	if err != nil {
   257  		return namespaces, "", err
   258  	}
   259  	podInfraID, err := pod.InfraContainerID()
   260  	if err != nil {
   261  		return namespaces, "", err
   262  	}
   263  	hasUserns := false
   264  	if podInfraID != "" {
   265  		podCtr, err := runtime.GetContainer(podInfraID)
   266  		if err != nil {
   267  			return namespaces, "", err
   268  		}
   269  		mappings, err := podCtr.IDMappings()
   270  		if err != nil {
   271  			return namespaces, "", err
   272  		}
   273  		hasUserns = len(mappings.UIDMap) > 0
   274  	}
   275  
   276  	if (namespaces["pid"] == cc.Pod) || (!c.IsSet("pid") && pod.SharesPID()) {
   277  		namespaces["pid"] = fmt.Sprintf("container:%s", podInfraID)
   278  	}
   279  	if (namespaces["net"] == cc.Pod) || (!c.IsSet("net") && !c.IsSet("network") && pod.SharesNet()) {
   280  		namespaces["net"] = fmt.Sprintf("container:%s", podInfraID)
   281  	}
   282  	if hasUserns && (namespaces["user"] == cc.Pod) || (!c.IsSet("user") && pod.SharesUser()) {
   283  		namespaces["user"] = fmt.Sprintf("container:%s", podInfraID)
   284  	}
   285  	if (namespaces["ipc"] == cc.Pod) || (!c.IsSet("ipc") && pod.SharesIPC()) {
   286  		namespaces["ipc"] = fmt.Sprintf("container:%s", podInfraID)
   287  	}
   288  	if (namespaces["uts"] == cc.Pod) || (!c.IsSet("uts") && pod.SharesUTS()) {
   289  		namespaces["uts"] = fmt.Sprintf("container:%s", podInfraID)
   290  	}
   291  	return namespaces, podInfraID, nil
   292  }
   293  
   294  // Parses CLI options related to container creation into a config which can be
   295  // parsed into an OCI runtime spec
   296  func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.Runtime, imageName string, rawImageName string, data *inspect.ImageData) (*cc.CreateConfig, error) {
   297  	var (
   298  		inputCommand, command                                    []string
   299  		memoryLimit, memoryReservation, memorySwap, memoryKernel int64
   300  		blkioWeight                                              uint16
   301  		namespaces                                               map[string]string
   302  	)
   303  
   304  	idmappings, err := util.ParseIDMapping(ns.UsernsMode(c.String("userns")), c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidname"), c.String("subgidname"))
   305  	if err != nil {
   306  		return nil, err
   307  	}
   308  
   309  	imageID := ""
   310  
   311  	inputCommand = c.InputArgs[1:]
   312  	if data != nil {
   313  		imageID = data.ID
   314  	}
   315  
   316  	rootfs := ""
   317  	if c.Bool("rootfs") {
   318  		rootfs = c.InputArgs[0]
   319  	}
   320  
   321  	if c.String("memory") != "" {
   322  		memoryLimit, err = units.RAMInBytes(c.String("memory"))
   323  		if err != nil {
   324  			return nil, errors.Wrapf(err, "invalid value for memory")
   325  		}
   326  	}
   327  	if c.String("memory-reservation") != "" {
   328  		memoryReservation, err = units.RAMInBytes(c.String("memory-reservation"))
   329  		if err != nil {
   330  			return nil, errors.Wrapf(err, "invalid value for memory-reservation")
   331  		}
   332  	}
   333  	if c.String("memory-swap") != "" {
   334  		if c.String("memory-swap") == "-1" {
   335  			memorySwap = -1
   336  		} else {
   337  			memorySwap, err = units.RAMInBytes(c.String("memory-swap"))
   338  			if err != nil {
   339  				return nil, errors.Wrapf(err, "invalid value for memory-swap")
   340  			}
   341  		}
   342  	}
   343  	if c.String("kernel-memory") != "" {
   344  		memoryKernel, err = units.RAMInBytes(c.String("kernel-memory"))
   345  		if err != nil {
   346  			return nil, errors.Wrapf(err, "invalid value for kernel-memory")
   347  		}
   348  	}
   349  	if c.String("blkio-weight") != "" {
   350  		u, err := strconv.ParseUint(c.String("blkio-weight"), 10, 16)
   351  		if err != nil {
   352  			return nil, errors.Wrapf(err, "invalid value for blkio-weight")
   353  		}
   354  		blkioWeight = uint16(u)
   355  	}
   356  
   357  	tty := c.Bool("tty")
   358  
   359  	if c.Changed("cpu-period") && c.Changed("cpus") {
   360  		return nil, errors.Errorf("--cpu-period and --cpus cannot be set together")
   361  	}
   362  	if c.Changed("cpu-quota") && c.Changed("cpus") {
   363  		return nil, errors.Errorf("--cpu-quota and --cpus cannot be set together")
   364  	}
   365  
   366  	if c.Bool("no-hosts") && c.Changed("add-host") {
   367  		return nil, errors.Errorf("--no-hosts and --add-host cannot be set together")
   368  	}
   369  
   370  	// EXPOSED PORTS
   371  	var portBindings map[nat.Port][]nat.PortBinding
   372  	if data != nil {
   373  		portBindings, err = cc.ExposedPorts(c.StringSlice("expose"), c.StringSlice("publish"), c.Bool("publish-all"), data.Config.ExposedPorts)
   374  		if err != nil {
   375  			return nil, err
   376  		}
   377  	}
   378  
   379  	usernsType := c.String("userns")
   380  	if !c.IsSet("userns") && !idmappings.HostUIDMapping {
   381  		usernsType = "private"
   382  	}
   383  	// Kernel Namespaces
   384  	// TODO Fix handling of namespace from pod
   385  	// Instead of integrating here, should be done in libpod
   386  	// However, that also involves setting up security opts
   387  	// when the pod's namespace is integrated
   388  	namespaces = map[string]string{
   389  		"cgroup": c.String("cgroupns"),
   390  		"pid":    c.String("pid"),
   391  		"net":    c.String("network"),
   392  		"ipc":    c.String("ipc"),
   393  		"user":   usernsType,
   394  		"uts":    c.String("uts"),
   395  	}
   396  
   397  	originalPodName := c.String("pod")
   398  	podName := strings.Replace(originalPodName, "new:", "", 1)
   399  	// after we strip out :new, make sure there is something left for a pod name
   400  	if len(podName) < 1 && c.IsSet("pod") {
   401  		return nil, errors.Errorf("new pod name must be at least one character")
   402  	}
   403  
   404  	// If we are adding a container to a pod, we would like to add an annotation for the infra ID
   405  	// so kata containers can share VMs inside the pod
   406  	var podInfraID string
   407  	if c.IsSet("pod") {
   408  		if strings.HasPrefix(originalPodName, "new:") {
   409  			// pod does not exist; lets make it
   410  			var podOptions []libpod.PodCreateOption
   411  			podOptions = append(podOptions, libpod.WithPodName(podName), libpod.WithInfraContainer(), libpod.WithPodCgroups())
   412  			if len(portBindings) > 0 {
   413  				ociPortBindings, err := cc.NatToOCIPortBindings(portBindings)
   414  				if err != nil {
   415  					return nil, err
   416  				}
   417  				podOptions = append(podOptions, libpod.WithInfraContainerPorts(ociPortBindings))
   418  			}
   419  
   420  			podNsOptions, err := GetNamespaceOptions(strings.Split(DefaultKernelNamespaces, ","))
   421  			if err != nil {
   422  				return nil, err
   423  			}
   424  			podOptions = append(podOptions, podNsOptions...)
   425  			// make pod
   426  			pod, err := runtime.NewPod(ctx, podOptions...)
   427  			if err != nil {
   428  				return nil, err
   429  			}
   430  			logrus.Debugf("pod %s created by new container request", pod.ID())
   431  
   432  			// The container now cannot have port bindings; so we reset the map
   433  			portBindings = make(map[nat.Port][]nat.PortBinding)
   434  		}
   435  		namespaces, podInfraID, err = configurePod(c, runtime, namespaces, podName)
   436  		if err != nil {
   437  			return nil, err
   438  		}
   439  	}
   440  
   441  	pidMode := ns.PidMode(namespaces["pid"])
   442  	if !cc.Valid(string(pidMode), pidMode) {
   443  		return nil, errors.Errorf("--pid %q is not valid", c.String("pid"))
   444  	}
   445  
   446  	usernsMode := ns.UsernsMode(namespaces["user"])
   447  	if !cc.Valid(string(usernsMode), usernsMode) {
   448  		return nil, errors.Errorf("--userns %q is not valid", namespaces["user"])
   449  	}
   450  
   451  	utsMode := ns.UTSMode(namespaces["uts"])
   452  	if !cc.Valid(string(utsMode), utsMode) {
   453  		return nil, errors.Errorf("--uts %q is not valid", namespaces["uts"])
   454  	}
   455  
   456  	cgroupMode := ns.CgroupMode(namespaces["cgroup"])
   457  	if !cgroupMode.Valid() {
   458  		return nil, errors.Errorf("--cgroup %q is not valid", namespaces["cgroup"])
   459  	}
   460  
   461  	ipcMode := ns.IpcMode(namespaces["ipc"])
   462  	if !cc.Valid(string(ipcMode), ipcMode) {
   463  		return nil, errors.Errorf("--ipc %q is not valid", ipcMode)
   464  	}
   465  
   466  	// Make sure if network is set to container namespace, port binding is not also being asked for
   467  	netMode := ns.NetworkMode(namespaces["net"])
   468  	if netMode.IsContainer() {
   469  		if len(portBindings) > 0 {
   470  			return nil, errors.Errorf("cannot set port bindings on an existing container network namespace")
   471  		}
   472  	}
   473  
   474  	// USER
   475  	user := c.String("user")
   476  	if user == "" {
   477  		switch {
   478  		case usernsMode.IsKeepID():
   479  			user = fmt.Sprintf("%d:%d", rootless.GetRootlessUID(), rootless.GetRootlessGID())
   480  		case data == nil:
   481  			user = "0"
   482  		default:
   483  			user = data.Config.User
   484  		}
   485  	}
   486  
   487  	// STOP SIGNAL
   488  	stopSignal := syscall.SIGTERM
   489  	signalString := ""
   490  	if data != nil {
   491  		signalString = data.Config.StopSignal
   492  	}
   493  	if c.IsSet("stop-signal") {
   494  		signalString = c.String("stop-signal")
   495  	}
   496  	if signalString != "" {
   497  		stopSignal, err = util.ParseSignal(signalString)
   498  		if err != nil {
   499  			return nil, err
   500  		}
   501  	}
   502  
   503  	// ENVIRONMENT VARIABLES
   504  	//
   505  	// Precedence order (higher index wins):
   506  	//  1) env-host, 2) image data, 3) env-file, 4) env
   507  	env := map[string]string{
   508  		"container": "podman",
   509  	}
   510  
   511  	// First transform the os env into a map. We need it for the labels later in
   512  	// any case.
   513  	osEnv, err := envLib.ParseSlice(os.Environ())
   514  	if err != nil {
   515  		return nil, errors.Wrap(err, "error parsing host environment variables")
   516  	}
   517  
   518  	// Start with env-host
   519  
   520  	if c.Bool("env-host") {
   521  		env = envLib.Join(env, osEnv)
   522  	}
   523  
   524  	// Image data overrides any previous variables
   525  	if data != nil {
   526  		configEnv, err := envLib.ParseSlice(data.Config.Env)
   527  		if err != nil {
   528  			return nil, errors.Wrap(err, "error passing image environment variables")
   529  		}
   530  		env = envLib.Join(env, configEnv)
   531  	}
   532  
   533  	// env-file overrides any previous variables
   534  	if c.IsSet("env-file") {
   535  		for _, f := range c.StringSlice("env-file") {
   536  			fileEnv, err := envLib.ParseFile(f)
   537  			if err != nil {
   538  				return nil, err
   539  			}
   540  			// File env is overridden by env.
   541  			env = envLib.Join(env, fileEnv)
   542  		}
   543  	}
   544  
   545  	if c.IsSet("env") {
   546  		// env overrides any previous variables
   547  		cmdlineEnv := c.StringSlice("env")
   548  		if len(cmdlineEnv) > 0 {
   549  			parsedEnv, err := envLib.ParseSlice(cmdlineEnv)
   550  			if err != nil {
   551  				return nil, err
   552  			}
   553  			env = envLib.Join(env, parsedEnv)
   554  		}
   555  	}
   556  
   557  	// LABEL VARIABLES
   558  	labels, err := parse.GetAllLabels(c.StringSlice("label-file"), c.StringArray("label"))
   559  	if err != nil {
   560  		return nil, errors.Wrapf(err, "unable to process labels")
   561  	}
   562  	if data != nil {
   563  		for key, val := range data.Config.Labels {
   564  			if _, ok := labels[key]; !ok {
   565  				labels[key] = val
   566  			}
   567  		}
   568  	}
   569  
   570  	if systemdUnit, exists := osEnv[systemdGen.EnvVariable]; exists {
   571  		labels[systemdGen.EnvVariable] = systemdUnit
   572  	}
   573  
   574  	// ANNOTATIONS
   575  	annotations := make(map[string]string)
   576  
   577  	// First, add our default annotations
   578  	annotations[ann.TTY] = "false"
   579  	if tty {
   580  		annotations[ann.TTY] = "true"
   581  	}
   582  
   583  	// in the event this container is in a pod, and the pod has an infra container
   584  	// we will want to configure it as a type "container" instead defaulting to
   585  	// the behavior of a "sandbox" container
   586  	// In Kata containers:
   587  	// - "sandbox" is the annotation that denotes the container should use its own
   588  	//   VM, which is the default behavior
   589  	// - "container" denotes the container should join the VM of the SandboxID
   590  	//   (the infra container)
   591  	if podInfraID != "" {
   592  		annotations[ann.SandboxID] = podInfraID
   593  		annotations[ann.ContainerType] = ann.ContainerTypeContainer
   594  	}
   595  
   596  	if data != nil {
   597  		// Next, add annotations from the image
   598  		for key, value := range data.Annotations {
   599  			annotations[key] = value
   600  		}
   601  	}
   602  	// Last, add user annotations
   603  	for _, annotation := range c.StringSlice("annotation") {
   604  		splitAnnotation := strings.SplitN(annotation, "=", 2)
   605  		if len(splitAnnotation) < 2 {
   606  			return nil, errors.Errorf("Annotations must be formatted KEY=VALUE")
   607  		}
   608  		annotations[splitAnnotation[0]] = splitAnnotation[1]
   609  	}
   610  
   611  	// WORKING DIRECTORY
   612  	workDir := "/"
   613  	if c.IsSet("workdir") {
   614  		workDir = c.String("workdir")
   615  	} else if data != nil && data.Config.WorkingDir != "" {
   616  		workDir = data.Config.WorkingDir
   617  	}
   618  
   619  	userCommand := []string{}
   620  	entrypoint := configureEntrypoint(c, data)
   621  	// Build the command
   622  	// If we have an entry point, it goes first
   623  	if len(entrypoint) > 0 {
   624  		command = entrypoint
   625  	}
   626  	if len(inputCommand) > 0 {
   627  		// User command overrides data CMD
   628  		command = append(command, inputCommand...)
   629  		userCommand = append(userCommand, inputCommand...)
   630  	} else if data != nil && len(data.Config.Cmd) > 0 && !c.IsSet("entrypoint") {
   631  		// If not user command, add CMD
   632  		command = append(command, data.Config.Cmd...)
   633  		userCommand = append(userCommand, data.Config.Cmd...)
   634  	}
   635  
   636  	if data != nil && len(command) == 0 {
   637  		return nil, errors.Errorf("No command specified on command line or as CMD or ENTRYPOINT in this image")
   638  	}
   639  
   640  	// SHM Size
   641  	shmSize, err := units.FromHumanSize(c.String("shm-size"))
   642  	if err != nil {
   643  		return nil, errors.Wrapf(err, "unable to translate --shm-size")
   644  	}
   645  
   646  	if c.IsSet("add-host") {
   647  		// Verify the additional hosts are in correct format
   648  		for _, host := range c.StringSlice("add-host") {
   649  			if _, err := parse.ValidateExtraHost(host); err != nil {
   650  				return nil, err
   651  			}
   652  		}
   653  	}
   654  
   655  	var (
   656  		dnsSearches []string
   657  		dnsServers  []string
   658  		dnsOptions  []string
   659  	)
   660  	if c.Changed("dns-search") {
   661  		dnsSearches = c.StringSlice("dns-search")
   662  		// Check for explicit dns-search domain of ''
   663  		if len(dnsSearches) == 0 {
   664  			return nil, errors.Errorf("'' is not a valid domain")
   665  		}
   666  		// Validate domains are good
   667  		for _, dom := range dnsSearches {
   668  			if dom == "." {
   669  				if len(dnsSearches) > 1 {
   670  					return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'")
   671  				}
   672  				continue
   673  			}
   674  			if _, err := parse.ValidateDomain(dom); err != nil {
   675  				return nil, err
   676  			}
   677  		}
   678  	}
   679  	if c.IsSet("dns") {
   680  		dnsServers = append(dnsServers, c.StringSlice("dns")...)
   681  	}
   682  	if c.IsSet("dns-opt") {
   683  		dnsOptions = c.StringSlice("dns-opt")
   684  	}
   685  
   686  	var ImageVolumes map[string]struct{}
   687  	if data != nil && c.String("image-volume") != "ignore" {
   688  		ImageVolumes = data.Config.Volumes
   689  	}
   690  
   691  	var imageVolType = map[string]string{
   692  		"bind":   "",
   693  		"tmpfs":  "",
   694  		"ignore": "",
   695  	}
   696  	if _, ok := imageVolType[c.String("image-volume")]; !ok {
   697  		return nil, errors.Errorf("invalid image-volume type %q. Pick one of bind, tmpfs, or ignore", c.String("image-volume"))
   698  	}
   699  
   700  	systemd := c.String("systemd") == "always"
   701  	if !systemd && command != nil {
   702  		x, err := strconv.ParseBool(c.String("systemd"))
   703  		if err != nil {
   704  			return nil, errors.Wrapf(err, "cannot parse bool %s", c.String("systemd"))
   705  		}
   706  		if x && (command[0] == "/usr/sbin/init" || command[0] == "/sbin/init" || (filepath.Base(command[0]) == "systemd")) {
   707  			systemd = true
   708  		}
   709  	}
   710  	if systemd {
   711  		if signalString == "" {
   712  			stopSignal, err = util.ParseSignal("RTMIN+3")
   713  			if err != nil {
   714  				return nil, errors.Wrapf(err, "error parsing systemd signal")
   715  			}
   716  		}
   717  	}
   718  	// This is done because cobra cannot have two aliased flags. So we have to check
   719  	// both
   720  	memorySwappiness := c.Int64("memory-swappiness")
   721  
   722  	logDriver := libpod.KubernetesLogging
   723  	if c.Changed("log-driver") {
   724  		logDriver = c.String("log-driver")
   725  	}
   726  
   727  	pidsLimit := c.Int64("pids-limit")
   728  	if c.String("cgroups") == "disabled" && !c.Changed("pids-limit") {
   729  		pidsLimit = -1
   730  	}
   731  
   732  	pid := &cc.PidConfig{
   733  		PidMode: pidMode,
   734  	}
   735  	ipc := &cc.IpcConfig{
   736  		IpcMode: ipcMode,
   737  	}
   738  
   739  	cgroup := &cc.CgroupConfig{
   740  		Cgroups:      c.String("cgroups"),
   741  		Cgroupns:     c.String("cgroupns"),
   742  		CgroupParent: c.String("cgroup-parent"),
   743  		CgroupMode:   cgroupMode,
   744  	}
   745  
   746  	userns := &cc.UserConfig{
   747  		GroupAdd:   c.StringSlice("group-add"),
   748  		IDMappings: idmappings,
   749  		UsernsMode: usernsMode,
   750  		User:       user,
   751  	}
   752  
   753  	uts := &cc.UtsConfig{
   754  		UtsMode:  utsMode,
   755  		NoHosts:  c.Bool("no-hosts"),
   756  		HostAdd:  c.StringSlice("add-host"),
   757  		Hostname: c.String("hostname"),
   758  	}
   759  	net := &cc.NetworkConfig{
   760  		DNSOpt:       dnsOptions,
   761  		DNSSearch:    dnsSearches,
   762  		DNSServers:   dnsServers,
   763  		HTTPProxy:    c.Bool("http-proxy"),
   764  		MacAddress:   c.String("mac-address"),
   765  		Network:      c.String("network"),
   766  		NetMode:      netMode,
   767  		IPAddress:    c.String("ip"),
   768  		Publish:      c.StringSlice("publish"),
   769  		PublishAll:   c.Bool("publish-all"),
   770  		PortBindings: portBindings,
   771  	}
   772  
   773  	sysctl := map[string]string{}
   774  	if c.Changed("sysctl") {
   775  		sysctl, err = util.ValidateSysctls(c.StringSlice("sysctl"))
   776  		if err != nil {
   777  			return nil, errors.Wrapf(err, "invalid value for sysctl")
   778  		}
   779  	}
   780  
   781  	secConfig := &cc.SecurityConfig{
   782  		CapAdd:         c.StringSlice("cap-add"),
   783  		CapDrop:        c.StringSlice("cap-drop"),
   784  		Privileged:     c.Bool("privileged"),
   785  		ReadOnlyRootfs: c.Bool("read-only"),
   786  		ReadOnlyTmpfs:  c.Bool("read-only-tmpfs"),
   787  		Sysctl:         sysctl,
   788  	}
   789  
   790  	var securityOpt []string
   791  	if c.Changed("security-opt") {
   792  		securityOpt = c.StringArray("security-opt")
   793  	}
   794  	if err := secConfig.SetSecurityOpts(runtime, securityOpt); err != nil {
   795  		return nil, err
   796  	}
   797  
   798  	// SECCOMP
   799  	if data != nil {
   800  		if value, exists := labels[seccomp.ContainerImageLabel]; exists {
   801  			secConfig.SeccompProfileFromImage = value
   802  		}
   803  	}
   804  	if policy, err := seccomp.LookupPolicy(c.String("seccomp-policy")); err != nil {
   805  		return nil, err
   806  	} else {
   807  		secConfig.SeccompPolicy = policy
   808  	}
   809  	rtc, err := runtime.GetConfig()
   810  	if err != nil {
   811  		return nil, err
   812  	}
   813  	volumes := rtc.Containers.Volumes
   814  	if c.Changed("volume") {
   815  		volumes = append(volumes, c.StringSlice("volume")...)
   816  	}
   817  
   818  	devices := rtc.Containers.Devices
   819  	if c.Changed("device") {
   820  		devices = append(devices, c.StringSlice("device")...)
   821  	}
   822  
   823  	config := &cc.CreateConfig{
   824  		Annotations:       annotations,
   825  		BuiltinImgVolumes: ImageVolumes,
   826  		ConmonPidFile:     c.String("conmon-pidfile"),
   827  		ImageVolumeType:   c.String("image-volume"),
   828  		CidFile:           c.String("cidfile"),
   829  		Command:           command,
   830  		UserCommand:       userCommand,
   831  		Detach:            c.Bool("detach"),
   832  		Devices:           devices,
   833  		Entrypoint:        entrypoint,
   834  		Env:               env,
   835  		// ExposedPorts:   ports,
   836  		Init:         c.Bool("init"),
   837  		InitPath:     c.String("init-path"),
   838  		Image:        imageName,
   839  		RawImageName: rawImageName,
   840  		ImageID:      imageID,
   841  		Interactive:  c.Bool("interactive"),
   842  		// IP6Address:     c.String("ipv6"), // Not implemented yet - needs CNI support for static v6
   843  		Labels: labels,
   844  		// LinkLocalIP:    c.StringSlice("link-local-ip"), // Not implemented yet
   845  		LogDriver:    logDriver,
   846  		LogDriverOpt: c.StringSlice("log-opt"),
   847  		Name:         c.String("name"),
   848  		// NetworkAlias:   c.StringSlice("network-alias"), // Not implemented - does this make sense in Podman?
   849  		Pod:   podName,
   850  		Quiet: c.Bool("quiet"),
   851  		Resources: cc.CreateResourceConfig{
   852  			BlkioWeight:       blkioWeight,
   853  			BlkioWeightDevice: c.StringSlice("blkio-weight-device"),
   854  			CPUShares:         c.Uint64("cpu-shares"),
   855  			CPUPeriod:         c.Uint64("cpu-period"),
   856  			CPUsetCPUs:        c.String("cpuset-cpus"),
   857  			CPUsetMems:        c.String("cpuset-mems"),
   858  			CPUQuota:          c.Int64("cpu-quota"),
   859  			CPURtPeriod:       c.Uint64("cpu-rt-period"),
   860  			CPURtRuntime:      c.Int64("cpu-rt-runtime"),
   861  			CPUs:              c.Float64("cpus"),
   862  			DeviceCgroupRules: c.StringSlice("device-cgroup-rule"),
   863  			DeviceReadBps:     c.StringSlice("device-read-bps"),
   864  			DeviceReadIOps:    c.StringSlice("device-read-iops"),
   865  			DeviceWriteBps:    c.StringSlice("device-write-bps"),
   866  			DeviceWriteIOps:   c.StringSlice("device-write-iops"),
   867  			DisableOomKiller:  c.Bool("oom-kill-disable"),
   868  			ShmSize:           shmSize,
   869  			Memory:            memoryLimit,
   870  			MemoryReservation: memoryReservation,
   871  			MemorySwap:        memorySwap,
   872  			MemorySwappiness:  int(memorySwappiness),
   873  			KernelMemory:      memoryKernel,
   874  			OomScoreAdj:       c.Int("oom-score-adj"),
   875  			PidsLimit:         pidsLimit,
   876  			Ulimit:            c.StringSlice("ulimit"),
   877  		},
   878  		RestartPolicy: c.String("restart"),
   879  		Rm:            c.Bool("rm"),
   880  		Security:      *secConfig,
   881  		StopSignal:    stopSignal,
   882  		StopTimeout:   c.Uint("stop-timeout"),
   883  		Systemd:       systemd,
   884  		Tmpfs:         c.StringArray("tmpfs"),
   885  		Tty:           tty,
   886  		MountsFlag:    c.StringArray("mount"),
   887  		Volumes:       volumes,
   888  		WorkDir:       workDir,
   889  		Rootfs:        rootfs,
   890  		VolumesFrom:   c.StringSlice("volumes-from"),
   891  		Syslog:        c.Bool("syslog"),
   892  
   893  		Pid:     *pid,
   894  		Ipc:     *ipc,
   895  		Cgroup:  *cgroup,
   896  		User:    *userns,
   897  		Uts:     *uts,
   898  		Network: *net,
   899  	}
   900  
   901  	warnings, err := verifyContainerResources(config, false)
   902  	if err != nil {
   903  		return nil, err
   904  	}
   905  	for _, warning := range warnings {
   906  		fmt.Fprintln(os.Stderr, warning)
   907  	}
   908  	return config, nil
   909  }
   910  
   911  func CreateContainerFromCreateConfig(r *libpod.Runtime, createConfig *cc.CreateConfig, ctx context.Context, pod *libpod.Pod) (*libpod.Container, error) {
   912  	runtimeSpec, options, err := createConfig.MakeContainerConfig(r, pod)
   913  	if err != nil {
   914  		return nil, err
   915  	}
   916  
   917  	// Set the CreateCommand explicitly.  Some (future) consumers of libpod
   918  	// might not want to set it.
   919  	options = append(options, libpod.WithCreateCommand())
   920  
   921  	ctr, err := r.NewContainer(ctx, runtimeSpec, options...)
   922  	if err != nil {
   923  		return nil, err
   924  	}
   925  	return ctr, nil
   926  }
   927  
   928  func makeHealthCheckFromCli(c *GenericCLIResults) (*manifest.Schema2HealthConfig, error) {
   929  	inCommand := c.String("healthcheck-command")
   930  	inInterval := c.String("healthcheck-interval")
   931  	inRetries := c.Uint("healthcheck-retries")
   932  	inTimeout := c.String("healthcheck-timeout")
   933  	inStartPeriod := c.String("healthcheck-start-period")
   934  
   935  	// Every healthcheck requires a command
   936  	if len(inCommand) == 0 {
   937  		return nil, errors.New("Must define a healthcheck command for all healthchecks")
   938  	}
   939  
   940  	// first try to parse option value as JSON array of strings...
   941  	cmd := []string{}
   942  	err := json.Unmarshal([]byte(inCommand), &cmd)
   943  	if err != nil {
   944  		// ...otherwise pass it to "/bin/sh -c" inside the container
   945  		cmd = []string{"CMD-SHELL", inCommand}
   946  	}
   947  	hc := manifest.Schema2HealthConfig{
   948  		Test: cmd,
   949  	}
   950  
   951  	if inInterval == "disable" {
   952  		inInterval = "0"
   953  	}
   954  	intervalDuration, err := time.ParseDuration(inInterval)
   955  	if err != nil {
   956  		return nil, errors.Wrapf(err, "invalid healthcheck-interval %s ", inInterval)
   957  	}
   958  
   959  	hc.Interval = intervalDuration
   960  
   961  	if inRetries < 1 {
   962  		return nil, errors.New("healthcheck-retries must be greater than 0.")
   963  	}
   964  	hc.Retries = int(inRetries)
   965  	timeoutDuration, err := time.ParseDuration(inTimeout)
   966  	if err != nil {
   967  		return nil, errors.Wrapf(err, "invalid healthcheck-timeout %s", inTimeout)
   968  	}
   969  	if timeoutDuration < time.Duration(1) {
   970  		return nil, errors.New("healthcheck-timeout must be at least 1 second")
   971  	}
   972  	hc.Timeout = timeoutDuration
   973  
   974  	startPeriodDuration, err := time.ParseDuration(inStartPeriod)
   975  	if err != nil {
   976  		return nil, errors.Wrapf(err, "invalid healthcheck-start-period %s", inStartPeriod)
   977  	}
   978  	if startPeriodDuration < time.Duration(0) {
   979  		return nil, errors.New("healthcheck-start-period must be 0 seconds or greater")
   980  	}
   981  	hc.StartPeriod = startPeriodDuration
   982  
   983  	return &hc, nil
   984  }