Load runtimes dynamically via go1.8 plugins
Signed-off-by: Michael Crosby <crosbymichael@gmail.com> Add registration for more subsystems via plugins Signed-off-by: Michael Crosby <crosbymichael@gmail.com> Move content service to separate package Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
b7805198b1
commit
3101be93bc
25 changed files with 435 additions and 264 deletions
89
services/execution/collector.go
Normal file
89
services/execution/collector.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
package execution
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/docker/containerd"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func newCollector(ctx context.Context, runtimes map[string]containerd.Runtime) (*collector, error) {
|
||||
c := &collector{
|
||||
context: ctx,
|
||||
ch: make(chan *containerd.Event, 2048),
|
||||
eventClients: make(map[*eventClient]struct{}),
|
||||
}
|
||||
for _, r := range runtimes {
|
||||
if err := c.collect(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// run the publisher
|
||||
go c.publisher()
|
||||
// run a goroutine that waits for the context to be done
|
||||
// and closes the channel after all writes have finished
|
||||
go c.waitDone()
|
||||
return c, nil
|
||||
}
|
||||
|
||||
type eventClient struct {
|
||||
eCh chan error
|
||||
w *grpcEventWriter
|
||||
}
|
||||
|
||||
type collector struct {
|
||||
mu sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
|
||||
context context.Context
|
||||
ch chan *containerd.Event
|
||||
eventClients map[*eventClient]struct{}
|
||||
}
|
||||
|
||||
// collect collects events from the provided runtime
|
||||
func (c *collector) collect(r containerd.Runtime) error {
|
||||
c.wg.Add(1)
|
||||
go func() {
|
||||
defer c.wg.Done()
|
||||
for e := range r.Events(c.context) {
|
||||
c.ch <- e
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *collector) forward(w *grpcEventWriter) error {
|
||||
client := &eventClient{
|
||||
w: w,
|
||||
eCh: make(chan error, 1),
|
||||
}
|
||||
c.mu.Lock()
|
||||
c.eventClients[client] = struct{}{}
|
||||
c.mu.Unlock()
|
||||
err := <-client.eCh
|
||||
c.mu.Lock()
|
||||
delete(c.eventClients, client)
|
||||
c.mu.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *collector) publisher() {
|
||||
for e := range c.ch {
|
||||
c.mu.Lock()
|
||||
for client := range c.eventClients {
|
||||
if err := client.w.Write(e); err != nil {
|
||||
client.eCh <- err
|
||||
}
|
||||
}
|
||||
c.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// waitDone waits for the context to finish, waits for all the goroutines to finish
|
||||
// collecting grpc events from the shim, and closes the output channel
|
||||
func (c *collector) waitDone() {
|
||||
<-c.context.Done()
|
||||
c.wg.Wait()
|
||||
close(c.ch)
|
||||
}
|
|
@ -1,11 +1,14 @@
|
|||
package execution
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/docker/containerd"
|
||||
api "github.com/docker/containerd/api/services/execution"
|
||||
"github.com/docker/containerd/api/types/container"
|
||||
google_protobuf "github.com/golang/protobuf/ptypes/empty"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -13,15 +16,46 @@ var (
|
|||
empty = &google_protobuf.Empty{}
|
||||
)
|
||||
|
||||
// New creates a new GRPC service for the ContainerService
|
||||
func New(s *containerd.Supervisor) *Service {
|
||||
return &Service{
|
||||
s: s,
|
||||
func init() {
|
||||
containerd.Register("runtime-grpc", &containerd.Registration{
|
||||
Type: containerd.GRPCPlugin,
|
||||
Init: New,
|
||||
})
|
||||
}
|
||||
|
||||
func New(ic *containerd.InitContext) (interface{}, error) {
|
||||
c, err := newCollector(ic.Context, ic.Runtimes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Service{
|
||||
runtimes: ic.Runtimes,
|
||||
containers: make(map[string]containerd.Container),
|
||||
collector: c,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
s *containerd.Supervisor
|
||||
mu sync.Mutex
|
||||
|
||||
runtimes map[string]containerd.Runtime
|
||||
containers map[string]containerd.Container
|
||||
collector *collector
|
||||
}
|
||||
|
||||
func (s *Service) Register(server *grpc.Server) error {
|
||||
api.RegisterContainerServiceServer(server, s)
|
||||
// load all containers
|
||||
for _, r := range s.runtimes {
|
||||
containers, err := r.Containers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, c := range containers {
|
||||
s.containers[c.Info().ID] = c
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) Create(ctx context.Context, r *api.CreateRequest) (*api.CreateResponse, error) {
|
||||
|
@ -41,13 +75,28 @@ func (s *Service) Create(ctx context.Context, r *api.CreateRequest) (*api.Create
|
|||
Options: m.Options,
|
||||
})
|
||||
}
|
||||
c, err := s.s.Create(ctx, r.ID, r.Runtime, opts)
|
||||
runtime, err := s.getRuntime(r.Runtime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.mu.Lock()
|
||||
if _, ok := s.containers[r.ID]; ok {
|
||||
s.mu.Unlock()
|
||||
return nil, containerd.ErrContainerExists
|
||||
}
|
||||
c, err := runtime.Create(ctx, r.ID, opts)
|
||||
if err != nil {
|
||||
s.mu.Unlock()
|
||||
return nil, err
|
||||
}
|
||||
s.containers[r.ID] = c
|
||||
s.mu.Unlock()
|
||||
state, err := c.State(ctx)
|
||||
if err != nil {
|
||||
s.s.Delete(ctx, r.ID)
|
||||
s.mu.Lock()
|
||||
delete(s.containers, r.ID)
|
||||
runtime.Delete(ctx, c)
|
||||
s.mu.Unlock()
|
||||
return nil, err
|
||||
}
|
||||
return &api.CreateResponse{
|
||||
|
@ -57,7 +106,7 @@ func (s *Service) Create(ctx context.Context, r *api.CreateRequest) (*api.Create
|
|||
}
|
||||
|
||||
func (s *Service) Start(ctx context.Context, r *api.StartRequest) (*google_protobuf.Empty, error) {
|
||||
c, err := s.s.Get(r.ID)
|
||||
c, err := s.getContainer(r.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -68,7 +117,15 @@ func (s *Service) Start(ctx context.Context, r *api.StartRequest) (*google_proto
|
|||
}
|
||||
|
||||
func (s *Service) Delete(ctx context.Context, r *api.DeleteRequest) (*google_protobuf.Empty, error) {
|
||||
if err := s.s.Delete(ctx, r.ID); err != nil {
|
||||
c, err := s.getContainer(r.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
runtime, err := s.getRuntime(c.Info().Runtime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := runtime.Delete(ctx, c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return empty, nil
|
||||
|
@ -76,7 +133,7 @@ func (s *Service) Delete(ctx context.Context, r *api.DeleteRequest) (*google_pro
|
|||
|
||||
func (s *Service) List(ctx context.Context, r *api.ListRequest) (*api.ListResponse, error) {
|
||||
resp := &api.ListResponse{}
|
||||
for _, c := range s.s.Containers() {
|
||||
for _, c := range s.containers {
|
||||
state, err := c.State(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -105,7 +162,25 @@ func (s *Service) Events(r *api.EventsRequest, server api.ContainerService_Event
|
|||
w := &grpcEventWriter{
|
||||
server: server,
|
||||
}
|
||||
return s.s.ForwardEvents(w)
|
||||
return s.collector.forward(w)
|
||||
}
|
||||
|
||||
func (s *Service) getContainer(id string) (containerd.Container, error) {
|
||||
s.mu.Lock()
|
||||
c, ok := s.containers[id]
|
||||
s.mu.Unlock()
|
||||
if !ok {
|
||||
return nil, containerd.ErrContainerNotExist
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (s *Service) getRuntime(name string) (containerd.Runtime, error) {
|
||||
runtime, ok := s.runtimes[name]
|
||||
if !ok {
|
||||
return nil, containerd.ErrUnknownRuntime
|
||||
}
|
||||
return runtime, nil
|
||||
}
|
||||
|
||||
type grpcEventWriter struct {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue