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>
90 lines
3.4 KiB
Go
90 lines
3.4 KiB
Go
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) {
|
|
volumes := mkByteVolumes(1, 1, 1)
|
|
source := makeByteNode("source", 10, 0, 0, volumes)
|
|
fuller := makeByteNode("fuller", 10, 0, 0, mkByteVolumes(10, 4, 1))
|
|
emptier := makeByteNode("emptier", 10, 0, 0, mkByteVolumes(20, 2, 1))
|
|
|
|
// Put the 40%-full server first: truncating 0.2 - 0.4 to int makes the ratios tie.
|
|
moved, err := moveAwayOneNormalVolume(nil, nil, volumes[0], source, []*Node{fuller, emptier}, false)
|
|
if err != nil || !moved {
|
|
t.Fatalf("evacuate: moved=%v, err=%v", moved, err)
|
|
}
|
|
if _, found := emptier.selectedVolumes[volumes[0].Id]; !found {
|
|
t.Fatal("expected evacuation to choose the server with lower occupancy")
|
|
}
|
|
}
|
|
|
|
func TestVolumeServerEvacuate(t *testing.T) {
|
|
targetServer := ""
|
|
volumeRack := "rack1"
|
|
c := commandVolumeServerEvacuate{
|
|
targetServer: &targetServer,
|
|
volumeRack: &volumeRack,
|
|
}
|
|
c.topologyInfo = parseOutput(topoData)
|
|
|
|
volumeServer := "192.168.1.4:8080"
|
|
if err := c.evacuateNormalVolumes(nil, volumeServer, true, false, os.Stdout); err != nil {
|
|
t.Errorf("evacuate: %v", err)
|
|
}
|
|
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|