diff --git a/metadata/snapshot.go b/metadata/snapshot.go index 542091719..6b12c1522 100644 --- a/metadata/snapshot.go +++ b/metadata/snapshot.go @@ -34,6 +34,10 @@ import ( bolt "go.etcd.io/bbolt" ) +const ( + inheritedLabelsPrefix = "containerd.io/snapshot/" +) + type snapshotter struct { snapshots.Snapshotter name string @@ -338,12 +342,14 @@ func (s *snapshotter) createSnapshot(ctx context.Context, key, parent string, re return err } + inheritedOpt := snapshots.WithLabels(filterInheritedLabels(base.Labels)) + // TODO: Consider doing this outside of transaction to lessen // metadata lock time if readonly { - m, err = s.Snapshotter.View(ctx, bkey, bparent) + m, err = s.Snapshotter.View(ctx, bkey, bparent, inheritedOpt) } else { - m, err = s.Snapshotter.Prepare(ctx, bkey, bparent) + m, err = s.Snapshotter.Prepare(ctx, bkey, bparent, inheritedOpt) } return err }); err != nil { @@ -445,9 +451,11 @@ func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap return err } + inheritedOpt := snapshots.WithLabels(filterInheritedLabels(base.Labels)) + // TODO: Consider doing this outside of transaction to lessen // metadata lock time - return s.Snapshotter.Commit(ctx, nameKey, bkey) + return s.Snapshotter.Commit(ctx, nameKey, bkey, inheritedOpt) }) } @@ -761,3 +769,19 @@ func (s *snapshotter) pruneBranch(ctx context.Context, node *treeNode) error { func (s *snapshotter) Close() error { return s.Snapshotter.Close() } + +// filterInheritedLabels filters the provided labels by removing any key which doesn't have +// a prefix of "containerd.io/snapshot/". +func filterInheritedLabels(labels map[string]string) map[string]string { + if labels == nil { + return nil + } + + filtered := make(map[string]string) + for k, v := range labels { + if strings.HasPrefix(k, inheritedLabelsPrefix) { + filtered[k] = v + } + } + return filtered +} diff --git a/metadata/snapshot_test.go b/metadata/snapshot_test.go index e2cb3417c..74aa89aeb 100644 --- a/metadata/snapshot_test.go +++ b/metadata/snapshot_test.go @@ -20,6 +20,7 @@ import ( "context" "os" "path/filepath" + "reflect" "runtime" "testing" @@ -63,3 +64,41 @@ func TestMetadata(t *testing.T) { testutil.RequiresRoot(t) testsuite.SnapshotterSuite(t, "Metadata", newTestSnapshotter) } + +func TestFilterInheritedLabels(t *testing.T) { + tests := []struct { + labels map[string]string + expected map[string]string + }{ + { + nil, + nil, + }, + { + map[string]string{}, + map[string]string{}, + }, + { + map[string]string{"": ""}, + map[string]string{}, + }, + { + map[string]string{"foo": "bar"}, + map[string]string{}, + }, + { + map[string]string{inheritedLabelsPrefix + "foo": "bar"}, + map[string]string{inheritedLabelsPrefix + "foo": "bar"}, + }, + { + map[string]string{inheritedLabelsPrefix + "foo": "bar", "qux": "qaz"}, + map[string]string{inheritedLabelsPrefix + "foo": "bar"}, + }, + } + + for _, test := range tests { + if actual := filterInheritedLabels(test.labels); !reflect.DeepEqual(actual, test.expected) { + t.Fatalf("expected %v but got %v", test.expected, actual) + } + } +} diff --git a/snapshots/snapshotter.go b/snapshots/snapshotter.go index b4af6a308..514538f7e 100644 --- a/snapshots/snapshotter.go +++ b/snapshots/snapshotter.go @@ -86,10 +86,15 @@ func (k *Kind) UnmarshalJSON(b []byte) error { // Info provides information about a particular snapshot. // JSON marshallability is supported for interactive with tools like ctr, type Info struct { - Kind Kind // active or committed snapshot - Name string // name or key of snapshot - Parent string `json:",omitempty"` // name of parent snapshot - Labels map[string]string `json:",omitempty"` // Labels for snapshot + Kind Kind // active or committed snapshot + Name string // name or key of snapshot + Parent string `json:",omitempty"` // name of parent snapshot + + // Labels for a snapshot. + // + // Note: only labels prefixed with `containerd.io/snapshot/` will be inherited by the + // snapshotter's `Prepare`, `View`, or `Commit` calls. + Labels map[string]string `json:",omitempty"` Created time.Time `json:",omitempty"` // Created time Updated time.Time `json:",omitempty"` // Last update time }