Files
seaweedfs/weed/shell/command_cluster_ps.go
T
bac2196d65 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>
2026-11-13 12:19:44 -08:00

196 lines
5.8 KiB
Go

package shell
import (
"context"
"flag"
"fmt"
"io"
"time"
"github.com/seaweedfs/seaweedfs/weed/cluster"
"github.com/seaweedfs/seaweedfs/weed/filer"
"github.com/seaweedfs/seaweedfs/weed/pb"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
"github.com/seaweedfs/seaweedfs/weed/pb/volume_server_pb"
"github.com/seaweedfs/seaweedfs/weed/util"
"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
)
func init() {
Commands = append(Commands, &commandClusterPs{})
}
type commandClusterPs struct {
}
func (c *commandClusterPs) Name() string {
return "cluster.ps"
}
func (c *commandClusterPs) Help() string {
return `check current cluster process status
cluster.ps
`
}
func (c *commandClusterPs) HasTag(CommandTag) bool {
return false
}
func (c *commandClusterPs) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) {
clusterPsCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
if err = clusterPsCommand.Parse(args); err != nil {
return err
}
listClusterNodes := func(clientType string) ([]*master_pb.ListClusterNodesResponse_ClusterNode, error) {
var nodes []*master_pb.ListClusterNodesResponse_ClusterNode
err := commandEnv.MasterClient.WithClient(context.Background(), false, func(client master_pb.SeaweedClient) error {
resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
ClientType: clientType,
FilerGroup: *commandEnv.option.FilerGroup,
})
if err != nil {
return err
}
nodes = resp.ClusterNodes
return nil
})
return nodes, err
}
filerNodes, err := listClusterNodes(cluster.FilerType)
if err != nil {
return
}
mqBrokerNodes, err := listClusterNodes(cluster.BrokerType)
if err != nil {
return
}
s3Nodes, err := listClusterNodes(cluster.S3Type)
if err != nil {
return
}
vectorNodes, err := listClusterNodes(cluster.VectorType)
if err != nil {
return
}
if len(mqBrokerNodes) > 0 {
fmt.Fprintf(writer, "* message queue brokers %d\n", len(mqBrokerNodes))
for _, node := range mqBrokerNodes {
fmt.Fprintf(writer, " * %s (%v)\n", node.Address, node.Version)
if node.DataCenter != "" {
fmt.Fprintf(writer, " DataCenter: %v\n", node.DataCenter)
}
if node.Rack != "" {
fmt.Fprintf(writer, " Rack: %v\n", node.Rack)
}
}
}
if len(s3Nodes) > 0 {
fmt.Fprintf(writer, "* s3 servers %d\n", len(s3Nodes))
for _, node := range s3Nodes {
fmt.Fprintf(writer, " * %s (%v)\n", node.Address, node.Version)
if node.DataCenter != "" {
fmt.Fprintf(writer, " DataCenter: %v\n", node.DataCenter)
}
if node.Rack != "" {
fmt.Fprintf(writer, " Rack: %v\n", node.Rack)
}
}
}
if len(vectorNodes) > 0 {
fmt.Fprintf(writer, "* vector servers %d\n", len(vectorNodes))
for _, node := range vectorNodes {
fmt.Fprintf(writer, " * %s (%v)\n", node.Address, node.Version)
if node.DataCenter != "" {
fmt.Fprintf(writer, " DataCenter: %v\n", node.DataCenter)
}
if node.Rack != "" {
fmt.Fprintf(writer, " Rack: %v\n", node.Rack)
}
}
}
filerSignatures := make(map[*master_pb.ListClusterNodesResponse_ClusterNode]int32)
fmt.Fprintf(writer, "* filers %d\n", len(filerNodes))
for _, node := range filerNodes {
fmt.Fprintf(writer, " * %s (%v) %v\n", node.Address, node.Version, time.Unix(0, node.CreatedAtNs).UTC())
if node.DataCenter != "" {
fmt.Fprintf(writer, " DataCenter: %v\n", node.DataCenter)
}
if node.Rack != "" {
fmt.Fprintf(writer, " Rack: %v\n", node.Rack)
}
pb.WithFilerClient(false, 0, pb.ServerAddress(node.Address), commandEnv.option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{})
if err == nil {
if resp.FilerGroup != "" {
fmt.Fprintf(writer, " filer group: %s\n", resp.FilerGroup)
}
fmt.Fprintf(writer, " signature: %d\n", resp.Signature)
filerSignatures[node] = resp.Signature
} else {
fmt.Fprintf(writer, " failed to connect: %v\n", err)
}
return err
})
}
for _, node := range filerNodes {
pb.WithFilerClient(false, 0, pb.ServerAddress(node.Address), commandEnv.option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
fmt.Fprintf(writer, "* filer %s metadata sync time\n", node.Address)
selfSignature := filerSignatures[node]
for peer, peerSignature := range filerSignatures {
if selfSignature == peerSignature {
continue
}
if resp, err := client.KvGet(context.Background(), &filer_pb.KvGetRequest{Key: filer.GetPeerMetaOffsetKey(peerSignature)}); err == nil && len(resp.Value) == 8 {
lastTsNs := int64(util.BytesToUint64(resp.Value))
fmt.Fprintf(writer, " %s: %v\n", peer.Address, time.Unix(0, lastTsNs).UTC())
}
}
return nil
})
}
// collect volume servers
var volumeServers []pb.ServerAddress
t, _, err := collectTopologyInfo(commandEnv, 0)
if err != nil {
return err
}
for _, dc := range t.DataCenterInfos {
for _, r := range dc.RackInfos {
for _, dn := range r.DataNodeInfos {
volumeServers = append(volumeServers, pb.NewServerAddressFromDataNode(dn))
}
}
}
fmt.Fprintf(writer, "* volume servers %d\n", len(volumeServers))
for _, dc := range t.DataCenterInfos {
fmt.Fprintf(writer, " * data center: %s\n", dc.Id)
for _, r := range dc.RackInfos {
fmt.Fprintf(writer, " * rack: %s\n", r.Id)
for _, dn := range r.DataNodeInfos {
pb.WithVolumeServerClient(false, pb.NewServerAddressFromDataNode(dn), commandEnv.option.GrpcDialOption, func(client volume_server_pb.VolumeServerClient) error {
resp, err := client.VolumeServerStatus(context.Background(), &volume_server_pb.VolumeServerStatusRequest{})
if err == nil {
fmt.Fprintf(writer, " * %s (%v)\n", dn.Id, resp.Version)
}
return err
})
}
}
}
return nil
}