diff --git a/docs/guide/ingress/annotations.md b/docs/guide/ingress/annotations.md index edcf0babc..a5eb44dac 100644 --- a/docs/guide/ingress/annotations.md +++ b/docs/guide/ingress/annotations.md @@ -965,6 +965,15 @@ Custom attributes to LoadBalancers and TargetGroups can be controlled with follo ``` alb.ingress.kubernetes.io/listener-attributes.HTTP-80: routing.http.response.server.enabled=true ``` + - Add Access-Control-Allow-Headers header value (with comma) + ``` + alb.ingress.kubernetes.io/listener-attributes.HTTPS-443: "routing.http.response.access_control_allow_headers.header_value=exampleValue\\,anotherExampleValue" + ``` + or + ``` + alb.ingress.kubernetes.io/listener-attributes.HTTPS-443: routing.http.response.access_control_allow_headers.header_value=exampleValue\,anotherExampleValue + ``` + both result Access-Control-Allow-Headers header value: exampleValue,anotherExampleValue ## Resource Tags diff --git a/pkg/annotations/parser.go b/pkg/annotations/parser.go index 85f7de2b0..1ee4e4f16 100644 --- a/pkg/annotations/parser.go +++ b/pkg/annotations/parser.go @@ -3,9 +3,10 @@ package annotations import ( "encoding/json" "fmt" - "github.com/pkg/errors" "strconv" "strings" + + "github.com/pkg/errors" ) type ParseOptions struct { @@ -151,15 +152,15 @@ func (p *suffixAnnotationParser) ParseStringMapAnnotation(annotation string, val if !exists { return false, nil } - rawKVPairs := splitCommaSeparatedString(raw) + rawKVPairs := splitKeyValuePairs(raw) keyValues := make(map[string]string) for _, kvPair := range rawKVPairs { parts := strings.SplitN(kvPair, "=", 2) if len(parts) != 2 { return false, errors.Errorf("failed to parse stringMap annotation, %v: %v", matchedKey, raw) } - key := parts[0] - value := parts[1] + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) if len(key) == 0 { return false, errors.Errorf("failed to parse stringMap annotation, %v: %v", matchedKey, raw) } @@ -212,3 +213,41 @@ func splitCommaSeparatedString(commaSeparatedString string) []string { } return result } + +// splitKeyValuePairs this function supports escaped comma +func splitKeyValuePairs(s string) []string { + var result []string + var currentString strings.Builder + + for i := 0; i < len(s); i++ { + if s[i] == '\\' && i+1 < len(s) { + // Escape sequence - add the escaped character literally + currentString.WriteByte(s[i+1]) + i++ // skip the escaped character + } else if s[i] == ',' { + // Check if next part contains '=' (a new key-value pair) + remaining := strings.TrimSpace(s[i+1:]) + containsEqual := strings.Index(remaining, "=") + if containsEqual != -1 { + result = append(result, strings.TrimSpace(currentString.String())) + currentString.Reset() + } else if len(remaining) > 0 { + // There's content after comma but no '=' - this is malformed + // Add current content and the malformed part as separate items + result = append(result, strings.TrimSpace(currentString.String())) + result = append(result, remaining) + return result + } else { + // Empty after comma, add comma to current + currentString.WriteByte(s[i]) + } + } else { + currentString.WriteByte(s[i]) + } + } + + if currentString.Len() > 0 { + result = append(result, strings.TrimSpace(currentString.String())) + } + return result +} diff --git a/pkg/annotations/parser_test.go b/pkg/annotations/parser_test.go index 6a7fceba2..972adf834 100644 --- a/pkg/annotations/parser_test.go +++ b/pkg/annotations/parser_test.go @@ -2,8 +2,9 @@ package annotations import ( "errors" - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) func Test_annotationParser_ParseStringAnnotation(t *testing.T) { @@ -278,6 +279,22 @@ func Test_serviceAnnotationParser_ParseStringMapAnnotation(t *testing.T) { "key4/empty-value": "", }, }, + { + name: "multiple values", + prefix: "p.co", + suffix: "sfx", + annotations: map[string]string{ + "first-value": "1", + "p.co/sfx": "key1=value1\\,valueOne, key2= value2, key3/with-slash=value3\\,valueThree, key4/empty-value=", + }, + wantExist: true, + wantValue: map[string]string{ + "key1": "value1,valueOne", + "key2": "value2", + "key3/with-slash": "value3,valueThree", + "key4/empty-value": "", + }, + }, { name: "values containing equals sign", prefix: "prefix",