Skip to content

Commit

Permalink
feat: add support for filter_func for ApisixRoute (#1545)
Browse files Browse the repository at this point in the history
Signed-off-by: Aryan <75756768+aynp@users.noreply.github.com>
  • Loading branch information
aynp authored Jan 29, 2023
1 parent 9476e13 commit 123d080
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 0 deletions.
4 changes: 4 additions & 0 deletions pkg/kube/apisix/apis/config/v2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ type ApisixRouteHTTPMatch struct {
// - "127.0.0.1"
// - "10.0.5.11"
NginxVars []ApisixRouteHTTPMatchExpr `json:"exprs,omitempty" yaml:"exprs,omitempty"`
// Matches based on a user-defined filtering function.
// These functions can accept an input parameter `vars`
// which can be used to access the Nginx variables.
FilterFunc string `json:"filter_func,omitempty" yaml:"filter_func,omitempty"`
}

// ApisixRouteHTTPMatchExpr represents a binary route match expression .
Expand Down
3 changes: 3 additions & 0 deletions pkg/providers/apisix/translation/apisix_route.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,9 +325,12 @@ func (t *translator) translateHTTPRouteV2(ctx *translation.TranslateContext, ar
route.EnableWebsocket = part.Websocket
route.Plugins = pluginMap
route.Timeout = timeout
route.FilterFunc = part.Match.FilterFunc

if part.PluginConfigName != "" {
route.PluginConfigId = id.GenID(apisixv1.ComposePluginConfigName(ar.Namespace, part.PluginConfigName))
}

for k, v := range ar.ObjectMeta.Labels {
route.Metadata.Labels[k] = v
}
Expand Down
1 change: 1 addition & 0 deletions pkg/types/apisix/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ type Route struct {
UpstreamId string `json:"upstream_id,omitempty" yaml:"upstream_id,omitempty"`
Plugins Plugins `json:"plugins,omitempty" yaml:"plugins,omitempty"`
PluginConfigId string `json:"plugin_config_id,omitempty" yaml:"plugin_config_id,omitempty"`
FilterFunc string `json:"filter_func,omitempty" yaml:"filter_func,omitempty"`
}

// Vars represents the route match expressions of APISIX.
Expand Down
2 changes: 2 additions & 0 deletions samples/deploy/crd/v1/ApisixRoute.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,8 @@ spec:
oneOf:
- required: ["subject", "op", "value"]
- required: ["subject", "op", "set"]
filter_func:
type: string
websocket:
type: boolean
plugin_config_name:
Expand Down
79 changes: 79 additions & 0 deletions test/e2e/suite-features/route_filter_func.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package features

import (
"fmt"
"net/http"

ginkgo "github.com/onsi/ginkgo/v2"
"github.com/stretchr/testify/assert"

"github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
)

var _ = ginkgo.Describe("suite-features: filter_func", func() {
suites := func(scaffoldFunc func() *scaffold.Scaffold) {
s := scaffoldFunc()
ginkgo.It("filter using body", func() {
backendSvc, backendPorts := s.DefaultHTTPBackend()
ar := fmt.Sprintf(`
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: httpbin-route
spec:
http:
- name: rule1
match:
hosts:
- httpbin.org
paths:
- /ip
filter_func: "function(vars)\n local core = require ('apisix.core')\n local body, err = core.request.get_body()\n if not body then\n return false\n end\n\n local data, err = core.json.decode(body)\n if not data then\n return false\n end\n\n if data['foo'] == 'bar' then\n return true\n end\n\n return false\nend"
backends:
- serviceName: %s
servicePort: %d
`, backendSvc, backendPorts[0])
assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(ar))

err := s.EnsureNumApisixUpstreamsCreated(1)
assert.Nil(ginkgo.GinkgoT(), err, "Checking number of upstreams")
err = s.EnsureNumApisixRoutesCreated(1)
assert.Nil(ginkgo.GinkgoT(), err, "Checking number of routes")

_ = s.NewAPISIXClient().GET("/ip").
WithHeader("Host", "httpbin.org").
Expect().
Status(http.StatusNotFound).
Body().
Contains("404 Route Not Found")

type MyJSON struct {
Foo string `json:"foo"`
}

_ = s.NewAPISIXClient().GET("/ip").
WithHeader("Host", "httpbin.org").
WithJSON(MyJSON{Foo: "bar"}).
Expect().
Status(http.StatusOK)
})
}

ginkgo.Describe("suite-features: scaffold v2", func() {
suites(scaffold.NewDefaultV2Scaffold)
})
})

0 comments on commit 123d080

Please sign in to comment.