mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-11-11 14:28:33 +01:00
* weed/shell: support -noLock flag in volumeServer.evacuate (#12501) * shell: return flag parse errors from volumeServer.evacuate A typo such as -noLock=invalid printed the parse error but the command still succeeded, so a piped script treated the evacuation as done. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * shell: volumeServer.evacuate -h prints usage without failing Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.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:
2 files changed
+61
-2
No files matched your search
@@ -2,6 +2,7 @@ package shell
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -43,6 +44,7 @@ func (c *commandVolumeServerEvacuate) Help() string {
|
||||
good destination to meet the replication requirement.
|
||||
E.g. a volume replication 001 in a cluster with 2 volume servers can not be moved.
|
||||
You can use "-skipNonMoveable" to move the rest volumes.
|
||||
You can use "-noLock" to run without cluster-wide lock.
|
||||
|
||||
`
|
||||
}
|
||||
@@ -62,14 +64,20 @@ func (c *commandVolumeServerEvacuate) Do(args []string, commandEnv *CommandEnv,
|
||||
// TODO: remove this alias
|
||||
applyChangeAlias := vsEvacuateCommand.Bool("force", false, "actually apply the changes (alias for -apply)")
|
||||
retryCount := vsEvacuateCommand.Int("retry", 0, "how many times to retry")
|
||||
noLock := vsEvacuateCommand.Bool("noLock", false, "do not lock the admin shell at one's own risk")
|
||||
if err = vsEvacuateCommand.Parse(args); err != nil {
|
||||
return nil
|
||||
if errors.Is(err, flag.ErrHelp) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
handleDeprecatedForceFlag(writer, vsEvacuateCommand, applyChangeAlias, applyChange)
|
||||
infoAboutSimulationMode(writer, *applyChange, "-apply")
|
||||
|
||||
if err = commandEnv.confirmIsLocked(args); err != nil && *applyChange {
|
||||
if *noLock {
|
||||
commandEnv.SetNoLock(true)
|
||||
} else if err = commandEnv.confirmIsLocked(args); err != nil && *applyChange {
|
||||
return
|
||||
}
|
||||
if *volumeServer == "" && *c.volumeRack == "" {
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/cluster"
|
||||
"github.com/seaweedfs/seaweedfs/weed/wdclient/exclusive_locks"
|
||||
)
|
||||
|
||||
func TestEvacuatePrefersLowerVolumeRatio(t *testing.T) {
|
||||
@@ -36,3 +41,49 @@ func TestVolumeServerEvacuate(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestVolumeServerEvacuateNoLockFlag(t *testing.T) {
|
||||
c := &commandVolumeServerEvacuate{}
|
||||
|
||||
// Test without -noLock: when -apply is set and shell is unlocked, Do must fail with lock error
|
||||
unlockedEnv := &CommandEnv{locker: exclusive_locks.NewExclusiveLocker(nil, cluster.AdminShellLockName)}
|
||||
err := c.Do([]string{"-apply"}, unlockedEnv, io.Discard)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error when running with -apply without lock or -noLock")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "need to run \"lock\" first to continue") {
|
||||
t.Fatalf("expected lock error message, got: %v", err)
|
||||
}
|
||||
|
||||
// Test with -noLock: should set commandEnv.noLock to true and bypass the lock requirement
|
||||
noLockEnv := &CommandEnv{locker: exclusive_locks.NewExclusiveLocker(nil, cluster.AdminShellLockName)}
|
||||
errNoLock := c.Do([]string{"-noLock", "-apply"}, noLockEnv, io.Discard)
|
||||
if !noLockEnv.noLock {
|
||||
t.Fatalf("expected commandEnv.noLock to be true when -noLock flag is provided")
|
||||
}
|
||||
if err := noLockEnv.confirmIsLocked([]string{"volumeServer.evacuate"}); err != nil {
|
||||
t.Fatalf("confirmIsLocked should succeed when noLock is true: %v", err)
|
||||
}
|
||||
// The lock error should not be returned because -noLock bypassed it; it should reach node validation
|
||||
if errNoLock == nil || !strings.Contains(errNoLock.Error(), "need to specify volume server") {
|
||||
t.Fatalf("expected node validation error after bypassing lock check, got: %v", errNoLock)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVolumeServerEvacuateReturnsFlagParseError(t *testing.T) {
|
||||
env := &CommandEnv{locker: exclusive_locks.NewExclusiveLocker(nil, cluster.AdminShellLockName)}
|
||||
err := (&commandVolumeServerEvacuate{}).Do([]string{"-noLock=invalid", "-apply"}, env, io.Discard)
|
||||
if err == nil || !strings.Contains(err.Error(), "invalid boolean value") {
|
||||
t.Fatalf("expected a flag parse error, got: %v", err)
|
||||
}
|
||||
if env.noLock {
|
||||
t.Fatalf("a rejected -noLock must not unlock the shell")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVolumeServerEvacuateHelpIsNotAnError(t *testing.T) {
|
||||
env := &CommandEnv{locker: exclusive_locks.NewExclusiveLocker(nil, cluster.AdminShellLockName)}
|
||||
if err := (&commandVolumeServerEvacuate{}).Do([]string{"-h"}, env, io.Discard); err != nil {
|
||||
t.Fatalf("-h should print usage without failing, got: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user