// NewNode creates a new raft node to use for tests func NewNode(t *testing.T, clockSource *fakeclock.FakeClock, tc *cautils.TestCA, opts ...raft.NodeOptions) *TestNode { l, err := net.Listen("tcp", "127.0.0.1:0") require.NoError(t, err, "can't bind to raft service port") wrappedListener := NewWrappedListener(l) securityConfig, err := tc.NewNodeConfig(ca.ManagerRole) require.NoError(t, err) serverOpts := []grpc.ServerOption{grpc.Creds(securityConfig.ServerTLSCreds)} s := grpc.NewServer(serverOpts...) cfg := raft.DefaultNodeConfig() stateDir, err := ioutil.TempDir("", "test-raft") require.NoError(t, err, "can't create temporary state directory") keyRotator := NewSimpleKeyRotator(raft.EncryptionKeys{CurrentDEK: []byte("current")}) newNodeOpts := raft.NodeOptions{ ID: securityConfig.ClientTLSCreds.NodeID(), Addr: l.Addr().String(), Config: cfg, StateDir: stateDir, ClockSource: clockSource, TLSCredentials: securityConfig.ClientTLSCreds, KeyRotator: keyRotator, } if len(opts) > 1 { panic("more than one optional argument provided") } if len(opts) == 1 { newNodeOpts.JoinAddr = opts[0].JoinAddr if opts[0].Addr != "" { newNodeOpts.Addr = opts[0].Addr } } n := raft.NewNode(newNodeOpts) healthServer := health.NewHealthServer() api.RegisterHealthServer(s, healthServer) raft.Register(s, n) go func() { // After stopping, we should receive an error from Serve assert.Error(t, s.Serve(wrappedListener)) }() healthServer.SetServingStatus("Raft", api.HealthCheckResponse_SERVING) return &TestNode{ Node: n, Listener: wrappedListener, SecurityConfig: securityConfig, Address: newNodeOpts.Addr, StateDir: newNodeOpts.StateDir, Server: s, KeyRotator: keyRotator, } }
// CopyNode returns a copy of a node func CopyNode(t *testing.T, clockSource *fakeclock.FakeClock, oldNode *TestNode, forceNewCluster bool, kr *SimpleKeyRotator) (*TestNode, context.Context) { wrappedListener := RecycleWrappedListener(oldNode.Listener) securityConfig := oldNode.SecurityConfig serverOpts := []grpc.ServerOption{grpc.Creds(securityConfig.ServerTLSCreds)} s := grpc.NewServer(serverOpts...) cfg := raft.DefaultNodeConfig() if kr == nil { kr = oldNode.KeyRotator } newNodeOpts := raft.NodeOptions{ ID: securityConfig.ClientTLSCreds.NodeID(), Addr: oldNode.Address, Config: cfg, StateDir: oldNode.StateDir, ForceNewCluster: forceNewCluster, ClockSource: clockSource, SendTimeout: 2 * time.Second, TLSCredentials: securityConfig.ClientTLSCreds, KeyRotator: kr, } ctx, cancel := context.WithCancel(context.Background()) n := raft.NewNode(newNodeOpts) healthServer := health.NewHealthServer() api.RegisterHealthServer(s, healthServer) raft.Register(s, n) go func() { // After stopping, we should receive an error from Serve require.Error(t, s.Serve(wrappedListener)) }() healthServer.SetServingStatus("Raft", api.HealthCheckResponse_SERVING) return &TestNode{ Node: n, Listener: wrappedListener, SecurityConfig: securityConfig, Address: newNodeOpts.Addr, StateDir: newNodeOpts.StateDir, cancel: cancel, Server: s, KeyRotator: kr, }, ctx }
// RestartNode restarts a raft test node func RestartNode(t *testing.T, clockSource *fakeclock.FakeClock, oldNode *TestNode, forceNewCluster bool) *TestNode { wrappedListener := RecycleWrappedListener(oldNode.Listener) securityConfig := oldNode.SecurityConfig serverOpts := []grpc.ServerOption{grpc.Creds(securityConfig.ServerTLSCreds)} s := grpc.NewServer(serverOpts...) cfg := raft.DefaultNodeConfig() newNodeOpts := raft.NewNodeOptions{ ID: securityConfig.ClientTLSCreds.NodeID(), Addr: oldNode.Address, Config: cfg, StateDir: oldNode.StateDir, ForceNewCluster: forceNewCluster, ClockSource: clockSource, SendTimeout: 10 * time.Second, TLSCredentials: securityConfig.ClientTLSCreds, } ctx := context.Background() n := raft.NewNode(ctx, newNodeOpts) n.Server = s healthServer := health.NewHealthServer() api.RegisterHealthServer(s, healthServer) raft.Register(s, n) go func() { // After stopping, we should receive an error from Serve assert.Error(t, s.Serve(wrappedListener)) }() healthServer.SetServingStatus("Raft", api.HealthCheckResponse_SERVING) err := n.JoinAndStart() require.NoError(t, err, "can't join cluster") go n.Run(ctx) return &TestNode{Node: n, Listener: wrappedListener, SecurityConfig: securityConfig} }
// New creates a Manager which has not started to accept requests yet. func New(config *Config) (*Manager, error) { dispatcherConfig := dispatcher.DefaultConfig() if config.ProtoAddr == nil { config.ProtoAddr = make(map[string]string) } if config.ProtoListener != nil && config.ProtoListener["tcp"] != nil { config.ProtoAddr["tcp"] = config.ProtoListener["tcp"].Addr().String() } // If an AdvertiseAddr was specified, we use that as our // externally-reachable address. tcpAddr := config.AdvertiseAddr if tcpAddr == "" { // Otherwise, we know we are joining an existing swarm. Use a // wildcard address to trigger remote autodetection of our // address. _, tcpAddrPort, err := net.SplitHostPort(config.ProtoAddr["tcp"]) if err != nil { return nil, fmt.Errorf("missing or invalid listen address %s", config.ProtoAddr["tcp"]) } // Even with an IPv6 listening address, it's okay to use // 0.0.0.0 here. Any "unspecified" (wildcard) IP will // be substituted with the actual source address. tcpAddr = net.JoinHostPort("0.0.0.0", tcpAddrPort) } err := os.MkdirAll(filepath.Dir(config.ProtoAddr["unix"]), 0700) if err != nil { return nil, fmt.Errorf("failed to create socket directory: %v", err) } err = os.MkdirAll(config.StateDir, 0700) if err != nil { return nil, fmt.Errorf("failed to create state directory: %v", err) } raftStateDir := filepath.Join(config.StateDir, "raft") err = os.MkdirAll(raftStateDir, 0700) if err != nil { return nil, fmt.Errorf("failed to create raft state directory: %v", err) } var listeners map[string]net.Listener if len(config.ProtoListener) > 0 { listeners = config.ProtoListener } else { listeners = make(map[string]net.Listener) for proto, addr := range config.ProtoAddr { l, err := net.Listen(proto, addr) // A unix socket may fail to bind if the file already // exists. Try replacing the file. unwrappedErr := err if op, ok := unwrappedErr.(*net.OpError); ok { unwrappedErr = op.Err } if sys, ok := unwrappedErr.(*os.SyscallError); ok { unwrappedErr = sys.Err } if proto == "unix" && unwrappedErr == syscall.EADDRINUSE { os.Remove(addr) l, err = net.Listen(proto, addr) if err != nil { return nil, err } } else if err != nil { return nil, err } listeners[proto] = l } } raftCfg := raft.DefaultNodeConfig() if config.ElectionTick > 0 { raftCfg.ElectionTick = int(config.ElectionTick) } if config.HeartbeatTick > 0 { raftCfg.HeartbeatTick = int(config.HeartbeatTick) } newNodeOpts := raft.NewNodeOptions{ ID: config.SecurityConfig.ClientTLSCreds.NodeID(), Addr: tcpAddr, JoinAddr: config.JoinRaft, Config: raftCfg, StateDir: raftStateDir, ForceNewCluster: config.ForceNewCluster, TLSCredentials: config.SecurityConfig.ClientTLSCreds, } RaftNode := raft.NewNode(context.TODO(), newNodeOpts) opts := []grpc.ServerOption{ grpc.Creds(config.SecurityConfig.ServerTLSCreds)} m := &Manager{ config: config, listeners: listeners, caserver: ca.NewServer(RaftNode.MemoryStore(), config.SecurityConfig), Dispatcher: dispatcher.New(RaftNode, dispatcherConfig), server: grpc.NewServer(opts...), localserver: grpc.NewServer(opts...), RaftNode: RaftNode, started: make(chan struct{}), stopped: make(chan struct{}), } return m, nil }
// New creates a Manager which has not started to accept requests yet. func New(config *Config) (*Manager, error) { dispatcherConfig := dispatcher.DefaultConfig() if config.ProtoAddr == nil { config.ProtoAddr = make(map[string]string) } if config.ProtoListener != nil && config.ProtoListener["tcp"] != nil { config.ProtoAddr["tcp"] = config.ProtoListener["tcp"].Addr().String() } tcpAddr := config.ProtoAddr["tcp"] if tcpAddr == "" { return nil, errors.New("no tcp listen address or listener provided") } listenHost, listenPort, err := net.SplitHostPort(tcpAddr) if err == nil { ip := net.ParseIP(listenHost) if ip != nil && ip.IsUnspecified() { // Find our local IP address associated with the default route. // This may not be the appropriate address to use for internal // cluster communications, but it seems like the best default. // The admin can override this address if necessary. conn, err := net.Dial("udp", "8.8.8.8:53") if err != nil { return nil, fmt.Errorf("could not determine local IP address: %v", err) } localAddr := conn.LocalAddr().String() conn.Close() listenHost, _, err = net.SplitHostPort(localAddr) if err != nil { return nil, fmt.Errorf("could not split local IP address: %v", err) } tcpAddr = net.JoinHostPort(listenHost, listenPort) } } // TODO(stevvooe): Reported address of manager is plumbed to listen addr // for now, may want to make this separate. This can be tricky to get right // so we need to make it easy to override. This needs to be the address // through which agent nodes access the manager. dispatcherConfig.Addr = tcpAddr err = os.MkdirAll(filepath.Dir(config.ProtoAddr["unix"]), 0700) if err != nil { return nil, fmt.Errorf("failed to create socket directory: %v", err) } err = os.MkdirAll(config.StateDir, 0700) if err != nil { return nil, fmt.Errorf("failed to create state directory: %v", err) } raftStateDir := filepath.Join(config.StateDir, "raft") err = os.MkdirAll(raftStateDir, 0700) if err != nil { return nil, fmt.Errorf("failed to create raft state directory: %v", err) } var listeners map[string]net.Listener if len(config.ProtoListener) > 0 { listeners = config.ProtoListener } else { listeners = make(map[string]net.Listener) for proto, addr := range config.ProtoAddr { l, err := net.Listen(proto, addr) // A unix socket may fail to bind if the file already // exists. Try replacing the file. unwrappedErr := err if op, ok := unwrappedErr.(*net.OpError); ok { unwrappedErr = op.Err } if sys, ok := unwrappedErr.(*os.SyscallError); ok { unwrappedErr = sys.Err } if proto == "unix" && unwrappedErr == syscall.EADDRINUSE { os.Remove(addr) l, err = net.Listen(proto, addr) if err != nil { return nil, err } } else if err != nil { return nil, err } listeners[proto] = l } } raftCfg := raft.DefaultNodeConfig() if config.ElectionTick > 0 { raftCfg.ElectionTick = int(config.ElectionTick) } if config.HeartbeatTick > 0 { raftCfg.HeartbeatTick = int(config.HeartbeatTick) } newNodeOpts := raft.NewNodeOptions{ ID: config.SecurityConfig.ClientTLSCreds.NodeID(), Addr: tcpAddr, JoinAddr: config.JoinRaft, Config: raftCfg, StateDir: raftStateDir, ForceNewCluster: config.ForceNewCluster, TLSCredentials: config.SecurityConfig.ClientTLSCreds, } RaftNode := raft.NewNode(context.TODO(), newNodeOpts) opts := []grpc.ServerOption{ grpc.Creds(config.SecurityConfig.ServerTLSCreds)} m := &Manager{ config: config, listeners: listeners, caserver: ca.NewServer(RaftNode.MemoryStore(), config.SecurityConfig), Dispatcher: dispatcher.New(RaftNode, dispatcherConfig), server: grpc.NewServer(opts...), localserver: grpc.NewServer(opts...), RaftNode: RaftNode, stopped: make(chan struct{}), } return m, nil }
// New creates a Manager which has not started to accept requests yet. func New(config *Config) (*Manager, error) { dispatcherConfig := dispatcher.DefaultConfig() // If an AdvertiseAddr was specified, we use that as our // externally-reachable address. advertiseAddr := config.RemoteAPI.AdvertiseAddr var advertiseAddrPort string if advertiseAddr == "" { // Otherwise, we know we are joining an existing swarm. Use a // wildcard address to trigger remote autodetection of our // address. var err error _, advertiseAddrPort, err = net.SplitHostPort(config.RemoteAPI.ListenAddr) if err != nil { return nil, fmt.Errorf("missing or invalid listen address %s", config.RemoteAPI.ListenAddr) } // Even with an IPv6 listening address, it's okay to use // 0.0.0.0 here. Any "unspecified" (wildcard) IP will // be substituted with the actual source address. advertiseAddr = net.JoinHostPort("0.0.0.0", advertiseAddrPort) } err := os.MkdirAll(config.StateDir, 0700) if err != nil { return nil, errors.Wrap(err, "failed to create state directory") } raftStateDir := filepath.Join(config.StateDir, "raft") err = os.MkdirAll(raftStateDir, 0700) if err != nil { return nil, errors.Wrap(err, "failed to create raft state directory") } var listeners []net.Listener // don't create a socket directory if we're on windows. we used named pipe if runtime.GOOS != "windows" { err := os.MkdirAll(filepath.Dir(config.ControlAPI), 0700) if err != nil { return nil, errors.Wrap(err, "failed to create socket directory") } } l, err := xnet.ListenLocal(config.ControlAPI) // A unix socket may fail to bind if the file already // exists. Try replacing the file. if runtime.GOOS != "windows" { unwrappedErr := err if op, ok := unwrappedErr.(*net.OpError); ok { unwrappedErr = op.Err } if sys, ok := unwrappedErr.(*os.SyscallError); ok { unwrappedErr = sys.Err } if unwrappedErr == syscall.EADDRINUSE { os.Remove(config.ControlAPI) l, err = xnet.ListenLocal(config.ControlAPI) } } if err != nil { return nil, errors.Wrap(err, "failed to listen on control API address") } listeners = append(listeners, l) l, err = net.Listen("tcp", config.RemoteAPI.ListenAddr) if err != nil { return nil, errors.Wrap(err, "failed to listen on remote API address") } if advertiseAddrPort == "0" { advertiseAddr = l.Addr().String() config.RemoteAPI.ListenAddr = advertiseAddr } listeners = append(listeners, l) raftCfg := raft.DefaultNodeConfig() if config.ElectionTick > 0 { raftCfg.ElectionTick = int(config.ElectionTick) } if config.HeartbeatTick > 0 { raftCfg.HeartbeatTick = int(config.HeartbeatTick) } newNodeOpts := raft.NodeOptions{ ID: config.SecurityConfig.ClientTLSCreds.NodeID(), Addr: advertiseAddr, JoinAddr: config.JoinRaft, Config: raftCfg, StateDir: raftStateDir, ForceNewCluster: config.ForceNewCluster, TLSCredentials: config.SecurityConfig.ClientTLSCreds, } raftNode := raft.NewNode(newNodeOpts) opts := []grpc.ServerOption{ grpc.Creds(config.SecurityConfig.ServerTLSCreds)} m := &Manager{ config: config, listeners: listeners, caserver: ca.NewServer(raftNode.MemoryStore(), config.SecurityConfig), dispatcher: dispatcher.New(raftNode, dispatcherConfig), logbroker: logbroker.New(), server: grpc.NewServer(opts...), localserver: grpc.NewServer(opts...), raftNode: raftNode, started: make(chan struct{}), } return m, nil }
// New creates a Manager which has not started to accept requests yet. func New(config *Config) (*Manager, error) { dispatcherConfig := dispatcher.DefaultConfig() if config.ProtoAddr == nil { config.ProtoAddr = make(map[string]string) } if config.ProtoListener != nil && config.ProtoListener["tcp"] != nil { config.ProtoAddr["tcp"] = config.ProtoListener["tcp"].Addr().String() } tcpAddr := config.ProtoAddr["tcp"] if config.AdvertiseAddr != "" { tcpAddr = config.AdvertiseAddr } if tcpAddr == "" { return nil, errors.New("no tcp listen address or listener provided") } dispatcherConfig.Addr = tcpAddr err := os.MkdirAll(filepath.Dir(config.ProtoAddr["unix"]), 0700) if err != nil { return nil, fmt.Errorf("failed to create socket directory: %v", err) } err = os.MkdirAll(config.StateDir, 0700) if err != nil { return nil, fmt.Errorf("failed to create state directory: %v", err) } raftStateDir := filepath.Join(config.StateDir, "raft") err = os.MkdirAll(raftStateDir, 0700) if err != nil { return nil, fmt.Errorf("failed to create raft state directory: %v", err) } var listeners map[string]net.Listener if len(config.ProtoListener) > 0 { listeners = config.ProtoListener } else { listeners = make(map[string]net.Listener) for proto, addr := range config.ProtoAddr { l, err := net.Listen(proto, addr) // A unix socket may fail to bind if the file already // exists. Try replacing the file. unwrappedErr := err if op, ok := unwrappedErr.(*net.OpError); ok { unwrappedErr = op.Err } if sys, ok := unwrappedErr.(*os.SyscallError); ok { unwrappedErr = sys.Err } if proto == "unix" && unwrappedErr == syscall.EADDRINUSE { os.Remove(addr) l, err = net.Listen(proto, addr) if err != nil { return nil, err } } else if err != nil { return nil, err } listeners[proto] = l } } raftCfg := raft.DefaultNodeConfig() if config.ElectionTick > 0 { raftCfg.ElectionTick = int(config.ElectionTick) } if config.HeartbeatTick > 0 { raftCfg.HeartbeatTick = int(config.HeartbeatTick) } newNodeOpts := raft.NewNodeOptions{ ID: config.SecurityConfig.ClientTLSCreds.NodeID(), Addr: tcpAddr, JoinAddr: config.JoinRaft, Config: raftCfg, StateDir: raftStateDir, ForceNewCluster: config.ForceNewCluster, TLSCredentials: config.SecurityConfig.ClientTLSCreds, } RaftNode := raft.NewNode(context.TODO(), newNodeOpts) opts := []grpc.ServerOption{ grpc.Creds(config.SecurityConfig.ServerTLSCreds)} m := &Manager{ config: config, listeners: listeners, caserver: ca.NewServer(RaftNode.MemoryStore(), config.SecurityConfig), Dispatcher: dispatcher.New(RaftNode, dispatcherConfig), server: grpc.NewServer(opts...), localserver: grpc.NewServer(opts...), RaftNode: RaftNode, stopped: make(chan struct{}), } return m, nil }
// New creates a Manager which has not started to accept requests yet. func New(config *Config) (*Manager, error) { err := os.MkdirAll(config.StateDir, 0700) if err != nil { return nil, errors.Wrap(err, "failed to create state directory") } raftStateDir := filepath.Join(config.StateDir, "raft") err = os.MkdirAll(raftStateDir, 0700) if err != nil { return nil, errors.Wrap(err, "failed to create raft state directory") } raftCfg := raft.DefaultNodeConfig() if config.ElectionTick > 0 { raftCfg.ElectionTick = int(config.ElectionTick) } if config.HeartbeatTick > 0 { raftCfg.HeartbeatTick = int(config.HeartbeatTick) } dekRotator, err := NewRaftDEKManager(config.SecurityConfig.KeyWriter()) if err != nil { return nil, err } newNodeOpts := raft.NodeOptions{ ID: config.SecurityConfig.ClientTLSCreds.NodeID(), JoinAddr: config.JoinRaft, Config: raftCfg, StateDir: raftStateDir, ForceNewCluster: config.ForceNewCluster, TLSCredentials: config.SecurityConfig.ClientTLSCreds, KeyRotator: dekRotator, } raftNode := raft.NewNode(newNodeOpts) opts := []grpc.ServerOption{ grpc.Creds(config.SecurityConfig.ServerTLSCreds)} m := &Manager{ config: *config, caserver: ca.NewServer(raftNode.MemoryStore(), config.SecurityConfig), dispatcher: dispatcher.New(raftNode, dispatcher.DefaultConfig()), logbroker: logbroker.New(raftNode.MemoryStore()), server: grpc.NewServer(opts...), localserver: grpc.NewServer(opts...), raftNode: raftNode, started: make(chan struct{}), dekRotator: dekRotator, remoteListener: make(chan net.Listener, 1), controlListener: make(chan net.Listener, 1), errServe: make(chan error, 2), } if config.ControlAPI != "" { m.config.ControlAPI = "" if err := m.BindControl(config.ControlAPI); err != nil { return nil, err } } if config.RemoteAPI != nil { m.config.RemoteAPI = nil // The context isn't used in this case (before (*Manager).Run). if err := m.BindRemote(context.Background(), *config.RemoteAPI); err != nil { if config.ControlAPI != "" { l := <-m.controlListener l.Close() } return nil, err } } return m, nil }