From 66c504d1bb072df8b85520809fc1a7d074357b3e Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Wed, 12 Apr 2017 17:56:04 -0700 Subject: [PATCH] snapshot: add Usage method to Snapshotter To allow the querying of usage for snapshots, we define a new method on the snapshotter to query the resources in use by a single snapshot. Conversely, it can be said that if the snapshot was deleted, the reported amount of usage would be recovered. There are few problems with this model in the implementation of btrfs that need to be worked out. In btrfs, it is hard to resolve the amount of data usage with the use of quotas but these may report valuables that are incompatible with the model. Signed-off-by: Stephen J Day --- fs/du.go | 12 +++ fs/du_unix.go | 42 +++++++++ fs/du_windows.go | 33 +++++++ snapshot/btrfs/btrfs.go | 25 +++++- snapshot/naive/naive.go | 43 ++++++++- snapshot/overlay/overlay.go | 54 +++++++++++- snapshot/snapshotter.go | 32 ++++++- snapshot/storage/bolt.go | 17 ++-- snapshot/storage/metastore_bench_test.go | 14 +-- snapshot/storage/metastore_test.go | 24 ++--- snapshot/storage/proto/record.pb.go | 107 ++++++++++++++++++----- snapshot/storage/proto/record.proto | 11 +++ snapshot/windows/windows.go | 4 + 13 files changed, 367 insertions(+), 51 deletions(-) create mode 100644 fs/du.go create mode 100644 fs/du_unix.go create mode 100644 fs/du_windows.go diff --git a/fs/du.go b/fs/du.go new file mode 100644 index 000000000..8dfdaebda --- /dev/null +++ b/fs/du.go @@ -0,0 +1,12 @@ +package fs + +type Usage struct { + Inodes int64 + Size int64 +} + +// DiskUsage counts the number of inodes and disk usage for the resources under +// path. +func DiskUsage(roots ...string) (Usage, error) { + return diskUsage(roots...) +} diff --git a/fs/du_unix.go b/fs/du_unix.go new file mode 100644 index 000000000..ad9bfe5b3 --- /dev/null +++ b/fs/du_unix.go @@ -0,0 +1,42 @@ +// +build !windows + +package fs + +import ( + "os" + "path/filepath" + "syscall" +) + +func diskUsage(roots ...string) (Usage, error) { + type inode struct { + // TODO(stevvooe): Can probably reduce memory usage by not tracking + // device, but we can leave this right for now. + dev, ino uint64 + } + + var ( + size int64 + inodes = map[inode]struct{}{} // expensive! + ) + + for _, root := range roots { + if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + + stat := fi.Sys().(*syscall.Stat_t) + inodes[inode{dev: stat.Dev, ino: stat.Ino}] = struct{}{} + size += fi.Size() + return nil + }); err != nil { + return Usage{}, err + } + } + + return Usage{ + Inodes: int64(len(inodes)), + Size: size, + }, nil +} diff --git a/fs/du_windows.go b/fs/du_windows.go new file mode 100644 index 000000000..4a0363c06 --- /dev/null +++ b/fs/du_windows.go @@ -0,0 +1,33 @@ +// +build windows + +package fs + +import ( + "os" + "path/filepath" +) + +func diskUsage(roots ...string) (Usage, error) { + var ( + size int64 + ) + + // TODO(stevvooe): Support inodes (or equivalent) for windows. + + for _, root := range roots { + if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + + size += fi.Size() + return nil + }); err != nil { + return Usage{}, err + } + } + + return Usage{ + Size: size, + }, nil +} diff --git a/snapshot/btrfs/btrfs.go b/snapshot/btrfs/btrfs.go index 61c609438..442ac1c0a 100644 --- a/snapshot/btrfs/btrfs.go +++ b/snapshot/btrfs/btrfs.go @@ -88,7 +88,28 @@ func (b *snapshotter) Stat(ctx context.Context, key string) (snapshot.Info, erro return snapshot.Info{}, err } defer t.Rollback() - return storage.GetInfo(ctx, key) + _, info, _, err := storage.GetInfo(ctx, key) + if err != nil { + return snapshot.Info{}, err + } + + return info, nil +} + +// Usage retrieves the disk usage of the top-level snapshot. +func (b *snapshotter) Usage(ctx context.Context, key string) (snapshot.Usage, error) { + panic("not implemented") + + // TODO(stevvooe): Btrfs has a quota model where data can be exclusive to a + // snapshot or shared among other resources. We may find that this is the + // correct value to reoprt but the stability of the implementation is under + // question. + // + // In general, this has impact on the model we choose for reporting usage. + // Ideally, the value should allow aggregration. For overlay, this is + // simple since we can scan the diff directory to get a unique value. This + // breaks down when start looking the behavior when data is shared between + // snapshots, such as that for btrfs. } // Walk the committed snapshots. @@ -193,7 +214,7 @@ func (b *snapshotter) Commit(ctx context.Context, name, key string) (err error) } }() - id, err := storage.CommitActive(ctx, key, name) + id, err := storage.CommitActive(ctx, key, name, snapshot.Usage{}) // TODO(stevvooe): Resolve a usage value for btrfs if err != nil { return errors.Wrap(err, "failed to commit") } diff --git a/snapshot/naive/naive.go b/snapshot/naive/naive.go index 9fb52d846..0a1d88ffd 100644 --- a/snapshot/naive/naive.go +++ b/snapshot/naive/naive.go @@ -61,7 +61,35 @@ func (o *snapshotter) Stat(ctx context.Context, key string) (snapshot.Info, erro return snapshot.Info{}, err } defer t.Rollback() - return storage.GetInfo(ctx, key) + _, info, _, err := storage.GetInfo(ctx, key) + if err != nil { + return snapshot.Info{}, err + } + + return info, nil +} + +func (o *snapshotter) Usage(ctx context.Context, key string) (snapshot.Usage, error) { + ctx, t, err := o.ms.TransactionContext(ctx, false) + if err != nil { + return snapshot.Usage{}, err + } + defer t.Rollback() + + id, info, usage, err := storage.GetInfo(ctx, key) + if err != nil { + return snapshot.Usage{}, err + } + + if info.Kind == snapshot.KindActive { + du, err := fs.DiskUsage(o.getSnapshotDir(id)) + if err != nil { + return snapshot.Usage{}, err + } + usage = snapshot.Usage(du) + } + + return usage, nil } func (o *snapshotter) Prepare(ctx context.Context, key, parent string) ([]containerd.Mount, error) { @@ -94,7 +122,18 @@ func (o *snapshotter) Commit(ctx context.Context, name, key string) error { if err != nil { return err } - if _, err := storage.CommitActive(ctx, key, name); err != nil { + + id, _, _, err := storage.GetInfo(ctx, key) + if err != nil { + return err + } + + usage, err := fs.DiskUsage(o.getSnapshotDir(id)) + if err != nil { + return err + } + + if _, err := storage.CommitActive(ctx, key, name, snapshot.Usage(usage)); err != nil { if rerr := t.Rollback(); rerr != nil { log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction") } diff --git a/snapshot/overlay/overlay.go b/snapshot/overlay/overlay.go index 9df80bd70..c60c15212 100644 --- a/snapshot/overlay/overlay.go +++ b/snapshot/overlay/overlay.go @@ -11,6 +11,7 @@ import ( "strings" "github.com/containerd/containerd" + "github.com/containerd/containerd/fs" "github.com/containerd/containerd/log" "github.com/containerd/containerd/plugin" "github.com/containerd/containerd/snapshot" @@ -72,7 +73,44 @@ func (o *snapshotter) Stat(ctx context.Context, key string) (snapshot.Info, erro return snapshot.Info{}, err } defer t.Rollback() - return storage.GetInfo(ctx, key) + _, info, _, err := storage.GetInfo(ctx, key) + if err != nil { + return snapshot.Info{}, err + } + + return info, nil +} + +// Usage returns the resources taken by the snapshot identified by key. +// +// For active snapshots, this will scan the usage of the overlay "diff" (aka +// "upper") directory and may take some time. +// +// For committed snapshots, the value is returned from the metadata database. +func (o *snapshotter) Usage(ctx context.Context, key string) (snapshot.Usage, error) { + ctx, t, err := o.ms.TransactionContext(ctx, false) + if err != nil { + return snapshot.Usage{}, err + } + id, info, usage, err := storage.GetInfo(ctx, key) + if err != nil { + return snapshot.Usage{}, err + } + + upperPath := o.upperPath(id) + t.Rollback() // transaction no longer needed at this point. + + if info.Kind == snapshot.KindActive { + du, err := fs.DiskUsage(upperPath) + if err != nil { + // TODO(stevvooe): Consider not reporting an error in this case. + return snapshot.Usage{}, err + } + + usage = snapshot.Usage(du) + } + + return usage, nil } func (o *snapshotter) Prepare(ctx context.Context, key, parent string) ([]containerd.Mount, error) { @@ -105,7 +143,19 @@ func (o *snapshotter) Commit(ctx context.Context, name, key string) error { if err != nil { return err } - if _, err := storage.CommitActive(ctx, key, name); err != nil { + + // grab the existing id + id, _, _, err := storage.GetInfo(ctx, key) + if err != nil { + return err + } + + usage, err := fs.DiskUsage(o.upperPath(id)) + if err != nil { + return err + } + + if _, err := storage.CommitActive(ctx, key, name, snapshot.Usage(usage)); err != nil { if rerr := t.Rollback(); rerr != nil { log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction") } diff --git a/snapshot/snapshotter.go b/snapshot/snapshotter.go index 11735f80e..d50f9beb3 100644 --- a/snapshot/snapshotter.go +++ b/snapshot/snapshotter.go @@ -23,6 +23,24 @@ type Info struct { Readonly bool // true if readonly, only valid for active } +// Usage defines statistics for disk resources consumed by the snapshot. +// +// These resources only include the resources consumed by the snapshot itself +// and does not include resources usage by the parent. +type Usage struct { + Inodes int64 // number of inodes in use. + Size int64 // provides usage, in bytes, of snapshot +} + +func (u *Usage) Add(other Usage) { + u.Size += other.Size + + // TODO(stevvooe): assumes independent inodes, but provides and upper + // bound. This should be pretty close, assumming the inodes for a + // snapshot are roughly unique to it. Don't trust this assumption. + u.Inodes += other.Inodes +} + // Snapshotter defines the methods required to implement a snapshot snapshotter for // allocating, snapshotting and mounting filesystem changesets. The model works // by building up sets of changes with parent-child relationships. @@ -145,6 +163,16 @@ type Snapshotter interface { // the kind of snapshot. Stat(ctx context.Context, key string) (Info, error) + // Usage returns the resource usage of an active or committed snapshot + // excluding the usage of parent snapshots. + // + // The running time of this call for active snapshots is dependent on + // implementation, but may be proportional to the size of the resource. + // Callers should take this into consideration. Implementations should + // attempt to honer context cancellation and avoid taking locks when making + // the calculation. + Usage(ctx context.Context, key string) (Usage, error) + // Mounts returns the mounts for the active snapshot transaction identified // by key. Can be called on an read-write or readonly transaction. This is // available only for active snapshots. @@ -203,7 +231,7 @@ type Snapshotter interface { // removed before proceeding. Remove(ctx context.Context, key string) error - // Walk the committed snapshots. For each snapshot in the snapshotter, the - // function will be called. + // Walk all snapshots in the snapshotter. For each snapshot in the + // snapshotter, the function will be called. Walk(ctx context.Context, fn func(context.Context, Info) error) error } diff --git a/snapshot/storage/bolt.go b/snapshot/storage/bolt.go index 455f760e2..9bb6673b8 100644 --- a/snapshot/storage/bolt.go +++ b/snapshot/storage/bolt.go @@ -63,21 +63,26 @@ func getParentPrefix(b []byte) uint64 { // GetInfo returns the snapshot Info directly from the metadata. Requires a // context with a storage transaction. -func GetInfo(ctx context.Context, key string) (snapshot.Info, error) { +func GetInfo(ctx context.Context, key string) (string, snapshot.Info, snapshot.Usage, error) { var ss db.Snapshot err := withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error { return getSnapshot(bkt, key, &ss) }) if err != nil { - return snapshot.Info{}, err + return "", snapshot.Info{}, snapshot.Usage{}, err } - return snapshot.Info{ + usage := snapshot.Usage{ + Inodes: ss.Inodes, + Size: ss.Size_, + } + + return fmt.Sprint(ss.ID), snapshot.Info{ Name: key, Parent: ss.Parent, Kind: fromProtoKind(ss.Kind), Readonly: ss.Readonly, - }, nil + }, usage, nil } // WalkInfo iterates through all metadata Info for the stored snapshots and @@ -263,7 +268,7 @@ func Remove(ctx context.Context, key string) (id string, k snapshot.Kind, err er // lookup or removal. The returned string identifier for the committed snapshot // is the same identifier of the original active snapshot. The provided context // must contain a writable transaction. -func CommitActive(ctx context.Context, key, name string) (id string, err error) { +func CommitActive(ctx context.Context, key, name string, usage snapshot.Usage) (id string, err error) { err = withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error { b := bkt.Get([]byte(name)) if len(b) != 0 { @@ -283,6 +288,8 @@ func CommitActive(ctx context.Context, key, name string) (id string, err error) ss.Kind = db.KindCommitted ss.Readonly = true + ss.Inodes = usage.Inodes + ss.Size_ = usage.Size if err := putSnapshot(bkt, name, &ss); err != nil { return err diff --git a/snapshot/storage/metastore_bench_test.go b/snapshot/storage/metastore_bench_test.go index fd5cf2e27..21406f09c 100644 --- a/snapshot/storage/metastore_bench_test.go +++ b/snapshot/storage/metastore_bench_test.go @@ -6,6 +6,8 @@ import ( "io/ioutil" "os" "testing" + + "github.com/containerd/containerd/snapshot" ) // Benchmarks returns a benchmark suite using the provided metadata store @@ -107,7 +109,7 @@ func createActiveFromBase(ctx context.Context, ms *MetaStore, active, base strin if _, err := CreateActive(ctx, "bottom", "", false); err != nil { return err } - if _, err := CommitActive(ctx, "bottom", base); err != nil { + if _, err := CommitActive(ctx, "bottom", base, snapshot.Usage{}); err != nil { return err } @@ -122,7 +124,7 @@ func statActiveBenchmark(ctx context.Context, b *testing.B, ms *MetaStore) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := GetInfo(ctx, "active") + _, _, _, err := GetInfo(ctx, "active") if err != nil { b.Fatal(err) } @@ -133,13 +135,13 @@ func statCommittedBenchmark(ctx context.Context, b *testing.B, ms *MetaStore) { if err := createActiveFromBase(ctx, ms, "active", "base"); err != nil { b.Fatal(err) } - if _, err := CommitActive(ctx, "active", "committed"); err != nil { + if _, err := CommitActive(ctx, "active", "committed", snapshot.Usage{}); err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := GetInfo(ctx, "committed") + _, _, _, err := GetInfo(ctx, "committed") if err != nil { b.Fatal(err) } @@ -179,7 +181,7 @@ func commitBenchmark(ctx context.Context, b *testing.B, ms *MetaStore) { b.Fatal(err) } b.StartTimer() - if _, err := CommitActive(ctx, "active", "committed"); err != nil { + if _, err := CommitActive(ctx, "active", "committed", snapshot.Usage{}); err != nil { b.Fatal(err) } b.StopTimer() @@ -196,7 +198,7 @@ func getActiveBenchmark(ctx context.Context, b *testing.B, ms *MetaStore) { b.Fatalf("create active failed: %+v", err) } base = fmt.Sprintf("base-%d", i) - if _, err := CommitActive(ctx, "tmp", base); err != nil { + if _, err := CommitActive(ctx, "tmp", base, snapshot.Usage{}); err != nil { b.Fatalf("commit failed: %+v", err) } diff --git a/snapshot/storage/metastore_test.go b/snapshot/storage/metastore_test.go index f2e223f23..d12d0660a 100644 --- a/snapshot/storage/metastore_test.go +++ b/snapshot/storage/metastore_test.go @@ -128,13 +128,13 @@ func basePopulate(ctx context.Context, ms *MetaStore) error { if _, err := CreateActive(ctx, "committed-tmp-1", "", false); err != nil { return errors.Wrap(err, "failed to create active") } - if _, err := CommitActive(ctx, "committed-tmp-1", "committed-1"); err != nil { + if _, err := CommitActive(ctx, "committed-tmp-1", "committed-1", snapshot.Usage{Size: 1}); err != nil { return errors.Wrap(err, "failed to create active") } if _, err := CreateActive(ctx, "committed-tmp-2", "committed-1", false); err != nil { return errors.Wrap(err, "failed to create active") } - if _, err := CommitActive(ctx, "committed-tmp-2", "committed-2"); err != nil { + if _, err := CommitActive(ctx, "committed-tmp-2", "committed-2", snapshot.Usage{Size: 2}); err != nil { return errors.Wrap(err, "failed to create active") } if _, err := CreateActive(ctx, "active-1", "", false); err != nil { @@ -238,7 +238,7 @@ func assertExist(t *testing.T, err error) { func testGetInfo(ctx context.Context, t *testing.T, ms *MetaStore) { for key, expected := range baseInfo { - info, err := GetInfo(ctx, key) + _, info, _, err := GetInfo(ctx, key) if err != nil { t.Fatalf("GetInfo on %v failed: %+v", key, err) } @@ -247,7 +247,7 @@ func testGetInfo(ctx context.Context, t *testing.T, ms *MetaStore) { } func testGetInfoNotExist(ctx context.Context, t *testing.T, ms *MetaStore) { - _, err := GetInfo(ctx, "active-not-exist") + _, _, _, err := GetInfo(ctx, "active-not-exist") assertNotExist(t, err) } @@ -272,7 +272,7 @@ func testGetActive(ctx context.Context, t *testing.T, ms *MetaStore) { if _, err := CreateActive(ctx, "committed-tmp-1", "", false); err != nil { return errors.Wrap(err, "failed to create active") } - if _, err := CommitActive(ctx, "committed-tmp-1", "committed-1"); err != nil { + if _, err := CommitActive(ctx, "committed-tmp-1", "committed-1", snapshot.Usage{}); err != nil { return errors.Wrap(err, "failed to create active") } @@ -350,7 +350,7 @@ func testCreateActive(ctx context.Context, t *testing.T, ms *MetaStore) { t.Fatal("Expected readonly active") } - commitID, err := CommitActive(ctx, "active-1", "committed-1") + commitID, err := CommitActive(ctx, "active-1", "committed-1", snapshot.Usage{}) if err != nil { t.Fatal(err) } @@ -425,7 +425,7 @@ func testCommit(ctx context.Context, t *testing.T, ms *MetaStore) { t.Fatal("Expected writable active") } - commitID, err := CommitActive(ctx, "active-1", "committed-1") + commitID, err := CommitActive(ctx, "active-1", "committed-1", snapshot.Usage{}) if err != nil { t.Fatal(err) } @@ -440,7 +440,7 @@ func testCommit(ctx context.Context, t *testing.T, ms *MetaStore) { } func testCommitNotExist(ctx context.Context, t *testing.T, ms *MetaStore) { - _, err := CommitActive(ctx, "active-not-exist", "committed-1") + _, err := CommitActive(ctx, "active-not-exist", "committed-1", snapshot.Usage{}) assertNotExist(t, err) } @@ -448,7 +448,7 @@ func testCommitExist(ctx context.Context, t *testing.T, ms *MetaStore) { if err := basePopulate(ctx, ms); err != nil { t.Fatalf("Populate failed: %+v", err) } - _, err := CommitActive(ctx, "active-1", "committed-1") + _, err := CommitActive(ctx, "active-1", "committed-1", snapshot.Usage{}) assertExist(t, err) } @@ -456,7 +456,7 @@ func testCommitCommitted(ctx context.Context, t *testing.T, ms *MetaStore) { if err := basePopulate(ctx, ms); err != nil { t.Fatalf("Populate failed: %+v", err) } - _, err := CommitActive(ctx, "committed-1", "committed-3") + _, err := CommitActive(ctx, "committed-1", "committed-3", snapshot.Usage{}) assertNotActive(t, err) } @@ -464,7 +464,7 @@ func testCommitReadonly(ctx context.Context, t *testing.T, ms *MetaStore) { if err := basePopulate(ctx, ms); err != nil { t.Fatalf("Populate failed: %+v", err) } - _, err := CommitActive(ctx, "active-5", "committed-3") + _, err := CommitActive(ctx, "active-5", "committed-3", snapshot.Usage{}) if err == nil { t.Fatal("Expected error committing readonly active") } @@ -476,7 +476,7 @@ func testRemove(ctx context.Context, t *testing.T, ms *MetaStore) { t.Fatal(err) } - commitID, err := CommitActive(ctx, "active-1", "committed-1") + commitID, err := CommitActive(ctx, "active-1", "committed-1", snapshot.Usage{}) if err != nil { t.Fatal(err) } diff --git a/snapshot/storage/proto/record.pb.go b/snapshot/storage/proto/record.pb.go index 6b092699a..e8081c686 100644 --- a/snapshot/storage/proto/record.pb.go +++ b/snapshot/storage/proto/record.pb.go @@ -65,6 +65,15 @@ type Snapshot struct { Parent string `protobuf:"bytes,2,opt,name=parent,proto3" json:"parent,omitempty"` Kind Kind `protobuf:"varint,4,opt,name=kind,proto3,enum=containerd.v1.Kind" json:"kind,omitempty"` Readonly bool `protobuf:"varint,5,opt,name=readonly,proto3" json:"readonly,omitempty"` + // inodes stores the number inodes in use for the snapshot. + // + // Only valid for committed snapshots. + Inodes int64 `protobuf:"varint,6,opt,name=inodes,proto3" json:"inodes,omitempty"` + // Size reports the disk used by the snapshot, excluding the parents. + // + // Only valid for committed snapshots, active snapshots must read the + // current usage from the disk. + Size_ int64 `protobuf:"varint,7,opt,name=size,proto3" json:"size,omitempty"` } func (m *Snapshot) Reset() { *m = Snapshot{} } @@ -116,6 +125,16 @@ func (m *Snapshot) MarshalTo(dAtA []byte) (int, error) { } i++ } + if m.Inodes != 0 { + dAtA[i] = 0x30 + i++ + i = encodeVarintRecord(dAtA, i, uint64(m.Inodes)) + } + if m.Size_ != 0 { + dAtA[i] = 0x38 + i++ + i = encodeVarintRecord(dAtA, i, uint64(m.Size_)) + } return i, nil } @@ -162,6 +181,12 @@ func (m *Snapshot) Size() (n int) { if m.Readonly { n += 2 } + if m.Inodes != 0 { + n += 1 + sovRecord(uint64(m.Inodes)) + } + if m.Size_ != 0 { + n += 1 + sovRecord(uint64(m.Size_)) + } return n } @@ -187,6 +212,8 @@ func (this *Snapshot) String() string { `Parent:` + fmt.Sprintf("%v", this.Parent) + `,`, `Kind:` + fmt.Sprintf("%v", this.Kind) + `,`, `Readonly:` + fmt.Sprintf("%v", this.Readonly) + `,`, + `Inodes:` + fmt.Sprintf("%v", this.Inodes) + `,`, + `Size_:` + fmt.Sprintf("%v", this.Size_) + `,`, `}`, }, "") return s @@ -315,6 +342,44 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { } } m.Readonly = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Inodes", wireType) + } + m.Inodes = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Inodes |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Size_", wireType) + } + m.Size_ = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Size_ |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipRecord(dAtA[iNdEx:]) @@ -446,24 +511,26 @@ func init() { } var fileDescriptorRecord = []byte{ - // 304 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x72, 0x49, 0xcf, 0x2c, 0xc9, - 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, - 0x4a, 0x41, 0x66, 0x16, 0xe7, 0x25, 0x16, 0x14, 0x67, 0xe4, 0x97, 0xe8, 0x17, 0x97, 0xe4, 0x17, - 0x25, 0xa6, 0xa7, 0xea, 0x17, 0x14, 0xe5, 0x97, 0xe4, 0xeb, 0x17, 0xa5, 0x26, 0xe7, 0x17, 0xa5, - 0xe8, 0x81, 0x39, 0x42, 0xbc, 0x08, 0xf5, 0x7a, 0x65, 0x86, 0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, - 0x10, 0x65, 0x20, 0x16, 0x44, 0x91, 0x52, 0x3d, 0x17, 0x47, 0x30, 0xd4, 0x2c, 0x21, 0x31, 0x2e, - 0xa6, 0xcc, 0x14, 0x09, 0x46, 0x05, 0x46, 0x0d, 0x16, 0x27, 0xb6, 0x47, 0xf7, 0xe4, 0x99, 0x3c, - 0x5d, 0x82, 0x98, 0x32, 0x53, 0x84, 0xc4, 0xb8, 0xd8, 0x0a, 0x12, 0x8b, 0x52, 0xf3, 0x4a, 0x24, - 0x98, 0x14, 0x18, 0x35, 0x38, 0x83, 0xa0, 0x3c, 0x21, 0x75, 0x2e, 0x96, 0xec, 0xcc, 0xbc, 0x14, - 0x09, 0x16, 0x05, 0x46, 0x0d, 0x3e, 0x23, 0x61, 0x3d, 0x14, 0xfb, 0xf4, 0xbc, 0x33, 0xf3, 0x52, - 0x82, 0xc0, 0x0a, 0x84, 0xa4, 0xb8, 0x38, 0x8a, 0x52, 0x13, 0x53, 0xf2, 0xf3, 0x72, 0x2a, 0x25, - 0x58, 0x15, 0x18, 0x35, 0x38, 0x82, 0xe0, 0x7c, 0xad, 0x20, 0x2e, 0x16, 0x6f, 0x88, 0x1a, 0x36, - 0x47, 0xe7, 0x10, 0xcf, 0x30, 0x57, 0x01, 0x06, 0x29, 0xbe, 0xae, 0xb9, 0x0a, 0x5c, 0x20, 0x51, - 0xc7, 0xe4, 0x92, 0xcc, 0xb2, 0x54, 0x21, 0x05, 0x2e, 0x4e, 0x67, 0x7f, 0x5f, 0x5f, 0xcf, 0x90, - 0x10, 0x57, 0x17, 0x01, 0x46, 0x29, 0xc1, 0xae, 0xb9, 0x0a, 0xbc, 0x20, 0x69, 0xe7, 0xfc, 0xdc, - 0xdc, 0xcc, 0x92, 0x92, 0xd4, 0x14, 0x29, 0x9e, 0x8e, 0xc5, 0x72, 0x0c, 0xbb, 0x96, 0xc8, 0x81, - 0xcd, 0x72, 0x92, 0x38, 0xf1, 0x50, 0x8e, 0xe1, 0xc6, 0x43, 0x39, 0x86, 0x86, 0x47, 0x72, 0x8c, - 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0x63, 0x12, 0x1b, 0xd8, - 0xd7, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xbb, 0x89, 0x2c, 0x37, 0x62, 0x01, 0x00, 0x00, + // 330 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x90, 0xbd, 0x4e, 0xf3, 0x30, + 0x18, 0x85, 0xe3, 0x7c, 0xf9, 0x42, 0x6b, 0xd1, 0xaa, 0x18, 0x54, 0x59, 0x19, 0x8c, 0xc5, 0x42, + 0xc4, 0x90, 0x08, 0xb8, 0x82, 0xfe, 0x0d, 0x55, 0x55, 0x21, 0x85, 0x8a, 0x3d, 0x8d, 0xad, 0xd4, + 0x82, 0xda, 0x95, 0x63, 0x2a, 0xc1, 0xc4, 0x88, 0x7a, 0x0f, 0x9d, 0x60, 0xe3, 0x0e, 0xb8, 0x82, + 0x8e, 0x8c, 0x4c, 0x88, 0xe6, 0x4a, 0x50, 0xd2, 0x8a, 0x9f, 0xed, 0x9c, 0xe3, 0x47, 0x8f, 0xa5, + 0x17, 0x76, 0x53, 0x61, 0x26, 0xb7, 0xe3, 0x20, 0x51, 0xd3, 0x30, 0x51, 0xd2, 0xc4, 0x42, 0x72, + 0xcd, 0x7e, 0xc7, 0x4c, 0xc6, 0xb3, 0x6c, 0xa2, 0x4c, 0x98, 0x19, 0xa5, 0xe3, 0x94, 0x87, 0x33, + 0xad, 0x8c, 0x0a, 0x35, 0x4f, 0x94, 0x66, 0x41, 0x59, 0x50, 0xed, 0x87, 0x0f, 0xe6, 0xa7, 0xde, + 0x41, 0xaa, 0x52, 0xb5, 0xc1, 0x8a, 0xb4, 0x81, 0x8e, 0x5e, 0x00, 0xac, 0x5c, 0x6e, 0x65, 0xa8, + 0x09, 0x6d, 0xc1, 0x30, 0xa0, 0xc0, 0x77, 0xda, 0x6e, 0xfe, 0x71, 0x68, 0xf7, 0xbb, 0x91, 0x2d, + 0x18, 0x6a, 0x42, 0x77, 0x16, 0x6b, 0x2e, 0x0d, 0xb6, 0x29, 0xf0, 0xab, 0xd1, 0xb6, 0xa1, 0x63, + 0xe8, 0x5c, 0x0b, 0xc9, 0xb0, 0x43, 0x81, 0x5f, 0x3f, 0xdb, 0x0f, 0xfe, 0x7c, 0x18, 0x0c, 0x84, + 0x64, 0x51, 0x09, 0x20, 0x0f, 0x56, 0x34, 0x8f, 0x99, 0x92, 0x37, 0x77, 0xf8, 0x3f, 0x05, 0x7e, + 0x25, 0xfa, 0xee, 0x85, 0x5c, 0x48, 0xc5, 0x78, 0x86, 0x5d, 0x0a, 0xfc, 0x7f, 0xd1, 0xb6, 0x21, + 0x04, 0x9d, 0x4c, 0xdc, 0x73, 0xbc, 0x53, 0xae, 0x65, 0x3e, 0x89, 0xa0, 0x33, 0xd8, 0xf8, 0xdc, + 0x56, 0x67, 0xd4, 0xbf, 0xea, 0x35, 0x2c, 0xaf, 0xbe, 0x58, 0x52, 0x58, 0xac, 0xad, 0xc4, 0x88, + 0x39, 0x47, 0x14, 0x56, 0x3b, 0x17, 0xc3, 0x61, 0x7f, 0x34, 0xea, 0x75, 0x1b, 0xc0, 0xdb, 0x5b, + 0x2c, 0x69, 0xad, 0x78, 0xee, 0xa8, 0xe9, 0x54, 0x18, 0xc3, 0x99, 0xb7, 0xfb, 0xf8, 0x44, 0xac, + 0xd7, 0x67, 0x52, 0xba, 0xda, 0x78, 0xb5, 0x26, 0xd6, 0xfb, 0x9a, 0x58, 0x0f, 0x39, 0x01, 0xab, + 0x9c, 0x80, 0xb7, 0x9c, 0x80, 0xcf, 0x9c, 0x80, 0xb1, 0x5b, 0x9e, 0xe8, 0xfc, 0x2b, 0x00, 0x00, + 0xff, 0xff, 0xce, 0x7e, 0x6f, 0x85, 0x8f, 0x01, 0x00, 0x00, } diff --git a/snapshot/storage/proto/record.proto b/snapshot/storage/proto/record.proto index 50fcfe652..64eb36b47 100644 --- a/snapshot/storage/proto/record.proto +++ b/snapshot/storage/proto/record.proto @@ -23,4 +23,15 @@ message Snapshot { string parent = 2; Kind kind = 4; bool readonly = 5; + + // inodes stores the number inodes in use for the snapshot. + // + // Only valid for committed snapshots. + int64 inodes = 6; + + // Size reports the disk used by the snapshot, excluding the parents. + // + // Only valid for committed snapshots, active snapshots must read the + // current usage from the disk. + int64 size = 7; } diff --git a/snapshot/windows/windows.go b/snapshot/windows/windows.go index b45e22347..829412525 100644 --- a/snapshot/windows/windows.go +++ b/snapshot/windows/windows.go @@ -44,6 +44,10 @@ func (o *Snapshotter) Stat(ctx context.Context, key string) (snapshot.Info, erro panic("not implemented") } +func (o *Snapshotter) Usage(ctx context.Context, key string) (snapshot.Usage, error) { + panic("not implemented") +} + func (o *Snapshotter) Prepare(ctx context.Context, key, parent string) ([]containerd.Mount, error) { panic("not implemented") }