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
+}