From 62335273a2bb7f72e40748a374a9faf874fc9ba0 Mon Sep 17 00:00:00 2001 From: ilyee <493647673@qq.com> Date: Tue, 1 Jun 2021 19:51:14 +0800 Subject: [PATCH] Add fuse-manager Signed-off-by: Zuti He --- Makefile | 5 +- cmd/containerd-stargz-grpc/main.go | 29 +- cmd/stargz-fuse-manager/main.go | 25 + fs/fs.go | 27 +- go.mod | 2 + service/service.go | 77 +- snapshot/snapshot.go | 33 +- snapshot/snapshot_test.go | 11 +- stargzfusemanager/api/api.pb.go | 1582 ++++++++++++++++++++++++++++ stargzfusemanager/api/api.proto | 47 + stargzfusemanager/client.go | 144 +++ stargzfusemanager/fusemanager.go | 215 ++++ stargzfusemanager/fusestore.go | 123 +++ stargzfusemanager/service.go | 318 ++++++ 14 files changed, 2607 insertions(+), 31 deletions(-) create mode 100644 cmd/stargz-fuse-manager/main.go create mode 100644 stargzfusemanager/api/api.pb.go create mode 100644 stargzfusemanager/api/api.proto create mode 100644 stargzfusemanager/client.go create mode 100644 stargzfusemanager/fusemanager.go create mode 100644 stargzfusemanager/fusestore.go create mode 100644 stargzfusemanager/service.go diff --git a/Makefile b/Makefile index a05a7057a..5c32d1664 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 stargz-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 +stargz-fuse-manager: FORCE + GO111MODULE=$(GO111MODULE_VALUE) go build -o $(PREFIX)$@ $(GO_BUILD_FLAGS) $(GO_LD_FLAGS) -v ./cmd/stargz-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 16c07dd54..3dc3a0c52 100644 --- a/cmd/containerd-stargz-grpc/main.go +++ b/cmd/containerd-stargz-grpc/main.go @@ -39,6 +39,7 @@ import ( "github.com/containerd/stargz-snapshotter/service/keychain/dockerconfig" "github.com/containerd/stargz-snapshotter/service/keychain/kubeconfig" "github.com/containerd/stargz-snapshotter/service/resolver" + fusemanager "github.com/containerd/stargz-snapshotter/stargzfusemanager" "github.com/containerd/stargz-snapshotter/version" sddaemon "github.com/coreos/go-systemd/v22/daemon" metrics "github.com/docker/go-metrics" @@ -60,11 +61,13 @@ const ( ) 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", "", "address for the fusemanager's GRPC server") + fuseManagerPath = flag.String("fusemanager-path", "", "path to the fusemanager's executable") + printVersion = flag.Bool("version", false, "print the version") ) type snapshotterConfig struct { @@ -153,7 +156,21 @@ func main() { runtime.RegisterImageServiceServer(rpc, criServer) credsFuncs = append(credsFuncs, f) } - rs, err := service.NewStargzSnapshotterService(ctx, *rootDir, &config.Config, service.WithCredsFuncs(credsFuncs...)) + + opts := []service.Option{service.WithCredsFuncs(credsFuncs...)} + if *fuseManagerAddr != "" { + err := service.StartFuseManager(ctx, *fuseManagerPath, *fuseManagerAddr, filepath.Join(*rootDir, "fusestore.db"), *logLevel, filepath.Join(*rootDir, "stargz-fuse-manager.log")) + if err != nil { + log.G(ctx).WithError(err).Fatalf("failed to start fuse manager") + } + fs, err := fusemanager.NewManagerClient(ctx, *rootDir, *fuseManagerAddr, &config.Config) + if err != nil { + log.G(ctx).WithError(err).Fatalf("failed to configure fuse manager") + } + opts = append(opts, service.WithFuseManagerCli(fs)) + } + + rs, err := service.NewStargzSnapshotterService(ctx, *rootDir, &config.Config, opts...) if err != nil { log.G(ctx).WithError(err).Fatalf("failed to configure snapshotter") } diff --git a/cmd/stargz-fuse-manager/main.go b/cmd/stargz-fuse-manager/main.go new file mode 100644 index 000000000..6f38a6441 --- /dev/null +++ b/cmd/stargz-fuse-manager/main.go @@ -0,0 +1,25 @@ +/* + 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 ( + fusemanager "github.com/containerd/stargz-snapshotter/stargzfusemanager" +) + +func main() { + fusemanager.Run() +} diff --git a/fs/fs.go b/fs/fs.go index 45eaefafc..2e12987cc 100644 --- a/fs/fs.go +++ b/fs/fs.go @@ -64,6 +64,13 @@ import ( const defaultMaxConcurrency = 2 +var ( + nsLock = sync.Mutex{} + + ns *metrics.Namespace + metricsCtr *fsmetrics.Controller +) + type Option func(*options) type options struct { @@ -97,13 +104,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/go.mod b/go.mod index a3a220bc4..c72924adb 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,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 @@ -28,6 +29,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/service.go b/service/service.go index 61cb098fa..4af380619 100644 --- a/service/service.go +++ b/service/service.go @@ -18,6 +18,7 @@ package service import ( "context" + "os" "os/exec" "path/filepath" @@ -27,17 +28,22 @@ import ( stargzfs "github.com/containerd/stargz-snapshotter/fs" "github.com/containerd/stargz-snapshotter/fs/source" "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" ) -const fusermountBin = "fusermount" +const ( + fusermountBin = "fusermount" + defaultFuseManagerPath = "/usr/bin/stargz-fuse-manager" +) type Option func(*options) type options struct { - credsFuncs []resolver.Credential + credsFuncs []resolver.Credential + fuseManagerCli snbase.FileSystem } func WithCredsFuncs(creds ...resolver.Credential) Option { @@ -46,8 +52,30 @@ func WithCredsFuncs(creds ...resolver.Credential) Option { } } +func WithFuseManagerCli(cli snbase.FileSystem) Option { + return func(o *options) { + o.fuseManagerCli = cli + } +} + // NewStargzSnapshotterService returns stargz snapshotter. func NewStargzSnapshotterService(ctx context.Context, root string, config *Config, opts ...Option) (snapshots.Snapshotter, error) { + fs, enableFuseManager, err := NewFileSystem(ctx, root, config, opts...) + if err != nil { + log.G(ctx).WithError(err).Fatalf("failed to configure filesystem") + } + + snOpts := make([]snbase.Opt, 0) + snOpts = append(snOpts, snbase.AsynchronousRemove) + if !enableFuseManager { + snOpts = append(snOpts, snbase.CleanupCommitted) + snOpts = append(snOpts, snbase.EnableRestoreSnapshots) + } + + return snbase.NewSnapshotter(ctx, snapshotterRoot(root), fs, snOpts...) +} + +func NewFileSystem(ctx context.Context, root string, config *Config, opts ...Option) (snapshot.FileSystem, bool, error) { var sOpts options for _, o := range opts { o(&sOpts) @@ -56,6 +84,10 @@ func NewStargzSnapshotterService(ctx context.Context, root string, config *Confi // Use RegistryHosts based on ResolverConfig and keychain hosts := resolver.RegistryHostsFromConfig(resolver.Config(config.ResolverConfig), sOpts.credsFuncs...) + if sOpts.fuseManagerCli != nil { + return sOpts.fuseManagerCli, true, nil + } + // Configure filesystem and snapshotter fs, err := stargzfs.NewFilesystem(fsRoot(root), config.Config, @@ -65,10 +97,47 @@ func NewStargzSnapshotterService(ctx context.Context, root string, config *Confi )), ) if err != nil { - log.G(ctx).WithError(err).Fatalf("failed to configure filesystem") + return nil, false, err + } + + return fs, false, nil +} + +func StartFuseManager(ctx context.Context, executable, address, fusestore, logLevel, logPath string) error { + // if socket exists, do not start it + if _, err := os.Stat(address); err == nil { + return nil + } else if !os.IsNotExist(err) { + return err + } + + if executable == "" { + executable = defaultFuseManagerPath + } + + if _, err := os.Stat(executable); err != nil { + log.G(ctx).WithError(err).Errorf("failed to stat fusemanager binary: %s", executable) + return err + } + + args := []string{ + "-action", "start", + "-address", address, + "-fusestore-path", fusestore, + "-log-level", logLevel, + "-log-path", logPath, + } + + cmd := exec.Command(executable, args...) + if err := cmd.Start(); err != nil { + return err + } + + if err := cmd.Wait(); err != nil { + return err } - return snbase.NewSnapshotter(ctx, snapshotterRoot(root), fs, snbase.AsynchronousRemove) + return nil } func snapshotterRoot(root string) string { 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 } diff --git a/stargzfusemanager/api/api.pb.go b/stargzfusemanager/api/api.pb.go new file mode 100644 index 000000000..3277bd5ea --- /dev/null +++ b/stargzfusemanager/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), "stargzfusemanager.InitRequest") + proto.RegisterType((*MountRequest)(nil), "stargzfusemanager.MountRequest") + proto.RegisterMapType((map[string]string)(nil), "stargzfusemanager.MountRequest.LabelsEntry") + proto.RegisterType((*CheckRequest)(nil), "stargzfusemanager.CheckRequest") + proto.RegisterMapType((map[string]string)(nil), "stargzfusemanager.CheckRequest.LabelsEntry") + proto.RegisterType((*UnmountRequest)(nil), "stargzfusemanager.UnmountRequest") + proto.RegisterType((*Response)(nil), "stargzfusemanager.Response") +} + +func init() { proto.RegisterFile("api.proto", fileDescriptor_00212fb1f9d3bf1c) } + +var fileDescriptor_00212fb1f9d3bf1c = []byte{ + // 362 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x93, 0xcf, 0x4e, 0xc2, 0x40, + 0x10, 0xc6, 0xdd, 0xf2, 0x47, 0x19, 0x88, 0xd1, 0x8d, 0x31, 0x0d, 0x26, 0x15, 0x39, 0x35, 0x31, + 0x16, 0x83, 0x17, 0xe1, 0x28, 0xc1, 0x84, 0x44, 0x2e, 0x25, 0x5e, 0xbc, 0x2d, 0x64, 0x80, 0x06, + 0xd8, 0xad, 0xdd, 0x2d, 0x09, 0x3e, 0x89, 0x4f, 0xe1, 0xd9, 0x47, 0xf0, 0xe8, 0x23, 0x18, 0x7c, + 0x11, 0xc3, 0x52, 0x4c, 0x0d, 0x0d, 0xc4, 0x84, 0xdb, 0xcc, 0x74, 0xe6, 0x37, 0x5f, 0xbf, 0x69, + 0x21, 0xc7, 0x7c, 0xcf, 0xf1, 0x03, 0xa1, 0x04, 0x3d, 0x96, 0x8a, 0x05, 0x83, 0x97, 0x7e, 0x28, + 0x71, 0xc2, 0x38, 0x1b, 0x60, 0x50, 0xae, 0x41, 0xbe, 0xc5, 0x3d, 0xe5, 0xe2, 0x73, 0x88, 0x52, + 0x51, 0x0a, 0xe9, 0x40, 0x08, 0x65, 0x92, 0x12, 0xb1, 0x73, 0xae, 0x8e, 0xe9, 0x29, 0x64, 0x7b, + 0x82, 0xf7, 0xbd, 0x81, 0x69, 0x94, 0x88, 0x5d, 0x70, 0xa3, 0xac, 0xfc, 0x46, 0xa0, 0xd0, 0x16, + 0x21, 0xff, 0x1d, 0xb6, 0x00, 0x26, 0x8b, 0xdc, 0x17, 0x1e, 0x5f, 0x21, 0x62, 0x15, 0xda, 0x80, + 0xec, 0x98, 0x75, 0x71, 0x2c, 0x4d, 0xa3, 0x94, 0xb2, 0xf3, 0xd5, 0x4b, 0x67, 0x4d, 0x8f, 0x13, + 0x07, 0x3a, 0x0f, 0xba, 0xbb, 0xc9, 0x55, 0x30, 0x73, 0xa3, 0xd1, 0x62, 0x0d, 0xf2, 0xb1, 0x32, + 0x3d, 0x82, 0xd4, 0x08, 0x67, 0xd1, 0xb2, 0x45, 0x48, 0x4f, 0x20, 0x33, 0x65, 0xe3, 0x10, 0xb5, + 0xda, 0x9c, 0xbb, 0x4c, 0xea, 0xc6, 0x2d, 0xd1, 0x82, 0x1b, 0x43, 0xec, 0x8d, 0x76, 0x29, 0x38, + 0x0e, 0xdc, 0xb5, 0xe0, 0x6b, 0x38, 0x7c, 0xe4, 0x93, 0x7f, 0x58, 0x5c, 0x06, 0x38, 0x70, 0x51, + 0xfa, 0x82, 0x4b, 0xac, 0xbe, 0x1b, 0x60, 0x76, 0xb4, 0xde, 0xfb, 0x50, 0x62, 0x7b, 0xa9, 0xb7, + 0x83, 0xc1, 0xd4, 0xeb, 0x21, 0x6d, 0x40, 0x7a, 0x71, 0x77, 0x6a, 0x25, 0xbc, 0x52, 0xec, 0x83, + 0x28, 0x9e, 0x25, 0x3c, 0x5f, 0x6d, 0xa0, 0x4d, 0xc8, 0xe8, 0x7b, 0xd1, 0xf3, 0x2d, 0x97, 0xdc, + 0x8a, 0xd1, 0x2e, 0x26, 0x62, 0xe2, 0xfe, 0x6e, 0xc6, 0xb4, 0x60, 0x3f, 0x72, 0x8b, 0x5e, 0x24, + 0xf4, 0xfd, 0x75, 0x72, 0x23, 0xea, 0xae, 0xfe, 0x31, 0xb7, 0xc8, 0xe7, 0xdc, 0x22, 0x5f, 0x73, + 0x8b, 0xbc, 0x7e, 0x5b, 0x7b, 0x4f, 0xf6, 0xb2, 0xfb, 0x4a, 0x72, 0xe6, 0xcb, 0xa1, 0x50, 0x0a, + 0x83, 0xca, 0x1a, 0xa0, 0xc2, 0x7c, 0xaf, 0x9b, 0xd5, 0xff, 0xda, 0xcd, 0x4f, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x34, 0x1c, 0xfd, 0xd9, 0x78, 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 + +// StargzFuseManagerServiceClient is the client API for StargzFuseManagerService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type StargzFuseManagerServiceClient 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 stargzFuseManagerServiceClient struct { + cc *grpc.ClientConn +} + +func NewStargzFuseManagerServiceClient(cc *grpc.ClientConn) StargzFuseManagerServiceClient { + return &stargzFuseManagerServiceClient{cc} +} + +func (c *stargzFuseManagerServiceClient) Init(ctx context.Context, in *InitRequest, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/stargzfusemanager.StargzFuseManagerService/Init", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *stargzFuseManagerServiceClient) Mount(ctx context.Context, in *MountRequest, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/stargzfusemanager.StargzFuseManagerService/Mount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *stargzFuseManagerServiceClient) Check(ctx context.Context, in *CheckRequest, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/stargzfusemanager.StargzFuseManagerService/Check", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *stargzFuseManagerServiceClient) Unmount(ctx context.Context, in *UnmountRequest, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/stargzfusemanager.StargzFuseManagerService/Unmount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// StargzFuseManagerServiceServer is the server API for StargzFuseManagerService service. +type StargzFuseManagerServiceServer 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) +} + +// UnimplementedStargzFuseManagerServiceServer can be embedded to have forward compatible implementations. +type UnimplementedStargzFuseManagerServiceServer struct { +} + +func (*UnimplementedStargzFuseManagerServiceServer) Init(ctx context.Context, req *InitRequest) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method Init not implemented") +} +func (*UnimplementedStargzFuseManagerServiceServer) Mount(ctx context.Context, req *MountRequest) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method Mount not implemented") +} +func (*UnimplementedStargzFuseManagerServiceServer) Check(ctx context.Context, req *CheckRequest) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method Check not implemented") +} +func (*UnimplementedStargzFuseManagerServiceServer) Unmount(ctx context.Context, req *UnmountRequest) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method Unmount not implemented") +} + +func RegisterStargzFuseManagerServiceServer(s *grpc.Server, srv StargzFuseManagerServiceServer) { + s.RegisterService(&_StargzFuseManagerService_serviceDesc, srv) +} + +func _StargzFuseManagerService_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.(StargzFuseManagerServiceServer).Init(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/stargzfusemanager.StargzFuseManagerService/Init", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StargzFuseManagerServiceServer).Init(ctx, req.(*InitRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StargzFuseManagerService_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.(StargzFuseManagerServiceServer).Mount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/stargzfusemanager.StargzFuseManagerService/Mount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StargzFuseManagerServiceServer).Mount(ctx, req.(*MountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StargzFuseManagerService_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.(StargzFuseManagerServiceServer).Check(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/stargzfusemanager.StargzFuseManagerService/Check", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StargzFuseManagerServiceServer).Check(ctx, req.(*CheckRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StargzFuseManagerService_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.(StargzFuseManagerServiceServer).Unmount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/stargzfusemanager.StargzFuseManagerService/Unmount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StargzFuseManagerServiceServer).Unmount(ctx, req.(*UnmountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _StargzFuseManagerService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "stargzfusemanager.StargzFuseManagerService", + HandlerType: (*StargzFuseManagerServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Init", + Handler: _StargzFuseManagerService_Init_Handler, + }, + { + MethodName: "Mount", + Handler: _StargzFuseManagerService_Mount_Handler, + }, + { + MethodName: "Check", + Handler: _StargzFuseManagerService_Check_Handler, + }, + { + MethodName: "Unmount", + Handler: _StargzFuseManagerService_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/stargzfusemanager/api/api.proto b/stargzfusemanager/api/api.proto new file mode 100644 index 000000000..673dadfce --- /dev/null +++ b/stargzfusemanager/api/api.proto @@ -0,0 +1,47 @@ +/* + 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. +*/ + +syntax = "proto3"; + +option go_package = "stargz-snapshotter/stargzfusemanager/api"; + +package stargzfusemanager; + +service StargzFuseManagerService { + 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/stargzfusemanager/client.go b/stargzfusemanager/client.go new file mode 100644 index 000000000..f0e0c2778 --- /dev/null +++ b/stargzfusemanager/client.go @@ -0,0 +1,144 @@ +/* + 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 stargzfusemanager + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/containerd/containerd/defaults" + "github.com/containerd/containerd/log" + "github.com/containerd/containerd/pkg/dialer" + "google.golang.org/grpc" + "google.golang.org/grpc/backoff" + + "github.com/containerd/stargz-snapshotter/service" + "github.com/containerd/stargz-snapshotter/snapshot" + pb "github.com/containerd/stargz-snapshotter/stargzfusemanager/api" +) + +type Client struct { + client pb.StargzFuseManagerServiceClient +} + +func NewManagerClient(ctx context.Context, root, socket string, config *service.Config) (snapshot.FileSystem, error) { + grpcCli, err := newClient(ctx, socket) + if err != nil { + return nil, err + } + + client := &Client{ + client: grpcCli, + } + + err = client.Init(ctx, root, config) + if err != nil { + return nil, err + } + + return client, nil +} + +func newClient(ctx context.Context, socket string) (pb.StargzFuseManagerServiceClient, error) { + connParams := grpc.ConnectParams{ + Backoff: backoff.DefaultConfig, + } + gopts := []grpc.DialOption{ + grpc.WithBlock(), + grpc.WithInsecure(), + grpc.FailOnNonTempDialError(true), + grpc.WithConnectParams(connParams), + grpc.WithContextDialer(dialer.ContextDialer), + grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)), + grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)), + } + + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + conn, err := grpc.DialContext(ctx, fmt.Sprintf("unix://%s", socket), gopts...) + if err != nil { + return nil, err + } + + return pb.NewStargzFuseManagerServiceClient(conn), 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/stargzfusemanager/fusemanager.go b/stargzfusemanager/fusemanager.go new file mode 100644 index 000000000..42e8d9078 --- /dev/null +++ b/stargzfusemanager/fusemanager.go @@ -0,0 +1,215 @@ +/* + 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 stargzfusemanager + +import ( + "context" + "flag" + "fmt" + golog "log" + "net" + "os" + "os/exec" + "os/signal" + "path/filepath" + "syscall" + + "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" + + pb "github.com/containerd/stargz-snapshotter/stargzfusemanager/api" + "github.com/containerd/stargz-snapshotter/version" +) + +var ( + debugFlag bool + versionFlag bool + fuseStoreAddr string + address string + logLevel string + logPath string + action string +) + +func parseFlags() { + flag.BoolVar(&debugFlag, "debug", false, "enable debug output in logs") + flag.BoolVar(&versionFlag, "v", false, "show the fusemanager version and exit") + flag.StringVar(&action, "action", "", "action of fusemanager") + flag.StringVar(&fuseStoreAddr, "fusestore-path", "/var/lib/containerd-stargz-grpc/fusestore.db", "address for the fusemanager's store") + flag.StringVar(&address, "address", "/run/containerd-stargz-grpc/fuse-manager.sock", "address for the fusemanager's gRPC socket") + flag.StringVar(&logLevel, "log-level", logrus.InfoLevel.String(), "set the logging level [trace, debug, info, warn, error, fatal, panic]") + flag.StringVar(&logPath, "log-path", "", "path to fusemanager's logs, no log recorded if empty") + + flag.Parse() +} + +func Run() { + if err := run(); err != nil { + fmt.Fprintf(os.Stderr, "failed to run fusemanager: %v", err) + os.Exit(1) + } +} + +func run() error { + parseFlags() + if versionFlag { + fmt.Printf("%s:\n", os.Args[0]) + fmt.Println(" Version: ", version.Version) + fmt.Println(" Revision:", version.Revision) + fmt.Println("") + return nil + } + + if fuseStoreAddr == "" || address == "" { + return fmt.Errorf("fusemanager fusestore and socket path cannot be empty") + } + + ctx := log.WithLogger(context.Background(), log.L) + + switch action { + case "start": + return startNew(ctx, logPath, address, fuseStoreAddr, logLevel) + default: + return runFuseManager(ctx) + } +} + +func startNew(ctx context.Context, logPath, address, fusestore, logLevel string) error { + self, err := os.Executable() + if err != nil { + return err + } + + cwd, err := os.Getwd() + if err != nil { + return err + } + + args := []string{ + "-address", address, + "-fusestore-path", fusestore, + "-log-level", logLevel, + } + + cmd := exec.CommandContext(ctx, self, args...) + cmd.Dir = cwd + cmd.SysProcAttr = &syscall.SysProcAttr{ + Setpgid: true, + } + + if logPath != "" { + err := os.Remove(logPath) + if err != nil && !os.IsNotExist(err) { + return err + } + file, err := os.Create(logPath) + if err != nil { + return err + } + cmd.Stdout = file + cmd.Stderr = file + } + + if err := cmd.Start(); err != nil { + return err + } + go cmd.Wait() + + return nil +} + +func runFuseManager(ctx context.Context) error { + 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, + }) + + golog.SetOutput(log.G(ctx).WriterLevel(logrus.DebugLevel)) + + // Prepare the directory for the socket + if err := os.MkdirAll(filepath.Dir(address), 0700); err != nil { + log.G(ctx).WithError(err).Errorf("failed to create directory %s", filepath.Dir(address)) + return err + } + + // Try to remove the socket file to avoid EADDRINUSE + if err := os.Remove(address); err != nil && !os.IsNotExist(err) { + log.G(ctx).WithError(err).Error("failed to remove old socket file") + return err + } + + l, err := net.Listen("unix", address) + if err != nil { + log.G(ctx).WithError(err).Error("failed to listen socket") + return 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) + } + }() + + server := grpc.NewServer() + fm, err := NewFuseManager(ctx, l, server, fuseStoreAddr) + if err != nil { + log.G(ctx).WithError(err).Error("failed to configure manager server") + return err + } + + err = fm.clearMounts(ctx) + if err != nil { + log.G(ctx).WithError(err).Errorf("failed to clear mounts") + return err + } + + pb.RegisterStargzFuseManagerServiceServer(server, fm) + + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, unix.SIGINT, unix.SIGTERM) + go func() { + sig := <-sigCh + log.G(ctx).Infof("Got %v", sig) + fm.server.Stop() + }() + + if err = server.Serve(l); err != nil { + log.G(ctx).WithError(err).Error("failed to serve fuse manager") + return err + } + + err = fm.Close(ctx) + if err != nil { + log.G(ctx).WithError(err).Error("failed to close fusemanager") + return err + } + + return err +} diff --git a/stargzfusemanager/fusestore.go b/stargzfusemanager/fusestore.go new file mode 100644 index 000000000..aba2e2e67 --- /dev/null +++ b/stargzfusemanager/fusestore.go @@ -0,0 +1,123 @@ +/* + 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 stargzfusemanager + +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 + }) +} + +// restoreFuseInfo restores fuseInfo when Init is called, it will skip mounted +// layers whose mountpoint can be found in fsMap +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/stargzfusemanager/service.go b/stargzfusemanager/service.go new file mode 100644 index 000000000..1903472c8 --- /dev/null +++ b/stargzfusemanager/service.go @@ -0,0 +1,318 @@ +/* + 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 stargzfusemanager + +import ( + "context" + "encoding/json" + "fmt" + "net" + "os" + "path/filepath" + "sync" + "syscall" + "time" + + "github.com/containerd/containerd/defaults" + "github.com/containerd/containerd/log" + "github.com/containerd/containerd/pkg/dialer" + "github.com/moby/sys/mountinfo" + "github.com/pkg/errors" + bolt "go.etcd.io/bbolt" + "google.golang.org/grpc" + "google.golang.org/grpc/backoff" + runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2" + + "github.com/containerd/stargz-snapshotter/service" + "github.com/containerd/stargz-snapshotter/service/keychain/cri" + "github.com/containerd/stargz-snapshotter/service/keychain/dockerconfig" + "github.com/containerd/stargz-snapshotter/service/keychain/kubeconfig" + "github.com/containerd/stargz-snapshotter/service/resolver" + "github.com/containerd/stargz-snapshotter/snapshot" + pb "github.com/containerd/stargz-snapshotter/stargzfusemanager/api" +) + +const ( + fuseManagerReady = iota + fuseManagerNotReady + + defaultImageServiceAddress = "/run/containerd/containerd.sock" +) + +type Server struct { + pb.UnimplementedStargzFuseManagerServiceServer + + lock sync.RWMutex + ready int32 + + listener net.Listener + server *grpc.Server + + // 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 maps mountpoint to its filesystem instance to ensure Mount/Check/Unmount + // call the proper filesystem + fsMap sync.Map + // curFs is filesystem created by latest config + curFs snapshot.FileSystem + ms *bolt.DB +} + +func NewFuseManager(ctx context.Context, listener net.Listener, server *grpc.Server, 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, + listener: listener, + server: server, + } + + 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 + + credsFuncs := []resolver.Credential{dockerconfig.NewDockerconfigKeychain(ctx)} + if config.KubeconfigKeychainConfig.EnableKeychain { + var opts []kubeconfig.Option + if kcp := config.KubeconfigKeychainConfig.KubeconfigPath; kcp != "" { + opts = append(opts, kubeconfig.WithKubeconfigPath(kcp)) + } + credsFuncs = append(credsFuncs, kubeconfig.NewKubeconfigKeychain(ctx, opts...)) + } + if config.CRIKeychainConfig.EnableKeychain { + // connects to the backend CRI service (defaults to containerd socket) + criAddr := defaultImageServiceAddress + if cp := config.CRIKeychainConfig.ImageServicePath; cp != "" { + criAddr = cp + } + connectCRI := func() (runtime.ImageServiceClient, error) { + // TODO: make gRPC options configurable from config.toml + backoffConfig := backoff.DefaultConfig + backoffConfig.MaxDelay = 3 * time.Second + connParams := grpc.ConnectParams{ + Backoff: backoffConfig, + } + gopts := []grpc.DialOption{ + grpc.WithInsecure(), + grpc.WithConnectParams(connParams), + grpc.WithContextDialer(dialer.ContextDialer), + grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)), + grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)), + } + conn, err := grpc.Dial(dialer.DialAddress(criAddr), gopts...) + if err != nil { + return nil, err + } + return runtime.NewImageServiceClient(conn), nil + } + f, _ := cri.NewCRIKeychain(ctx, connectCRI) + credsFuncs = append(credsFuncs, f) + } + opts := []service.Option{service.WithCredsFuncs(credsFuncs...)} + + fs, _, err := service.NewFileSystem(ctx, fm.root, fm.config, opts...) + 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() + fm.ready = fuseManagerNotReady + + err := fm.clearMounts(ctx) + if err != nil { + log.G(ctx).WithError(err).Errorf("failed to clear mounts") + 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 { + // mountpoint in fsMap means layer is already mounted, skip it + 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 +} + +func (fm *Server) clearMounts(ctx context.Context) error { + mountpoints, err := fm.listMountpoints(ctx) + if err != nil { + return err + } + + for _, mp := range mountpoints { + if err := syscall.Unmount(mp, syscall.MNT_FORCE); err != nil { + return err + } + } + + return nil +}