Containerd client integration
This commit: 1) Replaces the usage of containerd GRPC APIs with the containerd client for all operations related to containerd. 2) Updated containerd to v1.0alpha4+ 3) Updated runc to v1.0.0 Signed-off-by: Abhinandan Prativadi <abhi@docker.com>
This commit is contained in:
@@ -17,15 +17,13 @@ limitations under the License.
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/api/services/tasks/v1"
|
||||
"github.com/containerd/containerd/api/types"
|
||||
"github.com/containerd/containerd/api/types/task"
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||
@@ -54,7 +52,7 @@ func (c *criContainerdService) StartContainer(ctx context.Context, r *runtime.St
|
||||
if err := container.Status.Update(func(status containerstore.Status) (containerstore.Status, error) {
|
||||
// Always apply status change no matter startContainer fails or not. Because startContainer
|
||||
// may change container state no matter it fails or succeeds.
|
||||
startErr = c.startContainer(ctx, id, container.Metadata, &status)
|
||||
startErr = c.startContainer(ctx, container.Container, container.Metadata, &status)
|
||||
return status, nil
|
||||
}); startErr != nil {
|
||||
return nil, startErr
|
||||
@@ -66,13 +64,17 @@ func (c *criContainerdService) StartContainer(ctx context.Context, r *runtime.St
|
||||
|
||||
// startContainer actually starts the container. The function needs to be run in one transaction. Any updates
|
||||
// to the status passed in will be applied no matter the function returns error or not.
|
||||
func (c *criContainerdService) startContainer(ctx context.Context, id string, meta containerstore.Metadata, status *containerstore.Status) (retErr error) {
|
||||
func (c *criContainerdService) startContainer(ctx context.Context,
|
||||
container containerd.Container,
|
||||
meta containerstore.Metadata,
|
||||
status *containerstore.Status) (retErr error) {
|
||||
config := meta.Config
|
||||
id := container.ID()
|
||||
|
||||
// Return error if container is not in created state.
|
||||
if status.State() != runtime.ContainerState_CONTAINER_CREATED {
|
||||
return fmt.Errorf("container %q is in %s state", id, criContainerStateToString(status.State()))
|
||||
}
|
||||
|
||||
// Do not start the container when there is a removal in progress.
|
||||
if status.Removing {
|
||||
return fmt.Errorf("container %q is in removing state", id)
|
||||
@@ -97,102 +99,65 @@ func (c *criContainerdService) startContainer(ctx context.Context, id string, me
|
||||
sandboxConfig := sandbox.Config
|
||||
sandboxID := meta.SandboxID
|
||||
// Make sure sandbox is running.
|
||||
sandboxInfo, err := c.taskService.Get(ctx, &tasks.GetTaskRequest{ContainerID: sandboxID})
|
||||
s, err := sandbox.Container.Task(ctx, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get sandbox container %q info: %v", sandboxID, err)
|
||||
}
|
||||
// This is only a best effort check, sandbox may still exit after this. If sandbox fails
|
||||
// before starting the container, the start will fail.
|
||||
if sandboxInfo.Task.Status != task.StatusRunning {
|
||||
taskStatus, err := s.Status(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get task status for sandbox container %q: %v", id, err)
|
||||
}
|
||||
|
||||
if taskStatus.Status != containerd.Running {
|
||||
return fmt.Errorf("sandbox container %q is not running", sandboxID)
|
||||
}
|
||||
|
||||
containerRootDir := getContainerRootDir(c.rootDir, id)
|
||||
stdin, stdout, stderr := getStreamingPipes(containerRootDir)
|
||||
// Set stdin to empty if Stdin == false.
|
||||
if !config.GetStdin() {
|
||||
stdin = ""
|
||||
}
|
||||
stdinPipe, stdoutPipe, stderrPipe, err := c.prepareStreamingPipes(ctx, stdin, stdout, stderr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to prepare streaming pipes: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
if stdinPipe != nil {
|
||||
stdinPipe.Close()
|
||||
}
|
||||
stdoutPipe.Close()
|
||||
stderrPipe.Close()
|
||||
}
|
||||
}()
|
||||
// Redirect the stream to std for now.
|
||||
// TODO(random-liu): [P1] Support StdinOnce after container logging is added.
|
||||
if stdinPipe != nil {
|
||||
go func(w io.WriteCloser) {
|
||||
io.Copy(w, os.Stdin) // nolint: errcheck
|
||||
w.Close()
|
||||
}(stdinPipe)
|
||||
}
|
||||
rStdoutPipe, wStdoutPipe := io.Pipe()
|
||||
rStderrPipe, wStderrPipe := io.Pipe()
|
||||
stdin := new(bytes.Buffer)
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
rStdoutPipe.Close()
|
||||
rStderrPipe.Close()
|
||||
}
|
||||
}()
|
||||
if config.GetLogPath() != "" {
|
||||
// Only generate container log when log path is specified.
|
||||
logPath := filepath.Join(sandboxConfig.GetLogDirectory(), config.GetLogPath())
|
||||
if err = c.agentFactory.NewContainerLogger(logPath, agents.Stdout, stdoutPipe).Start(); err != nil {
|
||||
if err = c.agentFactory.NewContainerLogger(logPath, agents.Stdout, rStdoutPipe).Start(); err != nil {
|
||||
return fmt.Errorf("failed to start container stdout logger: %v", err)
|
||||
}
|
||||
// Only redirect stderr when there is no tty.
|
||||
if !config.GetTty() {
|
||||
if err = c.agentFactory.NewContainerLogger(logPath, agents.Stderr, stderrPipe).Start(); err != nil {
|
||||
if err = c.agentFactory.NewContainerLogger(logPath, agents.Stderr, rStderrPipe).Start(); err != nil {
|
||||
return fmt.Errorf("failed to start container stderr logger: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get rootfs mounts.
|
||||
rootfsMounts, err := c.snapshotService.Mounts(ctx, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get rootfs mounts %q: %v", id, err)
|
||||
}
|
||||
var rootfs []*types.Mount
|
||||
for _, m := range rootfsMounts {
|
||||
rootfs = append(rootfs, &types.Mount{
|
||||
Type: m.Type,
|
||||
Source: m.Source,
|
||||
Options: m.Options,
|
||||
})
|
||||
}
|
||||
|
||||
// Create containerd task.
|
||||
createOpts := &tasks.CreateTaskRequest{
|
||||
ContainerID: id,
|
||||
Rootfs: rootfs,
|
||||
Stdin: stdin,
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
Terminal: config.GetTty(),
|
||||
}
|
||||
glog.V(5).Infof("Create containerd task (id=%q, name=%q) with options %+v.",
|
||||
id, meta.Name, createOpts)
|
||||
createResp, err := c.taskService.Create(ctx, createOpts)
|
||||
//TODO(Abhi): close stdin/pass a managed IOCreation
|
||||
task, err := container.NewTask(ctx, containerd.NewIO(stdin, wStdoutPipe, wStderrPipe))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create containerd task: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
// Cleanup the containerd task if an error is returned.
|
||||
if _, err := c.taskService.Delete(ctx, &tasks.DeleteTaskRequest{ContainerID: id}); err != nil {
|
||||
if _, err := task.Delete(ctx, containerd.WithProcessKill); err != nil {
|
||||
glog.Errorf("Failed to delete containerd task %q: %v", id, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Start containerd task.
|
||||
if _, err := c.taskService.Start(ctx, &tasks.StartTaskRequest{ContainerID: id}); err != nil {
|
||||
if err := task.Start(ctx); err != nil {
|
||||
return fmt.Errorf("failed to start containerd task %q: %v", id, err)
|
||||
}
|
||||
|
||||
// Update container start timestamp.
|
||||
status.Pid = createResp.Pid
|
||||
status.Pid = task.Pid()
|
||||
status.StartedAt = time.Now().UnixNano()
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user