Skip to content

Commit

Permalink
TEST: add CORS e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanmatmati committed Apr 22, 2022
1 parent aabe46a commit e73ecc5
Show file tree
Hide file tree
Showing 6 changed files with 334 additions and 0 deletions.
1 change: 1 addition & 0 deletions deploy/tests/e2e/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ func (c *Client) Do() (res *http.Response, close func() error, err error) {
}
}
c.Req.Host = c.Host
c.Req.Header["Origin"] = []string{c.Req.URL.Scheme + "://" + c.Host}
c.Req.URL.Host = c.Host
c.Req.URL.Path = c.Path
res, err = client.Do(c.Req)
Expand Down
64 changes: 64 additions & 0 deletions deploy/tests/e2e/cors/config/deploy.yaml.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: http-echo
spec:
replicas: 1
selector:
matchLabels:
app: http-echo
template:
metadata:
labels:
app: http-echo
spec:
containers:
- name: http-echo
image: haproxytech/http-echo:latest
imagePullPolicy: Never
args:
- --default-response=hostname
ports:
- name: http
containerPort: 8888
protocol: TCP
- name: https
containerPort: 8443
protocol: TCP
---
kind: Service
apiVersion: v1
metadata:
name: http-echo
spec:
ports:
- name: http
protocol: TCP
port: 80
targetPort: http
- name: https
protocol: TCP
port: 443
targetPort: https
selector:
app: http-echo
---
kind: Ingress
apiVersion: networking.k8s.io/v1beta1
metadata:
name: http-echo
annotations:
ingress.class: haproxy
{{range .IngAnnotations}}
{{ .Key }}: {{ .Value }}
{{end}}
spec:
rules:
- host: {{ .Host }}
http:
paths:
- path: /
backend:
serviceName: http-echo
servicePort: http
14 changes: 14 additions & 0 deletions deploy/tests/e2e/cors/config/patternfile-a.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: patternfiles
namespace: haproxy-controller
data:
cors-allow-credentials: 'false'
cors-allow-headers: >-
x-api-key
cors-allow-methods: GET, POST
cors-allow-origin: >-
^https://configmap.com?$
cors-enable: 'true'
cors-max-age: 100s
6 changes: 6 additions & 0 deletions deploy/tests/e2e/cors/config/patternfile-empty.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: patternfiles
namespace: haproxy-controller
data: {}
192 changes: 192 additions & 0 deletions deploy/tests/e2e/cors/cors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// Copyright 2019 HAProxy Technologies LLC
//
// Licensed 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.

//go:build e2e_sequential

package cors

import (
"net/http"

"github.com/haproxytech/kubernetes-ingress/deploy/tests/e2e"
)

const (
AccessControlAllowOrigin = "Access-Control-Allow-Origin"
AccessControlAllowMethods = "Access-Control-Allow-Methods"
AccessControlAllowHeaders = "Access-Control-Allow-Headers"
AccessControlMaxAge = "Access-Control-Max-Age"
AccessControlAllowCredential = "Access-Control-Allow-Credentials"
AnnotationCorsEnable = "cors-enable"
AnnotationCorsOrigin = "cors-allow-origin"
AnnotationCorsMethods = "cors-allow-methods"
AnnotationCorsHeaders = "cors-allow-headers"
AnnotationCorsAge = "cors-max-age"
AnnotationCorsCredential = "cors-allow-credentials"
Star = "*"
)

