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: add wasm filter-chain entity support #72

Merged
merged 1 commit into from
May 13, 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
2 changes: 2 additions & 0 deletions pkg/diff/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,8 @@ func (sc *Syncer) init() error {
types.RBACRole, types.RBACEndpointPermission,

types.ServicePackage, types.ServiceVersion, types.Document,

types.FilterChain,
}

sc.entityDiffers = map[types.EntityType]types.Differ{}
Expand Down
4 changes: 3 additions & 1 deletion pkg/diff/order.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ L3 +---------------------------> Service <---+ +-> Route |
| Version | | |
| | | | |
| | | v |
L4 +----------> Document <---------+ +-> Plugins <----------+
L4 +----------> Document <---------+ +-> Plugins / <---------+
FilterChains
*/

// dependencyOrder defines the order in which entities will be synced by decK.
Expand Down Expand Up @@ -61,6 +62,7 @@ var dependencyOrder = [][]types.EntityType{
},
{
types.Plugin,
types.FilterChain,
types.Document,
},
}
Expand Down
40 changes: 40 additions & 0 deletions pkg/dump/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ type Config struct {

// IsConsumerGroupScopedPluginSupported
IsConsumerGroupScopedPluginSupported bool

// IsFilterChainsSupported
IsFilterChainsSupported bool
}

func deduplicate(stringSlice []string) []string {
Expand Down Expand Up @@ -252,6 +255,19 @@ func getProxyConfiguration(ctx context.Context, group *errgroup.Group,
return nil
})

if config.IsFilterChainsSupported {
group.Go(func() error {
filterChains, err := GetAllFilterChains(ctx, client, config.SelectorTags)
if err != nil {
return fmt.Errorf("filter chains: %w", err)
}
state.FilterChains = filterChains
return nil
})
} else {
state.FilterChains = make([]*kong.FilterChain, 0)
}

