github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/specgen/generate/container_create.go (about)

     1  package generate
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  
     9  	"github.com/containers/common/pkg/config"
    10  	"github.com/containers/podman/v2/libpod"
    11  	"github.com/containers/podman/v2/libpod/image"
    12  	"github.com/containers/podman/v2/pkg/specgen"
    13  	"github.com/containers/podman/v2/pkg/util"
    14  	"github.com/containers/storage"
    15  	"github.com/opencontainers/selinux/go-selinux/label"
    16  	"github.com/pkg/errors"
    17  	"github.com/sirupsen/logrus"
    18  )
    19  
    20  // MakeContainer creates a container based on the SpecGenerator.
    21  // Returns the created, container and any warnings resulting from creating the
    22  // container, or an error.
    23  func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator) (*libpod.Container, error) {
    24  	rtc, err := rt.GetConfig()
    25  	if err != nil {
    26  		return nil, err
    27  	}
    28  
    29  	// If joining a pod, retrieve the pod for use.
    30  	var pod *libpod.Pod
    31  	if s.Pod != "" {
    32  		pod, err = rt.LookupPod(s.Pod)
    33  		if err != nil {
    34  			return nil, errors.Wrapf(err, "error retrieving pod %s", s.Pod)
    35  		}
    36  	}
    37  
    38  	// Set defaults for unset namespaces
    39  	if s.PidNS.IsDefault() {
    40  		defaultNS, err := GetDefaultNamespaceMode("pid", rtc, pod)
    41  		if err != nil {
    42  			return nil, err
    43  		}
    44  		s.PidNS = defaultNS
    45  	}
    46  	if s.IpcNS.IsDefault() {
    47  		defaultNS, err := GetDefaultNamespaceMode("ipc", rtc, pod)
    48  		if err != nil {
    49  			return nil, err
    50  		}
    51  		s.IpcNS = defaultNS
    52  	}
    53  	if s.UtsNS.IsDefault() {
    54  		defaultNS, err := GetDefaultNamespaceMode("uts", rtc, pod)
    55  		if err != nil {
    56  			return nil, err
    57  		}
    58  		s.UtsNS = defaultNS
    59  	}
    60  	if s.UserNS.IsDefault() {
    61  		defaultNS, err := GetDefaultNamespaceMode("user", rtc, pod)
    62  		if err != nil {
    63  			return nil, err
    64  		}
    65  		s.UserNS = defaultNS
    66  	}
    67  	if s.NetNS.IsDefault() {
    68  		defaultNS, err := GetDefaultNamespaceMode("net", rtc, pod)
    69  		if err != nil {
    70  			return nil, err
    71  		}
    72  		s.NetNS = defaultNS
    73  	}
    74  	if s.CgroupNS.IsDefault() {
    75  		defaultNS, err := GetDefaultNamespaceMode("cgroup", rtc, pod)
    76  		if err != nil {
    77  			return nil, err
    78  		}
    79  		s.CgroupNS = defaultNS
    80  	}
    81  
    82  	options := []libpod.CtrCreateOption{}
    83  	if s.ContainerCreateCommand != nil {
    84  		options = append(options, libpod.WithCreateCommand(s.ContainerCreateCommand))
    85  	}
    86  
    87  	var newImage *image.Image
    88  	if s.Rootfs != "" {
    89  		options = append(options, libpod.WithRootFS(s.Rootfs))
    90  	} else {
    91  		newImage, err = rt.ImageRuntime().NewFromLocal(s.Image)
    92  		if err != nil {
    93  			return nil, err
    94  		}
    95  		// If the input name changed, we could properly resolve the
    96  		// image. Otherwise, it must have been an ID where we're
    97  		// defaulting to the first name or an empty one if no names are
    98  		// present.
    99  		imgName := newImage.InputName
   100  		if s.Image == newImage.InputName && strings.HasPrefix(newImage.ID(), s.Image) {
   101  			names := newImage.Names()
   102  			if len(names) > 0 {
   103  				imgName = names[0]
   104  			}
   105  		}
   106  
   107  		options = append(options, libpod.WithRootFSFromImage(newImage.ID(), imgName, s.RawImageName))
   108  	}
   109  	if err := s.Validate(); err != nil {
   110  		return nil, errors.Wrap(err, "invalid config provided")
   111  	}
   112  
   113  	finalMounts, finalVolumes, finalOverlays, err := finalizeMounts(ctx, s, rt, rtc, newImage)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	command, err := makeCommand(ctx, s, newImage, rtc)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, finalOverlays, newImage, command)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	options = append(options, opts...)
   128  
   129  	exitCommandArgs, err := CreateExitCommandArgs(rt.StorageConfig(), rtc, logrus.IsLevelEnabled(logrus.DebugLevel), s.Remove, false)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	options = append(options, libpod.WithExitCommand(exitCommandArgs))
   134  
   135  	if len(s.Aliases) > 0 {
   136  		options = append(options, libpod.WithNetworkAliases(s.Aliases))
   137  	}
   138  
   139  	runtimeSpec, err := SpecGenToOCI(ctx, s, rt, rtc, newImage, finalMounts, pod, command)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	return rt.NewContainer(ctx, runtimeSpec, options...)
   144  }
   145  
   146  func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator, pod *libpod.Pod, volumes []*specgen.NamedVolume, overlays []*specgen.OverlayVolume, img *image.Image, command []string) ([]libpod.CtrCreateOption, error) {
   147  	var options []libpod.CtrCreateOption
   148  	var err error
   149  
   150  	if s.PreserveFDs > 0 {
   151  		options = append(options, libpod.WithPreserveFDs(s.PreserveFDs))
   152  	}
   153  
   154  	if s.Stdin {
   155  		options = append(options, libpod.WithStdin())
   156  	}
   157  
   158  	if s.Timezone != "" {
   159  		options = append(options, libpod.WithTimezone(s.Timezone))
   160  	}
   161  	if s.Umask != "" {
   162  		options = append(options, libpod.WithUmask(s.Umask))
   163  	}
   164  
   165  	useSystemd := false
   166  	switch s.Systemd {
   167  	case "always":
   168  		useSystemd = true
   169  	case "false":
   170  		break
   171  	case "", "true":
   172  		if len(command) == 0 {
   173  			command, err = img.Cmd(ctx)
   174  			if err != nil {
   175  				return nil, err
   176  			}
   177  		}
   178  
   179  		if len(command) > 0 {
   180  			useSystemdCommands := map[string]bool{
   181  				"/sbin/init":           true,
   182  				"/usr/sbin/init":       true,
   183  				"/usr/local/sbin/init": true,
   184  			}
   185  			if useSystemdCommands[command[0]] || (filepath.Base(command[0]) == "systemd") {
   186  				useSystemd = true
   187  			}
   188  		}
   189  	default:
   190  		return nil, errors.Wrapf(err, "invalid value %q systemd option requires 'true, false, always'", s.Systemd)
   191  	}
   192  	logrus.Debugf("using systemd mode: %t", useSystemd)
   193  	if useSystemd {
   194  		// is StopSignal was not set by the user then set it to systemd
   195  		// expected StopSigal
   196  		if s.StopSignal == nil {
   197  			stopSignal, err := util.ParseSignal("RTMIN+3")
   198  			if err != nil {
   199  				return nil, errors.Wrapf(err, "error parsing systemd signal")
   200  			}
   201  			s.StopSignal = &stopSignal
   202  		}
   203  
   204  		options = append(options, libpod.WithSystemd())
   205  	}
   206  	if len(s.SdNotifyMode) > 0 {
   207  		options = append(options, libpod.WithSdNotifyMode(s.SdNotifyMode))
   208  	}
   209  
   210  	if len(s.Name) > 0 {
   211  		logrus.Debugf("setting container name %s", s.Name)
   212  		options = append(options, libpod.WithName(s.Name))
   213  	}
   214  	if pod != nil {
   215  		logrus.Debugf("adding container to pod %s", pod.Name())
   216  		options = append(options, rt.WithPod(pod))
   217  	}
   218  	destinations := []string{}
   219  	// Take all mount and named volume destinations.
   220  	for _, mount := range s.Mounts {
   221  		destinations = append(destinations, mount.Destination)
   222  	}
   223  	for _, volume := range volumes {
   224  		destinations = append(destinations, volume.Dest)
   225  	}
   226  	for _, overlayVolume := range overlays {
   227  		destinations = append(destinations, overlayVolume.Destination)
   228  	}
   229  	for _, imageVolume := range s.ImageVolumes {
   230  		destinations = append(destinations, imageVolume.Destination)
   231  	}
   232  	options = append(options, libpod.WithUserVolumes(destinations))
   233  
   234  	if len(volumes) != 0 {
   235  		var vols []*libpod.ContainerNamedVolume
   236  		for _, v := range volumes {
   237  			vols = append(vols, &libpod.ContainerNamedVolume{
   238  				Name:    v.Name,
   239  				Dest:    v.Dest,
   240  				Options: v.Options,
   241  			})
   242  		}
   243  		options = append(options, libpod.WithNamedVolumes(vols))
   244  	}
   245  
   246  	if len(overlays) != 0 {
   247  		var vols []*libpod.ContainerOverlayVolume
   248  		for _, v := range overlays {
   249  			vols = append(vols, &libpod.ContainerOverlayVolume{
   250  				Dest:   v.Destination,
   251  				Source: v.Source,
   252  			})
   253  		}
   254  		options = append(options, libpod.WithOverlayVolumes(vols))
   255  	}
   256  
   257  	if len(s.ImageVolumes) != 0 {
   258  		var vols []*libpod.ContainerImageVolume
   259  		for _, v := range s.ImageVolumes {
   260  			vols = append(vols, &libpod.ContainerImageVolume{
   261  				Dest:      v.Destination,
   262  				Source:    v.Source,
   263  				ReadWrite: v.ReadWrite,
   264  			})
   265  		}
   266  		options = append(options, libpod.WithImageVolumes(vols))
   267  	}
   268  
   269  	if s.Command != nil {
   270  		options = append(options, libpod.WithCommand(s.Command))
   271  	}
   272  	if s.Entrypoint != nil {
   273  		options = append(options, libpod.WithEntrypoint(s.Entrypoint))
   274  	}
   275  	// If the user did not set an workdir but the image did, ensure it is
   276  	// created.
   277  	if s.WorkDir == "" && img != nil {
   278  		options = append(options, libpod.WithCreateWorkingDir())
   279  	}
   280  	if s.StopSignal != nil {
   281  		options = append(options, libpod.WithStopSignal(*s.StopSignal))
   282  	}
   283  	if s.StopTimeout != nil {
   284  		options = append(options, libpod.WithStopTimeout(*s.StopTimeout))
   285  	}
   286  	if s.LogConfiguration != nil {
   287  		if len(s.LogConfiguration.Path) > 0 {
   288  			options = append(options, libpod.WithLogPath(s.LogConfiguration.Path))
   289  		}
   290  		if s.LogConfiguration.Size > 0 {
   291  			options = append(options, libpod.WithMaxLogSize(s.LogConfiguration.Size))
   292  		}
   293  		if len(s.LogConfiguration.Options) > 0 && s.LogConfiguration.Options["tag"] != "" {
   294  			// Note: I'm really guessing here.
   295  			options = append(options, libpod.WithLogTag(s.LogConfiguration.Options["tag"]))
   296  		}
   297  
   298  		if len(s.LogConfiguration.Driver) > 0 {
   299  			options = append(options, libpod.WithLogDriver(s.LogConfiguration.Driver))
   300  		}
   301  	}
   302  
   303  	// Security options
   304  	if len(s.SelinuxOpts) > 0 {
   305  		options = append(options, libpod.WithSecLabels(s.SelinuxOpts))
   306  	} else {
   307  		if pod != nil {
   308  			// duplicate the security options from the pod
   309  			processLabel, err := pod.ProcessLabel()
   310  			if err != nil {
   311  				return nil, err
   312  			}
   313  			if processLabel != "" {
   314  				selinuxOpts, err := label.DupSecOpt(processLabel)
   315  				if err != nil {
   316  					return nil, err
   317  				}
   318  				options = append(options, libpod.WithSecLabels(selinuxOpts))
   319  			}
   320  		}
   321  	}
   322  	options = append(options, libpod.WithPrivileged(s.Privileged))
   323  
   324  	// Get namespace related options
   325  	namespaceOptions, err := namespaceOptions(ctx, s, rt, pod, img)
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  	options = append(options, namespaceOptions...)
   330  
   331  	if len(s.ConmonPidFile) > 0 {
   332  		options = append(options, libpod.WithConmonPidFile(s.ConmonPidFile))
   333  	}
   334  	options = append(options, libpod.WithLabels(s.Labels))
   335  	if s.ShmSize != nil {
   336  		options = append(options, libpod.WithShmSize(*s.ShmSize))
   337  	}
   338  	if s.Rootfs != "" {
   339  		options = append(options, libpod.WithRootFS(s.Rootfs))
   340  	}
   341  	// Default used if not overridden on command line
   342  
   343  	if s.RestartPolicy != "" {
   344  		if s.RestartRetries != nil {
   345  			options = append(options, libpod.WithRestartRetries(*s.RestartRetries))
   346  		}
   347  		options = append(options, libpod.WithRestartPolicy(s.RestartPolicy))
   348  	}
   349  
   350  	if s.ContainerHealthCheckConfig.HealthConfig != nil {
   351  		options = append(options, libpod.WithHealthCheck(s.ContainerHealthCheckConfig.HealthConfig))
   352  		logrus.Debugf("New container has a health check")
   353  	}
   354  	return options, nil
   355  }
   356  
   357  func CreateExitCommandArgs(storageConfig storage.StoreOptions, config *config.Config, syslog, rm, exec bool) ([]string, error) {
   358  	// We need a cleanup process for containers in the current model.
   359  	// But we can't assume that the caller is Podman - it could be another
   360  	// user of the API.
   361  	// As such, provide a way to specify a path to Podman, so we can
   362  	// still invoke a cleanup process.
   363  
   364  	podmanPath, err := os.Executable()
   365  	if err != nil {
   366  		return nil, err
   367  	}
   368  
   369  	command := []string{podmanPath,
   370  		"--root", storageConfig.GraphRoot,
   371  		"--runroot", storageConfig.RunRoot,
   372  		"--log-level", logrus.GetLevel().String(),
   373  		"--cgroup-manager", config.Engine.CgroupManager,
   374  		"--tmpdir", config.Engine.TmpDir,
   375  	}
   376  	if config.Engine.OCIRuntime != "" {
   377  		command = append(command, []string{"--runtime", config.Engine.OCIRuntime}...)
   378  	}
   379  	if storageConfig.GraphDriverName != "" {
   380  		command = append(command, []string{"--storage-driver", storageConfig.GraphDriverName}...)
   381  	}
   382  	for _, opt := range storageConfig.GraphDriverOptions {
   383  		command = append(command, []string{"--storage-opt", opt}...)
   384  	}
   385  	if config.Engine.EventsLogger != "" {
   386  		command = append(command, []string{"--events-backend", config.Engine.EventsLogger}...)
   387  	}
   388  
   389  	if syslog {
   390  		command = append(command, "--syslog")
   391  	}
   392  	command = append(command, []string{"container", "cleanup"}...)
   393  
   394  	if rm {
   395  		command = append(command, "--rm")
   396  	}
   397  
   398  	// This has to be absolutely last, to ensure that the exec session ID
   399  	// will be added after it by Libpod.
   400  	if exec {
   401  		command = append(command, "--exec")
   402  	}
   403  
   404  	return command, nil
   405  }