Files
containerd/services/snapshot/client.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

159 lines
4.0 KiB
Go

package snapshot
import (
"context"
"io"
"strings"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"github.com/containerd/containerd"
snapshotapi "github.com/containerd/containerd/api/services/snapshot"
"github.com/containerd/containerd/snapshot"
"github.com/pkg/errors"
)
// NewSnapshotterFromClient returns a new Snapshotter which communicates
// over a GRPC connection.
func NewSnapshotterFromClient(client snapshotapi.SnapshotClient) snapshot.Snapshotter {
return &remoteSnapshotter{
client: client,
}
}
type remoteSnapshotter struct {
client snapshotapi.SnapshotClient
}
func (r *remoteSnapshotter) Stat(ctx context.Context, key string) (snapshot.Info, error) {
resp, err := r.client.Stat(ctx, &snapshotapi.StatRequest{Key: key})
if err != nil {
return snapshot.Info{}, rewriteGRPCError(err)
}
return toInfo(resp.Info), nil
}
func (r *remoteSnapshotter) Usage(ctx context.Context, key string) (snapshot.Usage, error) {
resp, err := r.client.Usage(ctx, &snapshotapi.UsageRequest{Key: key})
if err != nil {
return snapshot.Usage{}, rewriteGRPCError(err)
}
return toUsage(resp), nil
}
func (r *remoteSnapshotter) Mounts(ctx context.Context, key string) ([]containerd.Mount, error) {
resp, err := r.client.Mounts(ctx, &snapshotapi.MountsRequest{Key: key})
if err != nil {
return nil, rewriteGRPCError(err)
}
return toMounts(resp), nil
}
func (r *remoteSnapshotter) Prepare(ctx context.Context, key, parent string) ([]containerd.Mount, error) {
resp, err := r.client.Prepare(ctx, &snapshotapi.PrepareRequest{Key: key, Parent: parent})
if err != nil {
return nil, rewriteGRPCError(err)
}
return toMounts(resp), nil
}
func (r *remoteSnapshotter) View(ctx context.Context, key, parent string) ([]containerd.Mount, error) {
resp, err := r.client.View(ctx, &snapshotapi.PrepareRequest{Key: key, Parent: parent})
if err != nil {
return nil, rewriteGRPCError(err)
}
return toMounts(resp), nil
}
func (r *remoteSnapshotter) Commit(ctx context.Context, name, key string) error {
_, err := r.client.Commit(ctx, &snapshotapi.CommitRequest{
Name: name,
Key: key,
})
return rewriteGRPCError(err)
}
func (r *remoteSnapshotter) Remove(ctx context.Context, key string) error {
_, err := r.client.Remove(ctx, &snapshotapi.RemoveRequest{Key: key})
return rewriteGRPCError(err)
}
func (r *remoteSnapshotter) Walk(ctx context.Context, fn func(context.Context, snapshot.Info) error) error {
sc, err := r.client.List(ctx, &snapshotapi.ListRequest{})
if err != nil {
rewriteGRPCError(err)
}
for {
resp, err := sc.Recv()
if err != nil {
if err == io.EOF {
return nil
}
return rewriteGRPCError(err)
}
if resp == nil {
return nil
}
for _, info := range resp.Info {
if err := fn(ctx, toInfo(info)); err != nil {
return err
}
}
}
}
func rewriteGRPCError(err error) error {
switch grpc.Code(errors.Cause(err)) {
case codes.AlreadyExists:
return snapshot.ErrSnapshotExist
case codes.NotFound:
return snapshot.ErrSnapshotNotExist
case codes.FailedPrecondition:
desc := grpc.ErrorDesc(errors.Cause(err))
if strings.Contains(desc, snapshot.ErrSnapshotNotActive.Error()) {
return snapshot.ErrSnapshotNotActive
}
if strings.Contains(desc, snapshot.ErrSnapshotNotCommitted.Error()) {
return snapshot.ErrSnapshotNotCommitted
}
}
return err
}
func toKind(kind snapshotapi.Kind) snapshot.Kind {
if kind == snapshotapi.KindActive {
return snapshot.KindActive
}
return snapshot.KindCommitted
}
func toInfo(info snapshotapi.Info) snapshot.Info {
return snapshot.Info{
Name: info.Name,
Parent: info.Parent,
Kind: toKind(info.Kind),
Readonly: info.Readonly,
}
}
func toUsage(resp *snapshotapi.UsageResponse) snapshot.Usage {
return snapshot.Usage{
Inodes: resp.Inodes,
Size: resp.Size_,
}
}
func toMounts(resp *snapshotapi.MountsResponse) []containerd.Mount {
mounts := make([]containerd.Mount, len(resp.Mounts))
for i, m := range resp.Mounts {
mounts[i] = containerd.Mount{
Type: m.Type,
Source: m.Source,
Options: m.Options,
}
}
return mounts
}