package config import ( "encoding/json" "fmt" "io" "log" "net" "os" "codeberg.org/snonux/gorum/internal/utils" ) type Node struct { Port int Priority int Alias string `json:",omitempty"` Hostname string originalId string } func (n Node) Address() string { return fmt.Sprintf("%s:%d", n.Hostname, n.Port) } type Config struct { StateDir string LogToSyslog bool `json:"LogToSyslog,omitempty"` ScoreFile string Address string Nodes map[string]Node LoopIntervalS int64 `json:"LoopIntervalS,omitempty"` MyID string `json:"MyID,omitempty"` RelaxedMode bool `json:"RelaxedMode,omitempty"` EmailTo string `json:"EmailTo,omitempty"` EmailFrom string `json:"EmailFrom,omitempty"` SMTPServer string `json:"SMTPServer,omitempty"` } func NewFromConfigFile(configFile string) (Config, error) { var conf Config file, err := os.Open(configFile) if err != nil { return conf, err } defer file.Close() bytes, err := io.ReadAll(file) if err != nil { return conf, err } err = json.Unmarshal(bytes, &conf) if err != nil { return conf, err } // Make it so that the key is the Hostname for internal lookup. newNodes := make(map[string]Node, len(conf.Nodes)) for id, node := range conf.Nodes { log.Printf("adding node %s: %v", id, node) node.originalId = id if node.Alias != "" { newNodes[node.Alias] = node continue } newNodes[node.Hostname] = node } conf.Nodes = newNodes return conf.setDefaults() } func New(options ...func(*Config)) (Config, error) { var conf Config for _, opt := range options { opt(&conf) } return conf.setDefaults() } func WithStateDir(stateDir string) func(*Config) { return func(conf *Config) { conf.StateDir = stateDir } } func WithAddress(address string) func(*Config) { return func(conf *Config) { conf.Address = address } } func WithNodes(nodes ...Node) func(*Config) { return func(conf *Config) { if conf.Nodes == nil { conf.Nodes = make(map[string]Node) } for _, node := range nodes { conf.Nodes[node.Hostname] = node } } } func WithMyID(id string) func(*Config) { return func(conf *Config) { conf.MyID = id } } func WithRelaxedMode() func(*Config) { return func(conf *Config) { conf.RelaxedMode = true } } func (conf Config) setDefaults() (Config, error) { if conf.ScoreFile == "" { conf.ScoreFile = "scores" } if conf.LoopIntervalS == 0 { conf.LoopIntervalS = 10 } if conf.MyID == "" { hostname, err := os.Hostname() if err != nil { return conf, err } conf.MyID = hostname } return conf, nil } func (conf Config) NodePriority(id string) (int, error) { node, err := conf.findNode(id) if err != nil { return 0, err } return node.Priority, nil } func (conf Config) IsNode(remoteAddr string) bool { _, err := conf.findNode(utils.StripPort(remoteAddr)) return err == nil } func (conf Config) IsNodeWithLookup(remoteAddr string, lookupIP func(string) ([]net.IP, error)) bool { remoteAddr = utils.StripPort(remoteAddr) compare := func(hostname string) bool { ips, err := lookupIP(hostname) if err != nil { log.Println("config:", err) return false } for _, ip := range ips { if remoteAddr == ip.String() { return true } } return false } for _, node := range conf.Nodes { if compare(node.Hostname) || compare(node.Alias) { return true } } return false } func (conf Config) findNode(hostname string) (Node, error) { if node, ok := conf.Nodes[hostname]; ok { return node, nil } for _, node := range conf.Nodes { if hostname == node.Alias { return node, nil } } return Node{}, fmt.Errorf("node %s not found in %v", hostname, conf.Nodes) }