diff --git a/containerd-shim-v2/container.go b/containerd-shim-v2/container.go index 86a90d3edf..e56a4bce9c 100644 --- a/containerd-shim-v2/container.go +++ b/containerd-shim-v2/container.go @@ -10,6 +10,9 @@ import ( "time" "github.com/containerd/containerd/api/types/task" + "github.com/containerd/containerd/errdefs" + taskAPI "github.com/containerd/containerd/runtime/v2/task" + vc "github.com/kata-containers/runtime/virtcontainers" "github.com/kata-containers/runtime/virtcontainers/pkg/oci" ) @@ -33,3 +36,32 @@ type container struct { status task.Status terminal bool } + +func newContainer(s *service, r *taskAPI.CreateTaskRequest, containerType vc.ContainerType, spec *oci.CompatOCISpec) (*container, error) { + if r == nil { + return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, " CreateTaskRequest points to nil") + } + + // in order to avoid deferencing a nil pointer in test + if spec == nil { + spec = &oci.CompatOCISpec{} + } + + c := &container{ + s: s, + spec: spec, + id: r.ID, + bundle: r.Bundle, + stdin: r.Stdin, + stdout: r.Stdout, + stderr: r.Stderr, + terminal: r.Terminal, + cType: containerType, + execs: make(map[string]*exec), + status: task.StatusCreated, + exitIOch: make(chan struct{}), + exitCh: make(chan uint32, 1), + time: time.Now(), + } + return c, nil +} diff --git a/containerd-shim-v2/create.go b/containerd-shim-v2/create.go new file mode 100644 index 0000000000..3a483b419a --- /dev/null +++ b/containerd-shim-v2/create.go @@ -0,0 +1,102 @@ +// Copyright (c) 2014,2015,2016 Docker, Inc. +// Copyright (c) 2017 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "context" + "fmt" + + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + + taskAPI "github.com/containerd/containerd/runtime/v2/task" + + "github.com/kata-containers/runtime/pkg/katautils" + "github.com/opencontainers/runtime-spec/specs-go" +) + +func create(ctx context.Context, s *service, r *taskAPI.CreateTaskRequest, netns string, + runtimeConfig *oci.RuntimeConfig) (*container, error) { + + detach := !r.Terminal + + // Checks the MUST and MUST NOT from OCI runtime specification + bundlePath, err := validBundle(r.ID, r.Bundle) + if err != nil { + return nil, err + } + + ociSpec, err := oci.ParseConfigJSON(bundlePath) + if err != nil { + return nil, err + } + + containerType, err := ociSpec.ContainerType() + if err != nil { + return nil, err + } + + // Todo: + // Since there is a bug in kata for sharedPidNs, here to + // remove the pidns to disable the sharePidNs temporarily, + // once kata fixed this issue, we can remove this line. + // For the bug, please see: + // https://github.com/kata-containers/runtime/issues/930 + removeNamespace(&ociSpec, specs.PIDNamespace) + + //set the network namespace path + //this set will be applied to sandbox's + //network config and has nothing to + //do with containers in the sandbox since + //networkNamespace has been ignored by + //kata-agent in sandbox. + + for _, n := range ociSpec.Linux.Namespaces { + if n.Type != specs.NetworkNamespace { + continue + } + + if n.Path == "" { + n.Path = netns + } + } + + katautils.HandleFactory(ctx, vci, runtimeConfig) + + disableOutput := noNeedForOutput(detach, ociSpec.Process.Terminal) + + switch containerType { + case vc.PodSandbox: + if s.sandbox != nil { + return nil, fmt.Errorf("cannot create another sandbox in sandbox: %s", s.sandbox.ID()) + } + + sandbox, _, err := katautils.CreateSandbox(ctx, vci, ociSpec, *runtimeConfig, r.ID, bundlePath, "", disableOutput, false, true) + if err != nil { + return nil, err + } + s.sandbox = sandbox + + case vc.PodContainer: + if s.sandbox == nil { + return nil, fmt.Errorf("BUG: Cannot start the container, since the sandbox hasn't been created") + } + + _, err = katautils.CreateContainer(ctx, vci, s.sandbox, ociSpec, r.ID, bundlePath, "", disableOutput, true) + if err != nil { + return nil, err + } + } + + container, err := newContainer(s, r, containerType, &ociSpec) + if err != nil { + return nil, err + } + + return container, nil +} diff --git a/containerd-shim-v2/service.go b/containerd-shim-v2/service.go index c2fbf30ede..0dc6fa6dfe 100644 --- a/containerd-shim-v2/service.go +++ b/containerd-shim-v2/service.go @@ -8,6 +8,7 @@ import ( "context" "os" sysexec "os/exec" + "path/filepath" "sync" "syscall" "time" @@ -15,6 +16,7 @@ import ( eventstypes "github.com/containerd/containerd/api/events" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/events" + "github.com/containerd/containerd/mount" "github.com/containerd/containerd/namespaces" cdruntime "github.com/containerd/containerd/runtime" cdshim "github.com/containerd/containerd/runtime/v2/shim" @@ -23,7 +25,9 @@ import ( vc "github.com/kata-containers/runtime/virtcontainers" "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/containerd/containerd/api/types/task" ptypes "github.com/gogo/protobuf/types" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -235,7 +239,46 @@ func (s *service) Cleanup(ctx context.Context) (*taskAPI.DeleteResponse, error) // Create a new sandbox or container with the underlying OCI runtime func (s *service) Create(ctx context.Context, r *taskAPI.CreateTaskRequest) (_ *taskAPI.CreateTaskResponse, err error) { - return nil, errdefs.ErrNotImplemented + s.Lock() + defer s.Unlock() + + //the network namespace created by cni plugin + netns, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return nil, errors.Wrap(err, "create namespace") + } + + rootfs := filepath.Join(r.Bundle, "rootfs") + defer func() { + if err != nil { + if err2 := mount.UnmountAll(rootfs, 0); err2 != nil { + logrus.WithError(err2).Warn("failed to cleanup rootfs mount") + } + } + }() + for _, rm := range r.Rootfs { + m := &mount.Mount{ + Type: rm.Type, + Source: rm.Source, + Options: rm.Options, + } + if err := m.Mount(rootfs); err != nil { + return nil, errors.Wrapf(err, "failed to mount rootfs component %v", m) + } + } + + container, err := create(ctx, s, r, netns, s.config) + if err != nil { + return nil, err + } + + container.status = task.StatusCreated + + s.containers[r.ID] = container + + return &taskAPI.CreateTaskResponse{ + Pid: s.pid, + }, nil } // Start a process diff --git a/containerd-shim-v2/utils.go b/containerd-shim-v2/utils.go index c59c07d26e..959db7d28f 100644 --- a/containerd-shim-v2/utils.go +++ b/containerd-shim-v2/utils.go @@ -15,9 +15,10 @@ import ( "github.com/kata-containers/runtime/pkg/katautils" vc "github.com/kata-containers/runtime/virtcontainers" "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/opencontainers/runtime-spec/specs-go" ) -func validCreateParams(containerID, bundlePath string) (string, error) { +func validBundle(containerID, bundlePath string) (string, error) { // container ID MUST be provided. if containerID == "" { return "", fmt.Errorf("Missing container ID") @@ -49,7 +50,7 @@ func getAddress(ctx context.Context, bundlePath, id string) (string, error) { var err error // Checks the MUST and MUST NOT from OCI runtime specification - if bundlePath, err = validCreateParams(id, bundlePath); err != nil { + if bundlePath, err = validBundle(id, bundlePath); err != nil { return "", err } @@ -77,3 +78,24 @@ func getAddress(ctx context.Context, bundlePath, id string) (string, error) { return "", nil } + +func noNeedForOutput(detach bool, tty bool) bool { + if !detach { + return false + } + + if !tty { + return false + } + + return true +} + +func removeNamespace(s *oci.CompatOCISpec, nsType specs.LinuxNamespaceType) { + for i, n := range s.Linux.Namespaces { + if n.Type == nsType { + s.Linux.Namespaces = append(s.Linux.Namespaces[:i], s.Linux.Namespaces[i+1:]...) + return + } + } +}