summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libpod/container_api.go2
-rw-r--r--libpod/container_internal.go89
-rw-r--r--libpod/container_internal_linux.go34
-rw-r--r--libpod/oci.go61
-rw-r--r--libpod/runtime_ctr.go2
-rw-r--r--libpod/runtime_pod_linux.go19
-rw-r--r--test/e2e/run_dns_test.go21
7 files changed, 179 insertions, 49 deletions
diff --git a/libpod/container_api.go b/libpod/container_api.go
index c706c5b30..d99aec5b4 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -688,7 +688,7 @@ func (c *Container) Sync() error {
(c.state.State != ContainerStateConfigured) {
oldState := c.state.State
// TODO: optionally replace this with a stat for the exit file
- if err := c.runtime.ociRuntime.updateContainerStatus(c); err != nil {
+ if err := c.runtime.ociRuntime.updateContainerStatus(c, true); err != nil {
return err
}
// Only save back to DB if state changed
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index de15736f4..6843759a6 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -13,8 +13,10 @@ import (
"strconv"
"strings"
"syscall"
+ "time"
"github.com/containers/buildah/imagebuildah"
+ "github.com/containers/libpod/pkg/ctime"
"github.com/containers/libpod/pkg/hooks"
"github.com/containers/libpod/pkg/hooks/exec"
"github.com/containers/libpod/pkg/lookup"
@@ -31,6 +33,7 @@ import (
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/text/language"
+ kwait "k8s.io/apimachinery/pkg/util/wait"
)
const (
@@ -147,6 +150,77 @@ func (c *Container) execPidPath(sessionID string) string {
return filepath.Join(c.state.RunDir, "exec_pid_"+sessionID)
}
+// exitFilePath gets the path to the container's exit file
+func (c *Container) exitFilePath() string {
+ return filepath.Join(c.runtime.ociRuntime.exitsDir, c.ID())
+}
+
+// Wait for the container's exit file to appear.
+// When it does, update our state based on it.
+func (c *Container) waitForExitFileAndSync() error {
+ exitFile := c.exitFilePath()
+
+ err := kwait.ExponentialBackoff(
+ kwait.Backoff{
+ Duration: 500 * time.Millisecond,
+ Factor: 1.2,
+ Steps: 6,
+ },
+ func() (bool, error) {
+ _, err := os.Stat(exitFile)
+ if err != nil {
+ // wait longer
+ return false, nil
+ }
+ return true, nil
+ })
+ if err != nil {
+ // Exit file did not appear
+ // Reset our state
+ c.state.ExitCode = -1
+ c.state.FinishedTime = time.Now()
+ c.state.State = ContainerStateStopped
+
+ if err2 := c.save(); err2 != nil {
+ logrus.Errorf("Error saving container %s state: %v", c.ID(), err2)
+ }
+
+ return err
+ }
+
+ if err := c.runtime.ociRuntime.updateContainerStatus(c, false); err != nil {
+ return err
+ }
+
+ return c.save()
+}
+
+// Handle the container exit file.
+// The exit file is used to supply container exit time and exit code.
+// This assumes the exit file already exists.
+func (c *Container) handleExitFile(exitFile string, fi os.FileInfo) error {
+ c.state.FinishedTime = ctime.Created(fi)
+ statusCodeStr, err := ioutil.ReadFile(exitFile)
+ if err != nil {
+ return errors.Wrapf(err, "failed to read exit file for container %s", c.ID())
+ }
+ statusCode, err := strconv.Atoi(string(statusCodeStr))
+ if err != nil {
+ return errors.Wrapf(err, "error converting exit status code (%q) for container %s to int",
+ c.ID(), statusCodeStr)
+ }
+ c.state.ExitCode = int32(statusCode)
+
+ oomFilePath := filepath.Join(c.bundlePath(), "oom")
+ if _, err = os.Stat(oomFilePath); err == nil {
+ c.state.OOMKilled = true
+ }
+
+ c.state.Exited = true
+
+ return nil
+}
+
// Sync this container with on-disk state and runtime status
// Should only be called with container lock held
// This function should suffice to ensure a container's state is accurate and
@@ -162,7 +236,7 @@ func (c *Container) syncContainer() error {
(c.state.State != ContainerStateExited) {
oldState := c.state.State
// TODO: optionally replace this with a stat for the exit file
- if err := c.runtime.ociRuntime.updateContainerStatus(c); err != nil {
+ if err := c.runtime.ociRuntime.updateContainerStatus(c, false); err != nil {
return err
}
// Only save back to DB if state changed
@@ -674,13 +748,8 @@ func (c *Container) stop(timeout uint) error {
return err
}
- // Sync the container's state to pick up return code
- if err := c.runtime.ociRuntime.updateContainerStatus(c); err != nil {
- return err
- }
-
- // Container should clean itself up
- return nil
+ // Wait until we have an exit file, and sync once we do
+ return c.waitForExitFileAndSync()
}
// Internal, non-locking function to pause a container
@@ -1158,6 +1227,10 @@ func (c *Container) generateHosts() (string, error) {
hosts += fmt.Sprintf("%s %s\n", fields[1], fields[0])
}
}
+ if len(c.state.NetworkStatus) > 0 && len(c.state.NetworkStatus[0].IPs) > 0 {
+ ipAddress := strings.Split(c.state.NetworkStatus[0].IPs[0].Address.String(), "/")[0]
+ hosts += fmt.Sprintf("%s\t%s\n", ipAddress, c.Hostname())
+ }
return c.writeStringToRundir("hosts", hosts)
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index df94a0571..163cd75e7 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -360,19 +360,31 @@ func (c *Container) setupSystemd(mounts []spec.Mount, g generate.Generator) erro
g.AddMount(tmpfsMnt)
}
- cgroupPath, err := c.CGroupPath()
- if err != nil {
- return err
- }
- sourcePath := filepath.Join("/sys/fs/cgroup/systemd", cgroupPath)
+ // rootless containers have no write access to /sys/fs/cgroup, so don't
+ // add any mount into the container.
+ if !rootless.IsRootless() {
+ cgroupPath, err := c.CGroupPath()
+ if err != nil {
+ return err
+ }
+ sourcePath := filepath.Join("/sys/fs/cgroup/systemd", cgroupPath)
- systemdMnt := spec.Mount{
- Destination: "/sys/fs/cgroup/systemd",
- Type: "bind",
- Source: sourcePath,
- Options: []string{"bind", "private"},
+ systemdMnt := spec.Mount{
+ Destination: "/sys/fs/cgroup/systemd",
+ Type: "bind",
+ Source: sourcePath,
+ Options: []string{"bind", "private"},
+ }
+ g.AddMount(systemdMnt)
+ } else {
+ systemdMnt := spec.Mount{
+ Destination: "/sys/fs/cgroup/systemd",
+ Type: "bind",
+ Source: "/sys/fs/cgroup/systemd",
+ Options: []string{"bind", "nodev", "noexec", "nosuid"},
+ }
+ g.AddMount(systemdMnt)
}
- g.AddMount(systemdMnt)
return nil
}
diff --git a/libpod/oci.go b/libpod/oci.go
index ca8f967c4..233bacfbb 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -11,12 +11,10 @@ import (
"os/exec"
"path/filepath"
"runtime"
- "strconv"
"strings"
"syscall"
"time"
- "github.com/containers/libpod/pkg/ctime"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/util"
"github.com/coreos/go-systemd/activation"
@@ -443,6 +441,7 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res
}
return errors.Wrapf(ErrInternal, "container create failed")
}
+ ctr.state.PID = ss.si.Pid
case <-time.After(ContainerCreateTimeout):
return errors.Wrapf(ErrInternal, "container creation timeout")
}
@@ -451,17 +450,47 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res
// updateContainerStatus retrieves the current status of the container from the
// runtime. It updates the container's state but does not save it.
-func (r *OCIRuntime) updateContainerStatus(ctr *Container) error {
- state := new(spec.State)
+// If useRunc is false, we will not directly hit runc to see the container's
+// status, but will instead only check for the existence of the conmon exit file
+// and update state to stopped if it exists.
+func (r *OCIRuntime) updateContainerStatus(ctr *Container, useRunc bool) error {
+ exitFile := ctr.exitFilePath()
runtimeDir, err := util.GetRootlessRuntimeDir()
if err != nil {
return err
}
+ // If not using runc, we don't need to do most of this.
+ if !useRunc {
+ // If the container's not running, nothing to do.
+ if ctr.state.State != ContainerStateRunning {
+ return nil
+ }
+
+ // Check for the exit file conmon makes
+ info, err := os.Stat(exitFile)
+ if err != nil {
+ if os.IsNotExist(err) {
+ // Container is still running, no error
+ return nil
+ }
+
+ return errors.Wrapf(err, "error running stat on container %s exit file", ctr.ID())
+ }
+
+ // Alright, it exists. Transition to Stopped state.
+ ctr.state.State = ContainerStateStopped
+
+ // Read the exit file to get our stopped time and exit code.
+ return ctr.handleExitFile(exitFile, info)
+ }
+
// Store old state so we know if we were already stopped
oldState := ctr.state.State
+ state := new(spec.State)
+
cmd := exec.Command(r.path, "state", ctr.ID())
cmd.Env = append(cmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir))
outPipe, err := cmd.StdoutPipe()
@@ -480,6 +509,8 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container) error {
}
if strings.Contains(string(out), "does not exist") {
ctr.removeConmonFiles()
+ ctr.state.ExitCode = -1
+ ctr.state.FinishedTime = time.Now()
ctr.state.State = ContainerStateExited
return nil
}
@@ -514,7 +545,6 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container) error {
// Only grab exit status if we were not already stopped
// If we were, it should already be in the database
if ctr.state.State == ContainerStateStopped && oldState != ContainerStateStopped {
- exitFile := filepath.Join(r.exitsDir, ctr.ID())
var fi os.FileInfo
err = kwait.ExponentialBackoff(
kwait.Backoff{
@@ -538,24 +568,7 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container) error {
return nil
}
- ctr.state.FinishedTime = ctime.Created(fi)
- statusCodeStr, err := ioutil.ReadFile(exitFile)
- if err != nil {
- return errors.Wrapf(err, "failed to read exit file for container %s", ctr.ID())
- }
- statusCode, err := strconv.Atoi(string(statusCodeStr))
- if err != nil {
- return errors.Wrapf(err, "error converting exit status code for container %s to int",
- ctr.ID())
- }
- ctr.state.ExitCode = int32(statusCode)
-
- oomFilePath := filepath.Join(ctr.bundlePath(), "oom")
- if _, err = os.Stat(oomFilePath); err == nil {
- ctr.state.OOMKilled = true
- }
-
- ctr.state.Exited = true
+ return ctr.handleExitFile(exitFile, fi)
}
return nil
@@ -601,6 +614,8 @@ func (r *OCIRuntime) killContainer(ctr *Container, signal uint) error {
// Does not set finished time for container, assumes you will run updateStatus
// after to pull the exit code
func (r *OCIRuntime) stopContainer(ctr *Container, timeout uint) error {
+ logrus.Debugf("Stopping container %s (PID %d)", ctr.ID(), ctr.state.PID)
+
// Ping the container to see if it's alive
// If it's not, it's already stopped, return
err := unix.Kill(ctr.state.PID, 0)
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index b63726f29..09dc7c48b 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -256,7 +256,7 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool)
}
// Need to update container state to make sure we know it's stopped
- if err := c.syncContainer(); err != nil {
+ if err := c.waitForExitFileAndSync(); err != nil {
return err
}
} else if !(c.state.State == ContainerStateConfigured ||
diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go
index eb3d471dd..3d6fad52f 100644
--- a/libpod/runtime_pod_linux.go
+++ b/libpod/runtime_pod_linux.go
@@ -265,15 +265,26 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool)
}
case CgroupfsCgroupsManager:
// Delete the cgroupfs cgroup
+ // Make sure the conmon cgroup is deleted first
+ // Since the pod is almost gone, don't bother failing
+ // hard - instead, just log errors.
v1CGroups := GetV1CGroups(getExcludedCGroups())
+ conmonCgroupPath := filepath.Join(p.state.CgroupPath, "conmon")
+ conmonCgroup, err := cgroups.Load(v1CGroups, cgroups.StaticPath(conmonCgroupPath))
+ if err != nil && err != cgroups.ErrCgroupDeleted {
+ return err
+ }
+ if err == nil {
+ if err := conmonCgroup.Delete(); err != nil {
+ logrus.Errorf("Error deleting pod %s conmon cgroup %s: %v", p.ID(), conmonCgroupPath, err)
+ }
+ }
cgroup, err := cgroups.Load(v1CGroups, cgroups.StaticPath(p.state.CgroupPath))
if err != nil && err != cgroups.ErrCgroupDeleted {
return err
- } else if err == nil {
+ }
+ if err == nil {
if err := cgroup.Delete(); err != nil {
- // The pod is already almost gone.
- // No point in hard-failing if we fail
- // this bit of cleanup.
logrus.Errorf("Error deleting pod %s cgroup %s: %v", p.ID(), p.state.CgroupPath, err)
}
}
diff --git a/test/e2e/run_dns_test.go b/test/e2e/run_dns_test.go
index c5a02c776..674a57aeb 100644
--- a/test/e2e/run_dns_test.go
+++ b/test/e2e/run_dns_test.go
@@ -1,9 +1,10 @@
package integration
import (
+ "fmt"
"os"
+ "strings"
- "fmt"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
@@ -82,5 +83,23 @@ var _ = Describe("Podman run dns", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(Equal("foobar"))
+
+ session = podmanTest.Podman([]string{"run", "-d", "--hostname=foobar", ALPINE, "cat", "/etc/hosts"})
+ session.WaitWithDefaultTimeout()
+ cid := session.OutputToString()
+ session = podmanTest.Podman([]string{"start", "-ia", cid})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session.LineInOutputContains("foobar")
+ line := strings.Split(session.OutputToStringArray()[len(session.OutputToStringArray())-1], "\t")
+ ip1 := line[0]
+
+ session = podmanTest.Podman([]string{"start", "-ia", cid})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session.LineInOutputContains("foobar")
+ line = strings.Split(session.OutputToStringArray()[len(session.OutputToStringArray())-1], "\t")
+ ip2 := line[0]
+ Expect(ip2).To(Not(Equal(ip1)))
})
})