diff --git a/pkg/manifest/mantaray/marshal.go b/pkg/manifest/mantaray/marshal.go index fcd19c55411..4e0324c35dd 100644 --- a/pkg/manifest/mantaray/marshal.go +++ b/pkg/manifest/mantaray/marshal.go @@ -287,6 +287,9 @@ func (n *Node) UnmarshalBinary(data []byte) error { bb.fromBytes(data[offset:]) offset += 32 // skip forks return bb.iter(func(b byte) error { + if refBytesSize == 0 { + return nil + } f := &fork{} if len(data) < offset+nodeForkTypeBytesSize { @@ -296,7 +299,6 @@ func (n *Node) UnmarshalBinary(data []byte) error { nodeType := data[offset] nodeForkSize := nodeForkPreReferenceSize + refBytesSize - if nodeTypeIsWithMetadataType(nodeType) { if len(data) < offset+nodeForkPreReferenceSize+refBytesSize+nodeForkMetadataBytesSize { return fmt.Errorf("not enough bytes for node fork: %d (%d) on byte '%x': %w", (len(data) - offset), (nodeForkPreReferenceSize + refBytesSize + nodeForkMetadataBytesSize), []byte{b}, ErrInvalidManifest) diff --git a/pkg/manifest/mantaray/node.go b/pkg/manifest/mantaray/node.go index 9561c299db5..2e2754f200b 100644 --- a/pkg/manifest/mantaray/node.go +++ b/pkg/manifest/mantaray/node.go @@ -311,8 +311,11 @@ func (n *Node) Remove(ctx context.Context, path []byte, ls LoadSaver) error { return ErrNotFound } rest := path[len(f.prefix):] + defer func() { + n.ref = nil + }() if len(rest) == 0 { - // full path matched + delete(n.forks, path[0]) return nil } diff --git a/pkg/manifest/mantaray/persist.go b/pkg/manifest/mantaray/persist.go index 2bd07d74c94..8b18896fb61 100644 --- a/pkg/manifest/mantaray/persist.go +++ b/pkg/manifest/mantaray/persist.go @@ -7,7 +7,6 @@ package mantaray import ( "context" "errors" - "golang.org/x/sync/errgroup" ) diff --git a/pkg/manifest/mantaray/persist_test.go b/pkg/manifest/mantaray/persist_test.go index b6a6c47e699..301237eff89 100644 --- a/pkg/manifest/mantaray/persist_test.go +++ b/pkg/manifest/mantaray/persist_test.go @@ -8,6 +8,7 @@ import ( "bytes" "context" "crypto/sha256" + "errors" "sync" "testing" @@ -62,6 +63,118 @@ func TestPersistIdempotence(t *testing.T) { } } +func TestPersistRemove(t *testing.T) { + t.Parallel() + + for _, tc := range []struct { + name string + toAdd []mantaray.NodeEntry + toRemove [][]byte + }{ + { + name: "simple", + toAdd: []mantaray.NodeEntry{ + { + Path: []byte("/"), + Metadata: map[string]string{ + "index-document": "index.html", + }, + }, + { + Path: []byte("index.html"), + }, + { + Path: []byte("img/1.png"), + }, + { + Path: []byte("img/2.png"), + }, + { + Path: []byte("robots.txt"), + }, + }, + toRemove: [][]byte{ + []byte("img/2.png"), + }, + }, + { + name: "nested-prefix-is-not-collapsed", + toAdd: []mantaray.NodeEntry{ + { + Path: []byte("index.html"), + }, + { + Path: []byte("img/1.png"), + }, + { + Path: []byte("img/2/test1.png"), + }, + { + Path: []byte("img/2/test2.png"), + }, + { + Path: []byte("robots.txt"), + }, + }, + toRemove: [][]byte{ + []byte("img/2/test1.png"), + }, + }, + } { + ctx := context.Background() + var ls mantaray.LoadSaver = newMockLoadSaver() + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + // add and persist + n := mantaray.New() + for i := 0; i < len(tc.toAdd); i++ { + c := tc.toAdd[i].Path + e := tc.toAdd[i].Entry + if len(e) == 0 { + e = append(make([]byte, 32-len(c)), c...) + } + m := tc.toAdd[i].Metadata + err := n.Add(ctx, c, e, m, ls) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + } + err := n.Save(ctx, ls) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + ref := n.Reference() + // reload and remove + nn := mantaray.NewNodeRef(ref) + for i := 0; i < len(tc.toRemove); i++ { + c := tc.toRemove[i] + err := nn.Remove(ctx, c, ls) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + } + + err = nn.Save(ctx, ls) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + ref = nn.Reference() + // reload and lookup removed node + nnn := mantaray.NewNodeRef(ref) + for i := 0; i < len(tc.toRemove); i++ { + c := tc.toRemove[i] + _, err = nnn.LookupNode(ctx, c, ls) + if !errors.Is(err, mantaray.ErrNotFound) { + t.Fatalf("expected not found error, got %v", err) + } + } + }) + } +} + type addr [32]byte type mockLoadSaver struct { mtx sync.Mutex