Skip to content

Commit

Permalink
Merge pull request #661 from tursodatabase/from-dump
Browse files Browse the repository at this point in the history
Implement `--from-dump` to create db from sqlite dump
  • Loading branch information
athoscouto authored Sep 27, 2023
2 parents 932d62b + 7d2f75e commit 4d434d3
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 0 deletions.
42 changes: 42 additions & 0 deletions internal/cmd/db_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"fmt"
"os"
"time"

"github.com/athoscouto/codename"
Expand All @@ -11,10 +12,13 @@ import (
"github.com/spf13/cobra"
)

const MaxDumpFileSizeBytes = 2 << 30

func init() {
dbCmd.AddCommand(createCmd)
addGroupFlag(createCmd)
addFromDBFlag(createCmd)
addDbFromDumpFlag(createCmd)
addLocationFlag(createCmd, "Location ID. If no ID is specified, closest location to you is used by default.")

addWaitFlag(createCmd, "Wait for the database to be ready to receive requests.")
Expand Down Expand Up @@ -62,9 +66,29 @@ var createCmd = &cobra.Command{
return err
}

var dump *os.File
if fromDumpFlag != "" {
dump, err = validateDumpFile()
if err != nil {
return err
}
}

start := time.Now()
spinner := prompt.Spinner(fmt.Sprintf("Creating database %s in group %s...", internal.Emph(name), internal.Emph(group)))
defer spinner.Stop()

if dump != nil {
dumpURL, err := client.Databases.UploadDump(dump)
if err != nil {
return fmt.Errorf("could not upload dump: %w", err)
}
seed = &turso.DBSeed{
Type: "dump",
URL: dumpURL,
}
}

if _, err = client.Databases.Create(name, location, "", "", group, seed); err != nil {
return fmt.Errorf("could not create database %s: %w", name, err)
}
Expand Down Expand Up @@ -155,3 +179,21 @@ func shouldCreateGroup(client *turso.Client, name, location string) (bool, error
// we only create the default group automatically
return name == "default" && len(groups) == 0, nil
}

func validateDumpFile() (*os.File, error) {
file, err := os.Open(fromDumpFlag)
if err != nil {
return nil, fmt.Errorf("could not open file %s: %w", fromDumpFlag, err)
}
fileStat, err := file.Stat()
if err != nil {
return nil, fmt.Errorf("could not stat file %s: %w", fromDumpFlag, err)
}
if fileStat.Size() == 0 {
return nil, fmt.Errorf("dump file is empty")
}
if fileStat.Size() > MaxDumpFileSizeBytes {
return nil, fmt.Errorf("dump file is too large. max allowed size is 2GB")
}
return file, nil
}
9 changes: 9 additions & 0 deletions internal/cmd/from_dump_flag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package cmd

import "github.com/spf13/cobra"

var fromDumpFlag string

func addDbFromDumpFlag(cmd *cobra.Command) {
cmd.Flags().StringVar(&fromDumpFlag, "from-dump", "", "create the database from a local SQLite dump")
}
25 changes: 25 additions & 0 deletions internal/turso/databases.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,31 @@ func (d *DatabasesClient) Seed(name string, dbFile *os.File) error {
return nil
}

func (d *DatabasesClient) UploadDump(dbFile *os.File) (string, error) {
url := d.URL("/dumps")
res, err := d.client.Upload(url, dbFile)
if err != nil {
return "", fmt.Errorf("failed to upload the dump file: %w", err)
}
defer res.Body.Close()

org := d.client.Org
if isNotMemberErr(res.StatusCode, org) {
return "", notMemberErr(org)
}
if res.StatusCode != http.StatusOK {
return "", parseResponseError(res)
}
type response struct {
DumpURL string `json:"dump_url"`
}
data, err := unmarshal[response](res)
if err != nil {
return "", err
}
return data.DumpURL, nil
}

func (d *DatabasesClient) Token(database string, expiration string, readOnly bool) (string, error) {
authorization := ""
if readOnly {
Expand Down

0 comments on commit 4d434d3

Please sign in to comment.