Refactor checkpoint information
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
e21364603e
commit
9eb08b8971
10 changed files with 85 additions and 44 deletions
|
@ -26,7 +26,7 @@ func NewServer(supervisor *containerd.Supervisor) http.Handler {
|
|||
// checkpoint and restore handlers
|
||||
// TODO: PUT handler for adding a checkpoint to containerd??
|
||||
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")
|
||||
|
||||
// container handlers
|
||||
|
@ -263,7 +263,6 @@ func (s *server) createContainer(w http.ResponseWriter, r *http.Request) {
|
|||
if c.Checkpoint != nil {
|
||||
e.Checkpoint = &runtime.Checkpoint{
|
||||
Name: c.Checkpoint.Name,
|
||||
Path: c.Checkpoint.Path,
|
||||
}
|
||||
}
|
||||
e.Stdio = &runtime.Stdio{
|
||||
|
@ -309,13 +308,17 @@ func (s *server) listCheckpoints(w http.ResponseWriter, r *http.Request) {
|
|||
out := []Checkpoint{}
|
||||
for _, c := range checkpoints {
|
||||
out = append(out, Checkpoint{
|
||||
Path: c.Path,
|
||||
Name: c.Name,
|
||||
Tcp: c.Tcp,
|
||||
Shell: c.Shell,
|
||||
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) {
|
||||
|
@ -337,7 +340,6 @@ func (s *server) createCheckpoint(w http.ResponseWriter, r *http.Request) {
|
|||
e.ID = id
|
||||
e.Checkpoint = &runtime.Checkpoint{
|
||||
Name: name,
|
||||
Path: cp.Path,
|
||||
Exit: cp.Exit,
|
||||
Tcp: cp.Tcp,
|
||||
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) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,6 @@ type Event struct {
|
|||
|
||||
type Checkpoint struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Path string `json:"path"`
|
||||
Exit bool `json:"exit,omitempty"`
|
||||
Tcp bool `json:"tcp"`
|
||||
UnixSockets bool `json:"unixSockets"`
|
||||
|
|
|
@ -11,3 +11,15 @@ func (h *CreateCheckpointEvent) Handle(e *Event) error {
|
|||
}
|
||||
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)
|
||||
}
|
||||
|
|
1
event.go
1
event.go
|
@ -20,6 +20,7 @@ const (
|
|||
AddProcessEventType EventType = "addProcess"
|
||||
UpdateContainerEventType EventType = "updateContainer"
|
||||
CreateCheckpointEventType EventType = "createCheckpoint"
|
||||
DeleteCheckpointEventType EventType = "deleteCheckpoint"
|
||||
)
|
||||
|
||||
func NewEvent(t EventType) *Event {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
goruntime "runtime"
|
||||
|
@ -191,19 +192,39 @@ type libcontainerContainer struct {
|
|||
exitStatus int
|
||||
exited bool
|
||||
path string
|
||||
checkpoints map[string]runtime.Checkpoint
|
||||
}
|
||||
|
||||
func (c *libcontainerContainer) Checkpoints() ([]runtime.Checkpoint, error) {
|
||||
out := []runtime.Checkpoint{}
|
||||
for _, cp := range c.checkpoints {
|
||||
out = append(out, cp)
|
||||
files, err := ioutil.ReadDir(c.getCheckpointPath(""))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, fi := range files {
|
||||
out = append(out, runtime.Checkpoint{
|
||||
Name: fi.Name(),
|
||||
})
|
||||
}
|
||||
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 {
|
||||
opts := c.createCheckpointOpts(&cp)
|
||||
opts := c.createCheckpointOpts(cp)
|
||||
if err := os.MkdirAll(opts.ImagesDirectory, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -211,37 +232,24 @@ func (c *libcontainerContainer) Checkpoint(cp runtime.Checkpoint) error {
|
|||
return err
|
||||
}
|
||||
cp.Timestamp = time.Now()
|
||||
c.checkpoints[cp.Name] = cp
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *libcontainerContainer) createCheckpointOpts(cp *runtime.Checkpoint) *libcontainer.CriuOpts {
|
||||
func (c *libcontainerContainer) createCheckpointOpts(cp runtime.Checkpoint) *libcontainer.CriuOpts {
|
||||
opts := libcontainer.CriuOpts{}
|
||||
opts.LeaveRunning = !cp.Exit
|
||||
opts.ShellJob = cp.Shell
|
||||
opts.TcpEstablished = cp.Tcp
|
||||
opts.ExternalUnixConnections = cp.UnixSockets
|
||||
if cp.Path == "" {
|
||||
cp.Path = filepath.Join(c.path, "checkpoints", cp.Name)
|
||||
}
|
||||
opts.ImagesDirectory = cp.Path
|
||||
opts.ImagesDirectory = c.getCheckpointPath(cp.Name)
|
||||
return &opts
|
||||
}
|
||||
|
||||
func (c *libcontainerContainer) Restore(path, name string) error {
|
||||
if path == "" {
|
||||
path = filepath.Join(c.path, "checkpoints", name)
|
||||
}
|
||||
func (c *libcontainerContainer) Restore(name string) error {
|
||||
path := c.getCheckpointPath(name)
|
||||
var opts libcontainer.CriuOpts
|
||||
if cp, ok := c.checkpoints[name]; ok {
|
||||
opts = *c.createCheckpointOpts(&cp)
|
||||
} else {
|
||||
opts.ImagesDirectory = path
|
||||
}
|
||||
if err := c.c.Restore(c.initProcess.process, &opts); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
opts.ImagesDirectory = path
|
||||
return c.c.Restore(c.initProcess.process, &opts)
|
||||
}
|
||||
|
||||
func (c *libcontainerContainer) Resume() error {
|
||||
|
@ -352,7 +360,6 @@ func (r *libcontainerRuntime) Create(id, bundlePath string, stdio *runtime.Stdio
|
|||
c := &libcontainerContainer{
|
||||
c: container,
|
||||
additionalProcesses: make(map[int]*libcontainerProcess),
|
||||
checkpoints: make(map[string]runtime.Checkpoint),
|
||||
initProcess: &libcontainerProcess{
|
||||
process: process,
|
||||
spec: spec.Process,
|
||||
|
|
|
@ -32,8 +32,6 @@ type Stdio struct {
|
|||
type Checkpoint struct {
|
||||
// Timestamp is the time that checkpoint happened
|
||||
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 string `json:"name,omitempty"`
|
||||
// Tcp checkpoints open tcp connections
|
||||
|
@ -74,5 +72,7 @@ type Container interface {
|
|||
|
||||
Checkpoint(Checkpoint) error
|
||||
|
||||
Restore(path, name string) error
|
||||
DeleteCheckpoint(name string) error
|
||||
|
||||
Restore(name string) error
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
var (
|
||||
ErrNotChildProcess = errors.New("containerd: not a child process for container")
|
||||
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.
|
||||
|
|
5
start.go
5
start.go
|
@ -16,10 +16,7 @@ func (h *StartEvent) Handle(e *Event) error {
|
|||
Container: container,
|
||||
}
|
||||
if e.Checkpoint != nil {
|
||||
task.Checkpoint = &Checkpoint{
|
||||
Name: e.Checkpoint.Name,
|
||||
Path: e.Checkpoint.Path,
|
||||
}
|
||||
task.Checkpoint = e.Checkpoint.Name
|
||||
}
|
||||
h.s.tasks <- task
|
||||
return errDeferedResponse
|
||||
|
|
|
@ -52,6 +52,7 @@ func NewSupervisor(stateDir string, tasks chan *StartTask) (*Supervisor, error)
|
|||
AddProcessEventType: &AddProcessEvent{s},
|
||||
UpdateContainerEventType: &UpdateEvent{s},
|
||||
CreateCheckpointEventType: &CreateCheckpointEvent{s},
|
||||
DeleteCheckpointEventType: &DeleteCheckpointEvent{s},
|
||||
}
|
||||
// start the container workers for concurrent container starts
|
||||
return s, nil
|
||||
|
|
11
worker.go
11
worker.go
|
@ -11,14 +11,9 @@ type Worker interface {
|
|||
Start()
|
||||
}
|
||||
|
||||
type Checkpoint struct {
|
||||
Path string
|
||||
Name string
|
||||
}
|
||||
|
||||
type StartTask struct {
|
||||
Container runtime.Container
|
||||
Checkpoint *Checkpoint
|
||||
Checkpoint string
|
||||
Err chan error
|
||||
}
|
||||
|
||||
|
@ -38,8 +33,8 @@ func (w *worker) Start() {
|
|||
defer w.wg.Done()
|
||||
for t := range w.s.tasks {
|
||||
started := time.Now()
|
||||
if t.Checkpoint != nil {
|
||||
if err := t.Container.Restore(t.Checkpoint.Path, t.Checkpoint.Name); err != nil {
|
||||
if t.Checkpoint != "" {
|
||||
if err := t.Container.Restore(t.Checkpoint); err != nil {
|
||||
evt := NewEvent(DeleteEventType)
|
||||
evt.ID = t.Container.ID()
|
||||
w.s.SendEvent(evt)
|
||||
|
|
Loading…
Reference in a new issue