Files
containerd/services/snapshot/service.go
Derek McGowan 098ff94b24 Add snapshot and diff service
Remove rootfs service in place of snapshot service. Adds
diff service for extracting and creating diffs. Diff
creation is not yet implemented. This service allows
pulling or creating images without needing root access to
mount. Additionally in the future this will allow containerd
to ensure extractions happen safely in a chroot if needed.

Signed-off-by: Derek McGowan <derek@mcgstyle.net>
2017-05-15 16:50:16 -07:00

205 lines
5.4 KiB
Go

package snapshot
import (
gocontext "context"
"github.com/containerd/containerd"
snapshotapi "github.com/containerd/containerd/api/services/snapshot"
mounttypes "github.com/containerd/containerd/api/types/mount"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/snapshot"
protoempty "github.com/golang/protobuf/ptypes/empty"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
)
func init() {
plugin.Register("snapshots-grpc", &plugin.Registration{
Type: plugin.GRPCPlugin,
Init: func(ic *plugin.InitContext) (interface{}, error) {
return newService(ic.Snapshotter)
},
})
}
var empty = &protoempty.Empty{}
type service struct {
snapshotter snapshot.Snapshotter
}
func newService(snapshotter snapshot.Snapshotter) (*service, error) {
return &service{
snapshotter: snapshotter,
}, nil
}
func (s *service) Register(gs *grpc.Server) error {
snapshotapi.RegisterSnapshotServer(gs, s)
return nil
}
func (s *service) Prepare(ctx context.Context, pr *snapshotapi.PrepareRequest) (*snapshotapi.MountsResponse, error) {
log.G(ctx).WithField("parent", pr.Parent).WithField("key", pr.Key).Debugf("Preparing snapshot")
// TODO: Apply namespace
// TODO: Lookup snapshot id from metadata store
mounts, err := s.snapshotter.Prepare(ctx, pr.Key, pr.Parent)
if err != nil {
return nil, grpcError(err)
}
return fromMounts(mounts), nil
}
func (s *service) View(ctx context.Context, pr *snapshotapi.PrepareRequest) (*snapshotapi.MountsResponse, error) {
log.G(ctx).WithField("parent", pr.Parent).WithField("key", pr.Key).Debugf("Preparing view snapshot")
// TODO: Apply namespace
// TODO: Lookup snapshot id from metadata store
mounts, err := s.snapshotter.View(ctx, pr.Key, pr.Parent)
if err != nil {
return nil, grpcError(err)
}
return fromMounts(mounts), nil
}
func (s *service) Mounts(ctx context.Context, mr *snapshotapi.MountsRequest) (*snapshotapi.MountsResponse, error) {
log.G(ctx).WithField("key", mr.Key).Debugf("Getting snapshot mounts")
// TODO: Apply namespace
// TODO: Lookup snapshot id from metadata store
mounts, err := s.snapshotter.Mounts(ctx, mr.Key)
if err != nil {
return nil, grpcError(err)
}
return fromMounts(mounts), nil
}
func (s *service) Commit(ctx context.Context, cr *snapshotapi.CommitRequest) (*protoempty.Empty, error) {
log.G(ctx).WithField("key", cr.Key).WithField("name", cr.Name).Debugf("Committing snapshot")
// TODO: Apply namespace
// TODO: Lookup snapshot id from metadata store
if err := s.snapshotter.Commit(ctx, cr.Name, cr.Key); err != nil {
return nil, grpcError(err)
}
return empty, nil
}
func (s *service) Remove(ctx context.Context, rr *snapshotapi.RemoveRequest) (*protoempty.Empty, error) {
log.G(ctx).WithField("key", rr.Key).Debugf("Removing snapshot")
// TODO: Apply namespace
// TODO: Lookup snapshot id from metadata store
if err := s.snapshotter.Remove(ctx, rr.Key); err != nil {
return nil, grpcError(err)
}
return empty, nil
}
func (s *service) Stat(ctx context.Context, sr *snapshotapi.StatRequest) (*snapshotapi.StatResponse, error) {
log.G(ctx).WithField("key", sr.Key).Debugf("Statting snapshot")
// TODO: Apply namespace
info, err := s.snapshotter.Stat(ctx, sr.Key)
if err != nil {
return nil, grpcError(err)
}
return &snapshotapi.StatResponse{Info: fromInfo(info)}, nil
}
func (s *service) List(sr *snapshotapi.ListRequest, ss snapshotapi.Snapshot_ListServer) error {
// TODO: Apply namespace
var (
buffer []snapshotapi.Info
sendBlock = func(block []snapshotapi.Info) error {
return ss.Send(&snapshotapi.ListResponse{
Info: block,
})
}
)
err := s.snapshotter.Walk(ss.Context(), func(ctx gocontext.Context, info snapshot.Info) error {
buffer = append(buffer, fromInfo(info))
if len(buffer) >= 100 {
if err := sendBlock(buffer); err != nil {
return err
}
buffer = buffer[:0]
}
return nil
})
if err != nil {
return err
}
if len(buffer) > 0 {
// Send remaining infos
if err := sendBlock(buffer); err != nil {
return err
}
}
return nil
}
func (s *service) Usage(ctx context.Context, ur *snapshotapi.UsageRequest) (*snapshotapi.UsageResponse, error) {
// TODO: Apply namespace
usage, err := s.snapshotter.Usage(ctx, ur.Key)
if err != nil {
return nil, grpcError(err)
}
return fromUsage(usage), nil
}
func grpcError(err error) error {
if snapshot.IsNotExist(err) {
return grpc.Errorf(codes.NotFound, err.Error())
}
if snapshot.IsExist(err) {
return grpc.Errorf(codes.AlreadyExists, err.Error())
}
if snapshot.IsNotActive(err) || snapshot.IsNotCommitted(err) {
return grpc.Errorf(codes.FailedPrecondition, err.Error())
}
return err
}
func fromKind(kind snapshot.Kind) snapshotapi.Kind {
if kind == snapshot.KindActive {
return snapshotapi.KindActive
}
return snapshotapi.KindCommitted
}
func fromInfo(info snapshot.Info) snapshotapi.Info {
return snapshotapi.Info{
Name: info.Name,
Parent: info.Parent,
Kind: fromKind(info.Kind),
Readonly: info.Readonly,
}
}
func fromUsage(usage snapshot.Usage) *snapshotapi.UsageResponse {
return &snapshotapi.UsageResponse{
Inodes: usage.Inodes,
Size_: usage.Size,
}
}
func fromMounts(mounts []containerd.Mount) *snapshotapi.MountsResponse {
resp := &snapshotapi.MountsResponse{
Mounts: make([]*mounttypes.Mount, len(mounts)),
}
for i, m := range mounts {
resp.Mounts[i] = &mounttypes.Mount{
Type: m.Type,
Source: m.Source,
Options: m.Options,
}
}
return resp
}