Remove global-rate-limit feature (#11851)

This commit is contained in:
Ricardo Katz 2024-08-25 17:03:29 -03:00 committed by GitHub
parent 5243b9b90a
commit 21cd966d1c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 28 additions and 1326 deletions

View file

@ -718,31 +718,6 @@ type Configuration struct {
// Default: text/html
DefaultType string `json:"default-type"`
// GlobalRateLimitMemcachedHost configures memcached host.
GlobalRateLimitMemcachedHost string `json:"global-rate-limit-memcached-host"`
// GlobalRateLimitMemcachedPort configures memcached port.
GlobalRateLimitMemcachedPort int `json:"global-rate-limit-memcached-port"`
// GlobalRateLimitMemcachedConnectTimeout configures timeout when connecting to memcached.
// The unit is millisecond.
GlobalRateLimitMemcachedConnectTimeout int `json:"global-rate-limit-memcached-connect-timeout"`
// GlobalRateLimitMemcachedMaxIdleTimeout configured how long connections
// should be kept alive in idle state. The unit is millisecond.
GlobalRateLimitMemcachedMaxIdleTimeout int `json:"global-rate-limit-memcached-max-idle-timeout"`
// GlobalRateLimitMemcachedPoolSize configures how many connections
// should be kept alive in the pool.
// Note that this is per NGINX worker. Make sure your memcached server can
// handle `MemcachedPoolSize * <nginx worker count> * <nginx replica count>`
// simultaneous connections.
GlobalRateLimitMemcachedPoolSize int `json:"global-rate-limit-memcached-pool-size"`
// GlobalRateLimitStatusCode determines the HTTP status code to return
// when limit is exceeding during global rate limiting.
GlobalRateLimitStatusCode int `json:"global-rate-limit-status-code"`
// DebugConnections Enables debugging log for selected client connections
// http://nginx.org/en/docs/ngx_core_module.html#debug_connection
// Default: ""
@ -893,39 +868,34 @@ func NewDefault() Configuration {
ServiceUpstream: false,
AllowedResponseHeaders: []string{},
},
UpstreamKeepaliveConnections: 320,
UpstreamKeepaliveTime: "1h",
UpstreamKeepaliveTimeout: 60,
UpstreamKeepaliveRequests: 10000,
LimitConnZoneVariable: defaultLimitConnZoneVariable,
BindAddressIpv4: defBindAddress,
BindAddressIpv6: defBindAddress,
OpentelemetryTrustIncomingSpan: true,
OpentelemetryConfig: "/etc/ingress-controller/telemetry/opentelemetry.toml",
OtlpCollectorPort: "4317",
OtelServiceName: "nginx",
OtelSampler: "AlwaysOn",
OtelSamplerRatio: 0.01,
OtelSamplerParentBased: true,
OtelScheduleDelayMillis: 5000,
OtelMaxExportBatchSize: 512,
OtelMaxQueueSize: 2048,
LimitReqStatusCode: 503,
LimitConnStatusCode: 503,
SyslogPort: 514,
NoTLSRedirectLocations: "/.well-known/acme-challenge",
NoAuthLocations: "/.well-known/acme-challenge",
GlobalExternalAuth: defGlobalExternalAuth,
ProxySSLLocationOnly: false,
DefaultType: "text/html",
GlobalRateLimitMemcachedPort: 11211,
GlobalRateLimitMemcachedConnectTimeout: 50,
GlobalRateLimitMemcachedMaxIdleTimeout: 10000,
GlobalRateLimitMemcachedPoolSize: 50,
GlobalRateLimitStatusCode: 429,
DebugConnections: []string{},
StrictValidatePathType: true,
GRPCBufferSizeKb: 0,
UpstreamKeepaliveConnections: 320,
UpstreamKeepaliveTime: "1h",
UpstreamKeepaliveTimeout: 60,
UpstreamKeepaliveRequests: 10000,
LimitConnZoneVariable: defaultLimitConnZoneVariable,
BindAddressIpv4: defBindAddress,
BindAddressIpv6: defBindAddress,
OpentelemetryTrustIncomingSpan: true,
OpentelemetryConfig: "/etc/ingress-controller/telemetry/opentelemetry.toml",
OtlpCollectorPort: "4317",
OtelServiceName: "nginx",
OtelSampler: "AlwaysOn",
OtelSamplerRatio: 0.01,
OtelSamplerParentBased: true,
OtelScheduleDelayMillis: 5000,
OtelMaxExportBatchSize: 512,
OtelMaxQueueSize: 2048,
LimitReqStatusCode: 503,
LimitConnStatusCode: 503,
SyslogPort: 514,
NoTLSRedirectLocations: "/.well-known/acme-challenge",
NoAuthLocations: "/.well-known/acme-challenge",
GlobalExternalAuth: defGlobalExternalAuth,
ProxySSLLocationOnly: false,
DefaultType: "text/html",
DebugConnections: []string{},
StrictValidatePathType: true,
GRPCBufferSizeKb: 0,
}
if klog.V(5).Enabled() {

View file

@ -379,10 +379,6 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
if !cfg.AllowSnippetAnnotations && strings.HasSuffix(key, "-snippet") {
return fmt.Errorf("%s annotation cannot be used. Snippet directives are disabled by the Ingress administrator", key)
}
if cfg.GlobalRateLimitMemcachedHost == "" && strings.HasPrefix(key, fmt.Sprintf("%s/%s", parser.AnnotationsPrefix, "global-rate-limit")) {
return fmt.Errorf("'global-rate-limit*' annotations require 'global-rate-limit-memcached-host' settings configured in the global configmap")
}
}
k8s.SetDefaultNGINXPathType(ing)
@ -1516,7 +1512,6 @@ func locationApplyAnnotations(loc *ingress.Location, anns *annotations.Ingress)
loc.Proxy = anns.Proxy
loc.ProxySSL = anns.ProxySSL
loc.RateLimit = anns.RateLimit
loc.GlobalRateLimit = anns.GlobalRateLimit
loc.Redirect = anns.Redirect
loc.Rewrite = anns.Rewrite
loc.UpstreamVhost = anns.UpstreamVhost

View file

@ -82,7 +82,6 @@ var (
"balancer_ewma_locks": 1024,
"certificate_servers": 5120,
"ocsp_response_cache": 5120, // keep this same as certificate_servers
"global_throttle_cache": 10240,
}
defaultGlobalAuthRedirectParam = "rd"
)

