| /* |
| Copyright The containerd Authors. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package snapshots |
| |
| import ( |
| "context" |
| |
| snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1" |
| "github.com/containerd/errdefs" |
| "github.com/containerd/errdefs/pkg/errgrpc" |
| "github.com/containerd/log" |
| "github.com/containerd/plugin" |
| "github.com/containerd/plugin/registry" |
| "google.golang.org/grpc" |
| |
| "github.com/containerd/containerd/v2/core/mount" |
| "github.com/containerd/containerd/v2/core/snapshots" |
| "github.com/containerd/containerd/v2/core/snapshots/proxy" |
| ptypes "github.com/containerd/containerd/v2/pkg/protobuf/types" |
| "github.com/containerd/containerd/v2/plugins" |
| "github.com/containerd/containerd/v2/plugins/services" |
| ) |
| |
| func init() { |
| registry.Register(&plugin.Registration{ |
| Type: plugins.GRPCPlugin, |
| ID: "snapshots", |
| Requires: []plugin.Type{ |
| plugins.ServicePlugin, |
| }, |
| InitFn: newService, |
| }) |
| } |
| |
| var empty = &ptypes.Empty{} |
| |
| type service struct { |
| ss map[string]snapshots.Snapshotter |
| snapshotsapi.UnimplementedSnapshotsServer |
| } |
| |
| func newService(ic *plugin.InitContext) (interface{}, error) { |
| i, err := ic.GetByID(plugins.ServicePlugin, services.SnapshotsService) |
| if err != nil { |
| return nil, err |
| } |
| return &service{ss: i.(map[string]snapshots.Snapshotter)}, nil |
| } |
| |
| func (s *service) getSnapshotter(name string) (snapshots.Snapshotter, error) { |
| if name == "" { |
| return nil, errgrpc.ToGRPCf(errdefs.ErrInvalidArgument, "snapshotter argument missing") |
| } |
| |
| sn := s.ss[name] |
| if sn == nil { |
| return nil, errgrpc.ToGRPCf(errdefs.ErrInvalidArgument, "snapshotter not loaded: %s", name) |
| } |
| return sn, nil |
| } |
| |
| func (s *service) Register(gs *grpc.Server) error { |
| snapshotsapi.RegisterSnapshotsServer(gs, s) |
| return nil |
| } |
| |
| func (s *service) Prepare(ctx context.Context, pr *snapshotsapi.PrepareSnapshotRequest) (*snapshotsapi.PrepareSnapshotResponse, error) { |
| log.G(ctx).WithFields(log.Fields{"parent": pr.Parent, "key": pr.Key, "snapshotter": pr.Snapshotter}).Debugf("prepare snapshot") |
| sn, err := s.getSnapshotter(pr.Snapshotter) |
| if err != nil { |
| return nil, err |
| } |
| |
| var opts []snapshots.Opt |
| if pr.Labels != nil { |
| opts = append(opts, snapshots.WithLabels(pr.Labels)) |
| } |
| mounts, err := sn.Prepare(ctx, pr.Key, pr.Parent, opts...) |
| if err != nil { |
| return nil, errgrpc.ToGRPC(err) |
| } |
| |
| return &snapshotsapi.PrepareSnapshotResponse{ |
| Mounts: mount.ToProto(mounts), |
| }, nil |
| } |
| |
| func (s *service) View(ctx context.Context, pr *snapshotsapi.ViewSnapshotRequest) (*snapshotsapi.ViewSnapshotResponse, error) { |
| log.G(ctx).WithFields(log.Fields{"parent": pr.Parent, "key": pr.Key, "snapshotter": pr.Snapshotter}).Debugf("prepare view snapshot") |
| sn, err := s.getSnapshotter(pr.Snapshotter) |
| if err != nil { |
| return nil, err |
| } |
| var opts []snapshots.Opt |
| if pr.Labels != nil { |
| opts = append(opts, snapshots.WithLabels(pr.Labels)) |
| } |
| mounts, err := sn.View(ctx, pr.Key, pr.Parent, opts...) |
| if err != nil { |
| return nil, errgrpc.ToGRPC(err) |
| } |
| return &snapshotsapi.ViewSnapshotResponse{ |
| Mounts: mount.ToProto(mounts), |
| }, nil |
| } |
| |
| func (s *service) Mounts(ctx context.Context, mr *snapshotsapi.MountsRequest) (*snapshotsapi.MountsResponse, error) { |
| log.G(ctx).WithFields(log.Fields{"key": mr.Key, "snapshotter": mr.Snapshotter}).Debugf("get snapshot mounts") |
| sn, err := s.getSnapshotter(mr.Snapshotter) |
| if err != nil { |
| return nil, err |
| } |
| |
| mounts, err := sn.Mounts(ctx, mr.Key) |
| if err != nil { |
| return nil, errgrpc.ToGRPC(err) |
| } |
| return &snapshotsapi.MountsResponse{ |
| Mounts: mount.ToProto(mounts), |
| }, nil |
| } |
| |
| func (s *service) Commit(ctx context.Context, cr *snapshotsapi.CommitSnapshotRequest) (*ptypes.Empty, error) { |
| log.G(ctx).WithFields(log.Fields{"key": cr.Key, "snapshotter": cr.Snapshotter, "name": cr.Name}).Debugf("commit snapshot") |
| sn, err := s.getSnapshotter(cr.Snapshotter) |
| if err != nil { |
| return nil, err |
| } |
| |
| var opts []snapshots.Opt |
| if cr.Labels != nil { |
| opts = append(opts, snapshots.WithLabels(cr.Labels)) |
| } |
| if cr.Parent != "" { |
| opts = append(opts, snapshots.WithParent(cr.Parent)) |
| } |
| if err := sn.Commit(ctx, cr.Name, cr.Key, opts...); err != nil { |
| return nil, errgrpc.ToGRPC(err) |
| } |
| |
| return empty, nil |
| } |
| |
| func (s *service) Remove(ctx context.Context, rr *snapshotsapi.RemoveSnapshotRequest) (*ptypes.Empty, error) { |
| log.G(ctx).WithFields(log.Fields{"key": rr.Key, "snapshotter": rr.Snapshotter}).Debugf("remove snapshot") |
| sn, err := s.getSnapshotter(rr.Snapshotter) |
| if err != nil { |
| return nil, err |
| } |
| |
| if err := sn.Remove(ctx, rr.Key); err != nil { |
| return nil, errgrpc.ToGRPC(err) |
| } |
| |
| return empty, nil |
| } |
| |
| func (s *service) Stat(ctx context.Context, sr *snapshotsapi.StatSnapshotRequest) (*snapshotsapi.StatSnapshotResponse, error) { |
| log.G(ctx).WithFields(log.Fields{"key": sr.Key, "snapshotter": sr.Snapshotter}).Debugf("stat snapshot") |
| sn, err := s.getSnapshotter(sr.Snapshotter) |
| if err != nil { |
| return nil, err |
| } |
| |
| info, err := sn.Stat(ctx, sr.Key) |
| if err != nil { |
| return nil, errgrpc.ToGRPC(err) |
| } |
| |
| return &snapshotsapi.StatSnapshotResponse{Info: proxy.InfoToProto(info)}, nil |
| } |
| |
| func (s *service) Update(ctx context.Context, sr *snapshotsapi.UpdateSnapshotRequest) (*snapshotsapi.UpdateSnapshotResponse, error) { |
| log.G(ctx).WithFields(log.Fields{"key": sr.Info.Name, "snapshotter": sr.Snapshotter}).Debugf("update snapshot") |
| sn, err := s.getSnapshotter(sr.Snapshotter) |
| if err != nil { |
| return nil, err |
| } |
| |
| info, err := sn.Update(ctx, proxy.InfoFromProto(sr.Info), sr.UpdateMask.GetPaths()...) |
| if err != nil { |
| return nil, errgrpc.ToGRPC(err) |
| } |
| |
| return &snapshotsapi.UpdateSnapshotResponse{Info: proxy.InfoToProto(info)}, nil |
| } |
| |
| func (s *service) List(sr *snapshotsapi.ListSnapshotsRequest, ss snapshotsapi.Snapshots_ListServer) error { |
| sn, err := s.getSnapshotter(sr.Snapshotter) |
| if err != nil { |
| return err |
| } |
| |
| var ( |
| buffer []*snapshotsapi.Info |
| sendBlock = func(block []*snapshotsapi.Info) error { |
| return ss.Send(&snapshotsapi.ListSnapshotsResponse{ |
| Info: block, |
| }) |
| } |
| ) |
| err = sn.Walk(ss.Context(), func(ctx context.Context, info snapshots.Info) error { |
| buffer = append(buffer, proxy.InfoToProto(info)) |
| |
| if len(buffer) >= 100 { |
| if err := sendBlock(buffer); err != nil { |
| return err |
| } |
| |
| buffer = buffer[:0] |
| } |
| |
| return nil |
| }, sr.Filters...) |
| 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 *snapshotsapi.UsageRequest) (*snapshotsapi.UsageResponse, error) { |
| sn, err := s.getSnapshotter(ur.Snapshotter) |
| if err != nil { |
| return nil, err |
| } |
| |
| usage, err := sn.Usage(ctx, ur.Key) |
| if err != nil { |
| return nil, errgrpc.ToGRPC(err) |
| } |
| |
| return proxy.UsageToProto(usage), nil |
| } |
| |
| func (s *service) Cleanup(ctx context.Context, cr *snapshotsapi.CleanupRequest) (*ptypes.Empty, error) { |
| sn, err := s.getSnapshotter(cr.Snapshotter) |
| if err != nil { |
| return nil, err |
| } |
| |
| c, ok := sn.(snapshots.Cleaner) |
| if !ok { |
| return nil, errgrpc.ToGRPCf(errdefs.ErrNotImplemented, "snapshotter does not implement Cleanup method") |
| } |
| |
| err = c.Cleanup(ctx) |
| if err != nil { |
| return nil, errgrpc.ToGRPC(err) |
| } |
| |
| return empty, nil |
| } |