diff --git a/ocis/pkg/command/revisions.go b/ocis/pkg/command/revisions.go index 7de6e3367be..174a9df3a6a 100644 --- a/ocis/pkg/command/revisions.go +++ b/ocis/pkg/command/revisions.go @@ -3,9 +3,12 @@ package command import ( "errors" "fmt" + "path/filepath" ocisbs "github.com/cs3org/reva/v2/pkg/storage/fs/ocis/blobstore" + "github.com/cs3org/reva/v2/pkg/storage/fs/posix/lookup" s3bs "github.com/cs3org/reva/v2/pkg/storage/fs/s3ng/blobstore" + "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/owncloud/ocis/v2/ocis-pkg/config" "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" "github.com/owncloud/ocis/v2/ocis-pkg/config/parser" @@ -14,6 +17,11 @@ import ( "github.com/urfave/cli/v2" ) +var ( + // _nodesGlobPattern is the glob pattern to find all nodes + _nodesGlobPattern = "spaces/*/*/*/*/*/*/*/*" +) + // RevisionsCommand is the entrypoint for the revisions command. func RevisionsCommand(cfg *config.Config) *cli.Command { return &cli.Command{ @@ -36,7 +44,7 @@ func RevisionsCommand(cfg *config.Config) *cli.Command { func PurgeRevisionsCommand(cfg *config.Config) *cli.Command { return &cli.Command{ Name: "purge", - Usage: "purge all revisions", + Usage: "purge revisions", Flags: []cli.Flag{ &cli.StringFlag{ Name: "basepath", @@ -47,7 +55,7 @@ func PurgeRevisionsCommand(cfg *config.Config) *cli.Command { &cli.StringFlag{ Name: "blobstore", Aliases: []string{"b"}, - Usage: "the blobstore type. Can be (none, ocis, s3ng). Default ocis", + Usage: "the blobstore type. Can be (none, ocis, s3ng). Default ocis. Note: When using s3ng this needs same configuration as the storage-users service", Value: "ocis", }, &cli.BoolFlag{ @@ -61,6 +69,11 @@ func PurgeRevisionsCommand(cfg *config.Config) *cli.Command { Usage: "print verbose output", Value: false, }, + &cli.StringFlag{ + Name: "resource-id", + Aliases: []string{"r"}, + Usage: "purge all revisions of this file/space. If not set, all revisions will be purged", + }, }, Action: func(c *cli.Context) error { basePath := c.String("basepath") @@ -94,7 +107,14 @@ func PurgeRevisionsCommand(cfg *config.Config) *cli.Command { fmt.Println(err) return err } - if err := revisions.PurgeRevisions(basePath, bs, c.Bool("dry-run"), c.Bool("verbose")); err != nil { + + p, err := generatePath(basePath, c.String("resource-id")) + if err != nil { + fmt.Printf("❌ Error parsing resourceID: %s", err) + return err + } + + if err := revisions.PurgeRevisions(p, bs, c.Bool("dry-run"), c.Bool("verbose")); err != nil { fmt.Printf("❌ Error purging revisions: %s", err) return err } @@ -104,6 +124,29 @@ func PurgeRevisionsCommand(cfg *config.Config) *cli.Command { } } +func generatePath(basePath string, resourceID string) (string, error) { + if resourceID == "" { + return filepath.Join(basePath, _nodesGlobPattern), nil + } + + rid, err := storagespace.ParseID(resourceID) + if err != nil { + return "", err + } + + sid := lookup.Pathify(rid.GetSpaceId(), 1, 2) + if sid == "" { + sid = "*/*" + } + + nid := lookup.Pathify(rid.GetOpaqueId(), 4, 2) + if nid == "" { + nid = "*/*/*/*/" + } + + return filepath.Join(basePath, "spaces", sid, "nodes", nid+"*"), nil +} + func init() { register.AddCommand(RevisionsCommand) } diff --git a/ocis/pkg/revisions/revisions.go b/ocis/pkg/revisions/revisions.go index f30eca457db..249dbf37e1c 100644 --- a/ocis/pkg/revisions/revisions.go +++ b/ocis/pkg/revisions/revisions.go @@ -7,14 +7,13 @@ import ( "os" "path/filepath" "regexp" + "strings" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node" "github.com/shamaton/msgpack/v2" ) var ( - // _nodesGlobPattern is the glob pattern to find all nodes - _nodesGlobPattern = "spaces/*/*/*/*/*/*/*/*" // regex to determine if a node versioned. Examples: // 9113a718-8285-4b32-9042-f930f1a58ac2.REV.2024-05-22T07:32:53.89969726Z // 9113a718-8285-4b32-9042-f930f1a58ac2.REV.2024-05-22T07:32:53.89969726Z.mpk @@ -28,8 +27,7 @@ type DelBlobstore interface { } // PurgeRevisions removes all revisions from a storage provider. -func PurgeRevisions(p string, bs DelBlobstore, dryRun bool, verbose bool) error { - pattern := filepath.Join(p, _nodesGlobPattern) +func PurgeRevisions(pattern string, bs DelBlobstore, dryRun bool, verbose bool) error { if verbose { fmt.Println("Looking for nodes in", pattern) } @@ -87,15 +85,22 @@ func PurgeRevisions(p string, bs DelBlobstore, dryRun bool, verbose bool) error countFiles++ if verbose { + spaceID, nodeID := getIDsFromPath(d) if dryRun { - fmt.Println("Would delete", d) + fmt.Println("Would delete") + fmt.Println("\tResourceID:", spaceID+"!"+nodeID) + fmt.Println("\tSpaceID:", spaceID) + fmt.Println("\tPath:", d) if blobID != "" { - fmt.Println("Would delete blob", blobID) + fmt.Println("\tBlob:", blobID) } } else { - fmt.Println("Deleted", d) + fmt.Println("Deleted") + fmt.Println("\tResourceID:", spaceID+"!"+nodeID) + fmt.Println("\tSpaceID:", spaceID) + fmt.Println("\tPath:", d) if blobID != "" { - fmt.Println("Deleted blob", blobID) + fmt.Println("\tBlob:", blobID) } } } @@ -129,3 +134,24 @@ func getBlobID(path string) (string, error) { return "", nil } + +func getIDsFromPath(path string) (string, string) { + rawIDs := strings.Split(path, "/nodes/") + if len(rawIDs) != 2 { + return "", "" + } + + s := strings.Split(rawIDs[0], "/spaces/") + if len(s) != 2 { + return "", "" + } + + n := strings.Split(rawIDs[1], ".REV.") + if len(n) != 2 { + return "", "" + } + + spaceID := strings.Replace(s[1], "/", "", -1) + nodeID := strings.Replace(n[0], "/", "", -1) + return spaceID, filepath.Base(nodeID) +}