Reimplement dump and implment restore command
This commit is contained in:
parent
bb8014885f
commit
971b13c367
46
cmd/dump.go
46
cmd/dump.go
|
@ -1,5 +1,5 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
|
@ -44,10 +44,6 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
|
|||
Value: os.TempDir(),
|
||||
Usage: "Temporary dir path",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "database, d",
|
||||
Usage: "Specify the database SQL syntax",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -79,23 +75,15 @@ func runDump(ctx *cli.Context) error {
|
|||
os.Setenv("TMPDIR", tmpWorkDir)
|
||||
}
|
||||
|
||||
reposDump := path.Join(tmpWorkDir, "gitea-repo.zip")
|
||||
dbDump := path.Join(tmpWorkDir, "gitea-db.sql")
|
||||
|
||||
dbDump := path.Join(tmpWorkDir, "database")
|
||||
log.Printf("Dumping local repositories...%s", setting.RepoRootPath)
|
||||
zip.Verbose = ctx.Bool("verbose")
|
||||
if err := zip.PackTo(setting.RepoRootPath, reposDump, true); err != nil {
|
||||
log.Fatalf("Failed to dump local repositories: %v", err)
|
||||
if err := os.MkdirAll(dbDump, os.ModePerm); err != nil {
|
||||
log.Fatalf("Failed to create database dir: %v", err)
|
||||
}
|
||||
|
||||
targetDBType := ctx.String("database")
|
||||
if len(targetDBType) > 0 && targetDBType != models.DbCfg.Type {
|
||||
log.Printf("Dumping database %s => %s...", models.DbCfg.Type, targetDBType)
|
||||
} else {
|
||||
log.Printf("Dumping database...")
|
||||
}
|
||||
|
||||
if err := models.DumpDatabase(dbDump, targetDBType); err != nil {
|
||||
log.Printf("Dumping database ...")
|
||||
if err := models.DumpDatabaseFixtures(dbDump); err != nil {
|
||||
log.Fatalf("Failed to dump database: %v", err)
|
||||
}
|
||||
|
||||
|
@ -106,11 +94,12 @@ func runDump(ctx *cli.Context) error {
|
|||
log.Fatalf("Failed to create %s: %v", fileName, err)
|
||||
}
|
||||
|
||||
if err := z.AddFile("gitea-repo.zip", reposDump); err != nil {
|
||||
if err := z.AddDir("repositories", setting.RepoRootPath); err != nil {
|
||||
log.Fatalf("Failed to include gitea-repo.zip: %v", err)
|
||||
}
|
||||
if err := z.AddFile("gitea-db.sql", dbDump); err != nil {
|
||||
log.Fatalf("Failed to include gitea-db.sql: %v", err)
|
||||
|
||||
if err := z.AddDir("database", dbDump); err != nil {
|
||||
log.Fatalf("Failed to include database: %v", err)
|
||||
}
|
||||
customDir, err := os.Stat(setting.CustomPath)
|
||||
if err == nil && customDir.IsDir() {
|
||||
|
@ -133,8 +122,19 @@ func runDump(ctx *cli.Context) error {
|
|||
}
|
||||
}
|
||||
|
||||
if err := z.AddDir("log", setting.LogRootPath); err != nil {
|
||||
log.Fatalf("Failed to include log: %v", err)
|
||||
verPath := filepath.Join(tmpWorkDir, "VERSION")
|
||||
verf, err := os.Create(verPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create version file: %v", err)
|
||||
}
|
||||
_, err = verf.WriteString(setting.AppVer)
|
||||
verf.Close()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to write version to file: %v", err)
|
||||
}
|
||||
|
||||
if err = z.AddFile("VERSION", verPath); err != nil {
|
||||
log.Fatalf("Failed to add version file: %v", err)
|
||||
}
|
||||
|
||||
if err = z.Close(); err != nil {
|
||||
|
|
148
cmd/restore.go
Normal file
148
cmd/restore.go
Normal file
|
@ -0,0 +1,148 @@
|
|||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/Unknwon/cae/zip"
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// CmdRestore represents the available restore sub-command.
|
||||
var CmdRestore = cli.Command{
|
||||
Name: "restore",
|
||||
Usage: "Restore Gitea files and database",
|
||||
Description: `Restore will restore all data from zip file which dumped from gitea. It will use
|
||||
the custom config in this dump zip file, this operation will remove all the dest database and repositories.`,
|
||||
Action: runRestore,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "config, c",
|
||||
Value: "custom/conf/app.ini",
|
||||
Usage: "Custom configuration file path, if empty will use dumped config file",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "verbose, v",
|
||||
Usage: "Show process details",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "tempdir, t",
|
||||
Value: os.TempDir(),
|
||||
Usage: "Temporary dir path",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func runRestore(ctx *cli.Context) error {
|
||||
if len(os.Args) < 3 {
|
||||
return errors.New("need zip file path")
|
||||
}
|
||||
|
||||
tmpDir := ctx.String("tempdir")
|
||||
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
||||
log.Fatalf("Path does not exist: %s", tmpDir)
|
||||
}
|
||||
tmpWorkDir, err := ioutil.TempDir(tmpDir, "gitea-dump-")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create tmp work directory: %v", err)
|
||||
}
|
||||
log.Printf("Creating tmp work dir: %s", tmpWorkDir)
|
||||
|
||||
// work-around #1103
|
||||
if os.Getenv("TMPDIR") == "" {
|
||||
os.Setenv("TMPDIR", tmpWorkDir)
|
||||
}
|
||||
|
||||
srcPath := os.Args[2]
|
||||
|
||||
zip.Verbose = ctx.Bool("verbose")
|
||||
log.Printf("Extracting %s to tmp work dir", srcPath)
|
||||
err = zip.ExtractTo(srcPath, tmpWorkDir)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to extract %s to tmp work directory: %v", srcPath, err)
|
||||
}
|
||||
|
||||
verData, err := ioutil.ReadFile(filepath.Join(tmpWorkDir, "VERSION"))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to extract %s to tmp work directory: %v", srcPath, err)
|
||||
}
|
||||
|
||||
if setting.AppVer != string(verData) {
|
||||
log.Fatalf("Expected gitea version to restore is %s, but get %s", string(verData), setting.AppVer)
|
||||
}
|
||||
|
||||
if ctx.IsSet("config") {
|
||||
setting.CustomConf = ctx.String("config")
|
||||
} else {
|
||||
setting.CustomConf = filepath.Join(tmpWorkDir, "custom", "conf", "app.ini")
|
||||
}
|
||||
if !com.IsExist(setting.CustomConf) {
|
||||
log.Fatalf("Failed to load ini config file from %s", setting.CustomConf)
|
||||
}
|
||||
|
||||
setting.NewContext()
|
||||
//setting.CustomPath = filepath.Join(tmpWorkDir, "custom")
|
||||
setting.NewXORMLogService(false)
|
||||
models.LoadConfigs()
|
||||
|
||||
err = models.SetEngine()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to SetEngine: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("Restoring repo dir %s ...", setting.RepoRootPath)
|
||||
repoPath := filepath.Join(tmpWorkDir, "repositories")
|
||||
err = os.RemoveAll(setting.RepoRootPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to Remove repo root path %s: %v", setting.RepoRootPath, err)
|
||||
}
|
||||
|
||||
err = os.Rename(repoPath, setting.RepoRootPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to move %s to %s: %v", repoPath, setting.RepoRootPath, err)
|
||||
}
|
||||
|
||||
log.Printf("Restoring custom dir %s ...", setting.CustomPath)
|
||||
customPath := filepath.Join(tmpWorkDir, "custom")
|
||||
err = os.RemoveAll(setting.CustomPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to Remove repo root path %s: %v", setting.CustomPath, err)
|
||||
}
|
||||
|
||||
err = os.Rename(customPath, setting.CustomPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to move %s to %s: %v", customPath, setting.CustomPath, err)
|
||||
}
|
||||
|
||||
log.Printf("Restoring data dir %s ...", setting.AppDataPath)
|
||||
dataPath := filepath.Join(tmpWorkDir, "data")
|
||||
err = os.RemoveAll(setting.AppDataPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to Remove data root path %s: %v", setting.AppDataPath, err)
|
||||
}
|
||||
|
||||
err = os.Rename(dataPath, setting.AppDataPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to move %s to %s: %v", dataPath, setting.AppDataPath, err)
|
||||
}
|
||||
|
||||
log.Printf("Restoring database from ...")
|
||||
dbPath := filepath.Join(tmpWorkDir, "database")
|
||||
err = models.RestoreDatabaseFixtures(dbPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to restore database dir %s: %v", dbPath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
1
main.go
1
main.go
|
@ -43,6 +43,7 @@ arguments - which can alternatively be run by running the subcommand web.`
|
|||
cmd.CmdServ,
|
||||
cmd.CmdHook,
|
||||
cmd.CmdDump,
|
||||
cmd.CmdRestore,
|
||||
cmd.CmdCert,
|
||||
cmd.CmdAdmin,
|
||||
cmd.CmdGenerate,
|
||||
|
|
|
@ -8,10 +8,12 @@ import (
|
|||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
@ -21,6 +23,7 @@ import (
|
|||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/go-xorm/core"
|
||||
"github.com/go-xorm/xorm"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
// Needed for the Postgresql driver
|
||||
_ "github.com/lib/pq"
|
||||
|
@ -352,3 +355,80 @@ func DumpDatabase(filePath string, dbType string) error {
|
|||
}
|
||||
return x.DumpTablesToFile(tbs, filePath)
|
||||
}
|
||||
|
||||
// DumpDatabaseFixtures dumps all data from database to fixtures files on dirPath
|
||||
func DumpDatabaseFixtures(dirPath string) error {
|
||||
for _, t := range tables {
|
||||
if err := dumpTableFixtures(t, dirPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dumpTableFixtures(bean interface{}, dirPath string) error {
|
||||
table := x.TableInfo(bean)
|
||||
f, err := os.Create(filepath.Join(dirPath, table.Name+".yml"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
var bufferSize = 100
|
||||
var objs = make([]interface{}, 0, bufferSize)
|
||||
err = x.BufferSize(bufferSize).Iterate(bean, func(idx int, obj interface{}) error {
|
||||
objs = append(objs, obj)
|
||||
if len(objs) == bufferSize {
|
||||
// BLOCK: need yaml support gonic name mapper
|
||||
data, err := yaml.Marshal(objs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.Write(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
objs = make([]interface{}, 0, bufferSize)
|
||||
}
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(objs) > 0 {
|
||||
data, err := yaml.Marshal(objs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.Write(data)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// RestoreDatabaseFixtures restores all data from dir to database
|
||||
func RestoreDatabaseFixtures(dirPath string) error {
|
||||
for _, t := range tables {
|
||||
if err := restoreTableFixtures(t, dirPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func restoreTableFixtures(bean interface{}, dirPath string) error {
|
||||
table := x.TableInfo(bean)
|
||||
data, err := ioutil.ReadFile(filepath.Join(dirPath, table.Name+".yml"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var bufferSize = 100
|
||||
v := reflect.MakeSlice(table.Type, 0, bufferSize)
|
||||
// BLOCK: need yaml support gonic name mapper
|
||||
err = yaml.Unmarshal(data, v.Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = x.Insert(v.Interface())
|
||||
return err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user