package config import ( "errors" "fmt" "os" "path/filepath" "strings" "gopkg.in/yaml.v2" ) const ( RaftDir = "raft" RaftPort = 7001 RaftLogLevel = "off" EtcdLogLevel = "error" EtcdPrefix = "/vipman" ) func ParseFile(file string) (cfg *Config, err error) { if file == "" { return nil, errors.New("missing config file") } f, err := os.Open(file) if err != nil { return } defer f.Close() cfg = new(Config) err = yaml.NewDecoder(f).Decode(cfg) if err != nil { return } err = Validate(cfg) return } func Validate(c *Config) error { if len(c.VirtualIPs) == 0 { return errors.New("missing virtualIPs config") } if c.LeaderHook != "" { if !filepath.IsAbs(c.LeaderHook) { return errors.New("leaderHook path must be absolute") } } if c.FollowerHook != "" { if !filepath.IsAbs(c.FollowerHook) { return errors.New("followerHook path must be absolute") } } raft := c.Cluster.Raft if raft != nil { if raft.LogLevel == "" { raft.LogLevel = RaftLogLevel } if raft.Dir == "" { raft.Dir = RaftDir } if raft.ID == "" { return errors.New("missing raft.id config") } if raft.Address == "" { return errors.New("missing raft.address config") } if len(raft.Peers) < 2 { return errors.New("minimum number of remote peers: 2") } for i, v := range raft.Peers { if v.ID == "" { return fmt.Errorf("missing raft.peers[%d].id config", i) } if v.Address == "" { return fmt.Errorf("missing raft.peers[%d].address config", i) } } } etcd := c.Cluster.Etcd if etcd != nil { if etcd.LogLevel == "" { etcd.LogLevel = EtcdLogLevel } if len(etcd.Endpoints) == 0 { return errors.New("missing etcd.endpoints config") } if etcd.Prefix == "" { etcd.Prefix = EtcdPrefix } else { if !strings.HasPrefix(etcd.Prefix, "/") { etcd.Prefix = "/" + etcd.Prefix } if strings.HasSuffix(etcd.Prefix, "/") { etcd.Prefix = strings.TrimSuffix(etcd.Prefix, "/") } } if etcd.ClusterName == "" { return errors.New("missing etcd.clusterName config") } else { if strings.HasSuffix(etcd.ClusterName, "/") { etcd.ClusterName = strings.TrimSuffix(etcd.ClusterName, "/") } } } if raft == nil && etcd == nil { return errors.New("missing cluster config: raft or etcd") } if raft != nil && etcd != nil { return errors.New("only one cluster config allowed: raft or etcd") } return nil } type Config struct { VirtualIPs []string `yaml:"virtualIPs"` Interface string `yaml:"interface,omitempty"` Label string `yaml:"label,omitempty"` LeaderHook string `yaml:"leaderHook,omitempty"` FollowerHook string `yaml:"followerHook,omitempty"` Cluster Cluster `yaml:"cluster"` } type Cluster struct { Raft *Raft `yaml:"raft,omitempty"` Etcd *Etcd `yaml:"etcd,omitempty"` } type Raft struct { Dir string `yaml:"dir,omitempty"` ID string `yaml:"id"` Address string `yaml:"address"` Peers []RaftPeer `yaml:"peers"` TLS *TLS `yaml:"tls,omitempty"` LogLevel string `yaml:"logLevel,omitempty"` } type TLS struct { Cert string `yaml:"cert,omitempty"` Key string `yaml:"key,omitempty"` CA string `yaml:"ca,omitempty"` Insecure bool `yaml:"insecure,omitempty"` } // RaftPeer details the configuration of all cluster peers type RaftPeer struct { ID string `yaml:"id"` Address string `yaml:"address"` } type Etcd struct { Endpoints []string `yaml:"endpoints"` Prefix string `yaml:"prefix"` ClusterName string `yaml:"clusterName"` Username string `yaml:"username,omitempty"` Password string `yaml:"password,omitempty"` TLS *TLS `yaml:"tls,omitempty"` LogLevel string `yaml:"logLevel,omitempty"` }