-
Notifications
You must be signed in to change notification settings - Fork 351
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
filters/builtin: add annotate filter (#3022)
Add `annotate` filter to annotate routes and a helper to retrieve annotations. Signed-off-by: Alexander Yastrebov <alexander.yastrebov@zalando.de>
- Loading branch information
1 parent
36f8db4
commit f17057c
Showing
5 changed files
with
170 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package builtin | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/zalando/skipper/filters" | ||
) | ||
|
||
type ( | ||
annotateSpec struct{} | ||
|
||
annotateFilter struct { | ||
key, value string | ||
} | ||
) | ||
|
||
const annotateStateBagKey = "filter." + filters.AnnotateName | ||
|
||
// NewAnnotate is a filter to annotate a filter chain. | ||
// It stores its key and value arguments into the filter context. | ||
// Use [GetAnnotations] to retrieve the annotations from the context. | ||
func NewAnnotate() filters.Spec { | ||
return annotateSpec{} | ||
} | ||
|
||
func (annotateSpec) Name() string { | ||
return filters.AnnotateName | ||
} | ||
|
||
func (as annotateSpec) CreateFilter(args []interface{}) (filters.Filter, error) { | ||
if len(args) != 2 { | ||
return nil, fmt.Errorf("requires string key and value arguments") | ||
} | ||
|
||
af, ok := &annotateFilter{}, false | ||
if af.key, ok = args[0].(string); !ok { | ||
return nil, fmt.Errorf("key argument must be a string") | ||
} | ||
|
||
if af.value, ok = args[1].(string); !ok { | ||
return nil, fmt.Errorf("value argument must be a string") | ||
} | ||
|
||
return af, nil | ||
} | ||
|
||
func (af *annotateFilter) Request(ctx filters.FilterContext) { | ||
if v, ok := ctx.StateBag()[annotateStateBagKey]; ok { | ||
v.(map[string]string)[af.key] = af.value | ||
} else { | ||
ctx.StateBag()[annotateStateBagKey] = map[string]string{af.key: af.value} | ||
} | ||
} | ||
|
||
func (af *annotateFilter) Response(filters.FilterContext) {} | ||
|
||
func GetAnnotations(ctx filters.FilterContext) map[string]string { | ||
if v, ok := ctx.StateBag()[annotateStateBagKey]; ok { | ||
return v.(map[string]string) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package builtin_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"github.com/zalando/skipper/eskip" | ||
"github.com/zalando/skipper/filters/builtin" | ||
"github.com/zalando/skipper/filters/filtertest" | ||
) | ||
|
||
func TestAnnotate(t *testing.T) { | ||
spec := builtin.NewAnnotate() | ||
|
||
for _, tc := range []struct { | ||
name string | ||
def string | ||
expected map[string]string | ||
}{ | ||
{ | ||
name: "key and value", | ||
def: `annotate("akey", "avalue")`, | ||
expected: map[string]string{"akey": "avalue"}, | ||
}, | ||
{ | ||
name: "multiple annotations", | ||
def: `annotate("akey1", "avalue1") -> annotate("akey2", "avalue2")`, | ||
expected: map[string]string{"akey1": "avalue1", "akey2": "avalue2"}, | ||
}, | ||
{ | ||
name: "overwrite annotation", | ||
def: `annotate("akey1", "avalue1") -> annotate("akey1", "avalue2")`, | ||
expected: map[string]string{"akey1": "avalue2"}, | ||
}, | ||
} { | ||
t.Run(tc.name, func(t *testing.T) { | ||
ctx := &filtertest.Context{ | ||
FStateBag: make(map[string]interface{}), | ||
} | ||
|
||
for _, f := range eskip.MustParseFilters(tc.def) { | ||
filter, err := spec.CreateFilter(f.Args) | ||
require.NoError(t, err) | ||
|
||
filter.Request(ctx) | ||
} | ||
|
||
assert.Equal(t, tc.expected, builtin.GetAnnotations(ctx)) | ||
}) | ||
} | ||
} | ||
|
||
func TestAnnotateArgs(t *testing.T) { | ||
spec := builtin.NewAnnotate() | ||
|
||
t.Run("valid", func(t *testing.T) { | ||
for _, def := range []string{ | ||
`annotate("akey", "avalue")`, | ||
} { | ||
t.Run(def, func(t *testing.T) { | ||
args := eskip.MustParseFilters(def)[0].Args | ||
|
||
_, err := spec.CreateFilter(args) | ||
assert.NoError(t, err) | ||
}) | ||
} | ||
}) | ||
|
||
t.Run("invalid", func(t *testing.T) { | ||
for _, def := range []string{ | ||
`annotate()`, | ||
`annotate("akey")`, | ||
`annotate(1)`, | ||
`annotate("akey", 1)`, | ||
`annotate("akey", "avalue", "anextra")`, | ||
`annotate("akey", "avalue", 1)`, | ||
} { | ||
t.Run(def, func(t *testing.T) { | ||
args := eskip.MustParseFilters(def)[0].Args | ||
|
||
_, err := spec.CreateFilter(args) | ||
assert.Error(t, err) | ||
}) | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters