Add journaling

This commit is contained in:
Michael Crosby 2015-11-10 14:24:34 -08:00
parent d34d482a5f
commit 6ff2239019
5 changed files with 115 additions and 86 deletions

View File

@ -49,12 +49,11 @@ func (s *server) signalPid(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
e := &containerd.SignalEvent{
ID: id, e := containerd.NewEvent(containerd.SignalEventType)
Pid: pid, e.ID = id
Signal: syscall.Signal(signal.Signal), e.Pid = pid
Err: make(chan error, 1), e.Signal = syscall.Signal(signal.Signal)
}
s.supervisor.SendEvent(e) s.supervisor.SendEvent(e)
if err := <-e.Err; err != nil { if err := <-e.Err; err != nil {
status := http.StatusInternalServerError status := http.StatusInternalServerError
@ -69,9 +68,7 @@ func (s *server) signalPid(w http.ResponseWriter, r *http.Request) {
func (s *server) containers(w http.ResponseWriter, r *http.Request) { func (s *server) containers(w http.ResponseWriter, r *http.Request) {
var state State var state State
state.Containers = []Container{} state.Containers = []Container{}
e := &containerd.GetContainersEvent{ e := containerd.NewEvent(containerd.GetContainerEventType)
Err: make(chan error, 1),
}
s.supervisor.SendEvent(e) s.supervisor.SendEvent(e)
if err := <-e.Err; err != nil { if err := <-e.Err; err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
@ -112,11 +109,9 @@ func (s *server) createContainer(w http.ResponseWriter, r *http.Request) {
http.Error(w, "empty bundle path", http.StatusBadRequest) http.Error(w, "empty bundle path", http.StatusBadRequest)
return return
} }
e := &containerd.StartContainerEvent{ e := containerd.NewEvent(containerd.StartContainerEventType)
ID: id, e.ID = id
BundlePath: c.BundlePath, e.BundlePath = c.BundlePath
Err: make(chan error, 1),
}
s.supervisor.SendEvent(e) s.supervisor.SendEvent(e)
if err := <-e.Err; err != nil { if err := <-e.Err; err != nil {
code := http.StatusInternalServerError code := http.StatusInternalServerError

View File

@ -54,7 +54,7 @@ func daemon(stateDir string, concurrency, bufferSize int) error {
if err != nil { if err != nil {
return err return err
} }
events := make(chan containerd.Event, bufferSize) events := make(chan *containerd.Event, bufferSize)
// start the signal handler in the background. // start the signal handler in the background.
go startSignalHandler(supervisor, bufferSize) go startSignalHandler(supervisor, bufferSize)
if err := supervisor.Start(events); err != nil { if err := supervisor.Start(events); err != nil {
@ -71,6 +71,7 @@ func startSignalHandler(supervisor *containerd.Supervisor, bufferSize int) {
for s := range signals { for s := range signals {
switch s { switch s {
case syscall.SIGTERM, syscall.SIGINT, syscall.SIGSTOP: case syscall.SIGTERM, syscall.SIGINT, syscall.SIGSTOP:
supervisor.Close()
os.Exit(0) os.Exit(0)
case syscall.SIGCHLD: case syscall.SIGCHLD:
exits, err := reap() exits, err := reap()
@ -84,7 +85,7 @@ func startSignalHandler(supervisor *containerd.Supervisor, bufferSize int) {
} }
} }
func reap() (exits []*containerd.ExitEvent, err error) { func reap() (exits []*containerd.Event, err error) {
var ( var (
ws syscall.WaitStatus ws syscall.WaitStatus
rus syscall.Rusage rus syscall.Rusage
@ -100,9 +101,9 @@ func reap() (exits []*containerd.ExitEvent, err error) {
if pid <= 0 { if pid <= 0 {
return exits, nil return exits, nil
} }
exits = append(exits, &containerd.ExitEvent{ e := containerd.NewEvent(containerd.ExitEventType)
Pid: pid, e.Pid = pid
Status: utils.ExitStatus(ws), e.Status = utils.ExitStatus(ws)
}) exits = append(exits, e)
} }
} }

View File

@ -1,59 +1,36 @@
package containerd package containerd
import "os" import (
"os"
"time"
)
type Event interface { type EventType string
String() string
const (
ExitEventType EventType = "exit"
StartContainerEventType EventType = "startContainer"
ContainerStartErrorEventType EventType = "startContainerError"
GetContainerEventType EventType = "getContainer"
SignalEventType EventType = "signal"
)
func NewEvent(t EventType) *Event {
return &Event{
Type: t,
Timestamp: time.Now(),
Err: make(chan error, 1),
}
} }
type CallbackEvent interface { type Event struct {
Event() Event Type EventType `json:"type"`
Callback() chan Event Timestamp time.Time `json:"timestamp"`
} ID string `json:"id,omitempty"`
BundlePath string `json:"bundlePath,omitempty"`
type ExitEvent struct { Pid int `json:"pid,omitempty"`
Pid int Status int `json:"status,omitempty"`
Status int Signal os.Signal `json:"signal,omitempty"`
} Containers []Container `json:"-"`
Err chan error `json:"-"`
func (e *ExitEvent) String() string {
return "exit event"
}
type StartContainerEvent struct {
ID string
BundlePath string
Err chan error
}
func (c *StartContainerEvent) String() string {
return "create container"
}
type ContainerStartErrorEvent struct {
ID string
}
func (c *ContainerStartErrorEvent) String() string {
return "container start error"
}
type GetContainersEvent struct {
Containers []Container
Err chan error
}
func (c *GetContainersEvent) String() string {
return "get containers"
}
type SignalEvent struct {
ID string
Pid int
Signal os.Signal
Err chan error
}
func (s *SignalEvent) String() string {
return "signal event"
} }

41
journal.go Normal file
View File

@ -0,0 +1,41 @@
package containerd
import (
"encoding/json"
"os"
"path/filepath"
)
type entry struct {
Event *Event `json:"event"`
}
func newJournal(path string) (*journal, error) {
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return nil, err
}
f, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0755)
if err != nil {
return nil, err
}
return &journal{
f: f,
enc: json.NewEncoder(f),
}, nil
}
type journal struct {
f *os.File
enc *json.Encoder
}
func (j *journal) write(e *Event) error {
et := &entry{
Event: e,
}
return j.enc.Encode(et)
}
func (j *journal) Close() error {
return j.f.Close()
}

View File

@ -2,6 +2,7 @@ package containerd
import ( import (
"os" "os"
"path/filepath"
"sync" "sync"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
@ -18,11 +19,16 @@ func NewSupervisor(stateDir string, concurrency int) (*Supervisor, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
j, err := newJournal(filepath.Join(stateDir, "journal.json"))
if err != nil {
return nil, err
}
s := &Supervisor{ s := &Supervisor{
stateDir: stateDir, stateDir: stateDir,
containers: make(map[string]Container), containers: make(map[string]Container),
runtime: runtime, runtime: runtime,
tasks: make(chan *startTask, concurrency*100), tasks: make(chan *startTask, concurrency*100),
journal: j,
} }
for i := 0; i < concurrency; i++ { for i := 0; i < concurrency; i++ {
s.workerGroup.Add(1) s.workerGroup.Add(1)
@ -39,24 +45,33 @@ type Supervisor struct {
runtime Runtime runtime Runtime
events chan Event journal *journal
events chan *Event
tasks chan *startTask tasks chan *startTask
workerGroup sync.WaitGroup workerGroup sync.WaitGroup
} }
func (s *Supervisor) Close() error {
return s.journal.Close()
}
// Start is a non-blocking call that runs the supervisor for monitoring contianer processes and // Start is a non-blocking call that runs the supervisor for monitoring contianer processes and
// executing new containers. // executing new containers.
// //
// This event loop is the only thing that is allowed to modify state of containers and processes. // This event loop is the only thing that is allowed to modify state of containers and processes.
func (s *Supervisor) Start(events chan Event) error { func (s *Supervisor) Start(events chan *Event) error {
if events == nil { if events == nil {
return ErrEventChanNil return ErrEventChanNil
} }
s.events = events s.events = events
go func() { go func() {
for evt := range events { for e := range events {
switch e := evt.(type) { if err := s.journal.write(e); err != nil {
case *ExitEvent: logrus.WithField("error", err).Error("write journal entry")
}
switch e.Type {
case ExitEventType:
logrus.WithFields(logrus.Fields{"pid": e.Pid, "status": e.Status}). logrus.WithFields(logrus.Fields{"pid": e.Pid, "status": e.Status}).
Debug("containerd: process exited") Debug("containerd: process exited")
container, err := s.getContainerForPid(e.Pid) container, err := s.getContainerForPid(e.Pid)
@ -70,7 +85,7 @@ func (s *Supervisor) Start(events chan Event) error {
if err := s.deleteContainer(container); err != nil { if err := s.deleteContainer(container); err != nil {
logrus.WithField("error", err).Error("containerd: deleting container") logrus.WithField("error", err).Error("containerd: deleting container")
} }
case *StartContainerEvent: case StartContainerEventType:
container, err := s.runtime.Create(e.ID, e.BundlePath) container, err := s.runtime.Create(e.ID, e.BundlePath)
if err != nil { if err != nil {
e.Err <- err e.Err <- err
@ -81,18 +96,18 @@ func (s *Supervisor) Start(events chan Event) error {
err: e.Err, err: e.Err,
container: container, container: container,
} }
case *ContainerStartErrorEvent: case ContainerStartErrorEventType:
if container, ok := s.containers[e.ID]; ok { if container, ok := s.containers[e.ID]; ok {
if err := s.deleteContainer(container); err != nil { if err := s.deleteContainer(container); err != nil {
logrus.WithField("error", err).Error("containerd: deleting container") logrus.WithField("error", err).Error("containerd: deleting container")
} }
} }
case *GetContainersEvent: case GetContainerEventType:
for _, c := range s.containers { for _, c := range s.containers {
e.Containers = append(e.Containers, c) e.Containers = append(e.Containers, c)
} }
e.Err <- nil e.Err <- nil
case *SignalEvent: case SignalEventType:
container, ok := s.containers[e.ID] container, ok := s.containers[e.ID]
if !ok { if !ok {
e.Err <- ErrContainerNotFound e.Err <- ErrContainerNotFound
@ -139,7 +154,7 @@ func (s *Supervisor) getContainerForPid(pid int) (Container, error) {
return nil, errNoContainerForPid return nil, errNoContainerForPid
} }
func (s *Supervisor) SendEvent(evt Event) { func (s *Supervisor) SendEvent(evt *Event) {
s.events <- evt s.events <- evt
} }
@ -152,9 +167,9 @@ func (s *Supervisor) startContainerWorker(tasks chan *startTask) {
defer s.workerGroup.Done() defer s.workerGroup.Done()
for t := range tasks { for t := range tasks {
if err := t.container.Start(); err != nil { if err := t.container.Start(); err != nil {
s.SendEvent(&ContainerStartErrorEvent{ e := NewEvent(StartContainerEventType)
ID: t.container.ID(), e.ID = t.container.ID()
}) s.SendEvent(e)
t.err <- err t.err <- err
continue continue
} }