diff --git a/core/commands/commands_test.go b/core/commands/commands_test.go index 5c076307413..8b1e760f483 100644 --- a/core/commands/commands_test.go +++ b/core/commands/commands_test.go @@ -219,6 +219,7 @@ func TestCommands(t *testing.T) { "/repo/stat", "/repo/verify", "/repo/version", + "/repo/migrate", "/resolve", "/shutdown", "/stats", diff --git a/core/commands/repo.go b/core/commands/repo.go index 307b4ffc58f..9f665731653 100644 --- a/core/commands/repo.go +++ b/core/commands/repo.go @@ -11,11 +11,14 @@ import ( "sync" "text/tabwriter" - humanize "github.com/dustin/go-humanize" + oldcmds "github.com/ipfs/go-ipfs/commands" cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv" corerepo "github.com/ipfs/go-ipfs/core/corerepo" fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo" + migrate "github.com/ipfs/go-ipfs/repo/fsrepo/migrations" + "github.com/ipfs/go-ipfs/repo/fsrepo/migrations/ipfsfetcher" + humanize "github.com/dustin/go-humanize" cid "github.com/ipfs/go-cid" bstore "github.com/ipfs/go-ipfs-blockstore" cmds "github.com/ipfs/go-ipfs-cmds" @@ -39,6 +42,7 @@ var RepoCmd = &cmds.Command{ "fsck": repoFsckCmd, "version": repoVersionCmd, "verify": repoVerifyCmd, + "migrate": repoMigrateCmd, }, } @@ -49,8 +53,9 @@ type GcResult struct { } const ( - repoStreamErrorsOptionName = "stream-errors" - repoQuietOptionName = "quiet" + repoStreamErrorsOptionName = "stream-errors" + repoQuietOptionName = "quiet" + repoAllowDowngradeOptionName = "allow-downgrade" ) var repoGcCmd = &cmds.Command{ @@ -375,3 +380,62 @@ var repoVersionCmd = &cmds.Command{ }), }, } + +var repoMigrateCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Apply any outstanding migrations to the repo.", + }, + Options: []cmds.Option{ + cmds.BoolOption(repoAllowDowngradeOptionName, "Allow downgrading to a lower repo version"), + }, + NoRemote: true, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + cctx := env.(*oldcmds.Context) + allowDowngrade, _ := req.Options[repoAllowDowngradeOptionName].(bool) + + _, err := fsrepo.Open(cctx.ConfigRoot) + if err != fsrepo.ErrNeedMigration { + fmt.Println("Repo does not require migration") + return nil + } + + fmt.Println("Found outdated fs-repo, starting migration.") + + // Read Migration section of IPFS config + migrationCfg, err := migrate.ReadMigrationConfig(cctx.ConfigRoot) + if err != nil { + return err + } + + // Define function to create IPFS fetcher. Do not supply an + // already-constructed IPFS fetcher, because this may be expensive and + // not needed according to migration config. Instead, supply a function + // to construct the particular IPFS fetcher implementation used here, + // which is called only if an IPFS fetcher is needed. + newIpfsFetcher := func(distPath string) migrate.Fetcher { + return ipfsfetcher.NewIpfsFetcher(distPath, 0, &cctx.ConfigRoot) + } + + // Fetch migrations from current distribution, or location from environ + fetchDistPath := migrate.GetDistPathEnv(migrate.CurrentIpfsDist) + + // Create fetchers according to migrationCfg.DownloadSources + fetcher, err := migrate.GetMigrationFetcher(migrationCfg.DownloadSources, fetchDistPath, newIpfsFetcher) + if err != nil { + return err + } + defer fetcher.Close() + + err = migrate.RunMigration(cctx.Context(), fetcher, fsrepo.RepoVersion, "", allowDowngrade) + if err != nil { + fmt.Println("The migrations of fs-repo failed:") + fmt.Printf(" %s\n", err) + fmt.Println("If you think this is a bug, please file an issue and include this whole log output.") + fmt.Println(" https://github.com/ipfs/fs-repo-migrations") + return err + } + + fmt.Printf("Success: fs-repo has been migrated to version %d.\n", fsrepo.RepoVersion) + return nil + }, +} diff --git a/mk/golang.mk b/mk/golang.mk index 0b2a2c55ae2..52597ab4927 100644 --- a/mk/golang.mk +++ b/mk/golang.mk @@ -70,7 +70,7 @@ test_go_fmt: TEST_GO += test_go_fmt test_go_lint: test/bin/golangci-lint - golangci-lint run ./... + golangci-lint run --timeout 5m ./... .PHONY: test_go_lint test_go: $(TEST_GO) diff --git a/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go b/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go index 88f07b502ee..0d1a0c18d0f 100644 --- a/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go +++ b/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go @@ -239,8 +239,6 @@ func (f *IpfsFetcher) startTempNode(ctx context.Context) error { cancel() // Wait until ipfs is stopped <-node.Context().Done() - - fmt.Println("migration peer", node.Identity, "shutdown") } addrs, err := ipfs.Swarm().LocalAddrs(ctx) diff --git a/test/sharness/t0066-migration.sh b/test/sharness/t0066-migration.sh index aa40fd8a468..240c74594a2 100755 --- a/test/sharness/t0066-migration.sh +++ b/test/sharness/t0066-migration.sh @@ -84,4 +84,26 @@ test_expect_success "output looks good" ' grep "Please get fs-repo-migrations from https://dist.ipfs.io" daemon_out > /dev/null ' +test_expect_success "ipfs repo migrate succeed" ' + test_expect_code 0 ipfs repo migrate > migrate_out +' + +test_expect_success "output looks good" ' + grep "Found outdated fs-repo, starting migration." migrate_out > /dev/null && + grep "Success: fs-repo migrated to version $IPFS_REPO_VER" true_out > /dev/null +' + +test_expect_success "manually reset repo version to 11" ' + echo "$IPFS_REPO_VER" > "$IPFS_PATH"/version +' + +test_expect_success "detect repo does not need migration" ' + test_expect_code 0 ipfs repo migrate > migrate_out +' + +test_expect_success "output looks good" ' + grep "Repo does not require migration" migrate_out > /dev/null +' +cat migrate_out + test_done