summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/source/markdown/podman-pod-create.1.md2
-rw-r--r--libpod/container_inspect.go17
-rw-r--r--libpod/container_internal.go37
-rw-r--r--libpod/container_internal_linux.go13
-rw-r--r--libpod/define/pod_inspect.go6
-rw-r--r--libpod/networking_linux.go16
-rw-r--r--libpod/pod_api.go41
-rw-r--r--libpod/util.go45
-rw-r--r--pkg/specgen/generate/ports.go1
-rw-r--r--pkg/util/utils.go2
-rw-r--r--test/apiv2/20-containers.at25
-rw-r--r--test/e2e/pod_inspect_test.go21
-rw-r--r--test/e2e/run_networking_test.go14
-rw-r--r--test/system/030-run.bats42
-rw-r--r--test/system/075-exec.bats22
-rw-r--r--test/system/410-selinux.bats9
-rw-r--r--test/system/helpers.bash32
-rwxr-xr-xtest/system/helpers.t70
18 files changed, 340 insertions, 75 deletions
diff --git a/docs/source/markdown/podman-pod-create.1.md b/docs/source/markdown/podman-pod-create.1.md
index 1401400bb..d60fc65fe 100644
--- a/docs/source/markdown/podman-pod-create.1.md
+++ b/docs/source/markdown/podman-pod-create.1.md
@@ -108,7 +108,7 @@ If another pod with the same name already exists, replace and remove it. The de
**--share**=*namespace*
-A comma delimited list of kernel namespaces to share. If none or "" is specified, no namespaces will be shared. The namespaces to choose from are ipc, net, pid, user, uts.
+A comma delimited list of kernel namespaces to share. If none or "" is specified, no namespaces will be shared. The namespaces to choose from are ipc, net, pid, uts.
The operator can identify a pod in three ways:
UUID long identifier (“f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778”)
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index b1d86b0a5..680776dba 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -613,22 +613,11 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
// Port bindings.
// Only populate if we're using CNI to configure the network.
- portBindings := make(map[string][]define.InspectHostPort)
if c.config.CreateNetNS {
- for _, port := range c.config.PortMappings {
- key := fmt.Sprintf("%d/%s", port.ContainerPort, port.Protocol)
- hostPorts := portBindings[key]
- if hostPorts == nil {
- hostPorts = []define.InspectHostPort{}
- }
- hostPorts = append(hostPorts, define.InspectHostPort{
- HostIP: port.HostIP,
- HostPort: fmt.Sprintf("%d", port.HostPort),
- })
- portBindings[key] = hostPorts
- }
+ hostConfig.PortBindings = makeInspectPortBindings(c.config.PortMappings)
+ } else {
+ hostConfig.PortBindings = make(map[string][]define.InspectHostPort)
}
- hostConfig.PortBindings = portBindings
// Cap add and cap drop.
// We need a default set of capabilities to compare against.
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index e98e20b9b..a79b9e5a8 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -28,7 +28,6 @@ import (
securejoin "github.com/cyphar/filepath-securejoin"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
- "github.com/opencontainers/selinux/go-selinux/label"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -1759,32 +1758,40 @@ func (c *Container) postDeleteHooks(ctx context.Context) error {
return nil
}
-// writeStringToRundir copies the provided file to the runtimedir
-func (c *Container) writeStringToRundir(destFile, output string) (string, error) {
+// writeStringToRundir writes the given string to a file with the given name in
+// the container's temporary files directory. The file will be chown'd to the
+// container's root user and have an appropriate SELinux label set.
+// If a file with the same name already exists, it will be deleted and recreated
+// with the new contents.
+// Returns the full path to the new file.
+func (c *Container) writeStringToRundir(destFile, contents string) (string, error) {
destFileName := filepath.Join(c.state.RunDir, destFile)
if err := os.Remove(destFileName); err != nil && !os.IsNotExist(err) {
return "", errors.Wrapf(err, "error removing %s for container %s", destFile, c.ID())
}
- f, err := os.Create(destFileName)
- if err != nil {
- return "", errors.Wrapf(err, "unable to create %s", destFileName)
- }
- defer f.Close()
- if err := f.Chown(c.RootUID(), c.RootGID()); err != nil {
+ if err := writeStringToPath(destFileName, contents, c.config.MountLabel, c.RootUID(), c.RootGID()); err != nil {
return "", err
}
- if _, err := f.WriteString(output); err != nil {
- return "", errors.Wrapf(err, "unable to write %s", destFileName)
- }
- // Relabel runDirResolv for the container
- if err := label.Relabel(destFileName, c.config.MountLabel, false); err != nil {
+ return destFileName, nil
+}
+
+// writeStringToStaticDir writes the given string to a file with the given name
+// in the container's permanent files directory. The file will be chown'd to the
+// container's root user and have an appropriate SELinux label set.
+// Unlike writeStringToRundir, will *not* delete and re-create if the file
+// already exists (will instead error).
+// Returns the full path to the new file.
+func (c *Container) writeStringToStaticDir(filename, contents string) (string, error) {
+ destFileName := filepath.Join(c.config.StaticDir, filename)
+
+ if err := writeStringToPath(destFileName, contents, c.config.MountLabel, c.RootUID(), c.RootGID()); err != nil {
return "", err
}
- return filepath.Join(c.state.RunDir, destFile), nil
+ return destFileName, nil
}
// appendStringToRundir appends the provided string to the runtimedir file
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index b2711745e..255505416 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -214,6 +214,9 @@ func (c *Container) getUserOverrides() *lookup.Overrides {
}
}
}
+ if path, ok := c.state.BindMounts["/etc/passwd"]; ok {
+ overrides.ContainerEtcPasswdPath = path
+ }
return &overrides
}
@@ -1513,6 +1516,14 @@ func (c *Container) generatePasswd() (string, error) {
if !c.config.AddCurrentUserPasswdEntry && c.config.User == "" {
return "", nil
}
+ if MountExists(c.config.Spec.Mounts, "/etc/passwd") {
+ return "", nil
+ }
+ // Re-use passwd if possible
+ passwdPath := filepath.Join(c.config.StaticDir, "passwd")
+ if _, err := os.Stat(passwdPath); err == nil {
+ return passwdPath, nil
+ }
pwd := ""
if c.config.User != "" {
entry, err := c.generateUserPasswdEntry()
@@ -1536,7 +1547,7 @@ func (c *Container) generatePasswd() (string, error) {
if err != nil && !os.IsNotExist(err) {
return "", errors.Wrapf(err, "unable to read passwd file %s", originPasswdFile)
}
- passwdFile, err := c.writeStringToRundir("passwd", string(orig)+pwd)
+ passwdFile, err := c.writeStringToStaticDir("passwd", string(orig)+pwd)
if err != nil {
return "", errors.Wrapf(err, "failed to create temporary passwd file")
}
diff --git a/libpod/define/pod_inspect.go b/libpod/define/pod_inspect.go
index 7f06e16fc..634cbb728 100644
--- a/libpod/define/pod_inspect.go
+++ b/libpod/define/pod_inspect.go
@@ -3,8 +3,6 @@ package define
import (
"net"
"time"
-
- "github.com/cri-o/ocicni/pkg/ocicni"
)
// InspectPodData contains detailed information on a pod's configuration and
@@ -60,7 +58,7 @@ type InspectPodData struct {
type InspectPodInfraConfig struct {
// PortBindings are ports that will be forwarded to the infra container
// and then shared with the pod.
- PortBindings []ocicni.PortMapping
+ PortBindings map[string][]InspectHostPort
// HostNetwork is whether the infra container (and thus the whole pod)
// will use the host's network and not create a network namespace.
HostNetwork bool
@@ -89,6 +87,8 @@ type InspectPodInfraConfig struct {
// HostAdd adds a number of hosts to the infra container's resolv.conf
// which will be shared with the rest of the pod.
HostAdd []string
+ // Networks is a list of CNI networks te pod will join.
+ Networks []string
}
// InspectPodContainerInfo contains information on a container in a pod.
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 5a8faa7a4..1e79e8732 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -587,21 +587,7 @@ func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) {
// network.
func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, error) {
settings := new(define.InspectNetworkSettings)
- settings.Ports = make(map[string][]define.InspectHostPort)
- if c.config.PortMappings != nil {
- for _, port := range c.config.PortMappings {
- key := fmt.Sprintf("%d/%s", port.ContainerPort, port.Protocol)
- mapping := settings.Ports[key]
- if mapping == nil {
- mapping = []define.InspectHostPort{}
- }
- mapping = append(mapping, define.InspectHostPort{
- HostIP: port.HostIP,
- HostPort: fmt.Sprintf("%d", port.HostPort),
- })
- settings.Ports[key] = mapping
- }
- }
+ settings.Ports = makeInspectPortBindings(c.config.PortMappings)
// We can't do more if the network is down.
if c.state.NetNS == nil {
diff --git a/libpod/pod_api.go b/libpod/pod_api.go
index a02b171e1..f2ef81bec 100644
--- a/libpod/pod_api.go
+++ b/libpod/pod_api.go
@@ -481,6 +481,41 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
}
}
+ // Infra config contains detailed information on the pod's infra
+ // container.
+ var infraConfig *define.InspectPodInfraConfig
+ if p.config.InfraContainer != nil && p.config.InfraContainer.HasInfraContainer {
+ infraConfig = new(define.InspectPodInfraConfig)
+ infraConfig.HostNetwork = p.config.InfraContainer.HostNetwork
+ infraConfig.StaticIP = p.config.InfraContainer.StaticIP
+ infraConfig.StaticMAC = p.config.InfraContainer.StaticMAC
+ infraConfig.NoManageResolvConf = p.config.InfraContainer.UseImageResolvConf
+ infraConfig.NoManageHosts = p.config.InfraContainer.UseImageHosts
+
+ if len(p.config.InfraContainer.DNSServer) > 0 {
+ infraConfig.DNSServer = make([]string, 0, len(p.config.InfraContainer.DNSServer))
+ infraConfig.DNSServer = append(infraConfig.DNSServer, p.config.InfraContainer.DNSServer...)
+ }
+ if len(p.config.InfraContainer.DNSSearch) > 0 {
+ infraConfig.DNSSearch = make([]string, 0, len(p.config.InfraContainer.DNSSearch))
+ infraConfig.DNSSearch = append(infraConfig.DNSSearch, p.config.InfraContainer.DNSSearch...)
+ }
+ if len(p.config.InfraContainer.DNSOption) > 0 {
+ infraConfig.DNSOption = make([]string, 0, len(p.config.InfraContainer.DNSOption))
+ infraConfig.DNSOption = append(infraConfig.DNSOption, p.config.InfraContainer.DNSOption...)
+ }
+ if len(p.config.InfraContainer.HostAdd) > 0 {
+ infraConfig.HostAdd = make([]string, 0, len(p.config.InfraContainer.HostAdd))
+ infraConfig.HostAdd = append(infraConfig.HostAdd, p.config.InfraContainer.HostAdd...)
+ }
+ if len(p.config.InfraContainer.Networks) > 0 {
+ infraConfig.Networks = make([]string, 0, len(p.config.InfraContainer.Networks))
+ infraConfig.Networks = append(infraConfig.Networks, p.config.InfraContainer.Networks...)
+ }
+
+ infraConfig.PortBindings = makeInspectPortBindings(p.config.InfraContainer.PortBindings)
+ }
+
inspectData := define.InspectPodData{
ID: p.ID(),
Name: p.Name(),
@@ -490,12 +525,12 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
State: podState,
Hostname: p.config.Hostname,
Labels: p.Labels(),
- CreateCgroup: false,
+ CreateCgroup: p.config.UsePodCgroup,
CgroupParent: p.CgroupParent(),
CgroupPath: p.state.CgroupPath,
- CreateInfra: false,
+ CreateInfra: infraConfig != nil,
InfraContainerID: p.state.InfraContainerID,
- InfraConfig: nil,
+ InfraConfig: infraConfig,
SharedNamespaces: sharesNS,
NumContainers: uint(len(containers)),
Containers: ctrs,
diff --git a/libpod/util.go b/libpod/util.go
index 7504295f0..a8d405b5f 100644
--- a/libpod/util.go
+++ b/libpod/util.go
@@ -15,8 +15,10 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/libpod/v2/libpod/define"
"github.com/containers/libpod/v2/utils"
+ "github.com/cri-o/ocicni/pkg/ocicni"
"github.com/fsnotify/fsnotify"
spec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -254,3 +256,46 @@ func makeHTTPAttachHeader(stream byte, length uint32) []byte {
binary.BigEndian.PutUint32(header[4:], length)
return header
}
+
+// Convert OCICNI port bindings into Inspect-formatted port bindings.
+func makeInspectPortBindings(bindings []ocicni.PortMapping) map[string][]define.InspectHostPort {
+ portBindings := make(map[string][]define.InspectHostPort)
+ for _, port := range bindings {
+ key := fmt.Sprintf("%d/%s", port.ContainerPort, port.Protocol)
+ hostPorts := portBindings[key]
+ if hostPorts == nil {
+ hostPorts = []define.InspectHostPort{}
+ }
+ hostPorts = append(hostPorts, define.InspectHostPort{
+ HostIP: port.HostIP,
+ HostPort: fmt.Sprintf("%d", port.HostPort),
+ })
+ portBindings[key] = hostPorts
+ }
+ return portBindings
+}
+
+// Write a given string to a new file at a given path.
+// Will error if a file with the given name already exists.
+// Will be chown'd to the UID/GID provided and have the provided SELinux label
+// set.
+func writeStringToPath(path, contents, mountLabel string, uid, gid int) error {
+ f, err := os.Create(path)
+ if err != nil {
+ return errors.Wrapf(err, "unable to create %s", path)
+ }
+ defer f.Close()
+ if err := f.Chown(uid, gid); err != nil {
+ return err
+ }
+
+ if _, err := f.WriteString(contents); err != nil {
+ return errors.Wrapf(err, "unable to write %s", path)
+ }
+ // Relabel runDirResolv for the container
+ if err := label.Relabel(path, mountLabel, false); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/pkg/specgen/generate/ports.go b/pkg/specgen/generate/ports.go
index 9412ecfbf..c8d1c27c5 100644
--- a/pkg/specgen/generate/ports.go
+++ b/pkg/specgen/generate/ports.go
@@ -356,6 +356,7 @@ func checkProtocol(protocol string, allowSCTP bool) ([]string, error) {
splitProto := strings.Split(protocol, ",")
// Don't error on duplicates - just deduplicate
for _, p := range splitProto {
+ p = strings.ToLower(p)
switch p {
case protoTCP, "":
protocols[protoTCP] = struct{}{}
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index 47d3e231d..8a78e3e3c 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -555,7 +555,7 @@ func ValidatePullType(pullType string) (PullType, error) {
switch pullType {
case "always":
return PullImageAlways, nil
- case "missing":
+ case "missing", "IfNotPresent":
return PullImageMissing, nil
case "never":
return PullImageNever, nil
diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at
index 9a1db5154..4bb00398e 100644
--- a/test/apiv2/20-containers.at
+++ b/test/apiv2/20-containers.at
@@ -51,17 +51,19 @@ cid=$(jq -r '.[0].Id' <<<"$output")
t DELETE libpod/containers/$cid 204
-# Ensure that API does not occur: Create Container creates an invalid and the container fails to start
-# https://github.com/containers/libpod/issues/6799
-CNAME=testArgs
-t POST libpod/containers/create?name=${CNAME} Image=${IMAGE} 201 \
+# Issue #6799: it should be possible to start a container, even w/o args.
+t POST libpod/containers/create?name=test_noargs Image=${IMAGE} 201 \
.Id~[0-9a-f]\\{64\\}
-t GET libpod/containers/json?limit=1 200 \
- length=1 \
- .[0].Id~[0-9a-f]\\{64\\}
-cid=$(jq -r '.[0].Id' <<<"$output")
-# This step should start the container properly
-t POST libpod/containers/${cid}/start '' 204
+cid=$(jq -r '.Id' <<<"$output")
+# Prior to the fix in #6835, this would fail 500 "args must not be empty"
+t POST libpod/containers/${cid}/start '' 204
+# Container should exit almost immediately. Wait for it, confirm successful run
+t POST libpod/containers/${cid}/wait '' 200 '0'
+t GET libpod/containers/${cid}/json 200 \
+ .Id=$cid \
+ .State.Status~\\\(exited\\\|stopped\\\) \
+ .State.Running=false \
+ .State.ExitCode=0
t DELETE libpod/containers/$cid 204
CNAME=myfoo
@@ -75,7 +77,8 @@ t POST "libpod/commit?container=nonesuch" '' 404
# Comment can only be used with docker format, not OCI
cparam="repo=newrepo&comment=foo&author=bob"
-t POST "libpod/commit?container=$CNAME&$cparam" '' 500
+t POST "libpod/commit?container=$CNAME&$cparam" '' 500 \
+ .cause="messages are only compatible with the docker image format (-f docker)"
# Commit a new image from the container
t POST "libpod/commit?container=$CNAME" '' 200 \
diff --git a/test/e2e/pod_inspect_test.go b/test/e2e/pod_inspect_test.go
index 5e3634435..16bf1c4c9 100644
--- a/test/e2e/pod_inspect_test.go
+++ b/test/e2e/pod_inspect_test.go
@@ -1,8 +1,11 @@
package integration
import (
+ "encoding/json"
"os"
+ "github.com/containers/libpod/v2/libpod/define"
+
. "github.com/containers/libpod/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -79,4 +82,22 @@ var _ = Describe("Podman pod inspect", func() {
index := len(inspectCreateCommand) - len(createCommand)
Expect(inspectCreateCommand[index:]).To(Equal(createCommand))
})
+
+ It("podman pod inspect outputs port bindings", func() {
+ podName := "testPod"
+ create := podmanTest.Podman([]string{"pod", "create", "--name", podName, "-p", "8080:80"})
+ create.WaitWithDefaultTimeout()
+ Expect(create.ExitCode()).To(Equal(0))
+
+ inspectOut := podmanTest.Podman([]string{"pod", "inspect", podName})
+ inspectOut.WaitWithDefaultTimeout()
+ Expect(inspectOut.ExitCode()).To(Equal(0))
+
+ inspectJSON := new(define.InspectPodData)
+ err := json.Unmarshal(inspectOut.Out.Contents(), inspectJSON)
+ Expect(err).To(BeNil())
+ Expect(inspectJSON.InfraConfig).To(Not(BeNil()))
+ Expect(len(inspectJSON.InfraConfig.PortBindings["80/tcp"])).To(Equal(1))
+ Expect(inspectJSON.InfraConfig.PortBindings["80/tcp"][0].HostPort).To(Equal("8080"))
+ })
})
diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go
index 5a463d46f..467d0c5ef 100644
--- a/test/e2e/run_networking_test.go
+++ b/test/e2e/run_networking_test.go
@@ -88,6 +88,20 @@ var _ = Describe("Podman run networking", func() {
Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal(""))
})
+ It("podman run -p 8080:80/TCP", func() {
+ name := "testctr"
+ // "TCP" in upper characters
+ session := podmanTest.Podman([]string{"create", "-t", "-p", "8080:80/TCP", "--name", name, ALPINE, "/bin/sh"})
+ session.WaitWithDefaultTimeout()
+ inspectOut := podmanTest.InspectContainer(name)
+ Expect(len(inspectOut)).To(Equal(1))
+ Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1))
+ // "tcp" in lower characters
+ Expect(len(inspectOut[0].NetworkSettings.Ports["80/tcp"])).To(Equal(1))
+ Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Equal("8080"))
+ Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal(""))
+ })
+
It("podman run -p 80/udp", func() {
name := "testctr"
session := podmanTest.Podman([]string{"create", "-t", "-p", "80/udp", "--name", name, ALPINE, "/bin/sh"})
diff --git a/test/system/030-run.bats b/test/system/030-run.bats
index bc6347012..13fec20ad 100644
--- a/test/system/030-run.bats
+++ b/test/system/030-run.bats
@@ -242,4 +242,46 @@ echo $rand | 0 | $rand
run_podman rmi myi
}
+# #6735 : complex interactions with multiple user namespaces
+# The initial report has to do with bind mounts, but that particular
+# symptom only manifests on a fedora container image -- we have no
+# reproducer on alpine. Checking directory ownership is good enough.
+@test "podman run : user namespace preserved root ownership" {
+ for priv in "" "--privileged"; do
+ for user in "--user=0" "--user=100"; do
+ for keepid in "" "--userns=keep-id"; do
+ opts="$priv $user $keepid"
+
+ for dir in /etc /usr;do
+ run_podman run --rm $opts $IMAGE stat -c '%u:%g:%n' $dir
+ remove_same_dev_warning # grumble
+ is "$output" "0:0:$dir" "run $opts ($dir)"
+ done
+ done
+ done
+ done
+}
+
+# #6829 : add username to /etc/passwd inside container if --userns=keep-id
+@test "podman run : add username to /etc/passwd if --userns=keep-id" {
+ # Default: always run as root
+ run_podman run --rm $IMAGE id -un
+ is "$output" "root" "id -un on regular container"
+
+ # This would always work on root, but is new behavior on rootless: #6829
+ # adds a user entry to /etc/passwd
+ run_podman run --rm --userns=keep-id $IMAGE id -un
+ is "$output" "$(id -un)" "username on container with keep-id"
+
+ # --privileged should make no difference
+ run_podman run --rm --privileged --userns=keep-id $IMAGE id -un
+ remove_same_dev_warning # grumble
+ is "$output" "$(id -un)" "username on container with keep-id"
+
+ # ...but explicitly setting --user should override keep-id
+ run_podman run --rm --privileged --userns=keep-id --user=0 $IMAGE id -un
+ remove_same_dev_warning # grumble
+ is "$output" "root" "--user=0 overrides keep-id"
+}
+
# vim: filetype=sh
diff --git a/test/system/075-exec.bats b/test/system/075-exec.bats
index f8c7f2766..945bcfa2d 100644
--- a/test/system/075-exec.bats
+++ b/test/system/075-exec.bats
@@ -6,8 +6,6 @@
load helpers
@test "podman exec - basic test" {
- skip_if_remote
-
rand_filename=$(random_string 20)
rand_content=$(random_string 50)
@@ -80,4 +78,24 @@ load helpers
run_podman rm $cid
}
+# #6829 : add username to /etc/passwd inside container if --userns=keep-id
+# #6593 : doesn't actually work with podman exec
+@test "podman exec - with keep-id" {
+ skip "Please enable once #6593 is fixed"
+
+ run_podman run -d --userns=keep-id $IMAGE sh -c \
+ "echo READY;while [ ! -f /stop ]; do sleep 1; done"
+ cid="$output"
+ wait_for_ready $cid
+
+ run_podman exec $cid id -un
+ is "$output" "$(id -un)" "container is running as current user"
+
+ # Until #6593 gets fixed, this just hangs. The server process barfs with:
+ # unable to find user <username>: no matching entries in passwd file
+ run_podman exec --user=$(id -un) $cid touch /stop
+ run_podman wait $cid
+ run_podman rm $cid
+}
+
# vim: filetype=sh
diff --git a/test/system/410-selinux.bats b/test/system/410-selinux.bats
index 1769730f0..497e29b3e 100644
--- a/test/system/410-selinux.bats
+++ b/test/system/410-selinux.bats
@@ -19,15 +19,8 @@ function check_label() {
# FIXME: on some CI systems, 'run --privileged' emits a spurious
# warning line about dup devices. Ignore it.
+ remove_same_dev_warning
local context="$output"
- if [ ${#lines[@]} -gt 1 ]; then
- if expr "${lines[0]}" : "WARNING: .* type, major" >/dev/null; then
- echo "# ${lines[0]} [ignored]" >&3
- context="${lines[1]}"
- else
- die "FAILED: too much output, expected one single line"
- fi
- fi
is "$context" ".*_u:system_r:.*" "SELinux role should always be system_r"
diff --git a/test/system/helpers.bash b/test/system/helpers.bash
index 5301644d6..4239ef876 100644
--- a/test/system/helpers.bash
+++ b/test/system/helpers.bash
@@ -392,5 +392,37 @@ function find_exec_pid_files() {
find $storage_path -type f -iname 'exec_pid_*'
fi
}
+
+
+#############################
+# remove_same_dev_warning # Filter out useless warning from output
+#############################
+#
+# On some CI systems, 'podman run --privileged' emits a useless warning:
+#
+# WARNING: The same type, major and minor should not be used for multiple devices.
+#
+# This obviously screws us up when we look at output results.
+#
+# This function removes the warning from $output and $lines
+#
+function remove_same_dev_warning() {
+ # No input arguments. We operate in-place on $output and $lines
+
+ local i=0
+ local -a new_lines=()
+ while [[ $i -lt ${#lines[@]} ]]; do
+ if expr "${lines[$i]}" : 'WARNING: .* same type, major.* multiple' >/dev/null; then
+ :
+ else
+ new_lines+=("${lines[$i]}")
+ fi
+ i=$(( i + 1 ))
+ done
+
+ lines=("${new_lines[@]}")
+ output=$(printf '%s\n' "${lines[@]}")
+}
+
# END miscellaneous tools
###############################################################################
diff --git a/test/system/helpers.t b/test/system/helpers.t
index 7b4e48a84..a022f11c4 100755
--- a/test/system/helpers.t
+++ b/test/system/helpers.t
@@ -23,7 +23,8 @@ rc=0
function check_result {
testnum=$(expr $testnum + 1)
if [ "$1" = "$2" ]; then
- echo "ok $testnum $3 = $1"
+ # Multi-level echo flattens newlines, makes success messages readable
+ echo $(echo "ok $testnum $3 = $1")
else
echo "not ok $testnum $3"
echo "# expected: $2"
@@ -141,5 +142,72 @@ done < <(parse_table "$table")
# END dprint
###############################################################################
+# BEGIN remove_same_dev_warning
+
+# Test-helper function: runs remove_same_dev_warning, compares resulting
+# value of $lines and $output to expected values given on command line
+function check_same_dev() {
+ local testname="$1"; shift
+ local -a expect_lines=("$@")
+ local nl="
+"
+
+ remove_same_dev_warning
+
+ # After processing, check the expected number of lines
+ check_result "${#lines[@]}" "${#@}" "$testname: expected # of lines"
+
+ # ...and each expected line
+ local expect_output=""
+ local i=0
+ while [ $i -lt ${#expect_lines[@]} ]; do
+ check_result "${lines[$i]}" "${expect_lines[$i]}" "$testname: line $i"
+ expect_output+="${expect_lines[$i]}$nl"
+ i=$(( i + 1 ))
+ done
+
+ # ...and the possibly-multi-line $output
+ check_result "$output" "${expect_output%%$nl}" "$testname: output"
+}
+
+# Simplest case: nothing removed.
+declare -a lines=("a b c" "d" "e f")
+check_same_dev "abc" "a b c" "d" "e f"
+
+# Confirm that the warning message is removed from the beginning
+declare -a lines=(
+ "WARNING: The same type, major and minor should not be used for multiple devices."
+ "a"
+ "b"
+ "c"
+)
+check_same_dev "warning is removed" a b c
+
+# ...and from the middle (we do not expect to see this)
+declare -a lines=(
+ "WARNING: The same type, major and minor should not be used for multiple devices."
+ "a"
+ "b"
+ "WARNING: The same type, major and minor should not be used for multiple devices."
+ "c"
+)
+check_same_dev "multiple warnings removed" a b c
+
+# Corner case: two lines of output, only one of which we care about
+declare -a lines=(
+ "WARNING: The same type, major and minor should not be used for multiple devices."
+ "this is the only line we care about"
+)
+check_same_dev "one-line output" "this is the only line we care about"
+
+# Corner case: one line of output, but we expect zero.
+declare -a lines=(
+ "WARNING: The same type, major and minor should not be used for multiple devices."
+)
+check_same_dev "zero-line output"
+
+
+# END remove_same_dev_warning
+###############################################################################
exit $rc