From b4f4fcd437c2f9816f9511217bde703679808679 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Mon, 9 Sep 2019 21:44:58 -0600 Subject: Migrate some selection policy tests over to v2 --- .../reverseproxy/selectionpolicies_test.go | 604 +++++++++------------ 1 file changed, 257 insertions(+), 347 deletions(-) (limited to 'modules/caddyhttp/reverseproxy/selectionpolicies_test.go') diff --git a/modules/caddyhttp/reverseproxy/selectionpolicies_test.go b/modules/caddyhttp/reverseproxy/selectionpolicies_test.go index 8006fb1..e9939d6 100644 --- a/modules/caddyhttp/reverseproxy/selectionpolicies_test.go +++ b/modules/caddyhttp/reverseproxy/selectionpolicies_test.go @@ -14,350 +14,260 @@ package reverseproxy -// TODO: finish migrating these - -// import ( -// "net/http" -// "net/http/httptest" -// "os" -// "testing" -// ) - -// var workableServer *httptest.Server - -// func TestMain(m *testing.M) { -// workableServer = httptest.NewServer(http.HandlerFunc( -// func(w http.ResponseWriter, r *http.Request) { -// // do nothing -// })) -// r := m.Run() -// workableServer.Close() -// os.Exit(r) -// } - -// type customPolicy struct{} - -// func (customPolicy) Select(pool HostPool, _ *http.Request) Host { -// return pool[0] -// } - -// func testPool() HostPool { -// pool := []*UpstreamHost{ -// { -// Name: workableServer.URL, // this should resolve (healthcheck test) -// }, -// { -// Name: "http://localhost:99998", // this shouldn't -// }, -// { -// Name: "http://C", -// }, -// } -// return HostPool(pool) -// } - -// func TestRoundRobinPolicy(t *testing.T) { -// pool := testPool() -// rrPolicy := &RoundRobin{} -// request, _ := http.NewRequest("GET", "/", nil) - -// h := rrPolicy.Select(pool, request) -// // First selected host is 1, because counter starts at 0 -// // and increments before host is selected -// if h != pool[1] { -// t.Error("Expected first round robin host to be second host in the pool.") -// } -// h = rrPolicy.Select(pool, request) -// if h != pool[2] { -// t.Error("Expected second round robin host to be third host in the pool.") -// } -// h = rrPolicy.Select(pool, request) -// if h != pool[0] { -// t.Error("Expected third round robin host to be first host in the pool.") -// } -// // mark host as down -// pool[1].Unhealthy = 1 -// h = rrPolicy.Select(pool, request) -// if h != pool[2] { -// t.Error("Expected to skip down host.") -// } -// // mark host as up -// pool[1].Unhealthy = 0 - -// h = rrPolicy.Select(pool, request) -// if h == pool[2] { -// t.Error("Expected to balance evenly among healthy hosts") -// } -// // mark host as full -// pool[1].Conns = 1 -// pool[1].MaxConns = 1 -// h = rrPolicy.Select(pool, request) -// if h != pool[2] { -// t.Error("Expected to skip full host.") -// } -// } - -// func TestLeastConnPolicy(t *testing.T) { -// pool := testPool() -// lcPolicy := &LeastConn{} -// request, _ := http.NewRequest("GET", "/", nil) - -// pool[0].Conns = 10 -// pool[1].Conns = 10 -// h := lcPolicy.Select(pool, request) -// if h != pool[2] { -// t.Error("Expected least connection host to be third host.") -// } -// pool[2].Conns = 100 -// h = lcPolicy.Select(pool, request) -// if h != pool[0] && h != pool[1] { -// t.Error("Expected least connection host to be first or second host.") -// } -// } - -// func TestCustomPolicy(t *testing.T) { -// pool := testPool() -// customPolicy := &customPolicy{} -// request, _ := http.NewRequest("GET", "/", nil) - -// h := customPolicy.Select(pool, request) -// if h != pool[0] { -// t.Error("Expected custom policy host to be the first host.") -// } -// } - -// func TestIPHashPolicy(t *testing.T) { -// pool := testPool() -// ipHash := &IPHash{} -// request, _ := http.NewRequest("GET", "/", nil) -// // We should be able to predict where every request is routed. -// request.RemoteAddr = "172.0.0.1:80" -// h := ipHash.Select(pool, request) -// if h != pool[1] { -// t.Error("Expected ip hash policy host to be the second host.") -// } -// request.RemoteAddr = "172.0.0.2:80" -// h = ipHash.Select(pool, request) -// if h != pool[1] { -// t.Error("Expected ip hash policy host to be the second host.") -// } -// request.RemoteAddr = "172.0.0.3:80" -// h = ipHash.Select(pool, request) -// if h != pool[2] { -// t.Error("Expected ip hash policy host to be the third host.") -// } -// request.RemoteAddr = "172.0.0.4:80" -// h = ipHash.Select(pool, request) -// if h != pool[1] { -// t.Error("Expected ip hash policy host to be the second host.") -// } - -// // we should get the same results without a port -// request.RemoteAddr = "172.0.0.1" -// h = ipHash.Select(pool, request) -// if h != pool[1] { -// t.Error("Expected ip hash policy host to be the second host.") -// } -// request.RemoteAddr = "172.0.0.2" -// h = ipHash.Select(pool, request) -// if h != pool[1] { -// t.Error("Expected ip hash policy host to be the second host.") -// } -// request.RemoteAddr = "172.0.0.3" -// h = ipHash.Select(pool, request) -// if h != pool[2] { -// t.Error("Expected ip hash policy host to be the third host.") -// } -// request.RemoteAddr = "172.0.0.4" -// h = ipHash.Select(pool, request) -// if h != pool[1] { -// t.Error("Expected ip hash policy host to be the second host.") -// } - -// // we should get a healthy host if the original host is unhealthy and a -// // healthy host is available -// request.RemoteAddr = "172.0.0.1" -// pool[1].Unhealthy = 1 -// h = ipHash.Select(pool, request) -// if h != pool[2] { -// t.Error("Expected ip hash policy host to be the third host.") -// } - -// request.RemoteAddr = "172.0.0.2" -// h = ipHash.Select(pool, request) -// if h != pool[2] { -// t.Error("Expected ip hash policy host to be the third host.") -// } -// pool[1].Unhealthy = 0 - -// request.RemoteAddr = "172.0.0.3" -// pool[2].Unhealthy = 1 -// h = ipHash.Select(pool, request) -// if h != pool[0] { -// t.Error("Expected ip hash policy host to be the first host.") -// } -// request.RemoteAddr = "172.0.0.4" -// h = ipHash.Select(pool, request) -// if h != pool[1] { -// t.Error("Expected ip hash policy host to be the second host.") -// } - -// // We should be able to resize the host pool and still be able to predict -// // where a request will be routed with the same IP's used above -// pool = []*UpstreamHost{ -// { -// Name: workableServer.URL, // this should resolve (healthcheck test) -// }, -// { -// Name: "http://localhost:99998", // this shouldn't -// }, -// } -// pool = HostPool(pool) -// request.RemoteAddr = "172.0.0.1:80" -// h = ipHash.Select(pool, request) -// if h != pool[0] { -// t.Error("Expected ip hash policy host to be the first host.") -// } -// request.RemoteAddr = "172.0.0.2:80" -// h = ipHash.Select(pool, request) -// if h != pool[1] { -// t.Error("Expected ip hash policy host to be the second host.") -// } -// request.RemoteAddr = "172.0.0.3:80" -// h = ipHash.Select(pool, request) -// if h != pool[0] { -// t.Error("Expected ip hash policy host to be the first host.") -// } -// request.RemoteAddr = "172.0.0.4:80" -// h = ipHash.Select(pool, request) -// if h != pool[1] { -// t.Error("Expected ip hash policy host to be the second host.") -// } - -// // We should get nil when there are no healthy hosts -// pool[0].Unhealthy = 1 -// pool[1].Unhealthy = 1 -// h = ipHash.Select(pool, request) -// if h != nil { -// t.Error("Expected ip hash policy host to be nil.") -// } -// } - -// func TestFirstPolicy(t *testing.T) { -// pool := testPool() -// firstPolicy := &First{} -// req := httptest.NewRequest(http.MethodGet, "/", nil) - -// h := firstPolicy.Select(pool, req) -// if h != pool[0] { -// t.Error("Expected first policy host to be the first host.") -// } - -// pool[0].Unhealthy = 1 -// h = firstPolicy.Select(pool, req) -// if h != pool[1] { -// t.Error("Expected first policy host to be the second host.") -// } -// } - -// func TestUriPolicy(t *testing.T) { -// pool := testPool() -// uriPolicy := &URIHash{} - -// request := httptest.NewRequest(http.MethodGet, "/test", nil) -// h := uriPolicy.Select(pool, request) -// if h != pool[0] { -// t.Error("Expected uri policy host to be the first host.") -// } - -// pool[0].Unhealthy = 1 -// h = uriPolicy.Select(pool, request) -// if h != pool[1] { -// t.Error("Expected uri policy host to be the first host.") -// } - -// request = httptest.NewRequest(http.MethodGet, "/test_2", nil) -// h = uriPolicy.Select(pool, request) -// if h != pool[1] { -// t.Error("Expected uri policy host to be the second host.") -// } - -// // We should be able to resize the host pool and still be able to predict -// // where a request will be routed with the same URI's used above -// pool = []*UpstreamHost{ -// { -// Name: workableServer.URL, // this should resolve (healthcheck test) -// }, -// { -// Name: "http://localhost:99998", // this shouldn't -// }, -// } - -// request = httptest.NewRequest(http.MethodGet, "/test", nil) -// h = uriPolicy.Select(pool, request) -// if h != pool[0] { -// t.Error("Expected uri policy host to be the first host.") -// } - -// pool[0].Unhealthy = 1 -// h = uriPolicy.Select(pool, request) -// if h != pool[1] { -// t.Error("Expected uri policy host to be the first host.") -// } - -// request = httptest.NewRequest(http.MethodGet, "/test_2", nil) -// h = uriPolicy.Select(pool, request) -// if h != pool[1] { -// t.Error("Expected uri policy host to be the second host.") -// } - -// pool[0].Unhealthy = 1 -// pool[1].Unhealthy = 1 -// h = uriPolicy.Select(pool, request) -// if h != nil { -// t.Error("Expected uri policy policy host to be nil.") -// } -// } - -// func TestHeaderPolicy(t *testing.T) { -// pool := testPool() -// tests := []struct { -// Name string -// Policy *Header -// RequestHeaderName string -// RequestHeaderValue string -// NilHost bool -// HostIndex int -// }{ -// {"empty config", &Header{""}, "", "", true, 0}, -// {"empty config+header+value", &Header{""}, "Affinity", "somevalue", true, 0}, -// {"empty config+header", &Header{""}, "Affinity", "", true, 0}, - -// {"no header(fallback to roundrobin)", &Header{"Affinity"}, "", "", false, 1}, -// {"no header(fallback to roundrobin)", &Header{"Affinity"}, "", "", false, 2}, -// {"no header(fallback to roundrobin)", &Header{"Affinity"}, "", "", false, 0}, - -// {"hash route to host", &Header{"Affinity"}, "Affinity", "somevalue", false, 1}, -// {"hash route to host", &Header{"Affinity"}, "Affinity", "somevalue2", false, 0}, -// {"hash route to host", &Header{"Affinity"}, "Affinity", "somevalue3", false, 2}, -// {"hash route with empty value", &Header{"Affinity"}, "Affinity", "", false, 1}, -// } - -// for idx, test := range tests { -// request, _ := http.NewRequest("GET", "/", nil) -// if test.RequestHeaderName != "" { -// request.Header.Add(test.RequestHeaderName, test.RequestHeaderValue) -// } - -// host := test.Policy.Select(pool, request) -// if test.NilHost && host != nil { -// t.Errorf("%d: Expected host to be nil", idx) -// } -// if !test.NilHost && host == nil { -// t.Errorf("%d: Did not expect host to be nil", idx) -// } -// if !test.NilHost && host != pool[test.HostIndex] { -// t.Errorf("%d: Expected Header policy to be host %d", idx, test.HostIndex) -// } -// } -// } +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func testPool() UpstreamPool { + return UpstreamPool{ + {Host: new(upstreamHost)}, + {Host: new(upstreamHost)}, + {Host: new(upstreamHost)}, + } +} + +func TestRoundRobinPolicy(t *testing.T) { + pool := testPool() + rrPolicy := new(RoundRobinSelection) + req, _ := http.NewRequest("GET", "/", nil) + + h := rrPolicy.Select(pool, req) + // First selected host is 1, because counter starts at 0 + // and increments before host is selected + if h != pool[1] { + t.Error("Expected first round robin host to be second host in the pool.") + } + h = rrPolicy.Select(pool, req) + if h != pool[2] { + t.Error("Expected second round robin host to be third host in the pool.") + } + h = rrPolicy.Select(pool, req) + if h != pool[0] { + t.Error("Expected third round robin host to be first host in the pool.") + } + // mark host as down + pool[1].SetHealthy(false) + h = rrPolicy.Select(pool, req) + if h != pool[2] { + t.Error("Expected to skip down host.") + } + // mark host as up + pool[1].SetHealthy(true) + + h = rrPolicy.Select(pool, req) + if h == pool[2] { + t.Error("Expected to balance evenly among healthy hosts") + } + // mark host as full + pool[1].CountRequest(1) + pool[1].MaxRequests = 1 + h = rrPolicy.Select(pool, req) + if h != pool[2] { + t.Error("Expected to skip full host.") + } +} + +func TestLeastConnPolicy(t *testing.T) { + pool := testPool() + lcPolicy := new(LeastConnSelection) + req, _ := http.NewRequest("GET", "/", nil) + + pool[0].CountRequest(10) + pool[1].CountRequest(10) + h := lcPolicy.Select(pool, req) + if h != pool[2] { + t.Error("Expected least connection host to be third host.") + } + pool[2].CountRequest(100) + h = lcPolicy.Select(pool, req) + if h != pool[0] && h != pool[1] { + t.Error("Expected least connection host to be first or second host.") + } +} + +func TestIPHashPolicy(t *testing.T) { + pool := testPool() + ipHash := new(IPHashSelection) + req, _ := http.NewRequest("GET", "/", nil) + + // We should be able to predict where every request is routed. + req.RemoteAddr = "172.0.0.1:80" + h := ipHash.Select(pool, req) + if h != pool[1] { + t.Error("Expected ip hash policy host to be the second host.") + } + req.RemoteAddr = "172.0.0.2:80" + h = ipHash.Select(pool, req) + if h != pool[1] { + t.Error("Expected ip hash policy host to be the second host.") + } + req.RemoteAddr = "172.0.0.3:80" + h = ipHash.Select(pool, req) + if h != pool[2] { + t.Error("Expected ip hash policy host to be the third host.") + } + req.RemoteAddr = "172.0.0.4:80" + h = ipHash.Select(pool, req) + if h != pool[1] { + t.Error("Expected ip hash policy host to be the second host.") + } + + // we should get the same results without a port + req.RemoteAddr = "172.0.0.1" + h = ipHash.Select(pool, req) + if h != pool[1] { + t.Error("Expected ip hash policy host to be the second host.") + } + req.RemoteAddr = "172.0.0.2" + h = ipHash.Select(pool, req) + if h != pool[1] { + t.Error("Expected ip hash policy host to be the second host.") + } + req.RemoteAddr = "172.0.0.3" + h = ipHash.Select(pool, req) + if h != pool[2] { + t.Error("Expected ip hash policy host to be the third host.") + } + req.RemoteAddr = "172.0.0.4" + h = ipHash.Select(pool, req) + if h != pool[1] { + t.Error("Expected ip hash policy host to be the second host.") + } + + // we should get a healthy host if the original host is unhealthy and a + // healthy host is available + req.RemoteAddr = "172.0.0.1" + pool[1].SetHealthy(false) + h = ipHash.Select(pool, req) + if h != pool[2] { + t.Error("Expected ip hash policy host to be the third host.") + } + + req.RemoteAddr = "172.0.0.2" + h = ipHash.Select(pool, req) + if h != pool[2] { + t.Error("Expected ip hash policy host to be the third host.") + } + pool[1].SetHealthy(true) + + req.RemoteAddr = "172.0.0.3" + pool[2].SetHealthy(false) + h = ipHash.Select(pool, req) + if h != pool[0] { + t.Error("Expected ip hash policy host to be the first host.") + } + req.RemoteAddr = "172.0.0.4" + h = ipHash.Select(pool, req) + if h != pool[1] { + t.Error("Expected ip hash policy host to be the second host.") + } + + // We should be able to resize the host pool and still be able to predict + // where a req will be routed with the same IP's used above + pool = UpstreamPool{ + {Host: new(upstreamHost)}, + {Host: new(upstreamHost)}, + } + req.RemoteAddr = "172.0.0.1:80" + h = ipHash.Select(pool, req) + if h != pool[0] { + t.Error("Expected ip hash policy host to be the first host.") + } + req.RemoteAddr = "172.0.0.2:80" + h = ipHash.Select(pool, req) + if h != pool[1] { + t.Error("Expected ip hash policy host to be the second host.") + } + req.RemoteAddr = "172.0.0.3:80" + h = ipHash.Select(pool, req) + if h != pool[0] { + t.Error("Expected ip hash policy host to be the first host.") + } + req.RemoteAddr = "172.0.0.4:80" + h = ipHash.Select(pool, req) + if h != pool[1] { + t.Error("Expected ip hash policy host to be the second host.") + } + + // We should get nil when there are no healthy hosts + pool[0].SetHealthy(false) + pool[1].SetHealthy(false) + h = ipHash.Select(pool, req) + if h != nil { + t.Error("Expected ip hash policy host to be nil.") + } +} + +func TestFirstPolicy(t *testing.T) { + pool := testPool() + firstPolicy := new(FirstSelection) + req := httptest.NewRequest(http.MethodGet, "/", nil) + + h := firstPolicy.Select(pool, req) + if h != pool[0] { + t.Error("Expected first policy host to be the first host.") + } + + pool[0].SetHealthy(false) + h = firstPolicy.Select(pool, req) + if h != pool[1] { + t.Error("Expected first policy host to be the second host.") + } +} + +func TestURIHashPolicy(t *testing.T) { + pool := testPool() + uriPolicy := new(URIHashSelection) + + request := httptest.NewRequest(http.MethodGet, "/test", nil) + h := uriPolicy.Select(pool, request) + if h != pool[0] { + t.Error("Expected uri policy host to be the first host.") + } + + pool[0].SetHealthy(false) + h = uriPolicy.Select(pool, request) + if h != pool[1] { + t.Error("Expected uri policy host to be the first host.") + } + + request = httptest.NewRequest(http.MethodGet, "/test_2", nil) + h = uriPolicy.Select(pool, request) + if h != pool[1] { + t.Error("Expected uri policy host to be the second host.") + } + + // We should be able to resize the host pool and still be able to predict + // where a request will be routed with the same URI's used above + pool = UpstreamPool{ + {Host: new(upstreamHost)}, + {Host: new(upstreamHost)}, + } + + request = httptest.NewRequest(http.MethodGet, "/test", nil) + h = uriPolicy.Select(pool, request) + if h != pool[0] { + t.Error("Expected uri policy host to be the first host.") + } + + pool[0].SetHealthy(false) + h = uriPolicy.Select(pool, request) + if h != pool[1] { + t.Error("Expected uri policy host to be the first host.") + } + + request = httptest.NewRequest(http.MethodGet, "/test_2", nil) + h = uriPolicy.Select(pool, request) + if h != pool[1] { + t.Error("Expected uri policy host to be the second host.") + } + + pool[0].SetHealthy(false) + pool[1].SetHealthy(false) + h = uriPolicy.Select(pool, request) + if h != nil { + t.Error("Expected uri policy policy host to be nil.") + } +} -- cgit v1.2.3