예제 #1
0
파일: httploc.go 프로젝트: jbardin/vulcan
// Round trips the request to one of the endpoints and returns the response
func (l *HttpLocation) RoundTrip(req Request) (*http.Response, error) {
	for {
		_, err := req.GetBody().Seek(0, 0)
		if err != nil {
			return nil, err
		}

		endpoint, err := l.loadBalancer.NextEndpoint(req)
		if err != nil {
			log.Errorf("Load Balancer failure: %s", err)
			return nil, err
		}

		// Adds headers, changes urls
		newRequest := l.rewriteRequest(req.GetHttpRequest(), endpoint)

		// In case if error is not nil, we allow load balancer to choose the next endpoint
		// e.g. to do request failover. Nil error means that we got proxied the request successfully.
		response, err := l.proxyToEndpoint(endpoint, req, newRequest)
		if l.options.ShouldFailover(req) {
			continue
		} else {
			return response, err
		}
	}
	log.Errorf("All endpoints failed!")
	return nil, fmt.Errorf("All endpoints failed")
}
예제 #2
0
파일: service.go 프로젝트: shawnps/vulcand
func (s *Service) addLocation(host *Host, loc *Location) error {
	router, err := s.getPathRouter(host.Name)
	if err != nil {
		return err
	}
	// Create a load balancer that handles all the endpoints within the given location
	rr, err := roundrobin.NewRoundRobin()
	if err != nil {
		return err
	}

	before := callback.NewBeforeChain()
	after := callback.NewAfterChain()
	options := httploc.Options{
		Before: before,
		After:  after,
	}
	// Add rate limits
	for _, rl := range loc.RateLimits {
		limiter, err := s.newRateLimiter(rl)
		if err == nil {
			before.Add(rl.EtcdKey, limiter)
			after.Add(rl.EtcdKey, limiter)
		} else {
			log.Errorf("Failed to create limiter: %s", before)
		}
	}

	// Add connection limits
	for _, cl := range loc.ConnLimits {
		limiter, err := s.newConnLimiter(cl)
		if err == nil {
			before.Add(cl.EtcdKey, limiter)
			after.Add(cl.EtcdKey, limiter)
		} else {
			log.Errorf("Failed to create limiter: %s", before)
		}
	}

	// Create a location itself
	location, err := httploc.NewLocationWithOptions(loc.Name, rr, options)
	if err != nil {
		return err
	}
	// Add the location to the router
	if err := router.AddLocation(loc.Path, location); err != nil {
		return err
	}
	// Once the location added, configure all endpoints
	return s.configureLocation(host, loc)
}
예제 #3
0
func (c *Configurator) upsertLocation(host *Host, loc *Location) error {
	if err := c.upsertHost(host); err != nil {
		return err
	}

	// If location already exists, do nothing
	if loc := c.a.GetHttpLocation(host.Name, loc.Id); loc != nil {
		return nil
	}

	router := c.a.GetPathRouter(host.Name)
	if router == nil {
		return fmt.Errorf("Router not found for %s", host)
	}
	// Create a load balancer that handles all the endpoints within the given location
	rr, err := roundrobin.NewRoundRobin()
	if err != nil {
		return err
	}

	// Create a location itself
	location, err := httploc.NewLocation(loc.Id, rr)
	if err != nil {
		return err
	}

	// Always register a global connection watcher
	location.GetObserverChain().Upsert(ConnWatch, c.connWatcher)

	// Add the location to the router
	if err := router.AddLocation(loc.Path, location); err != nil {
		return err
	}

	// Add rate and connection limits
	for _, rl := range loc.RateLimits {

		if err := c.upsertLocationRateLimit(host, loc, rl); err != nil {
			log.Errorf("Failed to add rate limit: %s", err)
		}

	}
	for _, cl := range loc.ConnLimits {
		if err := c.upsertLocationConnLimit(host, loc, cl); err != nil {
			log.Errorf("Failed to add connection limit: %s", err)
		}
	}
	// Once the location added, configure all endpoints
	return c.syncLocationEndpoints(loc)
}
예제 #4
0
파일: httploc.go 프로젝트: jbardin/vulcan
// Proxy the request to the given endpoint, execute observers and middlewares chains
func (l *HttpLocation) proxyToEndpoint(endpoint Endpoint, req Request, httpReq *http.Request) (*http.Response, error) {

	a := &BaseAttempt{Endpoint: endpoint}

	l.observerChain.ObserveRequest(req)
	defer l.observerChain.ObserveResponse(req, a)
	defer req.AddAttempt(a)

	it := l.middlewareChain.GetIter()
	defer l.unwindIter(it, req, a)

	for v := it.Next(); v != nil; v = it.Next() {
		a.Response, a.Error = v.ProcessRequest(req)
		if a.Response != nil || a.Error != nil {
			// Move the iterator forward to count it again once we unwind the chain
			it.Next()
			log.Errorf("Midleware intercepted request with response=%s, error=%s", a.Response.Status, a.Error)
			return a.Response, a.Error
		}
	}

	// Forward the request and mirror the response
	start := l.options.TimeProvider.UtcNow()
	a.Response, a.Error = l.transport.RoundTrip(httpReq)
	a.Duration = l.options.TimeProvider.UtcNow().Sub(start)
	return a.Response, a.Error
}
예제 #5
0
func (s *Service) processChange(change *Change) {
	var err error
	switch child := (change.Child).(type) {
	case *Endpoint:
		switch change.Action {
		case "create":
			err = s.addEndpoint((change.Parent).(*Upstream), child)
		case "delete":
			err = s.deleteEndpoint((change.Parent).(*Upstream), child)
		}
	case *Location:
		switch change.Action {
		case "create":
			err = s.addLocation((change.Parent).(*Host), child)
		case "delete":
			err = s.deleteLocation((change.Parent).(*Host), child)
		case "set":
			if len(change.Keys["upstream"]) != 0 {
				err = s.updateLocationUpstream((change.Parent).(*Host), child, change.Keys["upstream"])
			} else {
				err = fmt.Errorf("Unknown property update: %s", change)
			}
		}
	case *Host:
		switch change.Action {
		case "create":
			err = s.addHost(child)
		case "delete":
			err = s.deleteHost(child)
		}
	}
	if err != nil {
		log.Errorf("Processing change failed: %s", err)
	}
}
예제 #6
0
func (c *Configurator) deleteEndpoint(upstream *Upstream, endpointId string, affectedLocations []*Location) error {
	for _, l := range affectedLocations {
		if err := c.syncLocationEndpoints(l); err != nil {
			log.Errorf("Failed to sync %s endpoints err: %s", l, err)
		}
	}
	return nil
}
예제 #7
0
func (c *Configurator) syncLocationEndpoints(location *Location) error {

	rr := c.a.GetHttpLocationLb(location.Hostname, location.Id)
	if rr == nil {
		return fmt.Errorf("%s lb not found", location)
	}

	// First, collect and parse endpoints to add
	newEndpoints := map[string]endpoint.Endpoint{}
	for _, e := range location.Upstream.Endpoints {
		ep, err := EndpointFromUrl(e.Url, e.Url)
		if err != nil {
			return fmt.Errorf("Failed to parse endpoint url: %s", e)
		}
		newEndpoints[e.Url] = ep
	}

	// Memorize what endpoints exist in load balancer at the moment
	existingEndpoints := map[string]endpoint.Endpoint{}
	for _, e := range rr.GetEndpoints() {
		existingEndpoints[e.GetUrl().String()] = e
	}

	// First, add endpoints, that should be added and are not in lb
	for _, e := range newEndpoints {
		if _, exists := existingEndpoints[e.GetUrl().String()]; !exists {
			if err := rr.AddEndpoint(e); err != nil {
				log.Errorf("Failed to add %s, err: %s", e, err)
			} else {
				log.Infof("Added %s to %s", e, location)
			}
		}
	}

	// Second, remove endpoints that should not be there any more
	for _, e := range existingEndpoints {
		if _, exists := newEndpoints[e.GetUrl().String()]; !exists {
			if err := rr.RemoveEndpoint(e); err != nil {
				log.Errorf("Failed to remove %s, err: %s", e, err)
			} else {
				log.Infof("Removed %s from %s", e, location)
			}
		}
	}
	return nil
}
예제 #8
0
func (s *Service) configureLocation(host *Host, location *Location) error {
	rr, err := s.getHttpLocationLb(host.Name, location.Name)
	if err != nil {
		return err
	}

	// First, collect and parse endpoints to add
	endpointsToAdd := map[string]endpoint.Endpoint{}
	for _, e := range location.Upstream.Endpoints {
		ep, err := EndpointFromUrl(e.Name, e.Url)
		if err != nil {
			return fmt.Errorf("Failed to parse endpoint url: %s", e)
		}
		endpointsToAdd[ep.GetId()] = ep
	}

	// Memorize what endpoints exist in load balancer at the moment
	existing := map[string]endpoint.Endpoint{}
	for _, e := range rr.GetEndpoints() {
		existing[e.GetId()] = e
	}

	// First, add endpoints, that should be added and are not in lb
	for eid, e := range endpointsToAdd {
		if _, exists := existing[eid]; !exists {
			if err := rr.AddEndpoint(e); err != nil {
				log.Errorf("Failed to add %s, err: %s", e, err)
			} else {
				log.Infof("Added %s", e)
			}
		}
	}

	// Second, remove endpoints that should not be there any more
	for eid, e := range existing {
		if _, exists := endpointsToAdd[eid]; !exists {
			if err := rr.RemoveEndpoint(e); err != nil {
				log.Errorf("Failed to remove %s, err: %s", e, err)
			} else {
				log.Infof("Removed %s", e)
			}
		}
	}
	return nil
}
예제 #9
0
func (c *Configurator) WatchChanges(changes chan interface{}) error {
	for {
		change := <-changes
		if err := c.processChange(change); err != nil {
			log.Errorf("Failed to process change %#v, err: %s", change, err)
		}
	}
	return nil
}
예제 #10
0
func (s *Service) configureHost(host *Host) error {
	for _, loc := range host.Locations {
		if err := s.addLocation(host, loc); err != nil {
			log.Errorf("Failed adding %s to %s, err: %s", loc, host, err)
		} else {
			log.Infof("Added %s to %s", loc, host)
		}
	}
	return nil
}
예제 #11
0
func (s *Service) configureProxy() error {
	hosts, err := s.backend.GetHosts()
	if err != nil {
		return err
	}
	for _, host := range hosts {
		log.Infof("Configuring %s", host)

		if err := s.addHost(host); err != nil {
			log.Errorf("Failed adding %s, err: %s", host, err)
			continue
		}
		if err := s.configureHost(host); err != nil {
			log.Errorf("Failed configuring %s", host)
			continue
		}
	}
	return nil
}
예제 #12
0
func (f *JsonFormatter) Format(err ProxyError) (int, []byte, string) {
	encodedError, e := json.Marshal(map[string]interface{}{
		"error": string(err.Error()),
	})
	if e != nil {
		log.Errorf("Failed to serialize: %s", e)
		encodedError = []byte("{}")
	}
	return err.GetStatusCode(), encodedError, "application/json"
}
예제 #13
0
func (s *EtcdBackend) readLocation(hostname, locationId string) (*Location, error) {
	locationKey := join(s.etcdKey, "hosts", hostname, "locations", locationId)
	_, err := s.client.Get(locationKey, false, false)
	if err != nil {
		if notFound(err) {
			return nil, fmt.Errorf("Location '%s' not found for Host '%s'", locationId, hostname)
		}
		return nil, err
	}
	path, ok := s.getVal(locationKey, "path")
	if !ok {
		return nil, fmt.Errorf("Missing location path: %s", locationKey)
	}
	upstreamKey, ok := s.getVal(locationKey, "upstream")
	if !ok {
		return nil, fmt.Errorf("Missing location upstream: %s", locationKey)
	}
	location := &Location{
		Name:       suffix(locationKey),
		EtcdKey:    locationKey,
		Path:       path,
		ConnLimits: []*ConnLimit{},
		RateLimits: []*RateLimit{},
	}
	upstream, err := s.readUpstream(upstreamKey)
	if err != nil {
		return nil, err
	}
	for _, e := range upstream.Endpoints {
		stats, err := s.statsGetter.GetStats(hostname, locationId, e.Name)
		if err == nil {
			e.Stats = stats
		} else {
			log.Errorf("Failed to get stats about endpoint: %s, err: %s", e, err)
		}
	}

	for _, cl := range s.getVals(locationKey, "limits", "connections") {
		connLimit, err := s.readLocationConnLimit(cl.Key)
		if err == nil {
			location.ConnLimits = append(location.ConnLimits, connLimit)
		}
	}

	for _, cl := range s.getVals(locationKey, "limits", "rates") {
		rateLimit, err := s.readLocationRateLimit(cl.Key)
		if err == nil {
			location.RateLimits = append(location.RateLimits, rateLimit)
		}
	}

	location.Upstream = upstream
	return location, nil
}
예제 #14
0
func (c *Configurator) addEndpoint(upstream *Upstream, e *Endpoint, affectedLocations []*Location) error {
	endpoint, err := EndpointFromUrl(e.EtcdKey, e.Url)
	if err != nil {
		return fmt.Errorf("Failed to parse endpoint url: %s", endpoint)
	}
	for _, l := range affectedLocations {
		if err := c.syncLocationEndpoints(l); err != nil {
			log.Errorf("Failed to sync %s endpoints err: %s", l, err)
		}
	}
	return nil
}
예제 #15
0
func (s *EtcdBackend) watchChanges() {
	waitIndex := uint64(0)
	for {
		response, err := s.client.Watch(s.etcdKey, waitIndex, true, nil, nil)
		if err != nil {
			log.Errorf("Failed to get response from etcd: %s, quitting watch goroutine", err)
			return
		}
		log.Infof("Got response: %s %s %d %s",
			response.Action, response.Node.Key, response.EtcdIndex, err)
		change, err := s.parseChange(response)
		if err != nil {
			log.Errorf("Failed to process change: %s, ignoring", err)
			continue
		}
		if change != nil {
			s.changes <- change
		}
		waitIndex = response.Node.ModifiedIndex + 1
	}
}
예제 #16
0
// Watches etcd changes and generates structured events telling vulcand to add or delete locations, hosts etc.
// if initialSetup is true, reads the existing configuration and generates events for inital configuration of the proxy.
func (s *EtcdBackend) WatchChanges(changes chan interface{}, initialSetup bool) error {
	if initialSetup == true {
		log.Infof("Etcd backend reading initial configuration")
		if err := s.generateChanges(changes); err != nil {
			log.Errorf("Failed to generate changes: %s, stopping watch.", err)
			return err
		}
	}
	// This index helps us to get changes in sequence, as they were performed by clients.
	waitIndex := uint64(0)
	for {
		response, err := s.client.Watch(s.etcdKey, waitIndex, true, nil, s.cancelC)
		if err != nil {
			if err == etcd.ErrWatchStoppedByUser {
				log.Infof("Stop watching: graceful shutdown")
				return nil
			}
			log.Errorf("Stop watching: Etcd client error: %v", err)
			return err
		}

		waitIndex = response.Node.ModifiedIndex + 1
		log.Infof("Got response: %s %s %d %v",
			response.Action, response.Node.Key, response.EtcdIndex, err)
		change, err := s.parseChange(response)
		if err != nil {
			log.Errorf("Failed to process change: %s, ignoring", err)
			continue
		}
		if change != nil {
			log.Infof("Sending change: %s", change)
			select {
			case changes <- change:
			case <-s.stopC:
				return nil
			}
		}
	}
	return nil
}
예제 #17
0
파일: proxy.go 프로젝트: jbardin/vulcan
// Accepts requests, round trips it to the endpoint and writes backe the response.
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Record the request body so we can replay it on errors.
	body, err := netutils.NewBodyBuffer(r.Body)
	if err != nil || body == nil {
		log.Errorf("Request read error %s", err)
		p.replyError(errors.FromStatus(http.StatusBadRequest), w, r)
		return
	}
	defer body.Close()
	r.Body = body

	req := &request.BaseRequest{
		HttpRequest: r,
		Id:          atomic.AddInt64(&p.lastRequestId, 1),
		Body:        body,
	}

	err = p.proxyRequest(w, req)
	if err != nil {
		log.Errorf("%s failed: %s", req, err)
		p.replyError(err, w, r)
	}
}
예제 #18
0
// Accepts requests, round trips it to the endpoint, and writes back the response.
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Record the request body so we can replay it on errors.
	body, err := netutils.NewBodyBuffer(r.Body)
	if err != nil || body == nil {
		log.Errorf("Request read error %s", err)
		if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
			p.replyError(errors.FromStatus(http.StatusRequestTimeout), w, r)
		} else {
			p.replyError(errors.FromStatus(http.StatusBadRequest), w, r)
		}
		return
	}
	defer body.Close()
	r.Body = body

	req := request.NewBaseRequest(r, atomic.AddInt64(&p.lastRequestId, 1), body)

	err = p.proxyRequest(w, req)
	if err != nil {
		log.Errorf("%s failed: %s", req, err)
		p.replyError(err, w, r)
	}
}
예제 #19
0
func (r *RoundRobin) adjustWeights() {
	if r.options.FailureHandler == nil {
		return
	}
	weights, err := r.options.FailureHandler.AdjustWeights()
	if err != nil {
		log.Errorf("%s returned error: %s", r.options.FailureHandler, err)
		return
	}
	changed := false
	for _, w := range weights {
		if w.GetEndpoint().GetEffectiveWeight() != w.GetWeight() {
			w.GetEndpoint().setEffectiveWeight(w.GetWeight())
			changed = true
		}
	}
	if changed {
		r.resetIterator()
	}
}
예제 #20
0
// Round trips the request to the selected location and writes back the response
func (p *Proxy) proxyRequest(w http.ResponseWriter, req *request.BaseRequest) error {
	location, err := p.router.Route(req)
	if err != nil {
		return err
	}
	// Router could not find a matching location, we can do nothing more
	if location == nil {
		log.Errorf("%s failed to route", req)
		return errors.FromStatus(http.StatusBadGateway)
	}
	response, err := location.RoundTrip(req)
	if response != nil {
		netutils.CopyHeaders(w.Header(), response.Header)
		w.WriteHeader(response.StatusCode)
		io.Copy(w, response.Body)
		defer response.Body.Close()
		return nil
	} else {
		return err
	}
}
예제 #21
0
func (s *Service) deleteEndpoint(upstream *Upstream, e *Endpoint) error {
	endpoint, err := EndpointFromUrl(e.Name, "http://delete.me:4000")
	if err != nil {
		return fmt.Errorf("Failed to parse endpoint url: %s", endpoint)
	}
	locations, err := s.getLocations(upstream.Name)
	if err != nil {
		return err
	}
	for _, l := range locations {
		rr, ok := l.GetLoadBalancer().(*roundrobin.RoundRobin)
		if !ok {
			return fmt.Errorf("Unexpected load balancer type: %T", l.GetLoadBalancer())
		}
		if err := rr.RemoveEndpoint(endpoint); err != nil {
			log.Errorf("Failed to remove endpoint: %s", err)
		} else {
			log.Infof("Removed %s", e)
		}
	}
	return nil
}