discovery: add ability to proxy discovery requests

release-2.0
Jonathan Boulle 2014-10-13 11:24:23 -07:00
parent 6a30d3ba04
commit 7f8f371b0e
2 changed files with 94 additions and 1 deletions

View File

@ -6,6 +6,7 @@ import (
"log" "log"
"net/http" "net/http"
"net/url" "net/url"
"os"
"path" "path"
"sort" "sort"
"strconv" "strconv"
@ -26,6 +27,8 @@ var (
) )
const ( const (
// Environment variable used to configure an HTTP proxy for discovery
DiscoveryProxyEnv = "ETCD_DISCOVERY_PROXY"
// Number of retries discovery will attempt before giving up and erroring out. // Number of retries discovery will attempt before giving up and erroring out.
nRetries = uint(3) nRetries = uint(3)
) )
@ -46,6 +49,35 @@ type discovery struct {
timeoutTimescale time.Duration timeoutTimescale time.Duration
} }
// proxyFuncFromEnv builds a proxy function if the appropriate environment
// variable is set. It performs basic sanitization of the environment variable
// and returns any error encountered.
func proxyFuncFromEnv() (func(*http.Request) (*url.URL, error), error) {
proxy := os.Getenv(DiscoveryProxyEnv)
if proxy == "" {
return nil, nil
}
// Do a small amount of URL sanitization to help the user
// Derived from net/http.ProxyFromEnvironment
proxyURL, err := url.Parse(proxy)
if err != nil || !strings.HasPrefix(proxyURL.Scheme, "http") {
// proxy was bogus. Try prepending "http://" to it and
// see if that parses correctly. If not, we ignore the
// error and complain about the original one
var err2 error
proxyURL, err2 = url.Parse("http://" + proxy)
if err2 == nil {
err = nil
}
}
if err != nil {
return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err)
}
log.Printf("discovery: using proxy %q", proxyURL.String())
return http.ProxyURL(proxyURL), nil
}
func New(durl string, id uint64, config string) (Discoverer, error) { func New(durl string, id uint64, config string) (Discoverer, error) {
u, err := url.Parse(durl) u, err := url.Parse(durl)
if err != nil { if err != nil {
@ -53,7 +85,11 @@ func New(durl string, id uint64, config string) (Discoverer, error) {
} }
token := u.Path token := u.Path
u.Path = "" u.Path = ""
c, err := client.NewHTTPClient(&http.Transport{}, u.String(), time.Second*5) pf, err := proxyFuncFromEnv()
if err != nil {
return nil, err
}
c, err := client.NewHTTPClient(&http.Transport{Proxy: pf}, u.String(), time.Second*5)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -3,6 +3,8 @@ package discovery
import ( import (
"errors" "errors"
"math/rand" "math/rand"
"net/http"
"os"
"sort" "sort"
"strconv" "strconv"
@ -13,6 +15,61 @@ import (
"github.com/coreos/etcd/client" "github.com/coreos/etcd/client"
) )
func TestProxyFuncFromEnvUnset(t *testing.T) {
os.Setenv(DiscoveryProxyEnv, "")
pf, err := proxyFuncFromEnv()
if pf != nil {
t.Fatal("unexpected non-nil proxyFunc")
}
if err != nil {
t.Fatalf("unexpected non-nil err: %v", err)
}
}
func TestProxyFuncFromEnvBad(t *testing.T) {
tests := []string{
"%%",
"http://foo.com/%1",
}
for i, in := range tests {
os.Setenv(DiscoveryProxyEnv, in)
pf, err := proxyFuncFromEnv()
if pf != nil {
t.Errorf("#%d: unexpected non-nil proxyFunc", i)
}
if err == nil {
t.Errorf("#%d: unexpected nil err", i)
}
}
}
func TestProxyFuncFromEnv(t *testing.T) {
tests := map[string]string{
"bar.com": "http://bar.com",
"http://disco.foo.bar": "http://disco.foo.bar",
}
for in, w := range tests {
os.Setenv(DiscoveryProxyEnv, in)
pf, err := proxyFuncFromEnv()
if pf == nil {
t.Errorf("%s: unexpected nil proxyFunc", in)
continue
}
if err != nil {
t.Errorf("%s: unexpected non-nil err: %v", in, err)
continue
}
g, err := pf(&http.Request{})
if err != nil {
t.Errorf("%s: unexpected non-nil err: %v", in, err)
}
if g.String() != w {
t.Errorf("%s: proxyURL=%q, want %q", g, w)
}
}
}
func TestCheckCluster(t *testing.T) { func TestCheckCluster(t *testing.T) {
cluster := "1000" cluster := "1000"
self := "/1000/1" self := "/1000/1"