func verifyMigrationSupportsVersion(fsrbin, v string) error { stump.VLog(" - verifying migration supports version %s", v) vn, err := strconv.Atoi(v) if err != nil { return fmt.Errorf("given migration version was not a number: %q", v) } sn, err := migrationsVersion(fsrbin) if err != nil { return err } if sn >= vn { return nil } stump.VLog(" - migrations doesnt support version %s, attempting to update", v) _, err = GetMigrations() if err != nil { return err } stump.VLog(" - migrations updated") sn, err = migrationsVersion(fsrbin) if err != nil { return err } if sn >= vn { return nil } return fmt.Errorf("no known migration supports version %s", v) }
func RunMigration(oldv, newv string) error { migrateBin := "fs-repo-migrations" stump.VLog(" - checking for migrations binary...") _, err := exec.LookPath(migrateBin) if err != nil { stump.VLog(" - migrations not found on system, attempting to install") loc, err := GetMigrations() if err != nil { return err } migrateBin = loc } // check to make sure migrations binary supports our target version err = verifyMigrationSupportsVersion(migrateBin, newv) if err != nil { return err } cmd := exec.Command(migrateBin, "-to", newv, "-y") cmd.Stdout = stump.LogOut cmd.Stderr = stump.ErrOut stump.Log("running migration: '%s -to %s -y'", migrateBin, newv) err = cmd.Run() if err != nil { return fmt.Errorf("migration failed: %s", err) } stump.Log("migration succeeded!") return nil }
func (i *Install) postInstallMigrationCheck() error { if util.BeforeVersion("v0.3.10", i.TargetVers) { stump.VLog(" - ipfs pre v0.3.10 does not support checking of repo version through the tool") stump.VLog(" - if a migration is needed, you will be prompted when starting ipfs") return nil } return CheckMigration() }
func GetCurrentVersion() (string, error) { fix := func(s string) string { if !strings.HasPrefix(s, "v") { s = "v" + s } return s } // try checking a locally running daemon first apiurl, err := util.ApiEndpoint(util.IpfsDir()) if err == nil { sh := api.NewShell(apiurl) v, _, err := sh.Version() if err == nil { return fix(v), nil } } stump.VLog("daemon check failed: %s", err) _, err = exec.LookPath("ipfs") if err != nil { return "none", nil } // try running the ipfs binary in the users path out, err := exec.Command("ipfs", "version", "-n").CombinedOutput() if err != nil { return "", fmt.Errorf("version check failed: %s - %s", string(out), err) } return fix(strings.Trim(string(out), " \n\t")), nil }
func testRefsList(tdir, bin string) error { stump.VLog(" - checking that file shows up in ipfs refs local") c := exec.Command(bin, "refs", "local") c.Env = []string{"IPFS_PATH=" + tdir} out, err := c.CombinedOutput() if err != nil { stump.Error("testfileadd fail: %s", err) stump.Error(string(out)) return err } hashes := strings.Split(string(out), "\n") exp := "QmTFJQ68kaArzsqz2Yjg1yMyEA5TXTfNw6d9wSFhxtBxz2" var found bool for _, h := range hashes { if h == exp { found = true break } } if !found { return fmt.Errorf("expected to see %s in the local refs!", exp) } return nil }
func testFileAdd(tdir, bin string) error { stump.VLog(" - checking that we can add and cat a file") text := "hello world! This node should work" data := bytes.NewBufferString(text) c := exec.Command(bin, "add", "-q", "--progress=false") c.Env = []string{"IPFS_PATH=" + tdir} c.Stdin = data out, err := c.CombinedOutput() if err != nil { stump.Error("testfileadd fail: %s", err) stump.Error(string(out)) return err } hash := strings.Trim(string(out), "\n \t\r") fiout, err := runCmd(tdir, bin, "cat", hash) if err != nil { return err } if fiout != text { return fmt.Errorf("add/cat check failed") } return nil }
func (i *Install) Run() error { defer i.RevertOnFailure() var err error i.CurrentVers, err = GetCurrentVersion() if err != nil { return err } if i.CurrentVers == "none" { stump.VLog("no pre-existing ipfs installation found") } else if i.CurrentVers == i.TargetVers { stump.Log("Already have version %s installed, skipping.", i.TargetVers) i.Succeeded = true return nil } err = i.DownloadNewBinary() if err != nil { return err } if !i.NoCheck { stump.Log("binary downloaded, verifying...") err = test.TestBinary(i.TmpBinPath, i.TargetVers) if err != nil { return err } } else { stump.Log("skipping tests since '--no-check' was passed") } err = i.MaybeStash() if err != nil { return err } err = i.SelectGoodInstallLoc() if err != nil { return err } stump.Log("installing new binary to %s", i.InstallPath) err = InstallBinaryTo(i.TmpBinPath, i.InstallPath) if err != nil { // in case of error here, replace old binary stump.Error("Install failed: ", err) return err } err = i.postInstallMigrationCheck() if err != nil { stump.Error("Migration Failed: ", err) return err } i.Succeeded = true return nil }
func Fetch(ipfspath string) (io.ReadCloser, error) { stump.VLog(" - fetching %q", ipfspath) ep, err := ApiEndpoint(IpfsDir()) if err == nil { sh := api.NewShell(ep) if sh.IsUp() { stump.VLog(" - using local ipfs daemon for transfer") rc, err := sh.Cat(ipfspath) if err != nil { return nil, err } return newLimitReadCloser(rc, fetchSizeLimit), nil } } return httpFetch(GlobalGatewayUrl + ipfspath) }
func getMigrationsGoGet() (string, error) { stump.VLog(" - fetching migrations using 'go get'") cmd := exec.Command("go", "get", "-u", "github.com/ipfs/fs-repo-migrations") out, err := cmd.CombinedOutput() if err != nil { return "", fmt.Errorf("%s %s", string(out), err) } stump.VLog(" - success. verifying...") // verify we can see the binary now p, err := exec.LookPath("fs-repo-migrations") if err != nil { return "", fmt.Errorf("install succeeded, but failed to find binary afterwards. (%s)", err) } stump.VLog(" - fs-repo-migrations now installed at %s", p) return filepath.Join(os.Getenv("GOPATH"), "bin", migrations), nil }
func CheckMigration() error { stump.Log("checking if repo migration is needed...") p := util.IpfsDir() vfilePath := filepath.Join(p, "version") _, err := os.Stat(vfilePath) if os.IsNotExist(err) { stump.VLog(" - no prexisting repo to migrate") return nil } oldverB, err := ioutil.ReadFile(vfilePath) if err != nil { return err } oldver := strings.Trim(string(oldverB), "\n \t") stump.VLog(" - old repo version is", oldver) nbinver, err := util.RunCmd("", "ipfs", "version", "--repo") if err != nil { stump.Log("Failed to check new binary repo version.") stump.VLog("Reason: ", err) stump.Log("This is not an error.") stump.Log("This just means that you may have to manually run the migration") stump.Log("You will be prompted to do so upon starting the ipfs daemon if necessary") return nil } stump.VLog(" - repo version of new binary is ", nbinver) if oldver != nbinver { stump.Log(" check complete, migration required.") return RunMigration(oldver, nbinver) } stump.VLog(" check complete, no migration required.") return nil }
func GetBinaryForVersion(distname, binnom, root, vers, out string) error { stump.Log("fetching %s version %s", distname, vers) dir, err := ioutil.TempDir("", "ipfs-update") if err != nil { return err } stump.VLog(" - using GOOS=%s and GOARCH=%s", runtime.GOOS, runtime.GOARCH) var archive string switch runtime.GOOS { case "windows": archive = "zip" default: archive = "tar.gz" } finame := fmt.Sprintf("%s_%s_%s-%s.%s", distname, vers, runtime.GOOS, runtime.GOARCH, archive) distpath := fmt.Sprintf("%s/%s/%s/%s", root, distname, vers, finame) data, err := util.Fetch(distpath) if err != nil { return err } arcpath := filepath.Join(dir, finame) fi, err := os.Create(arcpath) if err != nil { return err } stump.VLog(" - writing to", arcpath) _, err = io.Copy(fi, data) if err != nil { return err } fi.Close() return unpackArchive(distname, binnom, arcpath, out, archive) }
func runCmd(p, bin string, args ...string) (string, error) { cmd := exec.Command(bin, args...) cmd.Env = []string{"IPFS_PATH=" + p} stump.VLog(" - running: %s", cmd.Args) out, err := cmd.CombinedOutput() if err != nil { return "", fmt.Errorf("%s: %s", err, string(out)) } if out[len(out)-1] == '\n' { return string(out[:len(out)-1]), nil } return string(out), nil }
func waitForApi(ipfspath string) error { stump.VLog(" - waiting on daemon to come online") var endpoint string nloops := 15 var success bool for i := 0; i < nloops; i++ { ep, err := util.ApiEndpoint(ipfspath) if err == nil { stump.VLog(" - found api file") endpoint = ep success = true break } if !os.IsNotExist(err) { return err } time.Sleep(time.Millisecond * (100 * time.Duration(i+1))) } if !success { stump.VLog(" - no api file found, trying fallback (happens pre 0.3.8)") endpoint = "localhost:5001" } for i := 0; i < 10; i++ { _, err := net.Dial("tcp", endpoint) if err == nil { return nil } time.Sleep(time.Millisecond * (100 * time.Duration(i+1))) } return fmt.Errorf("failed to come online") }
func httpFetch(url string) (io.ReadCloser, error) { stump.VLog("fetching url: %s", url) resp, err := httpGet(url) if err != nil { return nil, err } if resp.StatusCode >= 400 { stump.Error("fetching resource: %s", resp.Status) mes, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("error reading error body: %s", err) } return nil, fmt.Errorf("%s: %s", resp.Status, string(mes)) } return newLimitReadCloser(resp.Body, fetchSizeLimit), nil }
func (i *Install) MaybeStash() error { if i.CurrentVers != "none" { stump.Log("stashing old binary") oldpath, err := StashOldBinary(i.CurrentVers, false) if err != nil { if strings.Contains(err.Error(), "could not find old") { stump.Log("stash failed, no binary found.") stump.Log(util.BoldText("this could be because you have a daemon running, but no ipfs binary in your path.")) stump.Log("continuing anyways, but skipping stash") return nil } return err } i.StashedFromPath = filepath.Dir(oldpath) } else { stump.VLog("skipping stash, no previous install") } return nil }
// StashOldBinary moves the existing ipfs binary to a backup directory // and returns the path to the original location of the old binary func StashOldBinary(tag string, keep bool) (string, error) { loc, err := exec.LookPath("ipfs") if err != nil { return "", fmt.Errorf("could not find old binary: %s", err) } ipfsdir := util.IpfsDir() olddir := filepath.Join(ipfsdir, "old-bin") npath := filepath.Join(olddir, "ipfs-"+tag) pathpath := filepath.Join(olddir, "path-old") err = os.MkdirAll(olddir, 0700) if err != nil { return "", fmt.Errorf("could not create dir to backup old binary: %s", err) } // write the old path of the binary to the backup dir err = ioutil.WriteFile(pathpath, []byte(loc), 0644) if err != nil { return "", fmt.Errorf("couldnt stash path: ", err) } f := util.Move if keep { f = util.CopyTo } stump.VLog(" - moving %s to %s", loc, npath) err = f(loc, npath) if err != nil { return "", fmt.Errorf("could not move old binary: %s", err) } return loc, nil }
}, Action: func(c *cli.Context) error { vers := c.Args().First() if vers == "" { stump.Fatal("please specify a version to install") } if vers == "latest" { latest, err := GetLatestVersion(util.IpfsVersionPath, "go-ipfs") if err != nil { stump.Fatal("error resolving 'latest': ", err) } vers = latest } if !strings.HasPrefix(vers, "v") && looksLikeSemver(vers) { stump.VLog("Version strings must start with 'v'. Autocorrecting...") vers = "v" + vers } i, err := NewInstall(util.IpfsVersionPath, vers, c.Bool("no-check")) if err != nil { return err } err = i.Run() if err != nil { return err } stump.Log("\nInstallation complete!") if util.HasDaemonRunning() {
func TestBinary(bin, version string) error { _, err := os.Stat(bin) if err != nil { return err } // make sure binary is executable err = os.Chmod(bin, 0755) if err != nil { return err } staging := filepath.Join(util.IpfsDir(), "update-staging") err = os.MkdirAll(staging, 0755) if err != nil { return fmt.Errorf("error creating test staging directory: %s", err) } tdir, err := ioutil.TempDir(staging, "test") if err != nil { return err } err = os.MkdirAll(tdir, 0755) if err != nil { return fmt.Errorf("error creating test staging directory: %s", err) } defer func(dir string) { // defer cleanup, bound param to avoid mistakes err = os.RemoveAll(dir) if err != nil { stump.Error("error cleaning up staging directory: ", err) } }(tdir) stump.VLog(" - running init in '%s' with new binary", tdir) _, err = runCmd(tdir, bin, "init") if err != nil { return fmt.Errorf("error initializing with new binary: %s", err) } stump.VLog(" - checking new binary outputs correct version") rversion, err := runCmd(tdir, bin, "version") if err != nil { return err } parts := strings.Fields(rversion) if !versionMatch(parts[len(parts)-1], version[1:]) { return fmt.Errorf("version didnt match") } if util.BeforeVersion("v0.3.8", version) { stump.Log("== skipping tests with daemon, versions before 0.3.8 do not support port zero ==") return nil } // set up ports in config so we dont interfere with an already running daemon stump.VLog(" - tweaking test config to avoid external interference") err = tweakConfig(tdir) if err != nil { return err } stump.VLog(" - starting up daemon") daemon, err := StartDaemon(tdir, bin) if err != nil { return fmt.Errorf("error starting daemon: %s", err) } defer func() { stump.VLog(" - killing test daemon") err := daemon.Close() if err != nil { stump.VLog(" - error killing test daemon: %s (continuing anyway)", err) } stump.Log("success!") }() // test some basic things against the daemon err = testFileAdd(tdir, bin) if err != nil { return fmt.Errorf("test file add: %s", err) } err = testRefsList(tdir, bin) if err != nil { return fmt.Errorf("test refs list: %s", err) } return nil }