mirror of
https://github.com/pomerium/pomerium.git
synced 2025-08-03 16:59:22 +02:00
envoy: add hash policy and routing key for hash-based load balancers (#2791)
* envoy: add hash policy and routing key for hash-based load balancers * fix integration test * fix nginx
This commit is contained in:
parent
bd0a5389bf
commit
c97dcf7e0f
56 changed files with 12935 additions and 182 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -4,12 +4,15 @@ import (
|
|||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/pomerium/pomerium/integration/flows"
|
||||
)
|
||||
|
||||
func TestCORS(t *testing.T) {
|
||||
|
@ -248,3 +251,72 @@ func TestGoogleCloudRun(t *testing.T) {
|
|||
assert.NotEmpty(t, result.Headers["authorization"], "expected authorization header when cloudrun is enabled")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadBalancer(t *testing.T) {
|
||||
if ClusterType == "traefik" || ClusterType == "nginx" {
|
||||
t.Skip()
|
||||
return
|
||||
}
|
||||
|
||||
ctx, clearTimeout := context.WithTimeout(context.Background(), time.Minute*10)
|
||||
defer clearTimeout()
|
||||
|
||||
getDistribution := func(t *testing.T, path string) map[string]float64 {
|
||||
client := getClient()
|
||||
distribution := map[string]float64{}
|
||||
|
||||
res, err := flows.Authenticate(ctx, client,
|
||||
mustParseURL("https://httpdetails.localhost.pomerium.io/"+path),
|
||||
flows.WithEmail("user1@dogs.test"))
|
||||
if !assert.NoError(t, err) {
|
||||
return distribution
|
||||
}
|
||||
_, _ = io.ReadAll(res.Body)
|
||||
_ = res.Body.Close()
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET",
|
||||
"https://httpdetails.localhost.pomerium.io/"+path, nil)
|
||||
if !assert.NoError(t, err) {
|
||||
return distribution
|
||||
}
|
||||
|
||||
res, err = client.Do(req)
|
||||
if !assert.NoError(t, err) {
|
||||
return distribution
|
||||
}
|
||||
|
||||
var result struct {
|
||||
Hostname string `json:"hostname"`
|
||||
}
|
||||
err = json.NewDecoder(res.Body).Decode(&result)
|
||||
_ = res.Body.Close()
|
||||
assert.NoError(t, err)
|
||||
distribution[result.Hostname]++
|
||||
}
|
||||
|
||||
return distribution
|
||||
}
|
||||
|
||||
t.Run("round robin", func(t *testing.T) {
|
||||
distribution := getDistribution(t, "round-robin")
|
||||
var xs []float64
|
||||
for _, x := range distribution {
|
||||
xs = append(xs, x)
|
||||
}
|
||||
assert.Lessf(t, standardDeviation(xs), 10.0, "should distribute requests evenly, got: %v",
|
||||
distribution)
|
||||
})
|
||||
|
||||
t.Run("ring hash", func(t *testing.T) {
|
||||
distribution := getDistribution(t, "ring-hash")
|
||||
assert.Lenf(t, distribution, 1, "should distribute requests to a single backend, got: %v",
|
||||
distribution)
|
||||
})
|
||||
|
||||
t.Run("maglev", func(t *testing.T) {
|
||||
distribution := getDistribution(t, "maglev")
|
||||
assert.Lenf(t, distribution, 1, "should distribute requests to a single backend, got: %v",
|
||||
distribution)
|
||||
})
|
||||
}
|
||||
|
|
26
integration/stats.go
Normal file
26
integration/stats.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package main
|
||||
|
||||
import "math"
|
||||
|
||||
func mean(xs []float64) float64 {
|
||||
var sum float64
|
||||
for _, x := range xs {
|
||||
sum += x
|
||||
}
|
||||
return sum / float64(len(xs))
|
||||
}
|
||||
|
||||
func variance(xs []float64) float64 {
|
||||
m := mean(xs)
|
||||
|
||||
var sum float64
|
||||
for _, x := range xs {
|
||||
dx := x - m
|
||||
sum += dx * dx
|
||||
}
|
||||
return sum / float64(len(xs))
|
||||
}
|
||||
|
||||
func standardDeviation(xs []float64) float64 {
|
||||
return math.Sqrt(variance(xs))
|
||||
}
|
|
@ -7,6 +7,21 @@ local Variations() =
|
|||
cert: importstr '../files/trusted.pem',
|
||||
key: importstr '../files/trusted-key.pem',
|
||||
},
|
||||
{
|
||||
name: 'trusted-1',
|
||||
cert: importstr '../files/trusted.pem',
|
||||
key: importstr '../files/trusted-key.pem',
|
||||
},
|
||||
{
|
||||
name: 'trusted-2',
|
||||
cert: importstr '../files/trusted.pem',
|
||||
key: importstr '../files/trusted-key.pem',
|
||||
},
|
||||
{
|
||||
name: 'trusted-3',
|
||||
cert: importstr '../files/trusted.pem',
|
||||
key: importstr '../files/trusted-key.pem',
|
||||
},
|
||||
{
|
||||
name: 'untrusted',
|
||||
cert: importstr '../files/untrusted.pem',
|
||||
|
|
|
@ -88,6 +88,9 @@ local RouteLocationConfig(route) =
|
|||
if std.objectHas(route, 'prefix') then '^~ ' + route.prefix
|
||||
else if std.objectHas(route, 'path') then '= ' + route.path
|
||||
else '/';
|
||||
local to =
|
||||
if std.isArray(route.to) then route.to[0]
|
||||
else route.to;
|
||||
|||
|
||||
location %s {
|
||||
proxy_pass %s;
|
||||
|
@ -100,7 +103,7 @@ local RouteLocationConfig(route) =
|
|||
auth_request_set $auth_cookie $upstream_http_set_cookie;
|
||||
add_header Set-Cookie $auth_cookie;
|
||||
}
|
||||
||| % [rule, route.to];
|
||||
||| % [rule, to];
|
||||
|
||||
local DomainServerConfig(domain, routes) =
|
||||
local locations = std.join('\n', std.map(function(route) RouteLocationConfig(route), routes));
|
||||
|
|
|
@ -121,6 +121,9 @@ local ComposeService(name, definition, additionalAliases=[]) =
|
|||
'mock-idp',
|
||||
'redis',
|
||||
'trusted-httpdetails',
|
||||
'trusted-1-httpdetails',
|
||||
'trusted-2-httpdetails',
|
||||
'trusted-3-httpdetails',
|
||||
'untrusted-httpdetails',
|
||||
'verify',
|
||||
'websocket-echo',
|
||||
|
|
|
@ -149,6 +149,42 @@ local Routes(mode, idp, dns_suffix) =
|
|||
allowed_users: ['user1@dogs.test'],
|
||||
pass_identity_headers: true,
|
||||
},
|
||||
// round robin load balancer
|
||||
{
|
||||
from: 'https://httpdetails.localhost.pomerium.io',
|
||||
to: [
|
||||
'http://trusted-1-httpdetails' + dns_suffix + ':8080',
|
||||
'http://trusted-2-httpdetails' + dns_suffix + ':8080',
|
||||
'http://trusted-3-httpdetails' + dns_suffix + ':8080',
|
||||
],
|
||||
prefix: '/round-robin',
|
||||
allow_any_authenticated_user: true,
|
||||
lb_policy: 'ROUND_ROBIN',
|
||||
},
|
||||
// ring hash load balancer
|
||||
{
|
||||
from: 'https://httpdetails.localhost.pomerium.io',
|
||||
to: [
|
||||
'http://trusted-1-httpdetails' + dns_suffix + ':8080',
|
||||
'http://trusted-2-httpdetails' + dns_suffix + ':8080',
|
||||
'http://trusted-3-httpdetails' + dns_suffix + ':8080',
|
||||
],
|
||||
prefix: '/ring-hash',
|
||||
allow_any_authenticated_user: true,
|
||||
lb_policy: 'RING_HASH',
|
||||
},
|
||||
// maglev load balancer
|
||||
{
|
||||
from: 'https://httpdetails.localhost.pomerium.io',
|
||||
to: [
|
||||
'http://trusted-1-httpdetails' + dns_suffix + ':8080',
|
||||
'http://trusted-2-httpdetails' + dns_suffix + ':8080',
|
||||
'http://trusted-3-httpdetails' + dns_suffix + ':8080',
|
||||
],
|
||||
prefix: '/maglev',
|
||||
allow_any_authenticated_user: true,
|
||||
lb_policy: 'MAGLEV',
|
||||
},
|
||||
// catch-all
|
||||
{
|
||||
from: 'https://httpdetails.localhost.pomerium.io',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue