Add initial container implementation.
Signed-off-by: Lantao Liu <lantaol@google.com>
This commit is contained in:
@@ -17,14 +17,115 @@ limitations under the License.
|
||||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/containerd/containerd/api/services/execution"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
||||
|
||||
"github.com/kubernetes-incubator/cri-containerd/pkg/metadata"
|
||||
)
|
||||
|
||||
const (
|
||||
// stopCheckPollInterval is the the interval to check whether a container
|
||||
// is stopped successfully.
|
||||
stopCheckPollInterval = 100 * time.Millisecond
|
||||
|
||||
// killContainerTimeout is the timeout that we wait for the container to
|
||||
// be SIGKILLed.
|
||||
killContainerTimeout = 2 * time.Minute
|
||||
)
|
||||
|
||||
// StopContainer stops a running container with a grace period (i.e., timeout).
|
||||
func (c *criContainerdService) StopContainer(ctx context.Context, r *runtime.StopContainerRequest) (*runtime.StopContainerResponse, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
func (c *criContainerdService) StopContainer(ctx context.Context, r *runtime.StopContainerRequest) (retRes *runtime.StopContainerResponse, retErr error) {
|
||||
glog.V(2).Infof("StopContainer for %q with timeout %d (s)", r.GetContainerId(), r.GetTimeout())
|
||||
defer func() {
|
||||
if retErr == nil {
|
||||
glog.V(2).Infof("StopContainer %q returns successfully", r.GetContainerId())
|
||||
}
|
||||
}()
|
||||
|
||||
// Get container config from container store.
|
||||
meta, err := c.containerStore.Get(r.GetContainerId())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("an error occurred when try to find container %q: %v", r.GetContainerId(), err)
|
||||
}
|
||||
id := r.GetContainerId()
|
||||
|
||||
// Return without error if container is not running. This makes sure that
|
||||
// stop only takes real action after the container is started.
|
||||
if meta.State() != runtime.ContainerState_CONTAINER_RUNNING {
|
||||
glog.V(2).Infof("Container to stop %q is not running, current state %q",
|
||||
id, criContainerStateToString(meta.State()))
|
||||
return &runtime.StopContainerResponse{}, nil
|
||||
}
|
||||
|
||||
// TODO(random-liu): [P1] Get stop signal from image config.
|
||||
stopSignal := unix.SIGTERM
|
||||
glog.V(2).Infof("Stop container %q with signal %v", id, stopSignal)
|
||||
_, err = c.containerService.Kill(ctx, &execution.KillRequest{ID: id, Signal: uint32(stopSignal)})
|
||||
if err != nil {
|
||||
if isContainerdContainerNotExistError(err) {
|
||||
return &runtime.StopContainerResponse{}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("failed to stop container %q: %v", id, err)
|
||||
}
|
||||
|
||||
err = c.waitContainerStop(id, time.Duration(r.GetTimeout())*time.Second)
|
||||
if err == nil {
|
||||
return &runtime.StopContainerResponse{}, nil
|
||||
}
|
||||
glog.Errorf("Stop container %q timed out: %v", id, err)
|
||||
|
||||
glog.V(2).Infof("Delete container from containerd %q", id)
|
||||
_, err = c.containerService.Delete(ctx, &execution.DeleteRequest{ID: id})
|
||||
if err != nil {
|
||||
if isContainerdContainerNotExistError(err) {
|
||||
return &runtime.StopContainerResponse{}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("failed to delete container %q: %v", id, err)
|
||||
}
|
||||
|
||||
// Wait forever until container stop is observed by event monitor.
|
||||
if err := c.waitContainerStop(id, killContainerTimeout); err != nil {
|
||||
return nil, fmt.Errorf("error occurs during waiting for container %q to stop: %v",
|
||||
id, err)
|
||||
}
|
||||
return &runtime.StopContainerResponse{}, nil
|
||||
}
|
||||
|
||||
// waitContainerStop polls container state until timeout exceeds or container is stopped.
|
||||
func (c *criContainerdService) waitContainerStop(id string, timeout time.Duration) error {
|
||||
ticker := time.NewTicker(stopCheckPollInterval)
|
||||
defer ticker.Stop()
|
||||
timeoutTimer := time.NewTimer(timeout)
|
||||
defer timeoutTimer.Stop()
|
||||
for {
|
||||
// Poll once before waiting for stopCheckPollInterval.
|
||||
meta, err := c.containerStore.Get(id)
|
||||
if err != nil {
|
||||
if !metadata.IsNotExistError(err) {
|
||||
return fmt.Errorf("failed to get container %q metadata: %v", id, err)
|
||||
}
|
||||
// Do not return error here because container was removed means
|
||||
// it is already stopped.
|
||||
glog.Warningf("Container %q was removed during stopping", id)
|
||||
return nil
|
||||
}
|
||||
// TODO(random-liu): Use channel with event handler instead of polling.
|
||||
if meta.State() == runtime.ContainerState_CONTAINER_EXITED {
|
||||
return nil
|
||||
}
|
||||
select {
|
||||
case <-timeoutTimer.C:
|
||||
return fmt.Errorf("wait container %q stop timeout", id)
|
||||
case <-ticker.C:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user