Skip to content

Commit 971b13c

Browse files
committed
Reimplement dump and implment restore command
1 parent bb80148 commit 971b13c

File tree

4 files changed

+252
-23
lines changed

4 files changed

+252
-23
lines changed

cmd/dump.go

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Copyright 2014 The Gogs Authors. All rights reserved.
2-
// Copyright 2016 The Gitea Authors. All rights reserved.
2+
// Copyright 2017 The Gitea Authors. All rights reserved.
33
// Use of this source code is governed by a MIT-style
44
// license that can be found in the LICENSE file.
55

@@ -44,10 +44,6 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
4444
Value: os.TempDir(),
4545
Usage: "Temporary dir path",
4646
},
47-
cli.StringFlag{
48-
Name: "database, d",
49-
Usage: "Specify the database SQL syntax",
50-
},
5147
},
5248
}
5349

@@ -79,23 +75,15 @@ func runDump(ctx *cli.Context) error {
7975
os.Setenv("TMPDIR", tmpWorkDir)
8076
}
8177

82-
reposDump := path.Join(tmpWorkDir, "gitea-repo.zip")
83-
dbDump := path.Join(tmpWorkDir, "gitea-db.sql")
84-
78+
dbDump := path.Join(tmpWorkDir, "database")
8579
log.Printf("Dumping local repositories...%s", setting.RepoRootPath)
8680
zip.Verbose = ctx.Bool("verbose")
87-
if err := zip.PackTo(setting.RepoRootPath, reposDump, true); err != nil {
88-
log.Fatalf("Failed to dump local repositories: %v", err)
89-
}
90-
91-
targetDBType := ctx.String("database")
92-
if len(targetDBType) > 0 && targetDBType != models.DbCfg.Type {
93-
log.Printf("Dumping database %s => %s...", models.DbCfg.Type, targetDBType)
94-
} else {
95-
log.Printf("Dumping database...")
81+
if err := os.MkdirAll(dbDump, os.ModePerm); err != nil {
82+
log.Fatalf("Failed to create database dir: %v", err)
9683
}
9784

98-
if err := models.DumpDatabase(dbDump, targetDBType); err != nil {
85+
log.Printf("Dumping database ...")
86+
if err := models.DumpDatabaseFixtures(dbDump); err != nil {
9987
log.Fatalf("Failed to dump database: %v", err)
10088
}
10189

@@ -106,11 +94,12 @@ func runDump(ctx *cli.Context) error {
10694
log.Fatalf("Failed to create %s: %v", fileName, err)
10795
}
10896

109-
if err := z.AddFile("gitea-repo.zip", reposDump); err != nil {
97+
if err := z.AddDir("repositories", setting.RepoRootPath); err != nil {
11098
log.Fatalf("Failed to include gitea-repo.zip: %v", err)
11199
}
112-
if err := z.AddFile("gitea-db.sql", dbDump); err != nil {
113-
log.Fatalf("Failed to include gitea-db.sql: %v", err)
100+
101+
if err := z.AddDir("database", dbDump); err != nil {
102+
log.Fatalf("Failed to include database: %v", err)
114103
}
115104
customDir, err := os.Stat(setting.CustomPath)
116105
if err == nil && customDir.IsDir() {
@@ -133,8 +122,19 @@ func runDump(ctx *cli.Context) error {
133122
}
134123
}
135124

136-
if err := z.AddDir("log", setting.LogRootPath); err != nil {
137-
log.Fatalf("Failed to include log: %v", err)
125+
verPath := filepath.Join(tmpWorkDir, "VERSION")
126+
verf, err := os.Create(verPath)
127+
if err != nil {
128+
log.Fatalf("Failed to create version file: %v", err)
129+
}
130+
_, err = verf.WriteString(setting.AppVer)
131+
verf.Close()
132+
if err != nil {
133+
log.Fatalf("Failed to write version to file: %v", err)
134+
}
135+
136+
if err = z.AddFile("VERSION", verPath); err != nil {
137+
log.Fatalf("Failed to add version file: %v", err)
138138
}
139139

140140
if err = z.Close(); err != nil {

cmd/restore.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// Copyright 2017 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package cmd
6+
7+
import (
8+
"errors"
9+
"io/ioutil"
10+
"log"
11+
"os"
12+
"path/filepath"
13+
14+
"code.gitea.io/gitea/models"
15+
"code.gitea.io/gitea/modules/setting"
16+
17+
"github.com/Unknwon/cae/zip"
18+
"github.com/Unknwon/com"
19+
"github.com/urfave/cli"
20+
)
21+
22+
// CmdRestore represents the available restore sub-command.
23+
var CmdRestore = cli.Command{
24+
Name: "restore",
25+
Usage: "Restore Gitea files and database",
26+
Description: `Restore will restore all data from zip file which dumped from gitea. It will use
27+
the custom config in this dump zip file, this operation will remove all the dest database and repositories.`,
28+
Action: runRestore,
29+
Flags: []cli.Flag{
30+
cli.StringFlag{
31+
Name: "config, c",
32+
Value: "custom/conf/app.ini",
33+
Usage: "Custom configuration file path, if empty will use dumped config file",
34+
},
35+
cli.BoolFlag{
36+
Name: "verbose, v",
37+
Usage: "Show process details",
38+
},
39+
cli.StringFlag{
40+
Name: "tempdir, t",
41+
Value: os.TempDir(),
42+
Usage: "Temporary dir path",
43+
},
44+
},
45+
}
46+
47+
func runRestore(ctx *cli.Context) error {
48+
if len(os.Args) < 3 {
49+
return errors.New("need zip file path")
50+
}
51+
52+
tmpDir := ctx.String("tempdir")
53+
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
54+
log.Fatalf("Path does not exist: %s", tmpDir)
55+
}
56+
tmpWorkDir, err := ioutil.TempDir(tmpDir, "gitea-dump-")
57+
if err != nil {
58+
log.Fatalf("Failed to create tmp work directory: %v", err)
59+
}
60+
log.Printf("Creating tmp work dir: %s", tmpWorkDir)
61+
62+
// work-around #1103
63+
if os.Getenv("TMPDIR") == "" {
64+
os.Setenv("TMPDIR", tmpWorkDir)
65+
}
66+
67+
srcPath := os.Args[2]
68+
69+
zip.Verbose = ctx.Bool("verbose")
70+
log.Printf("Extracting %s to tmp work dir", srcPath)
71+
err = zip.ExtractTo(srcPath, tmpWorkDir)
72+
if err != nil {
73+
log.Fatalf("Failed to extract %s to tmp work directory: %v", srcPath, err)
74+
}
75+
76+
verData, err := ioutil.ReadFile(filepath.Join(tmpWorkDir, "VERSION"))
77+
if err != nil {
78+
log.Fatalf("Failed to extract %s to tmp work directory: %v", srcPath, err)
79+
}
80+
81+
if setting.AppVer != string(verData) {
82+
log.Fatalf("Expected gitea version to restore is %s, but get %s", string(verData), setting.AppVer)
83+
}
84+
85+
if ctx.IsSet("config") {
86+
setting.CustomConf = ctx.String("config")
87+
} else {
88+
setting.CustomConf = filepath.Join(tmpWorkDir, "custom", "conf", "app.ini")
89+
}
90+
if !com.IsExist(setting.CustomConf) {
91+
log.Fatalf("Failed to load ini config file from %s", setting.CustomConf)
92+
}
93+
94+
setting.NewContext()
95+
//setting.CustomPath = filepath.Join(tmpWorkDir, "custom")
96+
setting.NewXORMLogService(false)
97+
models.LoadConfigs()
98+
99+
err = models.SetEngine()
100+
if err != nil {
101+
log.Fatalf("Failed to SetEngine: %v", err)
102+
}
103+
104+
log.Printf("Restoring repo dir %s ...", setting.RepoRootPath)
105+
repoPath := filepath.Join(tmpWorkDir, "repositories")
106+
err = os.RemoveAll(setting.RepoRootPath)
107+
if err != nil {
108+
log.Fatalf("Failed to Remove repo root path %s: %v", setting.RepoRootPath, err)
109+
}
110+
111+
err = os.Rename(repoPath, setting.RepoRootPath)
112+
if err != nil {
113+
log.Fatalf("Failed to move %s to %s: %v", repoPath, setting.RepoRootPath, err)
114+
}
115+
116+
log.Printf("Restoring custom dir %s ...", setting.CustomPath)
117+
customPath := filepath.Join(tmpWorkDir, "custom")
118+
err = os.RemoveAll(setting.CustomPath)
119+
if err != nil {
120+
log.Fatalf("Failed to Remove repo root path %s: %v", setting.CustomPath, err)
121+
}
122+
123+
err = os.Rename(customPath, setting.CustomPath)
124+
if err != nil {
125+
log.Fatalf("Failed to move %s to %s: %v", customPath, setting.CustomPath, err)
126+
}
127+
128+
log.Printf("Restoring data dir %s ...", setting.AppDataPath)
129+
dataPath := filepath.Join(tmpWorkDir, "data")
130+
err = os.RemoveAll(setting.AppDataPath)
131+
if err != nil {
132+
log.Fatalf("Failed to Remove data root path %s: %v", setting.AppDataPath, err)
133+
}
134+
135+
err = os.Rename(dataPath, setting.AppDataPath)
136+
if err != nil {
137+
log.Fatalf("Failed to move %s to %s: %v", dataPath, setting.AppDataPath, err)
138+
}
139+
140+
log.Printf("Restoring database from ...")
141+
dbPath := filepath.Join(tmpWorkDir, "database")
142+
err = models.RestoreDatabaseFixtures(dbPath)
143+
if err != nil {
144+
log.Fatalf("Failed to restore database dir %s: %v", dbPath, err)
145+
}
146+
147+
return nil
148+
}

main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ arguments - which can alternatively be run by running the subcommand web.`
4343
cmd.CmdServ,
4444
cmd.CmdHook,
4545
cmd.CmdDump,
46+
cmd.CmdRestore,
4647
cmd.CmdCert,
4748
cmd.CmdAdmin,
4849
cmd.CmdGenerate,

models/models.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ import (
88
"database/sql"
99
"errors"
1010
"fmt"
11+
"io/ioutil"
1112
"net/url"
1213
"os"
1314
"path"
1415
"path/filepath"
16+
"reflect"
1517
"strings"
1618

1719
"code.gitea.io/gitea/modules/log"
@@ -21,6 +23,7 @@ import (
2123
_ "github.com/go-sql-driver/mysql"
2224
"github.com/go-xorm/core"
2325
"github.com/go-xorm/xorm"
26+
"gopkg.in/yaml.v2"
2427

2528
// Needed for the Postgresql driver
2629
_ "github.com/lib/pq"
@@ -352,3 +355,80 @@ func DumpDatabase(filePath string, dbType string) error {
352355
}
353356
return x.DumpTablesToFile(tbs, filePath)
354357
}
358+
359+
// DumpDatabaseFixtures dumps all data from database to fixtures files on dirPath
360+
func DumpDatabaseFixtures(dirPath string) error {
361+
for _, t := range tables {
362+
if err := dumpTableFixtures(t, dirPath); err != nil {
363+
return err
364+
}
365+
}
366+
return nil
367+
}
368+
369+
func dumpTableFixtures(bean interface{}, dirPath string) error {
370+
table := x.TableInfo(bean)
371+
f, err := os.Create(filepath.Join(dirPath, table.Name+".yml"))
372+
if err != nil {
373+
return err
374+
}
375+
defer f.Close()
376+
var bufferSize = 100
377+
var objs = make([]interface{}, 0, bufferSize)
378+
err = x.BufferSize(bufferSize).Iterate(bean, func(idx int, obj interface{}) error {
379+
objs = append(objs, obj)
380+
if len(objs) == bufferSize {
381+
// BLOCK: need yaml support gonic name mapper
382+
data, err := yaml.Marshal(objs)
383+
if err != nil {
384+
return err
385+
}
386+
_, err = f.Write(data)
387+
if err != nil {
388+
return err
389+
}
390+
objs = make([]interface{}, 0, bufferSize)
391+
}
392+
return err
393+
})
394+
if err != nil {
395+
return err
396+
}
397+
if len(objs) > 0 {
398+
data, err := yaml.Marshal(objs)
399+
if err != nil {
400+
return err
401+
}
402+
_, err = f.Write(data)
403+
}
404+
return err
405+
}
406+
407+
// RestoreDatabaseFixtures restores all data from dir to database
408+
func RestoreDatabaseFixtures(dirPath string) error {
409+
for _, t := range tables {
410+
if err := restoreTableFixtures(t, dirPath); err != nil {
411+
return err
412+
}
413+
}
414+
return nil
415+
}
416+
417+
func restoreTableFixtures(bean interface{}, dirPath string) error {
418+
table := x.TableInfo(bean)
419+
data, err := ioutil.ReadFile(filepath.Join(dirPath, table.Name+".yml"))
420+
if err != nil {
421+
return err
422+
}
423+
424+
var bufferSize = 100
425+
v := reflect.MakeSlice(table.Type, 0, bufferSize)
426+
// BLOCK: need yaml support gonic name mapper
427+
err = yaml.Unmarshal(data, v.Interface())
428+
if err != nil {
429+
return err
430+
}
431+
432+
_, err = x.Insert(v.Interface())
433+
return err
434+
}

0 commit comments

Comments
 (0)