-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
Copy pathfile_handler.go
211 lines (182 loc) · 5.65 KB
/
file_handler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// +build !oss
/*
* Copyright 2018 Dgraph Labs, Inc. and Contributors
*
* Licensed under the Dgraph Community License (the "License"); you
* may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* https://github.com/dgraph-io/dgraph/blob/master/licenses/DCL.txt
*/
package backup
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/url"
"os"
"path/filepath"
"sort"
"strings"
"github.com/dgraph-io/dgraph/x"
"github.com/golang/glog"
"github.com/pkg/errors"
)
// fileHandler is used for 'file:' URI scheme.
type fileHandler struct {
fp *os.File
}
// readManifest reads a manifest file at path using the handler.
// Returns nil on success, otherwise an error.
func (h *fileHandler) readManifest(path string, m *Manifest) error {
b, err := ioutil.ReadFile(path)
if err != nil {
return err
}
return json.Unmarshal(b, m)
}
func (h *fileHandler) createFiles(uri *url.URL, req *Request, fileName string) error {
var dir, path string
dir = filepath.Join(uri.Path, fmt.Sprintf(backupPathFmt, req.Backup.UnixTs))
err := os.Mkdir(dir, 0700)
if err != nil && !os.IsExist(err) {
return err
}
path = filepath.Join(dir, fileName)
h.fp, err = os.Create(path)
if err != nil {
return err
}
glog.V(2).Infof("Using file path: %q", path)
return nil
}
// CreateBackupFiles prepares the a path to save backup files and computes the timestamp
// from which to start the backup.
func (h *fileHandler) CreateBackupFiles(uri *url.URL, req *Request) error {
var fileName string
if !pathExist(uri.Path) {
return errors.Errorf("The path %q does not exist or it is inaccessible.", uri.Path)
}
// Find the max Since value from the latest backup.
var lastManifest string
suffix := filepath.Join(string(filepath.Separator), backupManifest)
_ = x.WalkPathFunc(uri.Path, func(path string, isdir bool) bool {
if !isdir && strings.HasSuffix(path, suffix) && path > lastManifest {
lastManifest = path
}
return false
})
if lastManifest != "" {
var m Manifest
if err := h.readManifest(lastManifest, &m); err != nil {
return err
}
req.Since = m.Since
}
fileName = fmt.Sprintf(backupNameFmt, req.Backup.ReadTs, req.Backup.GroupId)
// If a full backup is being forced, force Since to zero to stream all
// the contents from the database.
if req.Backup.ForceFull {
req.Since = 0
}
return h.createFiles(uri, req, fileName)
}
// CreateManifest creates the backup manifest file.
func (h *fileHandler) CreateManifest(uri *url.URL, req *Request, manifest *Manifest) error {
if !pathExist(uri.Path) {
return errors.Errorf("The path %q does not exist or it is inaccessible.", uri.Path)
}
return h.createFiles(uri, req, backupManifest)
}
// Load uses tries to load any backup files found.
// Returns the maximum value of Since on success, error otherwise.
func (h *fileHandler) Load(uri *url.URL, fn loadFn) (uint64, error) {
if !pathExist(uri.Path) {
return 0, errors.Errorf("The path %q does not exist or it is inaccessible.", uri.Path)
}
suffix := filepath.Join(string(filepath.Separator), backupManifest)
manifests := x.WalkPathFunc(uri.Path, func(path string, isdir bool) bool {
return !isdir && strings.HasSuffix(path, suffix)
})
if len(manifests) == 0 {
return 0, errors.Errorf("No manifests found at path: %s", uri.Path)
}
sort.Strings(manifests)
if glog.V(3) {
fmt.Printf("Found backup manifest(s): %v\n", manifests)
}
// Process each manifest, first check that they are valid and then confirm the
// backup files for each group exist. Each group in manifest must have a backup file,
// otherwise this is a failure and the user must remedy.
var since uint64
for _, manifest := range manifests {
var m Manifest
if err := h.readManifest(manifest, &m); err != nil {
return 0, errors.Wrapf(err, "While reading %q", manifest)
}
if m.Since == 0 || len(m.Groups) == 0 {
if glog.V(2) {
fmt.Printf("Restore: skip backup: %s: %#v\n", manifest, &m)
}
continue
}
path := filepath.Dir(manifest)
for _, groupId := range m.Groups {
file := filepath.Join(path, fmt.Sprintf(backupNameFmt, m.Since, groupId))
fp, err := os.Open(file)
if err != nil {
return 0, errors.Wrapf(err, "Failed to open %q", file)
}
defer fp.Close()
if err = fn(fp, int(groupId)); err != nil {
return 0, err
}
}
since = m.Since
}
return since, nil
}
// ListManifests loads the manifests in the locations and returns them.
func (h *fileHandler) ListManifests(uri *url.URL) ([]string, error) {
if !pathExist(uri.Path) {
return nil, errors.Errorf("The path %q does not exist or it is inaccessible.", uri.Path)
}
suffix := filepath.Join(string(filepath.Separator), backupManifest)
manifests := x.WalkPathFunc(uri.Path, func(path string, isdir bool) bool {
return !isdir && strings.HasSuffix(path, suffix)
})
if len(manifests) == 0 {
return nil, errors.Errorf("No manifests found at path: %s", uri.Path)
}
sort.Strings(manifests)
if glog.V(3) {
fmt.Printf("Found backup manifest(s): %v\n", manifests)
}
return manifests, nil
}
func (h *fileHandler) ReadManifest(path string, m *Manifest) error {
return h.readManifest(path, m)
}
func (h *fileHandler) Close() error {
if h.fp == nil {
return nil
}
if err := h.fp.Sync(); err != nil {
glog.Errorf("While closing file: %s. Error: %v", h.fp.Name(), err)
x.Ignore(h.fp.Close())
return err
}
return h.fp.Close()
}
func (h *fileHandler) Write(b []byte) (int, error) {
return h.fp.Write(b)
}
// pathExist checks if a path (file or dir) is found at target.
// Returns true if found, false otherwise.
func pathExist(path string) bool {
_, err := os.Stat(path)
if err == nil {
return true
}
return !os.IsNotExist(err) && !os.IsPermission(err)
}