diff --git a/cmd/start.go b/cmd/start.go index 31e00af99..a08e079f9 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -30,13 +30,7 @@ import ( func newStartCommand() *cobra.Command { const defaultKeyPath = "~/.ssh/id_ed25519" - var ( - nodeId, httpAddr, raftAddr, mgmtAddr, joinAddr string - dbPath, raftPath, privateKeyPath string - gossipAddr string - gossipJoinAddr []string - profiling, tampering bool - ) + conf := server.DefaultConfig() cmd := &cobra.Command{ Use: "start", @@ -46,26 +40,12 @@ func newStartCommand() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { - if privateKeyPath == defaultKeyPath { + if conf.PrivateKeyPath == defaultKeyPath { usr, _ := user.Current() - privateKeyPath = fmt.Sprintf("%s/.ssh/id_ed25519", usr.HomeDir) + conf.PrivateKeyPath = fmt.Sprintf("%s/.ssh/id_ed25519", usr.HomeDir) } - srv, err := server.NewServer( - nodeId, - httpAddr, - raftAddr, - mgmtAddr, - joinAddr, - dbPath, - raftPath, - gossipAddr, - gossipJoinAddr, - privateKeyPath, - apiKey, - profiling, - tampering, - ) + srv, err := server.NewServer(conf) if err != nil { log.Fatalf("Can't start QED server: %v", err) @@ -80,20 +60,20 @@ func newStartCommand() *cobra.Command { } hostname, _ := os.Hostname() - cmd.Flags().StringVarP(&nodeId, "node-id", "", hostname, "Unique name for node. If not set, fallback to hostname") - cmd.Flags().StringVarP(&httpAddr, "http-addr", "", ":8080", "Endpoint for REST requests on (host:port)") - cmd.Flags().StringVarP(&raftAddr, "raft-addr", "", ":9000", "Raft bind address (host:port)") - cmd.Flags().StringVarP(&mgmtAddr, "mgmt-addr", "", ":8090", "Management endpoint bind address (host:port)") - cmd.Flags().StringVarP(&joinAddr, "join-addr", "", "", "Raft: Comma-delimited list of nodes ([host]:port), through which a cluster can be joined") - cmd.Flags().StringVarP(&gossipAddr, "gossip-addr", "", ":9100", "Gossip: management endpoint bind address (host:port)") - cmd.Flags().StringSliceVarP(&gossipJoinAddr, "gossip-join-addr", "", []string{}, "Gossip: Comma-delimited list of nodes ([host]:port), through which a cluster can be joined") - cmd.Flags().StringVarP(&dbPath, "dbpath", "p", "/var/tmp/qed/data", "Set default storage path") - cmd.Flags().StringVarP(&raftPath, "raftpath", "", "/var/tmp/qed/raft", "Set raft storage path") - cmd.Flags().StringVarP(&privateKeyPath, "keypath", "y", defaultKeyPath, "Path to the ed25519 key file") - cmd.Flags().BoolVarP(&profiling, "profiling", "f", false, "Allow a pprof url (localhost:6060) for profiling purposes") + cmd.Flags().StringVar(&conf.NodeID, "node-id", hostname, "Unique name for node. If not set, fallback to hostname") + cmd.Flags().StringVar(&conf.HttpAddr, "http-addr", ":8080", "Endpoint for REST requests on (host:port)") + cmd.Flags().StringVar(&conf.RaftAddr, "raft-addr", ":9000", "Raft bind address (host:port)") + cmd.Flags().StringVar(&conf.MgmtAddr, "mgmt-addr", ":8090", "Management endpoint bind address (host:port)") + cmd.Flags().StringSliceVar(&conf.RaftJoinAddr, "join-addr", []string{}, "Raft: Comma-delimited list of nodes ([host]:port), through which a cluster can be joined") + cmd.Flags().StringVar(&conf.GossipAddr, "gossip-addr", ":9100", "Gossip: management endpoint bind address (host:port)") + cmd.Flags().StringSliceVar(&conf.GossipJoinAddr, "gossip-join-addr", []string{}, "Gossip: Comma-delimited list of nodes ([host]:port), through which a cluster can be joined") + cmd.Flags().StringVarP(&conf.DBPath, "dbpath", "p", "/var/tmp/qed/data", "Set default storage path") + cmd.Flags().StringVar(&conf.RaftPath, "raftpath", "/var/tmp/qed/raft", "Set raft storage path") + cmd.Flags().StringVarP(&conf.PrivateKeyPath, "keypath", "y", defaultKeyPath, "Path to the ed25519 key file") + cmd.Flags().BoolVarP(&conf.EnableProfiling, "profiling", "f", false, "Allow a pprof url (localhost:6060) for profiling purposes") // INFO: testing purposes - cmd.Flags().BoolVar(&tampering, "tampering", false, "Allow tampering api for proof demostrations") + cmd.Flags().BoolVar(&conf.EnableTampering, "tampering", false, "Allow tampering api for proof demostrations") cmd.Flags().MarkHidden("tampering") return cmd diff --git a/go.sum b/go.sum index 5751c6d79..047112bc3 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,7 @@ cloud.google.com/go v0.28.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 h1:PqzgE6kAMi81xWQA2QIVxjWkFHptGgC547vchpUbtFo= github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/VictoriaMetrics/fastcache v1.0.2 h1:dj179vXczNtKDLjmiOHRasMMv437rPpUtX/bpojmyyc= github.com/VictoriaMetrics/fastcache v1.0.2/go.mod h1:4zLf+tCNHtaJzQfIkJ+vBXS1O98gXA/KbKtUv/W1Tdo= github.com/allegro/bigcache v1.1.0/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/armon/go-metrics v0.0.0-20180713145231-3c58d8115a78 h1:mdRSArcFLfW0VoL34LZAKSz6LkkK4jFxVx2xYavACMg= diff --git a/gossip/config.go b/gossip/config.go index f1017d166..b781fb569 100644 --- a/gossip/config.go +++ b/gossip/config.go @@ -83,7 +83,7 @@ type Config struct { // we leave. LeavePropagateDelay time.Duration - // MemberlistConfig is the memberlist configuration that Aidotpr will + // MemberlistConfig is the memberlist configuration that Agent will // use to do the underlying membership management and gossip. Some // fields in the MemberlistConfig will be overwritten by Auditor no // matter what: diff --git a/server/config.go b/server/config.go new file mode 100644 index 000000000..6d88abfd4 --- /dev/null +++ b/server/config.go @@ -0,0 +1,106 @@ +/* + Copyright 2018 Banco Bilbao Vizcaya Argentaria, S.A. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package server + +import ( + "net" + "os" + "path/filepath" +) + +type Config struct { + // Unique name for this node. It identifies itself both in raft and + // gossip clusters. If not set, fallback to hostname. + NodeID string + + // HTTP server bind address/port. + HttpAddr string + + // Raft communication bind address/port. + RaftAddr string + + // Raft management server bind address/port. Useful to join the cluster + // and get cluster information. + MgmtAddr string + + // List of raft nodes, through which a cluster can be joined + // (protocol://host:port). + RaftJoinAddr []string + + // Path to storage directory. + DBPath string + + // Path to Raft storage directory. + RaftPath string + + // Gossip management server bind address/port. + GossipAddr string + + // List of nodes, through which a gossip cluster can be joined (protocol://host:port). + GossipJoinAddr []string + + // Path to the private key file used to sign snapshots. + PrivateKeyPath string + + // Enables profiling endpoint. + EnableProfiling bool + + // Enables tampering endpoint. + EnableTampering bool +} + +func DefaultConfig() *Config { + hostname, _ := os.Hostname() + currentDir := getCurrentDir() + return &Config{ + NodeID: hostname, + HttpAddr: "127.0.0.1:8080", + RaftAddr: "127.0.0.1:9000", + MgmtAddr: "127.0.0.1:8090", + RaftJoinAddr: []string{}, + GossipAddr: "127.0.0.1:9100", + GossipJoinAddr: []string{}, + DBPath: currentDir + "/data", + RaftPath: currentDir + "/raft", + EnableProfiling: false, + EnableTampering: false, + } +} + +func getCurrentDir() string { + ex, err := os.Executable() + if err != nil { + panic(err) + } + exPath := filepath.Dir(ex) + return exPath +} + +// AddrParts returns the parts of and address/port. +func addrParts(address string) (string, int, error) { + _, _, err := net.SplitHostPort(address) + if err != nil { + return "", 0, err + } + + addr, err := net.ResolveTCPAddr("tcp", address) + if err != nil { + return "", 0, err + } + + return addr.IP.String(), addr.Port, nil +} diff --git a/server/server.go b/server/server.go index 7ecab0ec1..a09e8d0a8 100644 --- a/server/server.go +++ b/server/server.go @@ -44,27 +44,10 @@ import ( "github.com/bbva/qed/storage/badger" ) -type Store interface { - Add(key []byte, value []byte) error - GetRange(start, end []byte) [][]byte - Get(key []byte) ([]byte, error) - Close() error -} - // Server encapsulates the data and login to start/stop a QED server type Server struct { - nodeID string // unique name for node. If not set, fallback to hostname - httpAddr string // HTTP server bind address - raftAddr string // Raft communication bind address - mgmtAddr string // Raft: management server bind address - joinAddr string // Comma-delimited list of nodes, through which a cluster can be joined (protocol://host:port) - dbPath string // Path to storage directory - raftPath string // Path to Raft storage directory - gossipAddr string // Gossip: management server bind address - gossipJoinAddr []string // Gossip: Comma-delimited list of nodes, through which a cluster can be joined (protocol://host:port) - privateKeyPath string // Path to the private key file used to sign snapshot - apiKey string - bootstrap bool // Set bootstrap to true when bringing up the first node as a master + conf *Config + bootstrap bool // Set bootstrap to true when bringing up the first node as a master httpServer *http.Server mgmtServer *http.Server @@ -77,75 +60,52 @@ type Server struct { agentsQueue chan *protocol.Snapshot } -// NewServer synthesizes a new Server based on the parameters it receives. -func NewServer( - nodeID string, - httpAddr string, - raftAddr string, - mgmtAddr string, - joinAddr string, - dbPath string, - raftPath string, - gossipAddr string, - gossipJoinAddr []string, - privateKeyPath string, - apiKey string, - enableProfiling bool, - enableTampering bool, -) (*Server, error) { +// NewServer creates a new Server based on the parameters it receives. +func NewServer(conf *Config) (*Server, error) { bootstrap := false - if joinAddr == "" { + if len(conf.RaftJoinAddr) <= 0 { bootstrap = true } server := &Server{ - nodeID: nodeID, - httpAddr: httpAddr, - raftAddr: raftAddr, - mgmtAddr: mgmtAddr, - joinAddr: joinAddr, - dbPath: dbPath, - raftPath: raftPath, - gossipAddr: gossipAddr, - gossipJoinAddr: gossipJoinAddr, - apiKey: apiKey, - bootstrap: bootstrap, + conf: conf, + bootstrap: bootstrap, } - log.Infof("ensuring directory at %s exists", dbPath) - if err := os.MkdirAll(dbPath, 0755); err != nil { + log.Infof("ensuring directory at %s exists", conf.DBPath) + if err := os.MkdirAll(conf.DBPath, 0755); err != nil { return nil, err } - log.Infof("ensuring directory at %s exists", raftPath) - if err := os.MkdirAll(raftPath, 0755); err != nil { + log.Infof("ensuring directory at %s exists", conf.RaftPath) + if err := os.MkdirAll(conf.RaftPath, 0755); err != nil { return nil, err } // Open badger store - store, err := badger.NewBadgerStoreOpts(&badger.Options{Path: dbPath, ValueLogGC: true}) + store, err := badger.NewBadgerStoreOpts(&badger.Options{Path: conf.DBPath, ValueLogGC: true}) if err != nil { return nil, err } // Create signer - server.signer, err = sign.NewEd25519SignerFromFile(privateKeyPath) + server.signer, err = sign.NewEd25519SignerFromFile(conf.PrivateKeyPath) if err != nil { return nil, err } // Create gossip agent config := gossip.DefaultConfig() - config.BindAddr = gossipAddr + config.BindAddr = conf.GossipAddr config.Role = member.Server server.agent, err = gossip.NewAgent(config, nil) if err != nil { return nil, err } - if len(gossipJoinAddr) > 0 { - server.agent.Join(gossipJoinAddr) + if len(conf.GossipJoinAddr) > 0 { + server.agent.Join(conf.GossipJoinAddr) } // TODO: add queue size to config @@ -155,21 +115,21 @@ func NewServer( server.sender = sender.NewSender(server.agent, sender.DefaultConfig(), server.signer) // Create RaftBalloon - server.raftBalloon, err = raftwal.NewRaftBalloon(raftPath, raftAddr, nodeID, store, server.agentsQueue) + server.raftBalloon, err = raftwal.NewRaftBalloon(conf.RaftPath, conf.RaftAddr, conf.NodeID, store, server.agentsQueue) if err != nil { return nil, err } // Create http endpoints - server.httpServer = newHTTPServer(server.httpAddr, server.raftBalloon) + server.httpServer = newHTTPServer(conf.HttpAddr, server.raftBalloon) // Create management endpoints - server.mgmtServer = newMgmtServer(server.mgmtAddr, server.raftBalloon) + server.mgmtServer = newMgmtServer(conf.MgmtAddr, server.raftBalloon) - if enableTampering { + if conf.EnableTampering { server.tamperingServer = newTamperingServer("localhost:8081", store, hashing.NewSha256Hasher()) } - if enableProfiling { + if conf.EnableProfiling { server.profilingServer = newProfilingServer("localhost:6060") } @@ -219,25 +179,25 @@ func (s *Server) Start() error { } go func() { - log.Debug(" * Starting QED API HTTP server in addr: ", s.httpAddr) + log.Debug(" * Starting QED API HTTP server in addr: ", s.conf.HttpAddr) if err := s.httpServer.ListenAndServe(); err != http.ErrServerClosed { log.Errorf("Can't start QED API HTTP Server: %s", err) } }() go func() { - log.Debug(" * Starting QED MGMT HTTP server in addr: ", s.mgmtAddr) + log.Debug(" * Starting QED MGMT HTTP server in addr: ", s.conf.MgmtAddr) if err := s.mgmtServer.ListenAndServe(); err != http.ErrServerClosed { log.Errorf("Can't start QED MGMT HTTP Server: %s", err) } }() - log.Debugf(" ready on %s and %s\n", s.httpAddr, s.mgmtAddr) + log.Debugf(" ready on %s and %s\n", s.conf.HttpAddr, s.conf.MgmtAddr) if !s.bootstrap { - log.Debug(" * Joining existent cluster QED MGMT HTTP server in addr: ", s.mgmtAddr) - if err := join(s.joinAddr, s.raftAddr, s.nodeID); err != nil { - log.Fatalf("failed to join node at %s: %s", s.joinAddr, err.Error()) + log.Debug(" * Joining existent cluster QED MGMT HTTP server in addr: ", s.conf.MgmtAddr) + if err := join(s.conf.RaftJoinAddr[0], s.conf.RaftAddr, s.conf.NodeID); err != nil { + log.Fatalf("failed to join node at %s: %s", s.conf.RaftJoinAddr[0], err.Error()) } } diff --git a/tests/e2e/setup.go b/tests/e2e/setup.go index bc3ffa940..1f2af257c 100644 --- a/tests/e2e/setup.go +++ b/tests/e2e/setup.go @@ -49,15 +49,21 @@ func setup(id int, joinAddr string, t *testing.T) (scope.TestF, scope.TestF) { os.MkdirAll(path, os.FileMode(0755)) hostname, _ := os.Hostname() - nodeId := fmt.Sprintf("%s-%d", hostname, id) - httpAddr := fmt.Sprintf("127.0.0.1:850%d", id) - raftAddr := fmt.Sprintf("127.0.0.1:830%d", id) - mgmtAddr := fmt.Sprintf("127.0.0.1:840%d", id) - gossipAddr := fmt.Sprintf("127.0.0.1:860%d", id) - gossipJoinAddr := []string{} - dbPath := path + "data" - raftPath := path + "raft" - srv, err = server.NewServer(nodeId, httpAddr, raftAddr, mgmtAddr, joinAddr, dbPath, raftPath, gossipAddr, gossipJoinAddr, keyFile, apiKey, true, true) + conf := server.DefaultConfig() + conf.NodeID = fmt.Sprintf("%s-%d", hostname, id) + conf.HttpAddr = fmt.Sprintf("127.0.0.1:850%d", id) + conf.RaftAddr = fmt.Sprintf("127.0.0.1:830%d", id) + conf.MgmtAddr = fmt.Sprintf("127.0.0.1:840%d", id) + conf.GossipAddr = fmt.Sprintf("127.0.0.1:860%d", id) + conf.DBPath = path + "data" + conf.RaftPath = path + "raft" + conf.PrivateKeyPath = keyFile + conf.EnableProfiling = true + conf.EnableTampering = true + + fmt.Printf("%+v", conf) + + srv, err = server.NewServer(conf) if err != nil { t.Fatalf("Unable to create a new server: %v", err) }