aboutsummaryrefslogtreecommitdiff
path: root/libpod/oci.go
diff options
context:
space:
mode:
authorMatthew Heon <matthew.heon@gmail.com>2017-11-23 14:06:43 -0500
committerAtomic Bot <atomic-devel@projectatomic.io>2017-11-29 12:15:15 +0000
commit831e2c30d479a92c203d2caf82106cb85a6cdfc8 (patch)
tree9c3970ed34441fee8990265685fbdf7654c1af51 /libpod/oci.go
parenta1d0d9f5d1d72f3ca0d1d2af36f9542f6f21ff91 (diff)
downloadpodman-831e2c30d479a92c203d2caf82106cb85a6cdfc8.tar.gz
podman-831e2c30d479a92c203d2caf82106cb85a6cdfc8.tar.bz2
podman-831e2c30d479a92c203d2caf82106cb85a6cdfc8.zip
Add ability to kill and stop containers
Also migrates kpod kill and kpod stop to libpod to use the new code Fixes force removing containers, and actually deletes containers in runc when removing them Start is now capable of starting even when the container is unmounted Signed-off-by: Matthew Heon <matthew.heon@gmail.com> Closes: #68 Approved by: rhatdan
Diffstat (limited to 'libpod/oci.go')
-rw-r--r--libpod/oci.go102
1 files changed, 99 insertions, 3 deletions
diff --git a/libpod/oci.go b/libpod/oci.go
index eb8bd0d93..750e94fe7 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -34,6 +34,9 @@ const (
// ContainerCreateTimeout represents the value of container creating timeout
ContainerCreateTimeout = 240 * time.Second
+
+ // Timeout before declaring that runc has failed to kill a given container
+ killContainerTimeout = 5 * time.Second
)
// OCIRuntime represents an OCI-compatible runtime that libpod can call into
@@ -109,10 +112,39 @@ func createUnitName(prefix string, name string) string {
return fmt.Sprintf("%s-%s.scope", prefix, name)
}
+// Wait for a container which has been sent a signal to stop
+func waitContainerStop(ctr *Container, timeout time.Duration) error {
+ done := make(chan struct{})
+ chControl := make(chan struct{})
+ go func() {
+ for {
+ select {
+ case <-chControl:
+ return
+ default:
+ // Check if the process is still around
+ err := unix.Kill(ctr.state.PID, 0)
+ if err == unix.ESRCH {
+ close(done)
+ return
+ }
+ time.Sleep(100 * time.Millisecond)
+ }
+ }
+ }()
+ select {
+ case <-done:
+ return nil
+ case <-time.After(timeout):
+ close(chControl)
+ return errors.Errorf("container %s did not die within timeout", ctr.ID())
+ }
+}
+
// CreateContainer creates a container in the OCI runtime
// TODO terminal support for container
// Presently just ignoring conmon opts related to it
-func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string) error {
+func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string) (err error) {
var stderrBuf bytes.Buffer
parentPipe, childPipe, err := newPipe()
@@ -221,8 +253,13 @@ func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string) error
return err
}
- // TODO should do a defer r.deleteContainer(ctr) here if err != nil
- // Need deleteContainer to be working first, though...
+ defer func() {
+ if err != nil {
+ if err2 := r.deleteContainer(ctr); err2 != nil {
+ logrus.Errorf("Error removing container %s from runc after creation failed", ctr.ID())
+ }
+ }
+ }()
// Wait to get container pid from conmon
type syncStruct struct {
@@ -346,6 +383,65 @@ func (r *OCIRuntime) startContainer(ctr *Container) error {
return nil
}
+// killContainer sends the given signal to the given container
+func (r *OCIRuntime) killContainer(ctr *Container, signal uint) error {
+ if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.path, "kill", ctr.ID(), fmt.Sprintf("%d", signal)); err != nil {
+ return errors.Wrapf(err, "error sending signal to container %s", ctr.ID())
+ }
+
+ return nil
+}
+
+// stopContainer stops a container, first using its given stop signal (or
+// SIGTERM if no signal was specified), then using SIGKILL
+// Timeout is given in seconds. If timeout is 0, the container will be
+// immediately kill with SIGKILL
+// 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 int64) error {
+ // 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)
+ if err == unix.ESRCH {
+ return nil
+ }
+
+ stopSignal := ctr.config.StopSignal
+ if stopSignal == 0 {
+ stopSignal = uint(syscall.SIGTERM)
+ }
+
+ if timeout > 0 {
+ if err := r.killContainer(ctr, stopSignal); err != nil {
+ return err
+ }
+
+ if err := waitContainerStop(ctr, time.Duration(timeout)*time.Second); err != nil {
+ logrus.Warnf("Timed out stopping container %s, resorting to SIGKILL", ctr.ID())
+ } else {
+ // No error, the container is dead
+ return nil
+ }
+ }
+
+ if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.path, "kill", "--all", ctr.ID(), "KILL"); err != nil {
+ return errors.Wrapf(err, "error sending SIGKILL to container %s", ctr.ID())
+ }
+
+ // Give runc a few seconds to make it happen
+ if err := waitContainerStop(ctr, killContainerTimeout); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// deleteContainer deletes a container from runc
+func (r *OCIRuntime) deleteContainer(ctr *Container) error {
+ _, err := utils.ExecCmd(r.path, "delete", "--force", ctr.ID())
+ return err
+}
+
// pauseContainer pauses the given container
func (r *OCIRuntime) pauseContainer(ctr *Container) error {
return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.path, "pause", ctr.ID())