// 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 }
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") } }
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) } }
// 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 } } } }
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) }
// 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) }
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) } }
// 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 }
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) }
// 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 }
// 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 }
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 }
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 }
// 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) }
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 }