aboutsummaryrefslogtreecommitdiff
path: root/pkg/spec
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/spec')
-rw-r--r--pkg/spec/config_linux.go371
-rw-r--r--pkg/spec/config_linux_cgo.go47
-rw-r--r--pkg/spec/config_linux_nocgo.go11
-rw-r--r--pkg/spec/config_unsupported.go36
-rw-r--r--pkg/spec/containerconfig.go41
-rw-r--r--pkg/spec/createconfig.go426
-rw-r--r--pkg/spec/namespaces.go463
-rw-r--r--pkg/spec/parse.go225
-rw-r--r--pkg/spec/ports.go107
-rw-r--r--pkg/spec/security.go204
-rw-r--r--pkg/spec/spec.go593
-rw-r--r--pkg/spec/spec_test.go108
-rw-r--r--pkg/spec/storage.go875
-rw-r--r--pkg/spec/storage_test.go38
14 files changed, 0 insertions, 3545 deletions
diff --git a/pkg/spec/config_linux.go b/pkg/spec/config_linux.go
deleted file mode 100644
index 319cce61f..000000000
--- a/pkg/spec/config_linux.go
+++ /dev/null
@@ -1,371 +0,0 @@
-// +build linux
-
-package createconfig
-
-import (
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "strconv"
- "strings"
-
- "github.com/containers/podman/v2/pkg/rootless"
- "github.com/opencontainers/runc/libcontainer/configs"
- "github.com/opencontainers/runc/libcontainer/devices"
- spec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/opencontainers/runtime-tools/generate"
- "github.com/pkg/errors"
- "golang.org/x/sys/unix"
-)
-
-// Device transforms a libcontainer configs.Device to a specs.LinuxDevice object.
-func Device(d *configs.Device) spec.LinuxDevice {
- return spec.LinuxDevice{
- Type: string(d.Type),
- Path: d.Path,
- Major: d.Major,
- Minor: d.Minor,
- FileMode: fmPtr(int64(d.FileMode)),
- UID: u32Ptr(int64(d.Uid)),
- GID: u32Ptr(int64(d.Gid)),
- }
-}
-
-// DevicesFromPath computes a list of devices
-func DevicesFromPath(g *generate.Generator, devicePath string) error {
- devs := strings.Split(devicePath, ":")
- resolvedDevicePath := devs[0]
- // check if it is a symbolic link
- if src, err := os.Lstat(resolvedDevicePath); err == nil && src.Mode()&os.ModeSymlink == os.ModeSymlink {
- if linkedPathOnHost, err := filepath.EvalSymlinks(resolvedDevicePath); err == nil {
- resolvedDevicePath = linkedPathOnHost
- }
- }
- st, err := os.Stat(resolvedDevicePath)
- if err != nil {
- return errors.Wrapf(err, "cannot stat %s", devicePath)
- }
- if st.IsDir() {
- found := false
- src := resolvedDevicePath
- dest := src
- var devmode string
- if len(devs) > 1 {
- if len(devs[1]) > 0 && devs[1][0] == '/' {
- dest = devs[1]
- } else {
- devmode = devs[1]
- }
- }
- if len(devs) > 2 {
- if devmode != "" {
- return errors.Wrapf(unix.EINVAL, "invalid device specification %s", devicePath)
- }
- devmode = devs[2]
- }
-
- // mount the internal devices recursively
- if err := filepath.Walk(resolvedDevicePath, func(dpath string, f os.FileInfo, e error) error {
-
- if f.Mode()&os.ModeDevice == os.ModeDevice {
- found = true
- device := fmt.Sprintf("%s:%s", dpath, filepath.Join(dest, strings.TrimPrefix(dpath, src)))
- if devmode != "" {
- device = fmt.Sprintf("%s:%s", device, devmode)
- }
- if err := addDevice(g, device); err != nil {
- return errors.Wrapf(err, "failed to add %s device", dpath)
- }
- }
- return nil
- }); err != nil {
- return err
- }
- if !found {
- return errors.Wrapf(unix.EINVAL, "no devices found in %s", devicePath)
- }
- return nil
- }
-
- return addDevice(g, strings.Join(append([]string{resolvedDevicePath}, devs[1:]...), ":"))
-}
-
-func deviceCgroupRules(g *generate.Generator, deviceCgroupRules []string) error {
- for _, deviceCgroupRule := range deviceCgroupRules {
- if err := validateDeviceCgroupRule(deviceCgroupRule); err != nil {
- return err
- }
- ss := parseDeviceCgroupRule(deviceCgroupRule)
- if len(ss[0]) != 5 {
- return errors.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule)
- }
- matches := ss[0]
- var major, minor *int64
- if matches[2] == "*" {
- majorDev := int64(-1)
- major = &majorDev
- } else {
- majorDev, err := strconv.ParseInt(matches[2], 10, 64)
- if err != nil {
- return errors.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule)
- }
- major = &majorDev
- }
- if matches[3] == "*" {
- minorDev := int64(-1)
- minor = &minorDev
- } else {
- minorDev, err := strconv.ParseInt(matches[2], 10, 64)
- if err != nil {
- return errors.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule)
- }
- minor = &minorDev
- }
- g.AddLinuxResourcesDevice(true, matches[1], major, minor, matches[4])
- }
- return nil
-}
-
-func addDevice(g *generate.Generator, device string) error {
- src, dst, permissions, err := ParseDevice(device)
- if err != nil {
- return err
- }
- dev, err := devices.DeviceFromPath(src, permissions)
- if err != nil {
- return errors.Wrapf(err, "%s is not a valid device", src)
- }
- if rootless.IsRootless() {
- if _, err := os.Stat(src); err != nil {
- if os.IsNotExist(err) {
- return errors.Wrapf(err, "the specified device %s doesn't exist", src)
- }
- return errors.Wrapf(err, "stat device %s exist", src)
- }
- perm := "ro"
- if strings.Contains(permissions, "w") {
- perm = "rw"
- }
- devMnt := spec.Mount{
- Destination: dst,
- Type: TypeBind,
- Source: src,
- Options: []string{"slave", "nosuid", "noexec", perm, "rbind"},
- }
- g.Config.Mounts = append(g.Config.Mounts, devMnt)
- return nil
- }
- dev.Path = dst
- linuxdev := spec.LinuxDevice{
- Path: dev.Path,
- Type: string(dev.Type),
- Major: dev.Major,
- Minor: dev.Minor,
- FileMode: &dev.FileMode,
- UID: &dev.Uid,
- GID: &dev.Gid,
- }
- g.AddDevice(linuxdev)
- g.AddLinuxResourcesDevice(true, string(dev.Type), &dev.Major, &dev.Minor, string(dev.Permissions))
- return nil
-}
-
-// based on getDevices from runc (libcontainer/devices/devices.go)
-func getDevices(path string) ([]*configs.Device, error) {
- files, err := ioutil.ReadDir(path)
- if err != nil {
- if rootless.IsRootless() && os.IsPermission(err) {
- return nil, nil
- }
- return nil, err
- }
- out := []*configs.Device{}
- for _, f := range files {
- switch {
- case f.IsDir():
- switch f.Name() {
- // ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825
- case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts":
- continue
- default:
- sub, err := getDevices(filepath.Join(path, f.Name()))
- if err != nil {
- return nil, err
- }
- if sub != nil {
- out = append(out, sub...)
- }
- continue
- }
- case f.Name() == "console":
- continue
- case f.Mode()&os.ModeSymlink != 0:
- // do not add symlink'd devices to privileged devices
- continue
- }
- device, err := devices.DeviceFromPath(filepath.Join(path, f.Name()), "rwm")
- if err != nil {
- if err == devices.ErrNotADevice {
- continue
- }
- if os.IsNotExist(err) {
- continue
- }
- return nil, err
- }
- out = append(out, device)
- }
- return out, nil
-}
-
-func addPrivilegedDevices(g *generate.Generator) error {
- hostDevices, err := getDevices("/dev")
- if err != nil {
- return err
- }
- g.ClearLinuxDevices()
-
- if rootless.IsRootless() {
- mounts := make(map[string]interface{})
- for _, m := range g.Mounts() {
- mounts[m.Destination] = true
- }
- newMounts := []spec.Mount{}
- for _, d := range hostDevices {
- devMnt := spec.Mount{
- Destination: d.Path,
- Type: TypeBind,
- Source: d.Path,
- Options: []string{"slave", "nosuid", "noexec", "rw", "rbind"},
- }
- if d.Path == "/dev/ptmx" || strings.HasPrefix(d.Path, "/dev/tty") {
- continue
- }
- if _, found := mounts[d.Path]; found {
- continue
- }
- st, err := os.Stat(d.Path)
- if err != nil {
- if err == unix.EPERM {
- continue
- }
- return errors.Wrapf(err, "stat %s", d.Path)
- }
- // Skip devices that the user has not access to.
- if st.Mode()&0007 == 0 {
- continue
- }
- newMounts = append(newMounts, devMnt)
- }
- g.Config.Mounts = append(newMounts, g.Config.Mounts...)
- g.Config.Linux.Resources.Devices = nil
- } else {
- for _, d := range hostDevices {
- g.AddDevice(Device(d))
- }
- // Add resources device - need to clear the existing one first.
- g.Config.Linux.Resources.Devices = nil
- g.AddLinuxResourcesDevice(true, "", nil, nil, "rwm")
- }
-
- return nil
-}
-
-func (c *CreateConfig) createBlockIO() (*spec.LinuxBlockIO, error) {
- var ret *spec.LinuxBlockIO
- bio := &spec.LinuxBlockIO{}
- if c.Resources.BlkioWeight > 0 {
- ret = bio
- bio.Weight = &c.Resources.BlkioWeight
- }
- if len(c.Resources.BlkioWeightDevice) > 0 {
- var lwds []spec.LinuxWeightDevice
- ret = bio
- for _, i := range c.Resources.BlkioWeightDevice {
- wd, err := ValidateweightDevice(i)
- if err != nil {
- return ret, errors.Wrapf(err, "invalid values for blkio-weight-device")
- }
- wdStat, err := GetStatFromPath(wd.Path)
- if err != nil {
- return ret, errors.Wrapf(err, "error getting stat from path %q", wd.Path)
- }
- lwd := spec.LinuxWeightDevice{
- Weight: &wd.Weight,
- }
- lwd.Major = int64(unix.Major(wdStat.Rdev))
- lwd.Minor = int64(unix.Minor(wdStat.Rdev))
- lwds = append(lwds, lwd)
- }
- bio.WeightDevice = lwds
- }
- if len(c.Resources.DeviceReadBps) > 0 {
- ret = bio
- readBps, err := makeThrottleArray(c.Resources.DeviceReadBps, bps)
- if err != nil {
- return ret, err
- }
- bio.ThrottleReadBpsDevice = readBps
- }
- if len(c.Resources.DeviceWriteBps) > 0 {
- ret = bio
- writeBpds, err := makeThrottleArray(c.Resources.DeviceWriteBps, bps)
- if err != nil {
- return ret, err
- }
- bio.ThrottleWriteBpsDevice = writeBpds
- }
- if len(c.Resources.DeviceReadIOps) > 0 {
- ret = bio
- readIOps, err := makeThrottleArray(c.Resources.DeviceReadIOps, iops)
- if err != nil {
- return ret, err
- }
- bio.ThrottleReadIOPSDevice = readIOps
- }
- if len(c.Resources.DeviceWriteIOps) > 0 {
- ret = bio
- writeIOps, err := makeThrottleArray(c.Resources.DeviceWriteIOps, iops)
- if err != nil {
- return ret, err
- }
- bio.ThrottleWriteIOPSDevice = writeIOps
- }
- return ret, nil
-}
-
-func makeThrottleArray(throttleInput []string, rateType int) ([]spec.LinuxThrottleDevice, error) {
- var (
- ltds []spec.LinuxThrottleDevice
- t *throttleDevice
- err error
- )
- for _, i := range throttleInput {
- if rateType == bps {
- t, err = validateBpsDevice(i)
- } else {
- t, err = validateIOpsDevice(i)
- }
- if err != nil {
- return []spec.LinuxThrottleDevice{}, err
- }
- ltdStat, err := GetStatFromPath(t.path)
- if err != nil {
- return ltds, errors.Wrapf(err, "error getting stat from path %q", t.path)
- }
- ltd := spec.LinuxThrottleDevice{
- Rate: t.rate,
- }
- ltd.Major = int64(unix.Major(ltdStat.Rdev))
- ltd.Minor = int64(unix.Minor(ltdStat.Rdev))
- ltds = append(ltds, ltd)
- }
- return ltds, nil
-}
-
-func GetStatFromPath(path string) (unix.Stat_t, error) {
- s := unix.Stat_t{}
- err := unix.Stat(path, &s)
- return s, err
-}
diff --git a/pkg/spec/config_linux_cgo.go b/pkg/spec/config_linux_cgo.go
deleted file mode 100644
index d0891b574..000000000
--- a/pkg/spec/config_linux_cgo.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// +build linux,cgo
-
-package createconfig
-
-import (
- "io/ioutil"
-
- goSeccomp "github.com/containers/common/pkg/seccomp"
- "github.com/containers/podman/v2/pkg/seccomp"
- spec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) {
- var seccompConfig *spec.LinuxSeccomp
- var err error
-
- if config.SeccompPolicy == seccomp.PolicyImage && config.SeccompProfileFromImage != "" {
- logrus.Debug("Loading seccomp profile from the security config")
- seccompConfig, err = goSeccomp.LoadProfile(config.SeccompProfileFromImage, configSpec)
- if err != nil {
- return nil, errors.Wrap(err, "loading seccomp profile failed")
- }
- return seccompConfig, nil
- }
-
- if config.SeccompProfilePath != "" {
- logrus.Debugf("Loading seccomp profile from %q", config.SeccompProfilePath)
- seccompProfile, err := ioutil.ReadFile(config.SeccompProfilePath)
- if err != nil {
- return nil, errors.Wrap(err, "opening seccomp profile failed")
- }
- seccompConfig, err = goSeccomp.LoadProfile(string(seccompProfile), configSpec)
- if err != nil {
- return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath)
- }
- } else {
- logrus.Debug("Loading default seccomp profile")
- seccompConfig, err = goSeccomp.GetDefaultProfile(configSpec)
- if err != nil {
- return nil, errors.Wrapf(err, "loading default seccomp profile failed")
- }
- }
-
- return seccompConfig, nil
-}
diff --git a/pkg/spec/config_linux_nocgo.go b/pkg/spec/config_linux_nocgo.go
deleted file mode 100644
index 8d720b6d4..000000000
--- a/pkg/spec/config_linux_nocgo.go
+++ /dev/null
@@ -1,11 +0,0 @@
-// +build linux,!cgo
-
-package createconfig
-
-import (
- spec "github.com/opencontainers/runtime-spec/specs-go"
-)
-
-func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) {
- return nil, nil
-}
diff --git a/pkg/spec/config_unsupported.go b/pkg/spec/config_unsupported.go
deleted file mode 100644
index 568afde55..000000000
--- a/pkg/spec/config_unsupported.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// +build !linux
-
-package createconfig
-
-import (
- spec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/opencontainers/runtime-tools/generate"
- "github.com/pkg/errors"
-)
-
-func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) {
- return nil, errors.New("function not supported on non-linux OS's")
-}
-func addDevice(g *generate.Generator, device string) error {
- return errors.New("function not implemented")
-}
-
-func addPrivilegedDevices(g *generate.Generator) error {
- return errors.New("function not implemented")
-}
-
-func (c *CreateConfig) createBlockIO() (*spec.LinuxBlockIO, error) {
- return nil, errors.New("function not implemented")
-}
-
-func makeThrottleArray(throttleInput []string, rateType int) ([]spec.LinuxThrottleDevice, error) {
- return nil, errors.New("function not implemented")
-}
-
-func DevicesFromPath(g *generate.Generator, devicePath string) error {
- return errors.New("function not implemented")
-}
-
-func deviceCgroupRules(g *generate.Generator, deviceCgroupRules []string) error {
- return errors.New("function not implemented")
-}
diff --git a/pkg/spec/containerconfig.go b/pkg/spec/containerconfig.go
deleted file mode 100644
index f11d85b7e..000000000
--- a/pkg/spec/containerconfig.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package createconfig
-
-import (
- "github.com/containers/podman/v2/libpod"
- "github.com/containers/podman/v2/libpod/define"
- spec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-// MakeContainerConfig generates all configuration necessary to start a
-// container with libpod from a completed CreateConfig struct.
-func (config *CreateConfig) MakeContainerConfig(runtime *libpod.Runtime, pod *libpod.Pod) (*spec.Spec, []libpod.CtrCreateOption, error) {
- if config.Pod != "" && pod == nil {
- return nil, nil, errors.Wrapf(define.ErrInvalidArg, "pod was specified but no pod passed")
- } else if config.Pod == "" && pod != nil {
- return nil, nil, errors.Wrapf(define.ErrInvalidArg, "pod was given but no pod is specified")
- }
-
- // Parse volumes flag into OCI spec mounts and libpod Named Volumes.
- // If there is an identical mount in the OCI spec, we will replace it
- // with a mount generated here.
- mounts, namedVolumes, err := config.parseVolumes(runtime)
- if err != nil {
- return nil, nil, err
- }
-
- runtimeSpec, err := config.createConfigToOCISpec(runtime, mounts)
- if err != nil {
- return nil, nil, err
- }
-
- options, err := config.getContainerCreateOptions(runtime, pod, mounts, namedVolumes)
- if err != nil {
- return nil, nil, err
- }
-
- logrus.Debugf("created OCI spec and options for new container")
-
- return runtimeSpec, options, nil
-}
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
deleted file mode 100644
index 4887e9262..000000000
--- a/pkg/spec/createconfig.go
+++ /dev/null
@@ -1,426 +0,0 @@
-package createconfig
-
-import (
- "context"
- "os"
- "strconv"
- "strings"
- "syscall"
-
- "github.com/containers/image/v5/manifest"
- "github.com/containers/podman/v2/libpod"
- "github.com/containers/podman/v2/libpod/define"
- "github.com/containers/podman/v2/pkg/namespaces"
- "github.com/containers/podman/v2/pkg/seccomp"
- "github.com/containers/storage"
- "github.com/docker/go-connections/nat"
- spec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/opencontainers/runtime-tools/generate"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-// Type constants
-const (
- bps = iota
- iops
-)
-
-// CreateResourceConfig represents resource elements in CreateConfig
-// structures
-type CreateResourceConfig struct {
- BlkioWeight uint16 // blkio-weight
- BlkioWeightDevice []string // blkio-weight-device
- CgroupConf map[string]string
- CPUPeriod uint64 // cpu-period
- CPUQuota int64 // cpu-quota
- CPURtPeriod uint64 // cpu-rt-period
- CPURtRuntime int64 // cpu-rt-runtime
- CPUShares uint64 // cpu-shares
- CPUs float64 // cpus
- CPUsetCPUs string
- CPUsetMems string // cpuset-mems
- DeviceCgroupRules []string //device-cgroup-rule
- DeviceReadBps []string // device-read-bps
- DeviceReadIOps []string // device-read-iops
- DeviceWriteBps []string // device-write-bps
- DeviceWriteIOps []string // device-write-iops
- DisableOomKiller bool // oom-kill-disable
- KernelMemory int64 // kernel-memory
- Memory int64 //memory
- MemoryReservation int64 // memory-reservation
- MemorySwap int64 //memory-swap
- MemorySwappiness int // memory-swappiness
- OomScoreAdj int //oom-score-adj
- PidsLimit int64 // pids-limit
- ShmSize int64
- Ulimit []string //ulimit
-}
-
-// PidConfig configures the pid namespace for the container
-type PidConfig struct {
- PidMode namespaces.PidMode //pid
-}
-
-// IpcConfig configures the ipc namespace for the container
-type IpcConfig struct {
- IpcMode namespaces.IpcMode //ipc
-}
-
-// CgroupConfig configures the cgroup namespace for the container
-type CgroupConfig struct {
- Cgroups string
- Cgroupns string
- CgroupParent string // cgroup-parent
- CgroupMode namespaces.CgroupMode //cgroup
-}
-
-// UserConfig configures the user namespace for the container
-type UserConfig struct {
- GroupAdd []string // group-add
- IDMappings *storage.IDMappingOptions
- UsernsMode namespaces.UsernsMode //userns
- User string //user
-}
-
-// UtsConfig configures the uts namespace for the container
-type UtsConfig struct {
- UtsMode namespaces.UTSMode //uts
- NoHosts bool
- HostAdd []string //add-host
- Hostname string
-}
-
-// NetworkConfig configures the network namespace for the container
-type NetworkConfig struct {
- DNSOpt []string //dns-opt
- DNSSearch []string //dns-search
- DNSServers []string //dns
- ExposedPorts map[nat.Port]struct{}
- HTTPProxy bool
- IP6Address string //ipv6
- IPAddress string //ip
- LinkLocalIP []string // link-local-ip
- MacAddress string //mac-address
- NetMode namespaces.NetworkMode //net
- Network string //network
- NetworkAlias []string //network-alias
- PortBindings nat.PortMap
- Publish []string //publish
- PublishAll bool //publish-all
-}
-
-// SecurityConfig configures the security features for the container
-type SecurityConfig struct {
- CapAdd []string // cap-add
- CapDrop []string // cap-drop
- CapRequired []string // cap-required
- LabelOpts []string //SecurityOpts
- NoNewPrivs bool //SecurityOpts
- ApparmorProfile string //SecurityOpts
- SeccompProfilePath string //SecurityOpts
- SeccompProfileFromImage string // seccomp profile from the container image
- SeccompPolicy seccomp.Policy
- SecurityOpts []string
- Privileged bool //privileged
- ReadOnlyRootfs bool //read-only
- ReadOnlyTmpfs bool //read-only-tmpfs
- Sysctl map[string]string //sysctl
- ProcOpts []string
-}
-
-// CreateConfig is a pre OCI spec structure. It represents user input from varlink or the CLI
-// swagger:model CreateConfig
-type CreateConfig struct {
- Annotations map[string]string
- Args []string
- CidFile string
- ConmonPidFile string
- Command []string // Full command that will be used
- UserCommand []string // User-entered command (or image CMD)
- Detach bool // detach
- Devices []string // device
- Entrypoint []string //entrypoint
- Env map[string]string //env
- HealthCheck *manifest.Schema2HealthConfig
- Init bool // init
- InitPath string //init-path
- Image string
- ImageID string
- RawImageName string
- BuiltinImgVolumes map[string]struct{} // volumes defined in the image config
- ImageVolumeType string // how to handle the image volume, either bind, tmpfs, or ignore
- Interactive bool //interactive
- Labels map[string]string //label
- LogDriver string // log-driver
- LogDriverOpt []string // log-opt
- Name string //name
- PodmanPath string
- Pod string //pod
- Quiet bool //quiet
- Resources CreateResourceConfig
- RestartPolicy string
- Rm bool //rm
- Rmi bool //rmi
- StopSignal syscall.Signal // stop-signal
- StopTimeout uint // stop-timeout
- Systemd bool
- Tmpfs []string // tmpfs
- Tty bool //tty
- Mounts []spec.Mount
- MountsFlag []string // mounts
- NamedVolumes []*libpod.ContainerNamedVolume
- Volumes []string //volume
- VolumesFrom []string
- WorkDir string //workdir
- Rootfs string
- Security SecurityConfig
- Syslog bool // Whether to enable syslog on exit commands
-
- // Namespaces
- Pid PidConfig
- Ipc IpcConfig
- Cgroup CgroupConfig
- User UserConfig
- Uts UtsConfig
- Network NetworkConfig
-}
-
-func u32Ptr(i int64) *uint32 { u := uint32(i); return &u }
-func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm }
-
-// CreateBlockIO returns a LinuxBlockIO struct from a CreateConfig
-func (c *CreateConfig) CreateBlockIO() (*spec.LinuxBlockIO, error) {
- return c.createBlockIO()
-}
-
-func (c *CreateConfig) createExitCommand(runtime *libpod.Runtime) ([]string, error) {
- config, err := runtime.GetConfig()
- if err != nil {
- return nil, err
- }
- storageConfig := runtime.StorageConfig()
-
- // We need a cleanup process for containers in the current model.
- // But we can't assume that the caller is Podman - it could be another
- // user of the API.
- // As such, provide a way to specify a path to Podman, so we can
- // still invoke a cleanup process.
- cmd := c.PodmanPath
- if cmd == "" {
- cmd, _ = os.Executable()
- }
-
- command := []string{cmd,
- "--root", storageConfig.GraphRoot,
- "--runroot", storageConfig.RunRoot,
- "--log-level", logrus.GetLevel().String(),
- "--cgroup-manager", config.Engine.CgroupManager,
- "--tmpdir", config.Engine.TmpDir,
- }
- if config.Engine.OCIRuntime != "" {
- command = append(command, []string{"--runtime", config.Engine.OCIRuntime}...)
- }
- if storageConfig.GraphDriverName != "" {
- command = append(command, []string{"--storage-driver", storageConfig.GraphDriverName}...)
- }
- for _, opt := range storageConfig.GraphDriverOptions {
- command = append(command, []string{"--storage-opt", opt}...)
- }
- if config.Engine.EventsLogger != "" {
- command = append(command, []string{"--events-backend", config.Engine.EventsLogger}...)
- }
-
- if c.Syslog {
- command = append(command, "--syslog", "true")
- }
- command = append(command, []string{"container", "cleanup"}...)
-
- if c.Rm {
- command = append(command, "--rm")
- }
-
- if c.Rmi {
- command = append(command, "--rmi")
- }
-
- return command, nil
-}
-
-// GetContainerCreateOptions takes a CreateConfig and returns a slice of CtrCreateOptions
-func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod, mounts []spec.Mount, namedVolumes []*libpod.ContainerNamedVolume) ([]libpod.CtrCreateOption, error) {
- var options []libpod.CtrCreateOption
- var err error
-
- if c.Interactive {
- options = append(options, libpod.WithStdin())
- }
- if c.Systemd {
- options = append(options, libpod.WithSystemd())
- }
- if c.Name != "" {
- logrus.Debugf("setting container name %s", c.Name)
- options = append(options, libpod.WithName(c.Name))
- }
- if c.Pod != "" {
- logrus.Debugf("adding container to pod %s", c.Pod)
- options = append(options, runtime.WithPod(pod))
- }
-
- // handle some spec from the InfraContainer when it's a pod
- if pod != nil && pod.HasInfraContainer() {
- InfraCtr, err := pod.InfraContainer()
- if err != nil {
- return nil, err
- }
- // handle the pod.spec.hostAliases
- options = append(options, libpod.WithHosts(InfraCtr.HostsAdd()))
- }
-
- if len(mounts) != 0 || len(namedVolumes) != 0 {
- destinations := []string{}
-
- // Take all mount and named volume destinations.
- for _, mount := range mounts {
- destinations = append(destinations, mount.Destination)
- }
- for _, volume := range namedVolumes {
- destinations = append(destinations, volume.Dest)
- }
-
- options = append(options, libpod.WithUserVolumes(destinations))
- }
-
- if len(namedVolumes) != 0 {
- options = append(options, libpod.WithNamedVolumes(namedVolumes))
- }
-
- if len(c.UserCommand) != 0 {
- options = append(options, libpod.WithCommand(c.UserCommand))
- }
-
- // Add entrypoint if it was set
- // If it's empty it's because it was explicitly set to ""
- if c.Entrypoint != nil {
- options = append(options, libpod.WithEntrypoint(c.Entrypoint))
- }
-
- // TODO: MNT, USER, CGROUP
- options = append(options, libpod.WithStopSignal(c.StopSignal))
- options = append(options, libpod.WithStopTimeout(c.StopTimeout))
-
- logPath, logTag := getLoggingOpts(c.LogDriverOpt)
- if logPath != "" {
- options = append(options, libpod.WithLogPath(logPath))
- }
- if logTag != "" {
- options = append(options, libpod.WithLogTag(logTag))
- }
-
- if c.LogDriver != "" {
- options = append(options, libpod.WithLogDriver(c.LogDriver))
- }
-
- secOpts, err := c.Security.ToCreateOptions()
- if err != nil {
- return nil, err
- }
- options = append(options, secOpts...)
-
- nsOpts, err := c.Cgroup.ToCreateOptions(runtime)
- if err != nil {
- return nil, err
- }
- options = append(options, nsOpts...)
-
- nsOpts, err = c.Ipc.ToCreateOptions(runtime)
- if err != nil {
- return nil, err
- }
- options = append(options, nsOpts...)
-
- nsOpts, err = c.Pid.ToCreateOptions(runtime)
- if err != nil {
- return nil, err
- }
- options = append(options, nsOpts...)
-
- nsOpts, err = c.Network.ToCreateOptions(runtime, &c.User)
- if err != nil {
- return nil, err
- }
- options = append(options, nsOpts...)
-
- nsOpts, err = c.Uts.ToCreateOptions(runtime, pod)
- if err != nil {
- return nil, err
- }
- options = append(options, nsOpts...)
-
- nsOpts, err = c.User.ToCreateOptions(runtime)
- if err != nil {
- return nil, err
- }
- options = append(options, nsOpts...)
-
- // Gather up the options for NewContainer which consist of With... funcs
- options = append(options, libpod.WithRootFSFromImage(c.ImageID, c.Image, c.RawImageName))
- options = append(options, libpod.WithConmonPidFile(c.ConmonPidFile))
- options = append(options, libpod.WithLabels(c.Labels))
- options = append(options, libpod.WithShmSize(c.Resources.ShmSize))
- if c.Rootfs != "" {
- options = append(options, libpod.WithRootFS(c.Rootfs))
- }
- // Default used if not overridden on command line
-
- if c.RestartPolicy != "" {
- if c.RestartPolicy == "unless-stopped" {
- return nil, errors.Wrapf(define.ErrInvalidArg, "the unless-stopped restart policy is not supported")
- }
-
- split := strings.Split(c.RestartPolicy, ":")
- if len(split) > 1 {
- numTries, err := strconv.Atoi(split[1])
- if err != nil {
- return nil, errors.Wrapf(err, "%s is not a valid number of retries for restart policy", split[1])
- }
- if numTries < 0 {
- return nil, errors.Wrapf(define.ErrInvalidArg, "restart policy requires a positive number of retries")
- }
- options = append(options, libpod.WithRestartRetries(uint(numTries)))
- }
- options = append(options, libpod.WithRestartPolicy(split[0]))
- }
-
- // Always use a cleanup process to clean up Podman after termination
- exitCmd, err := c.createExitCommand(runtime)
- if err != nil {
- return nil, err
- }
- options = append(options, libpod.WithExitCommand(exitCmd))
-
- if c.HealthCheck != nil {
- options = append(options, libpod.WithHealthCheck(c.HealthCheck))
- logrus.Debugf("New container has a health check")
- }
- return options, nil
-}
-
-// AddPrivilegedDevices iterates through host devices and adds all
-// host devices to the spec
-func AddPrivilegedDevices(g *generate.Generator) error {
- return addPrivilegedDevices(g)
-}
-
-func CreateContainerFromCreateConfig(ctx context.Context, r *libpod.Runtime, createConfig *CreateConfig, pod *libpod.Pod) (*libpod.Container, error) {
- runtimeSpec, options, err := createConfig.MakeContainerConfig(r, pod)
- if err != nil {
- return nil, err
- }
-
- ctr, err := r.NewContainer(ctx, runtimeSpec, options...)
- if err != nil {
- return nil, err
- }
- return ctr, nil
-}
diff --git a/pkg/spec/namespaces.go b/pkg/spec/namespaces.go
deleted file mode 100644
index 8f98a9786..000000000
--- a/pkg/spec/namespaces.go
+++ /dev/null
@@ -1,463 +0,0 @@
-package createconfig
-
-import (
- "net"
- "os"
- "strconv"
- "strings"
-
- "github.com/containers/podman/v2/libpod"
- "github.com/containers/podman/v2/libpod/define"
- "github.com/containers/podman/v2/pkg/cgroups"
- "github.com/cri-o/ocicni/pkg/ocicni"
- "github.com/docker/go-connections/nat"
- spec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/opencontainers/runtime-tools/generate"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-// DefaultKernelNamespaces is a comma-separated list of default kernel
-// namespaces.
-const DefaultKernelNamespaces = "cgroup,ipc,net,uts"
-
-// ToCreateOptions converts the input to a slice of container create options.
-func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserConfig) ([]libpod.CtrCreateOption, error) {
- var portBindings []ocicni.PortMapping
- var err error
- if len(c.PortBindings) > 0 {
- portBindings, err = NatToOCIPortBindings(c.PortBindings)
- if err != nil {
- return nil, errors.Wrapf(err, "unable to create port bindings")
- }
- }
-
- options := make([]libpod.CtrCreateOption, 0)
- userNetworks := c.NetMode.UserDefined()
- networks := make([]string, 0)
-
- if IsPod(userNetworks) {
- userNetworks = ""
- }
- if userNetworks != "" {
- for _, netName := range strings.Split(userNetworks, ",") {
- if netName == "" {
- return nil, errors.Errorf("container networks %q invalid", userNetworks)
- }
- networks = append(networks, netName)
- }
- }
-
- switch {
- case c.NetMode.IsNS():
- ns := c.NetMode.NS()
- if ns == "" {
- return nil, errors.Errorf("invalid empty user-defined network namespace")
- }
- _, err := os.Stat(ns)
- if err != nil {
- return nil, err
- }
- case c.NetMode.IsContainer():
- connectedCtr, err := runtime.LookupContainer(c.NetMode.Container())
- if err != nil {
- return nil, errors.Wrapf(err, "container %q not found", c.NetMode.Container())
- }
- options = append(options, libpod.WithNetNSFrom(connectedCtr))
- case !c.NetMode.IsHost() && !c.NetMode.IsNone():
- postConfigureNetNS := userns.getPostConfigureNetNS()
- options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks))
- }
-
- if len(c.DNSSearch) > 0 {
- options = append(options, libpod.WithDNSSearch(c.DNSSearch))
- }
- if len(c.DNSServers) > 0 {
- if len(c.DNSServers) == 1 && strings.ToLower(c.DNSServers[0]) == "none" {
- options = append(options, libpod.WithUseImageResolvConf())
- } else {
- options = append(options, libpod.WithDNS(c.DNSServers))
- }
- }
- if len(c.DNSOpt) > 0 {
- options = append(options, libpod.WithDNSOption(c.DNSOpt))
- }
- if c.IPAddress != "" {
- ip := net.ParseIP(c.IPAddress)
- if ip == nil {
- return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as IP address", c.IPAddress)
- } else if ip.To4() == nil {
- return nil, errors.Wrapf(define.ErrInvalidArg, "%s is not an IPv4 address", c.IPAddress)
- }
- options = append(options, libpod.WithStaticIP(ip))
- }
-
- if c.MacAddress != "" {
- mac, err := net.ParseMAC(c.MacAddress)
- if err != nil {
- return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as MAC address: %v", c.MacAddress, err)
- }
- options = append(options, libpod.WithStaticMAC(mac))
- }
-
- return options, nil
-}
-
-// ConfigureGenerator configures the generator based according to the current
-// state of the NetworkConfig.
-func (c *NetworkConfig) ConfigureGenerator(g *generate.Generator) error {
- netMode := c.NetMode
- netCtr := netMode.Container()
- switch {
- case netMode.IsHost():
- logrus.Debug("Using host netmode")
- if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil {
- return err
- }
- case netMode.IsNone():
- logrus.Debug("Using none netmode")
- case netMode.IsBridge():
- logrus.Debug("Using bridge netmode")
- case netCtr != "":
- logrus.Debugf("using container %s netmode", netCtr)
- case IsNS(string(netMode)):
- logrus.Debug("Using ns netmode")
- if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), NS(string(netMode))); err != nil {
- return err
- }
- case IsPod(string(netMode)):
- logrus.Debug("Using pod netmode, unless pod is not sharing")
- case netMode.IsSlirp4netns():
- logrus.Debug("Using slirp4netns netmode")
- case netMode.IsUserDefined():
- logrus.Debug("Using user defined netmode")
- default:
- return errors.Errorf("unknown network mode")
- }
-
- if c.HTTPProxy {
- for _, envSpec := range []string{
- "http_proxy",
- "HTTP_PROXY",
- "https_proxy",
- "HTTPS_PROXY",
- "ftp_proxy",
- "FTP_PROXY",
- "no_proxy",
- "NO_PROXY",
- } {
- envVal := os.Getenv(envSpec)
- if envVal != "" {
- g.AddProcessEnv(envSpec, envVal)
- }
- }
- }
-
- if g.Config.Annotations == nil {
- g.Config.Annotations = make(map[string]string)
- }
-
- if c.PublishAll {
- g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue
- } else {
- g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse
- }
-
- return nil
-}
-
-// NatToOCIPortBindings iterates a nat.portmap slice and creates []ocicni portmapping slice
-func NatToOCIPortBindings(ports nat.PortMap) ([]ocicni.PortMapping, error) {
- var portBindings []ocicni.PortMapping
- for containerPb, hostPb := range ports {
- var pm ocicni.PortMapping
- pm.ContainerPort = int32(containerPb.Int())
- for _, i := range hostPb {
- var hostPort int
- var err error
- pm.HostIP = i.HostIP
- if i.HostPort == "" {
- hostPort = containerPb.Int()
- } else {
- hostPort, err = strconv.Atoi(i.HostPort)
- if err != nil {
- return nil, errors.Wrapf(err, "unable to convert host port to integer")
- }
- }
-
- pm.HostPort = int32(hostPort)
- pm.Protocol = containerPb.Proto()
- portBindings = append(portBindings, pm)
- }
- }
- return portBindings, nil
-}
-
-// ToCreateOptions converts the input to container create options.
-func (c *CgroupConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
- options := make([]libpod.CtrCreateOption, 0)
- if c.CgroupMode.IsNS() {
- ns := c.CgroupMode.NS()
- if ns == "" {
- return nil, errors.Errorf("invalid empty user-defined network namespace")
- }
- _, err := os.Stat(ns)
- if err != nil {
- return nil, err
- }
- } else if c.CgroupMode.IsContainer() {
- connectedCtr, err := runtime.LookupContainer(c.CgroupMode.Container())
- if err != nil {
- return nil, errors.Wrapf(err, "container %q not found", c.CgroupMode.Container())
- }
- options = append(options, libpod.WithCgroupNSFrom(connectedCtr))
- }
-
- if c.CgroupParent != "" {
- options = append(options, libpod.WithCgroupParent(c.CgroupParent))
- }
-
- if c.Cgroups != "" {
- options = append(options, libpod.WithCgroupsMode(c.Cgroups))
- }
-
- return options, nil
-}
-
-// ToCreateOptions converts the input to container create options.
-func (c *UserConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
- options := make([]libpod.CtrCreateOption, 0)
- switch {
- case c.UsernsMode.IsNS():
- ns := c.UsernsMode.NS()
- if ns == "" {
- return nil, errors.Errorf("invalid empty user-defined user namespace")
- }
- _, err := os.Stat(ns)
- if err != nil {
- return nil, err
- }
- options = append(options, libpod.WithIDMappings(*c.IDMappings))
- case c.UsernsMode.IsContainer():
- connectedCtr, err := runtime.LookupContainer(c.UsernsMode.Container())
- if err != nil {
- return nil, errors.Wrapf(err, "container %q not found", c.UsernsMode.Container())
- }
- options = append(options, libpod.WithUserNSFrom(connectedCtr))
- default:
- options = append(options, libpod.WithIDMappings(*c.IDMappings))
- }
-
- options = append(options, libpod.WithUser(c.User))
- options = append(options, libpod.WithGroups(c.GroupAdd))
-
- return options, nil
-}
-
-// ConfigureGenerator configures the generator according to the current state
-// of the UserConfig.
-func (c *UserConfig) ConfigureGenerator(g *generate.Generator) error {
- if IsNS(string(c.UsernsMode)) {
- if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), NS(string(c.UsernsMode))); err != nil {
- return err
- }
- // runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping
- g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1))
- g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1))
- }
-
- if (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost() {
- if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
- return err
- }
- }
- for _, uidmap := range c.IDMappings.UIDMap {
- g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
- }
- for _, gidmap := range c.IDMappings.GIDMap {
- g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
- }
- return nil
-}
-
-func (c *UserConfig) getPostConfigureNetNS() bool {
- hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || c.UsernsMode.IsAuto() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
- postConfigureNetNS := hasUserns && !c.UsernsMode.IsHost()
- return postConfigureNetNS
-}
-
-// InNS returns true if the UserConfig indicates to be in a dedicated user
-// namespace.
-func (c *UserConfig) InNS(isRootless bool) bool {
- hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || c.UsernsMode.IsAuto() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
- return isRootless || (hasUserns && !c.UsernsMode.IsHost())
-}
-
-// ToCreateOptions converts the input to container create options.
-func (c *IpcConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
- options := make([]libpod.CtrCreateOption, 0)
- if c.IpcMode.IsHost() {
- options = append(options, libpod.WithShmDir("/dev/shm"))
- } else if c.IpcMode.IsContainer() {
- connectedCtr, err := runtime.LookupContainer(c.IpcMode.Container())
- if err != nil {
- return nil, errors.Wrapf(err, "container %q not found", c.IpcMode.Container())
- }
-
- options = append(options, libpod.WithIPCNSFrom(connectedCtr))
- options = append(options, libpod.WithShmDir(connectedCtr.ShmDir()))
- }
-
- return options, nil
-}
-
-// ConfigureGenerator configures the generator according to the current state
-// of the IpcConfig.
-func (c *IpcConfig) ConfigureGenerator(g *generate.Generator) error {
- ipcMode := c.IpcMode
- if IsNS(string(ipcMode)) {
- return g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), NS(string(ipcMode)))
- }
- if ipcMode.IsHost() {
- return g.RemoveLinuxNamespace(string(spec.IPCNamespace))
- }
- if ipcCtr := ipcMode.Container(); ipcCtr != "" {
- logrus.Debugf("Using container %s ipcmode", ipcCtr)
- }
-
- return nil
-}
-
-// ConfigureGenerator configures the generator according to the current state
-// of the CgroupConfig.
-func (c *CgroupConfig) ConfigureGenerator(g *generate.Generator) error {
- cgroupMode := c.CgroupMode
- if cgroupMode.IsDefaultValue() {
- // If the value is not specified, default to "private" on cgroups v2 and "host" on cgroups v1.
- unified, err := cgroups.IsCgroup2UnifiedMode()
- if err != nil {
- return err
- }
- if unified {
- cgroupMode = "private"
- } else {
- cgroupMode = "host"
- }
- }
- if cgroupMode.IsNS() {
- return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), NS(string(cgroupMode)))
- }
- if cgroupMode.IsHost() {
- return g.RemoveLinuxNamespace(string(spec.CgroupNamespace))
- }
- if cgroupMode.IsPrivate() {
- return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "")
- }
- if cgCtr := cgroupMode.Container(); cgCtr != "" {
- logrus.Debugf("Using container %s cgroup mode", cgCtr)
- }
- return nil
-}
-
-// ToCreateOptions converts the input to container create options.
-func (c *PidConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
- options := make([]libpod.CtrCreateOption, 0)
- if c.PidMode.IsContainer() {
- connectedCtr, err := runtime.LookupContainer(c.PidMode.Container())
- if err != nil {
- return nil, errors.Wrapf(err, "container %q not found", c.PidMode.Container())
- }
-
- options = append(options, libpod.WithPIDNSFrom(connectedCtr))
- }
-
- return options, nil
-}
-
-// ConfigureGenerator configures the generator according to the current state
-// of the PidConfig.
-func (c *PidConfig) ConfigureGenerator(g *generate.Generator) error {
- pidMode := c.PidMode
- if IsNS(string(pidMode)) {
- return g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), NS(string(pidMode)))
- }
- if pidMode.IsHost() {
- return g.RemoveLinuxNamespace(string(spec.PIDNamespace))
- }
- if pidCtr := pidMode.Container(); pidCtr != "" {
- logrus.Debugf("using container %s pidmode", pidCtr)
- }
- if IsPod(string(pidMode)) {
- logrus.Debug("using pod pidmode")
- }
- return nil
-}
-
-// ToCreateOptions converts the input to container create options.
-func (c *UtsConfig) ToCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod) ([]libpod.CtrCreateOption, error) {
- options := make([]libpod.CtrCreateOption, 0)
- if IsPod(string(c.UtsMode)) {
- options = append(options, libpod.WithUTSNSFromPod(pod))
- }
- if c.UtsMode.IsContainer() {
- connectedCtr, err := runtime.LookupContainer(c.UtsMode.Container())
- if err != nil {
- return nil, errors.Wrapf(err, "container %q not found", c.UtsMode.Container())
- }
-
- options = append(options, libpod.WithUTSNSFrom(connectedCtr))
- }
- if c.NoHosts {
- options = append(options, libpod.WithUseImageHosts())
- }
- if len(c.HostAdd) > 0 && !c.NoHosts {
- options = append(options, libpod.WithHosts(c.HostAdd))
- }
-
- return options, nil
-}
-
-// ConfigureGenerator configures the generator according to the current state
-// of the UtsConfig.
-func (c *UtsConfig) ConfigureGenerator(g *generate.Generator, net *NetworkConfig, runtime *libpod.Runtime) error {
- hostname := c.Hostname
- utsCtrID := c.UtsMode.Container()
- var err error
- if hostname == "" {
- switch {
- case utsCtrID != "":
- utsCtr, err := runtime.LookupContainer(utsCtrID)
- if err != nil {
- return errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", utsCtrID)
- }
- hostname = utsCtr.Hostname()
- case net.NetMode.IsHost() || c.UtsMode.IsHost():
- hostname, err = os.Hostname()
- if err != nil {
- return errors.Wrap(err, "unable to retrieve hostname of the host")
- }
- default:
- logrus.Debug("No hostname set; container's hostname will default to runtime default")
- }
- }
- g.RemoveHostname()
- if c.Hostname != "" || !c.UtsMode.IsHost() {
- // Set the hostname in the OCI configuration only
- // if specified by the user or if we are creating
- // a new UTS namespace.
- g.SetHostname(hostname)
- }
- g.AddProcessEnv("HOSTNAME", hostname)
-
- utsMode := c.UtsMode
- if IsNS(string(utsMode)) {
- return g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), NS(string(utsMode)))
- }
- if utsMode.IsHost() {
- return g.RemoveLinuxNamespace(string(spec.UTSNamespace))
- }
- if utsCtr := utsMode.Container(); utsCtr != "" {
- logrus.Debugf("using container %s utsmode", utsCtr)
- }
- return nil
-}
diff --git a/pkg/spec/parse.go b/pkg/spec/parse.go
deleted file mode 100644
index 9ebcf8d29..000000000
--- a/pkg/spec/parse.go
+++ /dev/null
@@ -1,225 +0,0 @@
-package createconfig
-
-import (
- "fmt"
- "regexp"
- "strconv"
- "strings"
-
- "github.com/docker/go-units"
- "github.com/pkg/errors"
-)
-
-// deviceCgroupRulegex defines the valid format of device-cgroup-rule
-var deviceCgroupRuleRegex = regexp.MustCompile(`^([acb]) ([0-9]+|\*):([0-9]+|\*) ([rwm]{1,3})$`)
-
-// Pod signifies a kernel namespace is being shared
-// by a container with the pod it is associated with
-const Pod = "pod"
-
-// weightDevice is a structure that holds device:weight pair
-type weightDevice struct {
- Path string
- Weight uint16
-}
-
-func (w *weightDevice) String() string {
- return fmt.Sprintf("%s:%d", w.Path, w.Weight)
-}
-
-// LinuxNS is a struct that contains namespace information
-// It implemented Valid to show it is a valid namespace
-type LinuxNS interface {
- Valid() bool
-}
-
-// IsNS returns if the specified string has a ns: prefix
-func IsNS(s string) bool {
- parts := strings.SplitN(s, ":", 2)
- return len(parts) > 1 && parts[0] == "ns"
-}
-
-// IsPod returns if the specified string is pod
-func IsPod(s string) bool {
- return s == Pod
-}
-
-// Valid checks the validity of a linux namespace
-// s should be the string representation of ns
-func Valid(s string, ns LinuxNS) bool {
- return IsPod(s) || IsNS(s) || ns.Valid()
-}
-
-// NS is the path to the namespace to join.
-func NS(s string) string {
- parts := strings.SplitN(s, ":", 2)
- if len(parts) > 1 {
- return parts[1]
- }
- return ""
-}
-
-// ValidateweightDevice validates that the specified string has a valid device-weight format
-// for blkio-weight-device flag
-func ValidateweightDevice(val string) (*weightDevice, error) {
- split := strings.SplitN(val, ":", 2)
- if len(split) != 2 {
- return nil, fmt.Errorf("bad format: %s", val)
- }
- if !strings.HasPrefix(split[0], "/dev/") {
- return nil, fmt.Errorf("bad format for device path: %s", val)
- }
- weight, err := strconv.ParseUint(split[1], 10, 0)
- if err != nil {
- return nil, fmt.Errorf("invalid weight for device: %s", val)
- }
- if weight > 0 && (weight < 10 || weight > 1000) {
- return nil, fmt.Errorf("invalid weight for device: %s", val)
- }
-
- return &weightDevice{
- Path: split[0],
- Weight: uint16(weight),
- }, nil
-}
-
-// throttleDevice is a structure that holds device:rate_per_second pair
-type throttleDevice struct {
- path string
- rate uint64
-}
-
-func (t *throttleDevice) String() string {
- return fmt.Sprintf("%s:%d", t.path, t.rate)
-}
-
-// validateBpsDevice validates that the specified string has a valid device-rate format
-// for device-read-bps and device-write-bps flags
-func validateBpsDevice(val string) (*throttleDevice, error) {
- split := strings.SplitN(val, ":", 2)
- if len(split) != 2 {
- return nil, fmt.Errorf("bad format: %s", val)
- }
- if !strings.HasPrefix(split[0], "/dev/") {
- return nil, fmt.Errorf("bad format for device path: %s", val)
- }
- rate, err := units.RAMInBytes(split[1])
- if err != nil {
- return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
- }
- if rate < 0 {
- return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
- }
-
- return &throttleDevice{
- path: split[0],
- rate: uint64(rate),
- }, nil
-}
-
-// validateIOpsDevice validates that the specified string has a valid device-rate format
-// for device-write-iops and device-read-iops flags
-func validateIOpsDevice(val string) (*throttleDevice, error) { //nolint
- split := strings.SplitN(val, ":", 2)
- if len(split) != 2 {
- return nil, fmt.Errorf("bad format: %s", val)
- }
- if !strings.HasPrefix(split[0], "/dev/") {
- return nil, fmt.Errorf("bad format for device path: %s", val)
- }
- rate, err := strconv.ParseUint(split[1], 10, 64)
- if err != nil {
- return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", val)
- }
- return &throttleDevice{
- path: split[0],
- rate: rate,
- }, nil
-}
-
-// getLoggingOpts splits the path= and tag= options provided to --log-opt.
-func getLoggingOpts(opts []string) (string, string) {
- var path, tag string
- for _, opt := range opts {
- arr := strings.SplitN(opt, "=", 2)
- if len(arr) == 2 {
- if strings.TrimSpace(arr[0]) == "path" {
- path = strings.TrimSpace(arr[1])
- } else if strings.TrimSpace(arr[0]) == "tag" {
- tag = strings.TrimSpace(arr[1])
- }
- }
- if path != "" && tag != "" {
- break
- }
- }
- return path, tag
-}
-
-// ParseDevice parses device mapping string to a src, dest & permissions string
-func ParseDevice(device string) (string, string, string, error) { //nolint
- src := ""
- dst := ""
- permissions := "rwm"
- arr := strings.Split(device, ":")
- switch len(arr) {
- case 3:
- if !IsValidDeviceMode(arr[2]) {
- return "", "", "", fmt.Errorf("invalid device mode: %s", arr[2])
- }
- permissions = arr[2]
- fallthrough
- case 2:
- if IsValidDeviceMode(arr[1]) {
- permissions = arr[1]
- } else {
- if len(arr[1]) == 0 || arr[1][0] != '/' {
- return "", "", "", fmt.Errorf("invalid device mode: %s", arr[1])
- }
- dst = arr[1]
- }
- fallthrough
- case 1:
- src = arr[0]
- default:
- return "", "", "", fmt.Errorf("invalid device specification: %s", device)
- }
-
- if dst == "" {
- dst = src
- }
- return src, dst, permissions, nil
-}
-
-// IsValidDeviceMode checks if the mode for device is valid or not.
-// IsValid mode is a composition of r (read), w (write), and m (mknod).
-func IsValidDeviceMode(mode string) bool {
- var legalDeviceMode = map[rune]bool{
- 'r': true,
- 'w': true,
- 'm': true,
- }
- if mode == "" {
- return false
- }
- for _, c := range mode {
- if !legalDeviceMode[c] {
- return false
- }
- legalDeviceMode[c] = false
- }
- return true
-}
-
-// validateDeviceCgroupRule validates the format of deviceCgroupRule
-func validateDeviceCgroupRule(deviceCgroupRule string) error {
- if !deviceCgroupRuleRegex.MatchString(deviceCgroupRule) {
- return errors.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule)
- }
- return nil
-}
-
-// parseDeviceCgroupRule matches and parses the deviceCgroupRule into slice
-func parseDeviceCgroupRule(deviceCgroupRule string) [][]string {
- return deviceCgroupRuleRegex.FindAllStringSubmatch(deviceCgroupRule, -1)
-}
diff --git a/pkg/spec/ports.go b/pkg/spec/ports.go
deleted file mode 100644
index bdd26bd83..000000000
--- a/pkg/spec/ports.go
+++ /dev/null
@@ -1,107 +0,0 @@
-package createconfig
-
-import (
- "fmt"
- "net"
- "strconv"
-
- "github.com/docker/go-connections/nat"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-// ExposedPorts parses user and image ports and returns binding information
-func ExposedPorts(expose, publish []string, publishAll bool, imageExposedPorts map[string]struct{}) (map[nat.Port][]nat.PortBinding, error) {
- containerPorts := make(map[string]string)
-
- // add expose ports from the image itself
- for expose := range imageExposedPorts {
- _, port := nat.SplitProtoPort(expose)
- containerPorts[port] = ""
- }
-
- // add the expose ports from the user (--expose)
- // can be single or a range
- for _, expose := range expose {
- //support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
- _, port := nat.SplitProtoPort(expose)
- //parse the start and end port and create a sequence of ports to expose
- //if expose a port, the start and end port are the same
- start, end, err := nat.ParsePortRange(port)
- if err != nil {
- return nil, fmt.Errorf("invalid range format for --expose: %s, error: %s", expose, err)
- }
- for i := start; i <= end; i++ {
- containerPorts[strconv.Itoa(int(i))] = ""
- }
- }
-
- // parse user inputted port bindings
- pbPorts, portBindings, err := nat.ParsePortSpecs(publish)
- if err != nil {
- return nil, err
- }
-
- // delete exposed container ports if being used by -p
- for i := range pbPorts {
- delete(containerPorts, i.Port())
- }
-
- // iterate container ports and make port bindings from them
- if publishAll {
- for e := range containerPorts {
- //support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
- //proto, port := nat.SplitProtoPort(e)
- p, err := nat.NewPort("tcp", e)
- if err != nil {
- return nil, err
- }
- rp, err := getRandomPort()
- if err != nil {
- return nil, err
- }
- logrus.Debug(fmt.Sprintf("Using random host port %d with container port %d", rp, p.Int()))
- portBindings[p] = CreatePortBinding(rp, "")
- }
- }
-
- // We need to see if any host ports are not populated and if so, we need to assign a
- // random port to them.
- for k, pb := range portBindings {
- if pb[0].HostPort == "" {
- hostPort, err := getRandomPort()
- if err != nil {
- return nil, err
- }
- logrus.Debug(fmt.Sprintf("Using random host port %d with container port %s", hostPort, k.Port()))
- pb[0].HostPort = strconv.Itoa(hostPort)
- }
- }
- return portBindings, nil
-}
-
-func getRandomPort() (int, error) {
- l, err := net.Listen("tcp", ":0")
- if err != nil {
- return 0, errors.Wrapf(err, "unable to get free port")
- }
- defer l.Close()
- _, randomPort, err := net.SplitHostPort(l.Addr().String())
- if err != nil {
- return 0, errors.Wrapf(err, "unable to determine free port")
- }
- rp, err := strconv.Atoi(randomPort)
- if err != nil {
- return 0, errors.Wrapf(err, "unable to convert random port to int")
- }
- return rp, nil
-}
-
-//CreatePortBinding takes port (int) and IP (string) and creates an array of portbinding structs
-func CreatePortBinding(hostPort int, hostIP string) []nat.PortBinding {
- pb := nat.PortBinding{
- HostPort: strconv.Itoa(hostPort),
- }
- pb.HostIP = hostIP
- return []nat.PortBinding{pb}
-}
diff --git a/pkg/spec/security.go b/pkg/spec/security.go
deleted file mode 100644
index 5f7db7edb..000000000
--- a/pkg/spec/security.go
+++ /dev/null
@@ -1,204 +0,0 @@
-package createconfig
-
-import (
- "fmt"
- "strings"
-
- "github.com/containers/common/pkg/capabilities"
- "github.com/containers/podman/v2/libpod"
- "github.com/containers/podman/v2/libpod/define"
- "github.com/containers/podman/v2/pkg/util"
- "github.com/opencontainers/runtime-tools/generate"
- "github.com/opencontainers/selinux/go-selinux/label"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-// ToCreateOptions convert the SecurityConfig to a slice of container create
-// options.
-func (c *SecurityConfig) ToCreateOptions() ([]libpod.CtrCreateOption, error) {
- options := make([]libpod.CtrCreateOption, 0)
- options = append(options, libpod.WithSecLabels(c.LabelOpts))
- options = append(options, libpod.WithPrivileged(c.Privileged))
- return options, nil
-}
-
-// SetLabelOpts sets the label options of the SecurityConfig according to the
-// input.
-func (c *SecurityConfig) SetLabelOpts(runtime *libpod.Runtime, pidConfig *PidConfig, ipcConfig *IpcConfig) error {
- if c.Privileged {
- c.LabelOpts = label.DisableSecOpt()
- return nil
- }
-
- var labelOpts []string
- if pidConfig.PidMode.IsHost() {
- labelOpts = append(labelOpts, label.DisableSecOpt()...)
- } else if pidConfig.PidMode.IsContainer() {
- ctr, err := runtime.LookupContainer(pidConfig.PidMode.Container())
- if err != nil {
- return errors.Wrapf(err, "container %q not found", pidConfig.PidMode.Container())
- }
- secopts, err := label.DupSecOpt(ctr.ProcessLabel())
- if err != nil {
- return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
- }
- labelOpts = append(labelOpts, secopts...)
- }
-
- if ipcConfig.IpcMode.IsHost() {
- labelOpts = append(labelOpts, label.DisableSecOpt()...)
- } else if ipcConfig.IpcMode.IsContainer() {
- ctr, err := runtime.LookupContainer(ipcConfig.IpcMode.Container())
- if err != nil {
- return errors.Wrapf(err, "container %q not found", ipcConfig.IpcMode.Container())
- }
- secopts, err := label.DupSecOpt(ctr.ProcessLabel())
- if err != nil {
- return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
- }
- labelOpts = append(labelOpts, secopts...)
- }
-
- c.LabelOpts = append(c.LabelOpts, labelOpts...)
- return nil
-}
-
-// SetSecurityOpts the the security options (labels, apparmor, seccomp, etc.).
-func (c *SecurityConfig) SetSecurityOpts(runtime *libpod.Runtime, securityOpts []string) error {
- for _, opt := range securityOpts {
- if opt == "no-new-privileges" {
- c.NoNewPrivs = true
- } else {
- con := strings.SplitN(opt, "=", 2)
- if len(con) != 2 {
- return fmt.Errorf("invalid --security-opt 1: %q", opt)
- }
-
- switch con[0] {
- case "proc-opts":
- c.ProcOpts = strings.Split(con[1], ",")
- case "label":
- c.LabelOpts = append(c.LabelOpts, con[1])
- case "apparmor":
- c.ApparmorProfile = con[1]
- case "seccomp":
- c.SeccompProfilePath = con[1]
- default:
- return fmt.Errorf("invalid --security-opt 2: %q", opt)
- }
- }
- }
-
- if c.SeccompProfilePath == "" {
- var err error
- c.SeccompProfilePath, err = libpod.DefaultSeccompPath()
- if err != nil {
- return err
- }
- }
- c.SecurityOpts = securityOpts
- return nil
-}
-
-// ConfigureGenerator configures the generator according to the input.
-func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserConfig) error {
- // HANDLE CAPABILITIES
- // NOTE: Must happen before SECCOMP
- if c.Privileged {
- g.SetupPrivileged(true)
- }
-
- useNotRoot := func(user string) bool {
- if user == "" || user == "root" || user == "0" {
- return false
- }
- return true
- }
-
- configSpec := g.Config
- var err error
- var defaultCaplist []string
- bounding := configSpec.Process.Capabilities.Bounding
- if useNotRoot(user.User) {
- configSpec.Process.Capabilities.Bounding = defaultCaplist
- }
- defaultCaplist, err = capabilities.MergeCapabilities(configSpec.Process.Capabilities.Bounding, c.CapAdd, c.CapDrop)
- if err != nil {
- return err
- }
-
- privCapRequired := []string{}
-
- if !c.Privileged && len(c.CapRequired) > 0 {
- // Pass CapRequired in CapAdd field to normalize capabilities names
- capRequired, err := capabilities.MergeCapabilities(nil, c.CapRequired, nil)
- if err != nil {
- logrus.Errorf("capabilities requested by user or image are not valid: %q", strings.Join(c.CapRequired, ","))
- } else {
- // Verify all capRequiered are in the defaultCapList
- for _, cap := range capRequired {
- if !util.StringInSlice(cap, defaultCaplist) {
- privCapRequired = append(privCapRequired, cap)
- }
- }
- }
- if len(privCapRequired) == 0 {
- defaultCaplist = capRequired
- } else {
- logrus.Errorf("capabilities requested by user or image are not allowed by default: %q", strings.Join(privCapRequired, ","))
- }
- }
- configSpec.Process.Capabilities.Bounding = defaultCaplist
- configSpec.Process.Capabilities.Permitted = defaultCaplist
- configSpec.Process.Capabilities.Inheritable = defaultCaplist
- configSpec.Process.Capabilities.Effective = defaultCaplist
- configSpec.Process.Capabilities.Ambient = defaultCaplist
- if useNotRoot(user.User) {
- defaultCaplist, err = capabilities.MergeCapabilities(bounding, c.CapAdd, c.CapDrop)
- if err != nil {
- return err
- }
- }
- configSpec.Process.Capabilities.Bounding = defaultCaplist
-
- // HANDLE SECCOMP
- if c.SeccompProfilePath != "unconfined" {
- seccompConfig, err := getSeccompConfig(c, configSpec)
- if err != nil {
- return err
- }
- configSpec.Linux.Seccomp = seccompConfig
- }
-
- // Clear default Seccomp profile from Generator for privileged containers
- if c.SeccompProfilePath == "unconfined" || c.Privileged {
- configSpec.Linux.Seccomp = nil
- }
-
- for _, opt := range c.SecurityOpts {
- // Split on both : and =
- splitOpt := strings.SplitN(opt, "=", 2)
- if len(splitOpt) == 1 {
- splitOpt = strings.Split(opt, ":")
- }
- if len(splitOpt) < 2 {
- continue
- }
- switch splitOpt[0] {
- case "label":
- configSpec.Annotations[define.InspectAnnotationLabel] = splitOpt[1]
- case "seccomp":
- configSpec.Annotations[define.InspectAnnotationSeccomp] = splitOpt[1]
- case "apparmor":
- configSpec.Annotations[define.InspectAnnotationApparmor] = splitOpt[1]
- }
- }
-
- g.SetRootReadonly(c.ReadOnlyRootfs)
- for sysctlKey, sysctlVal := range c.Sysctl {
- g.AddLinuxSysctl(sysctlKey, sysctlVal)
- }
-
- return nil
-}
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
deleted file mode 100644
index 81620997f..000000000
--- a/pkg/spec/spec.go
+++ /dev/null
@@ -1,593 +0,0 @@
-package createconfig
-
-import (
- "strings"
-
- "github.com/containers/common/pkg/capabilities"
- cconfig "github.com/containers/common/pkg/config"
- "github.com/containers/common/pkg/sysinfo"
- "github.com/containers/podman/v2/libpod"
- "github.com/containers/podman/v2/libpod/define"
- "github.com/containers/podman/v2/pkg/cgroups"
- "github.com/containers/podman/v2/pkg/env"
- "github.com/containers/podman/v2/pkg/rootless"
- "github.com/containers/podman/v2/pkg/util"
- "github.com/docker/go-units"
- "github.com/opencontainers/runc/libcontainer/user"
- spec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/opencontainers/runtime-tools/generate"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
- "golang.org/x/sys/unix"
-)
-
-const CpuPeriod = 100000
-
-func GetAvailableGids() (int64, error) {
- idMap, err := user.ParseIDMapFile("/proc/self/gid_map")
- if err != nil {
- return 0, err
- }
- count := int64(0)
- for _, r := range idMap {
- count += r.Count
- }
- return count, nil
-}
-
-// CreateConfigToOCISpec parses information needed to create a container into an OCI runtime spec
-func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userMounts []spec.Mount) (*spec.Spec, error) {
- cgroupPerm := "ro"
- g, err := generate.New("linux")
- if err != nil {
- return nil, err
- }
- // Remove the default /dev/shm mount to ensure we overwrite it
- g.RemoveMount("/dev/shm")
- g.HostSpecific = true
- addCgroup := true
- canMountSys := true
-
- isRootless := rootless.IsRootless()
- inUserNS := config.User.InNS(isRootless)
-
- if inUserNS && config.Network.NetMode.IsHost() {
- canMountSys = false
- }
-
- if config.Security.Privileged && canMountSys {
- cgroupPerm = "rw"
- g.RemoveMount("/sys")
- sysMnt := spec.Mount{
- Destination: "/sys",
- Type: "sysfs",
- Source: "sysfs",
- Options: []string{"rprivate", "nosuid", "noexec", "nodev", "rw"},
- }
- g.AddMount(sysMnt)
- } else if !canMountSys {
- addCgroup = false
- g.RemoveMount("/sys")
- r := "ro"
- if config.Security.Privileged {
- r = "rw"
- }
- sysMnt := spec.Mount{
- Destination: "/sys",
- Type: TypeBind,
- Source: "/sys",
- Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"},
- }
- g.AddMount(sysMnt)
- if !config.Security.Privileged && isRootless {
- g.AddLinuxMaskedPaths("/sys/kernel")
- }
- }
- var runtimeConfig *cconfig.Config
-
- if runtime != nil {
- runtimeConfig, err = runtime.GetConfig()
- if err != nil {
- return nil, err
- }
- g.Config.Process.Capabilities.Bounding = runtimeConfig.Containers.DefaultCapabilities
- sysctls, err := util.ValidateSysctls(runtimeConfig.Containers.DefaultSysctls)
- if err != nil {
- return nil, err
- }
-
- for name, val := range config.Security.Sysctl {
- sysctls[name] = val
- }
- config.Security.Sysctl = sysctls
- if !util.StringInSlice("host", config.Resources.Ulimit) {
- config.Resources.Ulimit = append(runtimeConfig.Containers.DefaultUlimits, config.Resources.Ulimit...)
- }
- if config.Resources.PidsLimit < 0 && !config.cgroupDisabled() {
- config.Resources.PidsLimit = runtimeConfig.Containers.PidsLimit
- }
-
- } else {
- g.Config.Process.Capabilities.Bounding = cconfig.DefaultCapabilities
- if config.Resources.PidsLimit < 0 && !config.cgroupDisabled() {
- config.Resources.PidsLimit = cconfig.DefaultPidsLimit
- }
- }
-
- gid5Available := true
- if isRootless {
- nGids, err := GetAvailableGids()
- if err != nil {
- return nil, err
- }
- gid5Available = nGids >= 5
- }
- // When using a different user namespace, check that the GID 5 is mapped inside
- // the container.
- if gid5Available && len(config.User.IDMappings.GIDMap) > 0 {
- mappingFound := false
- for _, r := range config.User.IDMappings.GIDMap {
- if r.ContainerID <= 5 && 5 < r.ContainerID+r.Size {
- mappingFound = true
- break
- }
- }
- if !mappingFound {
- gid5Available = false
- }
-
- }
- if !gid5Available {
- // If we have no GID mappings, the gid=5 default option would fail, so drop it.
- g.RemoveMount("/dev/pts")
- devPts := spec.Mount{
- Destination: "/dev/pts",
- Type: "devpts",
- Source: "devpts",
- Options: []string{"rprivate", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"},
- }
- g.AddMount(devPts)
- }
-
- if inUserNS && config.Ipc.IpcMode.IsHost() {
- g.RemoveMount("/dev/mqueue")
- devMqueue := spec.Mount{
- Destination: "/dev/mqueue",
- Type: TypeBind,
- Source: "/dev/mqueue",
- Options: []string{"bind", "nosuid", "noexec", "nodev"},
- }
- g.AddMount(devMqueue)
- }
- if inUserNS && config.Pid.PidMode.IsHost() {
- g.RemoveMount("/proc")
- procMount := spec.Mount{
- Destination: "/proc",
- Type: TypeBind,
- Source: "/proc",
- Options: []string{"rbind", "nosuid", "noexec", "nodev"},
- }
- g.AddMount(procMount)
- }
-
- if addCgroup {
- cgroupMnt := spec.Mount{
- Destination: "/sys/fs/cgroup",
- Type: "cgroup",
- Source: "cgroup",
- Options: []string{"rprivate", "nosuid", "noexec", "nodev", "relatime", cgroupPerm},
- }
- g.AddMount(cgroupMnt)
- }
- g.SetProcessCwd(config.WorkDir)
-
- ProcessArgs := make([]string, 0)
- // We need to iterate the input for entrypoint because it is a []string
- // but "" is a legit json input, which translates into a []string with an
- // empty position. This messes up the eventual command being executed
- // in the container
- for _, a := range config.Entrypoint {
- if len(a) > 0 {
- ProcessArgs = append(ProcessArgs, a)
- }
- }
- // Same issue as explained above for config.Entrypoint.
- for _, a := range config.Command {
- if len(a) > 0 {
- ProcessArgs = append(ProcessArgs, a)
- }
- }
-
- g.SetProcessArgs(ProcessArgs)
- g.SetProcessTerminal(config.Tty)
-
- for key, val := range config.Annotations {
- g.AddAnnotation(key, val)
- }
-
- addedResources := false
-
- // RESOURCES - MEMORY
- if config.Resources.Memory != 0 {
- g.SetLinuxResourcesMemoryLimit(config.Resources.Memory)
- // If a swap limit is not explicitly set, also set a swap limit
- // Default to double the memory limit
- if config.Resources.MemorySwap == 0 {
- g.SetLinuxResourcesMemorySwap(2 * config.Resources.Memory)
- }
- addedResources = true
- }
- if config.Resources.MemoryReservation != 0 {
- g.SetLinuxResourcesMemoryReservation(config.Resources.MemoryReservation)
- addedResources = true
- }
- if config.Resources.MemorySwap != 0 {
- g.SetLinuxResourcesMemorySwap(config.Resources.MemorySwap)
- addedResources = true
- }
- if config.Resources.KernelMemory != 0 {
- g.SetLinuxResourcesMemoryKernel(config.Resources.KernelMemory)
- addedResources = true
- }
- if config.Resources.MemorySwappiness != -1 {
- g.SetLinuxResourcesMemorySwappiness(uint64(config.Resources.MemorySwappiness))
- addedResources = true
- }
- g.SetLinuxResourcesMemoryDisableOOMKiller(config.Resources.DisableOomKiller)
- g.SetProcessOOMScoreAdj(config.Resources.OomScoreAdj)
-
- // RESOURCES - CPU
- if config.Resources.CPUShares != 0 {
- g.SetLinuxResourcesCPUShares(config.Resources.CPUShares)
- addedResources = true
- }
- if config.Resources.CPUQuota != 0 {
- g.SetLinuxResourcesCPUQuota(config.Resources.CPUQuota)
- addedResources = true
- }
- if config.Resources.CPUPeriod != 0 {
- g.SetLinuxResourcesCPUPeriod(config.Resources.CPUPeriod)
- addedResources = true
- }
- if config.Resources.CPUs != 0 {
- g.SetLinuxResourcesCPUPeriod(CpuPeriod)
- g.SetLinuxResourcesCPUQuota(int64(config.Resources.CPUs * CpuPeriod))
- addedResources = true
- }
- if config.Resources.CPURtRuntime != 0 {
- g.SetLinuxResourcesCPURealtimeRuntime(config.Resources.CPURtRuntime)
- addedResources = true
- }
- if config.Resources.CPURtPeriod != 0 {
- g.SetLinuxResourcesCPURealtimePeriod(config.Resources.CPURtPeriod)
- addedResources = true
- }
- if config.Resources.CPUsetCPUs != "" {
- g.SetLinuxResourcesCPUCpus(config.Resources.CPUsetCPUs)
- addedResources = true
- }
- if config.Resources.CPUsetMems != "" {
- g.SetLinuxResourcesCPUMems(config.Resources.CPUsetMems)
- addedResources = true
- }
-
- // Devices
- if config.Security.Privileged {
- // If privileged, we need to add all the host devices to the
- // spec. We do not add the user provided ones because we are
- // already adding them all.
- if err := AddPrivilegedDevices(&g); err != nil {
- return nil, err
- }
- } else {
- for _, devicePath := range config.Devices {
- if err := DevicesFromPath(&g, devicePath); err != nil {
- return nil, err
- }
- }
- if len(config.Resources.DeviceCgroupRules) != 0 {
- if err := deviceCgroupRules(&g, config.Resources.DeviceCgroupRules); err != nil {
- return nil, err
- }
- addedResources = true
- }
- }
-
- g.SetProcessNoNewPrivileges(config.Security.NoNewPrivs)
-
- if !config.Security.Privileged {
- g.SetProcessApparmorProfile(config.Security.ApparmorProfile)
- }
-
- // Unless already set via the CLI, check if we need to disable process
- // labels or set the defaults.
- if len(config.Security.LabelOpts) == 0 && runtimeConfig != nil {
- if !runtimeConfig.Containers.EnableLabeling {
- // Disabled in the config.
- config.Security.LabelOpts = append(config.Security.LabelOpts, "disable")
- } else if err := config.Security.SetLabelOpts(runtime, &config.Pid, &config.Ipc); err != nil {
- // Defaults!
- return nil, err
- }
- }
-
- BlockAccessToKernelFilesystems(config.Security.Privileged, config.Pid.PidMode.IsHost(), &g)
-
- // RESOURCES - PIDS
- if config.Resources.PidsLimit > 0 {
- // if running on rootless on a cgroupv1 machine or using the cgroupfs manager, pids
- // limit is not supported. If the value is still the default
- // then ignore the settings. If the caller asked for a
- // non-default, then try to use it.
- setPidLimit := true
- if rootless.IsRootless() {
- cgroup2, err := cgroups.IsCgroup2UnifiedMode()
- if err != nil {
- return nil, err
- }
- if (!cgroup2 || (runtimeConfig != nil && runtimeConfig.Engine.CgroupManager != cconfig.SystemdCgroupsManager)) && config.Resources.PidsLimit == sysinfo.GetDefaultPidsLimit() {
- setPidLimit = false
- }
- }
- if setPidLimit {
- g.SetLinuxResourcesPidsLimit(config.Resources.PidsLimit)
- addedResources = true
- }
- }
-
- // Make sure to always set the default variables unless overridden in the
- // config.
- var defaultEnv map[string]string
- if runtimeConfig == nil {
- defaultEnv = env.DefaultEnvVariables()
- } else {
- defaultEnv, err = env.ParseSlice(runtimeConfig.Containers.Env)
- if err != nil {
- return nil, errors.Wrap(err, "Env fields in containers.conf failed to parse")
- }
- defaultEnv = env.Join(env.DefaultEnvVariables(), defaultEnv)
- }
-
- if err := addRlimits(config, &g); err != nil {
- return nil, err
- }
-
- // NAMESPACES
-
- if err := config.Pid.ConfigureGenerator(&g); err != nil {
- return nil, err
- }
-
- if err := config.User.ConfigureGenerator(&g); err != nil {
- return nil, err
- }
-
- if err := config.Network.ConfigureGenerator(&g); err != nil {
- return nil, err
- }
-
- if err := config.Uts.ConfigureGenerator(&g, &config.Network, runtime); err != nil {
- return nil, err
- }
-
- if err := config.Ipc.ConfigureGenerator(&g); err != nil {
- return nil, err
- }
-
- if err := config.Cgroup.ConfigureGenerator(&g); err != nil {
- return nil, err
- }
-
- config.Env = env.Join(defaultEnv, config.Env)
- for name, val := range config.Env {
- g.AddProcessEnv(name, val)
- }
- configSpec := g.Config
-
- // If the container image specifies an label with a
- // capabilities.ContainerImageLabel then split the comma separated list
- // of capabilities and record them. This list indicates the only
- // capabilities, required to run the container.
- var capRequired []string
- for key, val := range config.Labels {
- if util.StringInSlice(key, capabilities.ContainerImageLabels) {
- capRequired = strings.Split(val, ",")
- }
- }
- config.Security.CapRequired = capRequired
-
- if err := config.Security.ConfigureGenerator(&g, &config.User); err != nil {
- return nil, err
- }
-
- // BIND MOUNTS
- configSpec.Mounts = SupercedeUserMounts(userMounts, configSpec.Mounts)
- // Process mounts to ensure correct options
- if err := InitFSMounts(configSpec.Mounts); err != nil {
- return nil, err
- }
-
- // BLOCK IO
- blkio, err := config.CreateBlockIO()
- if err != nil {
- return nil, errors.Wrapf(err, "error creating block io")
- }
- if blkio != nil {
- configSpec.Linux.Resources.BlockIO = blkio
- addedResources = true
- }
-
- if rootless.IsRootless() {
- cgroup2, err := cgroups.IsCgroup2UnifiedMode()
- if err != nil {
- return nil, err
- }
- if !addedResources {
- configSpec.Linux.Resources = &spec.LinuxResources{}
- }
-
- canUseResources := cgroup2 && runtimeConfig != nil && (runtimeConfig.Engine.CgroupManager == cconfig.SystemdCgroupsManager)
-
- if addedResources && !canUseResources {
- return nil, errors.New("invalid configuration, cannot specify resource limits without cgroups v2 and --cgroup-manager=systemd")
- }
- if !canUseResources {
- // Force the resources block to be empty instead of having default values.
- configSpec.Linux.Resources = &spec.LinuxResources{}
- }
- }
-
- switch config.Cgroup.Cgroups {
- case "disabled":
- if addedResources {
- return nil, errors.New("cannot specify resource limits when cgroups are disabled is specified")
- }
- configSpec.Linux.Resources = &spec.LinuxResources{}
- case "enabled", "no-conmon", "":
- // Do nothing
- default:
- return nil, errors.New("unrecognized option for cgroups; supported are 'default', 'disabled', 'no-conmon'")
- }
-
- // Add annotations
- if configSpec.Annotations == nil {
- configSpec.Annotations = make(map[string]string)
- }
-
- if config.CidFile != "" {
- configSpec.Annotations[define.InspectAnnotationCIDFile] = config.CidFile
- }
-
- if config.Rm {
- configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseTrue
- } else {
- configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseFalse
- }
-
- if len(config.VolumesFrom) > 0 {
- configSpec.Annotations[define.InspectAnnotationVolumesFrom] = strings.Join(config.VolumesFrom, ",")
- }
-
- if config.Security.Privileged {
- configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseTrue
- } else {
- configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseFalse
- }
-
- if config.Init {
- configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseTrue
- } else {
- configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseFalse
- }
-
- return configSpec, nil
-}
-
-func (config *CreateConfig) cgroupDisabled() bool {
- return config.Cgroup.Cgroups == "disabled"
-}
-
-func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, g *generate.Generator) {
- if !privileged {
- for _, mp := range []string{
- "/proc/acpi",
- "/proc/kcore",
- "/proc/keys",
- "/proc/latency_stats",
- "/proc/timer_list",
- "/proc/timer_stats",
- "/proc/sched_debug",
- "/proc/scsi",
- "/sys/firmware",
- "/sys/fs/selinux",
- } {
- g.AddLinuxMaskedPaths(mp)
- }
-
- if pidModeIsHost && rootless.IsRootless() {
- return
- }
-
- for _, rp := range []string{
- "/proc/asound",
- "/proc/bus",
- "/proc/fs",
- "/proc/irq",
- "/proc/sys",
- "/proc/sysrq-trigger",
- } {
- g.AddLinuxReadonlyPaths(rp)
- }
- }
-}
-
-func addRlimits(config *CreateConfig, g *generate.Generator) error {
- var (
- isRootless = rootless.IsRootless()
- nofileSet = false
- nprocSet = false
- )
-
- for _, u := range config.Resources.Ulimit {
- if u == "host" {
- if len(config.Resources.Ulimit) != 1 {
- return errors.New("ulimit can use host only once")
- }
- g.Config.Process.Rlimits = nil
- break
- }
-
- ul, err := units.ParseUlimit(u)
- if err != nil {
- return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u)
- }
-
- if ul.Name == "nofile" {
- nofileSet = true
- } else if ul.Name == "nproc" {
- nprocSet = true
- }
-
- g.AddProcessRlimits("RLIMIT_"+strings.ToUpper(ul.Name), uint64(ul.Hard), uint64(ul.Soft))
- }
-
- // If not explicitly overridden by the user, default number of open
- // files and number of processes to the maximum they can be set to
- // (without overriding a sysctl)
- if !nofileSet {
- max := define.RLimitDefaultValue
- current := define.RLimitDefaultValue
- if isRootless {
- var rlimit unix.Rlimit
- if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit); err != nil {
- logrus.Warnf("failed to return RLIMIT_NOFILE ulimit %q", err)
- }
- if rlimit.Cur < current {
- current = rlimit.Cur
- }
- if rlimit.Max < max {
- max = rlimit.Max
- }
- }
- g.AddProcessRlimits("RLIMIT_NOFILE", max, current)
- }
- if !nprocSet {
- max := define.RLimitDefaultValue
- current := define.RLimitDefaultValue
- if isRootless {
- var rlimit unix.Rlimit
- if err := unix.Getrlimit(unix.RLIMIT_NPROC, &rlimit); err != nil {
- logrus.Warnf("failed to return RLIMIT_NPROC ulimit %q", err)
- }
- if rlimit.Cur < current {
- current = rlimit.Cur
- }
- if rlimit.Max < max {
- max = rlimit.Max
- }
- }
- g.AddProcessRlimits("RLIMIT_NPROC", max, current)
- }
-
- return nil
-}
diff --git a/pkg/spec/spec_test.go b/pkg/spec/spec_test.go
deleted file mode 100644
index d01102fa2..000000000
--- a/pkg/spec/spec_test.go
+++ /dev/null
@@ -1,108 +0,0 @@
-package createconfig
-
-import (
- "runtime"
- "testing"
-
- "github.com/containers/common/pkg/sysinfo"
- "github.com/containers/podman/v2/pkg/cgroups"
- "github.com/containers/podman/v2/pkg/rootless"
- "github.com/containers/storage"
- "github.com/containers/storage/pkg/idtools"
- "github.com/docker/go-units"
- "github.com/stretchr/testify/assert"
-)
-
-var (
- sysInfo = sysinfo.New(true)
-)
-
-// Make createconfig to test with
-func makeTestCreateConfig() *CreateConfig {
- cc := new(CreateConfig)
- cc.Resources = CreateResourceConfig{}
- cc.User.IDMappings = new(storage.IDMappingOptions)
- cc.User.IDMappings.UIDMap = []idtools.IDMap{}
- cc.User.IDMappings.GIDMap = []idtools.IDMap{}
-
- return cc
-}
-
-func doCommonSkipChecks(t *testing.T) {
- // The default configuration of podman enables seccomp, which is not available on non-Linux systems.
- // Thus, any tests that use the default seccomp setting would fail.
- // Skip the tests on non-Linux platforms rather than explicitly disable seccomp in the test and possibly affect the test result.
- if runtime.GOOS != "linux" {
- t.Skip("seccomp, which is enabled by default, is only supported on Linux")
- }
-
- if rootless.IsRootless() {
- isCgroupV2, err := cgroups.IsCgroup2UnifiedMode()
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
-
- if !isCgroupV2 {
- t.Skip("cgroups v1 cannot be used when rootless")
- }
- }
-}
-
-// TestPIDsLimit verifies the given pid-limit is correctly defined in the spec
-func TestPIDsLimit(t *testing.T) {
- doCommonSkipChecks(t)
-
- if !sysInfo.PidsLimit {
- t.Skip("running test not supported by the host system")
- }
-
- cc := makeTestCreateConfig()
- cc.Resources.PidsLimit = 22
-
- spec, err := cc.createConfigToOCISpec(nil, nil)
- assert.NoError(t, err)
-
- assert.Equal(t, spec.Linux.Resources.Pids.Limit, int64(22))
-}
-
-// TestBLKIOWeightDevice verifies the given blkio weight is correctly set in the
-// spec.
-func TestBLKIOWeightDevice(t *testing.T) {
- doCommonSkipChecks(t)
-
- if !sysInfo.BlkioWeightDevice {
- t.Skip("running test not supported by the host system")
- }
-
- cc := makeTestCreateConfig()
- cc.Resources.BlkioWeightDevice = []string{"/dev/zero:100"}
-
- spec, err := cc.createConfigToOCISpec(nil, nil)
- assert.NoError(t, err)
-
- // /dev/zero is guaranteed 1,5 by the Linux kernel
- assert.Equal(t, spec.Linux.Resources.BlockIO.WeightDevice[0].Major, int64(1))
- assert.Equal(t, spec.Linux.Resources.BlockIO.WeightDevice[0].Minor, int64(5))
- assert.Equal(t, *(spec.Linux.Resources.BlockIO.WeightDevice[0].Weight), uint16(100))
-}
-
-// TestMemorySwap verifies that the given swap memory limit is correctly set in
-// the spec.
-func TestMemorySwap(t *testing.T) {
- doCommonSkipChecks(t)
-
- if !sysInfo.SwapLimit {
- t.Skip("running test not supported by the host system")
- }
-
- swapLimit, err := units.RAMInBytes("45m")
- assert.NoError(t, err)
-
- cc := makeTestCreateConfig()
- cc.Resources.MemorySwap = swapLimit
-
- spec, err := cc.createConfigToOCISpec(nil, nil)
- assert.NoError(t, err)
-
- assert.Equal(t, *(spec.Linux.Resources.Memory.Swap), swapLimit)
-}
diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go
deleted file mode 100644
index b441daf08..000000000
--- a/pkg/spec/storage.go
+++ /dev/null
@@ -1,875 +0,0 @@
-package createconfig
-
-import (
- "fmt"
- "os"
- "path"
- "path/filepath"
- "strings"
-
- "github.com/containers/buildah/pkg/parse"
- "github.com/containers/podman/v2/libpod"
- "github.com/containers/podman/v2/pkg/util"
- spec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-const (
- // TypeBind is the type for mounting host dir
- TypeBind = "bind"
- // TypeVolume is the type for named volumes
- TypeVolume = "volume"
- // TypeTmpfs is the type for mounting tmpfs
- TypeTmpfs = "tmpfs"
-)
-
-var (
- errDuplicateDest = errors.Errorf("duplicate mount destination")
- optionArgError = errors.Errorf("must provide an argument for option")
- noDestError = errors.Errorf("must set volume destination")
-)
-
-// Parse all volume-related options in the create config into a set of mounts
-// and named volumes to add to the container.
-// Handles --volumes-from, --volumes, --tmpfs, --init, and --init-path flags.
-// TODO: Named volume options - should we default to rprivate? It bakes into a
-// bind mount under the hood...
-// TODO: handle options parsing/processing via containers/storage/pkg/mount
-func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount, []*libpod.ContainerNamedVolume, error) {
- // Add image volumes.
- baseMounts, baseVolumes, err := config.getImageVolumes()
- if err != nil {
- return nil, nil, err
- }
-
- // Add --volumes-from.
- // Overrides image volumes unconditionally.
- vFromMounts, vFromVolumes, err := config.getVolumesFrom(runtime)
- if err != nil {
- return nil, nil, err
- }
- for dest, mount := range vFromMounts {
- baseMounts[dest] = mount
- }
- for dest, volume := range vFromVolumes {
- baseVolumes[dest] = volume
- }
-
- // Next mounts from the --mounts flag.
- // Do not override yet.
- unifiedMounts, unifiedVolumes, err := config.getMounts()
- if err != nil {
- return nil, nil, err
- }
-
- // Next --volumes flag.
- // Do not override yet.
- volumeMounts, volumeVolumes, err := config.getVolumeMounts()
- if err != nil {
- return nil, nil, err
- }
-
- // Next --tmpfs flag.
- // Do not override yet.
- tmpfsMounts, err := config.getTmpfsMounts()
- if err != nil {
- return nil, nil, err
- }
-
- // Unify mounts from --mount, --volume, --tmpfs.
- // Also add mounts + volumes directly from createconfig.
- // Start with --volume.
- for dest, mount := range volumeMounts {
- if _, ok := unifiedMounts[dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, dest)
- }
- unifiedMounts[dest] = mount
- }
- for dest, volume := range volumeVolumes {
- if _, ok := unifiedVolumes[dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, dest)
- }
- unifiedVolumes[dest] = volume
- }
- // Now --tmpfs
- for dest, tmpfs := range tmpfsMounts {
- if _, ok := unifiedMounts[dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, dest)
- }
- unifiedMounts[dest] = tmpfs
- }
- // Now spec mounts and volumes
- for _, mount := range config.Mounts {
- dest := mount.Destination
- if _, ok := unifiedMounts[dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, dest)
- }
- unifiedMounts[dest] = mount
- }
- for _, volume := range config.NamedVolumes {
- dest := volume.Dest
- if _, ok := unifiedVolumes[dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, dest)
- }
- unifiedVolumes[dest] = volume
- }
-
- // If requested, add container init binary
- if config.Init {
- initPath := config.InitPath
- if initPath == "" {
- rtc, err := runtime.GetConfig()
- if err != nil {
- return nil, nil, err
- }
- initPath = rtc.Engine.InitPath
- }
- initMount, err := config.addContainerInitBinary(initPath)
- if err != nil {
- return nil, nil, err
- }
- if _, ok := unifiedMounts[initMount.Destination]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, "conflict with mount added by --init to %q", initMount.Destination)
- }
- unifiedMounts[initMount.Destination] = initMount
- }
-
- // Before superseding, we need to find volume mounts which conflict with
- // named volumes, and vice versa.
- // We'll delete the conflicts here as we supersede.
- for dest := range unifiedMounts {
- if _, ok := baseVolumes[dest]; ok {
- delete(baseVolumes, dest)
- }
- }
- for dest := range unifiedVolumes {
- if _, ok := baseMounts[dest]; ok {
- delete(baseMounts, dest)
- }
- }
-
- // Supersede volumes-from/image volumes with unified volumes from above.
- // This is an unconditional replacement.
- for dest, mount := range unifiedMounts {
- baseMounts[dest] = mount
- }
- for dest, volume := range unifiedVolumes {
- baseVolumes[dest] = volume
- }
-
- // If requested, add tmpfs filesystems for read-only containers.
- if config.Security.ReadOnlyRootfs && config.Security.ReadOnlyTmpfs {
- readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"}
- options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"}
- for _, dest := range readonlyTmpfs {
- if _, ok := baseMounts[dest]; ok {
- continue
- }
- if _, ok := baseVolumes[dest]; ok {
- continue
- }
- localOpts := options
- if dest == "/run" {
- localOpts = append(localOpts, "noexec", "size=65536k")
- } else {
- localOpts = append(localOpts, "exec")
- }
- baseMounts[dest] = spec.Mount{
- Destination: dest,
- Type: "tmpfs",
- Source: "tmpfs",
- Options: localOpts,
- }
- }
- }
-
- // Check for conflicts between named volumes and mounts
- for dest := range baseMounts {
- if _, ok := baseVolumes[dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
- }
- }
- for dest := range baseVolumes {
- if _, ok := baseMounts[dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
- }
- }
-
- // Final step: maps to arrays
- finalMounts := make([]spec.Mount, 0, len(baseMounts))
- for _, mount := range baseMounts {
- if mount.Type == TypeBind {
- absSrc, err := filepath.Abs(mount.Source)
- if err != nil {
- return nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source)
- }
- mount.Source = absSrc
- }
- finalMounts = append(finalMounts, mount)
- }
- finalVolumes := make([]*libpod.ContainerNamedVolume, 0, len(baseVolumes))
- for _, volume := range baseVolumes {
- finalVolumes = append(finalVolumes, volume)
- }
-
- return finalMounts, finalVolumes, nil
-}
-
-// Parse volumes from - a set of containers whose volumes we will mount in.
-// Grab the containers, retrieve any user-created spec mounts and all named
-// volumes, and return a list of them.
-// Conflicts are resolved simply - the last container specified wins.
-// Container names may be suffixed by mount options after a colon.
-// TODO: We should clean these paths if possible
-func (config *CreateConfig) getVolumesFrom(runtime *libpod.Runtime) (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) {
- // Both of these are maps of mount destination to mount type.
- // We ensure that each destination is only mounted to once in this way.
- finalMounts := make(map[string]spec.Mount)
- finalNamedVolumes := make(map[string]*libpod.ContainerNamedVolume)
-
- for _, vol := range config.VolumesFrom {
- var (
- options = []string{}
- err error
- splitVol = strings.SplitN(vol, ":", 2)
- )
- if len(splitVol) == 2 {
- splitOpts := strings.Split(splitVol[1], ",")
- for _, checkOpt := range splitOpts {
- switch checkOpt {
- case "z", "ro", "rw":
- // Do nothing, these are valid options
- default:
- return nil, nil, errors.Errorf("invalid options %q, can only specify 'ro', 'rw', and 'z'", splitVol[1])
- }
- }
-
- if options, err = parse.ValidateVolumeOpts(splitOpts); err != nil {
- return nil, nil, err
- }
- }
- ctr, err := runtime.LookupContainer(splitVol[0])
- if err != nil {
- return nil, nil, errors.Wrapf(err, "error looking up container %q for volumes-from", splitVol[0])
- }
-
- logrus.Debugf("Adding volumes from container %s", ctr.ID())
-
- // Look up the container's user volumes. This gets us the
- // destinations of all mounts the user added to the container.
- userVolumesArr := ctr.UserVolumes()
-
- // We're going to need to access them a lot, so convert to a map
- // to reduce looping.
- // We'll also use the map to indicate if we missed any volumes along the way.
- userVolumes := make(map[string]bool)
- for _, dest := range userVolumesArr {
- userVolumes[dest] = false
- }
-
- // Now we get the container's spec and loop through its volumes
- // and append them in if we can find them.
- spec := ctr.Spec()
- if spec == nil {
- return nil, nil, errors.Errorf("error retrieving container %s spec for volumes-from", ctr.ID())
- }
- for _, mnt := range spec.Mounts {
- if mnt.Type != TypeBind {
- continue
- }
- if _, exists := userVolumes[mnt.Destination]; exists {
- userVolumes[mnt.Destination] = true
-
- if len(options) != 0 {
- mnt.Options = options
- }
-
- if _, ok := finalMounts[mnt.Destination]; ok {
- logrus.Debugf("Overriding mount to %s with new mount from container %s", mnt.Destination, ctr.ID())
- }
- finalMounts[mnt.Destination] = mnt
- }
- }
-
- // We're done with the spec mounts. Add named volumes.
- // Add these unconditionally - none of them are automatically
- // part of the container, as some spec mounts are.
- namedVolumes := ctr.NamedVolumes()
- for _, namedVol := range namedVolumes {
- if _, exists := userVolumes[namedVol.Dest]; exists {
- userVolumes[namedVol.Dest] = true
- }
-
- if len(options) != 0 {
- namedVol.Options = options
- }
-
- if _, ok := finalMounts[namedVol.Dest]; ok {
- logrus.Debugf("Overriding named volume mount to %s with new named volume from container %s", namedVol.Dest, ctr.ID())
- }
- finalNamedVolumes[namedVol.Dest] = namedVol
- }
-
- // Check if we missed any volumes
- for volDest, found := range userVolumes {
- if !found {
- logrus.Warnf("Unable to match volume %s from container %s for volumes-from", volDest, ctr.ID())
- }
- }
- }
-
- return finalMounts, finalNamedVolumes, nil
-}
-
-// getMounts takes user-provided input from the --mount flag and creates OCI
-// spec mounts and Libpod named volumes.
-// podman run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ...
-// podman run --mount type=tmpfs,target=/dev/shm ...
-// podman run --mount type=volume,source=test-volume, ...
-func (config *CreateConfig) getMounts() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) {
- finalMounts := make(map[string]spec.Mount)
- finalNamedVolumes := make(map[string]*libpod.ContainerNamedVolume)
-
- errInvalidSyntax := errors.Errorf("incorrect mount format: should be --mount type=<bind|tmpfs|volume>,[src=<host-dir|volume-name>,]target=<ctr-dir>[,options]")
-
- // TODO(vrothberg): the manual parsing can be replaced with a regular expression
- // to allow a more robust parsing of the mount format and to give
- // precise errors regarding supported format versus supported options.
- for _, mount := range config.MountsFlag {
- arr := strings.SplitN(mount, ",", 2)
- if len(arr) < 2 {
- return nil, nil, errors.Wrapf(errInvalidSyntax, "%q", mount)
- }
- kv := strings.Split(arr[0], "=")
- // TODO: type is not explicitly required in Docker.
- // If not specified, it defaults to "volume".
- if len(kv) != 2 || kv[0] != "type" {
- return nil, nil, errors.Wrapf(errInvalidSyntax, "%q", mount)
- }
-
- tokens := strings.Split(arr[1], ",")
- switch kv[1] {
- case TypeBind:
- mount, err := getBindMount(tokens)
- if err != nil {
- return nil, nil, err
- }
- if _, ok := finalMounts[mount.Destination]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination)
- }
- finalMounts[mount.Destination] = mount
- case TypeTmpfs:
- mount, err := getTmpfsMount(tokens)
- if err != nil {
- return nil, nil, err
- }
- if _, ok := finalMounts[mount.Destination]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination)
- }
- finalMounts[mount.Destination] = mount
- case "volume":
- volume, err := getNamedVolume(tokens)
- if err != nil {
- return nil, nil, err
- }
- if _, ok := finalNamedVolumes[volume.Dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, volume.Dest)
- }
- finalNamedVolumes[volume.Dest] = volume
- default:
- return nil, nil, errors.Errorf("invalid filesystem type %q", kv[1])
- }
- }
-
- return finalMounts, finalNamedVolumes, nil
-}
-
-// Parse a single bind mount entry from the --mount flag.
-func getBindMount(args []string) (spec.Mount, error) {
- newMount := spec.Mount{
- Type: TypeBind,
- }
-
- var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool
-
- for _, val := range args {
- kv := strings.SplitN(val, "=", 2)
- switch kv[0] {
- case "bind-nonrecursive":
- newMount.Options = append(newMount.Options, "bind")
- case "ro", "rw":
- if setRORW {
- return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' or 'rw' options more than once")
- }
- setRORW = true
- // Can be formatted as one of:
- // ro
- // ro=[true|false]
- // rw
- // rw=[true|false]
- switch len(kv) {
- case 1:
- newMount.Options = append(newMount.Options, kv[0])
- case 2:
- switch strings.ToLower(kv[1]) {
- case "true":
- newMount.Options = append(newMount.Options, kv[0])
- case "false":
- // Set the opposite only for rw
- // ro's opposite is the default
- if kv[0] == "rw" {
- newMount.Options = append(newMount.Options, "ro")
- }
- default:
- return newMount, errors.Wrapf(optionArgError, "%s must be set to true or false, instead received %q", kv[0], kv[1])
- }
- default:
- return newMount, errors.Wrapf(optionArgError, "badly formatted option %q", val)
- }
- case "nosuid", "suid":
- if setSuid {
- return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
- }
- setSuid = true
- newMount.Options = append(newMount.Options, kv[0])
- case "nodev", "dev":
- if setDev {
- return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
- }
- setDev = true
- newMount.Options = append(newMount.Options, kv[0])
- case "noexec", "exec":
- if setExec {
- return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
- }
- setExec = true
- newMount.Options = append(newMount.Options, kv[0])
- case "shared", "rshared", "private", "rprivate", "slave", "rslave", "unbindable", "runbindable", "Z", "z":
- newMount.Options = append(newMount.Options, kv[0])
- case "bind-propagation":
- if len(kv) == 1 {
- return newMount, errors.Wrapf(optionArgError, kv[0])
- }
- newMount.Options = append(newMount.Options, kv[1])
- case "src", "source":
- if len(kv) == 1 {
- return newMount, errors.Wrapf(optionArgError, kv[0])
- }
- if err := parse.ValidateVolumeHostDir(kv[1]); err != nil {
- return newMount, err
- }
- newMount.Source = kv[1]
- setSource = true
- case "target", "dst", "destination":
- if len(kv) == 1 {
- return newMount, errors.Wrapf(optionArgError, kv[0])
- }
- if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
- return newMount, err
- }
- newMount.Destination = filepath.Clean(kv[1])
- setDest = true
- case "relabel":
- if setRelabel {
- return newMount, errors.Wrapf(optionArgError, "cannot pass 'relabel' option more than once")
- }
- setRelabel = true
- if len(kv) != 2 {
- return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0])
- }
- switch kv[1] {
- case "private":
- newMount.Options = append(newMount.Options, "z")
- case "shared":
- newMount.Options = append(newMount.Options, "Z")
- default:
- return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0])
- }
- default:
- return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
- }
- }
-
- if !setDest {
- return newMount, noDestError
- }
-
- if !setSource {
- newMount.Source = newMount.Destination
- }
-
- options, err := parse.ValidateVolumeOpts(newMount.Options)
- if err != nil {
- return newMount, err
- }
- newMount.Options = options
- return newMount, nil
-}
-
-// Parse a single tmpfs mount entry from the --mount flag
-func getTmpfsMount(args []string) (spec.Mount, error) {
- newMount := spec.Mount{
- Type: TypeTmpfs,
- Source: TypeTmpfs,
- }
-
- var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool
-
- for _, val := range args {
- kv := strings.SplitN(val, "=", 2)
- switch kv[0] {
- case "tmpcopyup", "notmpcopyup":
- if setTmpcopyup {
- return newMount, errors.Wrapf(optionArgError, "cannot pass 'tmpcopyup' and 'notmpcopyup' options more than once")
- }
- setTmpcopyup = true
- newMount.Options = append(newMount.Options, kv[0])
- case "ro", "rw":
- if setRORW {
- return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once")
- }
- setRORW = true
- newMount.Options = append(newMount.Options, kv[0])
- case "nosuid", "suid":
- if setSuid {
- return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
- }
- setSuid = true
- newMount.Options = append(newMount.Options, kv[0])
- case "nodev", "dev":
- if setDev {
- return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
- }
- setDev = true
- newMount.Options = append(newMount.Options, kv[0])
- case "noexec", "exec":
- if setExec {
- return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
- }
- setExec = true
- newMount.Options = append(newMount.Options, kv[0])
- case "tmpfs-mode":
- if len(kv) == 1 {
- return newMount, errors.Wrapf(optionArgError, kv[0])
- }
- newMount.Options = append(newMount.Options, fmt.Sprintf("mode=%s", kv[1]))
- case "tmpfs-size":
- if len(kv) == 1 {
- return newMount, errors.Wrapf(optionArgError, kv[0])
- }
- newMount.Options = append(newMount.Options, fmt.Sprintf("size=%s", kv[1]))
- case "src", "source":
- return newMount, errors.Errorf("source is not supported with tmpfs mounts")
- case "target", "dst", "destination":
- if len(kv) == 1 {
- return newMount, errors.Wrapf(optionArgError, kv[0])
- }
- if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
- return newMount, err
- }
- newMount.Destination = filepath.Clean(kv[1])
- setDest = true
- default:
- return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
- }
- }
-
- if !setDest {
- return newMount, noDestError
- }
-
- return newMount, nil
-}
-
-// Parse a single volume mount entry from the --mount flag.
-// Note that the volume-label option for named volumes is currently NOT supported.
-// TODO: add support for --volume-label
-func getNamedVolume(args []string) (*libpod.ContainerNamedVolume, error) {
- newVolume := new(libpod.ContainerNamedVolume)
-
- var setSource, setDest, setRORW, setSuid, setDev, setExec bool
-
- for _, val := range args {
- kv := strings.SplitN(val, "=", 2)
- switch kv[0] {
- case "ro", "rw":
- if setRORW {
- return nil, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once")
- }
- setRORW = true
- newVolume.Options = append(newVolume.Options, kv[0])
- case "nosuid", "suid":
- if setSuid {
- return nil, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
- }
- setSuid = true
- newVolume.Options = append(newVolume.Options, kv[0])
- case "nodev", "dev":
- if setDev {
- return nil, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
- }
- setDev = true
- newVolume.Options = append(newVolume.Options, kv[0])
- case "noexec", "exec":
- if setExec {
- return nil, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
- }
- setExec = true
- newVolume.Options = append(newVolume.Options, kv[0])
- case "volume-label":
- return nil, errors.Errorf("the --volume-label option is not presently implemented")
- case "src", "source":
- if len(kv) == 1 {
- return nil, errors.Wrapf(optionArgError, kv[0])
- }
- newVolume.Name = kv[1]
- setSource = true
- case "target", "dst", "destination":
- if len(kv) == 1 {
- return nil, errors.Wrapf(optionArgError, kv[0])
- }
- if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
- return nil, err
- }
- newVolume.Dest = filepath.Clean(kv[1])
- setDest = true
- default:
- return nil, errors.Wrapf(util.ErrBadMntOption, kv[0])
- }
- }
-
- if !setSource {
- return nil, errors.Errorf("must set source volume")
- }
- if !setDest {
- return nil, noDestError
- }
-
- return newVolume, nil
-}
-
-func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) {
- mounts := make(map[string]spec.Mount)
- volumes := make(map[string]*libpod.ContainerNamedVolume)
-
- volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]")
-
- for _, vol := range config.Volumes {
- var (
- options []string
- src string
- dest string
- err error
- )
-
- splitVol := strings.Split(vol, ":")
- if len(splitVol) > 3 {
- return nil, nil, errors.Wrapf(volumeFormatErr, vol)
- }
-
- src = splitVol[0]
- if len(splitVol) == 1 {
- // This is an anonymous named volume. Only thing given
- // is destination.
- // Name/source will be blank, and populated by libpod.
- src = ""
- dest = splitVol[0]
- } else if len(splitVol) > 1 {
- dest = splitVol[1]
- }
- if len(splitVol) > 2 {
- if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[2], ",")); err != nil {
- return nil, nil, err
- }
- }
-
- // Do not check source dir for anonymous volumes
- if len(splitVol) > 1 {
- if err := parse.ValidateVolumeHostDir(src); err != nil {
- return nil, nil, err
- }
- }
- if err := parse.ValidateVolumeCtrDir(dest); err != nil {
- return nil, nil, err
- }
-
- cleanDest := filepath.Clean(dest)
-
- if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") {
- // This is not a named volume
- newMount := spec.Mount{
- Destination: cleanDest,
- Type: string(TypeBind),
- Source: src,
- Options: options,
- }
- if _, ok := mounts[newMount.Destination]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, newMount.Destination)
- }
- mounts[newMount.Destination] = newMount
- } else {
- // This is a named volume
- newNamedVol := new(libpod.ContainerNamedVolume)
- newNamedVol.Name = src
- newNamedVol.Dest = cleanDest
- newNamedVol.Options = options
-
- if _, ok := volumes[newNamedVol.Dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, newNamedVol.Dest)
- }
- volumes[newNamedVol.Dest] = newNamedVol
- }
-
- logrus.Debugf("User mount %s:%s options %v", src, dest, options)
- }
-
- return mounts, volumes, nil
-}
-
-// Get mounts for container's image volumes
-func (config *CreateConfig) getImageVolumes() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) {
- mounts := make(map[string]spec.Mount)
- volumes := make(map[string]*libpod.ContainerNamedVolume)
-
- if config.ImageVolumeType == "ignore" {
- return mounts, volumes, nil
- }
-
- for vol := range config.BuiltinImgVolumes {
- cleanDest := filepath.Clean(vol)
- logrus.Debugf("Adding image volume at %s", cleanDest)
- if config.ImageVolumeType == "tmpfs" {
- // Tmpfs image volumes are handled as mounts
- mount := spec.Mount{
- Destination: cleanDest,
- Source: TypeTmpfs,
- Type: TypeTmpfs,
- Options: []string{"rprivate", "rw", "nodev", "exec"},
- }
- mounts[cleanDest] = mount
- } else {
- // Anonymous volumes have no name.
- namedVolume := new(libpod.ContainerNamedVolume)
- namedVolume.Options = []string{"rprivate", "rw", "nodev", "exec"}
- namedVolume.Dest = cleanDest
- volumes[cleanDest] = namedVolume
- }
- }
-
- return mounts, volumes, nil
-}
-
-// GetTmpfsMounts creates spec.Mount structs for user-requested tmpfs mounts
-func (config *CreateConfig) getTmpfsMounts() (map[string]spec.Mount, error) {
- m := make(map[string]spec.Mount)
- for _, i := range config.Tmpfs {
- // Default options if nothing passed
- var options []string
- spliti := strings.Split(i, ":")
- destPath := spliti[0]
- if err := parse.ValidateVolumeCtrDir(spliti[0]); err != nil {
- return nil, err
- }
- if len(spliti) > 1 {
- options = strings.Split(spliti[1], ",")
- }
-
- if _, ok := m[destPath]; ok {
- return nil, errors.Wrapf(errDuplicateDest, destPath)
- }
-
- mount := spec.Mount{
- Destination: filepath.Clean(destPath),
- Type: string(TypeTmpfs),
- Options: options,
- Source: string(TypeTmpfs),
- }
- m[destPath] = mount
- }
- return m, nil
-}
-
-// AddContainerInitBinary adds the init binary specified by path iff the
-// container will run in a private PID namespace that is not shared with the
-// host or another pre-existing container, where an init-like process is
-// already running.
-//
-// Note that AddContainerInitBinary prepends "/dev/init" "--" to the command
-// to execute the bind-mounted binary as PID 1.
-func (config *CreateConfig) addContainerInitBinary(path string) (spec.Mount, error) {
- mount := spec.Mount{
- Destination: "/dev/init",
- Type: TypeBind,
- Source: path,
- Options: []string{TypeBind, "ro"},
- }
-
- if path == "" {
- return mount, fmt.Errorf("please specify a path to the container-init binary")
- }
- if !config.Pid.PidMode.IsPrivate() {
- return mount, fmt.Errorf("cannot add init binary as PID 1 (PID namespace isn't private)")
- }
- if config.Systemd {
- return mount, fmt.Errorf("cannot use container-init binary with systemd")
- }
- if _, err := os.Stat(path); os.IsNotExist(err) {
- return mount, errors.Wrap(err, "container-init binary not found on the host")
- }
- config.Command = append([]string{"/dev/init", "--"}, config.Command...)
- return mount, nil
-}
-
-// Supersede existing mounts in the spec with new, user-specified mounts.
-// TODO: Should we unmount subtree mounts? E.g., if /tmp/ is mounted by
-// one mount, and we already have /tmp/a and /tmp/b, should we remove
-// the /tmp/a and /tmp/b mounts in favor of the more general /tmp?
-func SupercedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.Mount {
- if len(mounts) > 0 {
- // If we have overlappings mounts, remove them from the spec in favor of
- // the user-added volume mounts
- destinations := make(map[string]bool)
- for _, mount := range mounts {
- destinations[path.Clean(mount.Destination)] = true
- }
- // Copy all mounts from spec to defaultMounts, except for
- // - mounts overridden by a user supplied mount;
- // - all mounts under /dev if a user supplied /dev is present;
- mountDev := destinations["/dev"]
- for _, mount := range configMount {
- if _, ok := destinations[path.Clean(mount.Destination)]; !ok {
- if mountDev && strings.HasPrefix(mount.Destination, "/dev/") {
- // filter out everything under /dev if /dev is user-mounted
- continue
- }
-
- logrus.Debugf("Adding mount %s", mount.Destination)
- mounts = append(mounts, mount)
- }
- }
- return mounts
- }
- return configMount
-}
-
-// Ensure mount options on all mounts are correct
-func InitFSMounts(mounts []spec.Mount) error {
- for i, m := range mounts {
- switch {
- case m.Type == TypeBind:
- opts, err := util.ProcessOptions(m.Options, false, m.Source)
- if err != nil {
- return err
- }
- mounts[i].Options = opts
- case m.Type == TypeTmpfs && filepath.Clean(m.Destination) != "/dev":
- opts, err := util.ProcessOptions(m.Options, true, "")
- if err != nil {
- return err
- }
- mounts[i].Options = opts
- }
- }
- return nil
-}
diff --git a/pkg/spec/storage_test.go b/pkg/spec/storage_test.go
deleted file mode 100644
index 04a9d6976..000000000
--- a/pkg/spec/storage_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package createconfig
-
-import (
- "testing"
-
- spec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/stretchr/testify/assert"
-)
-
-func TestGetVolumeMountsOneVolume(t *testing.T) {
- data := spec.Mount{
- Destination: "/foobar",
- Type: "bind",
- Source: "/tmp",
- Options: []string{"ro"},
- }
- config := CreateConfig{
- Volumes: []string{"/tmp:/foobar:ro"},
- }
- specMount, _, err := config.getVolumeMounts()
- assert.NoError(t, err)
- assert.EqualValues(t, data, specMount[data.Destination])
-}
-
-func TestGetTmpfsMounts(t *testing.T) {
- data := spec.Mount{
- Destination: "/homer",
- Type: "tmpfs",
- Source: "tmpfs",
- Options: []string{"rw", "size=787448k", "mode=1777"},
- }
- config := CreateConfig{
- Tmpfs: []string{"/homer:rw,size=787448k,mode=1777"},
- }
- tmpfsMount, err := config.getTmpfsMounts()
- assert.NoError(t, err)
- assert.EqualValues(t, data, tmpfsMount[data.Destination])
-}