diff --git a/Makefile b/Makefile index 6a46b2272..c9b46abaf 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ VERSION=$(shell git describe --match 'v[0-9]*' --dirty='.m' --always --tags) REVISION=$(shell git rev-parse HEAD)$(shell if ! git diff --no-ext-diff --quiet --exit-code; then echo .m; fi) GO_LD_FLAGS=-ldflags '-s -w -X $(PKG)/version.Version=$(VERSION) -X $(PKG)/version.Revision=$(REVISION) $(GO_EXTRA_LDFLAGS)' -CMD=containerd-stargz-grpc ctr-remote stargz-store +CMD=containerd-stargz-grpc ctr-remote stargz-store fuse-manager CMD_BINARIES=$(addprefix $(PREFIX),$(CMD)) @@ -44,6 +44,9 @@ ctr-remote: FORCE stargz-store: FORCE GO111MODULE=$(GO111MODULE_VALUE) go build -o $(PREFIX)$@ $(GO_BUILD_FLAGS) $(GO_LD_FLAGS) -v ./cmd/stargz-store +fuse-manager: FORCE + GO111MODULE=$(GO111MODULE_VALUE) go build -o $(PREFIX)$@ $(GO_BUILD_FLAGS) $(GO_LD_FLAGS) -v ./cmd/fuse-manager + check: @echo "$@" @GO111MODULE=$(GO111MODULE_VALUE) golangci-lint run diff --git a/cmd/containerd-stargz-grpc/main.go b/cmd/containerd-stargz-grpc/main.go index 0652dcd47..467f0c98c 100644 --- a/cmd/containerd-stargz-grpc/main.go +++ b/cmd/containerd-stargz-grpc/main.go @@ -32,7 +32,9 @@ import ( "github.com/containerd/containerd/contrib/snapshotservice" "github.com/containerd/containerd/log" "github.com/containerd/containerd/snapshots" + "github.com/containerd/stargz-snapshotter/fusemanager" "github.com/containerd/stargz-snapshotter/service" + snbase "github.com/containerd/stargz-snapshotter/snapshot" "github.com/containerd/stargz-snapshotter/version" sddaemon "github.com/coreos/go-systemd/v22/daemon" metrics "github.com/docker/go-metrics" @@ -43,18 +45,21 @@ import ( ) const ( - defaultAddress = "/run/containerd-stargz-grpc/containerd-stargz-grpc.sock" - defaultConfigPath = "/etc/containerd-stargz-grpc/config.toml" - defaultLogLevel = logrus.InfoLevel - defaultRootDir = "/var/lib/containerd-stargz-grpc" + defaultAddress = "/run/containerd-stargz-grpc/containerd-stargz-grpc.sock" + defaultConfigPath = "/etc/containerd-stargz-grpc/config.toml" + defaultLogLevel = logrus.InfoLevel + defaultRootDir = "/var/lib/containerd-stargz-grpc" + defaultFuseManagerAddr = "unix:///run/containerd-stargz-grpc/fuse-manager.sock" ) var ( - address = flag.String("address", defaultAddress, "address for the snapshotter's GRPC server") - configPath = flag.String("config", defaultConfigPath, "path to the configuration file") - logLevel = flag.String("log-level", defaultLogLevel.String(), "set the logging level [trace, debug, info, warn, error, fatal, panic]") - rootDir = flag.String("root", defaultRootDir, "path to the root directory for this snapshotter") - printVersion = flag.Bool("version", false, "print the version") + address = flag.String("address", defaultAddress, "address for the snapshotter's GRPC server") + configPath = flag.String("config", defaultConfigPath, "path to the configuration file") + logLevel = flag.String("log-level", defaultLogLevel.String(), "set the logging level [trace, debug, info, warn, error, fatal, panic]") + rootDir = flag.String("root", defaultRootDir, "path to the root directory for this snapshotter") + fuseManagerAddr = flag.String("fusemanager-address", defaultFuseManagerAddr, "address for the fuse manager's GRPC server") + enableFuseManager = flag.Bool("enable-fusemanager", false, "whether enable fuse manager") + printVersion = flag.Bool("version", false, "print the version") ) type snapshotterConfig struct { @@ -97,9 +102,21 @@ func main() { log.G(ctx).WithError(err).Fatalf("snapshotter is not supported") } - rs, err := service.NewStargzSnapshotterService(ctx, *rootDir, &config.Config) - if err != nil { - log.G(ctx).WithError(err).Fatalf("failed to configure snapshotter") + var rs snapshots.Snapshotter + if *enableFuseManager { + fs, err := fusemanager.NewManagerClient(ctx, *rootDir, *fuseManagerAddr, &config.Config) + if err != nil { + log.G(ctx).WithError(err).Fatalf("failed to configure fuse manager") + } + rs, err = snbase.NewSnapshotter(ctx, *rootDir, fs, snbase.AsynchronousRemove) + if err != nil { + log.G(ctx).WithError(err).Fatalf("failed to configure snapshotter") + } + } else { + rs, err = service.NewStargzSnapshotterService(ctx, *rootDir, &config.Config) + if err != nil { + log.G(ctx).WithError(err).Fatalf("failed to configure snapshotter") + } } cleanup, err := serve(ctx, *address, rs, config) diff --git a/cmd/fuse-manager/main.go b/cmd/fuse-manager/main.go new file mode 100644 index 000000000..4e181907d --- /dev/null +++ b/cmd/fuse-manager/main.go @@ -0,0 +1,128 @@ +/* + 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 main + +import ( + "context" + "flag" + golog "log" + "net" + "os" + "os/signal" + "path/filepath" + + "github.com/containerd/containerd/log" + sddaemon "github.com/coreos/go-systemd/v22/daemon" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" + "google.golang.org/grpc" + + "github.com/containerd/stargz-snapshotter/fusemanager" + pb "github.com/containerd/stargz-snapshotter/fusemanager/api" +) + +const ( + defaultStoreAddr = "/var/lib/containerd-stargz-grpc/fusestore.db" + defaultSockerAddr = "/run/containerd-stargz-grpc/fuse-manager.sock" + defaultLogLevel = logrus.InfoLevel +) + +var ( + fuseStoreAddr = flag.String("fusestore", defaultStoreAddr, "address for the fusemanager's store") + socketAddr = flag.String("socket", defaultSockerAddr, "address for the fusemanager's gRPC socket") + logLevel = flag.String("log-level", defaultLogLevel.String(), "set the logging level [trace, debug, info, warn, error, fatal, panic]") +) + +func main() { + flag.Parse() + + lvl, err := logrus.ParseLevel(*logLevel) + if err != nil { + log.L.WithError(err).Fatal("failed to prepare logger") + } + + logrus.SetLevel(lvl) + logrus.SetFormatter(&logrus.JSONFormatter{ + TimestampFormat: log.RFC3339NanoFixed, + }) + + ctx := log.WithLogger(context.Background(), log.L) + + golog.SetOutput(log.G(ctx).WriterLevel(logrus.DebugLevel)) + + sigCh := make(chan os.Signal, 1) + errCh := make(chan error, 1) + server := grpc.NewServer() + + signal.Notify(sigCh, unix.SIGINT, unix.SIGTERM) + go func() { + select { + case sig := <-sigCh: + log.G(ctx).Infof("Got %v", sig) + server.Stop() + case err := <-errCh: + log.G(ctx).WithError(err).Fatal("failed to run stargz fuse manager") + } + }() + + // Prepare the directory for the socket + if err := os.MkdirAll(filepath.Dir(*socketAddr), 0700); err != nil { + log.G(ctx).WithError(err).Errorf("failed to create directory %s", filepath.Dir(*socketAddr)) + errCh <- &net.DNSConfigError{} + } + + // Try to remove the socket file to avoid EADDRINUSE + if err := os.Remove(*socketAddr); err != nil && !os.IsNotExist(err) { + log.G(ctx).WithError(err).Error("failed to remove old socket file") + errCh <- err + } + + l, err := net.Listen("unix", *socketAddr) + if err != nil { + log.G(ctx).WithError(err).Error("failed to listen socket") + errCh <- err + } + + if os.Getenv("NOTIFY_SOCKET") != "" { + notified, notifyErr := sddaemon.SdNotify(false, sddaemon.SdNotifyReady) + log.G(ctx).Debugf("SdNotifyReady notified=%v, err=%v", notified, notifyErr) + } + defer func() { + if os.Getenv("NOTIFY_SOCKET") != "" { + notified, notifyErr := sddaemon.SdNotify(false, sddaemon.SdNotifyStopping) + log.G(ctx).Debugf("SdNotifyStopping notified=%v, err=%v", notified, notifyErr) + } + }() + + fm, err := fusemanager.NewFuseManager(ctx, *fuseStoreAddr) + if err != nil { + log.G(ctx).WithError(err).Error("failed to configure manager server") + errCh <- err + } + + pb.RegisterFileSystemServiceServer(server, fm) + + if err := server.Serve(l); err != nil { + log.G(ctx).WithError(err).Error("failed to serve fuse manager") + errCh <- err + } + + err = fm.Close(ctx) + if err != nil { + log.G(ctx).WithError(err).Fatal("failed to close fusemanager") + } +} diff --git a/fs/fs.go b/fs/fs.go index f6819423d..430819b9b 100644 --- a/fs/fs.go +++ b/fs/fs.go @@ -63,6 +63,13 @@ import ( const defaultMaxConcurrency = 2 +var ( + nsLock = sync.Mutex{} + + ns *metrics.Namespace + metricsCtr *fsmetrics.Controller +) + type Option func(*options) type options struct { @@ -95,13 +102,19 @@ func NewFilesystem(root string, cfg config.Config, opts ...Option) (_ snapshot.F return nil, errors.Wrapf(err, "failed to setup resolver") } - var ns *metrics.Namespace - if !cfg.NoPrometheus { - ns = metrics.NewNamespace("stargz", "fs", nil) - } - c := fsmetrics.NewLayerMetrics(ns) - if ns != nil { - metrics.Register(ns) + nsLock.Lock() + defer nsLock.Unlock() + + var c *fsmetrics.Controller + if cfg.NoPrometheus { + c = fsmetrics.NewLayerMetrics(nil) + } else { + if ns == nil { + ns = metrics.NewNamespace("stargz", "fs", nil) + metrics.Register(ns) + metricsCtr = fsmetrics.NewLayerMetrics(ns) + } + c = metricsCtr } return &filesystem{ diff --git a/fusemanager/api/api.pb.go b/fusemanager/api/api.pb.go new file mode 100644 index 000000000..3a341da0e --- /dev/null +++ b/fusemanager/api/api.pb.go @@ -0,0 +1,1582 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: api.proto + +package api + +import ( + context "context" + fmt "fmt" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type InitRequest struct { + Root string `protobuf:"bytes,1,opt,name=root,proto3" json:"root,omitempty"` + Config []byte `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *InitRequest) Reset() { *m = InitRequest{} } +func (m *InitRequest) String() string { return proto.CompactTextString(m) } +func (*InitRequest) ProtoMessage() {} +func (*InitRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{0} +} +func (m *InitRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *InitRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_InitRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *InitRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_InitRequest.Merge(m, src) +} +func (m *InitRequest) XXX_Size() int { + return m.Size() +} +func (m *InitRequest) XXX_DiscardUnknown() { + xxx_messageInfo_InitRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_InitRequest proto.InternalMessageInfo + +func (m *InitRequest) GetRoot() string { + if m != nil { + return m.Root + } + return "" +} + +func (m *InitRequest) GetConfig() []byte { + if m != nil { + return m.Config + } + return nil +} + +type MountRequest struct { + Mountpoint string `protobuf:"bytes,1,opt,name=mountpoint,proto3" json:"mountpoint,omitempty"` + Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MountRequest) Reset() { *m = MountRequest{} } +func (m *MountRequest) String() string { return proto.CompactTextString(m) } +func (*MountRequest) ProtoMessage() {} +func (*MountRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{1} +} +func (m *MountRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MountRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MountRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MountRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MountRequest.Merge(m, src) +} +func (m *MountRequest) XXX_Size() int { + return m.Size() +} +func (m *MountRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MountRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MountRequest proto.InternalMessageInfo + +func (m *MountRequest) GetMountpoint() string { + if m != nil { + return m.Mountpoint + } + return "" +} + +func (m *MountRequest) GetLabels() map[string]string { + if m != nil { + return m.Labels + } + return nil +} + +type CheckRequest struct { + Mountpoint string `protobuf:"bytes,1,opt,name=mountpoint,proto3" json:"mountpoint,omitempty"` + Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CheckRequest) Reset() { *m = CheckRequest{} } +func (m *CheckRequest) String() string { return proto.CompactTextString(m) } +func (*CheckRequest) ProtoMessage() {} +func (*CheckRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{2} +} +func (m *CheckRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CheckRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CheckRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CheckRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CheckRequest.Merge(m, src) +} +func (m *CheckRequest) XXX_Size() int { + return m.Size() +} +func (m *CheckRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CheckRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_CheckRequest proto.InternalMessageInfo + +func (m *CheckRequest) GetMountpoint() string { + if m != nil { + return m.Mountpoint + } + return "" +} + +func (m *CheckRequest) GetLabels() map[string]string { + if m != nil { + return m.Labels + } + return nil +} + +type UnmountRequest struct { + Mountpoint string `protobuf:"bytes,1,opt,name=mountpoint,proto3" json:"mountpoint,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UnmountRequest) Reset() { *m = UnmountRequest{} } +func (m *UnmountRequest) String() string { return proto.CompactTextString(m) } +func (*UnmountRequest) ProtoMessage() {} +func (*UnmountRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{3} +} +func (m *UnmountRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UnmountRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UnmountRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UnmountRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UnmountRequest.Merge(m, src) +} +func (m *UnmountRequest) XXX_Size() int { + return m.Size() +} +func (m *UnmountRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UnmountRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_UnmountRequest proto.InternalMessageInfo + +func (m *UnmountRequest) GetMountpoint() string { + if m != nil { + return m.Mountpoint + } + return "" +} + +type Response struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Response) Reset() { *m = Response{} } +func (m *Response) String() string { return proto.CompactTextString(m) } +func (*Response) ProtoMessage() {} +func (*Response) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{4} +} +func (m *Response) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Response.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Response) XXX_Merge(src proto.Message) { + xxx_messageInfo_Response.Merge(m, src) +} +func (m *Response) XXX_Size() int { + return m.Size() +} +func (m *Response) XXX_DiscardUnknown() { + xxx_messageInfo_Response.DiscardUnknown(m) +} + +var xxx_messageInfo_Response proto.InternalMessageInfo + +func init() { + proto.RegisterType((*InitRequest)(nil), "stargz_fuse_manager.InitRequest") + proto.RegisterType((*MountRequest)(nil), "stargz_fuse_manager.MountRequest") + proto.RegisterMapType((map[string]string)(nil), "stargz_fuse_manager.MountRequest.LabelsEntry") + proto.RegisterType((*CheckRequest)(nil), "stargz_fuse_manager.CheckRequest") + proto.RegisterMapType((map[string]string)(nil), "stargz_fuse_manager.CheckRequest.LabelsEntry") + proto.RegisterType((*UnmountRequest)(nil), "stargz_fuse_manager.UnmountRequest") + proto.RegisterType((*Response)(nil), "stargz_fuse_manager.Response") +} + +func init() { proto.RegisterFile("api.proto", fileDescriptor_00212fb1f9d3bf1c) } + +var fileDescriptor_00212fb1f9d3bf1c = []byte{ + // 367 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x93, 0xcf, 0x4e, 0xea, 0x40, + 0x14, 0xc6, 0xef, 0x94, 0x3f, 0xf7, 0x72, 0x20, 0x37, 0x3a, 0x1a, 0x43, 0x48, 0x6c, 0x2a, 0x6e, + 0x58, 0x48, 0x31, 0xb8, 0x11, 0x97, 0x1a, 0x34, 0x24, 0xb2, 0x29, 0x71, 0xe3, 0x86, 0x0c, 0xf5, + 0x00, 0x0d, 0xed, 0x4c, 0xed, 0x4c, 0x49, 0xf0, 0x49, 0x7c, 0x0c, 0x5f, 0xc1, 0x9d, 0x4b, 0x1f, + 0xc1, 0xe0, 0x8b, 0x18, 0x4a, 0x31, 0x35, 0xa9, 0x41, 0x13, 0x76, 0xe7, 0x9c, 0x7c, 0xe7, 0xd7, + 0xaf, 0xdf, 0x69, 0xa1, 0xc0, 0x7c, 0xc7, 0xf4, 0x03, 0xa1, 0x04, 0xdd, 0x91, 0x8a, 0x05, 0xa3, + 0x87, 0xfe, 0x30, 0x94, 0xd8, 0xf7, 0x18, 0x67, 0x23, 0x0c, 0xaa, 0x2d, 0x28, 0x76, 0xb8, 0xa3, + 0x2c, 0xbc, 0x0f, 0x51, 0x2a, 0x4a, 0x21, 0x1b, 0x08, 0xa1, 0xca, 0xc4, 0x20, 0xb5, 0x82, 0x15, + 0xd5, 0x74, 0x0f, 0xf2, 0xb6, 0xe0, 0x43, 0x67, 0x54, 0xd6, 0x0c, 0x52, 0x2b, 0x59, 0x71, 0x57, + 0x7d, 0x22, 0x50, 0xea, 0x8a, 0x90, 0x7f, 0x2e, 0xeb, 0x00, 0xde, 0xa2, 0xf7, 0x85, 0xc3, 0x57, + 0x88, 0xc4, 0x84, 0xb6, 0x21, 0xef, 0xb2, 0x01, 0xba, 0xb2, 0xac, 0x19, 0x99, 0x5a, 0xb1, 0x59, + 0x37, 0x53, 0x1c, 0x99, 0x49, 0xa4, 0x79, 0x1d, 0xe9, 0xdb, 0x5c, 0x05, 0x33, 0x2b, 0x5e, 0xae, + 0xb4, 0xa0, 0x98, 0x18, 0xd3, 0x2d, 0xc8, 0x4c, 0x70, 0x16, 0x3f, 0x6e, 0x51, 0xd2, 0x5d, 0xc8, + 0x4d, 0x99, 0x1b, 0x62, 0xe4, 0xb7, 0x60, 0x2d, 0x9b, 0x33, 0xed, 0x94, 0x44, 0x96, 0x2f, 0xc6, + 0x68, 0x4f, 0x36, 0x6b, 0x39, 0x89, 0xdc, 0xb4, 0xe5, 0x63, 0xf8, 0x7f, 0xc3, 0xbd, 0x5f, 0xc4, + 0x5c, 0x05, 0xf8, 0x67, 0xa1, 0xf4, 0x05, 0x97, 0xd8, 0x7c, 0xd6, 0x60, 0xfb, 0xd2, 0x71, 0xb1, + 0x37, 0x93, 0x0a, 0xbd, 0x1e, 0x06, 0x53, 0xc7, 0x46, 0x7a, 0x05, 0xd9, 0xc5, 0xd1, 0xa9, 0x91, + 0xfa, 0x36, 0x89, 0xef, 0xa1, 0xb2, 0x9f, 0xaa, 0x58, 0xe1, 0x69, 0x07, 0x72, 0xd1, 0xb9, 0xe8, + 0xc1, 0xda, 0x53, 0xfe, 0x00, 0x15, 0xc5, 0xf8, 0x0d, 0x2a, 0x19, 0xf1, 0x3a, 0x54, 0x17, 0xfe, + 0xc6, 0x91, 0xd1, 0xc3, 0x54, 0xe5, 0xd7, 0x40, 0xd7, 0xe0, 0xce, 0x8f, 0x5e, 0xe6, 0x3a, 0x79, + 0x9d, 0xeb, 0xe4, 0x6d, 0xae, 0x93, 0xc7, 0x77, 0xfd, 0xcf, 0x6d, 0x65, 0xa9, 0xaf, 0x4b, 0xce, + 0x7c, 0x39, 0x16, 0x4a, 0x61, 0xd0, 0xb0, 0xbd, 0xbb, 0x06, 0xf3, 0x9d, 0x41, 0x3e, 0xfa, 0xd9, + 0x4e, 0x3e, 0x02, 0x00, 0x00, 0xff, 0xff, 0x3d, 0x7d, 0x98, 0x22, 0x79, 0x03, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// FileSystemServiceClient is the client API for FileSystemService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type FileSystemServiceClient interface { + Init(ctx context.Context, in *InitRequest, opts ...grpc.CallOption) (*Response, error) + Mount(ctx context.Context, in *MountRequest, opts ...grpc.CallOption) (*Response, error) + Check(ctx context.Context, in *CheckRequest, opts ...grpc.CallOption) (*Response, error) + Unmount(ctx context.Context, in *UnmountRequest, opts ...grpc.CallOption) (*Response, error) +} + +type fileSystemServiceClient struct { + cc *grpc.ClientConn +} + +func NewFileSystemServiceClient(cc *grpc.ClientConn) FileSystemServiceClient { + return &fileSystemServiceClient{cc} +} + +func (c *fileSystemServiceClient) Init(ctx context.Context, in *InitRequest, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/stargz_fuse_manager.FileSystemService/Init", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fileSystemServiceClient) Mount(ctx context.Context, in *MountRequest, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/stargz_fuse_manager.FileSystemService/Mount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fileSystemServiceClient) Check(ctx context.Context, in *CheckRequest, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/stargz_fuse_manager.FileSystemService/Check", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fileSystemServiceClient) Unmount(ctx context.Context, in *UnmountRequest, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/stargz_fuse_manager.FileSystemService/Unmount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// FileSystemServiceServer is the server API for FileSystemService service. +type FileSystemServiceServer interface { + Init(context.Context, *InitRequest) (*Response, error) + Mount(context.Context, *MountRequest) (*Response, error) + Check(context.Context, *CheckRequest) (*Response, error) + Unmount(context.Context, *UnmountRequest) (*Response, error) +} + +// UnimplementedFileSystemServiceServer can be embedded to have forward compatible implementations. +type UnimplementedFileSystemServiceServer struct { +} + +func (*UnimplementedFileSystemServiceServer) Init(ctx context.Context, req *InitRequest) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method Init not implemented") +} +func (*UnimplementedFileSystemServiceServer) Mount(ctx context.Context, req *MountRequest) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method Mount not implemented") +} +func (*UnimplementedFileSystemServiceServer) Check(ctx context.Context, req *CheckRequest) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method Check not implemented") +} +func (*UnimplementedFileSystemServiceServer) Unmount(ctx context.Context, req *UnmountRequest) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method Unmount not implemented") +} + +func RegisterFileSystemServiceServer(s *grpc.Server, srv FileSystemServiceServer) { + s.RegisterService(&_FileSystemService_serviceDesc, srv) +} + +func _FileSystemService_Init_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InitRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FileSystemServiceServer).Init(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/stargz_fuse_manager.FileSystemService/Init", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FileSystemServiceServer).Init(ctx, req.(*InitRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FileSystemService_Mount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FileSystemServiceServer).Mount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/stargz_fuse_manager.FileSystemService/Mount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FileSystemServiceServer).Mount(ctx, req.(*MountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FileSystemService_Check_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CheckRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FileSystemServiceServer).Check(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/stargz_fuse_manager.FileSystemService/Check", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FileSystemServiceServer).Check(ctx, req.(*CheckRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FileSystemService_Unmount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UnmountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FileSystemServiceServer).Unmount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/stargz_fuse_manager.FileSystemService/Unmount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FileSystemServiceServer).Unmount(ctx, req.(*UnmountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _FileSystemService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "stargz_fuse_manager.FileSystemService", + HandlerType: (*FileSystemServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Init", + Handler: _FileSystemService_Init_Handler, + }, + { + MethodName: "Mount", + Handler: _FileSystemService_Mount_Handler, + }, + { + MethodName: "Check", + Handler: _FileSystemService_Check_Handler, + }, + { + MethodName: "Unmount", + Handler: _FileSystemService_Unmount_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "api.proto", +} + +func (m *InitRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *InitRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *InitRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Config) > 0 { + i -= len(m.Config) + copy(dAtA[i:], m.Config) + i = encodeVarintApi(dAtA, i, uint64(len(m.Config))) + i-- + dAtA[i] = 0x12 + } + if len(m.Root) > 0 { + i -= len(m.Root) + copy(dAtA[i:], m.Root) + i = encodeVarintApi(dAtA, i, uint64(len(m.Root))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MountRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MountRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MountRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Labels) > 0 { + for k := range m.Labels { + v := m.Labels[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintApi(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintApi(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintApi(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Mountpoint) > 0 { + i -= len(m.Mountpoint) + copy(dAtA[i:], m.Mountpoint) + i = encodeVarintApi(dAtA, i, uint64(len(m.Mountpoint))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CheckRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CheckRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CheckRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Labels) > 0 { + for k := range m.Labels { + v := m.Labels[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintApi(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintApi(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintApi(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Mountpoint) > 0 { + i -= len(m.Mountpoint) + copy(dAtA[i:], m.Mountpoint) + i = encodeVarintApi(dAtA, i, uint64(len(m.Mountpoint))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UnmountRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UnmountRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UnmountRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Mountpoint) > 0 { + i -= len(m.Mountpoint) + copy(dAtA[i:], m.Mountpoint) + i = encodeVarintApi(dAtA, i, uint64(len(m.Mountpoint))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Response) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Response) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Response) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + return len(dAtA) - i, nil +} + +func encodeVarintApi(dAtA []byte, offset int, v uint64) int { + offset -= sovApi(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *InitRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Root) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + l = len(m.Config) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *MountRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Mountpoint) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovApi(uint64(len(k))) + 1 + len(v) + sovApi(uint64(len(v))) + n += mapEntrySize + 1 + sovApi(uint64(mapEntrySize)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *CheckRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Mountpoint) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovApi(uint64(len(k))) + 1 + len(v) + sovApi(uint64(len(v))) + n += mapEntrySize + 1 + sovApi(uint64(mapEntrySize)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *UnmountRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Mountpoint) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *Response) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovApi(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozApi(x uint64) (n int) { + return sovApi(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *InitRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: InitRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: InitRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Root", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Root = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Config = append(m.Config[:0], dAtA[iNdEx:postIndex]...) + if m.Config == nil { + m.Config = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MountRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MountRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MountRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Mountpoint", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Mountpoint = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Labels == nil { + m.Labels = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthApi + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthApi + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthApi + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthApi + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Labels[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CheckRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CheckRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CheckRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Mountpoint", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Mountpoint = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Labels == nil { + m.Labels = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthApi + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthApi + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthApi + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthApi + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Labels[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UnmountRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UnmountRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UnmountRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Mountpoint", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Mountpoint = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Response) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Response: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Response: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipApi(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthApi + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupApi + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthApi + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthApi = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowApi = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupApi = fmt.Errorf("proto: unexpected end of group") +) diff --git a/fusemanager/api/api.proto b/fusemanager/api/api.proto new file mode 100644 index 000000000..61b6f5a5d --- /dev/null +++ b/fusemanager/api/api.proto @@ -0,0 +1,34 @@ +syntax = "proto3"; + +option go_package = "stargz-snapshotter/fusemanager/api"; + +package stargz_fuse_manager; + +service FileSystemService { + rpc Init (InitRequest) returns (Response); + rpc Mount (MountRequest) returns (Response); + rpc Check (CheckRequest) returns (Response); + rpc Unmount (UnmountRequest) returns (Response); +} + +message InitRequest { + string root = 1; + bytes config = 2; +} + +message MountRequest { + string mountpoint = 1; + map labels = 2; +} + +message CheckRequest { + string mountpoint = 1; + map labels = 2; +} + +message UnmountRequest { + string mountpoint = 1; +} + +message Response { +} \ No newline at end of file diff --git a/fusemanager/client.go b/fusemanager/client.go new file mode 100644 index 000000000..85266fc03 --- /dev/null +++ b/fusemanager/client.go @@ -0,0 +1,121 @@ +/* + 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. +*/ + +/* + Copyright 2019 The Go Authors. All rights reserved. + Use of this source code is governed by a BSD-style + license that can be found in the NOTICE.md file. +*/ + +package fusemanager + +import ( + "context" + "encoding/json" + + "github.com/containerd/containerd/log" + "google.golang.org/grpc" + + pb "github.com/containerd/stargz-snapshotter/fusemanager/api" + "github.com/containerd/stargz-snapshotter/service" + "github.com/containerd/stargz-snapshotter/snapshot" +) + +type Client struct { + client pb.FileSystemServiceClient +} + +func NewManagerClient(ctx context.Context, root, socket string, config *service.Config) (snapshot.FileSystem, error) { + conn, err := grpc.Dial(socket, grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + return nil, err + } + + cli := &Client{ + client: pb.NewFileSystemServiceClient(conn), + } + + err = cli.Init(ctx, root, config) + if err != nil { + return nil, err + } + + return cli, nil +} + +func (cli *Client) Init(ctx context.Context, root string, config *service.Config) error { + configBytes, err := json.Marshal(config) + if err != nil { + return err + } + + req := &pb.InitRequest{ + Root: root, + Config: configBytes, + } + + _, err = cli.client.Init(ctx, req) + if err != nil { + log.G(ctx).WithError(err).Errorf("failed to call Init") + return err + } + + return nil +} + +func (cli *Client) Mount(ctx context.Context, mountpoint string, labels map[string]string) error { + req := &pb.MountRequest{ + Mountpoint: mountpoint, + Labels: labels, + } + + _, err := cli.client.Mount(ctx, req) + if err != nil { + log.G(ctx).WithError(err).Errorf("failed to call Mount") + return err + } + + return nil +} + +func (cli *Client) Check(ctx context.Context, mountpoint string, labels map[string]string) error { + req := &pb.CheckRequest{ + Mountpoint: mountpoint, + Labels: labels, + } + + _, err := cli.client.Check(ctx, req) + if err != nil { + log.G(ctx).WithError(err).Errorf("failed to call Check") + return err + } + + return nil +} + +func (cli *Client) Unmount(ctx context.Context, mountpoint string) error { + req := &pb.UnmountRequest{ + Mountpoint: mountpoint, + } + + _, err := cli.client.Unmount(ctx, req) + if err != nil { + log.G(ctx).WithError(err).Errorf("failed to call Unmount") + return err + } + + return nil +} diff --git a/fusemanager/fusemanager.go b/fusemanager/fusemanager.go new file mode 100644 index 000000000..a9d588004 --- /dev/null +++ b/fusemanager/fusemanager.go @@ -0,0 +1,257 @@ +/* + 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. +*/ + +/* + Copyright 2019 The Go Authors. All rights reserved. + Use of this source code is governed by a BSD-style + license that can be found in the NOTICE.md file. +*/ + +package fusemanager + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "sync" + "syscall" + "time" + + "github.com/containerd/containerd/log" + "github.com/moby/sys/mountinfo" + "github.com/pkg/errors" + bolt "go.etcd.io/bbolt" + + pb "github.com/containerd/stargz-snapshotter/fusemanager/api" + "github.com/containerd/stargz-snapshotter/service" + "github.com/containerd/stargz-snapshotter/snapshot" +) + +const ( + fuseManagerReady = iota + fuseManagerNotReady +) + +type Server struct { + pb.UnimplementedFileSystemServiceServer + + lock sync.RWMutex + ready int32 + + // root is the latest root passed from containerd-stargz-grpc + root string + // config is the latest config passed from containerd-stargz-grpc + config *service.Config + // fsMap stores all filesystems from various configs + fsMap sync.Map + // curFs is filesystem created by latest config + curFs snapshot.FileSystem + ms *bolt.DB +} + +func NewFuseManager(ctx context.Context, fuseStoreAddr string) (*Server, error) { + if err := os.MkdirAll(filepath.Dir(fuseStoreAddr), 0700); err != nil { + return nil, errors.Wrapf(err, "failed to create directory %q", filepath.Dir(fuseStoreAddr)) + } + + db, err := bolt.Open(fuseStoreAddr, 0666, &bolt.Options{Timeout: 10 * time.Second, ReadOnly: false}) + if err != nil { + return nil, errors.Wrap(err, "failed to configure fusestore") + } + + fm := &Server{ + ready: fuseManagerNotReady, + lock: sync.RWMutex{}, + fsMap: sync.Map{}, + ms: db, + } + + return fm, nil +} + +func (fm *Server) Init(ctx context.Context, req *pb.InitRequest) (*pb.Response, error) { + fm.lock.Lock() + fm.ready = fuseManagerNotReady + defer func() { + fm.ready = fuseManagerReady + fm.lock.Unlock() + }() + + ctx = log.WithLogger(ctx, log.G(ctx)) + + config := &service.Config{} + err := json.Unmarshal(req.Config, config) + if err != nil { + log.G(ctx).WithError(err).Errorf("failed to get config") + return &pb.Response{}, err + } + fm.root = req.Root + fm.config = config + + fs, err := service.NewFileSystem(ctx, fm.root, fm.config) + if err != nil { + return &pb.Response{}, err + } + fm.curFs = fs + + err = fm.restoreFuseInfo(ctx) + if err != nil { + log.G(ctx).WithError(err).Errorf("failed to restore fuse info") + return &pb.Response{}, err + } + + return &pb.Response{}, nil +} + +func (fm *Server) Mount(ctx context.Context, req *pb.MountRequest) (*pb.Response, error) { + fm.lock.RLock() + defer fm.lock.RUnlock() + if fm.ready != fuseManagerReady { + return &pb.Response{}, fmt.Errorf("fuse manager not ready") + } + + ctx = log.WithLogger(ctx, log.G(ctx).WithField("mountpoint", req.Mountpoint)) + + err := fm.mount(ctx, req.Mountpoint, req.Labels) + if err != nil { + log.G(ctx).WithError(err).Errorf("failed to mount stargz") + return &pb.Response{}, err + } + + fm.storeFuseInfo(&fuseInfo{ + Root: fm.root, + Mountpoint: req.Mountpoint, + Labels: req.Labels, + Config: *fm.config, + }) + + return &pb.Response{}, nil +} + +func (fm *Server) Check(ctx context.Context, req *pb.CheckRequest) (*pb.Response, error) { + fm.lock.RLock() + defer fm.lock.RUnlock() + if fm.ready != fuseManagerReady { + return &pb.Response{}, fmt.Errorf("fuse manager not ready") + } + + ctx = log.WithLogger(ctx, log.G(ctx).WithField("mountpoint", req.Mountpoint)) + + obj, found := fm.fsMap.Load(req.Mountpoint) + if !found { + err := fmt.Errorf("failed to find filesystem of mountpoint %s", req.Mountpoint) + log.G(ctx).WithError(err).Errorf("failed to check filesystem") + return &pb.Response{}, err + } + + fs := obj.(snapshot.FileSystem) + err := fs.Check(ctx, req.Mountpoint, req.Labels) + if err != nil { + log.G(ctx).WithError(err).Errorf("failed to check filesystem") + return &pb.Response{}, err + } + + return &pb.Response{}, nil +} + +func (fm *Server) Unmount(ctx context.Context, req *pb.UnmountRequest) (*pb.Response, error) { + fm.lock.RLock() + defer fm.lock.RUnlock() + if fm.ready != fuseManagerReady { + return &pb.Response{}, fmt.Errorf("fuse manager not ready") + } + + ctx = log.WithLogger(ctx, log.G(ctx).WithField("mountpoint", req.Mountpoint)) + + obj, found := fm.fsMap.Load(req.Mountpoint) + if !found { + // check whether already unmounted + mounts, err := mountinfo.GetMounts(func(info *mountinfo.Info) (skip, stop bool) { + if info.Mountpoint == req.Mountpoint { + return false, true + } + return true, false + }) + if err != nil { + log.G(ctx).WithError(err).Errorf("failed to get mount info") + return &pb.Response{}, err + } + + if len(mounts) <= 0 { + return &pb.Response{}, nil + } + err = fmt.Errorf("failed to find filesystem of mountpoint %s", req.Mountpoint) + log.G(ctx).WithError(err).Errorf("failed to unmount filesystem") + return &pb.Response{}, err + } + + fs := obj.(snapshot.FileSystem) + err := fs.Unmount(ctx, req.Mountpoint) + if err != nil { + log.G(ctx).WithError(err).Errorf("failed to unmount filesystem") + return &pb.Response{}, err + } + + fm.fsMap.Delete(req.Mountpoint) + fm.removeFuseInfo(&fuseInfo{ + Mountpoint: req.Mountpoint, + }) + + return &pb.Response{}, nil +} + +func (fm *Server) Close(ctx context.Context) error { + fm.lock.Lock() + defer fm.lock.Unlock() + + mountpoints, err := fm.listMountpoints(ctx) + if err != nil { + log.G(ctx).WithError(err).Errorf("failed to list mountpoints") + return err + } + + for _, mp := range mountpoints { + if err := syscall.Unmount(mp, syscall.MNT_FORCE); err != nil { + log.G(ctx).WithError(err).Errorf("failed to unmount %s", mp) + return err + } + } + + err = fm.ms.Close() + if err != nil { + log.G(ctx).WithError(err).Errorf("failed to close fusestore") + return err + } + + return nil +} + +func (fm *Server) mount(ctx context.Context, mountpoint string, labels map[string]string) error { + if _, found := fm.fsMap.Load(mountpoint); found { + return nil + } + + err := fm.curFs.Mount(ctx, mountpoint, labels) + if err != nil { + log.G(ctx).WithError(err).Errorf("failed to mount stargz") + return err + } + + fm.fsMap.Store(mountpoint, fm.curFs) + return nil +} diff --git a/fusemanager/fusestore.go b/fusemanager/fusestore.go new file mode 100644 index 000000000..0eede6fd0 --- /dev/null +++ b/fusemanager/fusestore.go @@ -0,0 +1,127 @@ +/* + 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. +*/ + +/* + Copyright 2019 The Go Authors. All rights reserved. + Use of this source code is governed by a BSD-style + license that can be found in the NOTICE.md file. +*/ + +package fusemanager + +import ( + "context" + "encoding/json" + + bolt "go.etcd.io/bbolt" + + "github.com/containerd/stargz-snapshotter/service" +) + +var ( + fuseInfoBucket = []byte("fuse-info-bucket") +) + +type fuseInfo struct { + Root string + Mountpoint string + Labels map[string]string + Config service.Config +} + +func (fm *Server) storeFuseInfo(fuseInfo *fuseInfo) error { + return fm.ms.Update(func(tx *bolt.Tx) error { + bucket, err := tx.CreateBucketIfNotExists(fuseInfoBucket) + if err != nil { + return err + } + + key := []byte(fuseInfo.Mountpoint) + + val, err := json.Marshal(fuseInfo) + if err != nil { + return err + } + + err = bucket.Put(key, val) + if err != nil { + return err + } + + return nil + }) +} + +func (fm *Server) removeFuseInfo(fuseInfo *fuseInfo) error { + return fm.ms.Update(func(tx *bolt.Tx) error { + bucket, err := tx.CreateBucketIfNotExists(fuseInfoBucket) + if err != nil { + return err + } + + key := []byte(fuseInfo.Mountpoint) + + err = bucket.Delete(key) + if err != nil { + return err + } + + return nil + }) +} + +func (fm *Server) restoreFuseInfo(ctx context.Context) error { + return fm.ms.View(func(tx *bolt.Tx) error { + bucket := tx.Bucket(fuseInfoBucket) + if bucket == nil { + return nil + } + + return bucket.ForEach(func(_, v []byte) error { + mi := &fuseInfo{} + err := json.Unmarshal(v, mi) + if err != nil { + return err + } + + return fm.mount(ctx, mi.Mountpoint, mi.Labels) + }) + }) +} + +func (fm *Server) listMountpoints(ctx context.Context) ([]string, error) { + mountpoints := make([]string, 0) + err := fm.ms.View(func(tx *bolt.Tx) error { + bucket := tx.Bucket(fuseInfoBucket) + if bucket == nil { + return nil + } + return bucket.ForEach(func(_, v []byte) error { + mi := &fuseInfo{} + err := json.Unmarshal(v, mi) + if err != nil { + return err + } + mountpoints = append(mountpoints, mi.Mountpoint) + return nil + }) + }) + if err != nil { + return nil, err + } + + return mountpoints, nil +} diff --git a/go.mod b/go.mod index 2c6ea34e6..eb05a2b59 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/docker/docker-credential-helpers v0.6.3 // indirect github.com/docker/go-metrics v0.0.1 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e + github.com/golang/protobuf v1.4.3 github.com/hanwen/go-fuse/v2 v2.1.0 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/golang-lru v0.5.3 // indirect @@ -29,6 +30,7 @@ require ( github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.0 // indirect github.com/urfave/cli v1.22.2 + go.etcd.io/bbolt v1.3.5 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a golang.org/x/sys v0.0.0-20210324051608-47abb6519492 google.golang.org/grpc v1.35.0 diff --git a/service/plugin/plugin.go b/service/plugin/plugin.go index f77d89e07..5181c6fa7 100644 --- a/service/plugin/plugin.go +++ b/service/plugin/plugin.go @@ -21,7 +21,12 @@ import ( "github.com/containerd/containerd/platforms" ctdplugin "github.com/containerd/containerd/plugin" + "github.com/containerd/containerd/snapshots" + multierr "github.com/pkg/errors" + + "github.com/containerd/stargz-snapshotter/fusemanager" "github.com/containerd/stargz-snapshotter/service" + snbase "github.com/containerd/stargz-snapshotter/snapshot" ) // Config represents configuration for the stargz snapshotter plugin. @@ -30,6 +35,12 @@ type Config struct { // RootPath is the directory for the plugin RootPath string `toml:"root_path"` + + // FuseManagerSocketAddr is the socket address of fuse-manager + FuseManagerSocketAddr string `toml:"fusemanager_socket_addr"` + + // EnableFuseManager whether fusemanager is enabled + EnableFuseManager bool `toml:"enable_fusemanager"` } func init() { @@ -50,7 +61,26 @@ func init() { root = config.RootPath } ic.Meta.Exports["root"] = root - return service.NewStargzSnapshotterService(ic.Context, root, &config.Config) + + var rs snapshots.Snapshotter + if config.EnableFuseManager { + fs, err := fusemanager.NewManagerClient(ic.Context, root, config.FuseManagerSocketAddr, &config.Config) + if err != nil { + return nil, multierr.Wrap(err, "failed to configure fuse manager") + } + rs, err = snbase.NewSnapshotter(ic.Context, root, fs, snbase.AsynchronousRemove) + if err != nil { + return nil, multierr.Wrap(err, "failed to configure snapshotter") + } + } else { + var err error + rs, err = service.NewStargzSnapshotterService(ic.Context, root, &config.Config) + if err != nil { + return nil, multierr.Wrap(err, "failed to configure snapshotter") + } + } + + return rs, nil }, }) } diff --git a/service/service.go b/service/service.go index f944f900c..c4e43ee79 100644 --- a/service/service.go +++ b/service/service.go @@ -28,6 +28,7 @@ import ( "github.com/containerd/stargz-snapshotter/fs/source" "github.com/containerd/stargz-snapshotter/service/keychain" "github.com/containerd/stargz-snapshotter/service/resolver" + "github.com/containerd/stargz-snapshotter/snapshot" snbase "github.com/containerd/stargz-snapshotter/snapshot" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" @@ -37,32 +38,13 @@ const fusermountBin = "fusermount" // NewStargzSnapshotterService returns stargz snapshotter. func NewStargzSnapshotterService(ctx context.Context, root string, config *Config) (snapshots.Snapshotter, error) { - // Prepare kubeconfig-based keychain if required - credsFuncs := []func(string) (string, string, error){keychain.NewDockerconfigKeychain(ctx)} - if config.KubeconfigKeychainConfig.EnableKeychain { - var opts []keychain.KubeconfigOption - if kcp := config.KubeconfigKeychainConfig.KubeconfigPath; kcp != "" { - opts = append(opts, keychain.WithKubeconfigPath(kcp)) - } - credsFuncs = append(credsFuncs, keychain.NewKubeconfigKeychain(ctx, opts...)) - } - - // Use RegistryHosts based on ResolverConfig and keychain - hosts := resolver.RegistryHostsFromConfig(resolver.Config(config.ResolverConfig), credsFuncs...) - - // Configure filesystem and snapshotter - fs, err := stargzfs.NewFilesystem(fsRoot(root), - config.Config, - stargzfs.WithGetSources(sources( - sourceFromCRILabels(hosts), // provides source info based on CRI labels - source.FromDefaultLabels(hosts), // provides source info based on default labels - )), - ) + fs, err := NewFileSystem(ctx, root, config) if err != nil { log.G(ctx).WithError(err).Fatalf("failed to configure filesystem") } - return snbase.NewSnapshotter(ctx, snapshotterRoot(root), fs, snbase.AsynchronousRemove) + return snbase.NewSnapshotter(ctx, snapshotterRoot(root), fs, snbase.AsynchronousRemove, snbase.CleanupCommitted, + snbase.EnableRestoreSnapshots) } func snapshotterRoot(root string) string { @@ -97,3 +79,32 @@ func Supported(root string) error { // Remote snapshotter is implemented based on overlayfs snapshotter. return overlayutils.Supported(snapshotterRoot(root)) } + +func NewFileSystem(ctx context.Context, root string, config *Config) (snapshot.FileSystem, error) { + // Prepare kubeconfig-based keychain if required + credsFuncs := []func(string) (string, string, error){keychain.NewDockerconfigKeychain(ctx)} + if config.KubeconfigKeychainConfig.EnableKeychain { + var opts []keychain.KubeconfigOption + if kcp := config.KubeconfigKeychainConfig.KubeconfigPath; kcp != "" { + opts = append(opts, keychain.WithKubeconfigPath(kcp)) + } + credsFuncs = append(credsFuncs, keychain.NewKubeconfigKeychain(ctx, opts...)) + } + + // Use RegistryHosts based on ResolverConfig and keychain + hosts := resolver.RegistryHostsFromConfig(resolver.Config(config.ResolverConfig), credsFuncs...) + + // Configure filesystem and snapshotter + fs, err := stargzfs.NewFilesystem(fsRoot(root), + config.Config, + stargzfs.WithGetSources(sources( + sourceFromCRILabels(hosts), // provides source info based on CRI labels + source.FromDefaultLabels(hosts), // provides source info based on default labels + )), + ) + if err != nil { + return nil, err + } + + return fs, nil +} diff --git a/snapshot/snapshot.go b/snapshot/snapshot.go index 715aecd30..bddc88f68 100644 --- a/snapshot/snapshot.go +++ b/snapshot/snapshot.go @@ -73,7 +73,9 @@ type FileSystem interface { // SnapshotterConfig is used to configure the remote snapshotter instance type SnapshotterConfig struct { - asyncRemove bool + asyncRemove bool + cleanupCommitted bool + enableRestoreSnapshots bool } // Opt is an option to configure the remote snapshotter @@ -88,10 +90,24 @@ func AsynchronousRemove(config *SnapshotterConfig) error { return nil } +// CleanupCommitted cleans committed snapshots when closing snapshotter +func CleanupCommitted(config *SnapshotterConfig) error { + config.cleanupCommitted = true + return nil +} + +// EnableRestoreSnapshots indicates whether restore snapshots when +// starting snapshotter +func EnableRestoreSnapshots(config *SnapshotterConfig) error { + config.enableRestoreSnapshots = true + return nil +} + type snapshotter struct { - root string - ms *storage.MetaStore - asyncRemove bool + root string + ms *storage.MetaStore + asyncRemove bool + cleanupCommitted bool // fs is a filesystem that this snapshotter recognizes. fs FileSystem @@ -146,8 +162,10 @@ func NewSnapshotter(ctx context.Context, root string, targetFs FileSystem, opts userxattr: userxattr, } - if err := o.restoreRemoteSnapshot(ctx); err != nil { - return nil, errors.Wrap(err, "failed to restore remote snapshot") + if config.enableRestoreSnapshots { + if err := o.restoreRemoteSnapshot(ctx); err != nil { + return nil, errors.Wrap(err, "failed to restore remote snapshot") + } } return o, nil @@ -632,9 +650,8 @@ func (o *snapshotter) workPath(id string) string { // Close closes the snapshotter func (o *snapshotter) Close() error { // unmount all mounts including Committed - const cleanupCommitted = true ctx := context.Background() - if err := o.cleanup(ctx, cleanupCommitted); err != nil { + if err := o.cleanup(ctx, o.cleanupCommitted); err != nil { log.G(ctx).WithError(err).Warn("failed to cleanup") } return o.ms.Close() diff --git a/snapshot/snapshot_test.go b/snapshot/snapshot_test.go index 902122f44..70b452fe4 100644 --- a/snapshot/snapshot_test.go +++ b/snapshot/snapshot_test.go @@ -60,7 +60,7 @@ func TestRemotePrepare(t *testing.T) { t.Fatal(err) } defer os.RemoveAll(root) - sn, err := NewSnapshotter(context.TODO(), root, bindFileSystem(t)) + sn, err := NewSnapshotter(context.TODO(), root, bindFileSystem(t), CleanupCommitted, EnableRestoreSnapshots) if err != nil { t.Fatalf("failed to make new remote snapshotter: %q", err) } @@ -111,7 +111,7 @@ func TestRemoteOverlay(t *testing.T) { t.Fatal(err) } defer os.RemoveAll(root) - sn, err := NewSnapshotter(context.TODO(), root, bindFileSystem(t)) + sn, err := NewSnapshotter(context.TODO(), root, bindFileSystem(t), CleanupCommitted, EnableRestoreSnapshots) if err != nil { t.Fatalf("failed to make new remote snapshotter: %q", err) } @@ -170,7 +170,7 @@ func TestRemoteCommit(t *testing.T) { t.Fatal(err) } defer os.RemoveAll(root) - sn, err := NewSnapshotter(context.TODO(), root, bindFileSystem(t)) + sn, err := NewSnapshotter(context.TODO(), root, bindFileSystem(t), CleanupCommitted, EnableRestoreSnapshots) if err != nil { t.Fatalf("failed to make new remote snapshotter: %q", err) } @@ -313,7 +313,7 @@ func TestFailureDetection(t *testing.T) { } defer os.RemoveAll(root) fi := bindFileSystem(t) - sn, err := NewSnapshotter(context.TODO(), root, fi) + sn, err := NewSnapshotter(context.TODO(), root, fi, CleanupCommitted, EnableRestoreSnapshots) if err != nil { t.Fatalf("failed to make new Snapshotter: %q", err) } @@ -439,7 +439,8 @@ func (fs *dummyFs) Unmount(ctx context.Context, mountpoint string) error { // Tests backword-comaptibility of overlayfs snapshotter. func newSnapshotter(ctx context.Context, root string) (snapshots.Snapshotter, func() error, error) { - snapshotter, err := NewSnapshotter(context.TODO(), root, dummyFileSystem()) + snapshotter, err := NewSnapshotter(context.TODO(), root, dummyFileSystem(), + CleanupCommitted, EnableRestoreSnapshots) if err != nil { return nil, nil, err }