func (c *RaftNodeConfig) VerifyBootstrap() error {
	if err := c.hasLocalMember(); err != nil {
		return err
	}
	if err := c.advertiseMatchesCluster(); err != nil {
		return err
	}
	if checkDuplicateURL(c.InitialPeerURLsMap) {
		return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap)
	}
	if c.InitialPeerURLsMap.String() == "" {
		return fmt.Errorf("initial cluster unset and no discovery URL found")
	}
	if c.ReserveDataSize < 4 {
		return fmt.Errorf("reserve data size is too small")
	}
	return nil
}
VerifyBootstrap sanity-checks the initial config for bootstrap case
and returns an error for things that should never happen.
package config
import (
	"context"
	"time"
	"errors"
	"fmt"
	"sort"
	"strings"
	"github.com/coreos/etcd/pkg/netutil"
	"github.com/coreos/etcd/pkg/types"
	"github.com/siddontang/go-mysql/replication"
	"github.com/spf13/viper"
)
const (
	ClusterStateFlagNew = "new"
	ClusterStateFlagExisting = "existing"
)
const (
	MaxRequestBytes uint64 = 20 * 1024 * 1024
)
type KingbusServerConfig struct {
	RaftNodeCfg RaftNodeConfig
	AdminURLs   types.URLs
	MetricsAddr string
	LogDir      string
	LogLevel    string
}
type SyncerConfig struct {
	replication.BinlogSyncerConfig
	MaxEventBytes uint64
}
type RaftNodeConfig struct {
	Name                string
	AppliedIndex        uint64
	PeerURLs            types.URLs
	InitialPeerURLsMap  types.URLsMap
	MaxRequestBytes     uint64
	HeartbeatMs         time.Duration
	ElectionTimeoutMs   time.Duration
	NewCluster          bool
	PreVote             bool
	DataDir             string
	ReserveDataSize     int
	StrictReconfigCheck bool
}
func NewKingbusServerConfig(configPath string) (*KingbusServerConfig, error) {
	if configPath == "" {
		return nil, errors.New("config path is nil")
	}
	viper.SetConfigFile(configPath)
	if err := viper.ReadInConfig(); err != nil {
		return nil, err
	}
	raftNodeCfg, err := getRaftNodeConfig()
	if err != nil {
		return nil, err
	}
	adminURLs, err := types.NewURLs([]string{viper.GetString("admin-url")})
	if err != nil {
		return nil, err
	}
	if len(adminURLs) != 1 {
		return nil, fmt.Errorf("adminURLs muster be 1")
	}
	ksCfg := &KingbusServerConfig{
		RaftNodeCfg: *raftNodeCfg,
		AdminURLs:   adminURLs,
		MetricsAddr: viper.GetString("metrics-addr"),
		LogDir:      viper.GetString("log-dir"),
		LogLevel:    viper.GetString("log-level"),
	}
	return ksCfg, nil
}
func getRaftNodeConfig() (*RaftNodeConfig, error) {
	peerURLs, err := types.NewURLs([]string{viper.GetString("peer-url")})
	if err != nil {
		return nil, err
	}
	initialPeerURLsMap, err := types.NewURLsMap(viper.GetString("initial-cluster"))
	if err != nil {
		return nil, err
	}
	clusterStatus := viper.GetString("initial-cluster-state")
	raftNodeCfg := &RaftNodeConfig{
		Name:                viper.GetString("name"),
		PeerURLs:            peerURLs,
		InitialPeerURLsMap:  initialPeerURLsMap,
		MaxRequestBytes:     MaxRequestBytes,
		HeartbeatMs:         viper.GetDuration("heartbeat-interval"),
		ElectionTimeoutMs:   viper.GetDuration("election-timeout"),
		PreVote:             true,
		DataDir:             viper.GetString("data-dir"),
		ReserveDataSize:     viper.GetInt("reserve-data-size"),
		StrictReconfigCheck: true,
		NewCluster:          clusterStatus == ClusterStateFlagNew,
	}
	return raftNodeCfg, nil
}