summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--cmd/podman/run.go1
-rw-r--r--libpod/runtime.go25
-rw-r--r--pkg/adapter/containers_remote.go9
-rw-r--r--pkg/rootless/rootless_linux.go89
-rw-r--r--pkg/rootless/rootless_unsupported.go6
-rw-r--r--pkg/varlinkapi/attach.go1
-rw-r--r--pkg/varlinkapi/virtwriter/virtwriter.go24
-rw-r--r--test/e2e/play_kube_test.go209
-rw-r--r--test/system/075-exec.bats20
-rw-r--r--test/system/helpers.bash14
-rw-r--r--troubleshooting.md19
12 files changed, 215 insertions, 204 deletions
diff --git a/README.md b/README.md
index 485ca7b6f..166a4bebe 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
Libpod provides a library for applications looking to use the Container Pod concept,
popularized by Kubernetes. Libpod also contains the Pod Manager tool `(Podman)`. Podman manages pods, containers, container images, and container volumes.
-* [Latest Version: 1.6.0](https://github.com/containers/libpod/releases/latest)
+* [Latest Version: 1.6.2](https://github.com/containers/libpod/releases/latest)
* [Continuous Integration:](contrib/cirrus/README.md) [![Build Status](https://api.cirrus-ci.com/github/containers/libpod.svg)](https://cirrus-ci.com/github/containers/libpod/master)
* [GoDoc: ![GoDoc](https://godoc.org/github.com/containers/libpod/libpod?status.svg)](https://godoc.org/github.com/containers/libpod/libpod)
* Automated continuous release downloads (including remote-client):
diff --git a/cmd/podman/run.go b/cmd/podman/run.go
index 4836c99dc..7aa4cb3c4 100644
--- a/cmd/podman/run.go
+++ b/cmd/podman/run.go
@@ -45,7 +45,6 @@ func runCmd(c *cliconfig.RunValues) error {
span, _ := opentracing.StartSpanFromContext(Ctx, "runCmd")
defer span.Finish()
}
-
if err := createInit(&c.PodmanCommand); err != nil {
return err
}
diff --git a/libpod/runtime.go b/libpod/runtime.go
index e8c80c67e..8f145a809 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -14,7 +14,6 @@ import (
"strings"
"sync"
"syscall"
- "time"
"github.com/BurntSushi/toml"
is "github.com/containers/image/v4/storage"
@@ -353,10 +352,6 @@ func defaultRuntimeConfig() (RuntimeConfig, error) {
// SetXdgDirs ensures the XDG_RUNTIME_DIR env and XDG_CONFIG_HOME variables are set.
// containers/image uses XDG_RUNTIME_DIR to locate the auth file, XDG_CONFIG_HOME is
// use for the libpod.conf configuration file.
-// SetXdgDirs internally calls EnableLinger() so that the user's processes are not
-// killed once the session is terminated. EnableLinger() also attempts to
-// get the runtime directory when XDG_RUNTIME_DIR is not specified.
-// This function should only be called when running rootless.
func SetXdgDirs() error {
if !rootless.IsRootless() {
return nil
@@ -365,21 +360,6 @@ func SetXdgDirs() error {
// Setup XDG_RUNTIME_DIR
runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
- runtimeDirLinger, err := rootless.EnableLinger()
- if err != nil {
- return errors.Wrapf(err, "error enabling user session")
- }
- if runtimeDir == "" && runtimeDirLinger != "" {
- if _, err := os.Stat(runtimeDirLinger); err != nil && os.IsNotExist(err) {
- chWait := make(chan error)
- defer close(chWait)
- if _, err := WaitForFile(runtimeDirLinger, chWait, time.Second*10); err != nil {
- return errors.Wrapf(err, "waiting for directory '%s'", runtimeDirLinger)
- }
- }
- runtimeDir = runtimeDirLinger
- }
-
if runtimeDir == "" {
var err error
runtimeDir, err = util.GetRuntimeDir()
@@ -400,10 +380,11 @@ func SetXdgDirs() error {
// Setup XDG_CONFIG_HOME
if cfgHomeDir := os.Getenv("XDG_CONFIG_HOME"); cfgHomeDir == "" {
- if cfgHomeDir, err = util.GetRootlessConfigHomeDir(); err != nil {
+ cfgHomeDir, err := util.GetRootlessConfigHomeDir()
+ if err != nil {
return err
}
- if err = os.Setenv("XDG_CONFIG_HOME", cfgHomeDir); err != nil {
+ if err := os.Setenv("XDG_CONFIG_HOME", cfgHomeDir); err != nil {
return errors.Wrapf(err, "cannot set XDG_CONFIG_HOME")
}
}
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index f7cb28b0c..f4e83a975 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -1092,6 +1092,7 @@ func configureVarlinkAttachStdio(reader *bufio.Reader, writer *bufio.Writer, std
// These are the special writers that encode input from the client.
varlinkStdinWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.ToStdin)
varlinkResizeWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.TerminalResize)
+ varlinkHangupWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.HangUpFromClient)
go func() {
// Read from the wire and direct to stdout or stderr
@@ -1117,7 +1118,6 @@ func configureVarlinkAttachStdio(reader *bufio.Reader, writer *bufio.Writer, std
}
}
}()
-
if stdin != nil {
// Takes stdinput and sends it over the wire after being encoded
go func() {
@@ -1126,7 +1126,12 @@ func configureVarlinkAttachStdio(reader *bufio.Reader, writer *bufio.Writer, std
sendGenericError(ecChan)
errChan <- err
}
-
+ _, err := varlinkHangupWriter.Write([]byte("EOF"))
+ if err != nil {
+ logrus.Errorf("unable to notify server to hangup: %q", err)
+ }
+ err = varlinkStdinWriter.Close()
+ errChan <- err
}()
}
return errChan
diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go
index 59f2880c3..94c42f7d0 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -11,16 +11,13 @@ import (
"os/exec"
gosignal "os/signal"
"os/user"
- "path/filepath"
"runtime"
"strconv"
- "strings"
"sync"
"unsafe"
"github.com/containers/libpod/pkg/errorhandling"
"github.com/containers/storage/pkg/idtools"
- "github.com/godbus/dbus"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
@@ -212,92 +209,6 @@ func getUserNSFirstChild(fd uintptr) (*os.File, error) {
}
}
-// EnableLinger configures the system to not kill the user processes once the session
-// terminates
-func EnableLinger() (string, error) {
- uid := fmt.Sprintf("%d", GetRootlessUID())
-
- conn, err := dbus.SystemBus()
- if err == nil {
- defer func() {
- if err := conn.Close(); err != nil {
- logrus.Errorf("unable to close dbus connection: %q", err)
- }
- }()
- }
-
- lingerEnabled := false
-
- // If we have a D-BUS connection, attempt to read the LINGER property from it.
- if conn != nil {
- path := dbus.ObjectPath(fmt.Sprintf("/org/freedesktop/login1/user/_%s", uid))
- ret, err := conn.Object("org.freedesktop.login1", path).GetProperty("org.freedesktop.login1.User.Linger")
- if err == nil && ret.Value().(bool) {
- lingerEnabled = true
- }
- }
-
- xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR")
- lingerFile := ""
- if xdgRuntimeDir != "" && !lingerEnabled {
- lingerFile = filepath.Join(xdgRuntimeDir, "libpod/linger")
- _, err := os.Stat(lingerFile)
- if err == nil {
- lingerEnabled = true
- }
- }
-
- if !lingerEnabled {
- // First attempt with D-BUS, if it fails, then attempt with "loginctl enable-linger"
- if conn != nil {
- o := conn.Object("org.freedesktop.login1", "/org/freedesktop/login1")
- ret := o.Call("org.freedesktop.login1.Manager.SetUserLinger", 0, uint32(GetRootlessUID()), true, true)
- if ret.Err == nil {
- lingerEnabled = true
- }
- }
- if !lingerEnabled {
- err := exec.Command("loginctl", "enable-linger", uid).Run()
- if err == nil {
- lingerEnabled = true
- } else {
- logrus.Debugf("cannot run `loginctl enable-linger` for the current user: %v", err)
- }
- }
- if lingerEnabled && lingerFile != "" {
- f, err := os.Create(lingerFile)
- if err == nil {
- if err := f.Close(); err != nil {
- logrus.Errorf("failed to close %s", f.Name())
- }
- } else {
- logrus.Debugf("could not create linger file: %v", err)
- }
- }
- }
-
- if !lingerEnabled {
- return "", nil
- }
-
- // If we have a D-BUS connection, attempt to read the RUNTIME PATH from it.
- if conn != nil {
- path := dbus.ObjectPath(fmt.Sprintf("/org/freedesktop/login1/user/_%s", uid))
- ret, err := conn.Object("org.freedesktop.login1", path).GetProperty("org.freedesktop.login1.User.RuntimePath")
- if err == nil {
- return strings.Trim(ret.String(), "\"\n"), nil
- }
- }
-
- // If XDG_RUNTIME_DIR is not set and the D-BUS call didn't work, try to get the runtime path with "loginctl"
- output, err := exec.Command("loginctl", "-pRuntimePath", "show-user", uid).Output()
- if err != nil {
- logrus.Debugf("could not get RuntimePath using loginctl: %v", err)
- return "", nil
- }
- return strings.Trim(strings.Replace(string(output), "RuntimePath=", "", -1), "\"\n"), nil
-}
-
// joinUserAndMountNS re-exec podman in a new userNS and join the user and mount
// namespace of the specified PID without looking up its parent. Useful to join directly
// the conmon process.
diff --git a/pkg/rootless/rootless_unsupported.go b/pkg/rootless/rootless_unsupported.go
index ce488f364..1499b737f 100644
--- a/pkg/rootless/rootless_unsupported.go
+++ b/pkg/rootless/rootless_unsupported.go
@@ -37,12 +37,6 @@ func GetRootlessGID() int {
return -1
}
-// EnableLinger configures the system to not kill the user processes once the session
-// terminates
-func EnableLinger() (string, error) {
- return "", nil
-}
-
// TryJoinFromFilePaths attempts to join the namespaces of the pid files in paths.
// This is useful when there are already running containers and we
// don't have a pause process yet. We can use the paths to the conmon
diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go
index f8557ae0c..37adbbf55 100644
--- a/pkg/varlinkapi/attach.go
+++ b/pkg/varlinkapi/attach.go
@@ -70,7 +70,6 @@ func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys st
}
reader, writer, _, pw, streams := setupStreams(call)
-
go func() {
if err := virtwriter.Reader(reader, nil, nil, pw, resize, nil); err != nil {
errChan <- err
diff --git a/pkg/varlinkapi/virtwriter/virtwriter.go b/pkg/varlinkapi/virtwriter/virtwriter.go
index 27ecd1f52..dd171943f 100644
--- a/pkg/varlinkapi/virtwriter/virtwriter.go
+++ b/pkg/varlinkapi/virtwriter/virtwriter.go
@@ -5,6 +5,7 @@ import (
"encoding/binary"
"encoding/json"
"io"
+ "time"
"github.com/pkg/errors"
"k8s.io/client-go/tools/remotecommand"
@@ -26,8 +27,14 @@ const (
TerminalResize SocketDest = iota
// Quit and detach
Quit SocketDest = iota
+ // Quit from the client
+ HangUpFromClient SocketDest = iota
)
+// ClientHangup signifies that the client wants to drop its
+// connection from the server
+var ClientHangup = errors.New("client hangup")
+
// IntToSocketDest returns a socketdest based on integer input
func IntToSocketDest(i int) SocketDest {
switch i {
@@ -41,6 +48,8 @@ func IntToSocketDest(i int) SocketDest {
return TerminalResize
case Quit.Int():
return Quit
+ case HangUpFromClient.Int():
+ return HangUpFromClient
default:
return ToStderr
}
@@ -65,7 +74,7 @@ func NewVirtWriteCloser(w *bufio.Writer, dest SocketDest) VirtWriteCloser {
// Close is a required method for a writecloser
func (v VirtWriteCloser) Close() error {
- return nil
+ return v.writer.Flush()
}
// Write prepends a header to the input message. The header is
@@ -96,7 +105,6 @@ func Reader(r *bufio.Reader, output, errput, input io.Writer, resize chan remote
if r == nil {
return errors.Errorf("Reader must not be nil")
}
-
for {
n, err := io.ReadFull(r, headerBytes)
if err != nil {
@@ -107,7 +115,6 @@ func Reader(r *bufio.Reader, output, errput, input io.Writer, resize chan remote
}
messageSize = int64(binary.BigEndian.Uint32(headerBytes[4:8]))
-
switch IntToSocketDest(int(headerBytes[0])) {
case ToStdout:
if output != nil {
@@ -161,7 +168,16 @@ func Reader(r *bufio.Reader, output, errput, input io.Writer, resize chan remote
execEcChan <- int(ecInt)
}
return nil
-
+ case HangUpFromClient:
+ // This sleep allows the pipes to flush themselves before tearing everything down.
+ // It makes me sick to do it but after a full day I cannot put my finger on the race
+ // that occurs when closing things up. It would require a significant rewrite of code
+ // to make the pipes close down properly. Given that we are currently discussing a
+ // rewrite of all things remote, this hardly seems worth resolving.
+ //
+ // reproducer: echo hello | (podman-remote run -i alpine cat)
+ time.Sleep(1 * time.Second)
+ return ClientHangup
default:
// Something really went wrong
return errors.New("unknown multiplex destination")
diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go
index 5d59f0eb0..7069e049d 100644
--- a/test/e2e/play_kube_test.go
+++ b/test/e2e/play_kube_test.go
@@ -23,7 +23,7 @@ metadata:
spec:
hostname: {{ .Hostname }}
containers:
-{{ with .Containers }}
+{{ with .Ctrs }}
{{ range . }}
- command:
{{ range .Cmd }}
@@ -67,47 +67,128 @@ spec:
status: {}
`
-type Pod struct {
- Name string
- Hostname string
- Containers []Container
-}
-
-type Container struct {
- Cmd []string
- Image string
- Name string
- SecurityContext bool
- Caps bool
- CapAdd []string
- CapDrop []string
-}
+var (
+ defaultCtrName = "testCtr"
+ defaultCtrCmd = []string{"top"}
+ defaultCtrImage = ALPINE
+ defaultPodName = "testPod"
+)
-func generateKubeYaml(name string, hostname string, ctrs []Container, fileName string) error {
+func generateKubeYaml(pod *Pod, fileName string) error {
f, err := os.Create(fileName)
if err != nil {
return err
}
defer f.Close()
- testPod := Pod{name, hostname, ctrs}
t, err := template.New("pod").Parse(yamlTemplate)
if err != nil {
return err
}
- if err := t.Execute(f, testPod); err != nil {
+ if err := t.Execute(f, pod); err != nil {
return err
}
return nil
}
+// Pod describes the options a kube yaml can be configured at pod level
+type Pod struct {
+ Name string
+ Hostname string
+ Ctrs []*Ctr
+}
+
+// getPod takes a list of podOptions and returns a pod with sane defaults
+// and the configured options
+// if no containers are added, it will add the default container
+func getPod(options ...podOption) *Pod {
+ p := Pod{defaultPodName, "", make([]*Ctr, 0)}
+ for _, option := range options {
+ option(&p)
+ }
+ if len(p.Ctrs) == 0 {
+ p.Ctrs = []*Ctr{getCtr()}
+ }
+ return &p
+}
+
+type podOption func(*Pod)
+
+func withHostname(h string) podOption {
+ return func(pod *Pod) {
+ pod.Hostname = h
+ }
+}
+
+func withCtr(c *Ctr) podOption {
+ return func(pod *Pod) {
+ pod.Ctrs = append(pod.Ctrs, c)
+ }
+}
+
+// Ctr describes the options a kube yaml can be configured at container level
+type Ctr struct {
+ Name string
+ Image string
+ Cmd []string
+ SecurityContext bool
+ Caps bool
+ CapAdd []string
+ CapDrop []string
+}
+
+// getCtr takes a list of ctrOptions and returns a Ctr with sane defaults
+// and the configured options
+func getCtr(options ...ctrOption) *Ctr {
+ c := Ctr{defaultCtrName, defaultCtrImage, defaultCtrCmd, true, false, nil, nil}
+ for _, option := range options {
+ option(&c)
+ }
+ return &c
+}
+
+type ctrOption func(*Ctr)
+
+func withCmd(cmd []string) ctrOption {
+ return func(c *Ctr) {
+ c.Cmd = cmd
+ }
+}
+
+func withImage(img string) ctrOption {
+ return func(c *Ctr) {
+ c.Image = img
+ }
+}
+
+func withSecurityContext(sc bool) ctrOption {
+ return func(c *Ctr) {
+ c.SecurityContext = sc
+ }
+}
+
+func withCapAdd(caps []string) ctrOption {
+ return func(c *Ctr) {
+ c.CapAdd = caps
+ c.Caps = true
+ }
+}
+
+func withCapDrop(caps []string) ctrOption {
+ return func(c *Ctr) {
+ c.CapDrop = caps
+ c.Caps = true
+ }
+}
+
var _ = Describe("Podman generate kube", func() {
var (
tempdir string
err error
podmanTest *PodmanTestIntegration
+ kubeYaml string
)
BeforeEach(func() {
@@ -118,6 +199,8 @@ var _ = Describe("Podman generate kube", func() {
podmanTest = PodmanTestCreate(tempdir)
podmanTest.Setup()
podmanTest.SeedImages()
+
+ kubeYaml = filepath.Join(podmanTest.TempDir, "kube.yaml")
})
AfterEach(func() {
@@ -127,123 +210,98 @@ var _ = Describe("Podman generate kube", func() {
})
It("podman play kube test correct command", func() {
- ctrName := "testCtr"
- ctrCmd := []string{"top"}
- testContainer := Container{ctrCmd, ALPINE, ctrName, true, false, nil, nil}
- tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml")
-
- err := generateKubeYaml("test", "", []Container{testContainer}, tempFile)
+ err := generateKubeYaml(getPod(), kubeYaml)
Expect(err).To(BeNil())
- kube := podmanTest.Podman([]string{"play", "kube", tempFile})
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
kube.WaitWithDefaultTimeout()
Expect(kube.ExitCode()).To(Equal(0))
- inspect := podmanTest.Podman([]string{"inspect", ctrName})
+ inspect := podmanTest.Podman([]string{"inspect", defaultCtrName})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(Equal(0))
- Expect(inspect.OutputToString()).To(ContainSubstring(ctrCmd[0]))
+ Expect(inspect.OutputToString()).To(ContainSubstring(defaultCtrCmd[0]))
})
It("podman play kube test correct output", func() {
- ctrName := "testCtr"
- ctrCmd := []string{"echo", "hello"}
- testContainer := Container{ctrCmd, ALPINE, ctrName, true, false, nil, nil}
- tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml")
+ p := getPod(withCtr(getCtr(withCmd([]string{"echo", "hello"}))))
- err := generateKubeYaml("test", "", []Container{testContainer}, tempFile)
+ err := generateKubeYaml(p, kubeYaml)
Expect(err).To(BeNil())
- kube := podmanTest.Podman([]string{"play", "kube", tempFile})
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
kube.WaitWithDefaultTimeout()
Expect(kube.ExitCode()).To(Equal(0))
- logs := podmanTest.Podman([]string{"logs", ctrName})
+ logs := podmanTest.Podman([]string{"logs", defaultCtrName})
logs.WaitWithDefaultTimeout()
Expect(logs.ExitCode()).To(Equal(0))
Expect(logs.OutputToString()).To(ContainSubstring("hello"))
- inspect := podmanTest.Podman([]string{"inspect", ctrName, "--format", "'{{ .Config.Cmd }}'"})
+ inspect := podmanTest.Podman([]string{"inspect", defaultCtrName, "--format", "'{{ .Config.Cmd }}'"})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(Equal(0))
Expect(inspect.OutputToString()).To(ContainSubstring("hello"))
})
It("podman play kube test hostname", func() {
- podName := "test"
- ctrName := "testCtr"
- ctrCmd := []string{"top"}
- testContainer := Container{ctrCmd, ALPINE, ctrName, true, false, nil, nil}
- tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml")
-
- err := generateKubeYaml(podName, "", []Container{testContainer}, tempFile)
+ err := generateKubeYaml(getPod(), kubeYaml)
Expect(err).To(BeNil())
- kube := podmanTest.Podman([]string{"play", "kube", tempFile})
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
kube.WaitWithDefaultTimeout()
Expect(kube.ExitCode()).To(Equal(0))
- inspect := podmanTest.Podman([]string{"inspect", ctrName, "--format", "{{ .Config.Hostname }}"})
+ inspect := podmanTest.Podman([]string{"inspect", defaultCtrName, "--format", "{{ .Config.Hostname }}"})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(Equal(0))
- Expect(inspect.OutputToString()).To(Equal(podName))
+ Expect(inspect.OutputToString()).To(Equal(defaultPodName))
})
It("podman play kube test with customized hostname", func() {
hostname := "myhostname"
- ctrName := "testCtr"
- ctrCmd := []string{"top"}
- testContainer := Container{ctrCmd, ALPINE, ctrName, true, false, nil, nil}
- tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml")
-
- err := generateKubeYaml("test", hostname, []Container{testContainer}, tempFile)
+ err := generateKubeYaml(getPod(withHostname(hostname)), kubeYaml)
Expect(err).To(BeNil())
- kube := podmanTest.Podman([]string{"play", "kube", tempFile})
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
kube.WaitWithDefaultTimeout()
Expect(kube.ExitCode()).To(Equal(0))
- inspect := podmanTest.Podman([]string{"inspect", ctrName, "--format", "{{ .Config.Hostname }}"})
+ inspect := podmanTest.Podman([]string{"inspect", defaultCtrName, "--format", "{{ .Config.Hostname }}"})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(Equal(0))
Expect(inspect.OutputToString()).To(Equal(hostname))
})
It("podman play kube cap add", func() {
- ctrName := "testCtr"
- ctrCmd := []string{"cat", "/proc/self/status"}
capAdd := "CAP_SYS_ADMIN"
- testContainer := Container{ctrCmd, ALPINE, ctrName, true, true, []string{capAdd}, nil}
- tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml")
+ ctr := getCtr(withCapAdd([]string{capAdd}), withCmd([]string{"cat", "/proc/self/status"}))
- err := generateKubeYaml("test", "", []Container{testContainer}, tempFile)
+ err := generateKubeYaml(getPod(withCtr(ctr)), kubeYaml)
Expect(err).To(BeNil())
- kube := podmanTest.Podman([]string{"play", "kube", tempFile})
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
kube.WaitWithDefaultTimeout()
Expect(kube.ExitCode()).To(Equal(0))
- inspect := podmanTest.Podman([]string{"inspect", ctrName})
+ inspect := podmanTest.Podman([]string{"inspect", defaultCtrName})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(Equal(0))
Expect(inspect.OutputToString()).To(ContainSubstring(capAdd))
})
- It("podman play kube cap add", func() {
- ctrName := "testCtr"
- ctrCmd := []string{"cat", "/proc/self/status"}
- capDrop := "CAP_SYS_ADMIN"
- testContainer := Container{ctrCmd, ALPINE, ctrName, true, true, []string{capDrop}, nil}
- tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml")
+ It("podman play kube cap drop", func() {
+ capDrop := "CAP_CHOWN"
+ ctr := getCtr(withCapDrop([]string{capDrop}))
- err := generateKubeYaml("test", "", []Container{testContainer}, tempFile)
+ err := generateKubeYaml(getPod(withCtr(ctr)), kubeYaml)
Expect(err).To(BeNil())
- kube := podmanTest.Podman([]string{"play", "kube", tempFile})
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
kube.WaitWithDefaultTimeout()
Expect(kube.ExitCode()).To(Equal(0))
- inspect := podmanTest.Podman([]string{"inspect", ctrName})
+ inspect := podmanTest.Podman([]string{"inspect", defaultCtrName})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(Equal(0))
Expect(inspect.OutputToString()).To(ContainSubstring(capDrop))
@@ -251,19 +309,14 @@ var _ = Describe("Podman generate kube", func() {
It("podman play kube no security context", func() {
// expect play kube to not fail if no security context is specified
- ctrName := "testCtr"
- ctrCmd := "ls"
- testContainer := Container{[]string{ctrCmd}, ALPINE, ctrName, false, false, nil, nil}
- tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml")
-
- err := generateKubeYaml("test", "", []Container{testContainer}, tempFile)
+ err := generateKubeYaml(getPod(withCtr(getCtr(withSecurityContext(false)))), kubeYaml)
Expect(err).To(BeNil())
- kube := podmanTest.Podman([]string{"play", "kube", tempFile})
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
kube.WaitWithDefaultTimeout()
Expect(kube.ExitCode()).To(Equal(0))
- inspect := podmanTest.Podman([]string{"inspect", ctrName})
+ inspect := podmanTest.Podman([]string{"inspect", defaultCtrName})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(Equal(0))
})
diff --git a/test/system/075-exec.bats b/test/system/075-exec.bats
index 11cb98269..472fdd1ab 100644
--- a/test/system/075-exec.bats
+++ b/test/system/075-exec.bats
@@ -29,4 +29,24 @@ load helpers
run_podman rm $cid
}
+@test "podman exec - leak check" {
+ skip_if_remote
+
+ # Start a container in the background then run exec command
+ # three times and make sure no any exec pid hash file leak
+ run_podman run -td $IMAGE /bin/sh
+ cid="$output"
+
+ is "$(check_exec_pid)" "" "exec pid hash file indeed doesn't exist"
+
+ for i in {1..3}; do
+ run_podman exec $cid /bin/true
+ done
+
+ is "$(check_exec_pid)" "" "there isn't any exec pid hash file leak"
+
+ run_podman stop --time 1 $cid
+ run_podman rm -f $cid
+}
+
# vim: filetype=sh
diff --git a/test/system/helpers.bash b/test/system/helpers.bash
index 3d607f4bd..8c061d2c9 100644
--- a/test/system/helpers.bash
+++ b/test/system/helpers.bash
@@ -373,5 +373,19 @@ function random_string() {
head /dev/urandom | tr -dc a-zA-Z0-9 | head -c$length
}
+
+#########################
+# find_exec_pid_files # Returns nothing or exec_pid hash files
+#########################
+#
+# Return exec_pid hash files if exists, otherwise, return nothing
+#
+function find_exec_pid_files() {
+ run_podman info --format '{{.store.RunRoot}}'
+ local storage_path="$output"
+ if [ -d $storage_path ]; then
+ find $storage_path -type f -iname 'exec_pid_*'
+ fi
+}
# END miscellaneous tools
###############################################################################
diff --git a/troubleshooting.md b/troubleshooting.md
index 6fed719f7..c4e577645 100644
--- a/troubleshooting.md
+++ b/troubleshooting.md
@@ -410,3 +410,22 @@ You'll need to either:
* configure the host to use cgroups v1
* update the image to use an updated version of systemd.
+
+### 17) rootless containers exit once the user session exits
+
+
+You need to set lingering mode through loginctl to prevent user processes to be killed once
+the user session completed.
+
+#### Symptom
+
+Once the user logs out all the containers exit.
+
+#### Solution
+You'll need to either:
+
+* loginctl enable-linger $UID
+
+or as root if your user has not enough privileges.
+
+* sudo loginctl enable-linger $UID