shell: return flag parse errors from every command (#12618)

* shell: return flag parse errors from every command

48 commands returned nil when their flags failed to parse, so a mistyped
flag printed the flag error but the command still counted as a success:
a piped weed shell run exited 0, the master maintenance loop logged no
error, and admin script jobs reported the line as done.

Return the parse error as s3.bucket.create (#12544) and the other shell
commands already do. TestCommandsReturnFlagParseErrors runs every
registered command with an unknown flag and fails for the 48 on master.

Signed-off-by: anant <170962632+antcybersec@users.noreply.github.com>

* shell: don't report -h as a failure

With the parse errors returned, -h on those commands now returns
flag.ErrHelp, so the shell printed "error: flag: help requested" and a
piped run exited 1. The flag package has already printed the usage, so
processEachCmd now treats flag.ErrHelp as success for every command.

Signed-off-by: anant <170962632+antcybersec@users.noreply.github.com>

* shell: keep the -h test beside the flag parse test

TestProcessEachCmdTreatsHelpAsSuccess covers the same change as
TestCommandsReturnFlagParseErrors, so it lives in the same new file and
shell_liner_test.go stays as it was.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Signed-off-by: anant <170962632+antcybersec@users.noreply.github.com>
Co-authored-by: anant <170962632+antcybersec@users.noreply.github.com>
Co-authored-by: Chris Lu <chrislusf@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-11-13 12:19:44 -08:00
1 parent c248ab7865
commit bac2196d65
51 files changed
+113 -51

No files matched your search

+12 -1
View File
@@ -1,6 +1,10 @@
package shell
import "io"
import (
"errors"
"flag"
"io"
)
type command interface {
Name() string
@@ -19,3 +23,10 @@ const (
ResourceHeavy CommandTag = "resourceHeavy"
Hidden CommandTag = "hidden"
)
// isHelpError reports whether a command only stopped because it was run
// with -h: the flag package has already printed its usage, so this is not a
// failure.
func isHelpError(err error) bool {
return errors.Is(err, flag.ErrHelp)
}
+1 -1
View File
@@ -41,7 +41,7 @@ func (c *commandClusterCheck) Do(args []string, commandEnv *CommandEnv, writer i
clusterPsCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
if err = clusterPsCommand.Parse(args); err != nil {
return nil
return err
}
// collect topology information
+1 -1
View File
@@ -44,7 +44,7 @@ func (c *commandClusterPs) Do(args []string, commandEnv *CommandEnv, writer io.W
clusterPsCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
if err = clusterPsCommand.Parse(args); err != nil {
return nil
return err
}
listClusterNodes := func(clientType string) ([]*master_pb.ListClusterNodesResponse_ClusterNode, error) {
+1 -1
View File
@@ -39,7 +39,7 @@ func (c *commandRaftServerAdd) Do(args []string, commandEnv *CommandEnv, writer
serverAddress := raftServerAddCommand.String("address", "", "server grpc address")
serverVoter := raftServerAddCommand.Bool("voter", true, "assign it a vote")
if err = raftServerAddCommand.Parse(args); err != nil {
return nil
return err
}
if *serverId == "" || *serverAddress == "" {
+1 -1
View File
@@ -35,7 +35,7 @@ func (c *commandRaftClusterPs) Do(args []string, commandEnv *CommandEnv, writer
raftClusterPsCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
if err = raftClusterPsCommand.Parse(args); err != nil {
return nil
return err
}
err = commandEnv.MasterClient.WithClient(context.Background(), false, func(client master_pb.SeaweedClient) error {
+1 -1
View File
@@ -37,7 +37,7 @@ func (c *commandRaftServerRemove) Do(args []string, commandEnv *CommandEnv, writ
raftServerAddCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
serverId := raftServerAddCommand.String("id", "", "server id")
if err = raftServerAddCommand.Parse(args); err != nil {
return nil
return err
}
if *serverId == "" {
+1 -1
View File
@@ -41,7 +41,7 @@ func (c *commandCollectionDelete) Do(args []string, commandEnv *CommandEnv, writ
applyBalancingAlias := colDeleteCommand.Bool("force", false, "apply the collection (alias for -apply)")
if err = colDeleteCommand.Parse(args); err != nil {
return nil
return err
}
handleDeprecatedForceFlag(writer, colDeleteCommand, applyBalancingAlias, applyBalancing)
+1 -1
View File
@@ -55,7 +55,7 @@ func (c *commandEcBalance) Do(args []string, commandEnv *CommandEnv, writer io.W
applyBalancingAlias := balanceCommand.Bool("force", false, "apply the balancing plan (alias for -apply)")
if err = balanceCommand.Parse(args); err != nil {
return nil
return err
}
handleDeprecatedForceFlag(writer, balanceCommand, applyBalancingAlias, applyBalancing)
+1 -1
View File
@@ -60,7 +60,7 @@ func (c *commandEcDecode) Do(args []string, commandEnv *CommandEnv, writer io.Wr
checkMinFreeSpace := decodeCommand.Bool("checkMinFreeSpace", true, "check min free space when selecting the decode target")
batchSize := decodeCommand.Int("batchSize", DefaultEcBatchSize, "decode up to this many volumes per topology refresh (0 = one snapshot for all volumes)")
if err = decodeCommand.Parse(args); err != nil {
return nil
return err
}
if *batchSize < 0 {
return fmt.Errorf("-batchSize must be >= 0")
+1 -1
View File
@@ -114,7 +114,7 @@ func (c *commandEcEncode) Do(args []string, commandEnv *CommandEnv, writer io.Wr
verbose := encodeCommand.Bool("verbose", false, "show detailed reasons why volumes are not selected for encoding")
if err = encodeCommand.Parse(args); err != nil {
return nil
return err
}
if err = commandEnv.confirmIsLocked(args); err != nil {
return
+1 -1
View File
@@ -88,7 +88,7 @@ func (c *commandEcRebuild) Do(args []string, commandEnv *CommandEnv, writer io.W
// TODO: remove this alias
applyChangesAlias := fixCommand.Bool("force", false, "apply the changes (alias for -apply)")
if err = fixCommand.Parse(args); err != nil {
return nil
return err
}
handleDeprecatedForceFlag(writer, fixCommand, applyChangesAlias, applyChanges)
infoAboutSimulationMode(writer, *applyChanges, "-apply")
+51
View File
@@ -0,0 +1,51 @@
package shell
import (
"bytes"
"flag"
"strings"
"testing"
)
// A command that parses flags must report a bad flag as an error. Returning
// nil makes a typo in a weed shell script or a master maintenance script look
// like a successful run that did nothing.
func TestCommandsReturnFlagParseErrors(t *testing.T) {
for _, c := range Commands {
t.Run(c.Name(), func(t *testing.T) {
// fs.meta.load parses every argument but the last as flags.
err, needsEnv := doWithoutCommandEnv(c, []string{"-notAFlagOfAnyCommand", "arg"})
if needsEnv {
t.Skip("uses the command environment before parsing any flags")
}
if err == nil {
t.Fatal("an unknown flag was reported as success")
}
if !strings.Contains(err.Error(), "flag provided but not defined") {
t.Skipf("rejects its arguments without parsing flags: %v", err)
}
})
}
}
// doWithoutCommandEnv runs c with a nil CommandEnv. A command that panics here
// used the environment before parsing flags, or takes no flags at all.
func doWithoutCommandEnv(c command, args []string) (err error, needsEnv bool) {
defer func() {
if recover() != nil {
needsEnv = true
}
}()
var out bytes.Buffer
return c.Do(args, nil, &out), false
}
func TestProcessEachCmdTreatsHelpAsSuccess(t *testing.T) {
Commands = append(Commands, &fakeCommand{name: "test.help.command", err: flag.ErrHelp})
t.Cleanup(func() { Commands = Commands[:len(Commands)-1] })
exit, err := processEachCmd("test.help.command -h", nil)
if exit || err != nil {
t.Errorf("-h must not count as a failure, got exit=%v err=%v", exit, err)
}
}
+1 -1
View File
@@ -80,7 +80,7 @@ func (c *commandFsConfigure) Do(args []string, commandEnv *CommandEnv, writer io
isDelete := fsConfigureCommand.Bool("delete", false, "delete the configuration by locationPrefix")
apply := fsConfigureCommand.Bool("apply", false, "update and apply filer configuration")
if err = fsConfigureCommand.Parse(args); err != nil {
return nil
return err
}
fc, err := filer.ReadFilerConf(commandEnv.FilerAddress(), commandEnv.option.GrpcDialOption, commandEnv.MasterClient)
+1 -1
View File
@@ -57,7 +57,7 @@ func (c *commandFsMetaLoad) Do(args []string, commandEnv *CommandEnv, writer io.
concurrency := metaLoadCommand.Int("concurrency", 1, "number of parallel meta load to filer")
verbose := metaLoadCommand.Bool("v", true, "verbose mode")
if err = metaLoadCommand.Parse(args[0 : len(args)-1]); err != nil {
return nil
return err
}
var dst io.Reader
+1 -1
View File
@@ -45,7 +45,7 @@ func (c *commandMountConfigure) Do(args []string, commandEnv *CommandEnv, writer
mountDir := mountConfigureCommand.String("dir", "", "the mount directory same as how \"weed mount -dir=<mount_directory>\" was started")
mountQuota := mountConfigureCommand.Int("quotaMB", 0, "the quota in MB")
if err = mountConfigureCommand.Parse(args); err != nil {
return nil
return err
}
mountDirHash := util.HashToInt32([]byte(*mountDir))
+1 -1
View File
@@ -74,7 +74,7 @@ func (c *commandRemoteCache) Do(args []string, commandEnv *CommandEnv, writer io
fileFiler := newFileFilter(remoteCacheCommand)
if err = remoteCacheCommand.Parse(args); err != nil {
return nil
return err
}
if *dir == "" {
+2 -2
View File
@@ -69,7 +69,7 @@ func (c *commandRemoteConfigure) Do(args []string, commandEnv *CommandEnv, write
fs, isDelete, uploadConcurrency, downloadConcurrency := c.configureFlagSet(conf, false)
if err = fs.Parse(args); err != nil {
return nil
return err
}
if conf.Name == "" {
@@ -110,7 +110,7 @@ func (c *commandRemoteConfigure) Do(args []string, commandEnv *CommandEnv, write
}
fs, isDelete, uploadConcurrency, downloadConcurrency = c.configureFlagSet(conf, true)
if err = fs.Parse(args); err != nil {
return nil
return err
}
}
+1 -1
View File
@@ -67,7 +67,7 @@ func (c *commandRemoteMetaSync) Do(args []string, commandEnv *CommandEnv, writer
statFiles := remoteMetaSyncCommand.Bool("statFiles", false, "stat each new or changed file when the listing does not report all metadata (S3 listings lack Content-Encoding); costs one extra remote request per file")
if err = remoteMetaSyncCommand.Parse(args); err != nil {
return nil
return err
}
mappings, localMountedDir, remoteStorageMountedLocation, remoteStorageConf, detectErr := detectMountInfo(commandEnv, writer, *dir)
+1 -1
View File
@@ -76,7 +76,7 @@ func (c *commandRemoteMount) Do(args []string, commandEnv *CommandEnv, writer io
cacheWait := remoteMountCommand.Duration("cacheWait", -1, "how long a read of an uncached object waits for the local cache, ex. 0 or 500ms (default: by object size)")
if err = remoteMountCommand.Parse(args); err != nil {
return nil
return err
}
if *dir == "" {
+1 -1
View File
@@ -52,7 +52,7 @@ func (c *commandRemoteMountBuckets) Do(args []string, commandEnv *CommandEnv, wr
trimBucketSuffix := remoteMountBucketsCommand.Bool("trimBucketSuffix", true, "remote suffix auto generated by 'weed filer.remote.sync'")
apply := remoteMountBucketsCommand.Bool("apply", false, "apply the mount for listed buckets")
if err = remoteMountBucketsCommand.Parse(args); err != nil {
return nil
return err
}
infoAboutSimulationMode(writer, *apply, "-apply")
+1 -1
View File
@@ -55,7 +55,7 @@ func (c *commandRemoteUncache) Do(args []string, commandEnv *CommandEnv, writer
fileFiler := newFileFilter(remoteUncacheCommand)
if err = remoteUncacheCommand.Parse(args); err != nil {
return nil
return err
}
mappings, listErr := filer.ReadMountMappings(commandEnv.option.GrpcDialOption, commandEnv.FilerAddress())
+1 -1
View File
@@ -49,7 +49,7 @@ func (c *commandRemoteUnmount) Do(args []string, commandEnv *CommandEnv, writer
dir := remoteMountCommand.String("dir", "", "a directory in filer")
if err = remoteMountCommand.Parse(args); err != nil {
return nil
return err
}
mappings, listErr := filer.ReadMountMappings(commandEnv.option.GrpcDialOption, commandEnv.FilerAddress())
+1 -1
View File
@@ -37,7 +37,7 @@ func (c *commandS3BucketDelete) Do(args []string, commandEnv *CommandEnv, writer
bucketCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
bucketName := bucketCommand.String("name", "", "bucket name")
if err = bucketCommand.Parse(args); err != nil {
return nil
return err
}
if *bucketName == "" {
+1 -1
View File
@@ -37,7 +37,7 @@ func (c *commandS3BucketList) Do(args []string, commandEnv *CommandEnv, writer i
bucketCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
if err = bucketCommand.Parse(args); err != nil {
return nil
return err
}
// collect collection information
+1 -1
View File
@@ -56,7 +56,7 @@ func (c *commandS3BucketOwner) Do(args []string, commandEnv *CommandEnv, writer
bucketOwner := bucketCommand.String("owner", "", "new bucket owner identity name")
deleteOwner := bucketCommand.Bool("delete", false, "remove the bucket owner (make admin-only)")
if err = bucketCommand.Parse(args); err != nil {
return nil
return err
}
if *bucketName == "" {
+1 -1
View File
@@ -43,7 +43,7 @@ func (c *commandS3BucketQuota) Do(args []string, commandEnv *CommandEnv, writer
operationName := bucketCommand.String("op", "set", "operation name [set|get|remove|enable|disable]")
sizeMB := bucketCommand.Int64("sizeMB", 0, "bucket quota size in MiB")
if err = bucketCommand.Parse(args); err != nil {
return nil
return err
}
if *bucketName == "" {
+1 -1
View File
@@ -40,7 +40,7 @@ func (c *commandS3BucketQuotaEnforce) Do(args []string, commandEnv *CommandEnv,
bucketCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
applyQuotaLimit := bucketCommand.Bool("apply", false, "actually change the buckets readonly attribute")
if err = bucketCommand.Parse(args); err != nil {
return nil
return err
}
infoAboutSimulationMode(writer, *applyQuotaLimit, "-apply")
+1 -1
View File
@@ -75,7 +75,7 @@ func (c *commandS3CircuitBreaker) Do(args []string, commandEnv *CommandEnv, writ
apply := s3CircuitBreakerCommand.Bool("apply", false, "update and apply current configuration")
if err = s3CircuitBreakerCommand.Parse(args); err != nil {
return nil
return err
}
+1 -1
View File
@@ -48,7 +48,7 @@ func (c *commandS3CleanUploads) Do(args []string, commandEnv *CommandEnv, writer
bucketCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
uploadedTimeAgo := bucketCommand.Duration("timeAgo", 24*time.Hour, "created time before now. \"1.5h\" or \"2h45m\". Valid time units are \"m\", \"h\"")
if err = bucketCommand.Parse(args); err != nil {
return nil
return err
}
var filerBucketsPath string
+1 -1
View File
@@ -63,7 +63,7 @@ func (c *commandS3Configure) Do(args []string, commandEnv *CommandEnv, writer io
isDelete := s3ConfigureCommand.Bool("delete", false, "delete users, actions, access keys or policies")
apply := s3ConfigureCommand.Bool("apply", false, "update and apply s3 configuration")
if err = s3ConfigureCommand.Parse(args); err != nil {
return nil
return err
}
// Case 1: List configuration (no user specified)
+1 -1
View File
@@ -158,7 +158,7 @@ func (c *commandVolumeBalance) Do(args []string, commandEnv *CommandEnv, writer
return fmt.Errorf("use \"ALL\", \"ACTIVE\" or \"FULL\"")
})
if err = balanceCommand.Parse(args); err != nil {
return nil
return err
}
handleDeprecatedForceFlag(writer, balanceCommand, applyBalancingAlias, applyBalancing)
c.applyBalancing = *applyBalancing
+1 -1
View File
@@ -112,7 +112,7 @@ func (c *commandVolumeCheckDisk) Do(args []string, commandEnv *CommandEnv, write
nonRepairThreshold := fsckCommand.Float64("nonRepairThreshold", 0.3, "repair when missing keys is not more than this limit")
resurrectMissingNeedles := fsckCommand.Bool("resurrectMissingNeedles", false, "copy needles absent on one replica back from the other, only into never-vacuumed replicas (compaction revision 0)")
if err = fsckCommand.Parse(args); err != nil {
return nil
return err
}
handleDeprecatedForceFlag(writer, fsckCommand, applyChangesAlias, applyChanges)
@@ -52,7 +52,7 @@ func (c *commandVolumeConfigureReplication) Do(args []string, commandEnv *Comman
collectionPattern := configureReplicationCommand.String("collectionPattern", "", "comma-separated collection names, with '*' and '?' wildcards; empty matches all")
maxParallelization := configureReplicationCommand.Int("maxParallelization", DefaultMaxParallelization, "run up to X tasks in parallel, whenever possible")
if err = configureReplicationCommand.Parse(args); err != nil {
return nil
return err
}
if err = commandEnv.confirmIsLocked(args); err != nil {
+1 -1
View File
@@ -44,7 +44,7 @@ func (c *commandVolumeCopy) Do(args []string, commandEnv *CommandEnv, writer io.
targetNodeStr := volCopyCommand.String("target", "", "the target volume server <host>:<port>")
noLock := volCopyCommand.Bool("noLock", false, "do not lock the admin shell at one's own risk")
if err = volCopyCommand.Parse(args); err != nil {
return nil
return err
}
if *noLock {
+1 -1
View File
@@ -43,7 +43,7 @@ func (c *commandVolumeDelete) Do(args []string, commandEnv *CommandEnv, writer i
nodeStr := volDeleteCommand.String("node", "", "the volume server <host>:<port>")
timeout := volDeleteCommand.Duration("timeout", 0, "wall-clock cap on the deletion; 0 = no timeout")
if err = volDeleteCommand.Parse(args); err != nil {
return nil
return err
}
if err = commandEnv.confirmIsLocked(args); err != nil {
+1 -1
View File
@@ -52,7 +52,7 @@ func (c *commandVolumeDeleteEmpty) Do(args []string, commandEnv *CommandEnv, wri
// TODO: remove this alias
applyBalancingAlias := volDeleteCommand.Bool("force", false, "apply to delete empty volumes (alias for -apply)")
if err = volDeleteCommand.Parse(args); err != nil {
return nil
return err
}
handleDeprecatedForceFlag(writer, volDeleteCommand, applyBalancingAlias, applyBalancing)
+1 -1
View File
@@ -84,7 +84,7 @@ func (c *commandVolumeFixReplication) Do(args []string, commandEnv *CommandEnv,
volumesPerStep := volFixReplicationCommand.Int("volumesPerStep", 0, "how many volumes to fix in one cycle")
if err = volFixReplicationCommand.Parse(args); err != nil {
return nil
return err
}
if c.collectionMatcher, err = wildcard.CompileCollectionMatcher(*collectionPattern); err != nil {
+1 -1
View File
@@ -141,7 +141,7 @@ func (c *commandVolumeFsck) Do(args []string, commandEnv *CommandEnv, writer io.
c.verifyNeedle = fsckCommand.Bool("verifyNeedles", false, "check needles status from volume server")
if err = fsckCommand.Parse(args); err != nil {
return nil
return err
}
// The command struct is a singleton registered in init(), so any state
+1 -1
View File
@@ -46,7 +46,7 @@ func (c *commandGrow) Do(args []string, commandEnv *CommandEnv, writer io.Writer
diskType := volumeVacuumCommand.String("diskType", "", "grow volumes only from the specified disk type")
if err = volumeVacuumCommand.Parse(args); err != nil {
return nil
return err
}
if *collection == "" {
return fmt.Errorf("collection option is required")
+1 -1
View File
@@ -63,7 +63,7 @@ func (c *commandVolumeList) Do(args []string, commandEnv *CommandEnv, writer io.
c.dataNode = volumeListCommand.String("dataNode", "", "show volumes only from the specified data node")
if err = volumeListCommand.Parse(args); err != nil {
return nil
return err
}
if c.collectionMatcher, err = wildcard.CompileCollectionMatcher(*collectionPattern); err != nil {
+1 -1
View File
@@ -53,7 +53,7 @@ func (c *commandVolumeMark) Do(args []string, commandEnv *CommandEnv, writer io.
readonly := volMarkCommand.Bool("readonly", false, "volume mark readonly")
readonlyCanDelete := volMarkCommand.Bool("readonlyCanDelete", false, "volume mark readonly but still accepting deletes")
if err = volMarkCommand.Parse(args); err != nil {
return nil
return err
}
collectionSet, nodeSet, volumeIdSet := false, false, false
volMarkCommand.Visit(func(f *flag.Flag) {
+1 -1
View File
@@ -43,7 +43,7 @@ func (c *commandVolumeMount) Do(args []string, commandEnv *CommandEnv, writer io
volumeIdInt := volMountCommand.Int("volumeId", 0, "the volume id")
nodeStr := volMountCommand.String("node", "", "the volume server <host>:<port>")
if err = volMountCommand.Parse(args); err != nil {
return nil
return err
}
if err = commandEnv.confirmIsLocked(args); err != nil {
+1 -1
View File
@@ -64,7 +64,7 @@ func (c *commandVolumeMove) Do(args []string, commandEnv *CommandEnv, writer io.
noLock := volMoveCommand.Bool("noLock", false, "do not lock the admin shell at one's own risk")
if err = volMoveCommand.Parse(args); err != nil {
return nil
return err
}
if *noLock {
+1 -1
View File
@@ -48,7 +48,7 @@ func (c *commandVolumeServerLeave) Do(args []string, commandEnv *CommandEnv, wri
// TODO: remove this alias
applyChangesAlias := vsLeaveCommand.Bool("force", false, "apply the changes (alias for -apply)")
if err = vsLeaveCommand.Parse(args); err != nil {
return nil
return err
}
handleDeprecatedForceFlag(writer, vsLeaveCommand, applyChangesAlias, applyChanges)
+1 -1
View File
@@ -68,7 +68,7 @@ func (c *commandVolumeTierCompact) Do(args []string, commandEnv *CommandEnv, wri
garbageThreshold := tierCommand.Float64("garbageThreshold", 0.3, "compact when garbage ratio exceeds this value")
concurrency := tierCommand.Int("concurrency", 0, "multipart transfer concurrency (0 = backend default)")
if err = tierCommand.Parse(args); err != nil {
return nil
return err
}
if err = validateTierConcurrency(*concurrency); err != nil {
+1 -1
View File
@@ -66,7 +66,7 @@ func (c *commandVolumeTierDownload) Do(args []string, commandEnv *CommandEnv, wr
collection := tierCommand.String("collection", "", "comma-separated collection names, wildcards, or regex patterns; empty matches the collection with no name")
concurrency := tierCommand.Int("concurrency", 0, "multipart download concurrency (0 = backend default)")
if err = tierCommand.Parse(args); err != nil {
return nil
return err
}
if err = validateTierConcurrency(*concurrency); err != nil {
+1 -1
View File
@@ -95,7 +95,7 @@ func (c *commandVolumeTierMove) Do(args []string, commandEnv *CommandEnv, writer
replicationString := tierCommand.String("toReplication", "", "the new target replication setting")
if err = tierCommand.Parse(args); err != nil {
return nil
return err
}
handleDeprecatedForceFlag(writer, tierCommand, applyChangeAlias, applyChange)
+1 -1
View File
@@ -97,7 +97,7 @@ func (c *commandVolumeTierUpload) Do(args []string, commandEnv *CommandEnv, writ
disk := tierCommand.String("disk", "", "[hdd|ssd|<tag>] hard drive or solid state drive or any tag")
concurrency := tierCommand.Int("concurrency", 0, "multipart upload concurrency (0 = backend default)")
if err = tierCommand.Parse(args); err != nil {
return nil
return err
}
if err = validateTierConcurrency(*concurrency); err != nil {
+1 -1
View File
@@ -44,7 +44,7 @@ func (c *commandVolumeUnmount) Do(args []string, commandEnv *CommandEnv, writer
volumeIdInt := volUnmountCommand.Int("volumeId", 0, "the volume id")
nodeStr := volUnmountCommand.String("node", "", "the volume server <host>:<port>")
if err = volUnmountCommand.Parse(args); err != nil {
return nil
return err
}
if *nodeStr == "" {
return fmt.Errorf("-node option is required")
+1 -1
View File
@@ -52,7 +52,7 @@ func (c *commandVacuum) Do(args []string, commandEnv *CommandEnv, writer io.Writ
collection := volumeVacuumCommand.String("collection", "", "comma-separated collection names, wildcards, or regex patterns; empty or \"*\" matches all, \""+CollectionDefault+"\" the collection with no name")
volumeIds := volumeVacuumCommand.String("volumeId", "", "comma-separated list of volume IDs")
if err = volumeVacuumCommand.Parse(args); err != nil {
return nil
return err
}
collectionMatcher, err := wildcard.CompileCollectionMatcher(*collection)
+1 -1
View File
@@ -137,7 +137,7 @@ func processEachCmd(cmd string, commandEnv *CommandEnv) (exit bool, cmdErr error
// every later command in the session unlocked, so a real
// mutation right after a simulation would skip its lock.
commandEnv.SetNoLock(false)
if err := c.Do(args, commandEnv, os.Stdout); err != nil {
if err := c.Do(args, commandEnv, os.Stdout); err != nil && !isHelpError(err) {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
cmdErr = err
}