containerd/windows/container.go
Michael Crosby 2b6d790ff4 Refactor runtime events into Task* types
This removes the RuntimeEvent super proto with enums into separate
runtime event protos to be inline with the other events that are output
by containerd.

This also renames the runtime events into Task* events.

Fixes #1071

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
2017-07-12 10:57:57 -07:00

238 lines
5.3 KiB
Go

// +build windows
package windows
import (
"context"
"encoding/json"
"fmt"
"sync"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/windows/hcs"
"github.com/gogo/protobuf/types"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
winsys "golang.org/x/sys/windows"
)
var ErrLoadedContainer = errors.New("loaded container can only be terminated")
func loadContainers(ctx context.Context, h *hcs.HCS) ([]*container, error) {
hCtr, err := h.LoadContainers(ctx)
if err != nil {
return nil, err
}
containers := make([]*container, 0)
for _, c := range hCtr {
containers = append(containers, &container{
ctr: c,
status: runtime.RunningStatus,
})
}
return containers, nil
}
func newContainer(ctx context.Context, h *hcs.HCS, id string, spec *RuntimeSpec, io runtime.IO) (*container, error) {
cio, err := hcs.NewIO(io.Stdin, io.Stdout, io.Stderr, io.Terminal)
if err != nil {
return nil, err
}
hcsCtr, err := h.CreateContainer(ctx, id, spec.OCISpec, spec.Configuration, cio)
if err != nil {
return nil, err
}
//sendEvent(id, events.RuntimeEvent_CREATE, hcsCtr.Pid(), 0, time.Time{})
return &container{
ctr: hcsCtr,
status: runtime.CreatedStatus,
}, nil
}
type container struct {
sync.Mutex
ctr *hcs.Container
status runtime.Status
}
func (c *container) ID() string {
return c.ctr.ID()
}
func (c *container) Info() runtime.TaskInfo {
return runtime.TaskInfo{
ID: c.ctr.ID(),
Runtime: runtimeName,
}
}
func (c *container) Start(ctx context.Context) error {
if c.ctr.Pid() == 0 {
return ErrLoadedContainer
}
err := c.ctr.Start(ctx)
if err != nil {
return err
}
c.setStatus(runtime.RunningStatus)
// c.sendEvent(c.ctr.ID(), events.RuntimeEvent_START, c.ctr.Pid(), 0, time.Time{})
// Wait for our process to terminate
go func() {
_, err := c.ctr.ExitCode()
if err != nil {
log.G(ctx).Debug(err)
}
c.setStatus(runtime.StoppedStatus)
// c.sendEvent(c.ctr.ID(), events.RuntimeEvent_EXIT, c.ctr.Pid(), ec, c.ctr.Processes()[0].ExitedAt())
}()
return nil
}
func (c *container) Pause(ctx context.Context) error {
if c.ctr.GetConfiguration().UseHyperV == false {
return fmt.Errorf("Windows non-HyperV containers do not support pause")
}
return c.ctr.Pause()
}
func (c *container) Resume(ctx context.Context) error {
if c.ctr.GetConfiguration().UseHyperV == false {
return fmt.Errorf("Windows non-HyperV containers do not support resume")
}
return c.ctr.Resume()
}
func (c *container) State(ctx context.Context) (runtime.State, error) {
return runtime.State{
Pid: c.Pid(),
Status: c.Status(),
}, nil
}
func (c *container) Kill(ctx context.Context, signal uint32, all bool) error {
if winsys.Signal(signal) == winsys.SIGKILL {
return c.ctr.Kill(ctx)
}
return c.ctr.Stop(ctx)
}
func (c *container) Process(ctx context.Context, id string) (runtime.Process, error) {
for _, p := range c.ctr.Processes() {
if p.ID() == id {
return &process{p}, nil
}
}
return nil, errors.Errorf("process %s not found", id)
}
func (c *container) Exec(ctx context.Context, id string, opts runtime.ExecOpts) (runtime.Process, error) {
if c.ctr.Pid() == 0 {
return nil, ErrLoadedContainer
}
pio, err := hcs.NewIO(opts.IO.Stdin, opts.IO.Stdout, opts.IO.Stderr, opts.IO.Terminal)
if err != nil {
return nil, err
}
var procSpec specs.Process
if err := json.Unmarshal(opts.Spec.Value, &procSpec); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal oci spec")
}
p, err := c.ctr.AddProcess(ctx, id, &procSpec, pio)
if err != nil {
return nil, err
}
go func() {
_, err := p.ExitCode()
if err != nil {
log.G(ctx).Debug(err)
}
//c.sendEvent(c.ctr.ID(), events.RuntimeEvent_EXEC_ADDED, p.Pid(), ec, p.ExitedAt())
}()
return &process{p}, nil
}
func (c *container) CloseIO(ctx context.Context) error {
return c.ctr.CloseIO(ctx)
}
func (c *container) ResizePty(ctx context.Context, size runtime.ConsoleSize) error {
return c.ctr.ResizePty(ctx, size)
}
func (c *container) Status() runtime.Status {
return c.getStatus()
}
func (c *container) Pid() uint32 {
return c.ctr.Pid()
}
func (c *container) Pids(ctx context.Context) ([]uint32, error) {
pl, err := c.ctr.ProcessList()
if err != nil {
return nil, err
}
pids := make([]uint32, 0, len(pl))
for _, p := range pl {
pids = append(pids, p.ProcessId)
}
return pids, nil
}
func (c *container) Checkpoint(ctx context.Context, _ string, _ *types.Any) error {
return fmt.Errorf("Windows containers do not support checkpoint")
}
func (c *container) DeleteProcess(ctx context.Context, id string) (*runtime.Exit, error) {
var process *hcs.Process
for _, p := range c.ctr.Processes() {
if p.ID() == id {
process = p
break
}
}
if process == nil {
return nil, fmt.Errorf("process %s not found", id)
}
ec, err := process.ExitCode()
if err != nil {
return nil, err
}
process.Delete()
return &runtime.Exit{
Status: ec,
Timestamp: process.ExitedAt(),
}, nil
}
func (c *container) Update(ctx context.Context, spec *types.Any) error {
return fmt.Errorf("Windows containers do not support update")
}
func (c *container) setStatus(status runtime.Status) {
c.Lock()
c.status = status
c.Unlock()
}
func (c *container) getStatus() runtime.Status {
c.Lock()
defer c.Unlock()
return c.status
}