Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introduce konghq.com/headers-separator annotation and make parsing more robust #5977

Merged
merged 1 commit into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ Adding a new version? You'll need three changes:
- Improve validation - reject `Ingresses`, `Services` or `KongConsumers` that have multiple instances
of the same type plugin attached.
[#5972](https://github.com/Kong/kubernetes-ingress-controller/pull/5972)
- Added support for `konghq.com/headers-separator` that allows to set custom separator (instead of default `,`)
for headers specified with `konghq.com/headers.*` annotations. Moreover parsing a content of `konghq.com/headers.*`
is more robust - leading and trailing whitespace characters are discarded.
[#5977](https://github.com/Kong/kubernetes-ingress-controller/pull/5977)

### Fixed

Expand Down
12 changes: 10 additions & 2 deletions internal/annotations/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"strings"

"github.com/samber/lo"
netv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

Expand Down Expand Up @@ -61,6 +62,7 @@ const (
ReadTimeoutKey = "/read-timeout"
RetriesKey = "/retries"
HeadersKey = "/headers"
HeadersSeparatorKey = "/headers-separator"
PathHandlingKey = "/path-handling"
UserTagKey = "/tags"
RewriteURIKey = "/rewrite"
Expand Down Expand Up @@ -311,14 +313,20 @@ func ExtractRetries(anns map[string]string) (string, bool) {
// ExtractHeaders extracts the parsed headers annotations values. It returns a map of header names to slices of values.
func ExtractHeaders(anns map[string]string) (map[string][]string, bool) {
headers := make(map[string][]string)
prefix := AnnotationPrefix + HeadersKey + "."
const prefix = AnnotationPrefix + HeadersKey + "."
separator, ok := anns[AnnotationPrefix+HeadersSeparatorKey]
if !ok {
separator = ","
}
for key, val := range anns {
if strings.HasPrefix(key, prefix) {
header := strings.TrimPrefix(key, prefix)
if len(header) == 0 || len(val) == 0 {
continue
}
headers[header] = strings.Split(val, ",")
headers[header] = lo.Map(strings.Split(val, separator), func(hv string, _ int) string {
return strings.TrimSpace(hv)
})
}
}
if len(headers) == 0 {
Expand Down
59 changes: 59 additions & 0 deletions internal/annotations/annotations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,15 @@ func TestExtractHeaders(t *testing.T) {
name: "empty",
want: map[string][]string{},
},
{
name: "empty with custom separator",
args: args{
anns: map[string]string{
"konghq.com/headers-separator": ";",
},
},
want: map[string][]string{},
},
{
name: "non-empty",
args: args{
Expand All @@ -863,6 +872,15 @@ func TestExtractHeaders(t *testing.T) {
},
want: map[string][]string{},
},
{
name: "separator with no header results in empty header value",
args: args{
anns: map[string]string{
"konghq.com/headers.foo": "foo,",
},
},
want: map[string][]string{"foo": {"foo", ""}},
},
{
name: "no header name",
args: args{
Expand All @@ -872,6 +890,47 @@ func TestExtractHeaders(t *testing.T) {
},
want: map[string][]string{},
},
{
name: "multiple header, multiple values, trailing spaces",
args: args{
anns: map[string]string{
"konghq.com/headers.x-example": "foo, bar, baz ",
"konghq.com/headers.x-additional": "foo",
},
},
want: map[string][]string{
"x-example": {"foo", "bar", "baz"},
"x-additional": {"foo"},
},
},
{
name: "multiple header, multiple values, custom separator",
args: args{
anns: map[string]string{
"konghq.com/headers-separator": ";",
"konghq.com/headers.x-example": "foo, bar;baz",
"konghq.com/headers.x-additional": "foo",
},
},
want: map[string][]string{
"x-example": {"foo, bar", "baz"},
"x-additional": {"foo"},
},
},
{
name: "multiple header, multiple values, custom separator, leading & trailing spaces",
args: args{
anns: map[string]string{
"konghq.com/headers-separator": ";",
"konghq.com/headers.x-example": " foo, bar;cat,dog ; baz ",
"konghq.com/headers.x-additional": "foo;",
},
},
want: map[string][]string{
"x-example": {"foo, bar", "cat,dog", "baz"},
"x-additional": {"foo", ""},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
Loading