diff --git a/api/types/container/container.proto b/api/types/container/container.proto index f0e714389..7e8985809 100644 --- a/api/types/container/container.proto +++ b/api/types/container/container.proto @@ -52,5 +52,4 @@ message Event { uint32 pid = 3; uint32 exit_status = 4; google.protobuf.Timestamp exited_at = 5 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - } diff --git a/windows/container.go b/windows/container.go index 9e18f5c99..6f7adc520 100644 --- a/windows/container.go +++ b/windows/container.go @@ -5,6 +5,7 @@ package windows import ( "encoding/json" "sync" + "time" "github.com/containerd/containerd" "github.com/containerd/containerd/log" @@ -19,7 +20,7 @@ var ( ErrLoadedContainer = errors.New("loaded container can only be terminated") ) -type eventCallback func(id string, evType containerd.EventType, pid, exitStatus uint32) +type eventCallback func(id string, evType containerd.EventType, pid, exitStatus uint32, exitedAt time.Time) func loadContainers(ctx context.Context, h *hcs.HCS, sendEvent eventCallback) ([]*container, error) { hCtr, err := h.LoadContainers(ctx) @@ -49,7 +50,7 @@ func newContainer(ctx context.Context, h *hcs.HCS, id string, spec RuntimeSpec, if err != nil { return nil, err } - sendEvent(id, containerd.CreateEvent, hcsCtr.Pid(), 0) + sendEvent(id, containerd.CreateEvent, hcsCtr.Pid(), 0, time.Time{}) return &container{ ctr: hcsCtr, @@ -85,7 +86,7 @@ func (c *container) Start(ctx context.Context) error { } c.setStatus(containerd.RunningStatus) - c.sendEvent(c.ctr.ID(), containerd.StartEvent, c.ctr.Pid(), 0) + c.sendEvent(c.ctr.ID(), containerd.StartEvent, c.ctr.Pid(), 0, time.Time{}) // Wait for our process to terminate go func() { @@ -94,7 +95,7 @@ func (c *container) Start(ctx context.Context) error { log.G(ctx).Debug(err) } c.setStatus(containerd.StoppedStatus) - c.sendEvent(c.ctr.ID(), containerd.ExitEvent, c.ctr.Pid(), ec) + c.sendEvent(c.ctr.ID(), containerd.ExitEvent, c.ctr.Pid(), ec, c.ctr.Processes()[0].ExitedAt()) }() return nil @@ -136,7 +137,7 @@ func (c *container) Exec(ctx context.Context, opts containerd.ExecOpts) (contain if err != nil { log.G(ctx).Debug(err) } - c.sendEvent(c.ctr.ID(), containerd.ExitEvent, p.Pid(), ec) + c.sendEvent(c.ctr.ID(), containerd.ExitEvent, p.Pid(), ec, p.ExitedAt()) }() return &process{p}, nil diff --git a/windows/hcs/hcs.go b/windows/hcs/hcs.go index cf1c94621..27794e999 100644 --- a/windows/hcs/hcs.go +++ b/windows/hcs/hcs.go @@ -167,6 +167,10 @@ func (c *Container) Pid() uint32 { return c.pid } +func (c *Container) Processes() []*Process { + return c.processes +} + func (c *Container) Start(ctx context.Context) error { _, err := c.addProcess(ctx, c.spec.Process, c.io) return err diff --git a/windows/hcs/process.go b/windows/hcs/process.go index 4229e6416..05cdc106c 100644 --- a/windows/hcs/process.go +++ b/windows/hcs/process.go @@ -4,6 +4,7 @@ package hcs import ( "syscall" + "time" "github.com/Microsoft/hcsshim" "github.com/containerd/containerd" @@ -13,11 +14,12 @@ import ( type Process struct { hcsshim.Process - pid uint32 - io *IO - ec uint32 - ecErr error - ecSync chan struct{} + pid uint32 + io *IO + ec uint32 + exitedAt time.Time + ecErr error + ecSync chan struct{} } func (p *Process) Pid() uint32 { @@ -29,6 +31,10 @@ func (p *Process) ExitCode() (uint32, error) { return p.ec, p.ecErr } +func (p *Process) ExitedAt() time.Time { + return p.exitedAt +} + func (p *Process) Status() containerd.Status { select { case <-p.ecSync: @@ -60,6 +66,6 @@ func processExitCode(containerID string, p *Process) (uint32, error) { // Well, unknown exit code it is ec = 255 } - + p.exitedAt = time.Now() return uint32(ec), err } diff --git a/windows/runtime.go b/windows/runtime.go index 58751cf28..c00b37f13 100644 --- a/windows/runtime.go +++ b/windows/runtime.go @@ -53,20 +53,16 @@ func New(ic *plugin.InitContext) (interface{}, error) { hcs: hcs.New(owner, rootDir), } - sendEvent := func(id string, evType containerd.EventType, pid, exitStatus uint32) { - r.sendEvent(id, evType, pid, exitStatus) - } - // Terminate all previous container that we may have started. We don't // support restoring containers - ctrs, err := loadContainers(ic.Context, r.hcs, sendEvent) + ctrs, err := loadContainers(ic.Context, r.hcs, r.sendEvent) if err != nil { return nil, err } for _, c := range ctrs { c.ctr.Delete(ic.Context) - r.sendEvent(c.ctr.ID(), containerd.ExitEvent, c.ctr.Pid(), 255) + r.sendEvent(c.ctr.ID(), containerd.ExitEvent, c.ctr.Pid(), 255, time.Time{}) } // Try to delete the old state dir and recreate it @@ -112,11 +108,7 @@ func (r *Runtime) Create(ctx context.Context, id string, opts containerd.CreateO return nil, errors.Wrap(err, "failed to unmarshal oci spec") } - sendEvent := func(id string, evType containerd.EventType, pid, exitStatus uint32) { - r.sendEvent(id, evType, pid, exitStatus) - } - - ctr, err := newContainer(ctx, r.hcs, id, rtSpec, opts.IO, sendEvent) + ctr, err := newContainer(ctx, r.hcs, id, rtSpec, opts.IO, r.sendEvent) if err != nil { return nil, err } @@ -128,10 +120,10 @@ func (r *Runtime) Create(ctx context.Context, id string, opts containerd.CreateO return ctr, nil } -func (r *Runtime) Delete(ctx context.Context, c containerd.Container) (uint32, error) { +func (r *Runtime) Delete(ctx context.Context, c containerd.Container) (*containerd.Exit, error) { wc, ok := c.(*container) if !ok { - return 0, fmt.Errorf("container cannot be cast as *windows.container") + return nil, fmt.Errorf("container cannot be cast as *windows.container") } ec, err := wc.ctr.ExitCode() if err != nil { @@ -144,7 +136,10 @@ func (r *Runtime) Delete(ctx context.Context, c containerd.Container) (uint32, e delete(r.containers, wc.ctr.ID()) r.Unlock() - return ec, err + return &containerd.Exit{ + Status: ec, + Timestamp: wc.ctr.Processes()[0].ExitedAt(), + }, nil } func (r *Runtime) Containers(ctx context.Context) ([]containerd.Container, error) { @@ -167,7 +162,7 @@ func (r *Runtime) Events(ctx context.Context) <-chan *containerd.Event { return r.events } -func (r *Runtime) sendEvent(id string, evType containerd.EventType, pid, exitStatus uint32) { +func (r *Runtime) sendEvent(id string, evType containerd.EventType, pid, exitStatus uint32, exitedAt time.Time) { r.events <- &containerd.Event{ Timestamp: time.Now(), Runtime: runtimeName, @@ -175,5 +170,6 @@ func (r *Runtime) sendEvent(id string, evType containerd.EventType, pid, exitSta Pid: pid, ID: id, ExitStatus: exitStatus, + ExitedAt: exitedAt, } }