240 lines
5.4 KiB
Go
240 lines
5.4 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/containerd/containerd/windows/hcsshimopts"
|
|
"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 (r *Runtime) newContainer(ctx context.Context, id string, spec *specs.Spec, createOpts *hcsshimopts.CreateOptions, io runtime.IO) (*container, error) {
|
|
cio, err := hcs.NewIO(io.Stdin, io.Stdout, io.Stderr, io.Terminal)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ctr, err := r.hcs.CreateContainer(ctx, id, spec, createOpts.TerminateDuration, cio)
|
|
if err != nil {
|
|
cio.Close()
|
|
return nil, err
|
|
}
|
|
//sendEvent(id, events.RuntimeEvent_CREATE, hcsCtr.Pid(), 0, time.Time{})
|
|
|
|
return &container{
|
|
ctr: ctr,
|
|
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.IsHyperV() {
|
|
return c.ctr.Pause()
|
|
}
|
|
return errors.New("Windows non-HyperV containers do not support pause")
|
|
}
|
|
|
|
func (c *container) Resume(ctx context.Context) error {
|
|
if c.ctr.IsHyperV() == false {
|
|
return errors.New("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
|
|
}
|