group.Go(func() error {
certificates, err := GetAllCertificates(ctx, client, config.SelectorTags)
if err != nil {
Expand Down Expand Up @@ -441,6 +457,30 @@ func GetAllPlugins(ctx context.Context,
return plugins, nil
}

// GetAllFilterChains queries Kong for all the filter chains using client.
func GetAllFilterChains(ctx context.Context,
client *kong.Client, tags []string,
) ([]*kong.FilterChain, error) {
var filterChains []*kong.FilterChain
opt := newOpt(tags)

for {
s, nextopt, err := client.FilterChains.List(ctx, opt)
if err != nil {
return nil, err
}
if err := ctx.Err(); err != nil {
return nil, err
}
filterChains = append(filterChains, s...)
if nextopt == nil {
break
}
opt = nextopt
}
return filterChains, nil
}

// GetAllCertificates queries Kong for all the certificates using client.
func GetAllCertificates(ctx context.Context, client *kong.Client,
tags []string,
Expand Down
97 changes: 97 additions & 0 deletions pkg/file/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ func (b *stateBuilder) build() (*utils.KongRawState, *utils.KonnectRawState, err
b.consumerGroups()
b.consumers()
b.plugins()
b.filterChains()
b.enterprise()

// konnect
Expand Down Expand Up @@ -894,6 +895,16 @@ func (b *stateBuilder) ingestService(s *FService) error {
return err
}

// filter chains for the service
var filterChains []FFilterChain
for _, f := range s.FilterChains {
f.Service = utils.GetServiceReference(s.Service)
filterChains = append(filterChains, *f)
}
if err := b.ingestFilterChains(filterChains); err != nil {
return err
}

// routes for the service
for _, r := range s.Routes {
r := r
Expand Down Expand Up @@ -1153,6 +1164,48 @@ func (b *stateBuilder) plugins() {
}
}

func (b *stateBuilder) filterChains() {
if b.err != nil {
return
}

var filterChains []FFilterChain
for _, f := range b.targetContent.FilterChains {
f := f
if f.Service != nil && !utils.Empty(f.Service.ID) {
s, err := b.intermediate.Services.Get(*f.Service.ID)
if errors.Is(err, state.ErrNotFound) {
b.err = fmt.Errorf("service %v for filterChain %v: %w",
f.Service.FriendlyName(), *f.Name, err)

return
} else if err != nil {
b.err = err
return
}
f.Service = utils.GetServiceReference(s.Service)
}
if f.Route != nil && !utils.Empty(f.Route.ID) {
r, err := b.intermediate.Routes.Get(*f.Route.ID)
if errors.Is(err, state.ErrNotFound) {
b.err = fmt.Errorf("route %v for filterChain %v: %w",
f.Route.FriendlyName(), *f.Name, err)

return
} else if err != nil {
b.err = err
return
}
f.Route = utils.GetRouteReference(r.Route)
}
filterChains = append(filterChains, f)
}
if err := b.ingestFilterChains(filterChains); err != nil {
b.err = err
return
}
}

func (b *stateBuilder) validatePlugin(p FPlugin) error {
if b.isConsumerGroupScopedPluginSupported && *p.Name == ratelimitingAdvancedPluginName {
// check if deprecated consumer-groups configuration is present in the config
Expand Down Expand Up @@ -1270,6 +1323,16 @@ func (b *stateBuilder) ingestRoute(r FRoute) error {
return err
}

// filter chains for the route
var filterChains []FFilterChain
for _, f := range r.FilterChains {
f.Route = utils.GetRouteReference(r.Route)
filterChains = append(filterChains, *f)
}
if err := b.ingestFilterChains(filterChains); err != nil {
return err
}

// plugins for the route
var plugins []FPlugin
for _, p := range r.Plugins {
Expand Down Expand Up @@ -1398,6 +1461,40 @@ func pluginRelations(plugin *kong.Plugin) (cID, rID, sID, cgID string) {
return
}

func (b *stateBuilder) ingestFilterChains(filterChains []FFilterChain) error {
for _, f := range filterChains {
f := f
rID, sID := filterChainRelations(&f.FilterChain)
filterChain, err := b.currentState.FilterChains.GetByProp(sID, rID)
if utils.Empty(f.ID) {
if errors.Is(err, state.ErrNotFound) {
f.ID = uuid()
} else if err != nil {
return err
} else {
f.ID = kong.String(*filterChain.ID)
}
}
fmt.Printf("name: %s, rID: %s, sID: %s, current: %v\n", f.FriendlyName(), rID, sID, filterChain)
if filterChain != nil {
f.FilterChain.CreatedAt = filterChain.CreatedAt
}
utils.MustMergeTags(&f, b.selectTags)
b.rawState.FilterChains = append(b.rawState.FilterChains, &f.FilterChain)
}
return nil
}

func filterChainRelations(filterChain *kong.FilterChain) (rID, sID string) {
if filterChain.Route != nil && !utils.Empty(filterChain.Route.ID) {
rID = *filterChain.Route.ID
}
if filterChain.Service != nil && !utils.Empty(filterChain.Service.ID) {
sID = *filterChain.Service.ID
}
return
}

func defaulter(
ctx context.Context, client *kong.Client, fileContent *Content, disableDynamicDefaults, isKonnect bool,
) (*utils.Defaulter, error) {
Expand Down
144 changes: 143 additions & 1 deletion pkg/file/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,46 @@ func existingDocumentState() *state.KongState {
return s
}

func existingFilterChainState() *state.KongState {
s, _ := state.NewKongState()
s.FilterChains.Add(state.FilterChain{
FilterChain: kong.FilterChain{
Name: kong.String("my-service-chain"),
ID: kong.String("fa7bd007-e0c6-4ef2-b254-e60d3a341b0c"),
Enabled: kong.Bool(true),
Service: &kong.Service{
ID: kong.String("ba54b737-38aa-49d1-87c4-64e756b0c6f9"),
},
Filters: []*kong.Filter{
{
Name: kong.String("my-filter"),
Config: jsonRawMessage(`"config!"`),
Enabled: kong.Bool(false),
},
},
},
})
s.FilterChains.Add(state.FilterChain{
FilterChain: kong.FilterChain{
Name: kong.String("my-route-chain"),
ID: kong.String("ac6758a5-41d4-4493-827f-de9df5b75859"),
Enabled: kong.Bool(true),
Route: &kong.Route{
ID: kong.String("ec9b7c35-8e95-4a7c-b0da-4fba8986d1cd"),
},
Filters: []*kong.Filter{
{
Name: kong.String("my-filter"),
Config: jsonRawMessage(`"config!"`),
Enabled: kong.Bool(false),
},
},
},
})

return s
}

var deterministicUUID = func() *string {
version := byte(4)
uuid := make([]byte, 16)
Expand Down Expand Up @@ -674,7 +714,7 @@ func Test_stateBuilder_ingestTargets(t *testing.T) {
d, _ := utils.GetDefaulter(ctx, defaulterTestOpts)
b.defaulter = d
if err := b.ingestTargets(tt.args.targets); (err != nil) != tt.wantErr {
t.Errorf("stateBuilder.ingestPlugins() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("stateBuilder.ingestTargets() error = %v, wantErr %v", err, tt.wantErr)
}
assert.Equal(t, tt.wantState, b.rawState)
})
Expand Down Expand Up @@ -871,6 +911,108 @@ func Test_pluginRelations(t *testing.T) {
}
}

func Test_stateBuilder_ingestFilterChains(t *testing.T) {
rand.Seed(42)
type fields struct {
currentState *state.KongState
}
type args struct {
filterChains []FFilterChain
}
tests := []struct {
name string
fields fields
args args
wantErr bool
wantState *utils.KongRawState
}{
{
name: "generates ID for a non-existing filter chain",
fields: fields{
currentState: emptyState(),
},
args: args{
filterChains: []FFilterChain{
{
FilterChain: kong.FilterChain{
Name: kong.String("my-filter-chain"),
Service: &kong.Service{
ID: kong.String("fdfd14cc-cd69-49a0-9e23-cd3375b6c0cd"),
},
},
},
},
},
wantErr: false,
wantState: &utils.KongRawState{
FilterChains: []*kong.FilterChain{
{
ID: kong.String("538c7f96-b164-4f1b-97bb-9f4bb472e89f"),
Name: kong.String("my-filter-chain"),
Service: &kong.Service{
ID: kong.String("fdfd14cc-cd69-49a0-9e23-cd3375b6c0cd"),
},
},
},
},
},
{
name: "matches up IDs of filter chains correctly",
fields: fields{
currentState: existingFilterChainState(),
},
args: args{
filterChains: []FFilterChain{
{
FilterChain: kong.FilterChain{
Service: &kong.Service{
ID: kong.String("ba54b737-38aa-49d1-87c4-64e756b0c6f9"),
},
},
},
{
FilterChain: kong.FilterChain{
Route: &kong.Route{
ID: kong.String("ec9b7c35-8e95-4a7c-b0da-4fba8986d1cd"),
},
},
},
},
},
wantErr: false,
wantState: &utils.KongRawState{
FilterChains: []*kong.FilterChain{
{
ID: kong.String("fa7bd007-e0c6-4ef2-b254-e60d3a341b0c"),
Service: &kong.Service{
ID: kong.String("ba54b737-38aa-49d1-87c4-64e756b0c6f9"),
},
},
{
ID: kong.String("ac6758a5-41d4-4493-827f-de9df5b75859"),
Route: &kong.Route{
ID: kong.String("ec9b7c35-8e95-4a7c-b0da-4fba8986d1cd"),
},
},
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := &stateBuilder{
currentState: tt.fields.currentState,
}
b.rawState = &utils.KongRawState{}
if err := b.ingestFilterChains(tt.args.filterChains); (err != nil) != tt.wantErr {
t.Errorf("stateBuilder.ingestFilterChains() error = %v, wantErr %v", err, tt.wantErr)
}
assert.Equal(t, tt.wantState, b.rawState)
})
}
}

func Test_stateBuilder_consumers(t *testing.T) {
rand.Seed(42)
type fields struct {
Expand Down
Loading
Loading