@cryptotaxi247 / kubo / commits / 7eb4a92ab

0-to-1 fsrepo migration

Juan Batiz-Benet committed Mar 20, 2015 at 06:53 UTC 7eb4a92aba06dfbf8b1cb5174a8ec7826cdf6d35
8 files changed +251
Godeps/Godeps.json
+4
@@ -159,6 +159,10 @@
159 "ImportPath": "github.com/jbenet/go-logging",
160 "Rev": "74bec4b83f6d45d1402c1e9d94c0c29e39f6e0ea"
161 },
162 + {
163 + "ImportPath": "github.com/jbenet/go-migrate",
164 + "Rev": "593be6b4b24a87e4d380e54339721ad4b4c6543c"
165 + },
166 {
167 "ImportPath": "github.com/jbenet/go-msgio",
168 "Rev": "dbae89193876910c736b2ce1291fa8bbcf299d77"
Godeps/_workspace/src/github.com/jbenet/go-migrate/LICENSE new
+21
@@ -0,0 +1,21 @@
1 +The MIT License (MIT)
2 +
3 +Copyright (c) 2014 Juan Batiz-Benet
4 +
5 +Permission is hereby granted, free of charge, to any person obtaining a copy
6 +of this software and associated documentation files (the "Software"), to deal
7 +in the Software without restriction, including without limitation the rights
8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 +copies of the Software, and to permit persons to whom the Software is
10 +furnished to do so, subject to the following conditions:
11 +
12 +The above copyright notice and this permission notice shall be included in
13 +all copies or substantial portions of the Software.
14 +
15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 +THE SOFTWARE.
Godeps/_workspace/src/github.com/jbenet/go-migrate/README.md new
+11
@@ -0,0 +1,11 @@
1 +# go-migrate
2 +
3 +This is a very simple migration framework. See "Migrations" in https://github.com/jbenet/random-ideas/issues/33
4 +
5 +This package includes:
6 +
7 +- `migrate` package -- lib to write migration programs
8 +
9 +## The model
10 +
11 +The idea here is that we have some thing -- usually a directory -- that needs to be migrated between different representation versions. This may be because there has been an upgrade.
Godeps/_workspace/src/github.com/jbenet/go-migrate/cli.go new
+51
@@ -0,0 +1,51 @@
1 +package migrate
2 +
3 +import (
4 + "flag"
5 + "fmt"
6 + "os"
7 +)
8 +
9 +type Flags struct {
10 + Force bool
11 + Revert bool
12 + Path string // file path to migrate for fs based migrations
13 +}
14 +
15 +func (f *Flags) Setup() {
16 + flag.BoolVar(&f.Force, "f", false, "whether to force a migration (ignores warnings)")
17 + flag.BoolVar(&f.Revert, "revert", false, "whether to apply the migration backwards")
18 + flag.StringVar(&f.Path, "path", "", "file path to migrate for fs based migrations")
19 +}
20 +
21 +func (f *Flags) Parse() {
22 + flag.Parse()
23 +}
24 +
25 +func Run(m Migration) error {
26 + f := Flags{}
27 + f.Setup()
28 + f.Parse()
29 +
30 + if !m.Reversible() {
31 + if f.Revert {
32 + return fmt.Errorf("migration %d is irreversible", m.Versions())
33 + }
34 + if !f.Force {
35 + return fmt.Errorf("migration %d is irreversible (use -f to proceed)", m.Versions())
36 + }
37 + }
38 +
39 + if f.Revert {
40 + return m.Revert(Options{f})
41 + } else {
42 + return m.Apply(Options{f})
43 + }
44 +}
45 +
46 +func Main(m Migration) {
47 + if err := Run(m); err != nil {
48 + fmt.Fprintf(os.Stderr, "error: %s\n", err)
49 + os.Exit(1)
50 + }
51 +}
Godeps/_workspace/src/github.com/jbenet/go-migrate/doc.go new
+2
@@ -0,0 +1,2 @@
1 +// Package migrate is used to write migrations between representations of things.
2 +package migrate
Godeps/_workspace/src/github.com/jbenet/go-migrate/migrate.go new
+37
@@ -0,0 +1,37 @@
1 +package migrate
2 +
3 +import (
4 + "fmt"
5 +)
6 +
7 +// Options are migration options. For now all flags are options.
8 +type Options struct {
9 + Flags
10 +}
11 +
12 +// Migration represents
13 +type Migration interface {
14 +
15 + // Versions is the "v-to-v" version string.
16 + Versions() string
17 +
18 + // Reversible returns whether this migration can be reverted.
19 + // Endeavor to make them all reversible. This is here only to warn users
20 + // in case this is not the case.
21 + Reversible() bool
22 +
23 + // Apply applies the migration in question.
24 + Apply(Options) error
25 +
26 + // Revert un-applies the migration in question. This should be best-effort.
27 + // Some migrations are definitively one-way. If so, return an error.
28 + Revert(Options) error
29 +}
30 +
31 +func SplitVersion(s string) (from int, to int) {
32 + _, err := fmt.Scanf(s, "%d-to-%d", &from, &to)
33 + if err != nil {
34 + panic(err.Error())
35 + }
36 + return
37 +}
repo/fsrepo/migrations/0-to-1/main.go new
+69
@@ -0,0 +1,69 @@
1 +package main
2 +
3 +import (
4 + "fmt"
5 + "os"
6 + "strings"
7 +
8 + migrate "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-migrate"
9 + mfsr "github.com/ipfs/go-ipfs/repo/fsrepo/migrations"
10 +)
11 +
12 +type migration struct {
13 +}
14 +
15 +// Version is the int version number. This could be a string
16 +// in future versions
17 +func (m migration) Versions() string {
18 + return "0-to-1"
19 +}
20 +
21 +// Reversible returns whether this migration can be reverted.
22 +// Endeavor to make them all reversible. This is here only to warn users
23 +// in case this is not the case.
24 +func (m migration) Reversible() bool {
25 + return true
26 +}
27 +
28 +// Apply applies the migration in question.
29 +// This migration merely adds a version file.
30 +func (m migration) Apply(opts migrate.Options) error {
31 + repo := mfsr.RepoPath(opts.Path)
32 +
33 + // first, check if there is a version file.
34 + // if there is, bail out.
35 + if v, err := repo.Version(); err == nil {
36 + return fmt.Errorf("repo at %s is version %s (not 0)", opts.Path, v)
37 + } else if !strings.Contains(err.Error(), "no version file in repo") {
38 + return err
39 + }
40 +
41 + // add the version file
42 + if err := repo.WriteVersion("1"); err != nil {
43 + return err
44 + }
45 +
46 + return nil
47 +}
48 +
49 +// Revert un-applies the migration in question. This should be best-effort.
50 +// Some migrations are definitively one-way. If so, return an error.
51 +func (m migration) Revert(opts migrate.Options) error {
52 + repo := mfsr.RepoPath(opts.Path)
53 +
54 + if err := repo.CheckVersion("1"); err != nil {
55 + return err
56 + }
57 +
58 + // remove the version file
59 + if err := os.Remove(repo.VersionFile()); err != nil {
60 + return err
61 + }
62 +
63 + return nil
64 +}
65 +
66 +func main() {
67 + m := migration{}
68 + migrate.Main(&m)
69 +}
repo/fsrepo/migrations/mfsr.go new
+56
@@ -0,0 +1,56 @@
1 +package mfsr
2 +
3 +import (
4 + "errors"
5 + "fmt"
6 + "io/ioutil"
7 + "os"
8 + "path"
9 + "strings"
10 +)
11 +
12 +const VersionFile = "version"
13 +
14 +type RepoPath string
15 +
16 +func (rp RepoPath) VersionFile() string {
17 + return path.Join(string(rp), VersionFile)
18 +}
19 +
20 +func (rp RepoPath) Version() (string, error) {
21 + if rp == "" {
22 + return "", fmt.Errorf("invalid repo path \"%s\"", rp)
23 + }
24 +
25 + fn := rp.VersionFile()
26 + if _, err := os.Stat(fn); os.IsNotExist(err) {
27 + return "", errors.New("no version file in repo at " + string(rp))
28 + }
29 +
30 + c, err := ioutil.ReadFile(fn)
31 + if err != nil {
32 + return "", err
33 + }
34 +
35 + s := string(c)
36 + s = strings.TrimSpace(s)
37 + return s, nil
38 +}
39 +
40 +func (rp RepoPath) CheckVersion(version string) error {
41 + v, err := rp.Version()
42 + if err != nil {
43 + return err
44 + }
45 +
46 + if v != version {
47 + return fmt.Errorf("versions differ (expected: %s, actual:%s)", version, v)
48 + }
49 +
50 + return nil
51 +}
52 +
53 +func (rp RepoPath) WriteVersion(version string) error {
54 + fn := rp.VersionFile()
55 + return ioutil.WriteFile(fn, []byte(version+"\n"), 0644)
56 +}