github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/spec/createconfig.go (about)

     1  package createconfig
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"strconv"
     7  	"strings"
     8  	"syscall"
     9  
    10  	"github.com/containers/image/v5/manifest"
    11  	"github.com/containers/podman/v2/libpod"
    12  	"github.com/containers/podman/v2/libpod/define"
    13  	"github.com/containers/podman/v2/pkg/namespaces"
    14  	"github.com/containers/podman/v2/pkg/seccomp"
    15  	"github.com/containers/storage"
    16  	"github.com/docker/go-connections/nat"
    17  	spec "github.com/opencontainers/runtime-spec/specs-go"
    18  	"github.com/opencontainers/runtime-tools/generate"
    19  	"github.com/pkg/errors"
    20  	"github.com/sirupsen/logrus"
    21  )
    22  
    23  // Type constants
    24  const (
    25  	bps = iota
    26  	iops
    27  )
    28  
    29  // CreateResourceConfig represents resource elements in CreateConfig
    30  // structures
    31  type CreateResourceConfig struct {
    32  	BlkioWeight       uint16   // blkio-weight
    33  	BlkioWeightDevice []string // blkio-weight-device
    34  	CgroupConf        map[string]string
    35  	CPUPeriod         uint64  // cpu-period
    36  	CPUQuota          int64   // cpu-quota
    37  	CPURtPeriod       uint64  // cpu-rt-period
    38  	CPURtRuntime      int64   // cpu-rt-runtime
    39  	CPUShares         uint64  // cpu-shares
    40  	CPUs              float64 // cpus
    41  	CPUsetCPUs        string
    42  	CPUsetMems        string   // cpuset-mems
    43  	DeviceCgroupRules []string //device-cgroup-rule
    44  	DeviceReadBps     []string // device-read-bps
    45  	DeviceReadIOps    []string // device-read-iops
    46  	DeviceWriteBps    []string // device-write-bps
    47  	DeviceWriteIOps   []string // device-write-iops
    48  	DisableOomKiller  bool     // oom-kill-disable
    49  	KernelMemory      int64    // kernel-memory
    50  	Memory            int64    //memory
    51  	MemoryReservation int64    // memory-reservation
    52  	MemorySwap        int64    //memory-swap
    53  	MemorySwappiness  int      // memory-swappiness
    54  	OomScoreAdj       int      //oom-score-adj
    55  	PidsLimit         int64    // pids-limit
    56  	ShmSize           int64
    57  	Ulimit            []string //ulimit
    58  }
    59  
    60  // PidConfig configures the pid namespace for the container
    61  type PidConfig struct {
    62  	PidMode namespaces.PidMode //pid
    63  }
    64  
    65  // IpcConfig configures the ipc namespace for the container
    66  type IpcConfig struct {
    67  	IpcMode namespaces.IpcMode //ipc
    68  }
    69  
    70  // CgroupConfig configures the cgroup namespace for the container
    71  type CgroupConfig struct {
    72  	Cgroups      string
    73  	Cgroupns     string
    74  	CgroupParent string                // cgroup-parent
    75  	CgroupMode   namespaces.CgroupMode //cgroup
    76  }
    77  
    78  // UserConfig configures the user namespace for the container
    79  type UserConfig struct {
    80  	GroupAdd   []string // group-add
    81  	IDMappings *storage.IDMappingOptions
    82  	UsernsMode namespaces.UsernsMode //userns
    83  	User       string                //user
    84  }
    85  
    86  // UtsConfig configures the uts namespace for the container
    87  type UtsConfig struct {
    88  	UtsMode  namespaces.UTSMode //uts
    89  	NoHosts  bool
    90  	HostAdd  []string //add-host
    91  	Hostname string
    92  }
    93  
    94  // NetworkConfig configures the network namespace for the container
    95  type NetworkConfig struct {
    96  	DNSOpt       []string //dns-opt
    97  	DNSSearch    []string //dns-search
    98  	DNSServers   []string //dns
    99  	ExposedPorts map[nat.Port]struct{}
   100  	HTTPProxy    bool
   101  	IP6Address   string                 //ipv6
   102  	IPAddress    string                 //ip
   103  	LinkLocalIP  []string               // link-local-ip
   104  	MacAddress   string                 //mac-address
   105  	NetMode      namespaces.NetworkMode //net
   106  	Network      string                 //network
   107  	NetworkAlias []string               //network-alias
   108  	PortBindings nat.PortMap
   109  	Publish      []string //publish
   110  	PublishAll   bool     //publish-all
   111  }
   112  
   113  // SecurityConfig configures the security features for the container
   114  type SecurityConfig struct {
   115  	CapAdd                  []string // cap-add
   116  	CapDrop                 []string // cap-drop
   117  	CapRequired             []string // cap-required
   118  	LabelOpts               []string //SecurityOpts
   119  	NoNewPrivs              bool     //SecurityOpts
   120  	ApparmorProfile         string   //SecurityOpts
   121  	SeccompProfilePath      string   //SecurityOpts
   122  	SeccompProfileFromImage string   // seccomp profile from the container image
   123  	SeccompPolicy           seccomp.Policy
   124  	SecurityOpts            []string
   125  	Privileged              bool              //privileged
   126  	ReadOnlyRootfs          bool              //read-only
   127  	ReadOnlyTmpfs           bool              //read-only-tmpfs
   128  	Sysctl                  map[string]string //sysctl
   129  	ProcOpts                []string
   130  }
   131  
   132  // CreateConfig is a pre OCI spec structure.  It represents user input from varlink or the CLI
   133  // swagger:model CreateConfig
   134  type CreateConfig struct {
   135  	Annotations       map[string]string
   136  	Args              []string
   137  	CidFile           string
   138  	ConmonPidFile     string
   139  	Command           []string          // Full command that will be used
   140  	UserCommand       []string          // User-entered command (or image CMD)
   141  	Detach            bool              // detach
   142  	Devices           []string          // device
   143  	Entrypoint        []string          //entrypoint
   144  	Env               map[string]string //env
   145  	HealthCheck       *manifest.Schema2HealthConfig
   146  	Init              bool   // init
   147  	InitPath          string //init-path
   148  	Image             string
   149  	ImageID           string
   150  	RawImageName      string
   151  	BuiltinImgVolumes map[string]struct{} // volumes defined in the image config
   152  	ImageVolumeType   string              // how to handle the image volume, either bind, tmpfs, or ignore
   153  	Interactive       bool                //interactive
   154  	Labels            map[string]string   //label
   155  	LogDriver         string              // log-driver
   156  	LogDriverOpt      []string            // log-opt
   157  	Name              string              //name
   158  	PodmanPath        string
   159  	Pod               string //pod
   160  	Quiet             bool   //quiet
   161  	Resources         CreateResourceConfig
   162  	RestartPolicy     string
   163  	Rm                bool           //rm
   164  	Rmi               bool           //rmi
   165  	StopSignal        syscall.Signal // stop-signal
   166  	StopTimeout       uint           // stop-timeout
   167  	Systemd           bool
   168  	Tmpfs             []string // tmpfs
   169  	Tty               bool     //tty
   170  	Mounts            []spec.Mount
   171  	MountsFlag        []string // mounts
   172  	NamedVolumes      []*libpod.ContainerNamedVolume
   173  	Volumes           []string //volume
   174  	VolumesFrom       []string
   175  	WorkDir           string //workdir
   176  	Rootfs            string
   177  	Security          SecurityConfig
   178  	Syslog            bool // Whether to enable syslog on exit commands
   179  
   180  	// Namespaces
   181  	Pid     PidConfig
   182  	Ipc     IpcConfig
   183  	Cgroup  CgroupConfig
   184  	User    UserConfig
   185  	Uts     UtsConfig
   186  	Network NetworkConfig
   187  }
   188  
   189  func u32Ptr(i int64) *uint32     { u := uint32(i); return &u }
   190  func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm }
   191  
   192  // CreateBlockIO returns a LinuxBlockIO struct from a CreateConfig
   193  func (c *CreateConfig) CreateBlockIO() (*spec.LinuxBlockIO, error) {
   194  	return c.createBlockIO()
   195  }
   196  
   197  func (c *CreateConfig) createExitCommand(runtime *libpod.Runtime) ([]string, error) {
   198  	config, err := runtime.GetConfig()
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  	storageConfig := runtime.StorageConfig()
   203  
   204  	// We need a cleanup process for containers in the current model.
   205  	// But we can't assume that the caller is Podman - it could be another
   206  	// user of the API.
   207  	// As such, provide a way to specify a path to Podman, so we can
   208  	// still invoke a cleanup process.
   209  	cmd := c.PodmanPath
   210  	if cmd == "" {
   211  		cmd, _ = os.Executable()
   212  	}
   213  
   214  	command := []string{cmd,
   215  		"--root", storageConfig.GraphRoot,
   216  		"--runroot", storageConfig.RunRoot,
   217  		"--log-level", logrus.GetLevel().String(),
   218  		"--cgroup-manager", config.Engine.CgroupManager,
   219  		"--tmpdir", config.Engine.TmpDir,
   220  	}
   221  	if config.Engine.OCIRuntime != "" {
   222  		command = append(command, []string{"--runtime", config.Engine.OCIRuntime}...)
   223  	}
   224  	if storageConfig.GraphDriverName != "" {
   225  		command = append(command, []string{"--storage-driver", storageConfig.GraphDriverName}...)
   226  	}
   227  	for _, opt := range storageConfig.GraphDriverOptions {
   228  		command = append(command, []string{"--storage-opt", opt}...)
   229  	}
   230  	if config.Engine.EventsLogger != "" {
   231  		command = append(command, []string{"--events-backend", config.Engine.EventsLogger}...)
   232  	}
   233  
   234  	if c.Syslog {
   235  		command = append(command, "--syslog", "true")
   236  	}
   237  	command = append(command, []string{"container", "cleanup"}...)
   238  
   239  	if c.Rm {
   240  		command = append(command, "--rm")
   241  	}
   242  
   243  	if c.Rmi {
   244  		command = append(command, "--rmi")
   245  	}
   246  
   247  	return command, nil
   248  }
   249  
   250  // GetContainerCreateOptions takes a CreateConfig and returns a slice of CtrCreateOptions
   251  func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod, mounts []spec.Mount, namedVolumes []*libpod.ContainerNamedVolume) ([]libpod.CtrCreateOption, error) {
   252  	var options []libpod.CtrCreateOption
   253  	var err error
   254  
   255  	if c.Interactive {
   256  		options = append(options, libpod.WithStdin())
   257  	}
   258  	if c.Systemd {
   259  		options = append(options, libpod.WithSystemd())
   260  	}
   261  	if c.Name != "" {
   262  		logrus.Debugf("setting container name %s", c.Name)
   263  		options = append(options, libpod.WithName(c.Name))
   264  	}
   265  	if c.Pod != "" {
   266  		logrus.Debugf("adding container to pod %s", c.Pod)
   267  		options = append(options, runtime.WithPod(pod))
   268  	}
   269  
   270  	// handle some spec from the InfraContainer when it's a pod
   271  	if pod != nil && pod.HasInfraContainer() {
   272  		InfraCtr, err := pod.InfraContainer()
   273  		if err != nil {
   274  			return nil, err
   275  		}
   276  		// handle the pod.spec.hostAliases
   277  		options = append(options, libpod.WithHosts(InfraCtr.HostsAdd()))
   278  	}
   279  
   280  	if len(mounts) != 0 || len(namedVolumes) != 0 {
   281  		destinations := []string{}
   282  
   283  		// Take all mount and named volume destinations.
   284  		for _, mount := range mounts {
   285  			destinations = append(destinations, mount.Destination)
   286  		}
   287  		for _, volume := range namedVolumes {
   288  			destinations = append(destinations, volume.Dest)
   289  		}
   290  
   291  		options = append(options, libpod.WithUserVolumes(destinations))
   292  	}
   293  
   294  	if len(namedVolumes) != 0 {
   295  		options = append(options, libpod.WithNamedVolumes(namedVolumes))
   296  	}
   297  
   298  	if len(c.UserCommand) != 0 {
   299  		options = append(options, libpod.WithCommand(c.UserCommand))
   300  	}
   301  
   302  	// Add entrypoint if it was set
   303  	// If it's empty it's because it was explicitly set to ""
   304  	if c.Entrypoint != nil {
   305  		options = append(options, libpod.WithEntrypoint(c.Entrypoint))
   306  	}
   307  
   308  	// TODO: MNT, USER, CGROUP
   309  	options = append(options, libpod.WithStopSignal(c.StopSignal))
   310  	options = append(options, libpod.WithStopTimeout(c.StopTimeout))
   311  
   312  	logPath, logTag := getLoggingOpts(c.LogDriverOpt)
   313  	if logPath != "" {
   314  		options = append(options, libpod.WithLogPath(logPath))
   315  	}
   316  	if logTag != "" {
   317  		options = append(options, libpod.WithLogTag(logTag))
   318  	}
   319  
   320  	if c.LogDriver != "" {
   321  		options = append(options, libpod.WithLogDriver(c.LogDriver))
   322  	}
   323  
   324  	secOpts, err := c.Security.ToCreateOptions()
   325  	if err != nil {
   326  		return nil, err
   327  	}
   328  	options = append(options, secOpts...)
   329  
   330  	nsOpts, err := c.Cgroup.ToCreateOptions(runtime)
   331  	if err != nil {
   332  		return nil, err
   333  	}
   334  	options = append(options, nsOpts...)
   335  
   336  	nsOpts, err = c.Ipc.ToCreateOptions(runtime)
   337  	if err != nil {
   338  		return nil, err
   339  	}
   340  	options = append(options, nsOpts...)
   341  
   342  	nsOpts, err = c.Pid.ToCreateOptions(runtime)
   343  	if err != nil {
   344  		return nil, err
   345  	}
   346  	options = append(options, nsOpts...)
   347  
   348  	nsOpts, err = c.Network.ToCreateOptions(runtime, &c.User)
   349  	if err != nil {
   350  		return nil, err
   351  	}
   352  	options = append(options, nsOpts...)
   353  
   354  	nsOpts, err = c.Uts.ToCreateOptions(runtime, pod)
   355  	if err != nil {
   356  		return nil, err
   357  	}
   358  	options = append(options, nsOpts...)
   359  
   360  	nsOpts, err = c.User.ToCreateOptions(runtime)
   361  	if err != nil {
   362  		return nil, err
   363  	}
   364  	options = append(options, nsOpts...)
   365  
   366  	// Gather up the options for NewContainer which consist of With... funcs
   367  	options = append(options, libpod.WithRootFSFromImage(c.ImageID, c.Image, c.RawImageName))
   368  	options = append(options, libpod.WithConmonPidFile(c.ConmonPidFile))
   369  	options = append(options, libpod.WithLabels(c.Labels))
   370  	options = append(options, libpod.WithShmSize(c.Resources.ShmSize))
   371  	if c.Rootfs != "" {
   372  		options = append(options, libpod.WithRootFS(c.Rootfs))
   373  	}
   374  	// Default used if not overridden on command line
   375  
   376  	if c.RestartPolicy != "" {
   377  		if c.RestartPolicy == "unless-stopped" {
   378  			return nil, errors.Wrapf(define.ErrInvalidArg, "the unless-stopped restart policy is not supported")
   379  		}
   380  
   381  		split := strings.Split(c.RestartPolicy, ":")
   382  		if len(split) > 1 {
   383  			numTries, err := strconv.Atoi(split[1])
   384  			if err != nil {
   385  				return nil, errors.Wrapf(err, "%s is not a valid number of retries for restart policy", split[1])
   386  			}
   387  			if numTries < 0 {
   388  				return nil, errors.Wrapf(define.ErrInvalidArg, "restart policy requires a positive number of retries")
   389  			}
   390  			options = append(options, libpod.WithRestartRetries(uint(numTries)))
   391  		}
   392  		options = append(options, libpod.WithRestartPolicy(split[0]))
   393  	}
   394  
   395  	// Always use a cleanup process to clean up Podman after termination
   396  	exitCmd, err := c.createExitCommand(runtime)
   397  	if err != nil {
   398  		return nil, err
   399  	}
   400  	options = append(options, libpod.WithExitCommand(exitCmd))
   401  
   402  	if c.HealthCheck != nil {
   403  		options = append(options, libpod.WithHealthCheck(c.HealthCheck))
   404  		logrus.Debugf("New container has a health check")
   405  	}
   406  	return options, nil
   407  }
   408  
   409  // AddPrivilegedDevices iterates through host devices and adds all
   410  // host devices to the spec
   411  func AddPrivilegedDevices(g *generate.Generator) error {
   412  	return addPrivilegedDevices(g)
   413  }
   414  
   415  func CreateContainerFromCreateConfig(ctx context.Context, r *libpod.Runtime, createConfig *CreateConfig, pod *libpod.Pod) (*libpod.Container, error) {
   416  	runtimeSpec, options, err := createConfig.MakeContainerConfig(r, pod)
   417  	if err != nil {
   418  		return nil, err
   419  	}
   420  
   421  	ctr, err := r.NewContainer(ctx, runtimeSpec, options...)
   422  	if err != nil {
   423  		return nil, err
   424  	}
   425  	return ctr, nil
   426  }