From c38e0959c1e7723345292170bf00c38b5e9720fb Mon Sep 17 00:00:00 2001 From: Avinash Sajjanshetty Date: Wed, 27 Sep 2023 19:37:57 +0530 Subject: [PATCH 1/2] Implement `--from-dump` to create db from sqlite dump --- internal/cmd/db_create.go | 42 ++++++++++++++++++++++++++++++++++ internal/cmd/from_dump_flag.go | 9 ++++++++ internal/turso/databases.go | 25 ++++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 internal/cmd/from_dump_flag.go diff --git a/internal/cmd/db_create.go b/internal/cmd/db_create.go index bdf8159e..c1eda675 100644 --- a/internal/cmd/db_create.go +++ b/internal/cmd/db_create.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "os" "time" "github.com/athoscouto/codename" @@ -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.") @@ -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) } @@ -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 +} diff --git a/internal/cmd/from_dump_flag.go b/internal/cmd/from_dump_flag.go new file mode 100644 index 00000000..29f6a002 --- /dev/null +++ b/internal/cmd/from_dump_flag.go @@ -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") +} diff --git a/internal/turso/databases.go b/internal/turso/databases.go index 1e9c95a2..22ff7a87 100644 --- a/internal/turso/databases.go +++ b/internal/turso/databases.go @@ -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(fmt.Sprintf("/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 { From 7d2f75e2c6a2422098c32974c6e1e41025f6b856 Mon Sep 17 00:00:00 2001 From: Avinash Sajjanshetty Date: Wed, 27 Sep 2023 19:45:18 +0530 Subject: [PATCH 2/2] remove unnecessary fmt.Sprintf --- internal/turso/databases.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/turso/databases.go b/internal/turso/databases.go index 22ff7a87..5ce2842c 100644 --- a/internal/turso/databases.go +++ b/internal/turso/databases.go @@ -150,7 +150,7 @@ func (d *DatabasesClient) Seed(name string, dbFile *os.File) error { } func (d *DatabasesClient) UploadDump(dbFile *os.File) (string, error) { - url := d.URL(fmt.Sprintf("/dumps")) + 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)