Commit 7eb4a92a authored by Juan Batiz-Benet's avatar Juan Batiz-Benet

0-to-1 fsrepo migration

parent c79dddd3
......@@ -159,6 +159,10 @@
"ImportPath": "github.com/jbenet/go-logging",
"Rev": "74bec4b83f6d45d1402c1e9d94c0c29e39f6e0ea"
},
{
"ImportPath": "github.com/jbenet/go-migrate",
"Rev": "593be6b4b24a87e4d380e54339721ad4b4c6543c"
},
{
"ImportPath": "github.com/jbenet/go-msgio",
"Rev": "dbae89193876910c736b2ce1291fa8bbcf299d77"
......
The MIT License (MIT)
Copyright (c) 2014 Juan Batiz-Benet
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# go-migrate
This is a very simple migration framework. See "Migrations" in https://github.com/jbenet/random-ideas/issues/33
This package includes:
- `migrate` package -- lib to write migration programs
## The model
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.
package migrate
import (
"flag"
"fmt"
"os"
)
type Flags struct {
Force bool
Revert bool
Path string // file path to migrate for fs based migrations
}
func (f *Flags) Setup() {
flag.BoolVar(&f.Force, "f", false, "whether to force a migration (ignores warnings)")
flag.BoolVar(&f.Revert, "revert", false, "whether to apply the migration backwards")
flag.StringVar(&f.Path, "path", "", "file path to migrate for fs based migrations")
}
func (f *Flags) Parse() {
flag.Parse()
}
func Run(m Migration) error {
f := Flags{}
f.Setup()
f.Parse()
if !m.Reversible() {
if f.Revert {
return fmt.Errorf("migration %d is irreversible", m.Versions())
}
if !f.Force {
return fmt.Errorf("migration %d is irreversible (use -f to proceed)", m.Versions())
}
}
if f.Revert {
return m.Revert(Options{f})
} else {
return m.Apply(Options{f})
}
}
func Main(m Migration) {
if err := Run(m); err != nil {
fmt.Fprintf(os.Stderr, "error: %s\n", err)
os.Exit(1)
}
}
// Package migrate is used to write migrations between representations of things.
package migrate
package migrate
import (
"fmt"
)
// Options are migration options. For now all flags are options.
type Options struct {
Flags
}
// Migration represents
type Migration interface {
// Versions is the "v-to-v" version string.
Versions() string
// Reversible returns whether this migration can be reverted.
// Endeavor to make them all reversible. This is here only to warn users
// in case this is not the case.
Reversible() bool
// Apply applies the migration in question.
Apply(Options) error
// Revert un-applies the migration in question. This should be best-effort.
// Some migrations are definitively one-way. If so, return an error.
Revert(Options) error
}
func SplitVersion(s string) (from int, to int) {
_, err := fmt.Scanf(s, "%d-to-%d", &from, &to)
if err != nil {
panic(err.Error())
}
return
}
package main
import (
"fmt"
"os"
"strings"
migrate "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-migrate"
mfsr "github.com/ipfs/go-ipfs/repo/fsrepo/migrations"
)
type migration struct {
}
// Version is the int version number. This could be a string
// in future versions
func (m migration) Versions() string {
return "0-to-1"
}
// Reversible returns whether this migration can be reverted.
// Endeavor to make them all reversible. This is here only to warn users
// in case this is not the case.
func (m migration) Reversible() bool {
return true
}
// Apply applies the migration in question.
// This migration merely adds a version file.
func (m migration) Apply(opts migrate.Options) error {
repo := mfsr.RepoPath(opts.Path)
// first, check if there is a version file.
// if there is, bail out.
if v, err := repo.Version(); err == nil {
return fmt.Errorf("repo at %s is version %s (not 0)", opts.Path, v)
} else if !strings.Contains(err.Error(), "no version file in repo") {
return err
}
// add the version file
if err := repo.WriteVersion("1"); err != nil {
return err
}
return nil
}
// Revert un-applies the migration in question. This should be best-effort.
// Some migrations are definitively one-way. If so, return an error.
func (m migration) Revert(opts migrate.Options) error {
repo := mfsr.RepoPath(opts.Path)
if err := repo.CheckVersion("1"); err != nil {
return err
}
// remove the version file
if err := os.Remove(repo.VersionFile()); err != nil {
return err
}
return nil
}
func main() {
m := migration{}
migrate.Main(&m)
}
package mfsr
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"strings"
)
const VersionFile = "version"
type RepoPath string
func (rp RepoPath) VersionFile() string {
return path.Join(string(rp), VersionFile)
}
func (rp RepoPath) Version() (string, error) {
if rp == "" {
return "", fmt.Errorf("invalid repo path \"%s\"", rp)
}
fn := rp.VersionFile()
if _, err := os.Stat(fn); os.IsNotExist(err) {
return "", errors.New("no version file in repo at " + string(rp))
}
c, err := ioutil.ReadFile(fn)
if err != nil {
return "", err
}
s := string(c)
s = strings.TrimSpace(s)
return s, nil
}
func (rp RepoPath) CheckVersion(version string) error {
v, err := rp.Version()
if err != nil {
return err
}
if v != version {
return fmt.Errorf("versions differ (expected: %s, actual:%s)", version, v)
}
return nil
}
func (rp RepoPath) WriteVersion(version string) error {
fn := rp.VersionFile()
return ioutil.WriteFile(fn, []byte(version+"\n"), 0644)
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment