예제 #1
0
// RegisterApp adds a new backend and a single server with Vulcand.
func (s *LeaderRegistry) RegisterApp(registration *AppRegistration) error {
	log.Infof("Registering app: %v", registration)

	endpoint, err := vulcan.NewEndpointWithID(s.Group, registration.Name, registration.Host, registration.Port)
	if err != nil {
		return err
	}

	err = s.client.RegisterBackend(endpoint)
	if err != nil {
		log.Errorf("Failed to register backend for endpoint: %v, %s", endpoint, err)
		return err
	}

	if s.IsMaster {
		err = s.maintainLeader(endpoint)
	} else {
		err = s.initLeader(endpoint)
	}

	if err != nil {
		log.Errorf("Failed to register server for endpoint: %v, %s", endpoint, err)
		return err
	}

	return nil
}
예제 #2
0
func initBundle(c *cli.Context) {
	b, err := NewBundler(c.StringSlice("middleware"))
	if err != nil {
		log.Errorf("Failed to bundle middlewares: %s", err)
		return
	}
	if err := b.bundle(); err != nil {
		log.Errorf("Failed to bundle middlewares: %s", err)
	} else {
		log.Infof("SUCCESS: bundle vulcand and vctl completed")
	}
}
예제 #3
0
func main() {
	log.InitWithConfig(log.Config{Name: "console"})

	app := cli.NewApp()
	app.Name = "vulcanbundle"
	app.Usage = "Command line interface to compile plugins into vulcan binary"
	app.Commands = []cli.Command{
		{
			Name:   "init",
			Usage:  "Init bundle",
			Action: initBundle,
			Flags: []cli.Flag{
				cli.StringSliceFlag{
					Name:  "middleware, m",
					Value: &cli.StringSlice{},
					Usage: "Path to repo and revision, e.g. github.com/mailgun/vulcand-plugins/auth",
				},
			},
		},
	}
	err := app.Run(os.Args)
	if err != nil {
		log.Errorf("Error: %s\n", err)
	}
}
예제 #4
0
// Subscribe watches etcd changes and generates structured events telling vulcand to add or delete frontends, hosts etc.
// It is a blocking function.
func (n *ng) Subscribe(changes chan interface{}, cancelC chan bool) error {
	// This index helps us to get changes in sequence, as they were performed by clients.
	waitIndex := uint64(0)
	for {
		response, err := n.client.Watch(n.etcdKey, waitIndex, true, nil, cancelC)
		if err != nil {
			switch err {
			case etcd.ErrWatchStoppedByUser:
				log.Infof("Stop watching: graceful shutdown")
				return nil
			default:
				log.Errorf("unexpected error: %s, stop watching", err)
				return err
			}
		}
		waitIndex = response.Node.ModifiedIndex + 1
		log.Infof("%s", responseToString(response))
		change, err := n.parseChange(response)
		if err != nil {
			log.Warningf("Ignore '%s', error: %s", responseToString(response), err)
			continue
		}
		if change != nil {
			log.Infof("%v", change)
			select {
			case changes <- change:
			case <-cancelC:
				return nil
			}
		}
	}
}
예제 #5
0
func latencyAtQuantile(q float64, s *engine.RoundTripStats) float64 {
	v, err := s.LatencyBrackets.GetQuantile(q)
	if err != nil {
		log.Errorf("Failed to get latency %f from %v, err: %v", q, s, err)
		return -1
	}
	return float64(v.Value) / float64(time.Millisecond)
}
예제 #6
0
// registerLocationForScope registers a location with a specified scope.
func (app *App) registerLocationForScope(methods []string, path string, scope Scope, middlewares []middleware.Middleware) {
	host, err := app.apiHostForScope(scope)
	if err != nil {
		log.Errorf("Failed to register a location: %v", err)
		return
	}
	app.registerLocationForHost(methods, path, host, middlewares)
}
예제 #7
0
func main() {
	log.InitWithConfig(log.Config{Name: "console"})

	cmd := command.NewCommand(registry.GetRegistry())
	err := cmd.Run(os.Args)
	if err != nil {
		log.Errorf("error: %s\n", err)
	}
}
예제 #8
0
// RegisterHandler registers the frontends and middlewares with Vulcand.
func (s *LBRegistry) RegisterHandler(registration *HandlerRegistration) error {
	log.Infof("Registering handler: %v", registration)

	location := vulcan.NewLocation(registration.Host, registration.Methods, registration.Path, registration.Name, registration.Middlewares)
	err := s.client.RegisterFrontend(location)
	if err != nil {
		log.Errorf("Failed to register frontend for location: %v, %s", location, err)
		return err
	}

	err = s.client.RegisterMiddleware(location)
	if err != nil {
		log.Errorf("Failed to register middleware for location: %v, %s", location, err)
		return err
	}

	return nil
}
예제 #9
0
func (rw *rewriteHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	oldURL := rawURL(req)

	// only continue if the Regexp param matches the URL
	if !rw.regexp.MatchString(oldURL) {
		rw.next.ServeHTTP(w, req)
		return
	}

	// apply a rewrite regexp to the URL
	newURL := rw.regexp.ReplaceAllString(oldURL, rw.replacement)

	// replace any variables that may be in there
	rewrittenURL := &bytes.Buffer{}
	if err := ApplyString(newURL, rewrittenURL, req); err != nil {
		rw.errHandler.ServeHTTP(w, req, err)
		return
	}

	// parse the rewritten URL and replace request URL with it
	parsedURL, err := url.Parse(rewrittenURL.String())
	if err != nil {
		rw.errHandler.ServeHTTP(w, req, err)
		return
	}

	if rw.redirect && newURL != oldURL {
		(&redirectHandler{u: parsedURL}).ServeHTTP(w, req)
		return
	}

	req.URL = parsedURL

	// make sure the request URI corresponds the rewritten URL
	req.RequestURI = req.URL.RequestURI()

	if !rw.rewriteBody {
		rw.next.ServeHTTP(w, req)
		return
	}

	bw := &bufferWriter{header: make(http.Header), buffer: &bytes.Buffer{}}
	newBody := &bytes.Buffer{}

	rw.next.ServeHTTP(bw, req)

	if err := Apply(bw.buffer, newBody, req); err != nil {
		log.Errorf("Failed to rewrite response body: %v", err)
		return
	}

	utils.CopyHeaders(w.Header(), bw.Header())
	w.Header().Set("Content-Length", strconv.Itoa(newBody.Len()))
	w.WriteHeader(bw.code)
	io.Copy(w, newBody)
}
예제 #10
0
// syncs backend servers and rebalancer state
func syncServers(m *mux, rb *roundrobin.Rebalancer, backend *backend, w *RTWatcher) error {
	// First, collect and parse servers to add
	newServers := map[string]*url.URL{}
	for _, s := range backend.servers {
		u, err := url.Parse(s.URL)
		if err != nil {
			return fmt.Errorf("failed to parse url %v", s.URL)
		}
		newServers[s.URL] = u
	}

	// Memorize what endpoints exist in load balancer at the moment
	existingServers := map[string]*url.URL{}
	for _, s := range rb.Servers() {
		existingServers[s.String()] = s
	}

	// First, add endpoints, that should be added and are not in lb
	for _, s := range newServers {
		if _, exists := existingServers[s.String()]; !exists {
			if err := rb.UpsertServer(s); err != nil {
				log.Errorf("%v failed to add %v, err: %s", m, s, err)
			} else {
				log.Infof("%v add %v", m, s)
			}
			w.upsertServer(s)
		}
	}

	// Second, remove endpoints that should not be there any more
	for k, v := range existingServers {
		if _, exists := newServers[k]; !exists {
			if err := rb.RemoveServer(v); err != nil {
				log.Errorf("%v failed to remove %v, err: %v", m, v, err)
			} else {
				log.Infof("%v removed %v", m, v)
			}
			w.removeServer(v)
		}
	}
	return nil
}
예제 #11
0
// RegisterApp adds a new backend and a single server with Vulcand.
func (s *LBRegistry) RegisterApp(registration *AppRegistration) error {
	log.Infof("Registering app: %v", registration)

	endpoint, err := vulcan.NewEndpoint(registration.Name, registration.Host, registration.Port)
	if err != nil {
		return err
	}

	err = s.client.RegisterBackend(endpoint)
	if err != nil {
		log.Errorf("Failed to register backend for endpoint: %v, %s", endpoint, err)
		return err
	}

	err = s.client.UpsertServer(endpoint, s.TTL)
	if err != nil {
		log.Errorf("Failed to register server for endpoint: %v, %s", endpoint, err)
		return err
	}

	return nil
}
예제 #12
0
func Run(registry *plugin.Registry) error {
	options, err := ParseCommandLine()
	if err != nil {
		return fmt.Errorf("failed to parse command line: %s", err)
	}
	service := NewService(options, registry)
	if err := service.Start(); err != nil {
		log.Errorf("Failed to start service: %v", err)
		return fmt.Errorf("service start failure: %s", err)
	} else {
		log.Infof("Service exited gracefully")
	}
	return nil
}
예제 #13
0
func (mx *mux) emitMetrics() error {
	c := mx.options.MetricsClient

	// Emit connection stats
	counts := mx.connTracker.counts()
	for state, values := range counts {
		for addr, count := range values {
			c.Gauge(c.Metric("conns", addr, state.String()), count, 1)
		}
	}

	// Emit frontend metrics stats
	frontends, err := mx.topFrontends(nil)
	if err != nil {
		log.Errorf("failed to get top frontends: %v", err)
		return err
	}
	for _, f := range frontends {
		m := c.Metric("frontend", strings.Replace(f.Id, ".", "_", -1))
		s := f.Stats
		for _, scode := range s.Counters.StatusCodes {
			// response codes counters
			c.Gauge(m.Metric("code", strconv.Itoa(scode.Code)), scode.Count, 1)
		}
		// network errors
		c.Gauge(m.Metric("neterr"), s.Counters.NetErrors, 1)
		// requests
		c.Gauge(m.Metric("reqs"), s.Counters.Total, 1)

		// round trip times in microsecond resolution
		for _, b := range s.LatencyBrackets {
			c.Gauge(m.Metric("rtt", strconv.Itoa(int(b.Quantile*10.0))), int64(b.Value/time.Microsecond), 1)
		}
	}

	return nil
}
예제 #14
0
// Helper that replies with the 500 code and happened error message.
func ReplyInternalError(w http.ResponseWriter, message string) {
	log.Errorf("Internal server error: %v", message)
	Reply(w, Response{"message": message}, http.StatusInternalServerError)
}
예제 #15
0
func (s *Supervisor) init() error {
	proxy, err := s.newProxy(s.lastId)
	if err != nil {
		return err
	}
	s.lastId += 1

	if err := initProxy(s.engine, proxy); err != nil {
		return err
	}

	// This is the first start, pass the files that could have been passed
	// to us by the parent process
	if s.lastId == 1 && len(s.options.Files) != 0 {
		log.Infof("Passing files %v to %v", s.options.Files, proxy)
		if err := proxy.TakeFiles(s.options.Files); err != nil {
			return err
		}
	}

	log.Infof("%v init() initial setup done", proxy)

	oldProxy := s.getCurrentProxy()
	if oldProxy != nil {
		files, err := oldProxy.GetFiles()
		if err != nil {
			return err
		}
		log.Infof("%v taking files from %v to %v", s, oldProxy, proxy)
		if err := proxy.TakeFiles(files); err != nil {
			return err
		}
	}

	if err := proxy.Start(); err != nil {
		return err
	}

	if oldProxy != nil {
		s.wg.Add(1)
		go func() {
			defer s.wg.Done()
			oldProxy.Stop(true)
		}()
	}

	// Watch and configure this instance of server
	s.setCurrentProxy(proxy)
	changesC := make(chan interface{})

	// This goroutine will connect to the backend and emit the changes to the changesC channel.
	// In case of any error it notifies supervisor of the error by sending an error to the channel triggering reload.
	go func() {
		cancelC := make(chan bool)
		if err := s.engine.Subscribe(changesC, cancelC); err != nil {
			log.Infof("%v engine watcher got error: '%v' will restart", proxy, err)
			close(cancelC)
			close(changesC)
			s.restartC <- err
		} else {
			close(cancelC)
			// Graceful shutdown without restart
			log.Infof("%v engine watcher got nil error, gracefully shutdown", proxy)
			s.broadcastCloseC <- true
		}
	}()

	// This goroutine will listen for changes arriving to the changes channel and reconfigure the given server
	go func() {
		for {
			change := <-changesC
			if change == nil {
				log.Infof("Stop watching changes for %s", proxy)
				return
			}
			if err := processChange(proxy, change); err != nil {
				log.Errorf("failed to process change %#v, err: %s", change, err)
			}
		}
	}()
	return nil
}