Skip to content

Commit fe5ef70

Browse files
committed
feat(dump,restore): Add --input/--output flags to configure the dump/restore name
1 parent 250a6b1 commit fe5ef70

File tree

12 files changed

+74
-68
lines changed

12 files changed

+74
-68
lines changed

cmd/dump/cmd.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,14 @@ func New() *cobra.Command {
6161
flags.Spinner(cmd)
6262
flags.Opts(cmd)
6363
flags.Progress(cmd)
64+
cmd.Flags().StringP(consts.FlagOutput, "o", "", "Output file path (can also be set using a positional arg)")
65+
must.Must(cmd.RegisterFlagCompletionFunc(consts.FlagOutput, validArgs))
6466

6567
return cmd
6668
}
6769

6870
func validArgs(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
69-
if len(args) != 0 {
71+
if len(args) != 0 || must.Must2(cmd.Flags().GetString(consts.FlagOutput)) != "" {
7072
return nil, cobra.ShellCompDirectiveNoFileComp
7173
}
7274

@@ -120,7 +122,7 @@ func preRun(cmd *cobra.Command, args []string) error {
120122
}
121123

122124
if len(args) > 0 {
123-
action.Filename = args[0]
125+
action.Output = args[0]
124126
}
125127

126128
setupOpts := util.SetupOptions{NoSurvey: config.Global.SkipSurvey}
@@ -133,9 +135,9 @@ func preRun(cmd *cobra.Command, args []string) error {
133135
return fmt.Errorf("%w: %s", util.ErrNoDump, action.Dialect.Name())
134136
}
135137

136-
isDir := action.Filename == "" || strings.HasSuffix(action.Filename, string(os.PathSeparator)) || storage.IsCloudDir(action.Filename)
137-
if !isDir && !storage.IsCloud(action.Filename) {
138-
if stat, err := os.Stat(action.Filename); err == nil {
138+
isDir := action.Output == "" || strings.HasSuffix(action.Output, string(os.PathSeparator)) || storage.IsCloudDir(action.Output)
139+
if !isDir && !storage.IsCloud(action.Output) {
140+
if stat, err := os.Stat(action.Output); err == nil {
139141
isDir = stat.IsDir()
140142
}
141143
}
@@ -147,18 +149,18 @@ func preRun(cmd *cobra.Command, args []string) error {
147149
Ext: database.GetExtension(db, action.Format),
148150
Date: time.Now(),
149151
}.Generate()
150-
if storage.IsCloud(action.Filename) {
151-
u, err := url.Parse(action.Filename)
152+
if storage.IsCloud(action.Output) {
153+
u, err := url.Parse(action.Output)
152154
if err != nil {
153155
return err
154156
}
155157
u.Path = path.Join(u.Path, generated)
156-
action.Filename = u.String()
158+
action.Output = u.String()
157159
} else {
158-
action.Filename = filepath.Join(action.Filename, generated)
160+
action.Output = filepath.Join(action.Output, generated)
159161
}
160162
} else if !cmd.Flags().Lookup(consts.FlagFormat).Changed {
161-
action.Format = database.DetectFormat(db, action.Filename)
163+
action.Format = database.DetectFormat(db, action.Output)
162164
}
163165

164166
if err := util.CreateJob(cmd.Context(), cmd, action.Global, setupOpts); err != nil {

cmd/restore/cmd.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,22 @@ func New() *cobra.Command {
6161
flags.Spinner(cmd)
6262
flags.Opts(cmd)
6363
flags.Progress(cmd)
64+
cmd.Flags().StringP(consts.FlagInput, "i", "", "Input file path (can also be set using a positional arg)")
65+
must.Must(cmd.RegisterFlagCompletionFunc(consts.FlagInput, validArgs))
6466
cmd.Flags().BoolP(consts.FlagForce, "f", false, "Do not prompt before restore")
6567
must.Must(cmd.RegisterFlagCompletionFunc(consts.FlagForce, completion.BoolCompletion))
6668

6769
return cmd
6870
}
6971

7072
func validArgs(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
71-
if len(args) != 0 {
73+
if len(args) != 0 || must.Must2(cmd.Flags().GetString(consts.FlagInput)) != "" {
7274
return nil, cobra.ShellCompDirectiveNoFileComp
7375
}
7476

7577
must.Must(config.K.Set(consts.FlagCreateJob, false))
7678
action.Force = true
77-
action.Filename = "-"
79+
action.Input = "-"
7880

7981
config.Global.SkipSurvey = true
8082
err := preRun(cmd, args)
@@ -135,12 +137,12 @@ func preRun(cmd *cobra.Command, args []string) error {
135137
}
136138

137139
if len(args) > 0 {
138-
action.Filename = args[0]
140+
action.Input = args[0]
139141
}
140142

141143
switch {
142-
case action.Filename == "-", storage.IsCloud(action.Filename):
143-
case action.Filename == "":
144+
case action.Input == "-", storage.IsCloud(action.Input):
145+
case action.Input == "":
144146
if termx.IsTerminal(cmd.InOrStdin()) {
145147
db, ok := action.Dialect.(conftypes.DBRestorer)
146148
if !ok {
@@ -161,7 +163,7 @@ func preRun(cmd *cobra.Command, args []string) error {
161163
ShowPermissions(false).
162164
Height(15).
163165
AllowedTypes(slices.Collect(maps.Values(db.Formats()))).
164-
Value(&action.Filename),
166+
Value(&action.Input),
165167
))
166168

167169
if err := form.Run(); err != nil {
@@ -172,7 +174,7 @@ func preRun(cmd *cobra.Command, args []string) error {
172174
}
173175
fallthrough
174176
default:
175-
if _, err := os.Stat(action.Filename); err != nil {
177+
if _, err := os.Stat(action.Input); err != nil {
176178
return err
177179
}
178180
}
@@ -183,7 +185,7 @@ func preRun(cmd *cobra.Command, args []string) error {
183185
return fmt.Errorf("%w: %s", util.ErrNoRestore, action.Dialect.Name())
184186
}
185187

186-
action.Format = database.DetectFormat(db, action.Filename)
188+
action.Format = database.DetectFormat(db, action.Input)
187189
}
188190

189191
switch {

docs/kubedb_dump.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ kubedb dump [filename | bucket URI] [flags]
4040
--job-pod-labels stringToString Pod labels to add to the job (default [])
4141
-O, --no-owner Skip restoration of object ownership in plain-text format (default true)
4242
--opts string Additional options to pass to the database client command
43+
-o, --output string Output file path (can also be set using a positional arg)
4344
-p, --password string Database password (default discovered)
4445
--port uint16 Database port (default discovered)
4546
--progress Enables the progress bar (default true)

docs/kubedb_restore.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ kubedb restore filename [flags]
3434
-F, --format string Output file format (one of gzip, custom, plain) (default "gzip")
3535
--halt-on-error Halt on error (Postgres only) (default true)
3636
-h, --help help for restore
37+
-i, --input string Input file path (can also be set using a positional arg)
3738
--job-pod-labels stringToString Pod labels to add to the job (default [])
3839
-O, --no-owner Skip restoration of object ownership in plain-text format (default true)
3940
--opts string Additional options to pass to the database client command

internal/actions/dump/dump.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ func (action Dump) Run(ctx context.Context) error {
3939
var f io.WriteCloser
4040
var rename bool
4141
switch {
42-
case action.Filename == "-":
42+
case action.Output == "-":
4343
f = os.Stdout
44-
case storage.IsS3(action.Filename):
44+
case storage.IsS3(action.Output):
4545
pr, pw := io.Pipe()
4646
f = pw
4747
defer func(pw *io.PipeWriter) {
@@ -52,23 +52,23 @@ func (action Dump) Run(ctx context.Context) error {
5252
defer func() {
5353
_ = pr.Close()
5454
}()
55-
return storage.UploadS3(ctx, pr, action.Filename)
55+
return storage.UploadS3(ctx, pr, action.Output)
5656
})
57-
case storage.IsGCS(action.Filename):
57+
case storage.IsGCS(action.Output):
5858
var err error
59-
if f, err = storage.UploadGCS(ctx, action.Filename); err != nil {
59+
if f, err = storage.UploadGCS(ctx, action.Output); err != nil {
6060
return err
6161
}
6262
defer func(f io.WriteCloser) {
6363
_ = f.Close()
6464
}(f)
6565
default:
66-
dir := filepath.Dir(action.Filename)
66+
dir := filepath.Dir(action.Output)
6767
if err := os.MkdirAll(dir, 0o755); err != nil && !os.IsExist(err) {
6868
return err
6969
}
7070

71-
tmp, err := os.CreateTemp(dir, filepath.Base(action.Filename)+"-*")
71+
tmp, err := os.CreateTemp(dir, filepath.Base(action.Output)+"-*")
7272
if err != nil {
7373
return err
7474
}
@@ -84,12 +84,12 @@ func (action Dump) Run(ctx context.Context) error {
8484
actionLog := slog.With(
8585
"namespace", action.Client.Namespace,
8686
"pod", action.DBPod.Name,
87-
"file", action.Filename,
87+
"file", action.Output,
8888
)
8989

9090
actionLog.Info("Exporting database")
9191

92-
if err := github.SetOutput("filename", action.Filename); err != nil {
92+
if err := github.SetOutput("filename", action.Output); err != nil {
9393
return err
9494
}
9595

@@ -175,7 +175,7 @@ func (action Dump) Run(ctx context.Context) error {
175175

176176
if rename {
177177
if f, ok := f.(*os.File); ok {
178-
if err := os.Rename(f.Name(), action.Filename); err != nil {
178+
if err := os.Rename(f.Name(), action.Output); err != nil {
179179
return err
180180
}
181181
}
@@ -228,7 +228,7 @@ func (action Dump) summary(err error, took time.Duration, written int64, plain b
228228
Row("Pod", action.DBPod.Name).
229229
RowIfNotEmpty("Username", action.Username).
230230
RowIfNotEmpty("Database", action.Database).
231-
Row("File", tui.OutPath(action.Filename, r)).
231+
Row("File", tui.OutPath(action.Output, r)).
232232
Row("Took", took.String())
233233
if err != nil {
234234
t.Row("Error", tui.ErrStyle(r).Render(err.Error()))
@@ -248,7 +248,7 @@ func (action Dump) summary(err error, took time.Duration, written int64, plain b
248248

249249
func (action Dump) printSummary(err error, took time.Duration, written int64) {
250250
out := os.Stdout
251-
if action.Filename == "-" {
251+
if action.Output == "-" {
252252
out = os.Stderr
253253
}
254254
_, _ = io.WriteString(out, "\n"+action.summary(err, took, written, false)+"\n")

internal/actions/dump/dump_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,19 @@ func Test_buildCommand(t *testing.T) {
3636
},
3737
{
3838
"postgres-plain",
39-
args{Dump{Dump: conftypes.Dump{Files: conftypes.Files{Format: sqlformat.Plain}, Global: &conftypes.Global{Dialect: postgres.Postgres{}, Host: "1.1.1.1", Database: "d", Username: "u", RemoteGzip: true}}}},
39+
args{Dump{Dump: conftypes.Dump{Format: sqlformat.Plain, Global: &conftypes.Global{Dialect: postgres.Postgres{}, Host: "1.1.1.1", Database: "d", Username: "u", RemoteGzip: true}}}},
4040
command.NewBuilder(command.Raw("{"), "pg_dump", "--host=1.1.1.1", "--username=u", "--dbname=d", "--verbose", command.Raw("|| kill $$; }"), command.Pipe, "gzip", "--force"),
4141
require.NoError,
4242
},
4343
{
4444
"postgres-custom",
45-
args{Dump{Dump: conftypes.Dump{Files: conftypes.Files{Format: sqlformat.Custom}, Global: &conftypes.Global{Dialect: postgres.Postgres{}, Host: "1.1.1.1", Database: "d", Username: "u", RemoteGzip: true}}}},
45+
args{Dump{Dump: conftypes.Dump{Format: sqlformat.Custom, Global: &conftypes.Global{Dialect: postgres.Postgres{}, Host: "1.1.1.1", Database: "d", Username: "u", RemoteGzip: true}}}},
4646
command.NewBuilder(command.Raw("{"), "pg_dump", "--host=1.1.1.1", "--username=u", "--dbname=d", "--format=custom", "--verbose", command.Raw("|| kill $$; }")),
4747
require.NoError,
4848
},
4949
{
5050
"mariadb-gzip",
51-
args{Dump{Dump: conftypes.Dump{Files: conftypes.Files{Format: sqlformat.Gzip}, Global: &conftypes.Global{Dialect: mariadb.MariaDB{}, Host: "1.1.1.1", Database: "d", Username: "u", RemoteGzip: true}}}},
51+
args{Dump{Dump: conftypes.Dump{Format: sqlformat.Gzip, Global: &conftypes.Global{Dialect: mariadb.MariaDB{}, Host: "1.1.1.1", Database: "d", Username: "u", RemoteGzip: true}}}},
5252
command.NewBuilder(command.Raw("{"), command.Raw(`"$(which mariadb-dump || which mysqldump)"`), "--host=1.1.1.1", "--user=u", "d", "--verbose", command.Raw("|| kill $$; }"), command.Pipe, "gzip", "--force"),
5353
require.NoError,
5454
},

internal/actions/restore/restore.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,29 +40,29 @@ func (action Restore) Run(ctx context.Context) error {
4040

4141
var f io.ReadCloser
4242
switch {
43-
case action.Filename == "-":
43+
case action.Input == "-":
4444
f = os.Stdin
45-
case storage.IsS3(action.Filename):
45+
case storage.IsS3(action.Input):
4646
pipe := storage.NewS3DownloadPipe()
4747
f = pipe
4848
defer func(pipe *storage.S3DownloadPipe) {
4949
_ = pipe.Close()
5050
}(pipe)
5151

5252
errGroup.Go(func() error {
53-
return storage.DownloadS3(ctx, pipe, action.Filename)
53+
return storage.DownloadS3(ctx, pipe, action.Input)
5454
})
55-
case storage.IsGCS(action.Filename):
55+
case storage.IsGCS(action.Input):
5656
var err error
57-
if f, err = storage.DownloadGCS(ctx, action.Filename); err != nil {
57+
if f, err = storage.DownloadGCS(ctx, action.Input); err != nil {
5858
return err
5959
}
6060
defer func(f io.ReadCloser) {
6161
_ = f.Close()
6262
}(f)
6363
default:
6464
var err error
65-
if f, err = os.Open(action.Filename); err != nil {
65+
if f, err = os.Open(action.Input); err != nil {
6666
return err
6767
}
6868
defer func(f io.ReadCloser) {
@@ -71,7 +71,7 @@ func (action Restore) Run(ctx context.Context) error {
7171
}
7272

7373
actionLog := slog.With(
74-
"file", action.Filename,
74+
"file", action.Input,
7575
"namespace", action.Client.Namespace,
7676
"pod", action.DBPod.Name,
7777
)
@@ -274,9 +274,9 @@ func (action Restore) Table(r *lipgloss.Renderer) *tui.Table {
274274
func (action Restore) Confirm() (bool, error) {
275275
table := action.Table(nil)
276276
var description string
277-
if action.Filename != "-" && !strings.Contains(action.Filename, action.Namespace) {
277+
if action.Input != "-" && !strings.Contains(action.Input, action.Namespace) {
278278
warnStyle := tui.WarnStyle(nil)
279-
table.Row("File", warnStyle.Render(tui.InPath(action.Filename, nil)))
279+
table.Row("File", warnStyle.Render(tui.InPath(action.Input, nil)))
280280

281281
description = lipgloss.JoinVertical(lipgloss.Left,
282282
table.Render(),
@@ -285,7 +285,7 @@ func (action Restore) Confirm() (bool, error) {
285285
"Please verify you are restoring to the correct namespace.",
286286
)
287287
} else {
288-
description = table.Row("File", tui.InPath(action.Filename, nil)).Render()
288+
description = table.Row("File", tui.InPath(action.Input, nil)).Render()
289289
}
290290

291291
theme := huh.ThemeCharm()
@@ -310,7 +310,7 @@ func (action Restore) summary(err error, took time.Duration, written int64, plai
310310
}
311311

312312
t := action.Table(r).
313-
Row("File", tui.InPath(action.Filename, r)).
313+
Row("File", tui.InPath(action.Input, r)).
314314
Row("Took", took.String())
315315
if err != nil {
316316
t.Row("Error", tui.ErrStyle(r).Render(err.Error()))
@@ -330,7 +330,7 @@ func (action Restore) summary(err error, took time.Duration, written int64, plai
330330

331331
func (action Restore) printSummary(err error, took time.Duration, written int64) {
332332
out := os.Stdout
333-
if action.Filename == "-" {
333+
if action.Input == "-" {
334334
out = os.Stderr
335335
}
336336
_, _ = io.WriteString(out, "\n"+action.summary(err, took, written, false)+"\n")

internal/config/conftypes/dump.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package conftypes
22

3+
import "github.com/clevyr/kubedb/internal/database/sqlformat"
4+
35
type Dump struct {
46
*Global `koanf:"-"`
5-
Files `koanf:",squash"`
6-
IfExists bool `koanf:"if-exists"`
7-
Clean bool `koanf:"clean"`
8-
NoOwner bool `koanf:"no-owner"`
9-
Table []string `koanf:"table"`
10-
ExcludeTable []string `koanf:"exclude-table"`
11-
ExcludeTableData []string `koanf:"exclude-table-data"`
7+
Output string `koanf:"output"`
8+
Format sqlformat.Format `koanf:"format"`
9+
IfExists bool `koanf:"if-exists"`
10+
Clean bool `koanf:"clean"`
11+
NoOwner bool `koanf:"no-owner"`
12+
Table []string `koanf:"table"`
13+
ExcludeTable []string `koanf:"exclude-table"`
14+
ExcludeTableData []string `koanf:"exclude-table-data"`
1215
}

internal/config/conftypes/files.go

Lines changed: 0 additions & 8 deletions
This file was deleted.

internal/config/conftypes/restore.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package conftypes
22

3+
import "github.com/clevyr/kubedb/internal/database/sqlformat"
4+
35
type Restore struct {
46
*Global `koanf:"-"`
5-
Files `koanf:",squash"`
6-
SingleTransaction bool `koanf:"single-transaction"`
7-
Clean bool `koanf:"clean"`
8-
NoOwner bool `koanf:"no-owner"`
9-
Force bool `koanf:"force"`
10-
Spinner string `koanf:"spinner"`
11-
HaltOnError bool `koanf:"halt-on-error"`
7+
Input string `koanf:"input"`
8+
Format sqlformat.Format `koanf:"format"`
9+
SingleTransaction bool `koanf:"single-transaction"`
10+
Clean bool `koanf:"clean"`
11+
NoOwner bool `koanf:"no-owner"`
12+
Force bool `koanf:"force"`
13+
Spinner string `koanf:"spinner"`
14+
HaltOnError bool `koanf:"halt-on-error"`
1215
}

internal/consts/flags.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ const (
4141
FlagListenPort = "listen-port"
4242
FlagAddress = "address"
4343
FlagCommand = "command"
44+
FlagInput = "input"
45+
FlagOutput = "output"
4446
FlagForce = "force"
4547

4648
KeyNamespaceColor = "ui.colors.namespace"

0 commit comments

Comments
 (0)