func (srv *DNSServer) ListenAndServe() error { if srv.GetStore() == nil { panic("missing Store") } if srv.Domain == "" { srv.Domain = dnsDomain } if err := srv.validateRecursors(); err != nil { return err } api := dnsAPI{srv} mux := dns.NewServeMux() mux.HandleFunc(srv.Domain, api.ServiceLookup) if len(srv.Recursors) > 0 { mux.HandleFunc(".", api.Recurse) } errors := make(chan error, 4) done := func() { errors <- nil } if srv.UDPAddr != "" { l, err := reuseport.NewReusablePortPacketConn("udp4", srv.UDPAddr) if err != nil { return err } srv.UDPAddr = l.(*net.UDPConn).LocalAddr().String() server := &dns.Server{ Net: "udp", PacketConn: l, Handler: mux, NotifyStartedFunc: done, } go func() { errors <- server.ActivateAndServe() }() srv.servers = append(srv.servers, server) } if srv.TCPAddr != "" { l, err := keepalive.ReusableListen("tcp4", srv.TCPAddr) if err != nil { return err } srv.TCPAddr = l.Addr().String() server := &dns.Server{ Net: "tcp", Listener: l, Handler: mux, NotifyStartedFunc: done, } go func() { errors <- server.ActivateAndServe() }() srv.servers = append(srv.servers, server) } for range srv.servers { if err := <-errors; err != nil { return err } } return nil }
// Ensure the store can be restored from a snapshot func TestStore_RestoreSnapshot(t *testing.T) { // open a store, add some services and trigger a snapshot s := MustOpenStore() serviceNames := []string{"service0", "service1"} for _, name := range serviceNames { if err := s.AddService(name, nil); err != nil { s.Close() t.Fatal(err) } } if err := s.TriggerSnapshot(); err != nil { s.Close() t.Fatal(err) } s.Store.Close() // open another store with same path and port which will attempt // to restore the snapshot _, port, _ := net.SplitHostPort(s.Listener.Addr().String()) ln, err := keepalive.ReusableListen("tcp4", "127.0.0.1:"+port) if err != nil { t.Fatal(err) } s = NewStoreWithConfig(StoreConfig{Path: s.Path(), Listener: ln}) defer s.Close() if err := s.Open(); err != nil { t.Fatal(err) } // check the data was restored if !reflect.DeepEqual(s.ServiceNames(), serviceNames) { t.Fatalf("expected service names %v, got %v", serviceNames, s.ServiceNames()) } }
func NewStoreWithConfig(config StoreConfig) *Store { if config.Path == "" { // Generate a temporary path. f, _ := ioutil.TempFile("", "discoverd-store-") f.Close() os.Remove(f.Name()) config.Path = f.Name() } // Initialize store. s := &Store{Store: server.NewStore(config.Path)} if config.Listener == nil { // Open listener on random port. ln, err := keepalive.ReusableListen("tcp4", "127.0.0.1:0") if err != nil { panic(err) } config.Listener = ln } _, port, _ := net.SplitHostPort(config.Listener.Addr().String()) // Set default test settings. s.Listener = config.Listener s.Advertise, _ = net.ResolveTCPAddr("tcp", net.JoinHostPort("localhost", port)) s.HeartbeatTimeout = 50 * time.Millisecond s.ElectionTimeout = 50 * time.Millisecond s.LeaderLeaseTimeout = 50 * time.Millisecond s.CommitTimeout = 5 * time.Millisecond s.EnableSingleNode = true // Turn off logs if verbose flag is not set. if !testing.Verbose() { s.LogOutput = ioutil.Discard } return s }