Skip to content

Commit

Permalink
CLI: copy (bucket | multi-object)
Browse files Browse the repository at this point in the history
* '--sync' and '--latest': CLI conventions
* usability: error reporting, error templates

Signed-off-by: Alex Aizman <[email protected]>
  • Loading branch information
alex-aizman committed Jan 7, 2024
1 parent db1b43e commit 54d3f9c
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 18 deletions.
2 changes: 1 addition & 1 deletion cmd/cli/cli/bucket_hdlr.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ var (
}
bucketCmdCopy = cli.Command{
Name: commandCopy,
Usage: "copy entire bucket or selected objects (to select multiple, use '--list' or '--template')",
Usage: "copy entire bucket or selected objects (to select multiple, use '--list', '--template', or '--prefix')",
ArgsUsage: bucketObjectSrcArgument + " " + bucketDstArgument,
Flags: bucketCmdsFlags[commandCopy],
Action: copyBucketHandler,
Expand Down
4 changes: 2 additions & 2 deletions cmd/cli/cli/err.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func commandNotFoundError(c *cli.Context, cmd string) *errUsage {
context: c,
message: msg,
helpData: c.App,
helpTemplate: teb.ShortUsageTmpl,
helpTemplate: teb.ExtendedUsageTmpl,
bottomMessage: didYouMeanMessage(c, cmd, similar, closestCommand, distance, trailingShow),
}
}
Expand Down Expand Up @@ -275,7 +275,7 @@ func _errUsage(c *cli.Context, msg string) *errUsage {
context: c,
message: msg,
helpData: c.Command,
helpTemplate: teb.UsageOnlyTmpl, // NOTE: `cli.CommandHelpTemplate` is often way too long
helpTemplate: teb.ShortUsageTmpl,
}
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/cli/cli/etl.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ var (
}
bckCmdETL = cli.Command{
Name: cmdBucket,
Usage: "transform entire bucket or selected objects (to select multiple, use '--list' or '--template')",
Usage: "transform entire bucket or selected objects (to select multiple, use '--list', '--template', or '--prefix')",
ArgsUsage: etlNameArgument + " " + bucketObjectSrcArgument + " " + bucketDstArgument,
Action: etlBucketHandler,
Flags: etlSubFlags[cmdBucket],
Expand Down
7 changes: 5 additions & 2 deletions cmd/cli/cli/parse_uri.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,11 @@ func parseBckObjURI(c *cli.Context, uri string, emptyObjnameOK bool) (bck cmn.Bc
}
bck, objName, err = cmn.ParseBckObjectURI(uri, opts)
if err != nil {
if len(uri) > 1 && uri[:2] == "--" { // FIXME: needed smth like c.LooksLikeFlag
return bck, objName, incorrectUsageMsg(c, "misplaced flag %q", uri)
if errV := errMisplacedFlag(c, uri); errV != nil {
return bck, objName, errV
}
if strings.Contains(err.Error(), cos.OnlyPlus) && strings.Contains(err.Error(), "bucket name") {
return bck, objName, fmt.Errorf("bucket name in %q is invalid: "+cos.OnlyPlus, uri)
}
var msg string
if emptyObjnameOK {
Expand Down
31 changes: 21 additions & 10 deletions cmd/cli/cli/tcbtco.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ import (

// via (I) x-copy-bucket ("full bucket") _or_ (II) x-copy-listrange ("multi-object")
// Notice a certain usable redundancy:
// (I) `ais cp from to --prefix abc"
// is the same as:
// (II) `ais cp from to --template abc"
//
// (I) `ais cp from to --prefix abc" is the same as (II) `ais cp from to --template abc"
//
// Also, note [CONVENTIONS] below.
func copyBucketHandler(c *cli.Context) (err error) {
var (
bckFrom, bckTo cmn.Bck
Expand All @@ -39,23 +40,33 @@ func copyBucketHandler(c *cli.Context) (err error) {
err = missingArgumentsError(c, c.Command.ArgsUsage)
case c.NArg() == 1:
bckFrom, objFrom, err = parseBckObjURI(c, c.Args().Get(0), true /*emptyObjnameOK*/)
if err != nil {
if strings.Contains(err.Error(), cos.OnlyPlus) && strings.Contains(err.Error(), "bucket name") {
err = fmt.Errorf("bucket name in %q is invalid: "+cos.OnlyPlus, c.Args().Get(0))
}
}
default:
bckFrom, bckTo, objFrom, err = parseBcks(c, bucketSrcArgument, bucketDstArgument, 0 /*shift*/, true /*optionalSrcObjname*/)
}
if err != nil {
return err
}

if flagIsSet(c, syncFlag) && bckTo.IsEmpty() {
// [CONVENTIONS]
// 1. '--sync' and '--latest' both require aistore to reach out for remote metadata and, therefore,
// if destination is omitted both options imply namesake in-cluster destination (ie., bckTo = bckFrom)
// 2. in addition, '--sync' also implies '--all' (will traverse non-present)

if bckTo.IsEmpty() {
if !flagIsSet(c, syncFlag) && !flagIsSet(c, latestVerFlag) {
var hint string
if bckFrom.IsRemote() {
hint = fmt.Sprintf(" (or, did you mean 'ais cp %s %s' or 'ais cp %s %s'?)",
c.Args().Get(0), flprn(syncFlag), c.Args().Get(0), flprn(latestVerFlag))
}
return incorrectUsageMsg(c, "missing destination bucket%s", hint)
}
bckTo = bckFrom
}

return copyTransform(c, "" /*etlName*/, objFrom, bckFrom, bckTo, flagIsSet(c, copyAllObjsFlag))
allIncludingRemote := flagIsSet(c, copyAllObjsFlag) || flagIsSet(c, syncFlag)

return copyTransform(c, "" /*etlName*/, objFrom, bckFrom, bckTo, allIncludingRemote)
}

//
Expand Down
7 changes: 7 additions & 0 deletions cmd/cli/cli/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ func arg0Node(c *cli.Context) (node *meta.Snode, sname string, err error) {
return
}

func errMisplacedFlag(c *cli.Context, arg string) (err error) {
if len(arg) > 1 && arg[0] == '-' {
err = incorrectUsageMsg(c, "missing command line argument (hint: flag '%s' misplaced?)", arg)
}
return err
}

func isWebURL(url string) bool { return cos.IsHTTP(url) || cos.IsHTTPS(url) }

func jsonMarshalIndent(v any) ([]byte, error) { return jsoniter.MarshalIndent(v, "", " ") }
Expand Down
7 changes: 5 additions & 2 deletions cmd/cli/teb/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ const (
"{{end}}" +
"TOTAL\t "

ShortUsageTmpl = "{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} - {{.Usage}}\n" +
ExtendedUsageTmpl = "{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} - {{.Usage}}\n" +
"\n\tCOMMANDS:\t" +
"{{range .VisibleCategories}}" +
"{{ range $index, $element := .VisibleCommands}}" +
Expand All @@ -277,8 +277,11 @@ const (
"--{{FlagName $flag }}" +
"{{end}}{{end}}\n"

UsageOnlyTmpl = `{{.HelpName}} - {{.Usage}}
ShortUsageTmpl = `{{.HelpName}} - {{.Usage}}
{{.UsageText}}
USAGE:
{{.HelpName}} {{.ArgsUsage}}
See '--help' and docs/cli for details.`

AuthNClusterTmpl = "CLUSTER ID\tALIAS\tURLs\n" +
Expand Down

0 comments on commit 54d3f9c

Please sign in to comment.