migrate decomposedfs cli command from urfave/cli to spf13/cobra

Signed-off-by: Christian Richter <c.richter@opencloud.eu>
This commit is contained in:
Christian Richter
2025-12-03 12:40:50 +01:00
committed by Florian Schade
parent 1e970499af
commit 56324caba1

View File

@@ -9,8 +9,6 @@ import (
"sort"
"strings"
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/opencloud-eu/opencloud/opencloud/pkg/register"
"github.com/opencloud-eu/opencloud/pkg/config"
revactx "github.com/opencloud-eu/reva/v2/pkg/ctx"
@@ -25,69 +23,61 @@ import (
"github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/tree"
"github.com/opencloud-eu/reva/v2/pkg/storagespace"
"github.com/opencloud-eu/reva/v2/pkg/store"
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/rs/zerolog"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
)
// DecomposedfsCommand is the entrypoint for the groups command.
func DecomposedfsCommand(cfg *config.Config) *cli.Command {
return &cli.Command{
Name: "decomposedfs",
Usage: `cli tools to inspect and manipulate a decomposedfs storage.`,
Category: "maintenance",
Subcommands: []*cli.Command{
metadataCmd(cfg),
checkCmd(cfg),
},
func DecomposedfsCommand(cfg *config.Config) *cobra.Command {
decomposedCmd := &cobra.Command{
Use: "decomposedfs",
Short: `cli tools to inspect and manipulate a decomposedfs storage.`,
}
decomposedCmd.AddCommand(metadataCmd(cfg), checkCmd(cfg))
return decomposedCmd
}
func init() {
register.AddCommand(DecomposedfsCommand)
}
func checkCmd(cfg *config.Config) *cli.Command {
return &cli.Command{
Name: "check-treesize",
Usage: `cli tool to check the treesize metadata of a Space`,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "root",
Aliases: []string{"r"},
Required: true,
Usage: "Path to the root directory of the decomposedfs",
},
&cli.StringFlag{
Name: "node",
Required: true,
Aliases: []string{"n"},
Usage: "Space ID of the Space to inspect",
},
&cli.BoolFlag{
Name: "repair",
Usage: "Try to repair nodes with incorrect treesize metadata. IMPORTANT: Only use this while OpenCloud is not running.",
},
&cli.BoolFlag{
Name: "force",
Usage: "Do not prompt for confirmation when running in repair mode.",
},
},
Action: check,
func checkCmd(cfg *config.Config) *cobra.Command {
cCmd := &cobra.Command{
Use: "check-treesize",
Short: `cli tool to check the treesize metadata of a Space`,
RunE: check,
}
cCmd.Flags().StringP("root", "r", "", "Path to the root directory of the decomposedfs")
err := cCmd.MarkFlagRequired("root")
if err != nil {
fmt.Println(err)
}
cCmd.Flags().StringP("node", "n", "", "Space ID of the Space to inspect")
err = cCmd.MarkFlagRequired("node")
if err != nil {
fmt.Println(err)
}
cCmd.Flags().Bool("repair", false, "Try to repair nodes with incorrect treesize metadata. IMPORTANT: Only use this while OpenCloud is not running.")
cCmd.Flags().Bool("force", false, "Do not prompt for confirmation when running in repair mode.")
return cCmd
}
func check(c *cli.Context) error {
rootFlag := c.String("root")
repairFlag := c.Bool("repair")
func check(cmd *cobra.Command, args []string) error {
rootFlag := cmd.Flag("root").Value.String()
repairFlag := cmd.Flag("repair").Changed
if repairFlag && !c.Bool("force") {
if repairFlag && !cmd.Flag("force").Changed {
answer := strings.ToLower(stringPrompt("IMPORTANT: Only use '--repair' when OpenCloud is not running. Do you want to continue? [yes | no = default]"))
if answer != "yes" && answer != "y" {
return nil
}
}
lu, backend := getBackend(c)
lu, backend := getBackend(cmd)
o := &options.Options{
MetadataBackend: backend.Name(),
MaxConcurrency: 100,
@@ -100,7 +90,7 @@ func check(c *cli.Context) error {
tree := tree.New(lu, bs, o, permissions.Permissions{}, store.Create(), &zerolog.Logger{})
nId := c.String("node")
nId := cmd.Flag("node").Value.String()
n, err := lu.NodeFromSpaceID(context.Background(), nId)
if err != nil || !n.Exists {
fmt.Println("Can not find node '" + nId + "'")
@@ -116,7 +106,7 @@ func check(c *cli.Context) error {
})
treeSize, err := walkTree(ctx, tree, lu, n, repairFlag)
treesizeFromMetadata, err := n.GetTreeSize(c.Context)
treesizeFromMetadata, err := n.GetTreeSize(cmd.Context())
if err != nil {
fmt.Printf("failed to read treesize of node: %s: %s\n", n.ID, err)
}
@@ -126,7 +116,7 @@ func check(c *cli.Context) error {
if repairFlag {
fmt.Printf("Fixing tree size for node: %s. Calculated treesize: %d\n",
n.ID, treeSize)
n.SetTreeSize(c.Context, treeSize)
n.SetTreeSize(cmd.Context(), treeSize)
}
}
return nil
@@ -185,105 +175,83 @@ func walkTree(ctx context.Context, tree *tree.Tree, lu *lookup.Lookup, root *nod
return treesize, nil
}
func metadataCmd(cfg *config.Config) *cli.Command {
return &cli.Command{
Name: "metadata",
Usage: `cli tools to inspect and manipulate node metadata`,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "root",
Aliases: []string{"r"},
Required: true,
Usage: "Path to the decomposedfs",
},
&cli.StringFlag{
Name: "node",
Required: true,
Aliases: []string{"n"},
Usage: "Path to or ID of the node to inspect",
},
},
Subcommands: []*cli.Command{dumpCmd(cfg), getCmd(cfg), setCmd(cfg)},
func metadataCmd(cfg *config.Config) *cobra.Command {
metaCmd := &cobra.Command{
Use: "metadata",
Short: `cli tools to inspect and manipulate node metadata`,
}
metaCmd.AddCommand(dumpCmd(cfg), getCmd(cfg), setCmd(cfg))
metaCmd.Flags().StringP("root", "r", "", "Path to the root directory of the decomposedfs")
err := metaCmd.MarkFlagRequired("root")
if err != nil {
fmt.Println(err)
}
metaCmd.Flags().StringP("node", "n", "", "Path to or ID of the node to inspect")
err = metaCmd.MarkFlagRequired("node")
if err != nil {
fmt.Println(err)
}
return metaCmd
}
func dumpCmd(cfg *config.Config) *cli.Command {
return &cli.Command{
Name: "dump",
Usage: `print the metadata of the given node. String attributes will be enclosed in quotes. Binary attributes will be returned encoded as base64 with their value being prefixed with '0s'.`,
Action: func(c *cli.Context) error {
lu, backend := getBackend(c)
path, err := getNode(c, lu)
func dumpCmd(cfg *config.Config) *cobra.Command {
return &cobra.Command{
Use: "dump",
Short: `print the metadata of the given node. String attributes will be enclosed in quotes. Binary attributes will be returned encoded as base64 with their value being prefixed with '0s'.`,
RunE: func(cmd *cobra.Command, args []string) error {
lu, backend := getBackend(cmd)
path, err := getNode(cmd, lu)
if err != nil {
return err
}
attribs, err := backend.All(c.Context, path)
attribs, err := backend.All(cmd.Context(), path)
if err != nil {
fmt.Println("Error reading attributes")
return err
}
printAttribs(attribs, c.String("attribute"))
printAttribs(attribs, cmd.Flag("attribute").Value.String())
return nil
},
}
}
func getCmd(cfg *config.Config) *cli.Command {
return &cli.Command{
Name: "get",
Usage: `print a specific attribute of the given node. String attributes will be enclosed in quotes. Binary attributes will be returned encoded as base64 with their value being prefixed with '0s'.`,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "attribute",
Aliases: []string{"a"},
Usage: "attribute to inspect",
},
},
Action: func(c *cli.Context) error {
lu, backend := getBackend(c)
path, err := getNode(c, lu)
func getCmd(cfg *config.Config) *cobra.Command {
gCmd := &cobra.Command{
Use: "get",
Short: `print a specific attribute of the given node. String attributes will be enclosed in quotes. Binary attributes will be returned encoded as base64 with their value being prefixed with '0s'.`,
RunE: func(cmd *cobra.Command, args []string) error {
lu, backend := getBackend(cmd)
path, err := getNode(cmd, lu)
if err != nil {
return err
}
attribs, err := backend.All(c.Context, path)
attribs, err := backend.All(cmd.Context(), path)
if err != nil {
fmt.Println("Error reading attributes")
return err
}
printAttribs(attribs, c.String("attribute"))
printAttribs(attribs, cmd.Flag("attribute").Value.String())
return nil
},
}
gCmd.Flags().StringP("attribute", "a", "", "attribute to inspect, can be a glob pattern (e.g. 'user.*' will match all attributes starting with 'user.').")
return gCmd
}
func setCmd(cfg *config.Config) *cli.Command {
return &cli.Command{
Name: "set",
Usage: `manipulate metadata of the given node. Binary attributes can be given hex encoded (prefix by '0x') or base64 encoded (prefix by '0s').`,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "attribute",
Required: true,
Aliases: []string{"a"},
Usage: "attribute to inspect",
},
&cli.StringFlag{
Name: "value",
Required: true,
Aliases: []string{"v"},
Usage: "value to set",
},
},
Action: func(c *cli.Context) error {
lu, backend := getBackend(c)
n, err := getNode(c, lu)
func setCmd(cfg *config.Config) *cobra.Command {
sCmd := &cobra.Command{
Use: "set",
Short: `manipulate metadata of the given node. Binary attributes can be given hex encoded (prefix by '0x') or base64 encoded (prefix by '0s').`,
RunE: func(cmd *cobra.Command, args []string) error {
lu, backend := getBackend(cmd)
n, err := getNode(cmd, lu)
if err != nil {
return err
}
v := c.String("value")
v := cmd.Flag("value").Value.String()
if strings.HasPrefix(v, "0s") {
b64, err := base64.StdEncoding.DecodeString(v[2:])
if err == nil {
@@ -300,7 +268,7 @@ func setCmd(cfg *config.Config) *cli.Command {
}
}
err = backend.Set(c.Context, n, c.String("attribute"), []byte(v))
err = backend.Set(cmd.Context(), n, cmd.Flag("attribute").Value.String(), []byte(v))
if err != nil {
fmt.Println("Error setting attribute")
return err
@@ -308,6 +276,19 @@ func setCmd(cfg *config.Config) *cli.Command {
return nil
},
}
sCmd.Flags().StringP("attribute", "a", "", "attribute to inspect, can be a glob pattern (e.g. 'user.*' will match all attributes starting with 'user.').")
err := sCmd.MarkFlagRequired("attribute")
if err != nil {
fmt.Println(err)
}
sCmd.Flags().StringP("value", "v", "", "value to set")
err = sCmd.MarkFlagRequired("value")
if err != nil {
fmt.Println(err)
}
return sCmd
}
func backend(root, backend string) metadata.Backend {
@@ -320,8 +301,8 @@ func backend(root, backend string) metadata.Backend {
return metadata.NullBackend{}
}
func getBackend(c *cli.Context) (*lookup.Lookup, metadata.Backend) {
rootFlag := c.String("root")
func getBackend(cmd *cobra.Command) (*lookup.Lookup, metadata.Backend) {
rootFlag := cmd.Flag("root").Value.String()
bod := lookup.DetectBackendOnDisk(rootFlag)
backend := backend(rootFlag, bod)
@@ -332,8 +313,8 @@ func getBackend(c *cli.Context) (*lookup.Lookup, metadata.Backend) {
return lu, backend
}
func getNode(c *cli.Context, lu *lookup.Lookup) (*node.Node, error) {
nodeFlag := c.String("node")
func getNode(cmd *cobra.Command, lu *lookup.Lookup) (*node.Node, error) {
nodeFlag := cmd.Flag("node").Value.String()
id, err := storagespace.ParseID(nodeFlag)
if err != nil {