Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 99 additions & 45 deletions internal/cri/nri/nri_api_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"fmt"
"maps"

eventtypes "github.com/containerd/containerd/api/events"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/core/containers"
"github.com/containerd/containerd/v2/internal/cri/annotations"
Expand All @@ -31,6 +32,7 @@ import (
ctrdutil "github.com/containerd/containerd/v2/internal/cri/util"
"github.com/containerd/containerd/v2/pkg/blockio"
cdispec "github.com/containerd/containerd/v2/pkg/cdi"
"github.com/containerd/containerd/v2/pkg/protobuf"
"github.com/containerd/errdefs"
"github.com/containerd/log"
"github.com/containerd/typeurl/v2"
Expand Down Expand Up @@ -141,7 +143,7 @@ func (a *API) RemovePodSandbox(ctx context.Context, criPod *sstore.Sandbox) erro
}

func (a *API) CreateContainer(ctx context.Context, ctrs *containers.Container, spec *runtimespec.Spec) (*api.ContainerAdjustment, error) {
ctr := a.nriContainer(ctrs, spec)
ctr := a.nriContainer(ctrs, withContainerSpec(spec))

criPod, err := a.cri.SandboxStore().Get(ctr.GetPodSandboxID())
if err != nil {
Expand All @@ -161,7 +163,7 @@ func (a *API) PostCreateContainer(ctx context.Context, criPod *sstore.Sandbox, c
}

pod := a.nriPodSandbox(criPod)
ctr := a.nriContainer(criCtr, nil)
ctr := a.nriContainer(criCtr)

err := a.nri.PostCreateContainer(ctx, pod, ctr)

Expand All @@ -174,7 +176,7 @@ func (a *API) StartContainer(ctx context.Context, criPod *sstore.Sandbox, criCtr
}

pod := a.nriPodSandbox(criPod)
ctr := a.nriContainer(criCtr, nil)
ctr := a.nriContainer(criCtr)

err := a.nri.StartContainer(ctx, pod, ctr)

Expand All @@ -187,7 +189,7 @@ func (a *API) PostStartContainer(ctx context.Context, criPod *sstore.Sandbox, cr
}

pod := a.nriPodSandbox(criPod)
ctr := a.nriContainer(criCtr, nil)
ctr := a.nriContainer(criCtr)

err := a.nri.PostStartContainer(ctx, pod, ctr)

Expand All @@ -202,7 +204,7 @@ func (a *API) UpdateContainerResources(ctx context.Context, criPod *sstore.Sandb
const noOomAdj = 0

pod := a.nriPodSandbox(criPod)
ctr := a.nriContainer(criCtr, nil)
ctr := a.nriContainer(criCtr)

r, err := a.nri.UpdateContainer(ctx, pod, ctr, fromCRILinuxResources(req))
if err != nil {
Expand All @@ -218,7 +220,7 @@ func (a *API) PostUpdateContainerResources(ctx context.Context, criPod *sstore.S
}

pod := a.nriPodSandbox(criPod)
ctr := a.nriContainer(criCtr, nil)
ctr := a.nriContainer(criCtr)

err := a.nri.PostUpdateContainer(ctx, pod, ctr)

Expand All @@ -230,7 +232,7 @@ func (a *API) StopContainer(ctx context.Context, criPod *sstore.Sandbox, criCtr
return nil
}

ctr := a.nriContainer(criCtr, nil)
ctr := a.nriContainer(criCtr)

if criPod == nil || criPod.ID == "" {
criPod = &sstore.Sandbox{
Expand All @@ -246,12 +248,12 @@ func (a *API) StopContainer(ctx context.Context, criPod *sstore.Sandbox, criCtr
return err
}

func (a *API) NotifyContainerExit(ctx context.Context, criCtr *cstore.Container) {
func (a *API) NotifyContainerExit(ctx context.Context, criCtr *cstore.Container, e *eventtypes.TaskExit) {
if a.IsDisabled() {
return
}

ctr := a.nriContainer(criCtr, nil)
ctr := a.nriContainer(criCtr, withExitEvent(e))

criPod, _ := a.cri.SandboxStore().Get(ctr.GetPodSandboxID())
if criPod.ID == "" {
Expand All @@ -272,7 +274,7 @@ func (a *API) RemoveContainer(ctx context.Context, criPod *sstore.Sandbox, criCt
}

pod := a.nriPodSandbox(criPod)
ctr := a.nriContainer(criCtr, nil)
ctr := a.nriContainer(criCtr)

err := a.nri.RemoveContainer(ctx, pod, ctr)

Expand All @@ -285,7 +287,7 @@ func (a *API) UndoCreateContainer(ctx context.Context, criPod *sstore.Sandbox, i
}

pod := a.nriPodSandbox(criPod)
ctr := a.nriContainer(&containers.Container{ID: id}, spec)
ctr := a.nriContainer(&containers.Container{ID: id}, withContainerSpec(spec))

err := a.nri.StopContainer(ctx, pod, ctr)
if err != nil {
Expand Down Expand Up @@ -381,15 +383,15 @@ func (a *API) WithContainerAdjustment() containerd.NewContainerOpts {
}
}

func (a *API) WithContainerExit(criCtr *cstore.Container) containerd.ProcessDeleteOpts {
func (a *API) WithContainerExit(criCtr *cstore.Container, e *eventtypes.TaskExit) containerd.ProcessDeleteOpts {
if a.IsDisabled() {
return func(_ context.Context, _ containerd.Process) error {
return nil
}
}

return func(_ context.Context, _ containerd.Process) error {
a.NotifyContainerExit(context.Background(), criCtr)
a.NotifyContainerExit(context.Background(), criCtr, e)
return nil
}
}
Expand Down Expand Up @@ -439,7 +441,7 @@ func (a *API) ListContainers() []nri.Container {
case cri.ContainerState_CONTAINER_UNKNOWN:
continue
}
containers = append(containers, a.nriContainer(&ctr, nil))
containers = append(containers, a.nriContainer(&ctr))
}
return containers
}
Expand All @@ -459,7 +461,7 @@ func (a *API) GetContainer(id string) (nri.Container, bool) {
return nil, false
}

return a.nriContainer(&ctr, nil), true
return a.nriContainer(&ctr), true
}

func (a *API) UpdateContainer(ctx context.Context, u *api.ContainerUpdate) error {
Expand Down Expand Up @@ -694,15 +696,36 @@ func (p *criPodSandbox) GetIPs() []string {
// NRI integration wrapper for CRI Containers
//

type criContainerOption func(*criContainer)

func withContainerSpec(spec *runtimespec.Spec) criContainerOption {
return func(c *criContainer) {
c.spec = spec
}
}

func withExitEvent(e *eventtypes.TaskExit) criContainerOption {
return func(c *criContainer) {
c.exit = e
}
}

type criContainer struct {
api *API
ctrs *containers.Container
spec *runtimespec.Spec
meta *cstore.Metadata
pid uint32
exit *eventtypes.TaskExit
}

func (a *API) nriContainer(ctr interface{}, spec *runtimespec.Spec) *criContainer {
func (a *API) nriContainer(ctr interface{}, opts ...criContainerOption) *criContainer {
criCtr := &criContainer{}
for _, o := range opts {
o(criCtr)
}
criCtr.api = a

switch c := ctr.(type) {
case *cstore.Container:
ctx := ctrdutil.NamespacedContext()
Expand All @@ -726,13 +749,12 @@ func (a *API) nriContainer(ctr interface{}, spec *runtimespec.Spec) *criContaine
pid = task.Pid()
}

return &criContainer{
api: a,
ctrs: &ctrs,
meta: &c.Metadata,
spec: spec,
pid: pid,
}
criCtr.ctrs = &ctrs
criCtr.meta = &c.Metadata
criCtr.spec = spec
criCtr.pid = pid

return criCtr

case *containers.Container:
ctrs := c
Expand All @@ -744,20 +766,17 @@ func (a *API) nriContainer(ctr interface{}, spec *runtimespec.Spec) *criContaine
}
}

return &criContainer{
api: a,
ctrs: ctrs,
meta: meta,
spec: spec,
}
criCtr.ctrs = ctrs
criCtr.meta = meta

return criCtr
}

log.L.Errorf("can't wrap %T as NRI container", ctr)
return &criContainer{
api: a,
meta: &cstore.Metadata{},
spec: &runtimespec.Spec{},
}
criCtr.meta = &cstore.Metadata{}
criCtr.spec = &runtimespec.Spec{}

return criCtr
}

func (c *criContainer) GetDomain() string {
Expand All @@ -779,21 +798,56 @@ func (c *criContainer) GetName() string {
return c.spec.Annotations[annotations.ContainerName]
}

func (c *criContainer) GetState() api.ContainerState {
func (c *criContainer) GetStatus() *nri.ContainerStatus {
const (
// completedExitReason is the exit reason when container exits with 0.
completedExitReason = "Completed"
// errorExitReason is the exit reason when container exits with non-zero.
errorExitReason = "Error"
)

status := &nri.ContainerStatus{
State: api.ContainerState_CONTAINER_UNKNOWN,
}

criCtr, err := c.api.cri.ContainerStore().Get(c.GetID())
if err != nil {
return api.ContainerState_CONTAINER_UNKNOWN
if err == nil {
s := criCtr.Status.Get()
switch s.State() {
case cri.ContainerState_CONTAINER_CREATED:
status.State = api.ContainerState_CONTAINER_CREATED
case cri.ContainerState_CONTAINER_RUNNING:
status.State = api.ContainerState_CONTAINER_RUNNING
case cri.ContainerState_CONTAINER_EXITED:
if s.ExitCode == 0 {
status.Reason = completedExitReason
} else {
status.Reason = errorExitReason
}
}

if status.Reason == "" {
status.Reason = s.Reason
}
status.Message = s.Message
status.Pid = s.Pid
status.CreatedAt = s.CreatedAt
status.StartedAt = s.StartedAt
status.FinishedAt = s.FinishedAt
status.ExitCode = s.ExitCode

if status.Pid == 0 { // in StartContainer we don't have the PID in status yet
status.Pid = c.pid
}
}
switch criCtr.Status.Get().State() {
case cri.ContainerState_CONTAINER_CREATED:
return api.ContainerState_CONTAINER_CREATED
case cri.ContainerState_CONTAINER_RUNNING:
return api.ContainerState_CONTAINER_RUNNING
case cri.ContainerState_CONTAINER_EXITED:
return api.ContainerState_CONTAINER_STOPPED

if c.exit != nil { // when handling an exit event use the exit info
status.State = api.ContainerState_CONTAINER_STOPPED
status.FinishedAt = protobuf.FromTimestamp(c.exit.ExitedAt).UnixNano()
status.ExitCode = int32(c.exit.ExitStatus)
}

return api.ContainerState_CONTAINER_UNKNOWN
return status
}

func (c *criContainer) GetLabels() map[string]string {
Expand Down
3 changes: 2 additions & 1 deletion internal/cri/nri/nri_api_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package nri
import (
"context"

eventtypes "github.com/containerd/containerd/api/events"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/core/containers"
cstore "github.com/containerd/containerd/v2/internal/cri/store/container"
Expand Down Expand Up @@ -110,7 +111,7 @@ func (*API) WithContainerAdjustment() containerd.NewContainerOpts {
}
}

func (*API) WithContainerExit(*cstore.Container) containerd.ProcessDeleteOpts {
func (*API) WithContainerExit(*cstore.Container, *eventtypes.TaskExit) containerd.ProcessDeleteOpts {
return func(_ context.Context, _ containerd.Process) error {
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion internal/cri/server/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ func (c *criService) handleContainerExit(ctx context.Context, e *eventtypes.Task
}

// TODO(random-liu): [P1] This may block the loop, we may want to spawn a worker
if _, err = task.Delete(ctx, c.nri.WithContainerExit(&cntr), containerd.WithProcessKill); err != nil {
if _, err = task.Delete(ctx, c.nri.WithContainerExit(&cntr, e), containerd.WithProcessKill); err != nil {
if !errdefs.IsNotFound(err) {
return fmt.Errorf("failed to stop container: %w", err)
}
Expand Down
24 changes: 19 additions & 5 deletions internal/nri/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,32 @@ import (
nri "github.com/containerd/nri/pkg/adaptation"
)

type ContainerStatus struct {
State nri.ContainerState
Reason string
Message string
Pid uint32
CreatedAt int64
StartedAt int64
FinishedAt int64
ExitCode int32
}

// Container interface for interacting with NRI.
type Container interface {
GetDomain() string

GetPodSandboxID() string
GetID() string
GetName() string
GetState() nri.ContainerState
GetStatus() *ContainerStatus
GetLabels() map[string]string
GetAnnotations() map[string]string
GetArgs() []string
GetEnv() []string
GetMounts() []*nri.Mount
GetHooks() *nri.Hooks
GetLinuxContainer() LinuxContainer

GetPid() uint32
GetCDIDevices() []*nri.CDIDevice
}

Expand All @@ -55,19 +64,24 @@ type LinuxContainer interface {
}

func commonContainerToNRI(ctr Container) *nri.Container {
status := ctr.GetStatus()
return &nri.Container{
Id: ctr.GetID(),
PodSandboxId: ctr.GetPodSandboxID(),
Name: ctr.GetName(),
State: ctr.GetState(),
State: status.State,
Labels: ctr.GetLabels(),
Annotations: ctr.GetAnnotations(),
Args: ctr.GetArgs(),
Env: ctr.GetEnv(),
Mounts: ctr.GetMounts(),
Hooks: ctr.GetHooks(),
Pid: ctr.GetPid(),
CDIDevices: ctr.GetCDIDevices(),
Pid: status.Pid,
CreatedAt: status.CreatedAt,
StartedAt: status.StartedAt,
FinishedAt: status.FinishedAt,
ExitCode: status.ExitCode,
}
}

Expand Down
Loading