feat: make space2share migration repeatable

This commit is contained in:
micbar committed 2026-10-09 14:17:19 +02:00
1 parent 84e10b3879
commit d3703fd855
1 file changed
+96
+96
View File
@@ -3,6 +3,9 @@ package command
import (
"context"
"errors"
"fmt"
"slices"
"strings"
"time"
"github.com/spf13/viper"
@@ -16,6 +19,7 @@ import (
sharingparser "github.com/opencloud-eu/opencloud/services/sharing/pkg/config/parser"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
"github.com/opencloud-eu/reva/v2/pkg/share/manager/jsoncs3"
migration "github.com/opencloud-eu/reva/v2/pkg/share/manager/jsoncs3/migrations"
"github.com/opencloud-eu/reva/v2/pkg/share/manager/registry"
"github.com/opencloud-eu/reva/v2/pkg/utils"
@@ -31,6 +35,10 @@ import (
// this executions is not returned this timeout is needed
const cleanupTimeout = 1 * time.Minute
// migrationTimeout bounds a forced migration run. Importing the grants of many
// project spaces is not fast, so this is generous.
const migrationTimeout = 30 * time.Minute
// SharesCommand is the entrypoint for the groups command.
func SharesCommand(cfg *config.Config) *cobra.Command {
sharesCmd := &cobra.Command{
@@ -48,6 +56,7 @@ func SharesCommand(cfg *config.Config) *cobra.Command {
},
}
sharesCmd.AddCommand(cleanupCmd(cfg))
sharesCmd.AddCommand(migrateCmd(cfg))
return sharesCmd
}
@@ -139,6 +148,93 @@ func cleanup(_ *cobra.Command, cfg *config.Config) error {
return nil
}
const migrationWaitTimeout = 30 * time.Minute
func migrateCmd(cfg *config.Config) *cobra.Command {
migrateCmd := &cobra.Command{
Use: "migrate [migration]",
Args: func(cmd *cobra.Command, args []string) error {
if list, _ := cmd.Flags().GetBool("list"); list {
return nil
}
if len(args) != 1 {
return fmt.Errorf("name the migration to run, one of: %s", strings.Join(migration.Names(), ", "))
}
// reject an unknown name before touching the configuration
if !slices.Contains(migration.Names(), args[0]) {
return fmt.Errorf("unknown migration %q, one of: %s", args[0], strings.Join(migration.Names(), ", "))
}
return nil
},
Short: `re-run share manager migrations that have already been applied.`,
Long: `Runs a share manager migration by name, whether or not it has been applied
before. On startup a migration runs only once; this runs the one you name.
The case this exists for is import_space_members: it reads the grants of every
project space from the storage provider and imports them into the share
manager. Space memberships that were written as storage grants by something
other than OpenCloud are invisible until that has run. The import is additive,
so running it again is safe.
Use --list to see the available migrations.`,
PreRunE: func(cmd *cobra.Command, args []string) error {
// listing the migrations needs no server configuration
if list, _ := cmd.Flags().GetBool("list"); list {
return nil
}
if err := parser.ParseConfig(cfg, true); err != nil {
return configlog.ReturnError(err)
}
cfg.Sharing.Commons = cfg.Commons
return configlog.ReturnError(sharingparser.ParseConfig(cfg.Sharing))
},
RunE: func(cmd *cobra.Command, args []string) error {
return migrate(cmd, cfg, args)
},
}
migrateCmd.Flags().BoolP("list", "l", false, "list the available migrations and exit")
_ = viper.BindPFlag("list", migrateCmd.Flags().Lookup("list"))
return migrateCmd
}
func migrate(_ *cobra.Command, cfg *config.Config, args []string) error {
if viper.GetBool("list") {
for _, name := range migration.Names() {
fmt.Println(name)
}
return nil
}
driver := cfg.Sharing.UserSharingDriver
if driver != "jsoncs3" {
return configlog.ReturnError(errors.New("migrate is only implemented for the jsoncs3 share manager"))
}
l := logger("migrate")
zerolog.SetGlobalLevel(zerolog.InfoLevel)
// Initialize registry to make service lookup work
_ = mregistry.GetRegistry()
rcfg := revaShareConfig(cfg.Sharing)
f, ok := registry.NewFuncs[driver]
if !ok {
return configlog.ReturnError(errors.New("Unknown share manager type '" + driver + "'"))
}
mgr, err := f(rcfg[driver].(map[string]any), &l)
if err != nil {
return configlog.ReturnError(err)
}
ctx, cancel := context.WithTimeout(l.WithContext(context.Background()), migrationTimeout)
defer cancel()
return configlog.ReturnError(mgr.(*jsoncs3.Manager).RunMigration(ctx, args[0]))
}
func revaShareConfig(cfg *sharing.Config) map[string]any {
return map[string]any{
"json": map[string]any{