Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically download and run migrations if needed #2939

Merged
merged 7 commits into from
Jul 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 58 additions & 9 deletions cmd/ipfs/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,27 @@ import (
"github.com/ipfs/go-ipfs/core/corerouting"
nodeMount "github.com/ipfs/go-ipfs/fuse/node"
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
migrate "github.com/ipfs/go-ipfs/repo/fsrepo/migrations"
pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore"
conn "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/net/conn"
util "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
prometheus "gx/ipfs/QmdhsRK1EK2fvAz2i2SH5DEfkL6seDuyMYEsxKa9Braim3/client_golang/prometheus"
)

const (
adjustFDLimitKwd = "manage-fdlimit"
enableGCKwd = "enable-gc"
initOptionKwd = "init"
routingOptionKwd = "routing"
routingOptionSupernodeKwd = "supernode"
mountKwd = "mount"
writableKwd = "writable"
ipfsMountKwd = "mount-ipfs"
ipnsMountKwd = "mount-ipns"
unrestrictedApiAccessKwd = "unrestricted-api"
unencryptTransportKwd = "disable-transport-encryption"
enableGCKwd = "enable-gc"
adjustFDLimitKwd = "manage-fdlimit"
migrateKwd = "migrate"
mountKwd = "mount"
offlineKwd = "offline"
routingOptionKwd = "routing"
routingOptionSupernodeKwd = "supernode"
unencryptTransportKwd = "disable-transport-encryption"
unrestrictedApiAccessKwd = "unrestricted-api"
writableKwd = "writable"
// apiAddrKwd = "address-api"
// swarmAddrKwd = "address-swarm"
)
Expand Down Expand Up @@ -139,6 +141,7 @@ Headers.
cmds.BoolOption(enableGCKwd, "Enable automatic periodic repo garbage collection").Default(false),
cmds.BoolOption(adjustFDLimitKwd, "Check and raise file descriptor limits if needed").Default(true),
cmds.BoolOption(offlineKwd, "Run offline. Do not connect to the rest of the network but provide local API.").Default(false),
cmds.BoolOption(migrateKwd, "If true, assume yes at the migrate prompt. If false, assume no."),

// TODO: add way to override addresses. tricky part: updating the config if also --init.
// cmds.StringOption(apiAddrKwd, "Address for the daemon rpc API (overrides config)"),
Expand Down Expand Up @@ -216,9 +219,36 @@ func daemonFunc(req cmds.Request, res cmds.Response) {
// acquire the repo lock _before_ constructing a node. we need to make
// sure we are permitted to access the resources (datastore, etc.)
repo, err := fsrepo.Open(req.InvocContext().ConfigRoot)
if err != nil {
switch err {
default:
res.SetError(err, cmds.ErrNormal)
return
case fsrepo.ErrNeedMigration:
domigrate, found, _ := req.Option(migrateKwd).Bool()
fmt.Println("Found old repo version, migrations need to be run.")

if !found {
domigrate = YesNoPrompt("Run migrations automatically? [y/N]")
}

if !domigrate {
res.SetError(fmt.Errorf("please run the migrations manually"), cmds.ErrNormal)
return
}

err = migrate.RunMigration(fsrepo.RepoVersion)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}

repo, err = fsrepo.Open(req.InvocContext().ConfigRoot)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
case nil:
break
}

cfg, err := ctx.GetConfig()
Expand Down Expand Up @@ -569,3 +599,22 @@ func merge(cs ...<-chan error) <-chan error {
}()
return out
}

