containerd/windows/runtime.go
Michael Crosby 7cc1b64bd8 Add checkpoint and restore
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>

Update go-runc to 49b2a02ec1ed3e4ae52d30b54a291b75

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>

Add shim to restore creation

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>

Keep checkpoint path in service

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>

Add C/R to non-shim build

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>

Checkpoint rw and image

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>

Pause container on bind checkpoints

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>

Return dump.log in error on checkpoint failure

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>

Pause container for checkpoint

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>

Update runc to 639454475cb9c8b861cc599f8bcd5c8c790ae402

For checkpoint into to work you need runc version
639454475cb9c8b861cc599f8bcd5c8c790ae402 + and criu 3.0 as this is what
I have been testing with.

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>

Move restore behind create calls

This remove the restore RPCs in favor of providing the checkpoint
information to the `Create` calls of a container.  If provided, the
container will be created/restored from the checkpoint instead of an
existing container.

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>

Regen protos after rebase

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
2017-05-22 15:34:45 -07:00

174 lines
3.8 KiB
Go

// +build windows
package windows
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"sync"
"time"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/windows/hcs"
"github.com/containerd/containerd/windows/pid"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
const (
runtimeName = "windows"
owner = "containerd"
)
var _ = (plugin.Runtime)(&Runtime{})
func init() {
plugin.Register(runtimeName, &plugin.Registration{
Type: plugin.RuntimePlugin,
Init: New,
})
}
func New(ic *plugin.InitContext) (interface{}, error) {
rootDir := filepath.Join(ic.Root, runtimeName)
if err := os.MkdirAll(rootDir, 0755); err != nil {
return nil, errors.Wrapf(err, "could not create state directory at %s", rootDir)
}
c, cancel := context.WithCancel(ic.Context)
r := &Runtime{
pidPool: pid.NewPool(),
containers: make(map[string]*container),
events: make(chan *plugin.Event, 2048),
eventsContext: c,
eventsCancel: cancel,
rootDir: rootDir,
hcs: hcs.New(owner, rootDir),
}
// Terminate all previous container that we may have started. We don't
// support restoring containers
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(), plugin.ExitEvent, c.ctr.Pid(), 255, time.Time{})
}
// Try to delete the old state dir and recreate it
stateDir := filepath.Join(ic.State, runtimeName)
if err := os.RemoveAll(stateDir); err != nil {
log.G(c).WithError(err).Warnf("failed to cleanup old state directory at %s", stateDir)
}
if err := os.MkdirAll(stateDir, 0755); err != nil {
return nil, errors.Wrapf(err, "could not create state directory at %s", stateDir)
}
r.stateDir = stateDir
return r, nil
}
type Runtime struct {
sync.Mutex
rootDir string
stateDir string
pidPool *pid.Pool
hcs *hcs.HCS
containers map[string]*container
events chan *plugin.Event
eventsContext context.Context
eventsCancel func()
}
type RuntimeSpec struct {
// Spec is the OCI spec
OCISpec specs.Spec
// HCS specific options
hcs.Configuration
}
func (r *Runtime) Create(ctx context.Context, id string, opts plugin.CreateOpts) (plugin.Container, error) {
var rtSpec RuntimeSpec
if err := json.Unmarshal(opts.Spec, &rtSpec); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal oci spec")
}
ctr, err := newContainer(ctx, r.hcs, id, rtSpec, opts.IO, r.sendEvent)
if err != nil {
return nil, err
}
r.Lock()
r.containers[id] = ctr
r.Unlock()
return ctr, nil
}
func (r *Runtime) Delete(ctx context.Context, c plugin.Container) (*plugin.Exit, error) {
wc, ok := c.(*container)
if !ok {
return nil, fmt.Errorf("container cannot be cast as *windows.container")
}
ec, err := wc.ctr.ExitCode()
if err != nil {
log.G(ctx).WithError(err).Errorf("failed to retrieve exit code for container %s", wc.ctr.ID())
}
wc.ctr.Delete(ctx)
r.Lock()
delete(r.containers, wc.ctr.ID())
r.Unlock()
return &plugin.Exit{
Status: ec,
Timestamp: wc.ctr.Processes()[0].ExitedAt(),
}, nil
}
func (r *Runtime) Containers(ctx context.Context) ([]plugin.Container, error) {
r.Lock()
defer r.Unlock()
list := make([]plugin.Container, len(r.containers))
for _, c := range r.containers {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
list = append(list, c)
}
}
return list, nil
}
func (r *Runtime) Events(ctx context.Context) <-chan *plugin.Event {
return r.events
}
func (r *Runtime) sendEvent(id string, evType plugin.EventType, pid, exitStatus uint32, exitedAt time.Time) {
r.events <- &plugin.Event{
Timestamp: time.Now(),
Runtime: runtimeName,
Type: evType,
Pid: pid,
ID: id,
ExitStatus: exitStatus,
ExitedAt: exitedAt,
}
}