1
0
Fork 0

Add .boot suport to CLI

master
Philip O'Toole 9 months ago
parent ae194837c2
commit d73edb12cf

@ -1,4 +1,7 @@
## 8.10.1 (unreleased)
## 8.11.0 (December 17th 2023)
### New features
- [PR #1488](https://github.com/rqlite/rqlite/pull/1488): Add `.boot` command to rqlite shell, to support _Booting_ nodes.
### Implementation changes and bug fixes
- [PR #1487](https://github.com/rqlite/rqlite/pull/1487): Small improvements to rqlite shell.

@ -82,6 +82,20 @@ func dump(ctx *cli.Context, filename string, argv *argT) error {
return nil
}
func validSQLiteFile(path string) bool {
file, err := os.Open(path)
if err != nil {
return false
}
defer file.Close()
b := make([]byte, 16)
_, err = file.Read(b)
if err != nil {
return false
}
return validSQLiteData(b)
}
func validSQLiteData(b []byte) bool {
return len(b) > 13 && string(b[0:13]) == "SQLite format"
}
@ -188,3 +202,89 @@ func restore(ctx *cli.Context, filename string, argv *argT) error {
ctx.String("database restored successfully\n")
return nil
}
func boot(ctx *cli.Context, filename string, argv *argT) error {
statusURL := fmt.Sprintf("%s://%s:%d/status", argv.Protocol, argv.Host, argv.Port)
client := http.Client{Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{InsecureSkipVerify: argv.Insecure},
}}
req, err := http.NewRequest("GET", statusURL, nil)
if err != nil {
return err
}
if argv.Credentials != "" {
creds := strings.Split(argv.Credentials, ":")
if len(creds) != 2 {
return fmt.Errorf("invalid Basic Auth credentials format")
}
req.SetBasicAuth(creds[0], creds[1])
}
// Check that we can talk to the node OK.
statusResp, err := client.Do(req)
if err != nil {
return err
}
defer statusResp.Body.Close()
if statusResp.StatusCode == http.StatusUnauthorized {
return fmt.Errorf("unauthorized")
}
body, err := io.ReadAll(statusResp.Body)
if err != nil {
return err
}
statusRet := &statusResponse{}
if err := parseResponse(&body, &statusRet); err != nil {
return err
}
if statusRet.Store == nil {
return fmt.Errorf("unexpected server response: store status not found")
}
// File is OK?
if !validSQLiteFile(filename) {
return fmt.Errorf("%s is not a valid SQLite file", filename)
}
// Do the boot.
fd, err := os.Open(filename)
if err != nil {
return err
}
defer fd.Close()
bootURL := fmt.Sprintf("%s://%s:%d/boot", argv.Protocol, argv.Host, argv.Port)
req, err = http.NewRequest("POST", bootURL, fd)
if err != nil {
return err
}
if argv.Credentials != "" {
creds := strings.Split(argv.Credentials, ":")
if len(creds) != 2 {
return fmt.Errorf("invalid Basic Auth credentials format")
}
req.SetBasicAuth(creds[0], creds[1])
}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, err = io.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
errMsg := fmt.Sprintf("boot failed, status code: %s", resp.Status)
if len(body) > 0 {
errMsg += fmt.Sprintf(", %s", string(body))
}
return fmt.Errorf(errMsg)
}
ctx.String("node booted successfully\n")
return nil
}

@ -50,6 +50,7 @@ type argT struct {
var cliHelp = []string{
`.backup <file> Write database backup to SQLite file`,
`.boot <file> Boot the node from a SQLite database file`,
`.consistency [none|weak|strong] Show or set read consistency level`,
`.dump <file> Dump the database in SQL text format to a file`,
`.exit Exit this program`,
@ -191,6 +192,12 @@ func main() {
break
}
err = restore(ctx, line[index+1:], argv)
case ".BOOT":
if index == -1 || index == len(line)-1 {
err = fmt.Errorf("please specify an input file to boot with")
break
}
err = boot(ctx, line[index+1:], argv)
case ".SYSDUMP":
if index == -1 || index == len(line)-1 {
err = fmt.Errorf("please specify an output file for the sysdump")

Loading…
Cancel
Save