func (suite *CorsSuite) Test_Ingress_Alone() {
suite.Run("Default", func() {
expectedHeaders := http.Header{
AccessControlAllowOrigin: {Star},
AccessControlAllowMethods: {Star},
AccessControlAllowHeaders: {Star},
AccessControlMaxAge: {"5"},
}
suite.tmplData.IngAnnotations = []struct{ Key, Value string }{
{AnnotationCorsEnable, q("true")},
}

suite.NoError(suite.test.Apply("config/deploy.yaml.tmpl", suite.test.GetNS(), suite.tmplData))

suite.eventuallyReturns(expectedHeaders, http.Header{})
})

suite.Run("CorsOriginAlone", func() {
expectedHeaders := http.Header{
AccessControlAllowOrigin: {"http://" + suite.tmplData.Host},
AccessControlAllowMethods: {Star},
AccessControlAllowHeaders: {Star},
AccessControlMaxAge: {"5"},
}
suite.tmplData.IngAnnotations = []struct{ Key, Value string }{
{AnnotationCorsEnable, q("true")},
{AnnotationCorsOrigin, q("http://" + suite.tmplData.Host)},
}

suite.NoError(suite.test.Apply("config/deploy.yaml.tmpl", suite.test.GetNS(), suite.tmplData))

suite.eventuallyReturns(expectedHeaders, http.Header{})
})

suite.Run("CorsMethodsAlone", func() {
expectedHeaders := http.Header{
AccessControlAllowOrigin: {Star},
AccessControlAllowMethods: {"GET"},
AccessControlAllowHeaders: {Star},
AccessControlMaxAge: {"5"},
}
suite.tmplData.IngAnnotations = []struct{ Key, Value string }{
{AnnotationCorsEnable, q("true")},
{AnnotationCorsMethods, q("GET")},
}

suite.NoError(suite.test.Apply("config/deploy.yaml.tmpl", suite.test.GetNS(), suite.tmplData))

suite.eventuallyReturns(expectedHeaders, http.Header{})
})

suite.Run("CorsMethodsHeaders", func() {
expectedHeaders := http.Header{
AccessControlAllowOrigin: {Star},
AccessControlAllowMethods: {Star},
AccessControlAllowHeaders: {"Accept"},
AccessControlMaxAge: {"5"},
}
suite.tmplData.IngAnnotations = []struct{ Key, Value string }{
{AnnotationCorsEnable, q("true")},
{AnnotationCorsHeaders, q("Accept")},
}

suite.NoError(suite.test.Apply("config/deploy.yaml.tmpl", suite.test.GetNS(), suite.tmplData))

suite.eventuallyReturns(expectedHeaders, http.Header{})
})

suite.Run("CorsMethodsAge", func() {
expectedHeaders := http.Header{
AccessControlAllowOrigin: {Star},
AccessControlAllowMethods: {Star},
AccessControlAllowHeaders: {Star},
AccessControlMaxAge: {"500"},
}
suite.tmplData.IngAnnotations = []struct{ Key, Value string }{
{AnnotationCorsEnable, q("true")},
{AnnotationCorsAge, q("500s")},
}

suite.NoError(suite.test.Apply("config/deploy.yaml.tmpl", suite.test.GetNS(), suite.tmplData))

suite.eventuallyReturns(expectedHeaders, http.Header{})
})

suite.Run("CorsMethodsCredential", func() {
expectedHeaders := http.Header{
AccessControlAllowOrigin: {Star},
AccessControlAllowMethods: {Star},
AccessControlAllowHeaders: {Star},
AccessControlAllowCredential: {"true"},
AccessControlMaxAge: {"5"},
}
suite.tmplData.IngAnnotations = []struct{ Key, Value string }{
{AnnotationCorsEnable, q("true")},
{AnnotationCorsCredential, q("true")},
}

suite.NoError(suite.test.Apply("config/deploy.yaml.tmpl", suite.test.GetNS(), suite.tmplData))

suite.eventuallyReturns(expectedHeaders, http.Header{})
})

suite.Run("CorsDisable", func() {
unexpectedHeaders := http.Header{
AccessControlAllowOrigin: {},
AccessControlAllowMethods: {},
AccessControlAllowHeaders: {},
AccessControlMaxAge: {},
AccessControlAllowCredential: {},
}

suite.tmplData.IngAnnotations = []struct{ Key, Value string }{
{AnnotationCorsEnable, q("false")},
{AnnotationCorsOrigin, q("http://wrong.com")},
{AnnotationCorsCredential, q("true")},
{AnnotationCorsMethods, q("GET")},
{AnnotationCorsHeaders, q("Accept")},
}

suite.NoError(suite.test.Apply("config/deploy.yaml.tmpl", suite.test.GetNS(), suite.tmplData))

suite.eventuallyReturns(http.Header{}, unexpectedHeaders)
})
}

func (suite *CorsSuite) eventuallyReturns(expecedHeaders, unexpectedHeaders http.Header) {
suite.Eventually(func() bool {
res, cls, err := suite.client.Do()
if err != nil {
suite.T().Logf("Connection ERROR: %s", err.Error())
return false
}
defer cls()
for expectedHeader, expectedValues := range expecedHeaders {
values, ok := res.Header[expectedHeader]
if !ok || len(values) != 1 || values[0] != expectedValues[0] {
return false
}

}
for unexpectedHeader := range unexpectedHeaders {
if _, ok := res.Header[unexpectedHeader]; ok {
return false
}
}
return true
}, e2e.WaitDuration, e2e.TickDuration)
}

func q(value string) string {
return "\"" + value + "\""
}
57 changes: 57 additions & 0 deletions deploy/tests/e2e/cors/suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2019 HAProxy Technologies LLC
//
// Licensed 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.

//go:build e2e_sequential

package cors

import (
"testing"

"github.com/stretchr/testify/suite"

"github.com/haproxytech/kubernetes-ingress/deploy/tests/e2e"
)

type CorsSuite struct {
suite.Suite
test e2e.Test
client *e2e.Client
tmplData tmplData
}

type tmplData struct {
IngAnnotations []struct{ Key, Value string }
Host string
}

func (suite *CorsSuite) SetupSuite() {
var err error
suite.test, err = e2e.NewTest()
suite.NoError(err)
suite.tmplData = tmplData{Host: suite.test.GetNS() + ".test"}
suite.client, err = e2e.NewHTTPClient(suite.tmplData.Host)
suite.NoError(err)

suite.NoError(suite.test.Apply("config/patternfile-empty.yml", "", nil))
}

func (suite *CorsSuite) TearDownSuite() {
suite.test.Apply("config/patternfile-empty.yml", "", nil)
suite.test.TearDown()
}

func TestCorsSuite(t *testing.T) {
suite.Run(t, new(CorsSuite))
}

0 comments on commit e73ecc5

Please sign in to comment.