summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common/specgen.go27
-rw-r--r--cmd/podman/common/volumes.go569
-rw-r--r--libpod/container_internal.go6
-rw-r--r--libpod/define/errors.go3
-rw-r--r--libpod/image/pull.go4
-rw-r--r--libpod/runtime_img.go6
-rw-r--r--pkg/api/handlers/libpod/images.go2
-rw-r--r--pkg/domain/infra/abi/images.go11
-rw-r--r--pkg/selinux/selinux.go40
-rw-r--r--pkg/specgen/generate/storage.go814
-rw-r--r--pkg/specgen/specgen.go17
-rw-r--r--pkg/util/utils.go36
-rw-r--r--test/e2e/rmi_test.go84
-rw-r--r--test/e2e/version_test.go2
14 files changed, 703 insertions, 918 deletions
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index 1e857ea01..10ae0bb2d 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -548,22 +548,31 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
}
s.SeccompPolicy = c.SeccompPolicy
+
+ // TODO: should parse out options
+ s.VolumesFrom = c.VolumesFrom
+
+ // Only add read-only tmpfs mounts in case that we are read-only and the
+ // read-only tmpfs flag has been set.
+ mounts, volumes, err := parseVolumes(c.Volume, c.Mount, c.TmpFS, (c.ReadOnlyTmpFS && c.ReadOnly))
+ if err != nil {
+ return err
+ }
+ s.Mounts = mounts
+ s.Volumes = volumes
+
// TODO any idea why this was done
- // storage.go from spec/
- // grab it
- //volumes := rtc.Containers.Volumes
+ //devices := rtc.Containers.Devices
// TODO conflict on populate?
- //if v := c.Volume; len(v)> 0 {
- // s.Volumes = append(volumes, c.StringSlice("volume")...)
+ //
+ //if c.Changed("device") {
+ // devices = append(devices, c.StringSlice("device")...)
//}
- //s.volu
-
- //s.Mounts = c.Mount
- s.VolumesFrom = c.VolumesFrom
for _, dev := range c.Devices {
s.Devices = append(s.Devices, specs.LinuxDevice{Path: dev})
}
+
// TODO things i cannot find in spec
// we dont think these are in the spec
// init - initbinary
diff --git a/cmd/podman/common/volumes.go b/cmd/podman/common/volumes.go
new file mode 100644
index 000000000..6b0b6e9cf
--- /dev/null
+++ b/cmd/podman/common/volumes.go
@@ -0,0 +1,569 @@
+package common
+
+import (
+ "fmt"
+ "path/filepath"
+ "strings"
+
+ "github.com/containers/buildah/pkg/parse"
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/containers/libpod/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, --mount, and --tmpfs flags.
+// Does not handle image volumes, init, and --volumes-from flags.
+// Can also add tmpfs mounts from read-only tmpfs.
+// TODO: handle options parsing/processing via containers/storage/pkg/mount
+func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bool) ([]spec.Mount, []*specgen.NamedVolume, error) {
+ // Get mounts from the --mounts flag.
+ unifiedMounts, unifiedVolumes, err := getMounts(mountFlag)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // Next --volumes flag.
+ volumeMounts, volumeVolumes, err := getVolumeMounts(volumeFlag)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // Next --tmpfs flag.
+ tmpfsMounts, err := getTmpfsMounts(tmpfsFlag)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // Unify mounts from --mount, --volume, --tmpfs.
+ // 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
+ }
+
+ // If requested, add tmpfs filesystems for read-only containers.
+ if addReadOnlyTmpfs {
+ readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"}
+ options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"}
+ for _, dest := range readonlyTmpfs {
+ if _, ok := unifiedMounts[dest]; ok {
+ continue
+ }
+ if _, ok := unifiedVolumes[dest]; ok {
+ continue
+ }
+ localOpts := options
+ if dest == "/run" {
+ localOpts = append(localOpts, "noexec", "size=65536k")
+ } else {
+ localOpts = append(localOpts, "exec")
+ }
+ unifiedMounts[dest] = spec.Mount{
+ Destination: dest,
+ Type: TypeTmpfs,
+ Source: "tmpfs",
+ Options: localOpts,
+ }
+ }
+ }
+
+ // Check for conflicts between named volumes and mounts
+ for dest := range unifiedMounts {
+ if _, ok := unifiedVolumes[dest]; ok {
+ return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
+ }
+ }
+ for dest := range unifiedVolumes {
+ if _, ok := unifiedMounts[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(unifiedMounts))
+ for _, mount := range unifiedMounts {
+ 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([]*specgen.NamedVolume, 0, len(unifiedVolumes))
+ for _, volume := range unifiedVolumes {
+ finalVolumes = append(finalVolumes, volume)
+ }
+
+ return finalMounts, finalVolumes, 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 getMounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, error) {
+ finalMounts := make(map[string]spec.Mount)
+ finalNamedVolumes := make(map[string]*specgen.NamedVolume)
+
+ 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 mountFlag {
+ 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.Split(val, "=")
+ 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", "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.Split(val, "=")
+ 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) (*specgen.NamedVolume, error) {
+ newVolume := new(specgen.NamedVolume)
+
+ var setSource, setDest, setRORW, setSuid, setDev, setExec bool
+
+ for _, val := range args {
+ kv := strings.Split(val, "=")
+ 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 getVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, error) {
+ mounts := make(map[string]spec.Mount)
+ volumes := make(map[string]*specgen.NamedVolume)
+
+ volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]")
+
+ for _, vol := range volumeFlag {
+ 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(specgen.NamedVolume)
+ 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
+}
+
+// GetTmpfsMounts creates spec.Mount structs for user-requested tmpfs mounts
+func getTmpfsMounts(tmpfsFlag []string) (map[string]spec.Mount, error) {
+ m := make(map[string]spec.Mount)
+ for _, i := range tmpfsFlag {
+ // 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
+}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 50bd9bc25..4cb80a98b 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -19,7 +19,7 @@ import (
"github.com/containers/libpod/pkg/hooks"
"github.com/containers/libpod/pkg/hooks/exec"
"github.com/containers/libpod/pkg/rootless"
- "github.com/containers/libpod/pkg/util"
+ "github.com/containers/libpod/pkg/selinux"
"github.com/containers/storage"
"github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/mount"
@@ -435,12 +435,12 @@ func (c *Container) setupStorage(ctx context.Context) error {
processLabel := containerInfo.ProcessLabel
switch {
case c.ociRuntime.SupportsKVM():
- processLabel, err = util.SELinuxKVMLabel(processLabel)
+ processLabel, err = selinux.SELinuxKVMLabel(processLabel)
if err != nil {
return err
}
case c.config.Systemd:
- processLabel, err = util.SELinuxInitLabel(processLabel)
+ processLabel, err = selinux.SELinuxInitLabel(processLabel)
if err != nil {
return err
}
diff --git a/libpod/define/errors.go b/libpod/define/errors.go
index 3ba343789..16df2a1cc 100644
--- a/libpod/define/errors.go
+++ b/libpod/define/errors.go
@@ -141,4 +141,7 @@ var (
// ErrConmonOutdated indicates the version of conmon found (whether via the configuration or $PATH)
// is out of date for the current podman version
ErrConmonOutdated = errors.New("outdated conmon version")
+
+ // ErrImageInUse indicates the requested operation failed because the image was in use
+ ErrImageInUse = errors.New("image is being used")
)
diff --git a/libpod/image/pull.go b/libpod/image/pull.go
index fd359d593..6b4c40ba2 100644
--- a/libpod/image/pull.go
+++ b/libpod/image/pull.go
@@ -334,11 +334,11 @@ func (ir *Runtime) doPullImage(ctx context.Context, sc *types.SystemContext, goa
// If the image passed in was fully-qualified, we will have 1 refpair. Bc the image is fq'd, we don't need to yap about registries.
if !goal.usedSearchRegistries {
if pullErrors != nil && len(pullErrors.Errors) > 0 { // this should always be true
- return nil, errors.Wrap(pullErrors.Errors[0], "unable to pull image")
+ return nil, pullErrors.Errors[0]
}
return nil, errors.Errorf("unable to pull image, or you do not have pull access")
}
- return nil, pullErrors
+ return nil, errors.Cause(pullErrors)
}
if len(images) > 0 {
ir.newImageEvent(events.Pull, images[0])
diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go
index 6ac32878b..919080c42 100644
--- a/libpod/runtime_img.go
+++ b/libpod/runtime_img.go
@@ -71,7 +71,8 @@ func (r *Runtime) RemoveImage(ctx context.Context, img *image.Image, force bool)
// to and untag it.
repoName, err := img.MatchRepoTag(img.InputName)
if hasChildren && errors.Cause(err) == image.ErrRepoTagNotFound {
- return nil, errors.Errorf("unable to delete %q (cannot be forced) - image has dependent child images", img.ID())
+ return nil, errors.Wrapf(define.ErrImageInUse,
+ "unable to delete %q (cannot be forced) - image has dependent child images", img.ID())
}
if err != nil {
return nil, err
@@ -84,7 +85,8 @@ func (r *Runtime) RemoveImage(ctx context.Context, img *image.Image, force bool)
} else if len(img.Names()) > 1 && img.InputIsID() && !force {
// If the user requests to delete an image by ID and the image has multiple
// reponames and no force is applied, we error out.
- return nil, fmt.Errorf("unable to delete %s (must force) - image is referred to in multiple tags", img.ID())
+ return nil, errors.Wrapf(define.ErrImageInUse,
+ "unable to delete %s (must force) - image is referred to in multiple tags", img.ID())
}
err = img.Remove(ctx, force)
if err != nil && errors.Cause(err) == storage.ErrImageUsedByContainer {
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index 46401e4f2..760ab1b7c 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -443,7 +443,7 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
nil,
util.PullImageAlways)
if err != nil {
- utils.InternalServerError(w, errors.Wrapf(err, "error pulling image %q", query.Reference))
+ utils.InternalServerError(w, err)
return
}
res = append(res, handlers.LibpodImagesPullReport{ID: newImage.ID()})
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index d0b7b42b5..8a2771a4c 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -101,7 +101,7 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti
if imageRef.Transport().Name() == dockerarchive.Transport.Name() {
newImage, err := ir.Libpod.ImageRuntime().LoadFromArchiveReference(ctx, imageRef, options.SignaturePolicy, writer)
if err != nil {
- return nil, errors.Wrapf(err, "error pulling image %q", rawImage)
+ return nil, err
}
return &entities.ImagePullReport{Images: []string{newImage[0].ID()}}, nil
}
@@ -125,7 +125,7 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti
if !options.AllTags {
newImage, err := ir.Libpod.ImageRuntime().New(ctx, rawImage, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways)
if err != nil {
- return nil, errors.Wrapf(err, "error pulling image %q", rawImage)
+ return nil, err
}
return &entities.ImagePullReport{Images: []string{newImage.ID()}}, nil
}
@@ -166,7 +166,7 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti
}
if len(tags) != len(foundIDs) {
- return nil, errors.Errorf("error pulling image %q", rawImage)
+ return nil, err
}
return &entities.ImagePullReport{Images: foundIDs}, nil
}
@@ -466,7 +466,7 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie
}()
// deleteImage is an anonymous function to conveniently delete an image
- // withouth having to pass all local data around.
+ // without having to pass all local data around.
deleteImage := func(img *image.Image) error {
results, err := ir.Libpod.RemoveImage(ctx, img, opts.Force)
switch errors.Cause(err) {
@@ -476,6 +476,9 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie
inUseErrors = true // Important for exit codes in Podman.
return errors.New(
fmt.Sprintf("A container associated with containers/storage, i.e. via Buildah, CRI-O, etc., may be associated with this image: %-12.12s\n", img.ID()))
+ case define.ErrImageInUse:
+ inUseErrors = true
+ return err
default:
otherErrors = true // Important for exit codes in Podman.
return err
diff --git a/pkg/selinux/selinux.go b/pkg/selinux/selinux.go
new file mode 100644
index 000000000..975519cce
--- /dev/null
+++ b/pkg/selinux/selinux.go
@@ -0,0 +1,40 @@
+package selinux
+
+import (
+ "github.com/opencontainers/selinux/go-selinux"
+)
+
+// SELinuxKVMLabel returns labels for running kvm isolated containers
+func SELinuxKVMLabel(cLabel string) (string, error) {
+ if cLabel == "" {
+ // selinux is disabled
+ return "", nil
+ }
+ processLabel, _ := selinux.KVMContainerLabels()
+ selinux.ReleaseLabel(processLabel)
+ return swapSELinuxLabel(cLabel, processLabel)
+}
+
+// SELinuxInitLabel returns labels for running systemd based containers
+func SELinuxInitLabel(cLabel string) (string, error) {
+ if cLabel == "" {
+ // selinux is disabled
+ return "", nil
+ }
+ processLabel, _ := selinux.InitContainerLabels()
+ selinux.ReleaseLabel(processLabel)
+ return swapSELinuxLabel(cLabel, processLabel)
+}
+
+func swapSELinuxLabel(cLabel, processLabel string) (string, error) {
+ dcon, err := selinux.NewContext(cLabel)
+ if err != nil {
+ return "", err
+ }
+ scon, err := selinux.NewContext(processLabel)
+ if err != nil {
+ return "", err
+ }
+ dcon["type"] = scon["type"]
+ return dcon.Get(), nil
+}
diff --git a/pkg/specgen/generate/storage.go b/pkg/specgen/generate/storage.go
index c9a36ed46..7650e4e9a 100644
--- a/pkg/specgen/generate/storage.go
+++ b/pkg/specgen/generate/storage.go
@@ -1,22 +1,16 @@
package generate
-//nolint
-
import (
- "fmt"
"path"
"path/filepath"
"strings"
- "github.com/containers/buildah/pkg/parse"
- "github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/specgen"
"github.com/containers/libpod/pkg/util"
spec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
+// TODO unify this in one place - maybe libpod/define
const (
// TypeBind is the type for mounting host dir
TypeBind = "bind"
@@ -26,812 +20,6 @@ const (
TypeTmpfs = "tmpfs"
)
-var (
- errDuplicateDest = errors.Errorf("duplicate mount destination") //nolint
- optionArgError = errors.Errorf("must provide an argument for option") //nolint
- noDestError = errors.Errorf("must set volume destination") //nolint
-)
-
-// 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 parseVolumes(s *specgen.SpecGenerator, mounts, volMounts, tmpMounts []string) error { //nolint
-
- // TODO this needs to come from the image and erquires a runtime
-
- // 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, _, err := getMounts(mounts)
- //if err != nil {
- // return err
- //}
- //
- //// Next --volumes flag.
- //// Do not override yet.
- //volumeMounts, _ , err := getVolumeMounts(volMounts)
- //if err != nil {
- // return err
- //}
- //
- //// Next --tmpfs flag.
- //// Do not override yet.
- //tmpfsMounts, err := getTmpfsMounts(tmpMounts)
- //if err != nil {
- // return 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([]*define.ContainerNamedVolume, 0, len(baseVolumes))
- //for _, volume := range baseVolumes {
- // finalVolumes = append(finalVolumes, volume)
- //}
-
- //return finalMounts, finalVolumes, nil
- return 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
-// TODO deferred baude
-func getVolumesFrom() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { //nolint
- // 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]*define.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
- return nil, nil, 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 getMounts(mounts []string) (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { //nolint
- 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 mounts {
- 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) { //nolint
- newMount := spec.Mount{
- Type: TypeBind,
- }
-
- var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool
-
- for _, val := range args {
- kv := strings.Split(val, "=")
- 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", "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) { //nolint
- newMount := spec.Mount{
- Type: TypeTmpfs,
- Source: TypeTmpfs,
- }
-
- var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool
-
- for _, val := range args {
- kv := strings.Split(val, "=")
- 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) { //nolint
- newVolume := new(libpod.ContainerNamedVolume)
-
- var setSource, setDest, setRORW, setSuid, setDev, setExec bool
-
- for _, val := range args {
- kv := strings.Split(val, "=")
- 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 getVolumeMounts(vols []string) (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { //nolint
- 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 vols {
- 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
-// TODO deferred baude
-func getImageVolumes() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { //nolint
- //mounts := make(map[string]spec.Mount)
- //volumes := make(map[string]*define.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(define.ContainerNamedVolume)
- // namedVolume.Options = []string{"rprivate", "rw", "nodev", "exec"}
- // namedVolume.Dest = cleanDest
- // volumes[cleanDest] = namedVolume
- // }
- //}
- //
- //return mounts, volumes, nil
- return nil, nil, nil
-}
-
-// GetTmpfsMounts creates spec.Mount structs for user-requested tmpfs mounts
-func getTmpfsMounts(mounts []string) (map[string]spec.Mount, error) { //nolint
- m := make(map[string]spec.Mount)
- for _, i := range mounts {
- // 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.
-// TODO this needs to be worked on to work in new env
-func addContainerInitBinary(path string) (spec.Mount, error) { //nolint
- 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
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index e102a3234..37f2b3190 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -171,7 +171,7 @@ type ContainerStorageConfig struct {
// These will supersede Image Volumes and VolumesFrom volumes where
// there are conflicts.
// Optional.
- Volumes []*Volumes `json:"volumes,omitempty"`
+ Volumes []*NamedVolume `json:"volumes,omitempty"`
// Devices are devices that will be added to the container.
// Optional.
Devices []spec.LinuxDevice `json:"devices,omitempty"`
@@ -387,10 +387,17 @@ type SpecGenerator struct {
ContainerHealthCheckConfig
}
-// Volumes is a temporary struct to hold input from the User
-type Volumes struct {
- Name string
- Dest string
+// NamedVolume holds information about a named volume that will be mounted into
+// the container.
+type NamedVolume struct {
+ // Name is the name of the named volume to be mounted. May be empty.
+ // If empty, a new named volume with a pseudorandomly generated name
+ // will be mounted at the given destination.
+ Name string
+ // Destination to mount the named volume within the container. Must be
+ // an absolute path. Path will be created if it does not exist.
+ Dest string
+ // Options are options that the named volume will be mounted with.
Options []string
}
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index 55e775d7a..64331cf66 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -22,7 +22,6 @@ import (
"github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
- "github.com/opencontainers/selinux/go-selinux"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh/terminal"
@@ -647,41 +646,6 @@ func ValidateSysctls(strSlice []string) (map[string]string, error) {
return sysctl, nil
}
-// SELinuxKVMLabel returns labels for running kvm isolated containers
-func SELinuxKVMLabel(cLabel string) (string, error) {
- if cLabel == "" {
- // selinux is disabled
- return "", nil
- }
- processLabel, _ := selinux.KVMContainerLabels()
- selinux.ReleaseLabel(processLabel)
- return swapSELinuxLabel(cLabel, processLabel)
-}
-
-// SELinuxInitLabel returns labels for running systemd based containers
-func SELinuxInitLabel(cLabel string) (string, error) {
- if cLabel == "" {
- // selinux is disabled
- return "", nil
- }
- processLabel, _ := selinux.InitContainerLabels()
- selinux.ReleaseLabel(processLabel)
- return swapSELinuxLabel(cLabel, processLabel)
-}
-
-func swapSELinuxLabel(cLabel, processLabel string) (string, error) {
- dcon, err := selinux.NewContext(cLabel)
- if err != nil {
- return "", err
- }
- scon, err := selinux.NewContext(processLabel)
- if err != nil {
- return "", err
- }
- dcon["type"] = scon["type"]
- return dcon.Get(), nil
-}
-
func DefaultContainerConfig() *config.Config {
return containerConfig
}
diff --git a/test/e2e/rmi_test.go b/test/e2e/rmi_test.go
index 765d2b19e..d556cbc72 100644
--- a/test/e2e/rmi_test.go
+++ b/test/e2e/rmi_test.go
@@ -7,6 +7,7 @@ import (
. "github.com/containers/libpod/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
+ . "github.com/onsi/gomega/gexec"
)
var _ = Describe("Podman rmi", func() {
@@ -17,7 +18,6 @@ var _ = Describe("Podman rmi", func() {
)
BeforeEach(func() {
- Skip(v2fail)
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
@@ -37,21 +37,21 @@ var _ = Describe("Podman rmi", func() {
It("podman rmi bogus image", func() {
session := podmanTest.Podman([]string{"rmi", "debian:6.0.10"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(1))
+ Expect(session).Should(Exit(1))
})
It("podman rmi with fq name", func() {
session := podmanTest.PodmanNoCache([]string{"rmi", ALPINE})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
})
It("podman rmi with short name", func() {
session := podmanTest.PodmanNoCache([]string{"rmi", "alpine"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
})
@@ -62,7 +62,7 @@ var _ = Describe("Podman rmi", func() {
images := podmanTest.PodmanNoCache([]string{"images"})
images.WaitWithDefaultTimeout()
fmt.Println(images.OutputToStringArray())
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
})
@@ -70,22 +70,22 @@ var _ = Describe("Podman rmi", func() {
podmanTest.RestoreArtifact(nginx)
session := podmanTest.PodmanNoCache([]string{"rmi", "-fa"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
})
It("podman rmi tagged image", func() {
setup := podmanTest.PodmanNoCache([]string{"images", "-q", ALPINE})
setup.WaitWithDefaultTimeout()
- Expect(setup.ExitCode()).To(Equal(0))
+ Expect(setup).Should(Exit(0))
session := podmanTest.PodmanNoCache([]string{"tag", "alpine", "foo:bar", "foo"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
result := podmanTest.PodmanNoCache([]string{"images", "-q", "foo"})
result.WaitWithDefaultTimeout()
- Expect(result.ExitCode()).To(Equal(0))
+ Expect(result).Should(Exit(0))
Expect(result.LineInOutputContains(setup.OutputToString())).To(BeTrue())
})
@@ -93,12 +93,12 @@ var _ = Describe("Podman rmi", func() {
It("podman rmi image with tags by ID cannot be done without force", func() {
setup := podmanTest.PodmanNoCache([]string{"images", "-q", ALPINE})
setup.WaitWithDefaultTimeout()
- Expect(setup.ExitCode()).To(Equal(0))
+ Expect(setup).Should(Exit(0))
alpineId := setup.OutputToString()
session := podmanTest.PodmanNoCache([]string{"tag", "alpine", "foo:bar", "foo"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
// Trying without --force should fail
result := podmanTest.PodmanNoCache([]string{"rmi", alpineId})
@@ -108,80 +108,80 @@ var _ = Describe("Podman rmi", func() {
// With --force it should work
resultForce := podmanTest.PodmanNoCache([]string{"rmi", "-f", alpineId})
resultForce.WaitWithDefaultTimeout()
- Expect(resultForce.ExitCode()).To(Equal(0))
+ Expect(resultForce).Should(Exit(0))
})
It("podman rmi image that is a parent of another image", func() {
SkipIfRemote()
session := podmanTest.PodmanNoCache([]string{"rmi", "-fa"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
session = podmanTest.PodmanNoCache([]string{"run", "--name", "c_test", ALPINE, "true"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
session = podmanTest.PodmanNoCache([]string{"commit", "-q", "c_test", "test"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
session = podmanTest.PodmanNoCache([]string{"rm", "c_test"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
session = podmanTest.PodmanNoCache([]string{"rmi", ALPINE})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
session = podmanTest.PodmanNoCache([]string{"images", "-q"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
Expect(len(session.OutputToStringArray())).To(Equal(1))
session = podmanTest.PodmanNoCache([]string{"images", "-q", "-a"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
Expect(len(session.OutputToStringArray())).To(Equal(2))
untaggedImg := session.OutputToStringArray()[1]
session = podmanTest.PodmanNoCache([]string{"rmi", "-f", untaggedImg})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(2))
+ Expect(session).Should(Exit(2))
})
It("podman rmi image that is created from another named imaged", func() {
SkipIfRemote()
session := podmanTest.PodmanNoCache([]string{"rmi", "-fa"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
session = podmanTest.PodmanNoCache([]string{"create", "--name", "c_test1", ALPINE, "true"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
session = podmanTest.PodmanNoCache([]string{"commit", "-q", "c_test1", "test1"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
session = podmanTest.PodmanNoCache([]string{"create", "--name", "c_test2", "test1", "true"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
session = podmanTest.PodmanNoCache([]string{"commit", "-q", "c_test2", "test2"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
session = podmanTest.PodmanNoCache([]string{"rm", "-a"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
session = podmanTest.PodmanNoCache([]string{"rmi", "test2"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
session = podmanTest.PodmanNoCache([]string{"images", "-q"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
Expect(len(session.OutputToStringArray())).To(Equal(2))
})
@@ -189,7 +189,7 @@ var _ = Describe("Podman rmi", func() {
SkipIfRemote()
session := podmanTest.PodmanNoCache([]string{"rmi", "-fa"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
dockerfile := `FROM docker.io/library/alpine:latest
RUN mkdir hello
@@ -208,51 +208,51 @@ var _ = Describe("Podman rmi", func() {
session = podmanTest.PodmanNoCache([]string{"images", "-q", "-a"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
numOfImages := len(session.OutputToStringArray())
session = podmanTest.PodmanNoCache([]string{"rmi", "test2"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
session = podmanTest.PodmanNoCache([]string{"images", "-q", "-a"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
Expect(numOfImages - len(session.OutputToStringArray())).To(Equal(2))
session = podmanTest.PodmanNoCache([]string{"rmi", "test"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
session = podmanTest.PodmanNoCache([]string{"images", "-q", "-a"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
Expect(len(session.OutputToStringArray())).To(Equal(1))
podmanTest.BuildImage(dockerfile, "test3", "true")
session = podmanTest.PodmanNoCache([]string{"rmi", ALPINE})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
session = podmanTest.PodmanNoCache([]string{"rmi", "test3"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
session = podmanTest.PodmanNoCache([]string{"images", "-q", "-a"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
Expect(len(session.OutputToString())).To(Equal(0))
})
It("podman rmi -a with no images should be exit 0", func() {
session := podmanTest.PodmanNoCache([]string{"rmi", "-fa"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
session2 := podmanTest.PodmanNoCache([]string{"rmi", "-fa"})
session2.WaitWithDefaultTimeout()
- Expect(session2.ExitCode()).To(Equal(0))
+ Expect(session2).Should(Exit(0))
})
It("podman rmi -a with parent|child images", func() {
@@ -269,11 +269,11 @@ RUN find $LOCAL
session := podmanTest.PodmanNoCache([]string{"rmi", "-a"})
session.WaitWithDefaultTimeout()
fmt.Println(session.OutputToString())
- Expect(session.ExitCode()).To(Equal(0))
+ Expect(session).Should(Exit(0))
images := podmanTest.PodmanNoCache([]string{"images", "-aq"})
images.WaitWithDefaultTimeout()
- Expect(images.ExitCode()).To(Equal(0))
+ Expect(images).Should(Exit(0))
Expect(len(images.OutputToStringArray())).To(Equal(0))
})
@@ -282,7 +282,7 @@ RUN find $LOCAL
It("podman image rm is the same as rmi", func() {
session := podmanTest.PodmanNoCache([]string{"image", "rm"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(125))
+ Expect(session).Should(Exit(125))
match, _ := session.ErrorGrepString("image name or ID must be specified")
Expect(match).To(BeTrue())
})
diff --git a/test/e2e/version_test.go b/test/e2e/version_test.go
index 036b6f621..4d2e14589 100644
--- a/test/e2e/version_test.go
+++ b/test/e2e/version_test.go
@@ -17,7 +17,6 @@ var _ = Describe("Podman version", func() {
)
BeforeEach(func() {
- Skip(v2fail)
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
@@ -43,6 +42,7 @@ var _ = Describe("Podman version", func() {
})
It("podman -v", func() {
+ Skip(v2fail)
session := podmanTest.Podman([]string{"-v"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))