1
0
Fork 0

Support backups from CLI

Port PR436.
master
Philip O'Toole 5 years ago
parent 193f958c20
commit 7a81104082

@ -32,8 +32,14 @@ func execute(ctx *cli.Context, cmd, line string, timer bool, argv *argT) error {
Path: fmt.Sprintf("%sdb/execute", argv.Prefix), Path: fmt.Sprintf("%sdb/execute", argv.Prefix),
RawQuery: queryStr.Encode(), RawQuery: queryStr.Encode(),
} }
response, err := sendRequest(ctx, "POST", u.String(), line, argv)
if err != nil {
return err
}
ret := &executeResponse{} ret := &executeResponse{}
if err := sendRequest(ctx, u.String(), line, argv, ret); err != nil { if err := parseResponse(response, &ret); err != nil {
return err return err
} }
if ret.Error != "" { if ret.Error != "" {

@ -6,6 +6,7 @@ import (
"crypto/x509" "crypto/x509"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strings" "strings"
@ -34,6 +35,7 @@ const cliHelp = `.help Show this message
.expvar Show expvar (Go runtime) information for connected node .expvar Show expvar (Go runtime) information for connected node
.tables List names of tables .tables List names of tables
.timer on|off Turn SQL timer on or off .timer on|off Turn SQL timer on or off
.backup <file> Write database backup to file
` `
func main() { func main() {
@ -78,6 +80,12 @@ func main() {
err = status(ctx, cmd, line, argv) err = status(ctx, cmd, line, argv)
case ".EXPVAR": case ".EXPVAR":
err = expvar(ctx, cmd, line, argv) err = expvar(ctx, cmd, line, argv)
case ".BACKUP":
if index == -1 || index == len(line)-1 {
err = fmt.Errorf("Please specify an output file for the backup")
break
}
err = backup(ctx, line[index+1:], argv)
case ".HELP": case ".HELP":
err = help(ctx, cmd, line, argv) err = help(ctx, cmd, line, argv)
case ".QUIT", "QUIT", "EXIT": case ".QUIT", "QUIT", "EXIT":
@ -127,22 +135,28 @@ func expvar(ctx *cli.Context, cmd, line string, argv *argT) error {
return cliJSON(ctx, cmd, line, url, argv) return cliJSON(ctx, cmd, line, url, argv)
} }
func sendRequest(ctx *cli.Context, urlStr string, line string, argv *argT, ret interface{}) error { func sendRequest(ctx *cli.Context, method string, urlStr string, line string, argv *argT) (*[]byte, error) {
data := makeJSONBody(line) var requestData io.Reader
if line != "" {
requestData = strings.NewReader(makeJSONBody(line))
} else {
requestData = nil
}
url := urlStr url := urlStr
var rootCAs *x509.CertPool var rootCAs *x509.CertPool
if argv.CACert != "" { if argv.CACert != "" {
pemCerts, err := ioutil.ReadFile(argv.CACert) pemCerts, err := ioutil.ReadFile(argv.CACert)
if err != nil { if err != nil {
return err return nil, err
} }
rootCAs = x509.NewCertPool() rootCAs = x509.NewCertPool()
ok := rootCAs.AppendCertsFromPEM(pemCerts) ok := rootCAs.AppendCertsFromPEM(pemCerts)
if !ok { if !ok {
return fmt.Errorf("failed to parse root CA certificate(s)") return nil, fmt.Errorf("failed to parse root CA certificate(s)")
} }
} }
@ -157,48 +171,52 @@ func sendRequest(ctx *cli.Context, urlStr string, line string, argv *argT, ret i
nRedirect := 0 nRedirect := 0
for { for {
req, err := http.NewRequest("POST", url, strings.NewReader(data)) req, err := http.NewRequest(method, url, requestData)
if err != nil { if err != nil {
return err return nil, err
} }
if argv.Credentials != "" { if argv.Credentials != "" {
creds := strings.Split(argv.Credentials, ":") creds := strings.Split(argv.Credentials, ":")
if len(creds) != 2 { if len(creds) != 2 {
return fmt.Errorf("invalid Basic Auth credentials format") return nil, fmt.Errorf("invalid Basic Auth credentials format")
} }
req.SetBasicAuth(creds[0], creds[1]) req.SetBasicAuth(creds[0], creds[1])
} }
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode == http.StatusUnauthorized { if resp.StatusCode == http.StatusUnauthorized {
return fmt.Errorf("unauthorized") return nil, fmt.Errorf("unauthorized")
} }
// Check for redirect. // Check for redirect.
if resp.StatusCode == http.StatusMovedPermanently { if resp.StatusCode == http.StatusMovedPermanently {
nRedirect++ nRedirect++
if nRedirect > maxRedirect { if nRedirect > maxRedirect {
return fmt.Errorf("maximum leader redirect limit exceeded") return nil, fmt.Errorf("maximum leader redirect limit exceeded")
} }
url = resp.Header["Location"][0] url = resp.Header["Location"][0]
continue continue
} }
body, err := ioutil.ReadAll(resp.Body) response, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return err return nil, err
} }
return json.Unmarshal(body, ret) return &response, nil
} }
} }
func parseResponse(response *[]byte, ret interface{}) error {
return json.Unmarshal(*response, ret)
}
// cliJSON fetches JSON from a URL, and displays it at the CLI. // cliJSON fetches JSON from a URL, and displays it at the CLI.
func cliJSON(ctx *cli.Context, cmd, line, url string, argv *argT) error { func cliJSON(ctx *cli.Context, cmd, line, url string, argv *argT) error {
// Recursive JSON printer. // Recursive JSON printer.

@ -91,8 +91,14 @@ func query(ctx *cli.Context, cmd, line string, timer bool, argv *argT) error {
Path: fmt.Sprintf("%sdb/query", argv.Prefix), Path: fmt.Sprintf("%sdb/query", argv.Prefix),
RawQuery: queryStr.Encode(), RawQuery: queryStr.Encode(),
} }
response, err := sendRequest(ctx, "POST", u.String(), line, argv)
if err != nil {
return err
}
ret := &queryResponse{} ret := &queryResponse{}
if err := sendRequest(ctx, u.String(), line, argv, ret); err != nil { if err := parseResponse(response, &ret); err != nil {
return err return err
} }
if ret.Error != "" { if ret.Error != "" {

Loading…
Cancel
Save