Skip to content

Commit 6db6d2f

Browse files
AMeceacalind
authored andcommitted
Use http trailer to check if the backup was done successfully
1 parent 0469b6e commit 6db6d2f

File tree

4 files changed

+85
-36
lines changed

4 files changed

+85
-36
lines changed

pkg/sidecar/appclone.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ func cloneFromBucket(initBucket string) error {
153153
func cloneFromSource(cfg *Config, host string) error {
154154
log.Info("cloning from node", "host", host)
155155

156-
backupBody, err := requestABackup(cfg, host, serverBackupEndpoint)
156+
response, err := requestABackup(cfg, host, serverBackupEndpoint)
157157
if err != nil {
158158
return fmt.Errorf("fail to get backup: %s", err)
159159
}
@@ -164,7 +164,7 @@ func cloneFromSource(cfg *Config, host string) error {
164164
// nolint: gosec
165165
xbstream := exec.Command("xbstream", "-x", "-C", dataDir)
166166

167-
xbstream.Stdin = backupBody
167+
xbstream.Stdin = response.Body
168168
xbstream.Stderr = os.Stderr
169169

170170
if err := xbstream.Start(); err != nil {
@@ -175,6 +175,10 @@ func cloneFromSource(cfg *Config, host string) error {
175175
return fmt.Errorf("xbstream wait error: %s", err)
176176
}
177177

178+
if err := checkBackupTrailers(response); err != nil {
179+
return err
180+
}
181+
178182
return nil
179183
}
180184

pkg/sidecar/apptakebackup.go

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ func RunTakeBackupCommand(cfg *Config, srcHost, destBucket string) error {
3131
}
3232

3333
func pushBackupFromTo(cfg *Config, srcHost, destBucket string) error {
34+
tmpDestBucket := fmt.Sprintf("%s.tmp", destBucket)
3435

35-
backupBody, err := requestABackup(cfg, srcHost, serverBackupEndpoint)
36+
response, err := requestABackup(cfg, srcHost, serverBackupEndpoint)
3637
if err != nil {
3738
return fmt.Errorf("getting backup: %s", err)
3839
}
@@ -42,9 +43,9 @@ func pushBackupFromTo(cfg *Config, srcHost, destBucket string) error {
4243

4344
// nolint: gosec
4445
rclone := exec.Command("rclone",
45-
fmt.Sprintf("--config=%s", rcloneConfigFile), "rcat", destBucket)
46+
fmt.Sprintf("--config=%s", rcloneConfigFile), "rcat", tmpDestBucket)
4647

47-
gzip.Stdin = backupBody
48+
gzip.Stdin = response.Body
4849
gzip.Stderr = os.Stderr
4950
rclone.Stderr = os.Stderr
5051

@@ -66,11 +67,33 @@ func pushBackupFromTo(cfg *Config, srcHost, destBucket string) error {
6667

6768
// wait for both commands to finish successful
6869
for i := 1; i <= 2; i++ {
69-
if err := <-errChan; err != nil {
70+
if err = <-errChan; err != nil {
7071
return err
7172
}
7273
}
7374

75+
if err = checkBackupTrailers(response); err != nil {
76+
// backup failed so delete it from remote
77+
log.Info("backup was partially taken", "trailers", response.Trailer)
78+
return err
79+
}
80+
81+
log.Info("backup was taken successfully now move, now move it to permanent URL")
82+
83+
// the backup was a success
84+
// remove .tmp extension
85+
// nolint: gosec
86+
rcMove := exec.Command("rclone",
87+
fmt.Sprintf("--config=%s", rcloneConfigFile), "moveto", tmpDestBucket, destBucket)
88+
89+
if err = rcMove.Start(); err != nil {
90+
return fmt.Errorf("final move failed: %s", err)
91+
}
92+
93+
if err = rcMove.Wait(); err != nil {
94+
return fmt.Errorf("final move failed: %s", err)
95+
}
96+
7497
return nil
7598
}
7699

pkg/sidecar/server.go

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ func (s *server) backupHandler(w http.ResponseWriter, r *http.Request) {
7979

8080
w.Header().Set("Content-Type", "application/octet-stream")
8181
w.Header().Set("Connection", "keep-alive")
82+
w.Header().Set("Trailer", "Success")
8283

8384
// nolint: gosec
8485
xtrabackup := exec.Command("xtrabackup", "--backup", "--slave-info", "--stream=xbstream",
@@ -112,13 +113,15 @@ func (s *server) backupHandler(w http.ResponseWriter, r *http.Request) {
112113
return
113114
}
114115

115-
flusher.Flush()
116-
117116
if err := xtrabackup.Wait(); err != nil {
118117
log.Error(err, "failed waiting for xtrabackup to finish")
119118
http.Error(w, "xtrabackup failed", http.StatusInternalServerError)
120119
return
121120
}
121+
122+
// success
123+
w.Header().Set("Success", "true")
124+
flusher.Flush()
122125
}
123126

124127
func (s *server) isAuthenticated(r *http.Request) bool {
@@ -137,3 +140,49 @@ func maxClients(h http.Handler, n int) http.Handler {
137140
h.ServeHTTP(w, r)
138141
})
139142
}
143+
144+
// requestABackup connects to specified host and endpoint and gets the backup
145+
func requestABackup(cfg *Config, host, endpoint string) (*http.Response, error) {
146+
log.Info("initialize a backup", "host", host, "endpoint", endpoint)
147+
148+
req, err := http.NewRequest("GET", fmt.Sprintf(
149+
"http://%s:%d%s", host, serverPort, endpoint), nil)
150+
151+
if err != nil {
152+
return nil, fmt.Errorf("fail to create request: %s", err)
153+
}
154+
155+
// set authentification user and password
156+
req.SetBasicAuth(cfg.BackupUser, cfg.BackupPassword)
157+
158+
client := &http.Client{}
159+
160+
resp, err := client.Do(req)
161+
if err != nil || resp.StatusCode != 200 {
162+
status := "unknown"
163+
if resp != nil {
164+
status = resp.Status
165+
}
166+
return nil, fmt.Errorf("fail to get backup: %s, code: %s", err, status)
167+
}
168+
169+
return resp, nil
170+
}
171+
172+
func checkBackupTrailers(resp *http.Response) error {
173+
if values, ok := resp.Trailer["Success"]; !ok || !stringInSlice("true", values) {
174+
// backup is failed, remove from remote
175+
return fmt.Errorf("backup failed to be taken: no 'Success' trailer found")
176+
}
177+
178+
return nil
179+
}
180+
181+
func stringInSlice(a string, list []string) bool {
182+
for _, b := range list {
183+
if b == a {
184+
return true
185+
}
186+
}
187+
return false
188+
}

pkg/sidecar/util.go

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"database/sql"
2121
"fmt"
2222
"io"
23-
"net/http"
2423
"os"
2524

2625
// add mysql driver
@@ -47,7 +46,7 @@ func runQuery(cfg *Config, q string, args ...interface{}) error {
4746
}
4847
}()
4948

50-
log.V(1).Info("running query", "query", q, "args", args)
49+
log.V(1).Info("running query", "query", q)
5150
if _, err := db.Exec(q, args...); err != nil {
5251
return err
5352
}
@@ -86,32 +85,6 @@ func copyFile(src, dst string) error {
8685
return nil
8786
}
8887

89-
// requestABackup connects to specified host and endpoint and gets the backup
90-
func requestABackup(cfg *Config, host, endpoint string) (io.Reader, error) {
91-
log.Info("initialize a backup", "host", host, "endpoint", endpoint)
92-
93-
req, err := http.NewRequest("GET", fmt.Sprintf("http://%s:%d%s", host, serverPort, endpoint), nil)
94-
if err != nil {
95-
return nil, fmt.Errorf("fail to create request: %s", err)
96-
}
97-
98-
// set authentification user and password
99-
req.SetBasicAuth(cfg.BackupUser, cfg.BackupPassword)
100-
101-
client := &http.Client{}
102-
103-
resp, err := client.Do(req)
104-
if err != nil || resp.StatusCode != 200 {
105-
status := "unknown"
106-
if resp != nil {
107-
status = resp.Status
108-
}
109-
return nil, fmt.Errorf("fail to get backup: %s, code: %s", err, status)
110-
}
111-
112-
return resp.Body, nil
113-
}
114-
11588
// shouldBootstrapNode checks if the mysql data is at the first initialization
11689
func shouldBootstrapNode() bool {
11790
_, err := os.Open(fmt.Sprintf("%s/%s/%s.CSV", dataDir,

0 commit comments

Comments
 (0)