Skip to content
36 changes: 36 additions & 0 deletions internal/configs/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,42 @@ func parseAnnotations(ingEx *IngressEx, baseCfgParams *ConfigParams, isPlus bool
//gocyclo:ignore
func parseRateLimitAnnotations(annotations map[string]string, cfgParams *ConfigParams, context apiObject) []error {
errors := make([]error, 0)

rateLimitAnnotations := []string{
"nginx.org/limit-req-rate",
"nginx.org/limit-req-key",
"nginx.org/limit-req-zone-size",
"nginx.org/limit-req-burst",
"nginx.org/limit-req-delay",
"nginx.org/limit-req-no-delay",
"nginx.org/limit-req-dry-run",
"nginx.org/limit-req-log-level",
"nginx.org/limit-req-reject-code",
"nginx.org/limit-req-scale",
}

hasRateLimitAnnotation := false
for _, annotation := range rateLimitAnnotations {
if _, exists := annotations[annotation]; exists {
hasRateLimitAnnotation = true
break
}
}

if hasRateLimitAnnotation {
mandatoryAnnotations := []string{
"nginx.org/limit-req-rate",
"nginx.org/limit-req-key",
"nginx.org/limit-req-zone-size",
}

for _, mandatory := range mandatoryAnnotations {
if _, exists := annotations[mandatory]; !exists {
errors = append(errors, fmt.Errorf("ingress %s/%s: rate-limiting configuration requires mandatory annotation %s", context.GetNamespace(), context.GetName(), mandatory))
}
}
}

if requestRateLimit, exists := annotations["nginx.org/limit-req-rate"]; exists {
if rate, err := ParseRequestRate(requestRateLimit); err != nil {
errors = append(errors, fmt.Errorf("ingress %s/%s: invalid value for nginx.org/limit-req-rate: got %s: %w", context.GetNamespace(), context.GetName(), requestRateLimit, err))
Expand Down
33 changes: 33 additions & 0 deletions internal/configs/annotations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,39 @@ func TestParseRateLimitAnnotations(t *testing.T) {
}, NewDefaultConfigParams(context.Background(), false), ctx); len(errors) == 0 {
t.Error("No Errors when parsing invalid log level")
}

if errors := parseRateLimitAnnotations(map[string]string{
"nginx.org/limit-req-rate": "1r/s",
}, NewDefaultConfigParams(context.Background(), false), ctx); len(errors) != 2 {
t.Errorf("Expected 2 errors for missing mandatory annotations, got %d", len(errors))
}

if errors := parseRateLimitAnnotations(map[string]string{
"nginx.org/limit-req-rate": "1r/s",
"nginx.org/limit-req-key": "${binary_remote_addr}",
}, NewDefaultConfigParams(context.Background(), false), ctx); len(errors) != 1 {
t.Errorf("Expected 1 error for missing mandatory annotation, got %d", len(errors))
}

if errors := parseRateLimitAnnotations(map[string]string{
"nginx.org/limit-req-burst": "10",
}, NewDefaultConfigParams(context.Background(), false), ctx); len(errors) != 3 {
t.Errorf("Expected 3 errors for missing all mandatory annotations, got %d", len(errors))
}

if errors := parseRateLimitAnnotations(map[string]string{
"nginx.org/proxy-connect-timeout": "30s",
}, NewDefaultConfigParams(context.Background(), false), ctx); len(errors) != 0 {
t.Errorf("Expected 0 errors for non rate-limiting annotations, got %d", len(errors))
}

if errors := parseRateLimitAnnotations(map[string]string{
"nginx.org/limit-req-rate": "1r/s",
"nginx.org/limit-req-key": "${binary_remote_addr}",
"nginx.org/limit-req-zone-size": "10m",
}, NewDefaultConfigParams(context.Background(), false), ctx); len(errors) != 0 {
t.Errorf("Expected 0 errors for complete mandatory annotations, got %d", len(errors))
}
}

func BenchmarkParseRewrites(b *testing.B) {
Expand Down
Loading