func YesNoPrompt(prompt string) bool {
var s string
for i := 0; i < 3; i++ {
fmt.Printf("%s ", prompt)
fmt.Scanf("%s", &s)
switch s {
case "y", "Y":
return true
case "n", "N":
return false
case "":
return false
}
fmt.Println("Please press either 'y' or 'n'")
}

return false
}
2 changes: 1 addition & 1 deletion core/commands/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ var repoVersionCmd = &cmds.Command{
},
Run: func(req cmds.Request, res cmds.Response) {
res.SetOutput(&RepoVersion{
Version: fsrepo.RepoVersion,
Version: fmt.Sprint(fsrepo.RepoVersion),
})
},
Type: RepoVersion{},
Expand Down
2 changes: 1 addition & 1 deletion core/commands/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ var VersionCmd = &cmds.Command{
res.SetOutput(&VersionOutput{
Version: config.CurrentVersionNumber,
Commit: config.CurrentCommit,
Repo: fsrepo.RepoVersion,
Repo: fmt.Sprint(fsrepo.RepoVersion),
System: runtime.GOARCH + "/" + runtime.GOOS, //TODO: Precise version here
Golang: runtime.Version(),
})
Expand Down
4 changes: 3 additions & 1 deletion core/corerepo/stat.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package corerepo

import (
"fmt"

"github.com/ipfs/go-ipfs/core"
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
Expand Down Expand Up @@ -40,6 +42,6 @@ func RepoStat(n *core.IpfsNode, ctx context.Context) (*Stat, error) {
NumObjects: count,
RepoSize: usage,
RepoPath: path,
Version: "fs-repo@" + fsrepo.RepoVersion,
Version: fmt.Sprintf("fs-repo@%d", fsrepo.RepoVersion),
}, nil
}
20 changes: 15 additions & 5 deletions repo/fsrepo/fsrepo.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
var log = logging.Logger("fsrepo")

// version number that we are currently expecting to see
var RepoVersion = "4"
var RepoVersion = 4

var migrationInstructions = `See https://github.com/ipfs/fs-repo-migrations/blob/master/run.md
Sorry for the inconvenience. In the future, these will run automatically.`
Expand All @@ -36,9 +36,16 @@ Program version is: %s
Please run the ipfs migration tool before continuing.
` + migrationInstructions

var programTooLowMessage = `Your programs version (%d) is lower than your repos (%d).
Please update ipfs to a version that supports the existing repo, or run
a migration in reverse.

See https://github.com/ipfs/fs-repo-migrations/blob/master/run.md for details.`

var (
ErrNoVersion = errors.New("no version file found, please run 0-to-1 migration tool.\n" + migrationInstructions)
ErrOldRepo = errors.New("ipfs repo found in old '~/.go-ipfs' location, please run migration tool.\n" + migrationInstructions)
ErrNoVersion = errors.New("no version file found, please run 0-to-1 migration tool.\n" + migrationInstructions)
ErrOldRepo = errors.New("ipfs repo found in old '~/.go-ipfs' location, please run migration tool.\n" + migrationInstructions)
ErrNeedMigration = errors.New("ipfs repo needs migration.")
)

type NoRepoError struct {
Expand Down Expand Up @@ -134,8 +141,11 @@ func open(repoPath string) (repo.Repo, error) {
return nil, err
}

if ver != RepoVersion {
return nil, fmt.Errorf(errIncorrectRepoFmt, ver, RepoVersion)
if RepoVersion > ver {
return nil, ErrNeedMigration
} else if ver > RepoVersion {
// program version too low for existing repo
return nil, fmt.Errorf(programTooLowMessage, RepoVersion, ver)
}

// check repo path, then check all constituent parts.
Expand Down
20 changes: 10 additions & 10 deletions repo/fsrepo/migrations/mfsr.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"io/ioutil"
"os"
"path"
"strconv"
"strings"
)

Expand All @@ -16,27 +17,26 @@ func (rp RepoPath) VersionFile() string {
return path.Join(string(rp), VersionFile)
}

func (rp RepoPath) Version() (string, error) {
func (rp RepoPath) Version() (int, error) {
if rp == "" {
return "", fmt.Errorf("invalid repo path \"%s\"", rp)
return 0, fmt.Errorf("invalid repo path \"%s\"", rp)
}

fn := rp.VersionFile()
if _, err := os.Stat(fn); os.IsNotExist(err) {
return "", VersionFileNotFound(rp)
return 0, VersionFileNotFound(rp)
}

c, err := ioutil.ReadFile(fn)
if err != nil {
return "", err
return 0, err
}

s := string(c)
s = strings.TrimSpace(s)
return s, nil
s := strings.TrimSpace(string(c))
return strconv.Atoi(s)
}

func (rp RepoPath) CheckVersion(version string) error {
func (rp RepoPath) CheckVersion(version int) error {
v, err := rp.Version()
if err != nil {
return err
Expand All @@ -49,9 +49,9 @@ func (rp RepoPath) CheckVersion(version string) error {
return nil
}

func (rp RepoPath) WriteVersion(version string) error {
func (rp RepoPath) WriteVersion(version int) error {
fn := rp.VersionFile()
return ioutil.WriteFile(fn, []byte(version+"\n"), 0644)
return ioutil.WriteFile(fn, []byte(fmt.Sprintf("%d\n", version)), 0644)
}

type VersionFileNotFound string
Expand Down
Loading