| 67 | } |
| 68 | |
| 69 | func NewProxy(cfg ProxyConfig, logger log.Logger, routes []Route, registerer prometheus.Registerer) (*Proxy, error) { |
| 70 | if cfg.CompareResponses && cfg.PreferredBackend == "" { |
| 71 | return nil, fmt.Errorf("when enabling comparison of results -backend.preferred flag must be set to hostname of preferred backend") |
| 72 | } |
| 73 | |
| 74 | if cfg.PassThroughNonRegisteredRoutes && cfg.PreferredBackend == "" { |
| 75 | return nil, fmt.Errorf("when enabling passthrough for non-registered routes -backend.preferred flag must be set to hostname of backend where those requests needs to be passed") |
| 76 | } |
| 77 | |
| 78 | p := &Proxy{ |
| 79 | cfg: cfg, |
| 80 | logger: logger, |
| 81 | metrics: NewProxyMetrics(registerer), |
| 82 | routes: routes, |
| 83 | } |
| 84 | |
| 85 | // Parse the backend endpoints (comma separated). |
| 86 | parts := strings.Split(cfg.BackendEndpoints, ",") |
| 87 | |
| 88 | for idx, part := range parts { |
| 89 | // Skip empty ones. |
| 90 | part = strings.TrimSpace(part) |
| 91 | if part == "" { |
| 92 | continue |
| 93 | } |
| 94 | |
| 95 | u, err := url.Parse(part) |
| 96 | if err != nil { |
| 97 | return nil, errors.Wrapf(err, "invalid backend endpoint %s", part) |
| 98 | } |
| 99 | |
| 100 | // The backend name is hardcoded as the backend hostname. |
| 101 | name := u.Hostname() |
| 102 | preferred := name == cfg.PreferredBackend |
| 103 | |
| 104 | // In tests we have the same hostname for all backends, so we also |
| 105 | // support a numeric preferred backend which is the index in the list |
| 106 | // of backends. |
| 107 | if preferredIdx, err := strconv.Atoi(cfg.PreferredBackend); err == nil { |
| 108 | preferred = preferredIdx == idx |
| 109 | } |
| 110 | |
| 111 | p.backends = append(p.backends, NewProxyBackend(name, u, cfg.BackendReadTimeout, preferred)) |
| 112 | } |
| 113 | |
| 114 | // At least 1 backend is required |
| 115 | if len(p.backends) < 1 { |
| 116 | return nil, errMinBackends |
| 117 | } |
| 118 | |
| 119 | // If the preferred backend is configured, then it must exists among the actual backends. |
| 120 | if cfg.PreferredBackend != "" { |
| 121 | exists := false |
| 122 | for _, b := range p.backends { |
| 123 | if b.preferred { |
| 124 | exists = true |
| 125 | break |
| 126 | } |