summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container_api.go109
-rw-r--r--libpod/container_internal.go6
-rw-r--r--libpod/define/errors.go4
-rw-r--r--libpod/kube.go21
-rw-r--r--libpod/networking_linux.go2
5 files changed, 108 insertions, 34 deletions
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 951227a4f..2473acec0 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -5,6 +5,7 @@ import (
"io/ioutil"
"net/http"
"os"
+ "sync"
"time"
"github.com/containers/podman/v2/libpod/define"
@@ -478,13 +479,13 @@ func (c *Container) RemoveArtifact(name string) error {
}
// Wait blocks until the container exits and returns its exit code.
-func (c *Container) Wait() (int32, error) {
- return c.WaitWithInterval(DefaultWaitInterval)
+func (c *Container) Wait(ctx context.Context) (int32, error) {
+ return c.WaitWithInterval(ctx, DefaultWaitInterval)
}
// WaitWithInterval blocks until the container to exit and returns its exit
// code. The argument is the interval at which checks the container's status.
-func (c *Container) WaitWithInterval(waitTimeout time.Duration) (int32, error) {
+func (c *Container) WaitWithInterval(ctx context.Context, waitTimeout time.Duration) (int32, error) {
if !c.valid {
return -1, define.ErrCtrRemoved
}
@@ -495,41 +496,111 @@ func (c *Container) WaitWithInterval(waitTimeout time.Duration) (int32, error) {
}
chWait := make(chan error, 1)
- defer close(chWait)
+ go func() {
+ <-ctx.Done()
+ chWait <- define.ErrCanceled
+ }()
for {
- // ignore errors here, it is only used to avoid waiting
+ // ignore errors here (with exception of cancellation), it is only used to avoid waiting
// too long.
- _, _ = WaitForFile(exitFile, chWait, waitTimeout)
+ _, e := WaitForFile(exitFile, chWait, waitTimeout)
+ if e == define.ErrCanceled {
+ return -1, define.ErrCanceled
+ }
- stopped, err := c.isStopped()
+ stopped, code, err := c.isStopped()
if err != nil {
return -1, err
}
if stopped {
- return c.state.ExitCode, nil
+ return code, nil
}
}
}
-func (c *Container) WaitForConditionWithInterval(waitTimeout time.Duration, condition define.ContainerStatus) (int32, error) {
+type waitResult struct {
+ code int32
+ err error
+}
+
+func (c *Container) WaitForConditionWithInterval(ctx context.Context, waitTimeout time.Duration, conditions ...define.ContainerStatus) (int32, error) {
if !c.valid {
return -1, define.ErrCtrRemoved
}
- if condition == define.ContainerStateStopped || condition == define.ContainerStateExited {
- return c.WaitWithInterval(waitTimeout)
+
+ if len(conditions) == 0 {
+ panic("at least one condition should be passed")
}
- for {
- state, err := c.State()
- if err != nil {
- return -1, err
+
+ ctx, cancelFn := context.WithCancel(ctx)
+ defer cancelFn()
+
+ resultChan := make(chan waitResult)
+ waitForExit := false
+ wantedStates := make(map[define.ContainerStatus]bool, len(conditions))
+
+ for _, condition := range conditions {
+ if condition == define.ContainerStateStopped || condition == define.ContainerStateExited {
+ waitForExit = true
+ continue
}
- if state == condition {
- break
+ wantedStates[condition] = true
+ }
+
+ trySend := func(code int32, err error) {
+ select {
+ case resultChan <- waitResult{code, err}:
+ case <-ctx.Done():
}
- time.Sleep(waitTimeout)
}
- return -1, nil
+
+ var wg sync.WaitGroup
+
+ if waitForExit {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+
+ code, err := c.WaitWithInterval(ctx, waitTimeout)
+ trySend(code, err)
+ }()
+ }
+
+ if len(wantedStates) > 0 {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+
+ for {
+ state, err := c.State()
+ if err != nil {
+ trySend(-1, err)
+ return
+ }
+ if _, found := wantedStates[state]; found {
+ trySend(-1, nil)
+ return
+ }
+ select {
+ case <-ctx.Done():
+ return
+ case <-time.After(waitTimeout):
+ continue
+ }
+ }
+ }()
+ }
+
+ var result waitResult
+ select {
+ case result = <-resultChan:
+ cancelFn()
+ case <-ctx.Done():
+ result = waitResult{-1, define.ErrCanceled}
+ }
+ wg.Wait()
+ return result.code, result.err
}
// Cleanup unmounts all mount points in container and cleans up container storage
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index b9ea50783..5a61f7fe6 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -754,17 +754,17 @@ func (c *Container) getArtifactPath(name string) string {
}
// Used with Wait() to determine if a container has exited
-func (c *Container) isStopped() (bool, error) {
+func (c *Container) isStopped() (bool, int32, error) {
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()
}
err := c.syncContainer()
if err != nil {
- return true, err
+ return true, -1, err
}
- return !c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused, define.ContainerStateStopping), nil
+ return !c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused, define.ContainerStateStopping), c.state.ExitCode, nil
}
// save container state to the database
diff --git a/libpod/define/errors.go b/libpod/define/errors.go
index d37bc397e..2e85454b2 100644
--- a/libpod/define/errors.go
+++ b/libpod/define/errors.go
@@ -198,4 +198,8 @@ var (
// ErrSecurityAttribute indicates that an error processing security attributes
// for the container
ErrSecurityAttribute = fmt.Errorf("%w: unable to process security attribute", ErrOCIRuntime)
+
+ // ErrCanceled indicates that an operation has been cancelled by a user.
+ // Useful for potentially long running tasks.
+ ErrCanceled = errors.New("cancelled by user")
)
diff --git a/libpod/kube.go b/libpod/kube.go
index bf314b9a3..f9ead027d 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -353,22 +353,21 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, *v1.PodDNS
return kubeContainer, kubeVolumes, nil, err
}
- containerCommands := c.Command()
- kubeContainer.Name = removeUnderscores(c.Name())
+ // Handle command and arguments.
+ if ep := c.Entrypoint(); len(ep) > 0 {
+ // If we have an entrypoint, set the container's command as
+ // arguments.
+ kubeContainer.Command = ep
+ kubeContainer.Args = c.Command()
+ } else {
+ kubeContainer.Command = c.Command()
+ }
+ kubeContainer.Name = removeUnderscores(c.Name())
_, image := c.Image()
kubeContainer.Image = image
kubeContainer.Stdin = c.Stdin()
- // prepend the entrypoint of the container to command
- if ep := c.Entrypoint(); len(c.Entrypoint()) > 0 {
- ep = append(ep, containerCommands...)
- containerCommands = ep
- }
- kubeContainer.Command = containerCommands
- // TODO need to figure out how we handle command vs entry point. Kube appears to prefer entrypoint.
- // right now we just take the container's command
- //container.Args = args
kubeContainer.WorkingDir = c.WorkingDir()
kubeContainer.Ports = ports
// This should not be applicable
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 01e4102d1..55d338e7d 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -1180,7 +1180,7 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro
// update network status if container is not running
networkStatus := c.state.NetworkStatus
// clip out the index of the network
- tmpNetworkStatus := make([]*cnitypes.Result, len(networkStatus)-1)
+ tmpNetworkStatus := make([]*cnitypes.Result, 0, len(networkStatus)-1)
for k, v := range networkStatus {
if index != k {
tmpNetworkStatus = append(tmpNetworkStatus, v)