// NewServer creates a new EtcdServer from the supplied configuration. The // configuration is considered static for the lifetime of the EtcdServer. func NewServer(cfg *ServerConfig) (*EtcdServer, error) { st := store.New(StoreAdminPrefix, StoreKeysPrefix) var w *wal.WAL var n raft.Node var s *raft.MemoryStorage var id types.ID walVersion, err := wal.DetectVersion(cfg.DataDir) if err != nil { return nil, err } if walVersion == wal.WALUnknown { return nil, fmt.Errorf("unknown wal version in data dir %s", cfg.DataDir) } haveWAL := walVersion != wal.WALNotExist ss := snap.New(cfg.SnapDir()) switch { case !haveWAL && !cfg.NewCluster: existingCluster, err := GetClusterFromRemotePeers(getRemotePeerURLs(cfg.Cluster, cfg.Name), cfg.Transport) if err != nil { return nil, fmt.Errorf("cannot fetch cluster info from peer urls: %v", err) } if err := ValidateClusterAndAssignIDs(cfg.Cluster, existingCluster); err != nil { return nil, fmt.Errorf("error validating peerURLs %s: %v", existingCluster, err) } cfg.Cluster.UpdateIndex(existingCluster.index) cfg.Cluster.SetID(existingCluster.id) cfg.Cluster.SetStore(st) cfg.Print() id, n, s, w = startNode(cfg, nil) case !haveWAL && cfg.NewCluster: if err := cfg.VerifyBootstrapConfig(); err != nil { return nil, err } m := cfg.Cluster.MemberByName(cfg.Name) if isMemberBootstrapped(cfg.Cluster, cfg.Name, cfg.Transport) { return nil, fmt.Errorf("member %s has already been bootstrapped", m.ID) } if cfg.ShouldDiscover() { str, err := discovery.JoinCluster(cfg.DiscoveryURL, cfg.DiscoveryProxy, m.ID, cfg.Cluster.String()) if err != nil { return nil, err } if cfg.Cluster, err = NewClusterFromString(cfg.Cluster.token, str); err != nil { return nil, err } if err := cfg.Cluster.Validate(); err != nil { return nil, fmt.Errorf("bad discovery cluster: %v", err) } } cfg.Cluster.SetStore(st) cfg.PrintWithInitial() id, n, s, w = startNode(cfg, cfg.Cluster.MemberIDs()) case haveWAL: // Run the migrations. if err := upgradeWAL(cfg.DataDir, cfg.Name, walVersion); err != nil { return nil, err } if err := fileutil.IsDirWriteable(cfg.DataDir); err != nil { return nil, fmt.Errorf("cannot write to data directory: %v", err) } if err := fileutil.IsDirWriteable(cfg.MemberDir()); err != nil { return nil, fmt.Errorf("cannot write to member directory: %v", err) } if cfg.ShouldDiscover() { log.Printf("etcdserver: discovery token ignored since a cluster has already been initialized. Valid log found at %q", cfg.WALDir()) } snapshot, err := ss.Load() if err != nil && err != snap.ErrNoSnapshot { return nil, err } if snapshot != nil { if err := st.Recovery(snapshot.Data); err != nil { log.Panicf("etcdserver: recovered store from snapshot error: %v", err) } log.Printf("etcdserver: recovered store from snapshot at index %d", snapshot.Metadata.Index) } cfg.Cluster = NewClusterFromStore(cfg.Cluster.token, st) cfg.Print() if snapshot != nil { log.Printf("etcdserver: loaded cluster information from store: %s", cfg.Cluster) } if !cfg.ForceNewCluster { id, n, s, w = restartNode(cfg, snapshot) } else { id, n, s, w = restartAsStandaloneNode(cfg, snapshot) } default: return nil, fmt.Errorf("unsupported bootstrap config") } sstats := &stats.ServerStats{ Name: cfg.Name, ID: id.String(), } lstats := stats.NewLeaderStats(id.String()) srv := &EtcdServer{ cfg: cfg, snapCount: cfg.SnapCount, errorc: make(chan error, 1), store: st, r: raftNode{ Node: n, ticker: time.Tick(time.Duration(cfg.TickMs) * time.Millisecond), raftStorage: s, storage: NewStorage(w, ss), }, id: id, attributes: Attributes{Name: cfg.Name, ClientURLs: cfg.ClientURLs.StringSlice()}, Cluster: cfg.Cluster, stats: sstats, lstats: lstats, SyncTicker: time.Tick(500 * time.Millisecond), reqIDGen: idutil.NewGenerator(uint8(id), time.Now()), } tr := rafthttp.NewTransporter(cfg.Transport, id, cfg.Cluster.ID(), srv, srv.errorc, sstats, lstats) // add all the remote members into sendhub for _, m := range cfg.Cluster.Members() { if m.ID != id { tr.AddPeer(m.ID, m.PeerURLs) } } srv.r.transport = tr return srv, nil }
func checkInternalVersion(fs *flag.FlagSet) version { // If it uses 2.0 env var explicitly, start 2.0 for _, name := range v2SpecialFlags { if fs.Lookup(name).Value.String() != "" { return internalV2 } } dataDir := fs.Lookup("data-dir").Value.String() if dataDir == "" { fmt.Printf("etcd-starter: data-dir is not set\n") return internalV2 } // check the data directory dataver, err := wal.DetectVersion(dataDir) if err != nil { log.Fatalf("etcd-starter: failed to detect etcd version in %v: %v", dataDir, err) } fmt.Printf("etcd-starter: detected etcd version %s in %s\n", dataver, dataDir) switch dataver { case wal.WALv2_0: return internalV2 case wal.WALv2_0_1: return internalV2 case wal.WALv2_0Proxy: return internalV2Proxy case wal.WALv0_4: standbyInfo, err := migrate.DecodeStandbyInfo4FromFile(standbyInfo4(dataDir)) if err != nil && !os.IsNotExist(err) { log.Fatalf("etcd-starter: failed to decode standbyInfo in %v: %v", dataDir, err) } inStandbyMode := standbyInfo != nil && standbyInfo.Running if inStandbyMode { ver, err := checkInternalVersionByClientURLs(standbyInfo.ClientURLs(), clientTLSInfo(fs)) if err != nil { fmt.Printf("etcd-starter: failed to check start version through peers: %v\n", err) return internalV1 } if ver == internalV2 { osutil.Unsetenv("ETCD_DISCOVERY") os.Args = append(os.Args, "-initial-cluster", standbyInfo.InitialCluster()) return internalV2Proxy } return ver } ver, err := checkInternalVersionByDataDir4(dataDir) if err != nil { log.Fatalf("etcd-starter: failed to check start version in %v: %v", dataDir, err) } return ver case wal.WALNotExist: discovery := fs.Lookup("discovery").Value.String() dpeers, err := getPeersFromDiscoveryURL(discovery) if err != nil { fmt.Printf("etcd-starter: failed to get peers from discovery %s: %v\n", discovery, err) } peerStr := fs.Lookup("peers").Value.String() ppeers := getPeersFromPeersFlag(peerStr, peerTLSInfo(fs)) urls := getClientURLsByPeerURLs(append(dpeers, ppeers...), peerTLSInfo(fs)) ver, err := checkInternalVersionByClientURLs(urls, clientTLSInfo(fs)) if err != nil { fmt.Printf("etcd-starter: failed to check start version through peers: %v\n", err) return internalV2 } return ver case wal.WALUnknown: fmt.Printf("etcd-starter: unrecognized contents in data directory %s\n", dataDir) return internalV2 } // never reach here log.Panicf("etcd-starter: unhandled etcd version in %v", dataDir) return internalUnknown }