Exemple #1
0
// Unregister returns an error if the service isn't registered in etcd.
func (s *ServiceTest) TestUnregisterUnregisteredService(c *C) {
	address := "http://localhost:8080"
	routes := switchboard.Routes{"GET": []string{"/users", "/user/:id"}}
	service := switchboard.NewService("test", s.client, address, routes)
	err := service.Unregister()
	c.Assert(err.(*etcd.EtcdError).ErrorCode, Equals, 100)
}
Exemple #2
0
// Register is effectively a no-op if the service record already exists in
// etcd.
func (s *ServiceTest) TestRegisterDuplicate(c *C) {
	address := "http://localhost:8080"
	routes := switchboard.Routes{"GET": []string{"/users", "/user/:id"}}
	service := switchboard.NewService("test", s.client, address, routes)
	_, err := service.Register(0)
	c.Assert(err, IsNil)
	_, err = service.Register(0)
	c.Assert(err, IsNil)
}
Exemple #3
0
// Watch observes unregistered services in etcd and removes the relevant
// patterns and addresses from the ExchangeServeMux.
func (s *ExchangeTest) TestWatchUnegisteredService(c *C) {
	// Create the namespace in etcd.
	_, err := s.client.CreateDir("/test", 0)
	c.Assert(err, IsNil)

	// Register a new service.
	address := "http://localhost:8080"
	routes := switchboard.Routes{"GET": []string{"/users"}}
	service := switchboard.NewService("test", s.client, address, routes)
	_, err = service.Register(0)
	c.Assert(err, IsNil)

	// Initialize the exchange.
	err = s.exchange.Init()
	c.Assert(err, IsNil)

	// Start the watcher goroutine.
	stop := make(chan bool)
	stopped := make(chan bool)
	go func() {
		s.exchange.Watch(stop)
		stopped <- true
	}()

	// Cleanly shutdown the watcher no matter how the test finishes.
	defer func() {
		stop <- true
		c.Assert(<-stopped, Equals, true)
	}()

	// Unregister the service.
	err = service.Unregister()
	c.Assert(err, IsNil)

	// Janky logic to wait for updates from etcd will fail when updates don't
	// propagate within 500ms.
	receivedUpdate := false
	for i := 0; i < 500; i++ {
		addresses, err := s.mux.Match("GET", "/users")
		if err == nil {
			time.Sleep(time.Duration(1) * time.Millisecond)
			continue
		} else {
			c.Assert(addresses, IsNil)
			receivedUpdate = true
			break
		}
	}
	c.Assert(receivedUpdate, Equals, true)
}
Exemple #4
0
// Broadcast stops pushing changes to etcd when when a bool value is sent to
// the stop channel.
func (s *ServiceTest) TestBroadcastStops(c *C) {
	address := "http://localhost:8080"
	routes := switchboard.Routes{"GET": []string{"/users", "/user/:id"}}
	service := switchboard.NewService("test", s.client, address, routes)

	stop := make(chan bool)
	stopped := make(chan bool)
	go func() {
		service.Broadcast(2, 1, stop)
		stopped <- true
	}()

	stop <- true
	c.Assert(<-stopped, Equals, true)
}
Exemple #5
0
// Unregister deletes the service record in etcd.
func (s *ServiceTest) TestUnregister(c *C) {
	address := "http://localhost:8080"
	routes := switchboard.Routes{"GET": []string{"/users", "/user/:id"}}
	service := switchboard.NewService("test", s.client, address, routes)
	_, err := service.Register(0)
	c.Assert(err, IsNil)
	err = service.Unregister()
	c.Assert(err, IsNil)

	key := "test/" + service.ID()
	sort := false
	recursive := true
	_, err = s.client.Get(key, sort, recursive)
	c.Assert(err.(*etcd.EtcdError).ErrorCode, Equals, 100)
}
Exemple #6
0
// Register creates a service record to represent the service and registers it
// in etcd.
func (s *ServiceTest) TestRegister(c *C) {
	address := "http://localhost:8080"
	routes := switchboard.Routes{"GET": []string{"/users", "/user/:id"}}
	service := switchboard.NewService("test", s.client, address, routes)
	record, err := service.Register(0)
	c.Assert(err, IsNil)

	key := "test/" + service.ID()
	sort := false
	recursive := true
	response, err := s.client.Get(key, sort, recursive)
	c.Assert(err, IsNil)
	recordJSON, _ := json.Marshal(record)
	c.Assert(response.Node.Value, Equals, bytes.NewBuffer(recordJSON).String())
}
Exemple #7
0
// Init returns an error if the specified namespace doesn't exist in etcd.
func (s *ExchangeTest) TestInit(c *C) {
	address := "http://localhost:8080"
	routes := switchboard.Routes{"GET": []string{"/users", "/user/:id"}}
	service := switchboard.NewService("test", s.client, address, routes)
	_, err := service.Register(0)
	c.Assert(err, IsNil)

	err = s.exchange.Init()
	c.Assert(err, IsNil)
	addresses, err := s.mux.Match("GET", "/users")
	c.Assert(err, IsNil)
	c.Assert(addresses, DeepEquals, &[]string{"http://localhost:8080"})
	addresses, err = s.mux.Match("GET", "/user/123")
	c.Assert(err, IsNil)
	c.Assert(addresses, DeepEquals, &[]string{"http://localhost:8080"})
}
Exemple #8
0
// Broadcast registers a service with etcd.
func (s *ServiceTest) TestBroadcast(c *C) {
	// Create a new service.
	address := "http://localhost:8080"
	routes := switchboard.Routes{"GET": []string{"/users", "/user/:id"}}
	service := switchboard.NewService("test", s.client, address, routes)

	// Start broadcasting service changes to etcd.
	stop := make(chan bool)
	stopped := make(chan bool)
	go func() {
		service.Broadcast(2, 1, stop)
		stopped <- true
	}()

	// Cleanly shutdown the broadcaster no matter how the test finishes.
	defer func() {
		stop <- true
		c.Assert(<-stopped, Equals, true)
	}()

	// Janky logic to wait for the broadcaster to write a value to etcd that
	// can be read back will fail when service registration doesn't complete
	// within 500ms.
	receivedUpdate := false
	for i := 0; i < 500; i++ {
		key := "test/" + service.ID()
		sort := false
		recursive := true
		response, err := s.client.Get(key, sort, recursive)
		if err != nil {
			time.Sleep(time.Duration(1) * time.Millisecond)
			continue
		} else {
			record := switchboard.ServiceRecord{
				ID:      service.ID(),
				Address: service.Address(),
				Routes:  service.Routes()}
			recordJSON, _ := json.Marshal(record)
			c.Assert(response.Node.Value, Equals, bytes.NewBuffer(recordJSON).String())
			receivedUpdate = true
			break
		}
	}
	c.Assert(receivedUpdate, Equals, true)
}
Exemple #9
0
// Watch stops observing changes in etcd when when a bool value is sent to the
// stop channel.
func (s *ExchangeTest) TestWatchStops(c *C) {
	address := "http://localhost:8080"
	routes := switchboard.Routes{"GET": []string{"/users", "/user/:id"}}
	service := switchboard.NewService("test", s.client, address, routes)
	_, err := service.Register(0)
	c.Assert(err, IsNil)

	stop := make(chan bool)
	stopped := make(chan bool)
	go func() {
		err = s.exchange.Init()
		c.Assert(err, IsNil)
		s.exchange.Watch(stop)
		stopped <- true
	}()

	stop <- true
	c.Assert(<-stopped, Equals, true)
}
Exemple #10
0
func main() {
	// Setup HTTP routes and initialize the service.
	port := os.Getenv("PORT")
	address := "http://localhost:" + port
	client := etcd.NewClient([]string{"http://127.0.0.1:4001"})
	routes := switchboard.Routes{"GET": []string{"/hello/:name"}}
	service := switchboard.NewService("example", client, address, routes)

	// Broadcast service presence to etcd every 5 seconds (with a TTL of 10
	// seconds).  If this service crashes the exchange will only attempt to
	// route requests to it during the TTL window.
	go func() {
		log.Print("Broadcasting service configuration to etcd")
		stop := make(chan bool)
		service.Broadcast(5, 10, stop)
	}()

	// Unregister the service in etcd when we receive a SIGTERM.
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)
	go func() {
		for sig := range interrupt {
			log.Print("Unregistering service")
			service.Unregister()
			log.Fatal(sig)
		}
	}()

	// Setup a handler to process requests for our registered routes and
	// listen for HTTP requests from the exchange.
	handler := pat.New()
	handler.Get("/hello/:name", Log(http.HandlerFunc(Hello)))
	log.Printf("Listening for HTTP requests on port %v", port)
	err := http.ListenAndServe("localhost:"+port, handler)
	if err != nil {
		log.Print(err)
		log.Print("Unregistering service")
		service.Unregister()
		log.Print("Shutting down")
	}
}