mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-26 15:00:48 +01:00
admin: respect filerGroup for cluster discovery (#10170)
* Respect filerGroup in admin discovery Admin discovery previously queried master cluster nodes with an empty filer group, so filers registered under a non-default group could not appear in the admin UI. Add an admin filerGroup flag and carry it through cluster-node discovery requests while preserving the empty default behavior. Constraint: SeaweedFS master ListClusterNodes filters by exact filer_group. Rejected: Discover all groups implicitly | no existing admin or shell behavior exposes cross-group discovery. Confidence: high Scope-risk: narrow Directive: Keep admin cluster discovery scoped to the configured filerGroup unless an explicit all-groups API is added. Tested: docker run --rm -v "$PWD:/src" -w /src golang:1.25 go test ./weed/admin/dash -run TestListClusterNodesRequest -count=1 Tested: docker run --rm -v "$PWD:/src" -w /src golang:1.25 go test ./weed/command -run '^$' -count=1 Not-tested: full repository test suite * mini: pass filer group to admin cluster discovery miniAdminOptions.filerGroup was never initialized, so startAdminServer dereferenced a nil *string. Share the filer.filerGroup flag pointer so the co-located admin queries the same group the filer registers under. --------- Co-authored-by: Chris Lu <chris.lu@gmail.com>
This commit is contained in:
1 parent
ebeab4b6ec
commit
9d75048594
8 files changed
+59
-35
No files matched your search
@@ -311,9 +311,7 @@ func (s *AdminServer) getFilerNodesStatus() []FilerNode {
|
||||
|
||||
// Get filer nodes from master using ListClusterNodes
|
||||
err := s.WithMasterClient(func(client master_pb.SeaweedClient) error {
|
||||
resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
|
||||
ClientType: cluster.FilerType,
|
||||
})
|
||||
resp, err := client.ListClusterNodes(context.Background(), s.listClusterNodesRequest(cluster.FilerType))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -352,9 +350,7 @@ func (s *AdminServer) getMessageBrokerNodesStatus() []MessageBrokerNode {
|
||||
|
||||
// Get message broker nodes from master using ListClusterNodes
|
||||
err := s.WithMasterClient(func(client master_pb.SeaweedClient) error {
|
||||
resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
|
||||
ClientType: cluster.BrokerType,
|
||||
})
|
||||
resp, err := client.ListClusterNodes(context.Background(), s.listClusterNodesRequest(cluster.BrokerType))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -393,9 +389,7 @@ func (s *AdminServer) getS3NodesStatus() []S3Node {
|
||||
|
||||
// Get S3 nodes from master using ListClusterNodes
|
||||
err := s.WithMasterClient(func(client master_pb.SeaweedClient) error {
|
||||
resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
|
||||
ClientType: cluster.S3Type,
|
||||
})
|
||||
resp, err := client.ListClusterNodes(context.Background(), s.listClusterNodesRequest(cluster.S3Type))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ type AdminServer struct {
|
||||
masterClient *wdclient.MasterClient
|
||||
templateFS http.FileSystem
|
||||
dataDir string
|
||||
filerGroup string
|
||||
grpcDialOption grpc.DialOption
|
||||
cacheExpiration time.Duration
|
||||
lastCacheUpdate time.Time
|
||||
@@ -134,13 +135,13 @@ type AdminServer struct {
|
||||
|
||||
// Type definitions moved to types.go
|
||||
|
||||
func NewAdminServer(masters string, templateFS http.FileSystem, dataDir string, icebergPort int) *AdminServer {
|
||||
func NewAdminServer(masters string, filerGroup string, templateFS http.FileSystem, dataDir string, icebergPort int) *AdminServer {
|
||||
grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.admin")
|
||||
|
||||
// Create master client with multiple master support
|
||||
masterClient := wdclient.NewMasterClient(
|
||||
grpcDialOption,
|
||||
"", // filerGroup - not needed for admin
|
||||
filerGroup,
|
||||
"admin", // clientType
|
||||
"", // clientHost - not needed for admin
|
||||
"", // dataCenter - not needed for admin
|
||||
@@ -162,6 +163,7 @@ func NewAdminServer(masters string, templateFS http.FileSystem, dataDir string,
|
||||
masterClient: masterClient,
|
||||
templateFS: templateFS,
|
||||
dataDir: dataDir,
|
||||
filerGroup: filerGroup,
|
||||
grpcDialOption: grpcDialOption,
|
||||
cacheExpiration: defaultCacheTimeout,
|
||||
filerCacheExpiration: defaultFilerCacheTimeout,
|
||||
@@ -293,6 +295,13 @@ func NewAdminServer(masters string, templateFS http.FileSystem, dataDir string,
|
||||
return server
|
||||
}
|
||||
|
||||
func (s *AdminServer) listClusterNodesRequest(clientType string) *master_pb.ListClusterNodesRequest {
|
||||
return &master_pb.ListClusterNodesRequest{
|
||||
ClientType: clientType,
|
||||
FilerGroup: s.filerGroup,
|
||||
}
|
||||
}
|
||||
|
||||
// vacuumToggler abstracts the master's vacuum enable/disable for testing.
|
||||
type vacuumToggler interface {
|
||||
disableVacuum() error
|
||||
@@ -1112,9 +1121,7 @@ func (s *AdminServer) GetClusterFilers() (*ClusterFilersData, error) {
|
||||
|
||||
// Get filer information from master using ListClusterNodes
|
||||
err := s.WithMasterClient(func(client master_pb.SeaweedClient) error {
|
||||
resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
|
||||
ClientType: cluster.FilerType,
|
||||
})
|
||||
resp, err := client.ListClusterNodes(context.Background(), s.listClusterNodesRequest(cluster.FilerType))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1159,9 +1166,7 @@ func (s *AdminServer) GetClusterBrokers() (*ClusterBrokersData, error) {
|
||||
|
||||
// Get broker information from master using ListClusterNodes
|
||||
err := s.WithMasterClient(func(client master_pb.SeaweedClient) error {
|
||||
resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
|
||||
ClientType: cluster.BrokerType,
|
||||
})
|
||||
resp, err := client.ListClusterNodes(context.Background(), s.listClusterNodesRequest(cluster.BrokerType))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1206,9 +1211,7 @@ func (s *AdminServer) GetClusterS3Servers() (*ClusterS3ServersData, error) {
|
||||
|
||||
// Get S3 server information from master using ListClusterNodes
|
||||
err := s.WithMasterClient(func(client master_pb.SeaweedClient) error {
|
||||
resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
|
||||
ClientType: cluster.S3Type,
|
||||
})
|
||||
resp, err := client.ListClusterNodes(context.Background(), s.listClusterNodesRequest(cluster.S3Type))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1642,9 +1645,7 @@ func (s *AdminServer) UpdateTopicRetention(namespace, name string, enabled bool,
|
||||
// Get broker information from master
|
||||
var brokerAddress string
|
||||
err := s.WithMasterClient(func(client master_pb.SeaweedClient) error {
|
||||
resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
|
||||
ClientType: cluster.BrokerType,
|
||||
})
|
||||
resp, err := client.ListClusterNodes(context.Background(), s.listClusterNodesRequest(cluster.BrokerType))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package dash
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/cluster"
|
||||
)
|
||||
|
||||
func TestListClusterNodesRequestIncludesFilerGroup(t *testing.T) {
|
||||
server := &AdminServer{filerGroup: "tenant-a"}
|
||||
|
||||
req := server.listClusterNodesRequest(cluster.FilerType)
|
||||
|
||||
if req.ClientType != cluster.FilerType {
|
||||
t.Fatalf("ClientType = %q, want %q", req.ClientType, cluster.FilerType)
|
||||
}
|
||||
if req.FilerGroup != "tenant-a" {
|
||||
t.Fatalf("FilerGroup = %q, want %q", req.FilerGroup, "tenant-a")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListClusterNodesRequestKeepsDefaultFilerGroupEmpty(t *testing.T) {
|
||||
server := &AdminServer{}
|
||||
|
||||
req := server.listClusterNodesRequest(cluster.FilerType)
|
||||
|
||||
if req.FilerGroup != "" {
|
||||
t.Fatalf("FilerGroup = %q, want empty", req.FilerGroup)
|
||||
}
|
||||
}
|
||||
@@ -76,9 +76,7 @@ func (s *AdminServer) getDiscoveredFilers() []string {
|
||||
// Discover filers from masters
|
||||
var filers []string
|
||||
err := s.WithMasterClient(func(client master_pb.SeaweedClient) error {
|
||||
resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
|
||||
ClientType: cluster.FilerType,
|
||||
})
|
||||
resp, err := client.ListClusterNodes(context.Background(), s.listClusterNodesRequest(cluster.FilerType))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -494,9 +494,7 @@ func (s *AdminServer) findBrokerLeader() (string, error) {
|
||||
// First, try to find any broker from the cluster
|
||||
var brokers []string
|
||||
err := s.WithMasterClient(func(client master_pb.SeaweedClient) error {
|
||||
resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
|
||||
ClientType: cluster.BrokerType,
|
||||
})
|
||||
resp, err := client.ListClusterNodes(context.Background(), s.listClusterNodesRequest(cluster.BrokerType))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -524,7 +522,7 @@ func (s *AdminServer) findBrokerLeader() (string, error) {
|
||||
|
||||
// Try to find broker leader
|
||||
_, err := client.FindBrokerLeader(ctx, &mq_pb.FindBrokerLeaderRequest{
|
||||
FilerGroup: "",
|
||||
FilerGroup: s.filerGroup,
|
||||
})
|
||||
if err == nil {
|
||||
return nil // This broker is the leader
|
||||
|
||||
@@ -805,9 +805,7 @@ func (s *AdminServer) buildDefaultPluginClusterContext() *plugin_pb.ClusterConte
|
||||
|
||||
s3Seen := map[string]struct{}{}
|
||||
if err := s.WithMasterClient(func(client master_pb.SeaweedClient) error {
|
||||
resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
|
||||
ClientType: cluster.S3Type,
|
||||
})
|
||||
resp, err := client.ListClusterNodes(context.Background(), s.listClusterNodesRequest(cluster.S3Type))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ type AdminOptions struct {
|
||||
grpcPort *int
|
||||
master *string
|
||||
masters *string // deprecated, for backward compatibility
|
||||
filerGroup *string
|
||||
adminUser *string
|
||||
adminPassword *string
|
||||
readOnlyUser *string
|
||||
@@ -65,6 +66,7 @@ func init() {
|
||||
a.grpcPort = cmdAdmin.Flag.Int("port.grpc", 0, "gRPC server port for worker connections (default: http port + 10000)")
|
||||
a.master = cmdAdmin.Flag.String("master", "localhost:9333", "comma-separated master servers")
|
||||
a.masters = cmdAdmin.Flag.String("masters", "", "comma-separated master servers (deprecated, use -master instead)")
|
||||
a.filerGroup = cmdAdmin.Flag.String("filerGroup", "", "filerGroup for the filers, brokers, and S3 servers")
|
||||
a.dataDir = cmdAdmin.Flag.String("dataDir", ".", "directory to store admin configuration and data files (default current dir; required for maintenance task state to persist)")
|
||||
|
||||
a.adminUser = cmdAdmin.Flag.String("adminUser", "admin", "admin interface username")
|
||||
@@ -82,7 +84,7 @@ func init() {
|
||||
}
|
||||
|
||||
var cmdAdmin = &Command{
|
||||
UsageLine: "admin -port=23646 -master=localhost:9333 [-port.grpc=33646] [-dataDir=/path/to/data]",
|
||||
UsageLine: "admin -port=23646 -master=localhost:9333 [-filerGroup=group] [-port.grpc=33646] [-dataDir=/path/to/data]",
|
||||
Short: "start SeaweedFS web admin interface",
|
||||
Long: `Start a web admin interface for SeaweedFS cluster management.
|
||||
|
||||
@@ -99,6 +101,7 @@ var cmdAdmin = &Command{
|
||||
|
||||
Example Usage:
|
||||
weed admin -port=23646 -master="master1:9333,master2:9333"
|
||||
weed admin -port=23646 -master="localhost:9333" -filerGroup="tenant-a"
|
||||
weed admin -port=23646 -master="localhost:9333" -dataDir="/var/lib/seaweedfs-admin"
|
||||
weed admin -port=23646 -port.grpc=33646 -master="localhost:9333" -dataDir="~/seaweedfs-admin"
|
||||
weed admin -port=9900 -port.grpc=19900 -master="localhost:9333"
|
||||
@@ -383,7 +386,7 @@ func startAdminServer(ctx context.Context, options AdminOptions, enableUI bool,
|
||||
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", admin.StaticHandler()))
|
||||
|
||||
// Create admin server (plugin is always enabled)
|
||||
adminServer := dash.NewAdminServer(*options.master, nil, dataDir, icebergPort)
|
||||
adminServer := dash.NewAdminServer(*options.master, *options.filerGroup, nil, dataDir, icebergPort)
|
||||
|
||||
// Show discovered filers
|
||||
filers := adminServer.GetAllFilers()
|
||||
|
||||
@@ -543,6 +543,8 @@ func initMiniAdminFlags() {
|
||||
miniAdminOptions.port = cmdMini.Flag.Int("admin.port", 23646, "admin server http listen port")
|
||||
miniAdminOptions.grpcPort = cmdMini.Flag.Int("admin.port.grpc", 0, "admin server grpc listen port (default: admin http port + GrpcPortOffset)")
|
||||
miniAdminOptions.master = cmdMini.Flag.String("admin.master", "", "master server address (automatically set)")
|
||||
// admin discovery must query the same group the co-located filer registers under
|
||||
miniAdminOptions.filerGroup = miniFilerOptions.filerGroup
|
||||
miniAdminOptions.dataDir = cmdMini.Flag.String("admin.dataDir", "", "directory to store admin configuration and data files")
|
||||
miniAdminOptions.adminUser = cmdMini.Flag.String("admin.user", "admin", "admin interface username")
|
||||
miniAdminOptions.adminPassword = cmdMini.Flag.String("admin.password", "", "admin interface password (if empty, auth is disabled)")
|
||||
|
||||
Reference in new issue
Block a user