1
0
Fork 0

Move config validation to object

master
Philip O'Toole 3 years ago
parent e0d2bcf4df
commit acbcd5e1cc

@ -1,9 +1,11 @@
package main
import (
"errors"
"flag"
"fmt"
"net"
"net/url"
"os"
"runtime"
"strings"
@ -163,9 +165,6 @@ type Config struct {
// CompressionBatch sets request batch threshold for compression attempt.
CompressionBatch int
// ShowVersion instructs the node to show version information and exit.
ShowVersion bool
// CPUProfile enables CPU profiling.
CPUProfile string
@ -173,6 +172,90 @@ type Config struct {
MemProfile string
}
// Validate checks the configuration for internal consistency, and activates
// important rqlite policies. It must be called at least once on a Config
// object before the Config object is used. It is OK to call more than
// once.
func (c *Config) Validate() error {
if c.OnDiskPath != "" && !c.OnDisk {
return errors.New("-on-disk-path is set, but -on-disk is not")
}
// Enforce policies regarding addresses
if c.RaftAdv == "" {
c.RaftAdv = c.RaftAddr
}
if c.HTTPAdv == "" {
c.HTTPAdv = c.HTTPAddr
}
// Node ID policy
if c.NodeID == "" {
c.NodeID = c.RaftAdv
}
// Perfom some address validity checks.
if strings.HasPrefix(strings.ToLower(c.HTTPAddr), "http") ||
strings.HasPrefix(strings.ToLower(c.HTTPAdv), "http") {
return errors.New("HTTP options should not include protocol (http:// or https://)")
}
if _, _, err := net.SplitHostPort(c.HTTPAddr); err != nil {
return errors.New("HTTP bind address not valid")
}
if _, _, err := net.SplitHostPort(c.HTTPAdv); err != nil {
return errors.New("HTTP advertised address not valid")
}
if _, _, err := net.SplitHostPort(c.RaftAddr); err != nil {
return errors.New("raft bind address not valid")
}
if _, _, err := net.SplitHostPort(c.RaftAdv); err != nil {
return errors.New("raft advertised address not valid")
}
// Enforce bootstrapping policies
if c.BootstrapExpect > 0 && c.RaftNonVoter {
return errors.New("bootstrapping only applicable to voting nodes")
}
// Join addresses OK?
if c.JoinAddr != "" {
addrs := strings.Split(c.JoinAddr, ",")
for i := range addrs {
u, err := url.Parse(addrs[i])
if err != nil {
return fmt.Errorf("%s is an invalid join adddress", addrs[i])
}
if c.BootstrapExpect == 0 {
if u.Host == c.HTTPAdv || addrs[i] == c.HTTPAddr {
return errors.New("node cannot join with itself unless bootstrapping")
}
}
}
}
// Valid disco mode?
switch c.DiscoMode {
case "":
case DiscoModeEtcdKV, DiscoModeConsulKV:
if c.BootstrapExpect > 0 {
return fmt.Errorf("bootstrapping not applicable when using %s", c.DiscoMode)
}
default:
return fmt.Errorf("disco mode must be %s or %s", DiscoModeConsulKV, DiscoModeEtcdKV)
}
return nil
}
// JoinAddresses returns the join addresses set at the command line. Returns nil
// if no join addresses were set.
func (c *Config) JoinAddresses() []string {
if c.JoinAddr == "" {
return nil
}
return strings.Split(c.JoinAddr, ",")
}
// HTTPURL returns the fully-formed, advertised HTTP API address for this config, including
// protocol, host and port.
func (c *Config) HTTPURL() string {
@ -195,7 +278,8 @@ func ParseFlags(name, desc string, build *BuildInfo) (*Config, error) {
if flag.Parsed() {
return nil, fmt.Errorf("command-line flags already parsed")
}
config := Config{}
config := &Config{}
showVersion := false
flag.StringVar(&config.NodeID, "node-id", "", "Unique name for node. If not set, set to advertised Raft address")
flag.StringVar(&config.HTTPAddr, "http-addr", "localhost:4001", "HTTP server bind address. To enable HTTPS, set X.509 cert and key")
@ -229,7 +313,7 @@ func ParseFlags(name, desc string, build *BuildInfo) (*Config, error) {
flag.StringVar(&config.OnDiskPath, "on-disk-path", "", "Path for SQLite on-disk database file. If not set, use file in data directory")
flag.BoolVar(&config.OnDiskStartup, "on-disk-startup", false, "Do not initialize on-disk database in memory first at startup")
flag.BoolVar(&config.FKConstraints, "fk", false, "Enable SQLite foreign key constraints")
flag.BoolVar(&config.ShowVersion, "version", false, "Show version information and exit")
flag.BoolVar(&showVersion, "version", false, "Show version information and exit")
flag.BoolVar(&config.RaftNonVoter, "raft-non-voter", false, "Configure as non-voting node")
flag.DurationVar(&config.RaftHeartbeatTimeout, "raft-timeout", time.Second, "Raft heartbeat timeout")
flag.DurationVar(&config.RaftElectionTimeout, "raft-election-timeout", time.Second, "Raft election timeout")
@ -251,8 +335,8 @@ func ParseFlags(name, desc string, build *BuildInfo) (*Config, error) {
}
flag.Parse()
if config.ShowVersion {
msg := fmt.Sprintf("%s %s %s %s %s (commit %s, branch %s, compiler %s)\n",
if showVersion {
msg := fmt.Sprintf("%s %s %s %s %s (commit %s, branch %s, compiler %s)",
name, build.Version, runtime.GOOS, runtime.GOARCH, runtime.Version(), build.Commit, build.Branch, runtime.Compiler)
errorExit(0, msg)
}
@ -268,60 +352,10 @@ func ParseFlags(name, desc string, build *BuildInfo) (*Config, error) {
errorExit(1, "arguments after data directory are not accepted")
}
if config.OnDiskPath != "" && !config.OnDisk {
errorExit(1, "on-disk-path is set, but on-disk is not")
}
// Enforce policies regarding addresses
if config.RaftAdv == "" {
config.RaftAdv = config.RaftAddr
}
if config.HTTPAdv == "" {
config.HTTPAdv = config.HTTPAddr
}
// Perfom some address validity checks.
if strings.HasPrefix(strings.ToLower(config.HTTPAddr), "http") ||
strings.HasPrefix(strings.ToLower(config.HTTPAdv), "http") {
errorExit(1, "HTTP options should not include protocol (http:// or https://)")
}
if _, _, err := net.SplitHostPort(config.HTTPAddr); err != nil {
errorExit(1, "HTTP bind address not valid")
}
if _, _, err := net.SplitHostPort(config.HTTPAdv); err != nil {
errorExit(1, "HTTP advertised address not valid")
}
if _, _, err := net.SplitHostPort(config.RaftAddr); err != nil {
errorExit(1, "raft bind address not valid")
}
if _, _, err := net.SplitHostPort(config.RaftAdv); err != nil {
errorExit(1, "raft advertised address not valid")
}
// Enforce bootstrapping policies
if config.BootstrapExpect > 0 && config.RaftNonVoter {
errorExit(1, "bootstrapping only applicable to voting nodes")
if err := config.Validate(); err != nil {
errorExit(1, err.Error())
}
// Valid disco mode?
switch config.DiscoMode {
case "":
case DiscoModeEtcdKV, DiscoModeConsulKV:
if config.BootstrapExpect > 0 {
errorExit(1, fmt.Sprintf("bootstrapping not applicable when using %s",
config.DiscoMode))
}
default:
errorExit(1, fmt.Sprintf("invalid disco mode, choose %s or %s",
DiscoModeConsulKV, DiscoModeEtcdKV))
}
// Node ID policy
if config.NodeID == "" {
config.NodeID = config.RaftAdv
}
return &config, nil
return config, nil
}
func errorExit(code int, msg string) {

@ -88,13 +88,6 @@ func main() {
log.Fatalf("failed to create store: %s", err.Error())
}
// Determine join addresses
var joins []string
joins, err = determineJoinAddresses(cfg)
if err != nil {
log.Fatalf("unable to determine join addresses: %s", err.Error())
}
// Now, open store.
if err := str.Open(); err != nil {
log.Fatalf("failed to open store: %s", err.Error())
@ -145,7 +138,7 @@ func main() {
if err != nil {
log.Fatalf("failed to get nodes %s", err.Error())
}
if err := createCluster(cfg, joins, &tlsConfig, len(nodes) > 0, str, httpServ, credStr); err != nil {
if err := createCluster(cfg, &tlsConfig, len(nodes) > 0, str, httpServ, credStr); err != nil {
log.Fatalf("clustering failure: %s", err.Error())
}
@ -167,27 +160,6 @@ func main() {
log.Println("rqlite server stopped")
}
// determineJoinAddresses returns the join addresses supplied at the command-line.
// removing any occurence of this nodes HTTP address.
func determineJoinAddresses(cfg *Config) ([]string, error) {
var addrs []string
if cfg.JoinAddr != "" {
addrs = strings.Split(cfg.JoinAddr, ",")
}
// It won't work to attempt an explicit self-join, so remove any such address.
var validAddrs []string
for i := range addrs {
if addrs[i] == cfg.HTTPAdv || addrs[i] == cfg.HTTPAddr {
log.Printf("ignoring join address %s equal to this node's address", addrs[i])
continue
}
validAddrs = append(validAddrs, addrs[i])
}
return validAddrs, nil
}
func createStore(cfg *Config, ln *tcp.Layer) (*store.Store, error) {
dataPath, err := filepath.Abs(cfg.DataPath)
if err != nil {
@ -355,9 +327,10 @@ func clusterService(cfg *Config, tn cluster.Transport, db cluster.Database) (*cl
return c, nil
}
func createCluster(cfg *Config, joins []string, tlsConfig *tls.Config, hasPeers bool, str *store.Store,
func createCluster(cfg *Config, tlsConfig *tls.Config, hasPeers bool, str *store.Store,
httpServ *httpd.Service, credStr *auth.CredentialsStore) error {
if len(joins) == 0 && cfg.DiscoMode == "" && !hasPeers {
joins := cfg.JoinAddresses()
if joins == nil && cfg.DiscoMode == "" && !hasPeers {
// Brand new node, told to bootstrap itself. So do it.
log.Println("bootstraping single new node")
if err := str.Bootstrap(store.NewServer(str.ID(), str.Addr(), true)); err != nil {
@ -366,11 +339,9 @@ func createCluster(cfg *Config, joins []string, tlsConfig *tls.Config, hasPeers
return nil
}
if len(joins) > 0 {
if joins != nil {
if cfg.BootstrapExpect == 0 {
// Explicit join operation requested, so do it.
log.Println("explicit join addresses are:", joins)
if err := addJoinCreds(joins, cfg.JoinAs, credStr); err != nil {
return fmt.Errorf("failed to add BasicAuth creds: %s", err.Error())
}
@ -388,13 +359,10 @@ func createCluster(cfg *Config, joins []string, tlsConfig *tls.Config, hasPeers
return nil
}
// Must self-notify when bootstrapping
targets := append(joins, cfg.HTTPAdv)
log.Println("bootstrap addresses are:", targets)
if err := addJoinCreds(targets, cfg.JoinAs, credStr); err != nil {
if err := addJoinCreds(joins, cfg.JoinAs, credStr); err != nil {
return fmt.Errorf("failed to add BasicAuth creds: %s", err.Error())
}
bs := cluster.NewBootstrapper(cluster.NewAddressProviderString(targets),
bs := cluster.NewBootstrapper(cluster.NewAddressProviderString(joins),
cfg.BootstrapExpect, tlsConfig)
done := func() bool {

Loading…
Cancel
Save