From afafae53fd790989686229a2b089cdd78eee9b9e Mon Sep 17 00:00:00 2001 From: Ryan Uber Date: Sat, 28 Nov 2015 20:40:05 -0800 Subject: [PATCH] consul: dev mode works --- command/agent/agent.go | 13 ++++-- command/agent/command.go | 12 ++++- command/agent/config.go | 15 +++++++ consul/config.go | 3 ++ consul/server.go | 96 +++++++++++++++++++++++++--------------- 5 files changed, 98 insertions(+), 41 deletions(-) diff --git a/command/agent/agent.go b/command/agent/agent.go index afe247e1d0..b74a1dcf10 100644 --- a/command/agent/agent.go +++ b/command/agent/agent.go @@ -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) diff --git a/command/agent/command.go b/command/agent/command.go index 050be0d3c8..ffdfcef070 100644 --- a/command/agent/command.go +++ b/command/agent/command.go @@ -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 } diff --git a/command/agent/config.go b/command/agent/config.go index 79836f8811..b00fa0f870 100644 --- a/command/agent/config.go +++ b/command/agent/config.go @@ -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) diff --git a/consul/config.go b/consul/config.go index bfb3fa809b..78b3fc8753 100644 --- a/consul/config.go +++ b/consul/config.go @@ -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 diff --git a/consul/server.go b/consul/server.go index eec5802127..7c41f20cd5 100644 --- a/consul/server.go +++ b/consul/server.go @@ -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.