mirror of
https://github.com/lightninglabs/lightning-terminal.git
synced 2026-08-13 12:33:36 +02:00
Merge pull request #1315 from ViktorT-11/2026-05-prompt-user-when-migrating
Some checks are pending
CI / frontend tests on macOS-latest (push) Waiting to run
CI / frontend tests on ubuntu-latest (push) Waiting to run
CI / frontend tests on windows-latest (push) Waiting to run
CI / backend build on macOS-latest (push) Waiting to run
CI / backend build on ubuntu-latest (push) Waiting to run
CI / backend build on windows-latest (push) Waiting to run
CI / cross compilation (push) Waiting to run
CI / cross compilation-1 (push) Waiting to run
CI / cross compilation-2 (push) Waiting to run
CI / RPC proto compilation check (push) Waiting to run
CI / check commits (push) Waiting to run
CI / Sqlc check (push) Waiting to run
CI / lint (push) Waiting to run
CI / run unit tests (push) Waiting to run
CI / run unit tests-1 (push) Waiting to run
CI / run unit tests-2 (push) Waiting to run
CI / run unit tests-3 (push) Waiting to run
CI / build itest binaries (push) Waiting to run
CI / integration test (push) Blocked by required conditions
CI / integration test-1 (push) Blocked by required conditions
CI / integration test-2 (push) Blocked by required conditions
CI / check release notes updated (push) Waiting to run
Some checks are pending
CI / frontend tests on macOS-latest (push) Waiting to run
CI / frontend tests on ubuntu-latest (push) Waiting to run
CI / frontend tests on windows-latest (push) Waiting to run
CI / backend build on macOS-latest (push) Waiting to run
CI / backend build on ubuntu-latest (push) Waiting to run
CI / backend build on windows-latest (push) Waiting to run
CI / cross compilation (push) Waiting to run
CI / cross compilation-1 (push) Waiting to run
CI / cross compilation-2 (push) Waiting to run
CI / RPC proto compilation check (push) Waiting to run
CI / check commits (push) Waiting to run
CI / Sqlc check (push) Waiting to run
CI / lint (push) Waiting to run
CI / run unit tests (push) Waiting to run
CI / run unit tests-1 (push) Waiting to run
CI / run unit tests-2 (push) Waiting to run
CI / run unit tests-3 (push) Waiting to run
CI / build itest binaries (push) Waiting to run
CI / integration test (push) Blocked by required conditions
CI / integration test-1 (push) Blocked by required conditions
CI / integration test-2 (push) Blocked by required conditions
CI / check release notes updated (push) Waiting to run
[sql-71] Prompt user before starting KVDB -> SQL migration
This commit is contained in:
commit
acc578770a
11 changed files with 556 additions and 0 deletions
|
|
@ -89,6 +89,15 @@ func DeprecateKVDB(dbDir string) error {
|
|||
)
|
||||
}
|
||||
|
||||
// HasActiveKVDB reports whether the accounts kvdb file in the given db
|
||||
// directory still holds data that is pending migration to SQL.
|
||||
func HasActiveKVDB(dbDir string) (bool, error) {
|
||||
return tombstone.HasActiveKVDB(
|
||||
filepath.Join(dbDir, DBFilename), accountBucketName,
|
||||
DefaultAccountDBTimeout,
|
||||
)
|
||||
}
|
||||
|
||||
func newBoltStore(dir, fileName string, clock clock.Clock,
|
||||
allowDeprecated bool) (*BoltStore, error) {
|
||||
|
||||
|
|
|
|||
35
config.go
35
config.go
|
|
@ -207,6 +207,17 @@ type Config struct {
|
|||
// account related data.
|
||||
DatabaseBackend string `long:"databasebackend" description:"The database backend to use for storing all account related data." choice:"bbolt" choice:"sqlite" choice:"postgres"`
|
||||
|
||||
// AutoMigrateKVDB enables the legacy kvdb to SQL migration without
|
||||
// showing the startup prompt. The same behavior can also be
|
||||
// requested via the environment fallback; if either is true, the
|
||||
// prompt is bypassed.
|
||||
AutoMigrateKVDB bool `long:"auto-migrate-to-sql" description:"Automatically approve a pending migration of legacy kvdb data to the configured SQL backend without showing the startup prompt."`
|
||||
|
||||
// autoMigrateKVDBApproved is the effective startup prompt bypass
|
||||
// setting after the config flag and environment fallback have been
|
||||
// resolved.
|
||||
autoMigrateKVDBApproved bool
|
||||
|
||||
// Sqlite holds the configuration options for a SQLite database
|
||||
// backend.
|
||||
Sqlite *db.SqliteConfig `group:"sqlite" namespace:"sqlite"`
|
||||
|
|
@ -879,6 +890,11 @@ func loadConfigFile(preCfg *Config, interceptor signal.Interceptor) (*Config,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = readAutoMigrateKVDB(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Now make sure we create the LiT directory if it doesn't yet exist.
|
||||
if err := makeDirectories(litDir); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -1114,6 +1130,25 @@ func readUIPassword(config *Config) error {
|
|||
"variable that contains the password")
|
||||
}
|
||||
|
||||
// readAutoMigrateKVDB resolves whether the legacy kvdb to SQL migration
|
||||
// prompt should be bypassed. The config flag takes precedence; when it is
|
||||
// not set, the environment fallback is consulted.
|
||||
func readAutoMigrateKVDB(config *Config) error {
|
||||
if config.AutoMigrateKVDB {
|
||||
config.autoMigrateKVDBApproved = true
|
||||
return nil
|
||||
}
|
||||
|
||||
autoMigrateKVDB, err := autoMigrateKVDBFromEnv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config.autoMigrateKVDBApproved = autoMigrateKVDB
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildTLSConfigForHttp2(config *Config) (*tls.Config, error) {
|
||||
var tlsConfig *tls.Config
|
||||
|
||||
|
|
|
|||
|
|
@ -85,6 +85,31 @@ func CheckKVDBDeprecated(path string, bucketKey []byte,
|
|||
return nil
|
||||
}
|
||||
|
||||
// HasActiveKVDB reports whether the legacy bbolt database at the given path
|
||||
// exists and has not yet been tombstoned by a SQL migration in the specified
|
||||
// top-level bucket. A missing file or a tombstoned database is reported as
|
||||
// inactive, so callers can use this to detect kvdb state that is still pending
|
||||
// migration to SQL.
|
||||
func HasActiveKVDB(path string, bucketKey []byte,
|
||||
timeout time.Duration) (bool, error) {
|
||||
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
err := CheckKVDBDeprecated(path, bucketKey, timeout)
|
||||
switch {
|
||||
case errors.Is(err, ErrKVDBDeprecated):
|
||||
return false, nil
|
||||
|
||||
case err != nil:
|
||||
return false, err
|
||||
|
||||
default:
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// IsMigrationTombstoneKey returns true if the given key is the kvdb migration
|
||||
// tombstone marker.
|
||||
func IsMigrationTombstoneKey(key []byte) bool {
|
||||
|
|
|
|||
|
|
@ -49,6 +49,16 @@
|
|||
(`databasebackend=postgres`). If the config option is not set, the database
|
||||
backend defaults to SQLite.
|
||||
|
||||
* [Add a startup confirmation prompt for the SQL
|
||||
migration](https://github.com/lightninglabs/lightning-terminal/pull/1315):
|
||||
Before the migration of the BBolt database to SQL is started, an explicit
|
||||
confirmation prompt is displayed. The prompt requires that the user
|
||||
explicitly confirms the migration by inputting "yes" via stdin. Any other
|
||||
input will cause the litd startup to be canceled.
|
||||
For non-interactive startup flows, the prompt can be skipped by setting the
|
||||
`auto-migrate-to-sql=true` config flag or by setting the following environment
|
||||
variable: `LIT_AUTO_MIGRATE_TO_SQL=true`.
|
||||
|
||||
* [BBolt databases are
|
||||
deprecated](https://github.com/lightninglabs/lightning-terminal/pull/1305):
|
||||
Users that explicitly want to continue using the legacy `bbolt` backend must
|
||||
|
|
|
|||
|
|
@ -75,6 +75,15 @@ func DeprecateKVDB(dbDir string) error {
|
|||
)
|
||||
}
|
||||
|
||||
// HasActiveKVDB reports whether the rules kvdb file in the given db directory
|
||||
// still holds data that is pending migration to SQL.
|
||||
func HasActiveKVDB(dbDir string) (bool, error) {
|
||||
return tombstone.HasActiveKVDB(
|
||||
filepath.Join(dbDir, DBFilename), rulesBucketKey,
|
||||
DefaultRulesDBTimeout,
|
||||
)
|
||||
}
|
||||
|
||||
func newBoltDB(dir, fileName string, sessionIDIndex SessionDB,
|
||||
accountsDB AccountsDB, clock clock.Clock,
|
||||
allowDeprecated bool) (*BoltDB, error) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
package itest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
|
@ -123,6 +124,11 @@ func testKvdbSQLMigration(ctx context.Context, net *NetworkHarness,
|
|||
rawConn.Close()
|
||||
|
||||
// Step 4: Restart with configured backend to trigger migration.
|
||||
//
|
||||
// During the startup, the user will be prompted to confirm the
|
||||
// migration by typing yes. We therefore buffer yes to std-in.
|
||||
migNode.stdin = bytes.NewBufferString("yes\n")
|
||||
|
||||
err = net.RestartNode(
|
||||
migNode, func() error { return nil }, []LitArgOption{
|
||||
WithLitArg("databasebackend", *litDBBackend),
|
||||
|
|
@ -183,6 +189,9 @@ func testKvdbSQLMigration(ctx context.Context, net *NetworkHarness,
|
|||
|
||||
// Step 9: Delete the SQL database and verify that starting with the
|
||||
// selected SQL backend reruns the kvdb -> SQL migration successfully.
|
||||
//
|
||||
// Note that we do not buffer yes to std-in again, as the prompt will
|
||||
// not be shown when the bbolt db has already been tombstoned.
|
||||
rerunSQLMigrationAndAssert(
|
||||
t, net, migNode, newStepCtx, newAdminCtx, beforeMigration,
|
||||
migrationRefs,
|
||||
|
|
|
|||
|
|
@ -342,6 +342,7 @@ type HarnessNode struct {
|
|||
cmd *exec.Cmd
|
||||
pidFile string
|
||||
logFile *os.File
|
||||
stdin io.Reader
|
||||
|
||||
// processExit is a channel that's closed once it's detected that the
|
||||
// process this instance of HarnessNode is bound to has exited.
|
||||
|
|
@ -636,6 +637,7 @@ func (hn *HarnessNode) Start(litdBinary string, litdError chan<- error,
|
|||
|
||||
args := hn.Cfg.GenArgs(litArgOpts...)
|
||||
hn.cmd = exec.Command(litdBinary, args...)
|
||||
hn.cmd.Stdin = hn.stdin
|
||||
|
||||
// Redirect stderr output to buffer
|
||||
var errb bytes.Buffer
|
||||
|
|
|
|||
211
migration_prompt.go
Normal file
211
migration_prompt.go
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
package terminal
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/lightninglabs/lightning-terminal/accounts"
|
||||
"github.com/lightninglabs/lightning-terminal/firewalldb"
|
||||
"github.com/lightninglabs/lightning-terminal/session"
|
||||
)
|
||||
|
||||
const autoMigrateKVDBEnvVar = "LIT_AUTO_MIGRATE_TO_SQL"
|
||||
|
||||
var kvdbToSQLMigrationPromptLines = []string{
|
||||
"",
|
||||
"CAUTION: litd is about to migrate your existing data to a new SQL " +
|
||||
"database.",
|
||||
"After this, litd will use the SQL database for your existing data " +
|
||||
"and any new data added after that point.",
|
||||
"However, after the migration you will not be able to switch back " +
|
||||
"to your old database, as it will be incompatible with litd " +
|
||||
"after the migration.",
|
||||
"NOTE: This also means that you will not be able to downgrade litd " +
|
||||
"to a version prior to when SQL database support was added " +
|
||||
"(v0.17.0-alpha).",
|
||||
"",
|
||||
"SQL databases are more performant, quicker to start, and much less " +
|
||||
"prone to database corruption.",
|
||||
"It is therefore strongly recommended that you proceed with this " +
|
||||
"database migration.",
|
||||
"",
|
||||
"If you want to abort the migration and keep using the old database " +
|
||||
"type instead, stop now and restart litd with the following " +
|
||||
"config option set: `databasebackend=bbolt`.",
|
||||
"Please note though that your old database type (bbolt) is " +
|
||||
"deprecated, and support for it will be removed in a future " +
|
||||
"release. Migration to SQL will at that point be mandatory.",
|
||||
"",
|
||||
"If your system cannot enter input here, restart litd with the " +
|
||||
"config option `auto-migrate-to-sql=true` or environment " +
|
||||
"variable `LIT_AUTO_MIGRATE_TO_SQL=true` set to approve the " +
|
||||
"migration automatically.",
|
||||
"",
|
||||
}
|
||||
|
||||
// confirmPendingKVDBToSQLMigration blocks startup until the user explicitly
|
||||
// acknowledges that litd is about to migrate legacy kvdb state to SQL and
|
||||
// tombstone the kvdb files afterwards, unless auto migration is enabled.
|
||||
func (c *Config) confirmPendingKVDBToSQLMigration() error {
|
||||
return c.confirmPendingKVDBToSQLMigrationWithInput(
|
||||
os.Stdin, os.Stderr,
|
||||
)
|
||||
}
|
||||
|
||||
// confirmPendingKVDBToSQLMigrationWithInput is the testable variant of the
|
||||
// startup migration confirmation.
|
||||
func (c *Config) confirmPendingKVDBToSQLMigrationWithInput(
|
||||
input io.Reader, output io.Writer) error {
|
||||
|
||||
// The config layer resolves the prompt bypass setting so startup code
|
||||
// can use the effective value directly.
|
||||
if c.autoMigrateKVDBApproved {
|
||||
return nil
|
||||
}
|
||||
|
||||
hasActiveKVDB, err := hasActiveLegacyKVDB(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !hasActiveKVDB {
|
||||
return nil
|
||||
}
|
||||
|
||||
return promptForKVDBToSQLMigrationConfirmation(input, output)
|
||||
}
|
||||
|
||||
// autoMigrateKVDBFromEnv reads the local environment override that approves
|
||||
// the legacy kvdb to SQL migration without showing the startup prompt.
|
||||
func autoMigrateKVDBFromEnv() (bool, error) {
|
||||
content := strings.TrimSpace(os.Getenv(autoMigrateKVDBEnvVar))
|
||||
if content == "" {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
autoMigrate, err := strconv.ParseBool(content)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("environment variable %s is not a "+
|
||||
"valid boolean: %w", autoMigrateKVDBEnvVar, err)
|
||||
}
|
||||
|
||||
return autoMigrate, nil
|
||||
}
|
||||
|
||||
// hasActiveLegacyKVDB reports whether any legacy LiT kvdb file exists and was
|
||||
// not already tombstoned by a previous SQL migration.
|
||||
func hasActiveLegacyKVDB(cfg *Config) (bool, error) {
|
||||
// The legacy accounts DB follows the macaroon directory, while the
|
||||
// session and rules DBs live under the network-scoped LiT directory.
|
||||
// We mirror those runtime locations here so the prompt checks the same
|
||||
// files that store initialization will later open.
|
||||
networkDir := filepath.Join(cfg.LitDir, cfg.Network)
|
||||
accountsDir := filepath.Dir(cfg.MacaroonPath)
|
||||
|
||||
checks := []struct {
|
||||
name string
|
||||
fn func(string) (bool, error)
|
||||
dir string
|
||||
}{
|
||||
{
|
||||
name: "accounts",
|
||||
fn: accounts.HasActiveKVDB,
|
||||
dir: accountsDir,
|
||||
},
|
||||
{
|
||||
name: "sessions",
|
||||
fn: session.HasActiveKVDB,
|
||||
dir: networkDir,
|
||||
},
|
||||
{
|
||||
name: "rules",
|
||||
fn: firewalldb.HasActiveKVDB,
|
||||
dir: networkDir,
|
||||
},
|
||||
}
|
||||
|
||||
for _, check := range checks {
|
||||
active, err := check.fn(check.dir)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("unable to inspect legacy "+
|
||||
"%s kvdb: %w", check.name, err)
|
||||
}
|
||||
|
||||
if active {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// promptForKVDBToSQLMigrationConfirmation requires a literal "yes" response
|
||||
// before litd continues with a pending kvdb-to-SQL migration.
|
||||
func promptForKVDBToSQLMigrationConfirmation(input io.Reader,
|
||||
output io.Writer) error {
|
||||
|
||||
logKVDBToSQLMigrationPrompt()
|
||||
|
||||
for _, line := range kvdbToSQLMigrationPromptLines {
|
||||
_, err := fmt.Fprintln(output, line)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err := fmt.Fprint(output,
|
||||
"Type \"yes\" to continue with the migration. Any other "+
|
||||
"answer will abort the startup of litd: ",
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(input)
|
||||
answer, err := reader.ReadString('\n')
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
return fmt.Errorf("manual confirmation required before kvdb "+
|
||||
"migration can continue: %w", err)
|
||||
}
|
||||
|
||||
if strings.TrimSpace(answer) != "yes" {
|
||||
return errors.New("manual confirmation declined; refusing to " +
|
||||
"continue kvdb-to-SQL migration")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// logKVDBToSQLMigrationPrompt mirrors the interactive migration warning to
|
||||
// the configured logger so the full operator guidance is preserved in logs.
|
||||
func logKVDBToSQLMigrationPrompt() {
|
||||
for _, line := range kvdbToSQLMigrationPromptLines {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Infof("%s", line)
|
||||
}
|
||||
}
|
||||
|
||||
// sqlMigrationsSkipped reports whether the configured SQL backend will skip
|
||||
// schema migrations during startup. In that case no kvdb-to-SQL migration is
|
||||
// attempted and the startup confirmation prompt must not be shown.
|
||||
func (c *Config) sqlMigrationsSkipped() bool {
|
||||
switch c.DatabaseBackend {
|
||||
case DatabaseBackendSqlite:
|
||||
return c.Sqlite != nil && c.Sqlite.SkipMigrations
|
||||
|
||||
case DatabaseBackendPostgres:
|
||||
return c.Postgres != nil && c.Postgres.SkipMigrations
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
228
migration_prompt_test.go
Normal file
228
migration_prompt_test.go
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
package terminal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btclog/v2"
|
||||
"github.com/lightninglabs/lightning-terminal/accounts"
|
||||
"github.com/lightningnetwork/lnd/clock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestConfirmPendingKVDBToSQLMigration verifies that the startup prompt is
|
||||
// shown only when active legacy kvdb state is about to be migrated.
|
||||
func TestConfirmPendingKVDBToSQLMigration(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
// setsEnv signals that the test mutates the process
|
||||
// environment via t.Setenv, which means t.Parallel can't be
|
||||
// used in the subtest.
|
||||
setsEnv bool
|
||||
|
||||
// setup configures the cfg and creates any on-disk legacy
|
||||
// kvdb state the test needs before the prompt runs.
|
||||
setup func(t *testing.T, cfg *Config, dbDir string)
|
||||
|
||||
// input is the response written to the prompt's stdin.
|
||||
input string
|
||||
|
||||
// expectErr is the substring expected in the error returned
|
||||
// by the prompt function. An empty value asserts no error.
|
||||
expectErr string
|
||||
|
||||
// expectOutput lists substrings that must appear in the
|
||||
// prompt's stdout.
|
||||
expectOutput []string
|
||||
|
||||
// expectNoOutput asserts that nothing was written to the
|
||||
// prompt's stdout, e.g. because the prompt was bypassed.
|
||||
expectNoOutput bool
|
||||
}{
|
||||
{
|
||||
name: "accepts yes for pending migration",
|
||||
setup: func(t *testing.T, _ *Config, dbDir string) {
|
||||
createActiveAccountsKVDB(t, dbDir)
|
||||
},
|
||||
input: "yes\n",
|
||||
expectOutput: []string{
|
||||
"about to migrate", "switch back",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rejects non yes answer",
|
||||
setup: func(t *testing.T, _ *Config, dbDir string) {
|
||||
createActiveAccountsKVDB(t, dbDir)
|
||||
},
|
||||
input: "no\n",
|
||||
expectErr: "manual confirmation declined",
|
||||
},
|
||||
{
|
||||
name: "skips prompt on auto migration config",
|
||||
setup: func(t *testing.T, cfg *Config, dbDir string) {
|
||||
cfg.AutoMigrateKVDB = true
|
||||
require.NoError(t, readAutoMigrateKVDB(cfg))
|
||||
createActiveAccountsKVDB(t, dbDir)
|
||||
},
|
||||
input: "no\n",
|
||||
expectNoOutput: true,
|
||||
},
|
||||
{
|
||||
name: "skips prompt on auto migration environment",
|
||||
setsEnv: true,
|
||||
setup: func(t *testing.T, cfg *Config, dbDir string) {
|
||||
t.Setenv(autoMigrateKVDBEnvVar, "true")
|
||||
require.NoError(t, readAutoMigrateKVDB(cfg))
|
||||
createActiveAccountsKVDB(t, dbDir)
|
||||
},
|
||||
input: "no\n",
|
||||
expectNoOutput: true,
|
||||
},
|
||||
{
|
||||
name: "skips prompt when no legacy kvdb exists",
|
||||
},
|
||||
{
|
||||
name: "skips prompt for tombstoned kvdb",
|
||||
setup: func(t *testing.T, _ *Config, dbDir string) {
|
||||
createActiveAccountsKVDB(t, dbDir)
|
||||
require.NoError(
|
||||
t, accounts.DeprecateKVDB(dbDir),
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "uses macaroon dir for accounts kvdb",
|
||||
setup: func(t *testing.T, cfg *Config, _ string) {
|
||||
customMacDir := filepath.Join(
|
||||
t.TempDir(), "custom",
|
||||
)
|
||||
cfg.MacaroonPath = filepath.Join(
|
||||
customMacDir, "lit.macaroon",
|
||||
)
|
||||
createActiveAccountsKVDB(t, customMacDir)
|
||||
},
|
||||
input: "yes\n",
|
||||
expectOutput: []string{"about to migrate"},
|
||||
},
|
||||
{
|
||||
name: "accepts yes without trailing newline",
|
||||
setup: func(t *testing.T, _ *Config, dbDir string) {
|
||||
createActiveAccountsKVDB(t, dbDir)
|
||||
},
|
||||
input: "yes",
|
||||
expectOutput: []string{"about to migrate"},
|
||||
},
|
||||
{
|
||||
name: "rejects non yes without trailing newline",
|
||||
setup: func(t *testing.T, _ *Config, dbDir string) {
|
||||
createActiveAccountsKVDB(t, dbDir)
|
||||
},
|
||||
input: "no",
|
||||
expectErr: "manual confirmation declined",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if !tc.setsEnv {
|
||||
t.Parallel()
|
||||
}
|
||||
|
||||
cfg, dbDir := testMigrationPromptConfig(t)
|
||||
if tc.setup != nil {
|
||||
tc.setup(t, cfg, dbDir)
|
||||
}
|
||||
|
||||
var output bytes.Buffer
|
||||
err := cfg.confirmPendingKVDBToSQLMigrationWithInput(
|
||||
strings.NewReader(tc.input), &output,
|
||||
)
|
||||
|
||||
if tc.expectErr != "" {
|
||||
require.ErrorContains(t, err, tc.expectErr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
if tc.expectNoOutput {
|
||||
require.Empty(t, output.String())
|
||||
}
|
||||
for _, want := range tc.expectOutput {
|
||||
require.Contains(t, output.String(), want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// The remaining subtests don't exercise the prompt path, so they
|
||||
// don't fit the table above.
|
||||
t.Run("rejects invalid auto migration env value", func(t *testing.T) {
|
||||
// Note we intentionally don't use t.Parallel() here as that
|
||||
// panics with t.Setenv.
|
||||
|
||||
cfg, _ := testMigrationPromptConfig(t)
|
||||
t.Setenv(autoMigrateKVDBEnvVar, "definitely-not-bool")
|
||||
err := readAutoMigrateKVDB(cfg)
|
||||
require.ErrorContains(t, err, "not a valid boolean")
|
||||
})
|
||||
|
||||
t.Run("logs migration prompt text", func(t *testing.T) {
|
||||
// Note we intentionally don't use t.Parallel() here as the
|
||||
// subtest calls UseLogger(...), which mutates the
|
||||
// package-global logger. Running it in parallel with
|
||||
// the other prompt subtests lets one goroutine read log while
|
||||
// another replaces it, which can lead to a race.
|
||||
|
||||
var logOutput bytes.Buffer
|
||||
|
||||
testLogger := btclog.NewSLogger(
|
||||
btclog.NewDefaultHandler(&logOutput),
|
||||
)
|
||||
|
||||
oldLogger := log
|
||||
UseLogger(testLogger.SubSystem(Subsystem))
|
||||
t.Cleanup(func() {
|
||||
UseLogger(oldLogger)
|
||||
})
|
||||
|
||||
err := promptForKVDBToSQLMigrationConfirmation(
|
||||
strings.NewReader("yes\n"), &bytes.Buffer{},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, logOutput.String(), "about to migrate")
|
||||
require.Contains(t, logOutput.String(), "databasebackend=bbolt")
|
||||
})
|
||||
}
|
||||
|
||||
// testMigrationPromptConfig creates a config whose LiT directory and network
|
||||
// point at the same network directory used by the legacy kvdb stores.
|
||||
func testMigrationPromptConfig(t *testing.T) (*Config, string) {
|
||||
t.Helper()
|
||||
|
||||
litDir := t.TempDir()
|
||||
network := "regtest"
|
||||
dbDir := filepath.Join(litDir, network)
|
||||
cfg := &Config{
|
||||
LitDir: litDir,
|
||||
Network: network,
|
||||
MacaroonPath: filepath.Join(dbDir, "lit.macaroon"),
|
||||
}
|
||||
|
||||
return cfg, dbDir
|
||||
}
|
||||
|
||||
// createActiveAccountsKVDB creates a minimal non-tombstoned legacy accounts
|
||||
// database so the startup confirmation sees pending kvdb state to migrate.
|
||||
func createActiveAccountsKVDB(t *testing.T, dbDir string) {
|
||||
t.Helper()
|
||||
|
||||
store, err := accounts.NewBoltStore(
|
||||
dbDir, accounts.DBFilename, clock.NewDefaultClock(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, store.Close())
|
||||
}
|
||||
|
|
@ -118,6 +118,15 @@ func DeprecateKVDB(dbDir string) error {
|
|||
)
|
||||
}
|
||||
|
||||
// HasActiveKVDB reports whether the session kvdb file in the given db
|
||||
// directory still holds data that is pending migration to SQL.
|
||||
func HasActiveKVDB(dbDir string) (bool, error) {
|
||||
return tombstone.HasActiveKVDB(
|
||||
filepath.Join(dbDir, DBFilename), sessionBucketKey,
|
||||
DefaultSessionDBTimeout,
|
||||
)
|
||||
}
|
||||
|
||||
func newDB(dir, fileName string, clock clock.Clock, store accounts.Store,
|
||||
allowDeprecated bool) (*BoltStore, error) {
|
||||
|
||||
|
|
|
|||
|
|
@ -444,6 +444,15 @@ func (g *LightningTerminal) start(ctx context.Context) error {
|
|||
return fmt.Errorf("could not create network directory: %v", err)
|
||||
}
|
||||
|
||||
if g.cfg.DatabaseBackend != DatabaseBackendBbolt &&
|
||||
!g.cfg.sqlMigrationsSkipped() {
|
||||
|
||||
err = g.cfg.confirmPendingKVDBToSQLMigration()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// We create a reference to the `accountRpcServer` here before starting
|
||||
// it and prior to setting up the LND connection. This is because when
|
||||
// the LND connection is set up for an integrated LND instance, LND will
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue