You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by zh...@apache.org on 2022/03/02 11:51:26 UTC

[apisix-ingress-controller] branch master updated: fix: check if stream_routes is disabled (#868)

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

zhangjintao 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 035c60e  fix: check if stream_routes is disabled (#868)
035c60e is described below

commit 035c60e456abf064af23634822f8453606fc5cc5
Author: Sarasa Kisaragi <li...@gmail.com>
AuthorDate: Wed Mar 2 19:51:19 2022 +0800

    fix: check if stream_routes is disabled (#868)
---
 pkg/apisix/cluster.go                         | 35 +++++++++++++++---
 pkg/apisix/noop.go                            | 51 +++++++++++++++++++++++++++
 pkg/apisix/stream_route.go                    |  8 ++++-
 test/e2e/ingress/sanity.go                    | 46 ++++++++++++++++++++++++
 test/e2e/testdata/apisix-stream-disabled.yaml | 42 ++++++++++++++++++++++
 5 files changed, 176 insertions(+), 6 deletions(-)

diff --git a/pkg/apisix/cluster.go b/pkg/apisix/cluster.go
index 8b7a938..20565bb 100644
--- a/pkg/apisix/cluster.go
+++ b/pkg/apisix/cluster.go
@@ -53,6 +53,8 @@ var (
 	// ErrDuplicatedCluster means the cluster adding request was
 	// rejected since the cluster was already created.
 	ErrDuplicatedCluster = errors.New("duplicated cluster")
+	// ErrFunctionDisabled means the APISIX function is disabled
+	ErrFunctionDisabled = errors.New("function disabled")
 
 	_errReadOnClosedResBody = errors.New("http: read on closed response body")
 
@@ -517,6 +519,10 @@ func (c *cluster) do(req *http.Request) (*http.Response, error) {
 	return c.cli.Do(req)
 }
 
+func (c *cluster) isFunctionDisabled(body string) bool {
+	return strings.Contains(body, "is disabled")
+}
+
 func (c *cluster) getResource(ctx context.Context, url, resource string) (*getResponse, error) {
 	req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
 	if err != nil {
@@ -532,11 +538,15 @@ func (c *cluster) getResource(ctx context.Context, url, resource string) (*getRe
 
 	defer drainBody(resp.Body, url)
 	if resp.StatusCode != http.StatusOK {
+		body := readBody(resp.Body, url)
+		if c.isFunctionDisabled(body) {
+			return nil, ErrFunctionDisabled
+		}
 		if resp.StatusCode == http.StatusNotFound {
 			return nil, cache.ErrNotFound
 		} else {
 			err = multierr.Append(err, fmt.Errorf("unexpected status code %d", resp.StatusCode))
-			err = multierr.Append(err, fmt.Errorf("error message: %s", readBody(resp.Body, url)))
+			err = multierr.Append(err, fmt.Errorf("error message: %s", body))
 		}
 		return nil, err
 	}
@@ -565,8 +575,12 @@ func (c *cluster) listResource(ctx context.Context, url, resource string) (*list
 
 	defer drainBody(resp.Body, url)
 	if resp.StatusCode != http.StatusOK {
+		body := readBody(resp.Body, url)
+		if c.isFunctionDisabled(body) {
+			return nil, ErrFunctionDisabled
+		}
 		err = multierr.Append(err, fmt.Errorf("unexpected status code %d", resp.StatusCode))
-		err = multierr.Append(err, fmt.Errorf("error message: %s", readBody(resp.Body, url)))
+		err = multierr.Append(err, fmt.Errorf("error message: %s", body))
 		return nil, err
 	}
 
@@ -595,8 +609,12 @@ func (c *cluster) createResource(ctx context.Context, url, resource string, body
 	defer drainBody(resp.Body, url)
 
 	if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
+		body := readBody(resp.Body, url)
+		if c.isFunctionDisabled(body) {
+			return nil, ErrFunctionDisabled
+		}
 		err = multierr.Append(err, fmt.Errorf("unexpected status code %d", resp.StatusCode))
-		err = multierr.Append(err, fmt.Errorf("error message: %s", readBody(resp.Body, url)))
+		err = multierr.Append(err, fmt.Errorf("error message: %s", body))
 		return nil, err
 	}
 
@@ -624,8 +642,12 @@ func (c *cluster) updateResource(ctx context.Context, url, resource string, body
 	defer drainBody(resp.Body, url)
 
 	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
+		body := readBody(resp.Body, url)
+		if c.isFunctionDisabled(body) {
+			return nil, ErrFunctionDisabled
+		}
 		err = multierr.Append(err, fmt.Errorf("unexpected status code %d", resp.StatusCode))
-		err = multierr.Append(err, fmt.Errorf("error message: %s", readBody(resp.Body, url)))
+		err = multierr.Append(err, fmt.Errorf("error message: %s", body))
 		return nil, err
 	}
 	var ur updateResponse
@@ -652,8 +674,11 @@ func (c *cluster) deleteResource(ctx context.Context, url, resource string) erro
 	defer drainBody(resp.Body, url)
 
 	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusNotFound {
-		err = multierr.Append(err, fmt.Errorf("unexpected status code %d", resp.StatusCode))
 		message := readBody(resp.Body, url)
+		if c.isFunctionDisabled(message) {
+			return ErrFunctionDisabled
+		}
+		err = multierr.Append(err, fmt.Errorf("unexpected status code %d", resp.StatusCode))
 		err = multierr.Append(err, fmt.Errorf("error message: %s", message))
 		if strings.Contains(message, "still using") {
 			return cache.ErrStillInUse
diff --git a/pkg/apisix/noop.go b/pkg/apisix/noop.go
new file mode 100644
index 0000000..af7dd87
--- /dev/null
+++ b/pkg/apisix/noop.go
@@ -0,0 +1,51 @@
+// 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 apisix
+
+import (
+	"context"
+
+	v1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
+)
+
+var (
+	_ StreamRoute = (*noopClient)(nil)
+)
+
+type noopClient struct {
+}
+
+func (r *noopClient) Get(ctx context.Context, name string) (*v1.StreamRoute, error) {
+	return nil, nil
+}
+
+func (r *noopClient) List(ctx context.Context) ([]*v1.StreamRoute, error) {
+	return nil, nil
+}
+
+func (r *noopClient) Create(ctx context.Context, obj *v1.StreamRoute) (*v1.StreamRoute, error) {
+	return nil, nil
+}
+
+func (r *noopClient) Delete(ctx context.Context, obj *v1.StreamRoute) error {
+	return nil
+}
+
+func (r *noopClient) Update(ctx context.Context, obj *v1.StreamRoute) (*v1.StreamRoute, error) {
+	return nil, nil
+}
diff --git a/pkg/apisix/stream_route.go b/pkg/apisix/stream_route.go
index e835eab..f6377c8 100644
--- a/pkg/apisix/stream_route.go
+++ b/pkg/apisix/stream_route.go
@@ -33,8 +33,14 @@ type streamRouteClient struct {
 }
 
 func newStreamRouteClient(c *cluster) StreamRoute {
+	url := c.baseURL + "/stream_routes"
+	_, err := c.listResource(context.Background(), url, "streamRoute")
+	if err == ErrFunctionDisabled {
+		log.Infow("resource stream_routes is disabled")
+		return &noopClient{}
+	}
 	return &streamRouteClient{
-		url:     c.baseURL + "/stream_routes",
+		url:     url,
 		cluster: c,
 	}
 }
diff --git a/test/e2e/ingress/sanity.go b/test/e2e/ingress/sanity.go
index ff3dd45..08247f7 100644
--- a/test/e2e/ingress/sanity.go
+++ b/test/e2e/ingress/sanity.go
@@ -186,3 +186,49 @@ var _ = ginkgo.Describe("leader election", func() {
 		}
 	})
 })
+
+var _ = ginkgo.Describe("stream_routes disabled", func() {
+	opts := &scaffold.Options{
+		Name:                  "default",
+		Kubeconfig:            scaffold.GetKubeconfig(),
+		APISIXConfigPath:      "testdata/apisix-stream-disabled.yaml",
+		IngressAPISIXReplicas: 1,
+		HTTPBinServicePort:    80,
+		APISIXRouteVersion:    "apisix.apache.org/v2beta3",
+	}
+	s := scaffold.NewScaffold(opts)
+	ginkgo.It("/ip should return your ip", func() {
+		backendSvc, backendSvcPort := s.DefaultHTTPBackend()
+		ar := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2beta3
+kind: ApisixRoute
+metadata:
+  name: httpbin-route
+spec:
+  http:
+  - name: rule1
+    match:
+      paths:
+      - /ip
+    backends:
+    - serviceName: %s
+      servicePort: %d
+`, backendSvc, backendSvcPort[0])
+		assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(ar))
+		err := s.EnsureNumApisixRoutesCreated(1)
+		assert.Nil(ginkgo.GinkgoT(), err, "checking number of routes")
+		err = s.EnsureNumApisixUpstreamsCreated(1)
+		assert.Nil(ginkgo.GinkgoT(), err, "checking number of upstreams")
+
+		// TODO When ingress controller can feedback the lifecycle of CRDs to the
+		// status field, we can poll it rather than sleeping.
+		time.Sleep(3 * time.Second)
+
+		body := s.NewAPISIXClient().GET("/ip").WithHeader("Host", "httpbin.com").Expect().Status(http.StatusOK).Body().Raw()
+		var placeholder ip
+		err = json.Unmarshal([]byte(body), &placeholder)
+		assert.Nil(ginkgo.GinkgoT(), err, "unmarshalling IP")
+		// It's not our focus point to check the IP address returned by httpbin,
+		// so here skip the IP address validation.
+	})
+})
diff --git a/test/e2e/testdata/apisix-stream-disabled.yaml b/test/e2e/testdata/apisix-stream-disabled.yaml
new file mode 100644
index 0000000..458b707
--- /dev/null
+++ b/test/e2e/testdata/apisix-stream-disabled.yaml
@@ -0,0 +1,42 @@
+#
+# 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.
+#
+# PLEASE DO NOT UPDATE THIS FILE!
+# If you want to set the specified configuration value, you can set the new
+# value in the conf/config.yaml file.
+#
+
+apisix:
+  enable_control: true
+  enable_reuseport: true          # Enable nginx SO_REUSEPORT switch if set to true.
+  allow_admin:
+    - 127.0.0.0/24
+    - 0.0.0.0/0
+  port_admin: 9180
+#  stream_proxy:                 # TCP/UDP proxy
+#    only: false
+#    tcp:                        # TCP proxy port list
+#      - 9100
+#    udp:
+#      - 9200
+etcd:
+  host:                           # it's possible to define multiple etcd hosts addresses of the same etcd cluster.
+    - "http://{{ .EtcdServiceFQDN }}:2379"     # multiple etcd address
+  prefix: "/apisix"               # apisix configurations prefix
+  timeout: 30                     # 30 seconds
+plugin_attr:
+  prometheus:
+    enable_export_server: false