You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							256 lines
						
					
					
						
							9.2 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							256 lines
						
					
					
						
							9.2 KiB
						
					
					
				
								package weed_server
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"context"
							 | 
						|
									"fmt"
							 | 
						|
									"net/http"
							 | 
						|
									"os"
							 | 
						|
									"strings"
							 | 
						|
									"sync"
							 | 
						|
									"sync/atomic"
							 | 
						|
									"time"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/stats"
							 | 
						|
								
							 | 
						|
									"google.golang.org/grpc"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/util/grace"
							 | 
						|
								
							 | 
						|
									"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/util"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/filer"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/arangodb"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/cassandra"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/cassandra2"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/elastic/v7"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/etcd"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/hbase"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/leveldb"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/leveldb2"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/leveldb3"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/mongodb"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/mysql"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/mysql2"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/postgres"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/postgres2"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/redis"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/redis2"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/redis3"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/sqlite"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/tarantool"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/filer/ydb"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/glog"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/notification"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/notification/aws_sqs"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/notification/gocdk_pub_sub"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/notification/google_pub_sub"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/notification/kafka"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/notification/log"
							 | 
						|
									_ "github.com/seaweedfs/seaweedfs/weed/notification/webhook"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/security"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								type FilerOption struct {
							 | 
						|
									Masters               *pb.ServerDiscovery
							 | 
						|
									FilerGroup            string
							 | 
						|
									Collection            string
							 | 
						|
									DefaultReplication    string
							 | 
						|
									DisableDirListing     bool
							 | 
						|
									MaxMB                 int
							 | 
						|
									DirListingLimit       int
							 | 
						|
									DataCenter            string
							 | 
						|
									Rack                  string
							 | 
						|
									DataNode              string
							 | 
						|
									DefaultLevelDbDir     string
							 | 
						|
									DisableHttp           bool
							 | 
						|
									Host                  pb.ServerAddress
							 | 
						|
									recursiveDelete       bool
							 | 
						|
									Cipher                bool
							 | 
						|
									SaveToFilerLimit      int64
							 | 
						|
									ConcurrentUploadLimit int64
							 | 
						|
									ShowUIDirectoryDelete bool
							 | 
						|
									DownloadMaxBytesPs    int64
							 | 
						|
									DiskType              string
							 | 
						|
									AllowedOrigins        []string
							 | 
						|
									ExposeDirectoryData   bool
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								type FilerServer struct {
							 | 
						|
									inFlightDataSize int64
							 | 
						|
									listenersWaits   int64
							 | 
						|
								
							 | 
						|
									// notifying clients
							 | 
						|
									listenersLock sync.Mutex
							 | 
						|
									listenersCond *sync.Cond
							 | 
						|
								
							 | 
						|
									inFlightDataLimitCond *sync.Cond
							 | 
						|
								
							 | 
						|
									filer_pb.UnimplementedSeaweedFilerServer
							 | 
						|
									option         *FilerOption
							 | 
						|
									secret         security.SigningKey
							 | 
						|
									filer          *filer.Filer
							 | 
						|
									filerGuard     *security.Guard
							 | 
						|
									volumeGuard    *security.Guard
							 | 
						|
									grpcDialOption grpc.DialOption
							 | 
						|
								
							 | 
						|
									// metrics read from the master
							 | 
						|
									metricsAddress     string
							 | 
						|
									metricsIntervalSec int
							 | 
						|
								
							 | 
						|
									// track known metadata listeners
							 | 
						|
									knownListenersLock sync.Mutex
							 | 
						|
									knownListeners     map[int32]int32
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func NewFilerServer(defaultMux, readonlyMux *http.ServeMux, option *FilerOption) (fs *FilerServer, err error) {
							 | 
						|
								
							 | 
						|
									v := util.GetViper()
							 | 
						|
									signingKey := v.GetString("jwt.filer_signing.key")
							 | 
						|
									v.SetDefault("jwt.filer_signing.expires_after_seconds", 10)
							 | 
						|
									expiresAfterSec := v.GetInt("jwt.filer_signing.expires_after_seconds")
							 | 
						|
								
							 | 
						|
									readSigningKey := v.GetString("jwt.filer_signing.read.key")
							 | 
						|
									v.SetDefault("jwt.filer_signing.read.expires_after_seconds", 60)
							 | 
						|
									readExpiresAfterSec := v.GetInt("jwt.filer_signing.read.expires_after_seconds")
							 | 
						|
								
							 | 
						|
									volumeSigningKey := v.GetString("jwt.signing.key")
							 | 
						|
									v.SetDefault("jwt.signing.expires_after_seconds", 10)
							 | 
						|
									volumeExpiresAfterSec := v.GetInt("jwt.signing.expires_after_seconds")
							 | 
						|
								
							 | 
						|
									volumeReadSigningKey := v.GetString("jwt.signing.read.key")
							 | 
						|
									v.SetDefault("jwt.signing.read.expires_after_seconds", 60)
							 | 
						|
									volumeReadExpiresAfterSec := v.GetInt("jwt.signing.read.expires_after_seconds")
							 | 
						|
								
							 | 
						|
									v.SetDefault("cors.allowed_origins.values", "*")
							 | 
						|
								
							 | 
						|
									allowedOrigins := v.GetString("cors.allowed_origins.values")
							 | 
						|
									domains := strings.Split(allowedOrigins, ",")
							 | 
						|
									option.AllowedOrigins = domains
							 | 
						|
								
							 | 
						|
									v.SetDefault("filer.expose_directory_metadata.enabled", true)
							 | 
						|
									returnDirMetadata := v.GetBool("filer.expose_directory_metadata.enabled")
							 | 
						|
									option.ExposeDirectoryData = returnDirMetadata
							 | 
						|
								
							 | 
						|
									fs = &FilerServer{
							 | 
						|
										option:                option,
							 | 
						|
										grpcDialOption:        security.LoadClientTLS(util.GetViper(), "grpc.filer"),
							 | 
						|
										knownListeners:        make(map[int32]int32),
							 | 
						|
										inFlightDataLimitCond: sync.NewCond(new(sync.Mutex)),
							 | 
						|
									}
							 | 
						|
									fs.listenersCond = sync.NewCond(&fs.listenersLock)
							 | 
						|
								
							 | 
						|
									option.Masters.RefreshBySrvIfAvailable()
							 | 
						|
									if len(option.Masters.GetInstances()) == 0 {
							 | 
						|
										glog.Fatal("master list is required!")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if !util.LoadConfiguration("filer", false) {
							 | 
						|
										v.SetDefault("leveldb2.enabled", true)
							 | 
						|
										v.SetDefault("leveldb2.dir", option.DefaultLevelDbDir)
							 | 
						|
										_, err := os.Stat(option.DefaultLevelDbDir)
							 | 
						|
										if os.IsNotExist(err) {
							 | 
						|
											os.MkdirAll(option.DefaultLevelDbDir, 0755)
							 | 
						|
										}
							 | 
						|
										glog.V(0).Infof("default to create filer store dir in %s", option.DefaultLevelDbDir)
							 | 
						|
									} else {
							 | 
						|
										glog.Warningf("skipping default store dir in %s", option.DefaultLevelDbDir)
							 | 
						|
									}
							 | 
						|
									util.LoadConfiguration("notification", false)
							 | 
						|
								
							 | 
						|
									v.SetDefault("filer.options.max_file_name_length", 255)
							 | 
						|
									maxFilenameLength := v.GetUint32("filer.options.max_file_name_length")
							 | 
						|
									glog.V(0).Infof("max_file_name_length %d", maxFilenameLength)
							 | 
						|
									fs.filer = filer.NewFiler(*option.Masters, fs.grpcDialOption, option.Host, option.FilerGroup, option.Collection, option.DefaultReplication, option.DataCenter, maxFilenameLength, func() {
							 | 
						|
										if atomic.LoadInt64(&fs.listenersWaits) > 0 {
							 | 
						|
											fs.listenersCond.Broadcast()
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
									fs.filer.Cipher = option.Cipher
							 | 
						|
									whiteList := util.StringSplit(v.GetString("guard.white_list"), ",")
							 | 
						|
									fs.filerGuard = security.NewGuard(whiteList, signingKey, expiresAfterSec, readSigningKey, readExpiresAfterSec)
							 | 
						|
									fs.volumeGuard = security.NewGuard([]string{}, volumeSigningKey, volumeExpiresAfterSec, volumeReadSigningKey, volumeReadExpiresAfterSec)
							 | 
						|
								
							 | 
						|
									fs.checkWithMaster()
							 | 
						|
								
							 | 
						|
									go stats.LoopPushingMetric("filer", string(fs.option.Host), fs.metricsAddress, fs.metricsIntervalSec)
							 | 
						|
									go fs.filer.KeepMasterClientConnected(context.Background())
							 | 
						|
								
							 | 
						|
									fs.option.recursiveDelete = v.GetBool("filer.options.recursive_delete")
							 | 
						|
									v.SetDefault("filer.options.buckets_folder", "/buckets")
							 | 
						|
									fs.filer.DirBucketsPath = v.GetString("filer.options.buckets_folder")
							 | 
						|
									// TODO deprecated, will be removed after 2020-12-31
							 | 
						|
									// replaced by https://github.com/seaweedfs/seaweedfs/wiki/Path-Specific-Configuration
							 | 
						|
									// fs.filer.FsyncBuckets = v.GetStringSlice("filer.options.buckets_fsync")
							 | 
						|
									isFresh := fs.filer.LoadConfiguration(v)
							 | 
						|
								
							 | 
						|
									notification.LoadConfiguration(v, "notification.")
							 | 
						|
								
							 | 
						|
									handleStaticResources(defaultMux)
							 | 
						|
									if !option.DisableHttp {
							 | 
						|
										defaultMux.HandleFunc("/healthz", requestIDMiddleware(fs.filerHealthzHandler))
							 | 
						|
										defaultMux.HandleFunc("/", fs.filerGuard.WhiteList(requestIDMiddleware(fs.filerHandler)))
							 | 
						|
									}
							 | 
						|
									if defaultMux != readonlyMux {
							 | 
						|
										handleStaticResources(readonlyMux)
							 | 
						|
										readonlyMux.HandleFunc("/healthz", requestIDMiddleware(fs.filerHealthzHandler))
							 | 
						|
										readonlyMux.HandleFunc("/", fs.filerGuard.WhiteList(requestIDMiddleware(fs.readonlyFilerHandler)))
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									existingNodes := fs.filer.ListExistingPeerUpdates(context.Background())
							 | 
						|
									startFromTime := time.Now().Add(-filer.LogFlushInterval)
							 | 
						|
									if isFresh {
							 | 
						|
										glog.V(0).Infof("%s bootstrap from peers %+v", option.Host, existingNodes)
							 | 
						|
										if err := fs.filer.MaybeBootstrapFromOnePeer(option.Host, existingNodes, startFromTime); err != nil {
							 | 
						|
											glog.Fatalf("%s bootstrap from %+v: %v", option.Host, existingNodes, err)
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
									fs.filer.AggregateFromPeers(option.Host, existingNodes, startFromTime)
							 | 
						|
								
							 | 
						|
									fs.filer.LoadFilerConf()
							 | 
						|
								
							 | 
						|
									fs.filer.LoadRemoteStorageConfAndMapping()
							 | 
						|
								
							 | 
						|
									grace.OnReload(fs.Reload)
							 | 
						|
									grace.OnInterrupt(func() {
							 | 
						|
										fs.filer.Shutdown()
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									fs.filer.Dlm.LockRing.SetTakeSnapshotCallback(fs.OnDlmChangeSnapshot)
							 | 
						|
								
							 | 
						|
									return fs, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (fs *FilerServer) checkWithMaster() {
							 | 
						|
								
							 | 
						|
									isConnected := false
							 | 
						|
									for !isConnected {
							 | 
						|
										fs.option.Masters.RefreshBySrvIfAvailable()
							 | 
						|
										for _, master := range fs.option.Masters.GetInstances() {
							 | 
						|
											readErr := operation.WithMasterServerClient(false, master, fs.grpcDialOption, func(masterClient master_pb.SeaweedClient) error {
							 | 
						|
												resp, err := masterClient.GetMasterConfiguration(context.Background(), &master_pb.GetMasterConfigurationRequest{})
							 | 
						|
												if err != nil {
							 | 
						|
													return fmt.Errorf("get master %s configuration: %v", master, err)
							 | 
						|
												}
							 | 
						|
												fs.metricsAddress, fs.metricsIntervalSec = resp.MetricsAddress, int(resp.MetricsIntervalSeconds)
							 | 
						|
												return nil
							 | 
						|
											})
							 | 
						|
											if readErr == nil {
							 | 
						|
												isConnected = true
							 | 
						|
											} else {
							 | 
						|
												time.Sleep(7 * time.Second)
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (fs *FilerServer) Reload() {
							 | 
						|
									glog.V(0).Infoln("Reload filer server...")
							 | 
						|
								
							 | 
						|
									util.LoadConfiguration("security", false)
							 | 
						|
									v := util.GetViper()
							 | 
						|
									fs.filerGuard.UpdateWhiteList(util.StringSplit(v.GetString("guard.white_list"), ","))
							 | 
						|
								}
							 |