mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-24 16:41:35 -04:00
Add command for inspecting and manipulating node metadata (#5858)
* Add command for inspecting and manipulating node metadata * Add changelog * Bump reva
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
Enhancement: add 'ocis decomposedfs metadata' command
|
||||
|
||||
We added a 'ocis decomposedfs metadata' command for inspecting and manipulating
|
||||
node metadata.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/5858
|
||||
2
go.mod
2
go.mod
@@ -11,7 +11,7 @@ require (
|
||||
github.com/blevesearch/bleve/v2 v2.3.6
|
||||
github.com/coreos/go-oidc/v3 v3.4.0
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20221012090518-ef2996678965
|
||||
github.com/cs3org/reva/v2 v2.12.1-0.20230315220346-44e55cb0de0a
|
||||
github.com/cs3org/reva/v2 v2.12.1-0.20230316154023-890c222e8de8
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/gabriel-vasile/mimetype v1.4.1
|
||||
github.com/ggwhite/go-masker v1.0.9
|
||||
|
||||
4
go.sum
4
go.sum
@@ -618,8 +618,8 @@ github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo
|
||||
github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4=
|
||||
github.com/crewjam/saml v0.4.10 h1:Rjs6x4s/aQFXiaPjw3uhB4VdxRqoxHXOJrrj4BsMn9o=
|
||||
github.com/crewjam/saml v0.4.10/go.mod h1:9Zh6dWPtB3MSzTRt8fIFH60Z351QQ+s7hCU3J/tTlA4=
|
||||
github.com/cs3org/reva/v2 v2.12.1-0.20230315220346-44e55cb0de0a h1:XMIKlCSPVpnFaE+wWCqSPLNwEpwiGSQnBYK/vulzhv4=
|
||||
github.com/cs3org/reva/v2 v2.12.1-0.20230315220346-44e55cb0de0a/go.mod h1:FNAYs5H3xs8v0OFmNgZtiMAzIMXd/6TJmO0uZuNn8pQ=
|
||||
github.com/cs3org/reva/v2 v2.12.1-0.20230316154023-890c222e8de8 h1:Gh1piputuyWbjATcRJ5liXCS+x6xc+X2HpR/3l0TUgM=
|
||||
github.com/cs3org/reva/v2 v2.12.1-0.20230316154023-890c222e8de8/go.mod h1:FNAYs5H3xs8v0OFmNgZtiMAzIMXd/6TJmO0uZuNn8pQ=
|
||||
github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI=
|
||||
github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY=
|
||||
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
||||
|
||||
225
ocis/pkg/command/decomposedfs.go
Normal file
225
ocis/pkg/command/decomposedfs.go
Normal file
@@ -0,0 +1,225 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/lookup"
|
||||
"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata"
|
||||
"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/options"
|
||||
"github.com/cs3org/reva/v2/pkg/storagespace"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/config"
|
||||
"github.com/owncloud/ocis/v2/ocis/pkg/register"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// 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)},
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
register.AddCommand(DecomposedfsCommand)
|
||||
}
|
||||
|
||||
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 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 := getPath(c, lu)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
attribs, err := backend.All(path)
|
||||
if err != nil {
|
||||
fmt.Println("Error reading attributes")
|
||||
return err
|
||||
}
|
||||
printAttribs(attribs, c.String("attribute"))
|
||||
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 := getPath(c, lu)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
attribs, err := backend.All(path)
|
||||
if err != nil {
|
||||
fmt.Println("Error reading attributes")
|
||||
return err
|
||||
}
|
||||
printAttribs(attribs, c.String("attribute"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
path, err := getPath(c, lu)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v := c.String("value")
|
||||
if strings.HasPrefix(v, "0s") {
|
||||
b64, err := base64.StdEncoding.DecodeString(v[2:])
|
||||
if err == nil {
|
||||
v = string(b64)
|
||||
}
|
||||
} else if strings.HasPrefix(v, "0x") {
|
||||
h, err := hex.DecodeString(v)
|
||||
if err == nil {
|
||||
v = string(h)
|
||||
}
|
||||
}
|
||||
|
||||
err = backend.Set(path, c.String("attribute"), []byte(v[2:]))
|
||||
if err != nil {
|
||||
fmt.Println("Error setting attribute")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func backend(root, backend string) metadata.Backend {
|
||||
switch backend {
|
||||
case "xattrs":
|
||||
return metadata.XattrsBackend{}
|
||||
case "mpk":
|
||||
return metadata.NewMessagePackBackend(root, options.CacheOptions{})
|
||||
}
|
||||
return metadata.NullBackend{}
|
||||
}
|
||||
|
||||
func getBackend(c *cli.Context) (*lookup.Lookup, metadata.Backend) {
|
||||
rootFlag := c.String("root")
|
||||
|
||||
bod := lookup.DetectBackendOnDisk(rootFlag)
|
||||
backend := backend(rootFlag, bod)
|
||||
lu := lookup.New(backend, &options.Options{
|
||||
Root: rootFlag,
|
||||
MetadataBackend: bod,
|
||||
})
|
||||
return lu, backend
|
||||
}
|
||||
|
||||
func getPath(c *cli.Context, lu *lookup.Lookup) (string, error) {
|
||||
nodeFlag := c.String("node")
|
||||
|
||||
path := ""
|
||||
if strings.HasPrefix(nodeFlag, "/") {
|
||||
path = nodeFlag
|
||||
} else {
|
||||
nId := c.String("node")
|
||||
id, err := storagespace.ParseID(nId)
|
||||
if err != nil {
|
||||
fmt.Println("Invalid node id.")
|
||||
return "", err
|
||||
}
|
||||
n, _ := lu.NodeFromID(context.Background(), &id)
|
||||
if err != nil || !n.Exists {
|
||||
fmt.Println("Can not find node '" + nId + "'")
|
||||
return "", err
|
||||
}
|
||||
path = n.InternalPath()
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func printAttribs(attribs map[string][]byte, onlyAttribute string) {
|
||||
if onlyAttribute != "" {
|
||||
fmt.Println(onlyAttribute + `=` + attribToString(attribs[onlyAttribute]))
|
||||
return
|
||||
}
|
||||
|
||||
names := []string{}
|
||||
for k, _ := range attribs {
|
||||
names = append(names, k)
|
||||
}
|
||||
|
||||
sort.Strings(names)
|
||||
|
||||
for _, n := range names {
|
||||
fmt.Println(n + `=` + attribToString(attribs[n]))
|
||||
}
|
||||
}
|
||||
|
||||
func attribToString(attrib []byte) string {
|
||||
for i := 0; i < len(attrib); i++ {
|
||||
if attrib[i] < 32 || attrib[i] >= 127 {
|
||||
return "0s" + base64.StdEncoding.EncodeToString(attrib)
|
||||
}
|
||||
}
|
||||
return `"` + string(attrib) + `"`
|
||||
}
|
||||
Reference in New Issue
Block a user