Remove bundles from API
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
1dc5d652ac
commit
ab8586b7c5
48 changed files with 3287 additions and 5946 deletions
49
linux/container.go
Normal file
49
linux/container.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package linux
|
||||
|
||||
import (
|
||||
"github.com/docker/containerd"
|
||||
"github.com/docker/containerd/api/services/shim"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type State struct {
|
||||
pid uint32
|
||||
status containerd.ContainerStatus
|
||||
}
|
||||
|
||||
func (s State) Pid() uint32 {
|
||||
return s.pid
|
||||
}
|
||||
|
||||
func (s State) Status() containerd.ContainerStatus {
|
||||
return s.status
|
||||
}
|
||||
|
||||
type Container struct {
|
||||
id string
|
||||
|
||||
shim shim.ShimClient
|
||||
}
|
||||
|
||||
func (c *Container) ID() string {
|
||||
return c.id
|
||||
}
|
||||
|
||||
func (Container) Runtime() string {
|
||||
return runtimeName
|
||||
}
|
||||
|
||||
func (c *Container) Start(ctx context.Context) error {
|
||||
_, err := c.shim.Start(ctx, &shim.StartRequest{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Container) State(ctx context.Context) (containerd.State, error) {
|
||||
response, err := c.shim.State(ctx, &shim.StateRequest{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &State{
|
||||
pid: response.Pid,
|
||||
}, nil
|
||||
}
|
197
linux/runtime.go
Normal file
197
linux/runtime.go
Normal file
|
@ -0,0 +1,197 @@
|
|||
package linux
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/docker/containerd"
|
||||
"github.com/docker/containerd/api/services/shim"
|
||||
"github.com/docker/containerd/api/types/container"
|
||||
"github.com/docker/containerd/api/types/mount"
|
||||
"github.com/docker/swarmkit/log"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
const (
|
||||
runtimeName = "linux"
|
||||
configFilename = "config.json"
|
||||
)
|
||||
|
||||
func init() {
|
||||
containerd.RegisterRuntime(runtimeName, New)
|
||||
}
|
||||
|
||||
func New(root string) (containerd.Runtime, error) {
|
||||
if err := os.MkdirAll(root, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c, cancel := context.WithCancel(context.Background())
|
||||
return &Runtime{
|
||||
root: root,
|
||||
events: make(chan *containerd.Event, 2048),
|
||||
eventsContext: c,
|
||||
eventsCancel: cancel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Runtime struct {
|
||||
root string
|
||||
|
||||
events chan *containerd.Event
|
||||
eventsContext context.Context
|
||||
eventsCancel func()
|
||||
}
|
||||
|
||||
func (r *Runtime) Create(ctx context.Context, id string, opts containerd.CreateOpts) (containerd.Container, error) {
|
||||
path, err := r.newBundle(id, opts.Spec)
|
||||
if err != nil {
|
||||
os.RemoveAll(path)
|
||||
return nil, err
|
||||
}
|
||||
s, err := newShim(path)
|
||||
if err != nil {
|
||||
os.RemoveAll(path)
|
||||
return nil, err
|
||||
}
|
||||
if err := r.handleEvents(s); err != nil {
|
||||
os.RemoveAll(path)
|
||||
return nil, err
|
||||
}
|
||||
sopts := &shim.CreateRequest{
|
||||
ID: id,
|
||||
Bundle: path,
|
||||
Runtime: "runc",
|
||||
Stdin: opts.IO.Stdin,
|
||||
Stdout: opts.IO.Stdout,
|
||||
Stderr: opts.IO.Stderr,
|
||||
Terminal: opts.IO.Terminal,
|
||||
}
|
||||
for _, m := range opts.Rootfs {
|
||||
sopts.Rootfs = append(sopts.Rootfs, &mount.Mount{
|
||||
Type: m.Type,
|
||||
Source: m.Source,
|
||||
Options: m.Options,
|
||||
})
|
||||
}
|
||||
if _, err := s.Create(ctx, sopts); err != nil {
|
||||
os.RemoveAll(path)
|
||||
return nil, err
|
||||
}
|
||||
return &Container{
|
||||
id: id,
|
||||
shim: s,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Runtime) Delete(ctx context.Context, c containerd.Container) error {
|
||||
lc, ok := c.(*Container)
|
||||
if !ok {
|
||||
return fmt.Errorf("container cannot be cast as *linux.Container")
|
||||
}
|
||||
if _, err := lc.shim.Delete(ctx, &shim.DeleteRequest{}); err != nil {
|
||||
return err
|
||||
}
|
||||
lc.shim.Exit(ctx, &shim.ExitRequest{})
|
||||
return r.deleteBundle(lc.id)
|
||||
}
|
||||
|
||||
func (r *Runtime) Containers() ([]containerd.Container, error) {
|
||||
dir, err := ioutil.ReadDir(r.root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var o []containerd.Container
|
||||
for _, fi := range dir {
|
||||
if !fi.IsDir() {
|
||||
continue
|
||||
}
|
||||
c, err := r.loadContainer(fi.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
o = append(o, c)
|
||||
}
|
||||
return o, nil
|
||||
}
|
||||
|
||||
func (r *Runtime) Events(ctx context.Context) <-chan *containerd.Event {
|
||||
return r.events
|
||||
}
|
||||
|
||||
func (r *Runtime) handleEvents(s shim.ShimClient) error {
|
||||
events, err := s.Events(r.eventsContext, &shim.EventsRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go r.forward(events)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Runtime) forward(events shim.Shim_EventsClient) {
|
||||
for {
|
||||
e, err := events.Recv()
|
||||
if err != nil {
|
||||
log.G(r.eventsContext).WithError(err).Error("get event from shim")
|
||||
return
|
||||
}
|
||||
var et containerd.EventType
|
||||
switch e.Type {
|
||||
case container.Event_CREATE:
|
||||
et = containerd.CreateEvent
|
||||
case container.Event_EXEC_ADDED:
|
||||
et = containerd.ExecAddEvent
|
||||
case container.Event_EXIT:
|
||||
et = containerd.ExitEvent
|
||||
case container.Event_OOM:
|
||||
et = containerd.OOMEvent
|
||||
case container.Event_START:
|
||||
et = containerd.StartEvent
|
||||
}
|
||||
r.events <- &containerd.Event{
|
||||
Timestamp: time.Now(),
|
||||
Runtime: runtimeName,
|
||||
Type: et,
|
||||
Pid: e.Pid,
|
||||
ID: e.ID,
|
||||
ExitStatus: e.ExitStatus,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Runtime) newBundle(id string, spec []byte) (string, error) {
|
||||
path := filepath.Join(r.root, id)
|
||||
if err := os.Mkdir(path, 0700); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.Mkdir(filepath.Join(path, "rootfs"), 0700); err != nil {
|
||||
return "", err
|
||||
}
|
||||
f, err := os.Create(filepath.Join(path, configFilename))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, err = io.Copy(f, bytes.NewReader(spec))
|
||||
return path, err
|
||||
}
|
||||
|
||||
func (r *Runtime) deleteBundle(id string) error {
|
||||
return os.RemoveAll(filepath.Join(r.root, id))
|
||||
}
|
||||
|
||||
func (r *Runtime) loadContainer(path string) (*Container, error) {
|
||||
id := filepath.Base(path)
|
||||
s, err := loadShim(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Container{
|
||||
id: id,
|
||||
shim: s,
|
||||
}, nil
|
||||
}
|
76
linux/shim.go
Normal file
76
linux/shim.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
package linux
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
|
||||
"github.com/docker/containerd/api/services/shim"
|
||||
"github.com/docker/containerd/utils"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func newShim(path string) (shim.ShimClient, error) {
|
||||
socket := filepath.Join(path, "shim.sock")
|
||||
l, err := utils.CreateUnixSocket(socket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd := exec.Command("containerd-shim")
|
||||
cmd.Dir = path
|
||||
f, err := l.(*net.UnixListener).File()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd.ExtraFiles = append(cmd.ExtraFiles, f)
|
||||
// make sure the shim can be re-parented to system init
|
||||
// and is cloned in a new mount namespace because the overlay/filesystems
|
||||
// will be mounted by the shim
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Cloneflags: syscall.CLONE_NEWNS,
|
||||
Setpgid: true,
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to start shim")
|
||||
}
|
||||
// close our side of the socket, do not close the listener as it will
|
||||
// remove the socket from disk
|
||||
f.Close()
|
||||
// since we are currently the parent go ahead and make sure we wait on the shim
|
||||
go cmd.Wait()
|
||||
return connectShim(socket)
|
||||
}
|
||||
|
||||
func loadShim(path string) (shim.ShimClient, error) {
|
||||
socket := filepath.Join(path, "shim.sock")
|
||||
return connectShim(socket)
|
||||
// TODO: failed to connect to the shim, check if it's alive
|
||||
// - if it is kill it
|
||||
// - in both case call runc killall and runc delete on the id
|
||||
}
|
||||
|
||||
func connectShim(socket string) (shim.ShimClient, error) {
|
||||
// reset the logger for grpc to log to dev/null so that it does not mess with our stdio
|
||||
grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags))
|
||||
dialOpts := []grpc.DialOption{grpc.WithInsecure(), grpc.WithTimeout(100 * time.Second)}
|
||||
dialOpts = append(dialOpts,
|
||||
grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
|
||||
return net.DialTimeout("unix", socket, timeout)
|
||||
}),
|
||||
grpc.WithBlock(),
|
||||
grpc.WithTimeout(2*time.Second),
|
||||
)
|
||||
conn, err := grpc.Dial(fmt.Sprintf("unix://%s", socket), dialOpts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to connect to shim via \"%s\"", fmt.Sprintf("unix://%s", socket))
|
||||
}
|
||||
return shim.NewShimClient(conn), nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue