Refactor checkpoint information

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2015-12-04 14:00:07 -08:00
parent e21364603e
commit 9eb08b8971
10 changed files with 85 additions and 44 deletions

View file

@ -26,7 +26,7 @@ func NewServer(supervisor *containerd.Supervisor) http.Handler {
// checkpoint and restore handlers // checkpoint and restore handlers
// TODO: PUT handler for adding a checkpoint to containerd?? // TODO: PUT handler for adding a checkpoint to containerd??
r.HandleFunc("/containers/{id:.*}/checkpoint/{name:.*}", s.createCheckpoint).Methods("POST") r.HandleFunc("/containers/{id:.*}/checkpoint/{name:.*}", s.createCheckpoint).Methods("POST")
// r.HandleFunc("/containers/{id:.*}/checkpoint/{cid:.*}", s.deleteCheckpoint).Methods("DELETE") r.HandleFunc("/containers/{id:.*}/checkpoint/{name:.*}", s.deleteCheckpoint).Methods("DELETE")
r.HandleFunc("/containers/{id:.*}/checkpoint", s.listCheckpoints).Methods("GET") r.HandleFunc("/containers/{id:.*}/checkpoint", s.listCheckpoints).Methods("GET")
// container handlers // container handlers
@ -263,7 +263,6 @@ func (s *server) createContainer(w http.ResponseWriter, r *http.Request) {
if c.Checkpoint != nil { if c.Checkpoint != nil {
e.Checkpoint = &runtime.Checkpoint{ e.Checkpoint = &runtime.Checkpoint{
Name: c.Checkpoint.Name, Name: c.Checkpoint.Name,
Path: c.Checkpoint.Path,
} }
} }
e.Stdio = &runtime.Stdio{ e.Stdio = &runtime.Stdio{
@ -309,13 +308,17 @@ func (s *server) listCheckpoints(w http.ResponseWriter, r *http.Request) {
out := []Checkpoint{} out := []Checkpoint{}
for _, c := range checkpoints { for _, c := range checkpoints {
out = append(out, Checkpoint{ out = append(out, Checkpoint{
Path: c.Path,
Name: c.Name, Name: c.Name,
Tcp: c.Tcp, Tcp: c.Tcp,
Shell: c.Shell, Shell: c.Shell,
UnixSockets: c.UnixSockets, UnixSockets: c.UnixSockets,
}) })
} }
if err := json.NewEncoder(w).Encode(out); err != nil {
logrus.WithField("error", err).Error("encode checkpoints")
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
} }
func (s *server) createCheckpoint(w http.ResponseWriter, r *http.Request) { func (s *server) createCheckpoint(w http.ResponseWriter, r *http.Request) {
@ -337,7 +340,6 @@ func (s *server) createCheckpoint(w http.ResponseWriter, r *http.Request) {
e.ID = id e.ID = id
e.Checkpoint = &runtime.Checkpoint{ e.Checkpoint = &runtime.Checkpoint{
Name: name, Name: name,
Path: cp.Path,
Exit: cp.Exit, Exit: cp.Exit,
Tcp: cp.Tcp, Tcp: cp.Tcp,
UnixSockets: cp.UnixSockets, UnixSockets: cp.UnixSockets,
@ -352,4 +354,30 @@ func (s *server) createCheckpoint(w http.ResponseWriter, r *http.Request) {
} }
func (s *server) deleteCheckpoint(w http.ResponseWriter, r *http.Request) { func (s *server) deleteCheckpoint(w http.ResponseWriter, r *http.Request) {
var (
vars = mux.Vars(r)
id = vars["id"]
name = vars["name"]
)
if name == "" {
http.Error(w, "checkpoint name cannot be empty", http.StatusBadRequest)
return
}
var cp Checkpoint
if r.ContentLength > 0 {
if err := json.NewDecoder(r.Body).Decode(&cp); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}
e := containerd.NewEvent(containerd.DeleteCheckpointEventType)
e.ID = id
e.Checkpoint = &runtime.Checkpoint{
Name: name,
}
s.supervisor.SendEvent(e)
if err := <-e.Err; err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
} }

View file

@ -58,7 +58,6 @@ type Event struct {
type Checkpoint struct { type Checkpoint struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Path string `json:"path"`
Exit bool `json:"exit,omitempty"` Exit bool `json:"exit,omitempty"`
Tcp bool `json:"tcp"` Tcp bool `json:"tcp"`
UnixSockets bool `json:"unixSockets"` UnixSockets bool `json:"unixSockets"`

View file

@ -11,3 +11,15 @@ func (h *CreateCheckpointEvent) Handle(e *Event) error {
} }
return container.Checkpoint(*e.Checkpoint) return container.Checkpoint(*e.Checkpoint)
} }
type DeleteCheckpointEvent struct {
s *Supervisor
}
func (h *DeleteCheckpointEvent) Handle(e *Event) error {
container, ok := h.s.containers[e.ID]
if !ok {
return ErrContainerNotFound
}
return container.DeleteCheckpoint(e.Checkpoint.Name)
}

View file

@ -20,6 +20,7 @@ const (
AddProcessEventType EventType = "addProcess" AddProcessEventType EventType = "addProcess"
UpdateContainerEventType EventType = "updateContainer" UpdateContainerEventType EventType = "updateContainer"
CreateCheckpointEventType EventType = "createCheckpoint" CreateCheckpointEventType EventType = "createCheckpoint"
DeleteCheckpointEventType EventType = "deleteCheckpoint"
) )
func NewEvent(t EventType) *Event { func NewEvent(t EventType) *Event {

View file

@ -6,6 +6,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
goruntime "runtime" goruntime "runtime"
@ -191,19 +192,39 @@ type libcontainerContainer struct {
exitStatus int exitStatus int
exited bool exited bool
path string path string
checkpoints map[string]runtime.Checkpoint
} }
func (c *libcontainerContainer) Checkpoints() ([]runtime.Checkpoint, error) { func (c *libcontainerContainer) Checkpoints() ([]runtime.Checkpoint, error) {
out := []runtime.Checkpoint{} out := []runtime.Checkpoint{}
for _, cp := range c.checkpoints { files, err := ioutil.ReadDir(c.getCheckpointPath(""))
out = append(out, cp) if err != nil {
return nil, err
}
for _, fi := range files {
out = append(out, runtime.Checkpoint{
Name: fi.Name(),
})
} }
return out, nil return out, nil
} }
func (c *libcontainerContainer) DeleteCheckpoint(name string) error {
path := c.getCheckpointPath(name)
if err := os.RemoveAll(path); err != nil {
if os.IsNotExist(err) {
return runtime.ErrCheckpointNotExists
}
return err
}
return nil
}
func (c *libcontainerContainer) getCheckpointPath(name string) string {
return filepath.Join(c.path, "checkpoints", name)
}
func (c *libcontainerContainer) Checkpoint(cp runtime.Checkpoint) error { func (c *libcontainerContainer) Checkpoint(cp runtime.Checkpoint) error {
opts := c.createCheckpointOpts(&cp) opts := c.createCheckpointOpts(cp)
if err := os.MkdirAll(opts.ImagesDirectory, 0755); err != nil { if err := os.MkdirAll(opts.ImagesDirectory, 0755); err != nil {
return err return err
} }
@ -211,37 +232,24 @@ func (c *libcontainerContainer) Checkpoint(cp runtime.Checkpoint) error {
return err return err
} }
cp.Timestamp = time.Now() cp.Timestamp = time.Now()
c.checkpoints[cp.Name] = cp
return nil return nil
} }
func (c *libcontainerContainer) createCheckpointOpts(cp *runtime.Checkpoint) *libcontainer.CriuOpts { func (c *libcontainerContainer) createCheckpointOpts(cp runtime.Checkpoint) *libcontainer.CriuOpts {
opts := libcontainer.CriuOpts{} opts := libcontainer.CriuOpts{}
opts.LeaveRunning = !cp.Exit opts.LeaveRunning = !cp.Exit
opts.ShellJob = cp.Shell opts.ShellJob = cp.Shell
opts.TcpEstablished = cp.Tcp opts.TcpEstablished = cp.Tcp
opts.ExternalUnixConnections = cp.UnixSockets opts.ExternalUnixConnections = cp.UnixSockets
if cp.Path == "" { opts.ImagesDirectory = c.getCheckpointPath(cp.Name)
cp.Path = filepath.Join(c.path, "checkpoints", cp.Name)
}
opts.ImagesDirectory = cp.Path
return &opts return &opts
} }
func (c *libcontainerContainer) Restore(path, name string) error { func (c *libcontainerContainer) Restore(name string) error {
if path == "" { path := c.getCheckpointPath(name)
path = filepath.Join(c.path, "checkpoints", name)
}
var opts libcontainer.CriuOpts var opts libcontainer.CriuOpts
if cp, ok := c.checkpoints[name]; ok { opts.ImagesDirectory = path
opts = *c.createCheckpointOpts(&cp) return c.c.Restore(c.initProcess.process, &opts)
} else {
opts.ImagesDirectory = path
}
if err := c.c.Restore(c.initProcess.process, &opts); err != nil {
return err
}
return nil
} }
func (c *libcontainerContainer) Resume() error { func (c *libcontainerContainer) Resume() error {
@ -352,7 +360,6 @@ func (r *libcontainerRuntime) Create(id, bundlePath string, stdio *runtime.Stdio
c := &libcontainerContainer{ c := &libcontainerContainer{
c: container, c: container,
additionalProcesses: make(map[int]*libcontainerProcess), additionalProcesses: make(map[int]*libcontainerProcess),
checkpoints: make(map[string]runtime.Checkpoint),
initProcess: &libcontainerProcess{ initProcess: &libcontainerProcess{
process: process, process: process,
spec: spec.Process, spec: spec.Process,

View file

@ -32,8 +32,6 @@ type Stdio struct {
type Checkpoint struct { type Checkpoint struct {
// Timestamp is the time that checkpoint happened // Timestamp is the time that checkpoint happened
Timestamp time.Time `json:"timestamp,omitempty"` Timestamp time.Time `json:"timestamp,omitempty"`
// Path is the custom path to the checkpoint, this is optional
Path string `json:"path,omitempty"`
// Name is the name of the checkpoint // Name is the name of the checkpoint
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
// Tcp checkpoints open tcp connections // Tcp checkpoints open tcp connections
@ -74,5 +72,7 @@ type Container interface {
Checkpoint(Checkpoint) error Checkpoint(Checkpoint) error
Restore(path, name string) error DeleteCheckpoint(name string) error
Restore(name string) error
} }

View file

@ -9,6 +9,7 @@ import (
var ( var (
ErrNotChildProcess = errors.New("containerd: not a child process for container") ErrNotChildProcess = errors.New("containerd: not a child process for container")
ErrInvalidContainerType = errors.New("containerd: invalid container type for runtime") ErrInvalidContainerType = errors.New("containerd: invalid container type for runtime")
ErrCheckpointNotExists = errors.New("containerd: checkpoint does not exist for container")
) )
// runtime handles containers, containers handle their own actions. // runtime handles containers, containers handle their own actions.

View file

@ -16,10 +16,7 @@ func (h *StartEvent) Handle(e *Event) error {
Container: container, Container: container,
} }
if e.Checkpoint != nil { if e.Checkpoint != nil {
task.Checkpoint = &Checkpoint{ task.Checkpoint = e.Checkpoint.Name
Name: e.Checkpoint.Name,
Path: e.Checkpoint.Path,
}
} }
h.s.tasks <- task h.s.tasks <- task
return errDeferedResponse return errDeferedResponse

View file

@ -52,6 +52,7 @@ func NewSupervisor(stateDir string, tasks chan *StartTask) (*Supervisor, error)
AddProcessEventType: &AddProcessEvent{s}, AddProcessEventType: &AddProcessEvent{s},
UpdateContainerEventType: &UpdateEvent{s}, UpdateContainerEventType: &UpdateEvent{s},
CreateCheckpointEventType: &CreateCheckpointEvent{s}, CreateCheckpointEventType: &CreateCheckpointEvent{s},
DeleteCheckpointEventType: &DeleteCheckpointEvent{s},
} }
// start the container workers for concurrent container starts // start the container workers for concurrent container starts
return s, nil return s, nil

View file

@ -11,14 +11,9 @@ type Worker interface {
Start() Start()
} }
type Checkpoint struct {
Path string
Name string
}
type StartTask struct { type StartTask struct {
Container runtime.Container Container runtime.Container
Checkpoint *Checkpoint Checkpoint string
Err chan error Err chan error
} }
@ -38,8 +33,8 @@ func (w *worker) Start() {
defer w.wg.Done() defer w.wg.Done()
for t := range w.s.tasks { for t := range w.s.tasks {
started := time.Now() started := time.Now()
if t.Checkpoint != nil { if t.Checkpoint != "" {
if err := t.Container.Restore(t.Checkpoint.Path, t.Checkpoint.Name); err != nil { if err := t.Container.Restore(t.Checkpoint); err != nil {
evt := NewEvent(DeleteEventType) evt := NewEvent(DeleteEventType)
evt.ID = t.Container.ID() evt.ID = t.Container.ID()
w.s.SendEvent(evt) w.s.SendEvent(evt)