8c74da3983
With this changeset, image store access is now moved to completely accessible over GRPC. No clients manipulate the image store database directly and the GRPC client is fully featured. The metadata database is now managed by the daemon and access coordinated via services. Signed-off-by: Stephen J Day <stephen.day@docker.com>
116 lines
3.1 KiB
Go
116 lines
3.1 KiB
Go
package rootfs
|
|
|
|
import (
|
|
"syscall"
|
|
|
|
"github.com/containerd/containerd"
|
|
rootfsapi "github.com/containerd/containerd/api/services/rootfs"
|
|
containerd_v1_types "github.com/containerd/containerd/api/types/mount"
|
|
"github.com/containerd/containerd/content"
|
|
"github.com/containerd/containerd/log"
|
|
"github.com/containerd/containerd/plugin"
|
|
"github.com/containerd/containerd/rootfs"
|
|
"github.com/containerd/containerd/snapshot"
|
|
digest "github.com/opencontainers/go-digest"
|
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"golang.org/x/net/context"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/codes"
|
|
)
|
|
|
|
func init() {
|
|
plugin.Register("rootfs-grpc", &plugin.Registration{
|
|
Type: plugin.GRPCPlugin,
|
|
Init: func(ic *plugin.InitContext) (interface{}, error) {
|
|
return NewService(ic.Content, ic.Snapshotter)
|
|
},
|
|
})
|
|
}
|
|
|
|
type Service struct {
|
|
store *content.Store
|
|
snapshotter snapshot.Snapshotter
|
|
}
|
|
|
|
func NewService(store *content.Store, snapshotter snapshot.Snapshotter) (*Service, error) {
|
|
return &Service{
|
|
store: store,
|
|
snapshotter: snapshotter,
|
|
}, nil
|
|
}
|
|
|
|
func (s *Service) Register(gs *grpc.Server) error {
|
|
rootfsapi.RegisterRootFSServer(gs, s)
|
|
return nil
|
|
}
|
|
|
|
func (s *Service) Unpack(ctx context.Context, pr *rootfsapi.UnpackRequest) (*rootfsapi.UnpackResponse, error) {
|
|
layers := make([]ocispec.Descriptor, len(pr.Layers))
|
|
for i, l := range pr.Layers {
|
|
layers[i] = ocispec.Descriptor{
|
|
MediaType: l.MediaType,
|
|
Digest: l.Digest,
|
|
Size: l.Size_,
|
|
}
|
|
}
|
|
log.G(ctx).Infof("Preparing %#v", layers)
|
|
chainID, err := rootfs.Prepare(ctx, s.snapshotter, mounter{}, layers, s.store.Reader, emptyResolver, noopRegister)
|
|
if err != nil {
|
|
log.G(ctx).Errorf("Rootfs Prepare failed!: %v", err)
|
|
return nil, err
|
|
}
|
|
log.G(ctx).Infof("ChainID %#v", chainID)
|
|
return &rootfsapi.UnpackResponse{
|
|
ChainID: chainID,
|
|
}, nil
|
|
}
|
|
|
|
func (s *Service) Prepare(ctx context.Context, ir *rootfsapi.PrepareRequest) (*rootfsapi.MountResponse, error) {
|
|
mounts, err := rootfs.InitRootFS(ctx, ir.Name, ir.ChainID, ir.Readonly, s.snapshotter, mounter{})
|
|
if err != nil {
|
|
return nil, grpc.Errorf(codes.AlreadyExists, "%v", err)
|
|
}
|
|
return &rootfsapi.MountResponse{
|
|
Mounts: apiMounts(mounts),
|
|
}, nil
|
|
}
|
|
|
|
func (s *Service) Mounts(ctx context.Context, mr *rootfsapi.MountsRequest) (*rootfsapi.MountResponse, error) {
|
|
mounts, err := s.snapshotter.Mounts(ctx, mr.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &rootfsapi.MountResponse{
|
|
Mounts: apiMounts(mounts),
|
|
}, nil
|
|
}
|
|
|
|
func apiMounts(mounts []containerd.Mount) []*containerd_v1_types.Mount {
|
|
am := make([]*containerd_v1_types.Mount, len(mounts))
|
|
for i, m := range mounts {
|
|
am[i] = &containerd_v1_types.Mount{
|
|
Type: m.Type,
|
|
Source: m.Source,
|
|
Options: m.Options,
|
|
}
|
|
}
|
|
return am
|
|
}
|
|
|
|
type mounter struct{}
|
|
|
|
func (mounter) Mount(dir string, mounts ...containerd.Mount) error {
|
|
return containerd.MountAll(mounts, dir)
|
|
}
|
|
|
|
func (mounter) Unmount(dir string) error {
|
|
return syscall.Unmount(dir, 0)
|
|
}
|
|
|
|
func emptyResolver(digest.Digest) digest.Digest {
|
|
return digest.Digest("")
|
|
}
|
|
|
|
func noopRegister(digest.Digest, digest.Digest) error {
|
|
return nil
|
|
}
|