From bac2196d654aadeac48b8bed98cd28eea9bdb9db Mon Sep 17 00:00:00 2001 From: anant Date: Sat, 14 Nov 2026 01:49:44 +0530 Subject: [PATCH] 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 --------- 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 Co-authored-by: Claude Opus 5.5 --- weed/shell/command.go | 13 ++++- weed/shell/command_cluster_check.go | 2 +- weed/shell/command_cluster_ps.go | 2 +- weed/shell/command_cluster_raft_add.go | 2 +- weed/shell/command_cluster_raft_ps.go | 2 +- weed/shell/command_cluster_raft_remove.go | 2 +- weed/shell/command_collection_delete.go | 2 +- weed/shell/command_ec_balance.go | 2 +- weed/shell/command_ec_decode.go | 2 +- weed/shell/command_ec_encode.go | 2 +- weed/shell/command_ec_rebuild.go | 2 +- weed/shell/command_flag_parse_test.go | 51 +++++++++++++++++++ weed/shell/command_fs_configure.go | 2 +- weed/shell/command_fs_meta_load.go | 2 +- weed/shell/command_mount_configure.go | 2 +- weed/shell/command_remote_cache.go | 2 +- weed/shell/command_remote_configure.go | 4 +- weed/shell/command_remote_meta_sync.go | 2 +- weed/shell/command_remote_mount.go | 2 +- weed/shell/command_remote_mount_buckets.go | 2 +- weed/shell/command_remote_uncache.go | 2 +- weed/shell/command_remote_unmount.go | 2 +- weed/shell/command_s3_bucket_delete.go | 2 +- weed/shell/command_s3_bucket_list.go | 2 +- weed/shell/command_s3_bucket_owner.go | 2 +- weed/shell/command_s3_bucket_quota.go | 2 +- weed/shell/command_s3_bucket_quota_check.go | 2 +- weed/shell/command_s3_circuitbreaker.go | 2 +- weed/shell/command_s3_clean_uploads.go | 2 +- weed/shell/command_s3_configure.go | 2 +- weed/shell/command_volume_balance.go | 2 +- weed/shell/command_volume_check_disk.go | 2 +- .../command_volume_configure_replication.go | 2 +- weed/shell/command_volume_copy.go | 2 +- weed/shell/command_volume_delete.go | 2 +- weed/shell/command_volume_delete_empty.go | 2 +- weed/shell/command_volume_fix_replication.go | 2 +- weed/shell/command_volume_fsck.go | 2 +- weed/shell/command_volume_grow.go | 2 +- weed/shell/command_volume_list.go | 2 +- weed/shell/command_volume_mark.go | 2 +- weed/shell/command_volume_mount.go | 2 +- weed/shell/command_volume_move.go | 2 +- weed/shell/command_volume_server_leave.go | 2 +- weed/shell/command_volume_tier_compact.go | 2 +- weed/shell/command_volume_tier_download.go | 2 +- weed/shell/command_volume_tier_move.go | 2 +- weed/shell/command_volume_tier_upload.go | 2 +- weed/shell/command_volume_unmount.go | 2 +- weed/shell/command_volume_vacuum.go | 2 +- weed/shell/shell_liner.go | 2 +- 51 files changed, 113 insertions(+), 51 deletions(-) create mode 100644 weed/shell/command_flag_parse_test.go diff --git a/weed/shell/command.go b/weed/shell/command.go index 14d963b5d5..115a4fb247 100644 --- a/weed/shell/command.go +++ b/weed/shell/command.go @@ -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) +} diff --git a/weed/shell/command_cluster_check.go b/weed/shell/command_cluster_check.go index 18e4232a38..27731af5a8 100644 --- a/weed/shell/command_cluster_check.go +++ b/weed/shell/command_cluster_check.go @@ -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 diff --git a/weed/shell/command_cluster_ps.go b/weed/shell/command_cluster_ps.go index 0e1d3c4dff..58bc98700d 100644 --- a/weed/shell/command_cluster_ps.go +++ b/weed/shell/command_cluster_ps.go @@ -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) { diff --git a/weed/shell/command_cluster_raft_add.go b/weed/shell/command_cluster_raft_add.go index f540831803..9aa5da5e4a 100644 --- a/weed/shell/command_cluster_raft_add.go +++ b/weed/shell/command_cluster_raft_add.go @@ -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 == "" { diff --git a/weed/shell/command_cluster_raft_ps.go b/weed/shell/command_cluster_raft_ps.go index d97272b42d..180c145844 100644 --- a/weed/shell/command_cluster_raft_ps.go +++ b/weed/shell/command_cluster_raft_ps.go @@ -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 { diff --git a/weed/shell/command_cluster_raft_remove.go b/weed/shell/command_cluster_raft_remove.go index bc18fc05ac..24dc87e82e 100644 --- a/weed/shell/command_cluster_raft_remove.go +++ b/weed/shell/command_cluster_raft_remove.go @@ -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 == "" { diff --git a/weed/shell/command_collection_delete.go b/weed/shell/command_collection_delete.go index f80c3d9a52..e16b7588c9 100644 --- a/weed/shell/command_collection_delete.go +++ b/weed/shell/command_collection_delete.go @@ -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) diff --git a/weed/shell/command_ec_balance.go b/weed/shell/command_ec_balance.go index 27cf0eaf96..9b5f2e9f0e 100644 --- a/weed/shell/command_ec_balance.go +++ b/weed/shell/command_ec_balance.go @@ -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) diff --git a/weed/shell/command_ec_decode.go b/weed/shell/command_ec_decode.go index a33a46c4af..f548e1679a 100644 --- a/weed/shell/command_ec_decode.go +++ b/weed/shell/command_ec_decode.go @@ -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") diff --git a/weed/shell/command_ec_encode.go b/weed/shell/command_ec_encode.go index 15a10fe516..5aab211eef 100644 --- a/weed/shell/command_ec_encode.go +++ b/weed/shell/command_ec_encode.go @@ -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 diff --git a/weed/shell/command_ec_rebuild.go b/weed/shell/command_ec_rebuild.go index d21946e849..c812b77e03 100644 --- a/weed/shell/command_ec_rebuild.go +++ b/weed/shell/command_ec_rebuild.go @@ -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") diff --git a/weed/shell/command_flag_parse_test.go b/weed/shell/command_flag_parse_test.go new file mode 100644 index 0000000000..b45e458444 --- /dev/null +++ b/weed/shell/command_flag_parse_test.go @@ -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) + } +} diff --git a/weed/shell/command_fs_configure.go b/weed/shell/command_fs_configure.go index 165e0b8e65..3f987b4787 100644 --- a/weed/shell/command_fs_configure.go +++ b/weed/shell/command_fs_configure.go @@ -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) diff --git a/weed/shell/command_fs_meta_load.go b/weed/shell/command_fs_meta_load.go index a8ff28a383..a85f45a9fb 100644 --- a/weed/shell/command_fs_meta_load.go +++ b/weed/shell/command_fs_meta_load.go @@ -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 diff --git a/weed/shell/command_mount_configure.go b/weed/shell/command_mount_configure.go index 185857b9a6..474c6fb5ad 100644 --- a/weed/shell/command_mount_configure.go +++ b/weed/shell/command_mount_configure.go @@ -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=\" 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)) diff --git a/weed/shell/command_remote_cache.go b/weed/shell/command_remote_cache.go index d2d74802f5..4406f56e07 100644 --- a/weed/shell/command_remote_cache.go +++ b/weed/shell/command_remote_cache.go @@ -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 == "" { diff --git a/weed/shell/command_remote_configure.go b/weed/shell/command_remote_configure.go index 0abceb1553..af29b52556 100644 --- a/weed/shell/command_remote_configure.go +++ b/weed/shell/command_remote_configure.go @@ -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 } } diff --git a/weed/shell/command_remote_meta_sync.go b/weed/shell/command_remote_meta_sync.go index 90c9888eae..0715e82532 100644 --- a/weed/shell/command_remote_meta_sync.go +++ b/weed/shell/command_remote_meta_sync.go @@ -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) diff --git a/weed/shell/command_remote_mount.go b/weed/shell/command_remote_mount.go index 04404cfb75..ba9af1946f 100644 --- a/weed/shell/command_remote_mount.go +++ b/weed/shell/command_remote_mount.go @@ -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 == "" { diff --git a/weed/shell/command_remote_mount_buckets.go b/weed/shell/command_remote_mount_buckets.go index 208d042f23..e226e3d15d 100644 --- a/weed/shell/command_remote_mount_buckets.go +++ b/weed/shell/command_remote_mount_buckets.go @@ -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") diff --git a/weed/shell/command_remote_uncache.go b/weed/shell/command_remote_uncache.go index e3eb43f735..cd4850ab17 100644 --- a/weed/shell/command_remote_uncache.go +++ b/weed/shell/command_remote_uncache.go @@ -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()) diff --git a/weed/shell/command_remote_unmount.go b/weed/shell/command_remote_unmount.go index fd905d00ee..6a36b2cd8c 100644 --- a/weed/shell/command_remote_unmount.go +++ b/weed/shell/command_remote_unmount.go @@ -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()) diff --git a/weed/shell/command_s3_bucket_delete.go b/weed/shell/command_s3_bucket_delete.go index 026e671d1f..05867c36d4 100644 --- a/weed/shell/command_s3_bucket_delete.go +++ b/weed/shell/command_s3_bucket_delete.go @@ -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 == "" { diff --git a/weed/shell/command_s3_bucket_list.go b/weed/shell/command_s3_bucket_list.go index b000652f74..4e2281587c 100644 --- a/weed/shell/command_s3_bucket_list.go +++ b/weed/shell/command_s3_bucket_list.go @@ -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 diff --git a/weed/shell/command_s3_bucket_owner.go b/weed/shell/command_s3_bucket_owner.go index 78484955ff..3797380cd6 100644 --- a/weed/shell/command_s3_bucket_owner.go +++ b/weed/shell/command_s3_bucket_owner.go @@ -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 == "" { diff --git a/weed/shell/command_s3_bucket_quota.go b/weed/shell/command_s3_bucket_quota.go index 5590e8dbc5..7916d7fad2 100644 --- a/weed/shell/command_s3_bucket_quota.go +++ b/weed/shell/command_s3_bucket_quota.go @@ -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 == "" { diff --git a/weed/shell/command_s3_bucket_quota_check.go b/weed/shell/command_s3_bucket_quota_check.go index eda0c42842..fb5b2ac781 100644 --- a/weed/shell/command_s3_bucket_quota_check.go +++ b/weed/shell/command_s3_bucket_quota_check.go @@ -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") diff --git a/weed/shell/command_s3_circuitbreaker.go b/weed/shell/command_s3_circuitbreaker.go index e4d7cbef14..b73955ab96 100644 --- a/weed/shell/command_s3_circuitbreaker.go +++ b/weed/shell/command_s3_circuitbreaker.go @@ -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 } diff --git a/weed/shell/command_s3_clean_uploads.go b/weed/shell/command_s3_clean_uploads.go index ed8c09188d..1be89813fc 100644 --- a/weed/shell/command_s3_clean_uploads.go +++ b/weed/shell/command_s3_clean_uploads.go @@ -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 diff --git a/weed/shell/command_s3_configure.go b/weed/shell/command_s3_configure.go index eb6b8a5cc2..255cd441f5 100644 --- a/weed/shell/command_s3_configure.go +++ b/weed/shell/command_s3_configure.go @@ -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) diff --git a/weed/shell/command_volume_balance.go b/weed/shell/command_volume_balance.go index 60baebcf4f..320c14d56d 100644 --- a/weed/shell/command_volume_balance.go +++ b/weed/shell/command_volume_balance.go @@ -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 diff --git a/weed/shell/command_volume_check_disk.go b/weed/shell/command_volume_check_disk.go index d819290657..01e45dc1ba 100644 --- a/weed/shell/command_volume_check_disk.go +++ b/weed/shell/command_volume_check_disk.go @@ -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) diff --git a/weed/shell/command_volume_configure_replication.go b/weed/shell/command_volume_configure_replication.go index cc85bb5f34..4ab58756f5 100644 --- a/weed/shell/command_volume_configure_replication.go +++ b/weed/shell/command_volume_configure_replication.go @@ -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 { diff --git a/weed/shell/command_volume_copy.go b/weed/shell/command_volume_copy.go index 6fe486751d..b11c90a376 100644 --- a/weed/shell/command_volume_copy.go +++ b/weed/shell/command_volume_copy.go @@ -44,7 +44,7 @@ func (c *commandVolumeCopy) Do(args []string, commandEnv *CommandEnv, writer io. targetNodeStr := volCopyCommand.String("target", "", "the target volume server :") 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 { diff --git a/weed/shell/command_volume_delete.go b/weed/shell/command_volume_delete.go index e6a1180736..b602f2ac00 100644 --- a/weed/shell/command_volume_delete.go +++ b/weed/shell/command_volume_delete.go @@ -43,7 +43,7 @@ func (c *commandVolumeDelete) Do(args []string, commandEnv *CommandEnv, writer i nodeStr := volDeleteCommand.String("node", "", "the volume server :") 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 { diff --git a/weed/shell/command_volume_delete_empty.go b/weed/shell/command_volume_delete_empty.go index b0874467ea..83ba40a51a 100644 --- a/weed/shell/command_volume_delete_empty.go +++ b/weed/shell/command_volume_delete_empty.go @@ -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) diff --git a/weed/shell/command_volume_fix_replication.go b/weed/shell/command_volume_fix_replication.go index f1e2d69804..39e9de9592 100644 --- a/weed/shell/command_volume_fix_replication.go +++ b/weed/shell/command_volume_fix_replication.go @@ -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 { diff --git a/weed/shell/command_volume_fsck.go b/weed/shell/command_volume_fsck.go index 20c15e6cd4..a7d7ec918b 100644 --- a/weed/shell/command_volume_fsck.go +++ b/weed/shell/command_volume_fsck.go @@ -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 diff --git a/weed/shell/command_volume_grow.go b/weed/shell/command_volume_grow.go index b1f11d8694..8a4e6177f7 100644 --- a/weed/shell/command_volume_grow.go +++ b/weed/shell/command_volume_grow.go @@ -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") diff --git a/weed/shell/command_volume_list.go b/weed/shell/command_volume_list.go index 32e8c59355..141bec1ae9 100644 --- a/weed/shell/command_volume_list.go +++ b/weed/shell/command_volume_list.go @@ -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 { diff --git a/weed/shell/command_volume_mark.go b/weed/shell/command_volume_mark.go index 443a7a943a..4ce12f2135 100644 --- a/weed/shell/command_volume_mark.go +++ b/weed/shell/command_volume_mark.go @@ -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) { diff --git a/weed/shell/command_volume_mount.go b/weed/shell/command_volume_mount.go index 121d2bbdc5..208392b2f9 100644 --- a/weed/shell/command_volume_mount.go +++ b/weed/shell/command_volume_mount.go @@ -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 :") if err = volMountCommand.Parse(args); err != nil { - return nil + return err } if err = commandEnv.confirmIsLocked(args); err != nil { diff --git a/weed/shell/command_volume_move.go b/weed/shell/command_volume_move.go index 3486f413a4..163659834d 100644 --- a/weed/shell/command_volume_move.go +++ b/weed/shell/command_volume_move.go @@ -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 { diff --git a/weed/shell/command_volume_server_leave.go b/weed/shell/command_volume_server_leave.go index 673fa5cbf2..9d63441447 100644 --- a/weed/shell/command_volume_server_leave.go +++ b/weed/shell/command_volume_server_leave.go @@ -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) diff --git a/weed/shell/command_volume_tier_compact.go b/weed/shell/command_volume_tier_compact.go index 282b437340..f8097adc0a 100644 --- a/weed/shell/command_volume_tier_compact.go +++ b/weed/shell/command_volume_tier_compact.go @@ -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 { diff --git a/weed/shell/command_volume_tier_download.go b/weed/shell/command_volume_tier_download.go index f028fe43a7..7db79c0c34 100644 --- a/weed/shell/command_volume_tier_download.go +++ b/weed/shell/command_volume_tier_download.go @@ -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 { diff --git a/weed/shell/command_volume_tier_move.go b/weed/shell/command_volume_tier_move.go index 11a8481a9a..5030dfcee3 100644 --- a/weed/shell/command_volume_tier_move.go +++ b/weed/shell/command_volume_tier_move.go @@ -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) diff --git a/weed/shell/command_volume_tier_upload.go b/weed/shell/command_volume_tier_upload.go index 962d55f47c..8c59bbf7f7 100644 --- a/weed/shell/command_volume_tier_upload.go +++ b/weed/shell/command_volume_tier_upload.go @@ -97,7 +97,7 @@ func (c *commandVolumeTierUpload) Do(args []string, commandEnv *CommandEnv, writ disk := tierCommand.String("disk", "", "[hdd|ssd|] 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 { diff --git a/weed/shell/command_volume_unmount.go b/weed/shell/command_volume_unmount.go index ee273ed081..b8ba162090 100644 --- a/weed/shell/command_volume_unmount.go +++ b/weed/shell/command_volume_unmount.go @@ -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 :") if err = volUnmountCommand.Parse(args); err != nil { - return nil + return err } if *nodeStr == "" { return fmt.Errorf("-node option is required") diff --git a/weed/shell/command_volume_vacuum.go b/weed/shell/command_volume_vacuum.go index c1ee55d0c3..cd93dcddd5 100644 --- a/weed/shell/command_volume_vacuum.go +++ b/weed/shell/command_volume_vacuum.go @@ -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) diff --git a/weed/shell/shell_liner.go b/weed/shell/shell_liner.go index 303f2ab2ad..64cc669ca0 100644 --- a/weed/shell/shell_liner.go +++ b/weed/shell/shell_liner.go @@ -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 }