This is an automated email from the ASF dual-hosted git repository.

tokers pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-ingress-controller.git


The following commit(s) were added to refs/heads/master by this push:
     new c6e7180  fix: pass remote_addrs to APISIX (#347)
c6e7180 is described below

commit c6e71803cca5470679ac0433753b361753e38783
Author: Alex Zhang <[email protected]>
AuthorDate: Sat Apr 10 11:21:35 2021 +0800

    fix: pass remote_addrs to APISIX (#347)
---
 pkg/apisix/resource.go                  |  44 +++++++-------
 pkg/apisix/route.go                     |  70 +++++++++++-----------
 pkg/kube/translation/apisix_route.go    |   9 +++
 pkg/kube/translation/util.go            |  17 ++++++
 pkg/kube/translation/util_test.go       |  39 +++++++++++++
 pkg/types/apisix/v1/types.go            |   1 +
 test/e2e/features/remote_addrs_match.go | 100 ++++++++++++++++++++++++++++++++
 7 files changed, 226 insertions(+), 54 deletions(-)

diff --git a/pkg/apisix/resource.go b/pkg/apisix/resource.go
index 8808788..d5ea072 100644
--- a/pkg/apisix/resource.go
+++ b/pkg/apisix/resource.go
@@ -73,17 +73,18 @@ type item struct {
 }
 
 type routeItem struct {
-       UpstreamId string                 `json:"upstream_id"`
-       ServiceId  string                 `json:"service_id"`
-       Host       string                 `json:"host"`
-       Hosts      []string               `json:"hosts"`
-       URI        string                 `json:"uri"`
-       Vars       [][]v1.StringOrSlice   `json:"vars"`
-       Uris       []string               `json:"uris"`
-       Desc       string                 `json:"desc"`
-       Methods    []string               `json:"methods"`
-       Priority   int                    `json:"priority"`
-       Plugins    map[string]interface{} `json:"plugins"`
+       UpstreamId  string                 `json:"upstream_id"`
+       ServiceId   string                 `json:"service_id"`
+       RemoteAddrs []string               `json:"remote_addrs"`
+       Host        string                 `json:"host"`
+       Hosts       []string               `json:"hosts"`
+       URI         string                 `json:"uri"`
+       Vars        [][]v1.StringOrSlice   `json:"vars"`
+       Uris        []string               `json:"uris"`
+       Desc        string                 `json:"desc"`
+       Methods     []string               `json:"methods"`
+       Priority    int                    `json:"priority"`
+       Plugins     map[string]interface{} `json:"plugins"`
 }
 
 // route decodes item.Value and converts it to v1.Route.
@@ -108,16 +109,17 @@ func (i *item) route(clusterName string) (*v1.Route, 
error) {
                        Group:    clusterName,
                        Name:     route.Desc,
                },
-               Host:       route.Host,
-               Path:       route.URI,
-               Uris:       route.Uris,
-               Vars:       route.Vars,
-               Methods:    route.Methods,
-               UpstreamId: route.UpstreamId,
-               ServiceId:  route.ServiceId,
-               Plugins:    route.Plugins,
-               Hosts:      route.Hosts,
-               Priority:   route.Priority,
+               Host:        route.Host,
+               Path:        route.URI,
+               Uris:        route.Uris,
+               Vars:        route.Vars,
+               Methods:     route.Methods,
+               RemoteAddrs: route.RemoteAddrs,
+               UpstreamId:  route.UpstreamId,
+               ServiceId:   route.ServiceId,
+               Plugins:     route.Plugins,
+               Hosts:       route.Hosts,
+               Priority:    route.Priority,
        }, nil
 }
 
