package snapshot import ( gocontext "context" 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/mount" "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 []mount.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 }