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 aae2105e feat: ingress annotations support enable websocket (#1101)
aae2105e is described below

commit aae2105e123008a0170b68a0133432695ee230c9
Author: seven dickens <language...@163.com>
AuthorDate: Tue Jul 5 02:05:40 2022 +0800

    feat: ingress annotations support enable websocket (#1101)
---
 docs/en/latest/concepts/annotations.md  |  33 ++++
 pkg/kube/translation/ingress.go         |   8 +
 pkg/kube/translation/ingress_test.go    | 261 +++++++++++++++++++++++++++
 test/e2e/suite-annotations/websocket.go | 306 ++++++++++++++++++++++++++++++++
 4 files changed, 608 insertions(+)

diff --git a/docs/en/latest/concepts/annotations.md 
b/docs/en/latest/concepts/annotations.md
index ff215ccd..338c7562 100644
--- a/docs/en/latest/concepts/annotations.md
+++ b/docs/en/latest/concepts/annotations.md
@@ -171,3 +171,36 @@ spec:
             port:
               number: 80
 ```
+
+Enable websocket
+---------
+
+You can use the follow annotations to enable websocket
+
+* `k8s.apisix.apache.org/enable-websocket`
+  
+If this annotations set to `true` the route will enable websoket
+
+For example, the following Ingress, if we set 
`k8s.apisix.apache.org/enable-websocket: "true"`. `/api/*` route will enable 
websocket
+
+```yaml
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/enable-websocket: "true"
+  name: ingress-v1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /api/*
+        pathType: ImplementationSpecific
+        backend:
+          service:
+            name: service1
+            port:
+              number: 80
+```
diff --git a/pkg/kube/translation/ingress.go b/pkg/kube/translation/ingress.go
index 76fb1e07..8b0ab9d6 100644
--- a/pkg/kube/translation/ingress.go
+++ b/pkg/kube/translation/ingress.go
@@ -44,6 +44,8 @@ func (t *translator) translateIngressV1(ing 
*networkingv1.Ingress, skipVerify bo
        plugins := t.translateAnnotations(ing.Annotations)
        annoExtractor := annotations.NewExtractor(ing.Annotations)
        useRegex := 
annoExtractor.GetBoolAnnotation(annotations.AnnotationsPrefix + "use-regex")
+       enableWebsocket := 
annoExtractor.GetBoolAnnotation(annotations.AnnotationsPrefix + 
"enable-websocket")
+
        // add https
        for _, tls := range ing.Spec.TLS {
                apisixTls := kubev2.ApisixTls{
@@ -132,6 +134,7 @@ func (t *translator) translateIngressV1(ing 
*networkingv1.Ingress, skipVerify bo
                        route.ID = id.GenID(route.Name)
                        route.Host = rule.Host
                        route.Uris = uris
+                       route.EnableWebsocket = enableWebsocket
                        if len(nginxVars) > 0 {
                                routeVars, err := 
t.translateRouteMatchExprs(nginxVars)
                                if err != nil {
@@ -165,6 +168,8 @@ func (t *translator) translateIngressV1beta1(ing 
*networkingv1beta1.Ingress, ski
        plugins := t.translateAnnotations(ing.Annotations)
        annoExtractor := annotations.NewExtractor(ing.Annotations)
        useRegex := 
annoExtractor.GetBoolAnnotation(annotations.AnnotationsPrefix + "use-regex")
+       enableWebsocket := 
annoExtractor.GetBoolAnnotation(annotations.AnnotationsPrefix + 
"enable-websocket")
+
        // add https
        for _, tls := range ing.Spec.TLS {
                apisixTls := kubev2beta3.ApisixTls{
@@ -253,6 +258,7 @@ func (t *translator) translateIngressV1beta1(ing 
*networkingv1beta1.Ingress, ski
                        route.ID = id.GenID(route.Name)
                        route.Host = rule.Host
                        route.Uris = uris
+                       route.EnableWebsocket = enableWebsocket
                        if len(nginxVars) > 0 {
                                routeVars, err := 
t.translateRouteMatchExprs(nginxVars)
                                if err != nil {
@@ -340,6 +346,7 @@ func (t *translator) translateIngressExtensionsV1beta1(ing 
*extensionsv1beta1.In
        plugins := t.translateAnnotations(ing.Annotations)
        annoExtractor := annotations.NewExtractor(ing.Annotations)
        useRegex := 
annoExtractor.GetBoolAnnotation(annotations.AnnotationsPrefix + "use-regex")
+       enableWebsocket := 
annoExtractor.GetBoolAnnotation(annotations.AnnotationsPrefix + 
"enable-websocket")
 
        for _, rule := range ing.Spec.Rules {
                for _, pathRule := range rule.HTTP.Paths {
@@ -400,6 +407,7 @@ func (t *translator) translateIngressExtensionsV1beta1(ing 
*extensionsv1beta1.In
                        route.ID = id.GenID(route.Name)
                        route.Host = rule.Host
                        route.Uris = uris
+                       route.EnableWebsocket = enableWebsocket
                        if len(nginxVars) > 0 {
                                routeVars, err := 
t.translateRouteMatchExprs(nginxVars)
                                if err != nil {
diff --git a/pkg/kube/translation/ingress_test.go 
b/pkg/kube/translation/ingress_test.go
index a2365c66..fed2ae28 100644
--- a/pkg/kube/translation/ingress_test.go
+++ b/pkg/kube/translation/ingress_test.go
@@ -1048,3 +1048,264 @@ func TestTranslateIngressExtensionsV1beta1WithRegex(t 
*testing.T) {
        assert.Equal(t, []string{"/*"}, ctx.Routes[0].Uris)
        assert.Equal(t, expectedVars, ctx.Routes[0].Vars)
 }
+
+func TestTranslateIngressV1WithWebsocket(t *testing.T) {
+       prefix := networkingv1.PathTypeImplementationSpecific
+       regexPath := "/foo/*/bar"
+       ing := &networkingv1.Ingress{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test",
+                       Namespace: "default",
+                       Annotations: map[string]string{
+                               "k8s.apisix.apache.org/enable-websocket": 
"true",
+                       },
+               },
+               Spec: networkingv1.IngressSpec{
+                       Rules: []networkingv1.IngressRule{
+                               {
+                                       Host: "apisix.apache.org",
+                                       IngressRuleValue: 
networkingv1.IngressRuleValue{
+                                               HTTP: 
&networkingv1.HTTPIngressRuleValue{
+                                                       Paths: 
[]networkingv1.HTTPIngressPath{
+                                                               {
+                                                                       Path:   
  regexPath,
+                                                                       
PathType: &prefix,
+                                                                       
Backend: networkingv1.IngressBackend{
+                                                                               
Service: &networkingv1.IngressServiceBackend{
+                                                                               
        Name: "test-service",
+                                                                               
        Port: networkingv1.ServiceBackendPort{
+                                                                               
                Name: "port1",
+                                                                               
        },
+                                                                               
},
+                                                                       },
+                                                               },
+                                                       },
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+       client := fake.NewSimpleClientset()
+       informersFactory := informers.NewSharedInformerFactory(client, 0)
+       svcInformer := informersFactory.Core().V1().Services().Informer()
+       svcLister := informersFactory.Core().V1().Services().Lister()
+       epLister, epInformer := 
kube.NewEndpointListerAndInformer(informersFactory, false)
+       apisixClient := fakeapisix.NewSimpleClientset()
+       apisixInformersFactory := 
apisixinformers.NewSharedInformerFactory(apisixClient, 0)
+       processCh := make(chan struct{})
+       svcInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
+               AddFunc: func(obj interface{}) {
+                       processCh <- struct{}{}
+               },
+       })
+       epInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
+               AddFunc: func(obj interface{}) {
+                       processCh <- struct{}{}
+               },
+       })
+
+       stopCh := make(chan struct{})
+       defer close(stopCh)
+       go svcInformer.Run(stopCh)
+       go epInformer.Run(stopCh)
+       cache.WaitForCacheSync(stopCh, svcInformer.HasSynced)
+
+       _, err := 
client.CoreV1().Services("default").Create(context.Background(), _testSvc, 
metav1.CreateOptions{})
+       assert.Nil(t, err)
+       _, err = 
client.CoreV1().Endpoints("default").Create(context.Background(), _testEp, 
metav1.CreateOptions{})
+       assert.Nil(t, err)
+
+       tr := &translator{
+               TranslatorOptions: &TranslatorOptions{
+                       ServiceLister:        svcLister,
+                       EndpointLister:       epLister,
+                       ApisixUpstreamLister: 
apisixInformersFactory.Apisix().V2beta3().ApisixUpstreams().Lister(),
+               },
+       }
+
+       <-processCh
+       <-processCh
+       ctx, err := tr.translateIngressV1(ing, false)
+       assert.Nil(t, err)
+       assert.Len(t, ctx.Routes, 1)
+       assert.Len(t, ctx.Upstreams, 1)
+       // the number of the PluginConfigs should be zero, cause there no 
available Annotations matched te rule
+       assert.Len(t, ctx.PluginConfigs, 0)
+
+       assert.Equal(t, true, ctx.Routes[0].EnableWebsocket)
+}
+
+func TestTranslateIngressV1beta1WithWebsocket(t *testing.T) {
+       prefix := networkingv1beta1.PathTypeImplementationSpecific
+       // no backend.
+       regexPath := "/foo/*/bar"
+       ing := &networkingv1beta1.Ingress{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test",
+                       Namespace: "default",
+                       Annotations: map[string]string{
+                               "k8s.apisix.apache.org/enable-websocket": 
"true",
+                       },
+               },
+               Spec: networkingv1beta1.IngressSpec{
+                       Rules: []networkingv1beta1.IngressRule{
+                               {
+                                       Host: "apisix.apache.org",
+                                       IngressRuleValue: 
networkingv1beta1.IngressRuleValue{
+                                               HTTP: 
&networkingv1beta1.HTTPIngressRuleValue{
+                                                       Paths: 
[]networkingv1beta1.HTTPIngressPath{
+                                                               {
+                                                                       Path:   
  regexPath,
+                                                                       
PathType: &prefix,
+                                                                       
Backend: networkingv1beta1.IngressBackend{
+                                                                               
ServiceName: "test-service",
+                                                                               
ServicePort: intstr.IntOrString{
+                                                                               
        Type:   intstr.String,
+                                                                               
        StrVal: "port1",
+                                                                               
},
+                                                                       },
+                                                               },
+                                                       },
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+       client := fake.NewSimpleClientset()
+       informersFactory := informers.NewSharedInformerFactory(client, 0)
+       svcInformer := informersFactory.Core().V1().Services().Informer()
+       svcLister := informersFactory.Core().V1().Services().Lister()
+       epLister, epInformer := 
kube.NewEndpointListerAndInformer(informersFactory, false)
+       apisixClient := fakeapisix.NewSimpleClientset()
+       apisixInformersFactory := 
apisixinformers.NewSharedInformerFactory(apisixClient, 0)
+       processCh := make(chan struct{})
+       svcInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
+               AddFunc: func(obj interface{}) {
+                       processCh <- struct{}{}
+               },
+       })
+       epInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
+               AddFunc: func(obj interface{}) {
+                       processCh <- struct{}{}
+               },
+       })
+
+       stopCh := make(chan struct{})
+       defer close(stopCh)
+       go svcInformer.Run(stopCh)
+       go epInformer.Run(stopCh)
+       cache.WaitForCacheSync(stopCh, svcInformer.HasSynced)
+
+       _, err := 
client.CoreV1().Services("default").Create(context.Background(), _testSvc, 
metav1.CreateOptions{})
+       assert.Nil(t, err)
+       _, err = 
client.CoreV1().Endpoints("default").Create(context.Background(), _testEp, 
metav1.CreateOptions{})
+       assert.Nil(t, err)
+
+       tr := &translator{
+               TranslatorOptions: &TranslatorOptions{
+                       ServiceLister:        svcLister,
+                       EndpointLister:       epLister,
+                       ApisixUpstreamLister: 
apisixInformersFactory.Apisix().V2beta3().ApisixUpstreams().Lister(),
+               },
+       }
+
+       <-processCh
+       <-processCh
+       ctx, err := tr.translateIngressV1beta1(ing, false)
+       assert.Nil(t, err)
+       assert.Len(t, ctx.Routes, 1)
+       assert.Len(t, ctx.Upstreams, 1)
+       // the number of the PluginConfigs should be zero, cause there no 
available Annotations matched te rule
+       assert.Len(t, ctx.PluginConfigs, 0)
+
+       assert.Nil(t, err)
+       assert.Equal(t, true, ctx.Routes[0].EnableWebsocket)
+}
+
+func TestTranslateIngressExtensionsV1beta1WithWebsocket(t *testing.T) {
+       prefix := extensionsv1beta1.PathTypeImplementationSpecific
+       regexPath := "/foo/*/bar"
+       ing := &extensionsv1beta1.Ingress{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test",
+                       Namespace: "default",
+                       Annotations: map[string]string{
+                               "k8s.apisix.apache.org/enable-websocket": 
"true",
+                       },
+               },
+               Spec: extensionsv1beta1.IngressSpec{
+                       Rules: []extensionsv1beta1.IngressRule{
+                               {
+                                       Host: "apisix.apache.org",
+                                       IngressRuleValue: 
extensionsv1beta1.IngressRuleValue{
+                                               HTTP: 
&extensionsv1beta1.HTTPIngressRuleValue{
+                                                       Paths: 
[]extensionsv1beta1.HTTPIngressPath{
+                                                               {
+                                                                       Path:   
  regexPath,
+                                                                       
PathType: &prefix,
+                                                                       
Backend: extensionsv1beta1.IngressBackend{
+                                                                               
ServiceName: "test-service",
+                                                                               
ServicePort: intstr.IntOrString{
+                                                                               
        Type:   intstr.String,
+                                                                               
        StrVal: "port1",
+                                                                               
},
+                                                                       },
+                                                               },
+                                                       },
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+       client := fake.NewSimpleClientset()
+       informersFactory := informers.NewSharedInformerFactory(client, 0)
+       svcInformer := informersFactory.Core().V1().Services().Informer()
+       svcLister := informersFactory.Core().V1().Services().Lister()
+       epLister, epInformer := 
kube.NewEndpointListerAndInformer(informersFactory, false)
+       apisixClient := fakeapisix.NewSimpleClientset()
+       apisixInformersFactory := 
apisixinformers.NewSharedInformerFactory(apisixClient, 0)
+       processCh := make(chan struct{})
+       svcInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
+               AddFunc: func(obj interface{}) {
+                       processCh <- struct{}{}
+               },
+       })
+       epInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
+               AddFunc: func(obj interface{}) {
+                       processCh <- struct{}{}
+               },
+       })
+
+       stopCh := make(chan struct{})
+       defer close(stopCh)
+       go svcInformer.Run(stopCh)
+       go epInformer.Run(stopCh)
+       cache.WaitForCacheSync(stopCh, svcInformer.HasSynced)
+
+       _, err := 
client.CoreV1().Services("default").Create(context.Background(), _testSvc, 
metav1.CreateOptions{})
+       assert.Nil(t, err)
+       _, err = 
client.CoreV1().Endpoints("default").Create(context.Background(), _testEp, 
metav1.CreateOptions{})
+       assert.Nil(t, err)
+
+       tr := &translator{
+               TranslatorOptions: &TranslatorOptions{
+                       ServiceLister:        svcLister,
+                       EndpointLister:       epLister,
+                       ApisixUpstreamLister: 
apisixInformersFactory.Apisix().V2beta3().ApisixUpstreams().Lister(),
+               },
+       }
+
+       <-processCh
+       <-processCh
+       ctx, err := tr.translateIngressExtensionsV1beta1(ing, false)
+       assert.Nil(t, err)
+       assert.Len(t, ctx.Routes, 1)
+       assert.Len(t, ctx.Upstreams, 1)
+       // the number of the PluginConfigs should be zero, cause there no 
available Annotations matched te rule
+       assert.Len(t, ctx.PluginConfigs, 0)
+
+       assert.Equal(t, true, ctx.Routes[0].EnableWebsocket)
+}
diff --git a/test/e2e/suite-annotations/websocket.go 
b/test/e2e/suite-annotations/websocket.go
new file mode 100644
index 00000000..3ffb5920
--- /dev/null
+++ b/test/e2e/suite-annotations/websocket.go
@@ -0,0 +1,306 @@
+// 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 annotations
+
+import (
+       "net/http"
+       "net/url"
+       "time"
+
+       "github.com/gorilla/websocket"
+       ginkgo "github.com/onsi/ginkgo/v2"
+       "github.com/stretchr/testify/assert"
+
+       "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+)
+
+var _ = ginkgo.Describe("suite-annotations: annotations.networking/v1 
websocket", func() {
+       opts := &scaffold.Options{
+               Name:                  "default",
+               Kubeconfig:            scaffold.GetKubeconfig(),
+               APISIXConfigPath:      "testdata/apisix-gw-config.yaml",
+               IngressAPISIXReplicas: 1,
+               HTTPBinServicePort:    80,
+               APISIXRouteVersion:    "apisix.apache.org/v2beta3",
+       }
+       s := scaffold.NewScaffold(opts)
+       ginkgo.It("sanity", func() {
+               resources := `
+apiVersion: v1
+kind: Pod
+metadata:
+  name: websocket-server
+  labels:
+    app: websocket-server
+spec:
+  containers:
+  - name: websocket-server
+    image: localhost:5000/jmalloc/echo-server:latest
+    ports:
+    - containerPort: 8080
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: websocket-server-service
+spec:
+  selector:
+    app: websocket-server
+  ports:
+  - name: ws
+    port: 48733
+    protocol: TCP
+    targetPort: 8080
+`
+               err := s.CreateResourceFromString(resources)
+               assert.Nil(ginkgo.GinkgoT(), err)
+               time.Sleep(5 * time.Second)
+
+               ing := `
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/enable-websocket: 'true'
+  name: ingress-v1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /echo
+        pathType: ImplementationSpecific
+        backend:
+          service:
+            name: websocket-server-service
+            port:
+              number: 48733
+`
+               assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(ing))
+               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")
+
+               dialer := websocket.Dialer{}
+               u := url.URL{
+                       Scheme: "ws",
+                       Host:   s.APISIXGatewayServiceEndpoint(),
+                       Path:   "/echo",
+               }
+               header := http.Header{
+                       "Host": []string{"httpbin.org"},
+               }
+               conn, resp, err := dialer.Dial(u.String(), header)
+               assert.Nil(ginkgo.GinkgoT(), err, "websocket handshake failure")
+               assert.Equal(ginkgo.GinkgoT(), resp.StatusCode, 
http.StatusSwitchingProtocols)
+
+               assert.Nil(ginkgo.GinkgoT(), 
conn.WriteMessage(websocket.TextMessage, []byte("hello, I'm gorilla")), 
"writing message")
+               msgType, buf, err := conn.ReadMessage()
+               assert.Nil(ginkgo.GinkgoT(), err, "reading message")
+               assert.Equal(ginkgo.GinkgoT(), string(buf), "Request served by 
websocket-server")
+               msgType, buf, err = conn.ReadMessage()
+               assert.Nil(ginkgo.GinkgoT(), err, "reading message")
+               assert.Equal(ginkgo.GinkgoT(), msgType, websocket.TextMessage)
+               assert.Equal(ginkgo.GinkgoT(), string(buf), "hello, I'm 
gorilla")
+               assert.Nil(ginkgo.GinkgoT(), conn.Close(), "closing ws 
connection")
+       })
+})
+
+var _ = ginkgo.Describe("suite-annotations: annotations.networking/v1beta1 
with websocket", func() {
+       opts := &scaffold.Options{
+               Name:                  "default",
+               Kubeconfig:            scaffold.GetKubeconfig(),
+               APISIXConfigPath:      "testdata/apisix-gw-config.yaml",
+               IngressAPISIXReplicas: 1,
+               HTTPBinServicePort:    80,
+               APISIXRouteVersion:    "apisix.apache.org/v2beta3",
+       }
+       s := scaffold.NewScaffold(opts)
+       ginkgo.It("sanity", func() {
+               resources := `
+apiVersion: v1
+kind: Pod
+metadata:
+  name: websocket-server
+  labels:
+    app: websocket-server
+spec:
+  containers:
+  - name: websocket-server
+    image: localhost:5000/jmalloc/echo-server:latest
+    ports:
+    - containerPort: 8080
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: websocket-server-service
+spec:
+  selector:
+    app: websocket-server
+  ports:
+  - name: ws
+    port: 48733
+    protocol: TCP
+    targetPort: 8080
+`
+               err := s.CreateResourceFromString(resources)
+               assert.Nil(ginkgo.GinkgoT(), err)
+               time.Sleep(5 * time.Second)
+
+               ing := `
+apiVersion: networking.k8s.io/v1beta1
+kind: Ingress
+metadata:
+  name: ingress-v1beta1
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/enable-websocket: 'true'
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /echo
+        pathType: Exact
+        backend:
+          serviceName: websocket-server-service
+          servicePort: 48733
+`
+               assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(ing))
+               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")
+
+               dialer := websocket.Dialer{}
+               u := url.URL{
+                       Scheme: "ws",
+                       Host:   s.APISIXGatewayServiceEndpoint(),
+                       Path:   "/echo",
+               }
+               header := http.Header{
+                       "Host": []string{"httpbin.org"},
+               }
+               conn, resp, err := dialer.Dial(u.String(), header)
+               assert.Nil(ginkgo.GinkgoT(), err, "websocket handshake failure")
+               assert.Equal(ginkgo.GinkgoT(), resp.StatusCode, 
http.StatusSwitchingProtocols)
+
+               assert.Nil(ginkgo.GinkgoT(), 
conn.WriteMessage(websocket.TextMessage, []byte("hello, I'm gorilla")), 
"writing message")
+               msgType, buf, err := conn.ReadMessage()
+               assert.Nil(ginkgo.GinkgoT(), err, "reading message")
+               assert.Equal(ginkgo.GinkgoT(), string(buf), "Request served by 
websocket-server")
+               msgType, buf, err = conn.ReadMessage()
+               assert.Nil(ginkgo.GinkgoT(), err, "reading message")
+               assert.Equal(ginkgo.GinkgoT(), msgType, websocket.TextMessage)
+               assert.Equal(ginkgo.GinkgoT(), string(buf), "hello, I'm 
gorilla")
+               assert.Nil(ginkgo.GinkgoT(), conn.Close(), "closing ws 
connection")
+       })
+})
+
+var _ = ginkgo.Describe("suite-annotations: ingress.networking/v1beta1 with 
websocket", func() {
+       opts := &scaffold.Options{
+               Name:                  "default",
+               Kubeconfig:            scaffold.GetKubeconfig(),
+               APISIXConfigPath:      "testdata/apisix-gw-config.yaml",
+               IngressAPISIXReplicas: 1,
+               HTTPBinServicePort:    80,
+               APISIXRouteVersion:    "apisix.apache.org/v2beta3",
+       }
+       s := scaffold.NewScaffold(opts)
+       ginkgo.It("sanity", func() {
+               resources := `
+apiVersion: v1
+kind: Pod
+metadata:
+  name: websocket-server
+  labels:
+    app: websocket-server
+spec:
+  containers:
+  - name: websocket-server
+    image: localhost:5000/jmalloc/echo-server:latest
+    ports:
+    - containerPort: 8080
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: websocket-server-service
+spec:
+  selector:
+    app: websocket-server
+  ports:
+  - name: ws
+    port: 48733
+    protocol: TCP
+    targetPort: 8080
+`
+               err := s.CreateResourceFromString(resources)
+               assert.Nil(ginkgo.GinkgoT(), err)
+               time.Sleep(5 * time.Second)
+
+               ing := `
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+  name: ingress-ext-v1beta1
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/enable-websocket: 'true'
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /echo
+        pathType: Exact
+        backend:
+          serviceName: websocket-server-service
+          servicePort: 48733
+`
+               assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(ing))
+               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")
+
+               dialer := websocket.Dialer{}
+               u := url.URL{
+                       Scheme: "ws",
+                       Host:   s.APISIXGatewayServiceEndpoint(),
+                       Path:   "/echo",
+               }
+               header := http.Header{
+                       "Host": []string{"httpbin.org"},
+               }
+               conn, resp, err := dialer.Dial(u.String(), header)
+               assert.Nil(ginkgo.GinkgoT(), err, "websocket handshake failure")
+               assert.Equal(ginkgo.GinkgoT(), resp.StatusCode, 
http.StatusSwitchingProtocols)
+
+               assert.Nil(ginkgo.GinkgoT(), 
conn.WriteMessage(websocket.TextMessage, []byte("hello, I'm gorilla")), 
"writing message")
+               msgType, buf, err := conn.ReadMessage()
+               assert.Nil(ginkgo.GinkgoT(), err, "reading message")
+               assert.Equal(ginkgo.GinkgoT(), string(buf), "Request served by 
websocket-server")
+               msgType, buf, err = conn.ReadMessage()
+               assert.Nil(ginkgo.GinkgoT(), err, "reading message")
+               assert.Equal(ginkgo.GinkgoT(), msgType, websocket.TextMessage)
+               assert.Equal(ginkgo.GinkgoT(), string(buf), "hello, I'm 
gorilla")
+               assert.Nil(ginkgo.GinkgoT(), conn.Close(), "closing ws 
connection")
+       })
+})

Reply via email to