188 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// +build windows
 | 
						|
 | 
						|
package windows
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/containerd/containerd"
 | 
						|
	"github.com/containerd/containerd/log"
 | 
						|
	"github.com/containerd/containerd/plugin"
 | 
						|
	"github.com/containerd/containerd/windows/hcs"
 | 
						|
	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")
 | 
						|
)
 | 
						|
 | 
						|
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)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	containers := make([]*container, 0)
 | 
						|
	for _, c := range hCtr {
 | 
						|
		containers = append(containers, &container{
 | 
						|
			ctr:       c,
 | 
						|
			status:    plugin.RunningStatus,
 | 
						|
			sendEvent: sendEvent,
 | 
						|
		})
 | 
						|
	}
 | 
						|
 | 
						|
	return containers, nil
 | 
						|
}
 | 
						|
 | 
						|
func newContainer(ctx context.Context, h *hcs.HCS, id string, spec RuntimeSpec, io plugin.IO, sendEvent eventCallback) (*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, containerd.CreateEvent, hcsCtr.Pid(), 0, time.Time{})
 | 
						|
 | 
						|
	return &container{
 | 
						|
		ctr:       hcsCtr,
 | 
						|
		status:    plugin.CreatedStatus,
 | 
						|
		sendEvent: sendEvent,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
type container struct {
 | 
						|
	sync.Mutex
 | 
						|
 | 
						|
	ctr       *hcs.Container
 | 
						|
	status    plugin.Status
 | 
						|
	sendEvent eventCallback
 | 
						|
}
 | 
						|
 | 
						|
func (c *container) Info() plugin.ContainerInfo {
 | 
						|
	return plugin.ContainerInfo{
 | 
						|
		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(plugin.RunningStatus)
 | 
						|
	c.sendEvent(c.ctr.ID(), containerd.StartEvent, c.ctr.Pid(), 0, time.Time{})
 | 
						|
 | 
						|
	// Wait for our process to terminate
 | 
						|
	go func() {
 | 
						|
		ec, err := c.ctr.ExitCode()
 | 
						|
		if err != nil {
 | 
						|
			log.G(ctx).Debug(err)
 | 
						|
		}
 | 
						|
		c.setStatus(plugin.StoppedStatus)
 | 
						|
		c.sendEvent(c.ctr.ID(), containerd.ExitEvent, 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) (plugin.State, error) {
 | 
						|
	return c, 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) Exec(ctx context.Context, opts plugin.ExecOpts) (plugin.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, &procSpec); err != nil {
 | 
						|
		return nil, errors.Wrap(err, "failed to unmarshal oci spec")
 | 
						|
	}
 | 
						|
 | 
						|
	p, err := c.ctr.AddProcess(ctx, procSpec, pio)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	go func() {
 | 
						|
		ec, err := p.ExitCode()
 | 
						|
		if err != nil {
 | 
						|
			log.G(ctx).Debug(err)
 | 
						|
		}
 | 
						|
		c.sendEvent(c.ctr.ID(), containerd.ExitEvent, p.Pid(), ec, p.ExitedAt())
 | 
						|
	}()
 | 
						|
 | 
						|
	return &process{p}, nil
 | 
						|
}
 | 
						|
 | 
						|
func (c *container) CloseStdin(ctx context.Context, pid uint32) error {
 | 
						|
	return c.ctr.CloseStdin(ctx, pid)
 | 
						|
}
 | 
						|
 | 
						|
func (c *container) Pty(ctx context.Context, pid uint32, size plugin.ConsoleSize) error {
 | 
						|
	return c.ctr.Pty(ctx, pid, size)
 | 
						|
}
 | 
						|
 | 
						|
func (c *container) Status() plugin.Status {
 | 
						|
	return c.getStatus()
 | 
						|
}
 | 
						|
 | 
						|
func (c *container) Pid() uint32 {
 | 
						|
	return c.ctr.Pid()
 | 
						|
}
 | 
						|
 | 
						|
func (c *container) setStatus(status plugin.Status) {
 | 
						|
	c.Lock()
 | 
						|
	c.status = status
 | 
						|
	c.Unlock()
 | 
						|
}
 | 
						|
 | 
						|
func (c *container) getStatus() plugin.Status {
 | 
						|
	c.Lock()
 | 
						|
	defer c.Unlock()
 | 
						|
	return c.status
 | 
						|
}
 |