Skip to content

Commit 7b417d8

Browse files
author
Tural Devrishev
committed
cli: change audit behaviour
Close #3977. Signed-off-by: Tural Devrishev <tural@nspcc.ru>
1 parent 296b4ef commit 7b417d8

File tree

1 file changed

+35
-146
lines changed

1 file changed

+35
-146
lines changed

cli/util/audit-bin.go

Lines changed: 35 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,22 @@ package util
33
import (
44
"errors"
55
"fmt"
6-
"io"
76
"strconv"
87

98
"github.com/nspcc-dev/neo-go/cli/cmdargs"
109
"github.com/nspcc-dev/neo-go/cli/options"
11-
"github.com/nspcc-dev/neo-go/pkg/services/helpers/neofs"
1210
"github.com/nspcc-dev/neo-go/pkg/wallet"
1311
"github.com/nspcc-dev/neofs-sdk-go/client"
1412
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
1513
"github.com/nspcc-dev/neofs-sdk-go/object"
1614
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
17-
"github.com/nspcc-dev/neofs-sdk-go/pool"
18-
"github.com/nspcc-dev/neofs-sdk-go/user"
1915
"github.com/urfave/cli/v2"
2016
)
2117

2218
func auditBin(ctx *cli.Context) error {
2319
if err := cmdargs.EnsureNone(ctx); err != nil {
2420
return err
2521
}
26-
indexAttrKey := ctx.String("index-attribute")
27-
indexFileSize := ctx.Uint("index-file-size")
2822
retries := ctx.Uint("retries")
2923
cnrID := ctx.String("container")
3024
debug := ctx.Bool("debug")
@@ -58,179 +52,74 @@ func auditBin(ctx *cli.Context) error {
5852
}
5953

6054
if skip > 0 {
61-
fmt.Fprintf(ctx.App.Writer, "Skipping %d index files\n", skip)
55+
fmt.Fprintf(ctx.App.Writer, "Skipping first %d blocks\n", skip)
6256
}
63-
filters := object.NewSearchFilters()
64-
filters.AddFilter(indexAttrKey, fmt.Sprintf("%d", skip), object.MatchNumGE)
65-
results, errs := neofs.ObjectSearch(ctx.Context, neoFSPool, acc.PrivateKey(), containerID, filters, []string{indexAttrKey})
57+
58+
f := object.NewSearchFilters()
59+
f.AddFilter(blockAttr, strconv.Itoa(skip), object.MatchNumGE)
6660

6761
var (
62+
cursor string
6863
originalID uint64
6964
originalOID oid.ID
7065
)
71-
loop:
72-
for {
73-
select {
74-
case <-ctx.Done():
75-
return cli.Exit("context cancelled", 1)
76-
case err, ok := <-errs:
77-
if !ok {
78-
break loop
79-
}
80-
if err != nil {
81-
return cli.Exit(fmt.Sprintf("search index files: %v", err), 1)
82-
}
83-
case itm, ok := <-results:
84-
if !ok {
85-
break loop
86-
}
87-
duplicateID, err := strconv.ParseUint(itm.Attributes[0], 10, 32)
88-
if err != nil {
89-
return cli.Exit(fmt.Errorf("failed to parse index file ID (%s): %w", itm.ID, err), 1)
90-
}
91-
92-
if !originalOID.IsZero() && duplicateID == originalID {
93-
if dryRun {
94-
fmt.Fprintf(ctx.App.Writer, "[dry-run] index file duplicate %s / %s (%d)\n", itm.ID, originalOID, originalID)
95-
} else {
96-
_, err := neoFSPool.ObjectDelete(ctx.Context, containerID, itm.ID, signer, client.PrmObjectDelete{})
97-
if err != nil {
98-
return cli.Exit(fmt.Errorf("failed to remove index file duplicate %s / %s (%d): %w", itm.ID, originalOID, originalID, err), 1)
99-
}
100-
fmt.Fprintf(ctx.App.Writer, "Index file duplicate %s / %s (%d) is removed\n", itm.ID, originalOID, originalID)
101-
}
102-
continue
103-
}
104-
originalID = duplicateID
105-
originalOID = itm.ID
106-
fmt.Fprintf(ctx.App.Writer, "Processing index file %d (%s)\n", originalID, originalOID)
107-
108-
originalOIDs, err := getBlockIDs(ctx, neoFSPool, containerID, originalOID, indexFileSize, signer, retries, debug)
109-
if err != nil {
110-
return cli.Exit(fmt.Errorf("failed to retrieve block OIDs for index file %d (%s): %w", originalID, originalOID, err), 1)
111-
}
112-
113-
startHeight := uint32(duplicateID) * uint32(indexFileSize)
114-
endHeight := startHeight + uint32(indexFileSize)
115-
err = deleteOrphans(ctx, neoFSPool, signer, containerID, blockAttr, originalOIDs,
116-
int(startHeight), int(endHeight), int(retries), debug, dryRun)
117-
if err != nil {
118-
return cli.Exit(fmt.Errorf("failed to remove block duplicates: %w", err), 1)
119-
}
120-
}
121-
}
122-
fmt.Fprintln(ctx.App.Writer, "Audit is completed.")
123-
return nil
124-
}
125-
126-
func getBlockIDs(ctx *cli.Context, p *pool.Pool, containerID cid.ID, indexFileID oid.ID, indexFileSize uint, signer user.Signer, maxRetries uint, debug bool) ([]oid.ID, error) {
127-
var rc io.ReadCloser
128-
129-
err := retry(func() error {
130-
var e error
131-
_, rc, e = p.ObjectGetInit(ctx.Context, containerID, indexFileID, signer, client.PrmObjectGet{})
132-
return e
133-
}, maxRetries, debug)
134-
if err != nil {
135-
return nil, fmt.Errorf("failed to get index file %s: %w", indexFileID, err)
136-
}
137-
defer rc.Close()
138-
139-
raw, err := io.ReadAll(rc)
140-
if err != nil {
141-
return nil, err
142-
}
143-
if len(raw) != int(indexFileSize)*oid.Size {
144-
return nil, fmt.Errorf("index file %s: size mismatch: expected %d bytes, got %d", indexFileID, int(indexFileSize)*oid.Size, len(raw))
145-
}
146-
147-
out := make([]oid.ID, 0, indexFileSize)
148-
for i := range indexFileSize {
149-
out = append(out, oid.ID(raw[i*oid.Size:(i+1)*oid.Size]))
150-
}
151-
return out, nil
152-
}
153-
154-
// deleteOrphans removes every block object those OID differs from the one
155-
// specified in the index file for the given height. It prints a WARN if the
156-
// expected object is missing. If dryRun is enabled, it prints duplicate OIDs
157-
// instead of removing them.
158-
func deleteOrphans(ctx *cli.Context, p *pool.Pool, signer user.Signer, containerID cid.ID, blockAttr string, originalOIDs []oid.ID, start, end, maxRetries int, debug, dryRun bool) error {
159-
var (
160-
cursor string
161-
oidIndex int
162-
)
163-
164-
// Search for block objects with height matching the expected one.
165-
f := object.NewSearchFilters()
166-
f.AddFilter(blockAttr, strconv.Itoa(start), object.MatchNumGE)
167-
f.AddFilter(blockAttr, strconv.Itoa(end), object.MatchNumLT)
16866

16967
for {
17068
var (
171-
err error
172-
nextCursor string
17369
page []client.SearchResultItem
70+
nextCursor string
17471
)
17572

17673
err = retry(func() error {
177-
page, nextCursor, err = p.SearchObjects(ctx.Context, containerID, f, []string{blockAttr}, cursor, signer, client.SearchObjectsOptions{})
178-
if err != nil {
179-
return fmt.Errorf("failed to search objects: %w", err)
74+
var e error
75+
page, nextCursor, e = neoFSPool.SearchObjects(ctx.Context, containerID, f, []string{blockAttr}, cursor, signer, client.SearchObjectsOptions{})
76+
if e != nil {
77+
return fmt.Errorf("failed to search objects: %w", e)
18078
}
18179
return nil
182-
}, uint(maxRetries), debug)
80+
}, retries, debug)
18381
if err != nil {
184-
return err
82+
return cli.Exit(fmt.Sprintf("search block objects: %v", err), 1)
18583
}
84+
18685
for _, itm := range page {
18786
select {
18887
case <-ctx.Done():
189-
return nil
88+
return cli.Exit("context cancelled", 1)
19089
default:
19190
}
192-
foundID := itm.ID
193-
foundIndex, err := strconv.Atoi(itm.Attributes[0])
91+
92+
duplicateID, err := strconv.ParseUint(itm.Attributes[0], 10, 64)
19493
if err != nil {
195-
return fmt.Errorf("incorrect index in result: %q", itm.Attributes[0])
196-
}
197-
if debug {
198-
fmt.Fprintf(ctx.App.Writer, "found block %d (%s), expected %d (%s)\n", foundIndex, foundID, oidIndex, originalOIDs[oidIndex])
199-
}
200-
if foundIndex == start+oidIndex && foundID == originalOIDs[oidIndex] {
201-
oidIndex++
202-
continue
94+
return cli.Exit(fmt.Errorf("failed to parse block ID (%s): %w", itm.ID, err), 1)
20395
}
20496

205-
if foundIndex > start+oidIndex {
206-
for start+oidIndex < foundIndex {
207-
fmt.Fprintf(ctx.App.Writer, "WARN: block %d (%s) is listed in the index file but missing from the storage\n", start+oidIndex, originalOIDs[oidIndex])
208-
oidIndex++
209-
}
210-
if foundID == originalOIDs[oidIndex] {
211-
continue
212-
}
213-
}
214-
if dryRun {
215-
fmt.Fprintf(ctx.App.Writer, "[dry-run] block duplicate %s / %s (%d)\n", foundID, originalOIDs[oidIndex], foundIndex)
216-
} else {
217-
err := retry(func() error {
218-
_, errDelete := p.ObjectDelete(ctx.Context, containerID, foundID, signer, client.PrmObjectDelete{})
219-
return errDelete
220-
}, uint(maxRetries), debug)
221-
222-
if err != nil {
223-
fmt.Fprintf(ctx.App.Writer, "WARN: failed to remove block %s / %s (%d): %s\n", foundID, originalOIDs[foundIndex-start], foundIndex, err)
224-
} else if debug {
225-
fmt.Fprintf(ctx.App.Writer, "Block duplicate %s / %s (%d) is removed\n", foundID, originalOIDs[foundIndex-start], foundIndex)
97+
if !originalOID.IsZero() && duplicateID == originalID {
98+
if dryRun {
99+
fmt.Fprintf(ctx.App.Writer, "[dry-run] block duplicate %s / %s (%d)\n", itm.ID, originalOID, originalID)
100+
} else {
101+
err = retry(func() error {
102+
_, e := neoFSPool.ObjectDelete(ctx.Context, containerID, itm.ID, signer, client.PrmObjectDelete{})
103+
return e
104+
}, retries, debug)
105+
if err != nil {
106+
return cli.Exit(fmt.Errorf("failed to remove block duplicate %s / %s (%d): %w", itm.ID, originalOID, originalID, err), 1)
107+
}
108+
fmt.Fprintf(ctx.App.Writer, "block duplicate %s / %s (%d) is removed\n", itm.ID, originalOID, originalID)
226109
}
110+
continue
227111
}
112+
113+
originalID = duplicateID
114+
originalOID = itm.ID
228115
}
116+
229117
if nextCursor == "" {
230118
break
231119
}
232120
cursor = nextCursor
233121
}
234122

123+
fmt.Fprintln(ctx.App.Writer, "Audit is completed.")
235124
return nil
236125
}

0 commit comments

Comments
 (0)