Line data Source code
1 : // Copyright 2023 The LevelDB-Go and Pebble Authors. All rights reserved. Use
2 : // of this source code is governed by a BSD-style license that can be found in
3 : // the LICENSE file.
4 :
5 : package tool
6 :
7 : import (
8 : "fmt"
9 : "io"
10 : "sort"
11 :
12 : "github.com/cockroachdb/pebble"
13 : "github.com/cockroachdb/pebble/internal/base"
14 : "github.com/cockroachdb/pebble/objstorage"
15 : "github.com/cockroachdb/pebble/objstorage/objstorageprovider/remoteobjcat"
16 : "github.com/cockroachdb/pebble/record"
17 : "github.com/spf13/cobra"
18 : )
19 :
20 : // remoteCatalogT implements tools for the remote object catalog.
21 : type remoteCatalogT struct {
22 : Root *cobra.Command
23 : Dump *cobra.Command
24 :
25 : verbose bool
26 : opts *pebble.Options
27 : }
28 :
29 1 : func newRemoteCatalog(opts *pebble.Options) *remoteCatalogT {
30 1 : m := &remoteCatalogT{
31 1 : opts: opts,
32 1 : }
33 1 :
34 1 : m.Root = &cobra.Command{
35 1 : Use: "remotecat",
36 1 : Short: "remote object catalog introspection tools",
37 1 : }
38 1 :
39 1 : // Add dump command
40 1 : m.Dump = &cobra.Command{
41 1 : Use: "dump <remote-catalog-files>",
42 1 : Short: "print remote object catalog contents",
43 1 : Long: `
44 1 : Print the contents of the REMOTE-OBJ-CATALOG files.
45 1 : `,
46 1 : Args: cobra.MinimumNArgs(1),
47 1 : Run: m.runDump,
48 1 : }
49 1 : m.Dump.Flags().BoolVarP(&m.verbose, "verbose", "v", false, "show each record in the catalog")
50 1 : m.Root.AddCommand(m.Dump)
51 1 :
52 1 : return m
53 1 : }
54 :
55 1 : func (m *remoteCatalogT) runDump(cmd *cobra.Command, args []string) {
56 1 : for _, arg := range args {
57 1 : err := m.runDumpOne(cmd.OutOrStdout(), arg)
58 1 : if err != nil {
59 0 : fmt.Fprintf(cmd.OutOrStderr(), "%s\n", err)
60 0 : }
61 : }
62 : }
63 :
64 1 : func (m *remoteCatalogT) runDumpOne(stdout io.Writer, filename string) error {
65 1 : f, err := m.opts.FS.Open(filename)
66 1 : if err != nil {
67 0 : return err
68 0 : }
69 :
70 1 : var creatorID objstorage.CreatorID
71 1 : objects := make(map[base.DiskFileNum]remoteobjcat.RemoteObjectMetadata)
72 1 :
73 1 : fmt.Fprintf(stdout, "%s\n", filename)
74 1 : var editIdx int
75 1 : rr := record.NewReader(f, 0 /* logNum */)
76 1 : for {
77 1 : offset := rr.Offset()
78 1 : r, err := rr.Next()
79 1 : if err == io.EOF {
80 1 : break
81 1 : } else if err != nil {
82 0 : return err
83 0 : }
84 :
85 1 : var ve remoteobjcat.VersionEdit
86 1 : err = ve.Decode(r)
87 1 : if err != nil {
88 0 : return err
89 0 : }
90 :
91 1 : if m.verbose {
92 1 : fmt.Fprintf(stdout, "%d/%d\n", offset, editIdx)
93 1 : if ve.CreatorID.IsSet() {
94 1 : fmt.Fprintf(stdout, " CreatorID: %s\n", ve.CreatorID)
95 1 : }
96 1 : if len(ve.NewObjects) > 0 {
97 1 : fmt.Fprintf(stdout, " NewObjects:\n")
98 1 : for _, m := range ve.NewObjects {
99 1 : fmt.Fprintf(
100 1 : stdout, " %s CreatorID: %s CreatorFileNum: %s Locator: %q CustomObjectName: %q\n",
101 1 : m.FileNum, m.CreatorID, m.CreatorFileNum, m.Locator, m.CustomObjectName,
102 1 : )
103 1 : }
104 : }
105 1 : if len(ve.DeletedObjects) > 0 {
106 1 : fmt.Fprintf(stdout, " DeletedObjects:\n")
107 1 : for _, n := range ve.DeletedObjects {
108 1 : fmt.Fprintf(stdout, " %s\n", n)
109 1 : }
110 : }
111 : }
112 1 : editIdx++
113 1 : if err := ve.Apply(&creatorID, objects); err != nil {
114 0 : return err
115 0 : }
116 : }
117 1 : fmt.Fprintf(stdout, "CreatorID: %v\n", creatorID)
118 1 : var filenums []base.DiskFileNum
119 1 : for n := range objects {
120 1 : filenums = append(filenums, n)
121 1 : }
122 1 : sort.Slice(filenums, func(i, j int) bool {
123 1 : return filenums[i].FileNum() < filenums[j].FileNum()
124 1 : })
125 1 : fmt.Fprintf(stdout, "Objects:\n")
126 1 : for _, n := range filenums {
127 1 : m := objects[n]
128 1 : fmt.Fprintf(
129 1 : stdout, " %s CreatorID: %s CreatorFileNum: %s Locator: %q CustomObjectName: %q\n",
130 1 : n, m.CreatorID, m.CreatorFileNum, m.Locator, m.CustomObjectName,
131 1 : )
132 1 : }
133 1 : return nil
134 : }
|