diff --git a/pkg/apisix/route.go b/pkg/apisix/route.go
index 70c4f35..7d87754 100644
--- a/pkg/apisix/route.go
+++ b/pkg/apisix/route.go
@@ -29,17 +29,18 @@ import (
 )
 
 type routeReqBody struct {
-       Desc       string               `json:"desc,omitempty"`
-       Name       string               `json:"name,omitempty"`
-       URI        string               `json:"uri,omitempty"`
-       Priority   int                  `json:"priority,omitempty"`
-       Uris       []string             `json:"uris,omitempty"`
-       Vars       [][]v1.StringOrSlice `json:"vars,omitempty"`
-       Host       string               `json:"host,omitempty"`
-       Hosts      []string             `json:"hosts,omitempty"`
-       ServiceId  string               `json:"service_id,omitempty"`
-       UpstreamId string               `json:"upstream_id,omitempty"`
-       Plugins    v1.Plugins           `json:"plugins,omitempty"`
+       Desc        string               `json:"desc,omitempty"`
+       Name        string               `json:"name,omitempty"`
+       URI         string               `json:"uri,omitempty"`
+       Priority    int                  `json:"priority,omitempty"`
+       Uris        []string             `json:"uris,omitempty"`
+       Vars        [][]v1.StringOrSlice `json:"vars,omitempty"`
+       Host        string               `json:"host,omitempty"`
+       Hosts       []string             `json:"hosts,omitempty"`
+       ServiceId   string               `json:"service_id,omitempty"`
+       RemoteAddrs []string             `json:"remote_addrs,omitempty"`
+       UpstreamId  string               `json:"upstream_id,omitempty"`
+       Plugins     v1.Plugins           `json:"plugins,omitempty"`
 }
 
 type routeClient struct {
@@ -162,17 +163,18 @@ func (r *routeClient) Create(ctx context.Context, obj 
*v1.Route) (*v1.Route, err
                return nil, err
        }
        data, err := json.Marshal(routeReqBody{
-               Priority:   obj.Priority,
-               Desc:       obj.Name,
-               Name:       obj.Name,
-               URI:        obj.Path,
-               Host:       obj.Host,
-               Hosts:      obj.Hosts,
-               ServiceId:  obj.ServiceId,
-               UpstreamId: obj.UpstreamId,
-               Uris:       obj.Uris,
-               Plugins:    obj.Plugins,
-               Vars:       obj.Vars,
+               Priority:    obj.Priority,
+               Desc:        obj.Name,
+               Name:        obj.Name,
+               URI:         obj.Path,
+               Host:        obj.Host,
+               Hosts:       obj.Hosts,
+               ServiceId:   obj.ServiceId,
+               UpstreamId:  obj.UpstreamId,
+               Uris:        obj.Uris,
+               Plugins:     obj.Plugins,
+               Vars:        obj.Vars,
+               RemoteAddrs: obj.RemoteAddrs,
        })
        if err != nil {
                return nil, err
@@ -232,18 +234,20 @@ func (r *routeClient) Update(ctx context.Context, obj 
*v1.Route) (*v1.Route, err
        if err := r.cluster.HasSynced(ctx); err != nil {
                return nil, err
        }
+       // FIXME use unified v1.Route, removing routeReqBody.
        body, err := json.Marshal(routeReqBody{
-               Priority:   obj.Priority,
-               Desc:       obj.Name,
-               Name:       obj.Name,
-               URI:        obj.Path,
-               Host:       obj.Host,
-               Hosts:      obj.Hosts,
-               ServiceId:  obj.ServiceId,
-               UpstreamId: obj.UpstreamId,
-               Uris:       obj.Uris,
-               Plugins:    obj.Plugins,
-               Vars:       obj.Vars,
+               Priority:    obj.Priority,
+               Desc:        obj.Name,
+               Name:        obj.Name,
+               URI:         obj.Path,
+               Host:        obj.Host,
+               Hosts:       obj.Hosts,
+               ServiceId:   obj.ServiceId,
+               UpstreamId:  obj.UpstreamId,
+               Uris:        obj.Uris,
+               Plugins:     obj.Plugins,
+               Vars:        obj.Vars,
+               RemoteAddrs: obj.RemoteAddrs,
        })
        if err != nil {
                return nil, err
diff --git a/pkg/kube/translation/apisix_route.go 
b/pkg/kube/translation/apisix_route.go
index 6914293..6336455 100644
--- a/pkg/kube/translation/apisix_route.go
+++ b/pkg/kube/translation/apisix_route.go
@@ -165,6 +165,14 @@ func (t *translator) TranslateRouteV2alpha1(ar 
*configv2alpha1.ApisixRoute) ([]*
                                return nil, nil, err
                        }
                }
+               if err := validateRemoteAddrs(part.Match.RemoteAddrs); err != 
nil {
+                       log.Errorw("ApisixRoute with invalid remote addrs",
+                               zap.Error(err),
+                               zap.Strings("remote_addrs", 
part.Match.RemoteAddrs),
+                               zap.Any("ApisixRoute", ar),
+                       )
+                       return nil, nil, err
+               }
 
                routeName := apisixv1.ComposeRouteName(ar.Namespace, ar.Name, 
part.Name)
                upstreamName := apisixv1.ComposeUpstreamName(ar.Namespace, 
backend.ServiceName, svcPort)
@@ -177,6 +185,7 @@ func (t *translator) TranslateRouteV2alpha1(ar 
*configv2alpha1.ApisixRoute) ([]*
                                ResourceVersion: ar.ResourceVersion,
                        },
                        Priority:     part.Priority,
+                       RemoteAddrs:  part.Match.RemoteAddrs,
                        Vars:         exprs,
                        Hosts:        part.Match.Hosts,
                        Uris:         part.Match.Paths,
diff --git a/pkg/kube/translation/util.go b/pkg/kube/translation/util.go
index 3584dae..a66b7dd 100644
--- a/pkg/kube/translation/util.go
+++ b/pkg/kube/translation/util.go
@@ -16,6 +16,7 @@ package translation
 
 import (
        "errors"
+       "net"
 
        "go.uber.org/zap"
        "k8s.io/apimachinery/pkg/util/intstr"
@@ -26,6 +27,10 @@ import (
        apisixv1 
"github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
 )
 
+var (
+       _errInvalidAddress = errors.New("address is neither IP or CIDR")
+)
+
 func (t *translator) getServiceClusterIPAndPort(backend 
*configv2alpha1.ApisixRouteHTTPBackend, ar *configv2alpha1.ApisixRoute) 
(string, int32, error) {
        svc, err := 
t.ServiceLister.Services(ar.Namespace).Get(backend.ServiceName)
        if err != nil {
@@ -84,3 +89,15 @@ func (t *translator) translateUpstream(namespace, svcName, 
svcResolveGranularity
        ups.ID = id.GenID(ups.FullName)
        return ups, nil
 }
+
+func validateRemoteAddrs(remoteAddrs []string) error {
+       for _, addr := range remoteAddrs {
+               if ip := net.ParseIP(addr); ip == nil {
+                       // addr is not an IP address, try to parse it as a CIDR.
+                       if _, _, err := net.ParseCIDR(addr); err != nil {
+                               return _errInvalidAddress
+                       }
+               }
+       }
+       return nil
+}
diff --git a/pkg/kube/translation/util_test.go 
b/pkg/kube/translation/util_test.go
new file mode 100644
index 0000000..f990a4d
--- /dev/null
+++ b/pkg/kube/translation/util_test.go
@@ -0,0 +1,39 @@
+// 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 translation
+
+import (
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+)
+
+func TestValidateRemoteAddrs(t *testing.T) {
+       addrs := []string{
+               "192.168.3.4",
+               "192.168.3.3/16",
+               "123",
+       }
+       err := validateRemoteAddrs(addrs)
+       assert.Equal(t, err, _errInvalidAddress)
+
+       addrs = []string{
+               "192.168.3.4",
+               "192.168.3.3/16",
+               "2001:db8::68",
+               "2001:db8:a0b:12f0::1/32",
+       }
+       assert.Nil(t, validateRemoteAddrs(addrs))
+}
diff --git a/pkg/types/apisix/v1/types.go b/pkg/types/apisix/v1/types.go
index 3650a0b..f314689 100644
--- a/pkg/types/apisix/v1/types.go
+++ b/pkg/types/apisix/v1/types.go
@@ -89,6 +89,7 @@ type Route struct {
        Vars         [][]StringOrSlice `json:"vars,omitempty" 
yaml:"vars,omitempty"`
        Uris         []string          `json:"uris,omitempty" 
yaml:"uris,omitempty"`
        Methods      []string          `json:"methods,omitempty" 
yaml:"methods,omitempty"`
+       RemoteAddrs  []string          `json:"remote_addrs,omitempty" 
yaml:"remote_addrs,omitempty"`
        ServiceId    string            `json:"service_id,omitempty" 
yaml:"service_id,omitempty"`
        ServiceName  string            `json:"service_name,omitempty" 
yaml:"service_name,omitempty"`
        UpstreamId   string            `json:"upstream_id,omitempty" 
yaml:"upstream_id,omitempty"`
diff --git a/test/e2e/features/remote_addrs_match.go 
b/test/e2e/features/remote_addrs_match.go
new file mode 100644
index 0000000..f804e33
--- /dev/null
+++ b/test/e2e/features/remote_addrs_match.go
@@ -0,0 +1,100 @@
+// 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"
+
+       "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+       "github.com/onsi/ginkgo"
+       "github.com/stretchr/testify/assert"
+)
+
+var _ = ginkgo.Describe("traffic split", func() {
+       opts := &scaffold.Options{
+               Name:                    "default",
+               Kubeconfig:              scaffold.GetKubeconfig(),
+               APISIXConfigPath:        "testdata/apisix-gw-config.yaml",
+               APISIXDefaultConfigPath: 
"testdata/apisix-gw-config-default.yaml",
+               IngressAPISIXReplicas:   1,
+               HTTPBinServicePort:      80,
+               APISIXRouteVersion:      "apisix.apache.org/v2alpha1",
+       }
+       s := scaffold.NewScaffold(opts)
+       ginkgo.It("sanity", func() {
+               backendSvc, backendPorts := s.DefaultHTTPBackend()
+               ar := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2alpha1
+kind: ApisixRoute
+metadata:
+ name: httpbin-route
+spec:
+ http:
+ - name: rule1
+   match:
+     hosts:
+     - httpbin.org
+     paths:
+       - /ip
+     remoteAddrs:
+       - "10.0.5.0/8"
+   backends:
+   - serviceName: %s
+     servicePort: %d
+`, backendSvc, backendPorts[0])
+               assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(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")
+
+               resp := s.NewAPISIXClient().GET("/ip").WithHeader("Host", 
"httpbin.org").Expect()
+               resp.Status(http.StatusNotFound)
+               resp.Body().Contains("404 Route Not Found")
+
+               ar = fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2alpha1
+kind: ApisixRoute
+metadata:
+ name: httpbin-route
+spec:
+ http:
+ - name: rule1
+   match:
+     hosts:
+     - httpbin.org
+     paths:
+       - /ip
+     remoteAddrs:
+       - "127.0.0.1"
+   backends:
+   - serviceName: %s
+     servicePort: %d
+`, backendSvc, backendPorts[0])
+
+               assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(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")
+
+               resp = s.NewAPISIXClient().GET("/ip").WithHeader("Host", 
"httpbin.org").Expect()
+               resp.Status(http.StatusOK)
+               resp.Body().Contains("origin")
+       })
+})

Reply via email to