mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-05-23 10:11:28 +00:00
* fix(admin): switch file browser upload/download to filer gRPC + volume HTTP The admin file browser proxied uploads and downloads through the filer's HTTP listener, so the whole feature 404'd against filers started with -disableHttp=true even though S3 still worked on its own port. Re-route through the filer gRPC service: LookupDirectoryEntry + StreamContent for reads (chunks flow straight from the volume servers), AssignVolume + volume HTTP POST + CreateEntry for writes. Volume read tokens come from jwt.signing.read.key when configured; the old jwt.filer_signing tokens no longer apply since the filer HTTP surface is bypassed. * admin file browser: propagate request context + track response writes Pass r.Context() into uploadFileToFiler so a client disconnect cancels the in-flight chunked upload instead of letting it run to completion against the volume servers. For DownloadFile, replace the Content-Type probe with a small response-writer wrapper that records whether headers or bytes have actually been sent, so the error path can't silently convert a pre-stream failure into a partial response if future code moves the header-setting around.
111 lines
3.5 KiB
Go
111 lines
3.5 KiB
Go
package dash
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/cluster"
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
"github.com/seaweedfs/seaweedfs/weed/operation"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/volume_server_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/wdclient"
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
// WithMasterClient executes a function with a master client connection
|
|
func (s *AdminServer) WithMasterClient(f func(client master_pb.SeaweedClient) error) error {
|
|
return s.masterClient.WithClient(false, f)
|
|
}
|
|
|
|
// WithFilerClient executes a function with a filer client connection
|
|
func (s *AdminServer) WithFilerClient(f func(client filer_pb.SeaweedFilerClient) error) error {
|
|
filerAddr := s.GetFilerAddress()
|
|
if filerAddr == "" {
|
|
return fmt.Errorf("no filer available")
|
|
}
|
|
|
|
return pb.WithGrpcFilerClient(false, 0, pb.ServerAddress(filerAddr), s.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
|
|
return f(client)
|
|
})
|
|
}
|
|
|
|
// WithVolumeServerClient executes a function with a volume server client connection
|
|
func (s *AdminServer) WithVolumeServerClient(address pb.ServerAddress, f func(client volume_server_pb.VolumeServerClient) error) error {
|
|
return operation.WithVolumeServerClient(false, address, s.grpcDialOption, func(client volume_server_pb.VolumeServerClient) error {
|
|
return f(client)
|
|
})
|
|
}
|
|
|
|
// GetMasterClient returns the admin server's wdclient.MasterClient. It is used
|
|
// by file browser download paths that stream chunks straight from the volume
|
|
// servers via filer.PrepareStreamContent so they keep working when the filer
|
|
// has -disableHttp=true.
|
|
func (s *AdminServer) GetMasterClient() *wdclient.MasterClient {
|
|
return s.masterClient
|
|
}
|
|
|
|
// GetGrpcDialOption returns the dial option used for all admin-originated
|
|
// gRPC connections (TLS or insecure). File browser uploads need this when
|
|
// they perform the assign + volume HTTP POST + create-entry flow.
|
|
func (s *AdminServer) GetGrpcDialOption() grpc.DialOption {
|
|
return s.grpcDialOption
|
|
}
|
|
|
|
// GetFilerAddress returns a filer address, discovering from masters if needed
|
|
func (s *AdminServer) GetFilerAddress() string {
|
|
// Discover filers from masters
|
|
filers := s.getDiscoveredFilers()
|
|
if len(filers) > 0 {
|
|
return filers[0] // Return the first available filer
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
// getDiscoveredFilers returns cached filers or discovers them from masters
|
|
func (s *AdminServer) getDiscoveredFilers() []string {
|
|
// Check if cache is still valid
|
|
if time.Since(s.lastFilerUpdate) < s.filerCacheExpiration && len(s.cachedFilers) > 0 {
|
|
return s.cachedFilers
|
|
}
|
|
|
|
// 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,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, node := range resp.ClusterNodes {
|
|
filers = append(filers, node.Address)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
currentMaster := s.masterClient.GetMaster(context.Background())
|
|
glog.Warningf("Failed to discover filers from master %s: %v", currentMaster, err)
|
|
// Return cached filers even if expired, better than nothing
|
|
return s.cachedFilers
|
|
}
|
|
|
|
// Update cache
|
|
s.cachedFilers = filers
|
|
s.lastFilerUpdate = time.Now()
|
|
|
|
return filers
|
|
}
|
|
|
|
// GetAllFilers returns all discovered filers
|
|
func (s *AdminServer) GetAllFilers() []string {
|
|
return s.getDiscoveredFilers()
|
|
}
|