View file

@ -403,12 +403,6 @@ func configForLua(input interface{}) string {
hsts_include_subdomains = %t,
hsts_preload = %t,
global_throttle = {
memcached = {
host = "%v", port = %d, connect_timeout = %d, max_idle_timeout = %d, pool_size = %d,
},
status_code = %d,
}
}`,
all.Cfg.UseForwardedHeaders,
all.Cfg.UseProxyProtocol,
@ -421,13 +415,6 @@ func configForLua(input interface{}) string {
all.Cfg.HSTSMaxAge,
all.Cfg.HSTSIncludeSubdomains,
all.Cfg.HSTSPreload,
all.Cfg.GlobalRateLimitMemcachedHost,
all.Cfg.GlobalRateLimitMemcachedPort,
all.Cfg.GlobalRateLimitMemcachedConnectTimeout,
all.Cfg.GlobalRateLimitMemcachedMaxIdleTimeout,
all.Cfg.GlobalRateLimitMemcachedPoolSize,
all.Cfg.GlobalRateLimitStatusCode,
)
}
@ -445,30 +432,18 @@ func locationConfigForLua(l, a interface{}) string {
return "{}"
}
ignoredCIDRs, err := convertGoSliceIntoLuaTable(location.GlobalRateLimit.IgnoredCIDRs, false)
if err != nil {
klog.Errorf("failed to convert %v into Lua table: %q", location.GlobalRateLimit.IgnoredCIDRs, err)
ignoredCIDRs = "{}"
}
return fmt.Sprintf(`{
force_ssl_redirect = %t,
ssl_redirect = %t,
force_no_ssl_redirect = %t,
preserve_trailing_slash = %t,
use_port_in_redirects = %t,
global_throttle = { namespace = "%v", limit = %d, window_size = %d, key = %v, ignored_cidrs = %v },
}`,
location.Rewrite.ForceSSLRedirect,
location.Rewrite.SSLRedirect,
isLocationInLocationList(l, all.Cfg.NoTLSRedirectLocations),
location.Rewrite.PreserveTrailingSlash,
location.UsePortInRedirects,
location.GlobalRateLimit.Namespace,
location.GlobalRateLimit.Limit,
location.GlobalRateLimit.WindowSize,
parseComplexNginxVarIntoLuaTable(location.GlobalRateLimit.Key),
ignoredCIDRs,
)
}
@ -1690,54 +1665,6 @@ func buildServerName(hostname string) string {
return `~^(?<subdomain>[\w-]+)\.` + strings.Join(parts, "\\.") + `$`
}
// parseComplexNginxVarIntoLuaTable parses things like "$my${complex}ngx\$var" into
// [["$var", "complex", "my", "ngx"]]. In other words, 2nd and 3rd elements
// in the result are actual NGINX variable names, whereas first and 4th elements
// are string literals.
func parseComplexNginxVarIntoLuaTable(ngxVar string) string {
r := regexp.MustCompile(`(\\\$[0-9a-zA-Z_]+)|\$\{([0-9a-zA-Z_]+)\}|\$([0-9a-zA-Z_]+)|(\$|[^$\\]+)`)
matches := r.FindAllStringSubmatch(ngxVar, -1)
components := make([][]string, len(matches))
for i, match := range matches {
components[i] = match[1:]
}
luaTable, err := convertGoSliceIntoLuaTable(components, true)
if err != nil {
klog.Errorf("unexpected error: %v", err)
luaTable = "{}"
}
return luaTable
}
func convertGoSliceIntoLuaTable(goSliceInterface interface{}, emptyStringAsNil bool) (string, error) {
goSlice := reflect.ValueOf(goSliceInterface)
kind := goSlice.Kind()
switch kind {
case reflect.String:
if emptyStringAsNil && goSlice.Interface().(string) == "" {
return "nil", nil
}
return fmt.Sprintf(`"%v"`, goSlice.Interface()), nil
case reflect.Int, reflect.Bool:
return fmt.Sprintf(`%v`, goSlice.Interface()), nil
case reflect.Slice, reflect.Array:
luaTable := "{ "
for i := 0; i < goSlice.Len(); i++ {
luaEl, err := convertGoSliceIntoLuaTable(goSlice.Index(i).Interface(), emptyStringAsNil)
if err != nil {
return "", err
}
luaTable = luaTable + luaEl + ", "
}
luaTable += "}"
return luaTable, nil
default:
return "", fmt.Errorf("could not process type: %s", kind)
}
}
func buildOriginRegex(origin string) string {
origin = regexp.QuoteMeta(origin)
origin = strings.Replace(origin, "\\*", `[A-Za-z0-9\-]+`, 1)

View file

@ -1926,89 +1926,6 @@ func TestBuildServerName(t *testing.T) {
}
}
func TestParseComplexNginxVarIntoLuaTable(t *testing.T) {
testCases := []struct {
ngxVar string
expectedLuaTable string
}{
{"foo", `{ { nil, nil, nil, "foo", }, }`},
{"$foo", `{ { nil, nil, "foo", nil, }, }`},
{"${foo}", `{ { nil, "foo", nil, nil, }, }`},
{"\\$foo", `{ { "\$foo", nil, nil, nil, }, }`},
{
"foo\\$bar$baz${daz}xiyar$pomidor",
`{ { nil, nil, nil, "foo", }, { "\$bar", nil, nil, nil, }, { nil, nil, "baz", nil, }, ` +
`{ nil, "daz", nil, nil, }, { nil, nil, nil, "xiyar", }, { nil, nil, "pomidor", nil, }, }`,
},
}
for _, testCase := range testCases {
actualLuaTable := parseComplexNginxVarIntoLuaTable(testCase.ngxVar)
if actualLuaTable != testCase.expectedLuaTable {
t.Errorf("expected %v but returned %v", testCase.expectedLuaTable, actualLuaTable)
}
}
}
func TestConvertGoSliceIntoLuaTablet(t *testing.T) {
testCases := []struct {
title string
goSlice interface{}
emptyStringAsNil bool
expectedLuaTable string
expectedErr error
}{
{
"flat string slice",
[]string{"one", "two", "three"},
false,
`{ "one", "two", "three", }`,
nil,
},
{
"nested string slice",
[][]string{{"one", "", "three"}, {"foo", "bar"}},
false,
`{ { "one", "", "three", }, { "foo", "bar", }, }`,
nil,
},
{
"converts empty string to nil when enabled",
[][]string{{"one", "", "three"}, {"foo", "bar"}},
true,
`{ { "one", nil, "three", }, { "foo", "bar", }, }`,
nil,
},
{
"boolean slice",
[]bool{true, true, false},
false,
`{ true, true, false, }`,
nil,
},
{
"integer slice",
[]int{4, 3, 6},
false,
`{ 4, 3, 6, }`,
nil,
},
}
for _, testCase := range testCases {
actualLuaTable, err := convertGoSliceIntoLuaTable(testCase.goSlice, testCase.emptyStringAsNil)
if testCase.expectedErr != nil && err != nil && testCase.expectedErr.Error() != err.Error() {
t.Errorf("expected error '%v' but returned '%v'", testCase.expectedErr, err)
}
if testCase.expectedErr == nil && err != nil {
t.Errorf("expected error to be nil but returned '%v'", err)
}
if testCase.expectedLuaTable != actualLuaTable {
t.Errorf("%v: expected '%v' but returned '%v'", testCase.title, testCase.expectedLuaTable, actualLuaTable)
}
}
}
func TestCleanConf(t *testing.T) {
testDataDir, err := getTestDataDir()
if err != nil {