1
1
package restic
2
2
3
3
import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "io"
7
+ "os"
4
8
"path/filepath"
5
9
"strconv"
6
10
"strings"
7
11
"time"
8
12
9
13
"github.com/appscode/go/log"
10
14
"github.com/appscode/stash/apis/stash/v1alpha1"
11
- "github.com/pkg/errors "
15
+ "github.com/armon/circbuf "
12
16
)
13
17
14
18
const (
15
- Exe = "/bin/restic_0.9.4"
19
+ ResticCMD = "/bin/restic_0.9.4"
16
20
)
17
21
18
22
type Snapshot struct {
@@ -34,8 +38,11 @@ func (w *ResticWrapper) listSnapshots(snapshotIDs []string) ([]Snapshot, error)
34
38
for _ , id := range snapshotIDs {
35
39
args = append (args , id )
36
40
}
37
-
38
- err := w .sh .Command (Exe , args ... ).UnmarshalJSON (& result )
41
+ out , err := w .run (Command {Name : ResticCMD , Args : args })
42
+ if err != nil {
43
+ return nil , err
44
+ }
45
+ err = json .Unmarshal (out , & result )
39
46
return result , err
40
47
}
41
48
@@ -46,18 +53,18 @@ func (w *ResticWrapper) deleteSnapshots(snapshotIDs []string) ([]byte, error) {
46
53
args = append (args , id )
47
54
}
48
55
49
- return w .run (Exe , args )
56
+ return w .run (Command { Name : ResticCMD , Args : args } )
50
57
}
51
58
52
59
func (w * ResticWrapper ) initRepositoryIfAbsent () ([]byte , error ) {
53
60
log .Infoln ("Ensuring restic repository in the backend" )
54
61
args := w .appendCacheDirFlag ([]interface {}{"snapshots" , "--json" })
55
62
args = w .appendCaCertFlag (args )
56
- if _ , err := w .run (Exe , args ); err != nil {
63
+ if _ , err := w .run (Command { Name : ResticCMD , Args : args } ); err != nil {
57
64
args = w .appendCacheDirFlag ([]interface {}{"init" })
58
65
args = w .appendCaCertFlag (args )
59
66
60
- return w .run (Exe , args )
67
+ return w .run (Command { Name : ResticCMD , Args : args } )
61
68
}
62
69
return nil , nil
63
70
}
@@ -77,7 +84,32 @@ func (w *ResticWrapper) backup(path, host string, tags []string) ([]byte, error)
77
84
args = w .appendCacheDirFlag (args )
78
85
args = w .appendCaCertFlag (args )
79
86
80
- return w .run (Exe , args )
87
+ return w .run (Command {Name : ResticCMD , Args : args })
88
+ }
89
+
90
+ func (w * ResticWrapper ) backupFromStdin (options BackupOptions ) ([]byte , error ) {
91
+ log .Infoln ("Backing up stdin data" )
92
+
93
+ // first add StdinPipeCommand, then add restic command
94
+ var commands []Command
95
+ if options .StdinPipeCommand .Name != "" {
96
+ commands = append (commands , options .StdinPipeCommand )
97
+ }
98
+
99
+ args := []interface {}{"backup" , "--stdin" }
100
+ if options .StdinFileName != "" {
101
+ args = append (args , "--stdin-filename" )
102
+ args = append (args , options .StdinFileName )
103
+ }
104
+ if options .Host != "" {
105
+ args = append (args , "--host" )
106
+ args = append (args , options .Host )
107
+ }
108
+ args = w .appendCacheDirFlag (args )
109
+ args = w .appendCaCertFlag (args )
110
+
111
+ commands = append (commands , Command {Name : ResticCMD , Args : args })
112
+ return w .run (commands ... )
81
113
}
82
114
83
115
func (w * ResticWrapper ) cleanup (retentionPolicy v1alpha1.RetentionPolicy ) ([]byte , error ) {
@@ -124,41 +156,78 @@ func (w *ResticWrapper) cleanup(retentionPolicy v1alpha1.RetentionPolicy) ([]byt
124
156
args = w .appendCacheDirFlag (args )
125
157
args = w .appendCaCertFlag (args )
126
158
127
- return w .run (Exe , args )
159
+ return w .run (Command { Name : ResticCMD , Args : args } )
128
160
}
129
161
return nil , nil
130
162
}
131
163
132
164
func (w * ResticWrapper ) restore (path , host , snapshotID string ) ([]byte , error ) {
133
165
log .Infoln ("Restoring backed up data" )
166
+
134
167
args := []interface {}{"restore" }
135
168
if snapshotID != "" {
136
169
args = append (args , snapshotID )
137
170
} else {
138
171
args = append (args , "latest" )
139
172
}
140
- args = append (args , "--path" )
141
- args = append (args , path ) // source-path specified in restic fileGroup
142
- args = append (args , "--host" )
143
- args = append (args , host )
173
+ if path != "" {
174
+ args = append (args , "--path" )
175
+ args = append (args , path ) // source-path specified in restic fileGroup
176
+ }
177
+ if host != "" {
178
+ args = append (args , "--host" )
179
+ args = append (args , host )
180
+ }
181
+
182
+ args = append (args , "--target" , "/" ) // restore in absolute path
183
+ args = w .appendCacheDirFlag (args )
184
+ args = w .appendCaCertFlag (args )
185
+
186
+ return w .run (Command {Name : ResticCMD , Args : args })
187
+ }
144
188
145
- // Remove last part from the path.
146
- // https://github.com/appscode/stash/issues/392
147
- args = append (args , "--target" , "/" )
148
- // args = append(args, filepath.Dir(path))
189
+ func (w * ResticWrapper ) dump (dumpOptions DumpOptions ) ([]byte , error ) {
190
+ log .Infoln ("Dumping backed up data" )
191
+
192
+ args := []interface {}{"dump" }
193
+ if dumpOptions .Snapshot != "" {
194
+ args = append (args , dumpOptions .Snapshot )
195
+ } else {
196
+ args = append (args , "latest" )
197
+ }
198
+ if dumpOptions .FileName != "" {
199
+ args = append (args , dumpOptions .FileName )
200
+ } else {
201
+ args = append (args , "stdin" )
202
+ }
203
+ if dumpOptions .Host != "" {
204
+ args = append (args , "--host" )
205
+ args = append (args , dumpOptions .Host )
206
+ }
207
+ if dumpOptions .Path != "" {
208
+ args = append (args , "--path" )
209
+ args = append (args , dumpOptions .Path )
210
+ }
149
211
150
212
args = w .appendCacheDirFlag (args )
151
213
args = w .appendCaCertFlag (args )
152
214
153
- return w .run (Exe , args )
215
+ // first add restic command, then add StdoutPipeCommand
216
+ commands := []Command {
217
+ {Name : ResticCMD , Args : args },
218
+ }
219
+ if dumpOptions .StdoutPipeCommand .Name != "" {
220
+ commands = append (commands , dumpOptions .StdoutPipeCommand )
221
+ }
222
+ return w .run (commands ... )
154
223
}
155
224
156
225
func (w * ResticWrapper ) check () ([]byte , error ) {
157
226
log .Infoln ("Checking integrity of repository" )
158
227
args := w .appendCacheDirFlag ([]interface {}{"check" })
159
228
args = w .appendCaCertFlag (args )
160
229
161
- return w .run (Exe , args )
230
+ return w .run (Command { Name : ResticCMD , Args : args } )
162
231
}
163
232
164
233
func (w * ResticWrapper ) stats () ([]byte , error ) {
@@ -167,7 +236,7 @@ func (w *ResticWrapper) stats() ([]byte, error) {
167
236
args = append (args , "--mode=raw-data" , "--quiet" )
168
237
args = w .appendCaCertFlag (args )
169
238
170
- return w .run (Exe , args )
239
+ return w .run (Command { Name : ResticCMD , Args : args } )
171
240
}
172
241
173
242
func (w * ResticWrapper ) appendCacheDirFlag (args []interface {}) []interface {} {
@@ -185,15 +254,30 @@ func (w *ResticWrapper) appendCaCertFlag(args []interface{}) []interface{} {
185
254
return args
186
255
}
187
256
188
- func (w * ResticWrapper ) run (cmd string , args []interface {}) ([]byte , error ) {
189
- out , err := w .sh .Command (cmd , args ... ).Output ()
257
+ func (w * ResticWrapper ) run (commands ... Command ) ([]byte , error ) {
258
+ // write std errors into os.Stderr and buffer
259
+ errBuff , err := circbuf .NewBuffer (256 )
190
260
if err != nil {
191
- log .Errorf ("Error running command '%s %s' output:\n %s" , cmd , args , string (out ))
192
- parts := strings .Split (strings .TrimSuffix (string (out ), "\n " ), "\n " )
193
- if len (parts ) > 1 {
194
- parts = parts [len (parts )- 1 :]
195
- return nil , errors .New (parts [0 ])
196
- }
197
- }
198
- return out , err
261
+ return nil , err
262
+ }
263
+ w .sh .Stderr = io .MultiWriter (os .Stderr , errBuff )
264
+
265
+ for _ , cmd := range commands {
266
+ w .sh .Command (cmd .Name , cmd .Args ... )
267
+ }
268
+ out , err := w .sh .Output ()
269
+ if err != nil {
270
+ return nil , formatError (err , errBuff .String ())
271
+ }
272
+ log .Infoln ("sh-output:" , string (out ))
273
+ return out , nil
274
+ }
275
+
276
+ // return last line of std error as error reason
277
+ func formatError (err error , stdErr string ) error {
278
+ parts := strings .Split (strings .TrimSuffix (stdErr , "\n " ), "\n " )
279
+ if len (parts ) > 1 {
280
+ return fmt .Errorf ("%s, reason: %s" , err , parts [len (parts )- 1 :][0 ])
281
+ }
282
+ return err
199
283
}
0 commit comments