github.com/weaviate/weaviate@v1.24.6/usecases/cluster/state.go (about)

     1  //                           _       _
     2  // __      _____  __ ___   ___  __ _| |_ ___
     3  // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
     4  //  \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
     5  //   \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
     6  //
     7  //  Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
     8  //
     9  //  CONTACT: hello@weaviate.io
    10  //
    11  
    12  package cluster
    13  
    14  import (
    15  	"fmt"
    16  	"net"
    17  	"strings"
    18  
    19  	"github.com/hashicorp/memberlist"
    20  	"github.com/pkg/errors"
    21  	"github.com/sirupsen/logrus"
    22  )
    23  
    24  type State struct {
    25  	config   Config
    26  	list     *memberlist.Memberlist
    27  	delegate delegate
    28  }
    29  
    30  type Config struct {
    31  	Hostname                string     `json:"hostname" yaml:"hostname"`
    32  	GossipBindPort          int        `json:"gossipBindPort" yaml:"gossipBindPort"`
    33  	DataBindPort            int        `json:"dataBindPort" yaml:"dataBindPort"`
    34  	Join                    string     `json:"join" yaml:"join"`
    35  	IgnoreStartupSchemaSync bool       `json:"ignoreStartupSchemaSync" yaml:"ignoreStartupSchemaSync"`
    36  	SkipSchemaSyncRepair    bool       `json:"skipSchemaSyncRepair" yaml:"skipSchemaSyncRepair"`
    37  	AuthConfig              AuthConfig `json:"auth" yaml:"auth"`
    38  	AdvertiseAddr           string     `json:"advertiseAddr" yaml:"advertiseAddr"`
    39  	AdvertisePort           int        `json:"advertisePort" yaml:"advertisePort"`
    40  }
    41  
    42  type AuthConfig struct {
    43  	BasicAuth BasicAuth `json:"basic" yaml:"basic"`
    44  }
    45  
    46  type BasicAuth struct {
    47  	Username string `json:"username" yaml:"username"`
    48  	Password string `json:"password" yaml:"password"`
    49  }
    50  
    51  func (ba BasicAuth) Enabled() bool {
    52  	return ba.Username != "" || ba.Password != ""
    53  }
    54  
    55  func Init(userConfig Config, dataPath string, logger logrus.FieldLogger) (_ *State, err error) {
    56  	cfg := memberlist.DefaultLANConfig()
    57  	cfg.LogOutput = newLogParser(logger)
    58  	if userConfig.Hostname != "" {
    59  		cfg.Name = userConfig.Hostname
    60  	}
    61  	state := State{
    62  		config: userConfig,
    63  		delegate: delegate{
    64  			Name:     cfg.Name,
    65  			dataPath: dataPath,
    66  			log:      logger,
    67  		},
    68  	}
    69  	if err := state.delegate.init(diskSpace); err != nil {
    70  		logger.WithField("action", "init_state.delete_init").Error(err)
    71  	}
    72  	cfg.Delegate = &state.delegate
    73  	cfg.Events = events{&state.delegate}
    74  	if userConfig.GossipBindPort != 0 {
    75  		cfg.BindPort = userConfig.GossipBindPort
    76  	}
    77  
    78  	if userConfig.AdvertiseAddr != "" {
    79  		cfg.AdvertiseAddr = userConfig.AdvertiseAddr
    80  	}
    81  
    82  	if userConfig.AdvertisePort != 0 {
    83  		cfg.AdvertisePort = userConfig.AdvertisePort
    84  	}
    85  
    86  	if state.list, err = memberlist.Create(cfg); err != nil {
    87  		logger.WithField("action", "memberlist_init").
    88  			WithField("hostname", userConfig.Hostname).
    89  			WithField("bind_port", userConfig.GossipBindPort).
    90  			WithError(err).
    91  			Error("memberlist not created")
    92  		return nil, errors.Wrap(err, "create member list")
    93  	}
    94  
    95  	var joinAddr []string
    96  	if userConfig.Join != "" {
    97  		joinAddr = strings.Split(userConfig.Join, ",")
    98  	}
    99  
   100  	if len(joinAddr) > 0 {
   101  
   102  		_, err := net.LookupIP(strings.Split(joinAddr[0], ":")[0])
   103  		if err != nil {
   104  			logger.WithField("action", "cluster_attempt_join").
   105  				WithField("remote_hostname", joinAddr[0]).
   106  				WithError(err).
   107  				Warn("specified hostname to join cluster cannot be resolved. This is fine" +
   108  					"if this is the first node of a new cluster, but problematic otherwise.")
   109  		} else {
   110  			_, err := state.list.Join(joinAddr)
   111  			if err != nil {
   112  				logger.WithField("action", "memberlist_init").
   113  					WithField("remote_hostname", joinAddr).
   114  					WithError(err).
   115  					Error("memberlist join not successful")
   116  				return nil, errors.Wrap(err, "join cluster")
   117  			}
   118  		}
   119  	}
   120  	return &state, nil
   121  }
   122  
   123  // Hostnames for all live members, except self. Use AllHostnames to include
   124  // self, prefixes the data port.
   125  func (s *State) Hostnames() []string {
   126  	mem := s.list.Members()
   127  	out := make([]string, len(mem))
   128  
   129  	i := 0
   130  	for _, m := range mem {
   131  		if m.Name == s.list.LocalNode().Name {
   132  			continue
   133  		}
   134  		// TODO: how can we find out the actual data port as opposed to relying on
   135  		// the convention that it's 1 higher than the gossip port
   136  		out[i] = fmt.Sprintf("%s:%d", m.Addr.String(), m.Port+1)
   137  		i++
   138  	}
   139  
   140  	return out[:i]
   141  }
   142  
   143  // AllHostnames for live members, including self.
   144  func (s *State) AllHostnames() []string {
   145  	mem := s.list.Members()
   146  	out := make([]string, len(mem))
   147  
   148  	for i, m := range mem {
   149  		// TODO: how can we find out the actual data port as opposed to relying on
   150  		// the convention that it's 1 higher than the gossip port
   151  		out[i] = fmt.Sprintf("%s:%d", m.Addr.String(), m.Port+1)
   152  	}
   153  
   154  	return out
   155  }
   156  
   157  // All node names (not their hostnames!) for live members, including self.
   158  func (s *State) AllNames() []string {
   159  	mem := s.list.Members()
   160  	out := make([]string, len(mem))
   161  
   162  	for i, m := range mem {
   163  		out[i] = m.Name
   164  	}
   165  
   166  	return out
   167  }
   168  
   169  // Candidates returns list of nodes (names) sorted by the
   170  // free amount of disk space in descending order
   171  func (s *State) Candidates() []string {
   172  	return s.delegate.sortCandidates(s.AllNames())
   173  }
   174  
   175  // All node names (not their hostnames!) for live members, including self.
   176  func (s *State) NodeCount() int {
   177  	return s.list.NumMembers()
   178  }
   179  
   180  func (s *State) LocalName() string {
   181  	return s.list.LocalNode().Name
   182  }
   183  
   184  func (s *State) ClusterHealthScore() int {
   185  	return s.list.GetHealthScore()
   186  }
   187  
   188  func (s *State) NodeHostname(nodeName string) (string, bool) {
   189  	for _, mem := range s.list.Members() {
   190  		if mem.Name == nodeName {
   191  			// TODO: how can we find out the actual data port as opposed to relying on
   192  			// the convention that it's 1 higher than the gossip port
   193  			return fmt.Sprintf("%s:%d", mem.Addr.String(), mem.Port+1), true
   194  		}
   195  	}
   196  
   197  	return "", false
   198  }
   199  
   200  func (s *State) SchemaSyncIgnored() bool {
   201  	return s.config.IgnoreStartupSchemaSync
   202  }
   203  
   204  func (s *State) SkipSchemaRepair() bool {
   205  	return s.config.SkipSchemaSyncRepair
   206  }
   207  
   208  func (s *State) NodeInfo(node string) (NodeInfo, bool) {
   209  	return s.delegate.get(node)
   210  }