Skip to content

Commit

Permalink
go/oasis-node: Add storage namespace rename subcommand
Browse files Browse the repository at this point in the history
  • Loading branch information
kostko committed Apr 30, 2021
1 parent 05402d8 commit ffd0af2
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 0 deletions.
1 change: 1 addition & 0 deletions .changelog/3908.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
go/oasis-node: Add storage namespace rename subcommand
53 changes: 53 additions & 0 deletions go/oasis-node/cmd/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"time"

"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/oasisprotocol/oasis-core/go/common"
"github.com/oasisprotocol/oasis-core/go/common/crypto/hash"
"github.com/oasisprotocol/oasis-core/go/common/logging"
cmdCommon "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common"
Expand Down Expand Up @@ -40,6 +43,12 @@ var (
RunE: doCheck,
}

storageRenameNsCmd = &cobra.Command{
Use: "rename-ns <src-ns> <dst-ns>",
Short: "change the namespace of a runtime database",
RunE: doRenameNs,
}

logger = logging.GetLogger("cmd/storage")

pretty = cmdCommon.Isatty(1)
Expand Down Expand Up @@ -220,11 +229,55 @@ func doCheck(cmd *cobra.Command, args []string) error {
return nil
}

func doRenameNs(cmd *cobra.Command, args []string) error {
dataDir := cmdCommon.DataDir()

if len(args) != 2 {
return fmt.Errorf("need exactly two arguments (source and destination runtime IDs)")
}

var srcID, dstID common.Namespace
if err := srcID.UnmarshalHex(args[0]); err != nil {
return fmt.Errorf("malformed source runtime ID: %s", args[0])
}
if err := dstID.UnmarshalHex(args[1]); err != nil {
return fmt.Errorf("malformed source runtime ID: %s", args[0])
}
if pretty {
fmt.Printf("Renaming storage database for runtime from %s to %s...\n", srcID, dstID)
}

srcDir := registry.GetRuntimeStateDir(dataDir, srcID)
dstDir := registry.GetRuntimeStateDir(dataDir, dstID)

nodeCfg := &db.Config{
DB: workerStorage.GetLocalBackendDBDir(srcDir, viper.GetString(workerStorage.CfgBackend)),
Namespace: srcID,
}

err := badger.RenameNamespace(nodeCfg, dstID)
if err != nil {
return fmt.Errorf("failed to rename namespace: %w", err)
}

if err = os.Rename(srcDir, dstDir); err != nil {
return fmt.Errorf("failed to move directory: %w", err)
}

// Remove history directory as that will be invalid now.
if err = os.RemoveAll(filepath.Join(dstDir, history.DbFilename)); err != nil {
return fmt.Errorf("failed to remove history directory: %w", err)
}

return nil
}

// Register registers the client sub-command and all of its children.
func Register(parentCmd *cobra.Command) {
storageMigrateCmd.Flags().AddFlagSet(registry.Flags)
storageCheckCmd.Flags().AddFlagSet(registry.Flags)
storageCmd.AddCommand(storageMigrateCmd)
storageCmd.AddCommand(storageCheckCmd)
storageCmd.AddCommand(storageRenameNsCmd)
parentCmd.AddCommand(storageCmd)
}
74 changes: 74 additions & 0 deletions go/storage/mkvs/db/badger/rename.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package badger

import (
"fmt"

"github.com/dgraph-io/badger/v2"

"github.com/oasisprotocol/oasis-core/go/common"
"github.com/oasisprotocol/oasis-core/go/common/cbor"
"github.com/oasisprotocol/oasis-core/go/common/logging"
"github.com/oasisprotocol/oasis-core/go/storage/mkvs/db/api"
)

// RenameNamespace changes the namespace specified in the database.
func RenameNamespace(cfg *api.Config, newNamespace common.Namespace) error {
db := &badgerNodeDB{
logger: logging.GetLogger("mkvs/db/badger/rename"),
namespace: cfg.Namespace,
discardWriteLogs: cfg.DiscardWriteLogs,
}
opts := commonConfigToBadgerOptions(cfg, db)

var err error
if db.db, err = badger.OpenManaged(opts); err != nil {
return fmt.Errorf("failed to open database: %w", err)
}
defer db.Close()

tx := db.db.NewTransactionAt(tsMetadata, true)
defer tx.Discard()

item, err := tx.Get(metadataKeyFmt.Encode())
switch err {
case nil:
case badger.ErrKeyNotFound:
// Nothing to rename.
return nil
default:
return err
}

var meta metadata
err = item.Value(func(data []byte) error {
return cbor.UnmarshalTrusted(data, &meta.value)
})
if err != nil {
return fmt.Errorf("failed to load database metadata: %w", err)
}

// Sanity checks.
if meta.value.Version != dbVersion {
return fmt.Errorf("incompatible database version (expected: %d got: %d)",
dbVersion,
meta.value.Version,
)
}
if !meta.value.Namespace.Equal(&cfg.Namespace) {
return fmt.Errorf("incompatible namespace (expected: %s got: %s)",
cfg.Namespace,
meta.value.Namespace,
)
}

// Rename the namespace in database metadata.
meta.value.Namespace = newNamespace
if err = meta.save(tx); err != nil {
return fmt.Errorf("failed to save database metadata: %w", err)
}
if err = tx.CommitAt(tsMetadata, nil); err != nil {
return fmt.Errorf("failed to commit database metadata: %w", err)
}

return nil
}
47 changes: 47 additions & 0 deletions go/storage/mkvs/db/badger/rename_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package badger

import (
"io/ioutil"
"os"
"testing"

"github.com/stretchr/testify/require"

"github.com/oasisprotocol/oasis-core/go/common"
"github.com/oasisprotocol/oasis-core/go/storage/mkvs/db/api"
)

func TestRenameNamespace(t *testing.T) {
require := require.New(t)

srcNs := common.NewTestNamespaceFromSeed([]byte("badger node db test ns 1"), 0)
dstNs := common.NewTestNamespaceFromSeed([]byte("badger node db test ns 2"), 0)

// Create a new random temporary directory under /tmp.
dir, err := ioutil.TempDir("", "mkvs.test.badger")
require.NoError(err, "TempDir")
defer os.RemoveAll(dir)

dbCfg = &api.Config{
DB: dir,
Namespace: srcNs,
MaxCacheSize: 16 * 1024 * 1024,
NoFsync: true,
}

ndb, err := New(dbCfg)
require.NoError(err, "New(srcNs)")
ndb.Close()

err = RenameNamespace(dbCfg, dstNs)
require.NoError(err, "RenameNamespace")

_, err = New(dbCfg)
require.Error(err, "New(srcNs) should fail on renamed database")

dbCfg.Namespace = dstNs

ndb, err = New(dbCfg)
require.NoError(err, "New(dstNs)")
ndb.Close()
}

0 comments on commit ffd0af2

Please sign in to comment.