consul: dev mode works

This commit is contained in:
Ryan Uber 2015-11-28 20:40:05 -08:00
parent 256a2ad36a
commit ce4cf8a542
5 changed files with 98 additions and 41 deletions

View File

@ -124,7 +124,7 @@ func Create(config *Config, logOutput io.Writer) (*Agent, error) {
if config.Datacenter == "" {
return nil, fmt.Errorf("Must configure a Datacenter")
}
if config.DataDir == "" {
if config.DataDir == "" && !config.DevMode {
return nil, fmt.Errorf("Must configure a DataDir")
}
@ -227,6 +227,9 @@ func (a *Agent) consulConfig() *consul.Config {
base = consul.DefaultConfig()
}
// Apply dev mode
base.DevMode = a.config.DevMode
// Override with our config
if a.config.Datacenter != "" {
base.Datacenter = a.config.Datacenter
@ -748,7 +751,7 @@ func (a *Agent) AddService(service *structs.NodeService, chkTypes CheckTypes, pe
a.state.AddService(service, token)
// Persist the service to a file
if persist {
if persist && !a.config.DevMode {
if err := a.persistService(service); err != nil {
return err
}
@ -958,7 +961,7 @@ func (a *Agent) AddCheck(check *structs.HealthCheck, chkType *CheckType, persist
a.state.AddCheck(check, token)
// Persist the check
if persist {
if persist && !a.config.DevMode {
return a.persistCheck(check, chkType)
}
@ -1022,6 +1025,10 @@ func (a *Agent) UpdateCheck(checkID, status, output string) error {
// Set the status through CheckTTL to reset the TTL
check.SetStatus(status, output)
if a.config.DevMode {
return nil
}
// Always persist the state for TTL checks
if err := a.persistCheckState(check, status, output); err != nil {
return fmt.Errorf("failed persisting state for check %q: %s", checkID, err)

View File

@ -60,12 +60,14 @@ func (c *Command) readConfig() *Config {
var retryInterval string
var retryIntervalWan string
var dnsRecursors []string
var dev bool
cmdFlags := flag.NewFlagSet("agent", flag.ContinueOnError)
cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
cmdFlags.Var((*AppendSliceValue)(&configFiles), "config-file", "json file to read config from")
cmdFlags.Var((*AppendSliceValue)(&configFiles), "config-dir", "directory of json files to read")
cmdFlags.Var((*AppendSliceValue)(&dnsRecursors), "recursor", "address of an upstream DNS server")
cmdFlags.BoolVar(&dev, "dev", false, "development server mode")
cmdFlags.StringVar(&cmdConfig.LogLevel, "log-level", "", "log level")
cmdFlags.StringVar(&cmdConfig.NodeName, "node", "", "node name")
@ -137,7 +139,13 @@ func (c *Command) readConfig() *Config {
cmdConfig.RetryIntervalWan = dur
}
config := DefaultConfig()
var config *Config
if dev {
config = DevConfig()
} else {
config = DefaultConfig()
}
if len(configFiles) > 0 {
fileConfig, err := ReadConfigPaths(configFiles)
if err != nil {
@ -162,7 +170,7 @@ func (c *Command) readConfig() *Config {
}
// Ensure we have a data directory
if config.DataDir == "" {
if config.DataDir == "" && !dev {
c.Ui.Error("Must specify data directory using -data-dir")
return nil
}

View File

@ -94,6 +94,10 @@ type DNSConfig struct {
// Some of this is configurable as CLI flags, but most must
// be set using a configuration file.
type Config struct {
// DevMode enables a fast-path mode of opertaion to bring up an in-memory
// server with minimal configuration. Useful for developing Consul.
DevMode bool `mapstructure:"-"`
// Bootstrap is used to bring up the first Consul server, and
// permits that node to elect itself leader
Bootstrap bool `mapstructure:"bootstrap"`
@ -521,6 +525,17 @@ func DefaultConfig() *Config {
}
}
// DevConfig is used to return a set of configuration to use for dev mode.
func DevConfig() *Config {
conf := DefaultConfig()
conf.DevMode = true
conf.LogLevel = "DEBUG"
conf.Server = true
conf.EnableDebug = true
conf.DisableAnonymousSignature = true
return conf
}
// EncryptBytes returns the encryption key configured.
func (c *Config) EncryptBytes() ([]byte, error) {
return base64.StdEncoding.DecodeString(c.EncryptKey)

View File

@ -54,6 +54,9 @@ type Config struct {
// DataDir is the directory to store our state in
DataDir string
// DevMode is used to enable a development server mode.
DevMode bool
// Node name is the name we use to advertise. Defaults to hostname.
NodeName string

View File

@ -110,6 +110,7 @@ type Server struct {
raftPeers raft.PeerStore
raftStore *raftboltdb.BoltStore
raftTransport *raft.NetworkTransport
raftInmem *raft.InmemStore
// reconcileCh is used to pass events from the serf handler
// into the leader manager, so that the strong state can be
@ -173,7 +174,7 @@ func NewServer(config *Config) (*Server, error) {
}
// Check for a data directory!
if config.DataDir == "" {
if config.DataDir == "" && !config.DevMode {
return nil, fmt.Errorf("Config must provide a DataDir")
}
@ -327,7 +328,7 @@ func (s *Server) setupSerf(conf *serf.Config, ch chan serf.Event, path string, w
// setupRaft is used to setup and initialize Raft
func (s *Server) setupRaft() error {
// If we are in bootstrap mode, enable a single node cluster
if s.config.Bootstrap {
if s.config.Bootstrap || s.config.DevMode {
s.config.RaftConfig.EnableSingleNode = true
}
@ -338,45 +339,66 @@ func (s *Server) setupRaft() error {
return err
}
// Create the base raft path
path := filepath.Join(s.config.DataDir, raftState)
if err := ensurePath(path, true); err != nil {
return err
}
// Create the backend raft store for logs and stable storage
store, err := raftboltdb.NewBoltStore(filepath.Join(path, "raft.db"))
if err != nil {
return err
}
s.raftStore = store
// Wrap the store in a LogCache to improve performance
cacheStore, err := raft.NewLogCache(raftLogCacheSize, store)
if err != nil {
store.Close()
return err
}
// Create the snapshot store
snapshots, err := raft.NewFileSnapshotStore(path, snapshotsRetained, s.config.LogOutput)
if err != nil {
store.Close()
return err
}
// Create a transport layer
trans := raft.NewNetworkTransport(s.raftLayer, 3, 10*time.Second, s.config.LogOutput)
s.raftTransport = trans
// Setup the peer store
s.raftPeers = raft.NewJSONPeers(path, trans)
var log raft.LogStore
var stable raft.StableStore
var snap raft.SnapshotStore
var peers raft.PeerStore
if s.config.DevMode {
store := raft.NewInmemStore()
s.raftInmem = store
stable = store
log = store
snap = raft.NewDiscardSnapshotStore()
peers = &raft.StaticPeers{}
s.raftPeers = peers
} else {
// Create the base raft path
path := filepath.Join(s.config.DataDir, raftState)
if err := ensurePath(path, true); err != nil {
return err
}
// Create the backend raft store for logs and stable storage
store, err := raftboltdb.NewBoltStore(filepath.Join(path, "raft.db"))
if err != nil {
return err
}
s.raftStore = store
stable = store
// Wrap the store in a LogCache to improve performance
cacheStore, err := raft.NewLogCache(raftLogCacheSize, store)
if err != nil {
store.Close()
return err
}
log = cacheStore
// Create the snapshot store
snapshots, err := raft.NewFileSnapshotStore(path, snapshotsRetained, s.config.LogOutput)
if err != nil {
store.Close()
return err
}
snap = snapshots
// Setup the peer store
s.raftPeers = raft.NewJSONPeers(path, trans)
peers = s.raftPeers
}
// Ensure local host is always included if we are in bootstrap mode
if s.config.Bootstrap {
peers, err := s.raftPeers.Peers()
if err != nil {
store.Close()
if s.raftStore != nil {
s.raftStore.Close()
}
return err
}
if !raft.PeerContained(peers, trans.LocalAddr()) {
@ -388,10 +410,10 @@ func (s *Server) setupRaft() error {
s.config.RaftConfig.LogOutput = s.config.LogOutput
// Setup the Raft store
s.raft, err = raft.NewRaft(s.config.RaftConfig, s.fsm, cacheStore, store,
snapshots, s.raftPeers, trans)
s.raft, err = raft.NewRaft(s.config.RaftConfig, s.fsm, log, stable,
snap, s.raftPeers, trans)
if err != nil {
store.Close()
s.raftStore.Close()
trans.Close()
return err
}
@ -484,7 +506,9 @@ func (s *Server) Shutdown() error {
if err := future.Error(); err != nil {
s.logger.Printf("[WARN] consul: Error shutting down raft: %s", err)
}
s.raftStore.Close()
if s.raftStore != nil {
s.raftStore.Close()
}
// Clear the peer set on a graceful leave to avoid
// triggering elections on a rejoin.