Skip to content

Commit

Permalink
OSSM-3647: Add tests for 3scale plugin (#633)
Browse files Browse the repository at this point in the history
* OSSM-3647: Add a test for 3scale plugin applied to an ingress gateway

Signed-off-by: Jacek Ewertowski <[email protected]>

* Run TestThreeScaleWasmPlugin for all supported versions

Signed-off-by: Jacek Ewertowski <[email protected]>

* Rename test

Signed-off-by: Jacek Ewertowski <[email protected]>

* Remove unused file

Signed-off-by: Jacek Ewertowski <[email protected]>

* Use official wiremock image

Signed-off-by: Jacek Ewertowski <[email protected]>

* Fix execution order

Signed-off-by: Jacek Ewertowski <[email protected]>

* Test more cases with 3scale plugin

Signed-off-by: Jacek Ewertowski <[email protected]>

* Revert changes from auth tests

Signed-off-by: Jacek Ewertowski <[email protected]>

* Fix lint error

Signed-off-by: Jacek Ewertowski <[email protected]>

---------

Signed-off-by: Jacek Ewertowski <[email protected]>
  • Loading branch information
jewertow authored Oct 25, 2023
1 parent 6e5c5d4 commit 7934ac9
Show file tree
Hide file tree
Showing 9 changed files with 535 additions and 0 deletions.
44 changes: 44 additions & 0 deletions pkg/tests/tasks/extensions/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package extensions

import (
_ "embed"

"github.com/maistra/maistra-test-tool/pkg/tests/ossm"
"github.com/maistra/maistra-test-tool/pkg/util/env"
"github.com/maistra/maistra-test-tool/pkg/util/test"

"testing"
)

var (
smcpName = env.GetDefaultSMCPName()
meshNamespace = env.GetDefaultMeshNamespace()
threeScaleNs = "3scale"

//go:embed yaml/3scale-system.yaml
threeScaleSystem string

//go:embed yaml/3scale-system-service-entry.yaml
threeScaleSystemSvcEntry string

//go:embed yaml/3scale-backend.yaml
threeScaleBackend string

//go:embed yaml/3scale-backend-service-entry.yaml
threeScaleBackendSvcEntry string

//go:embed yaml/mesh.tmpl.yaml
meshTmpl string

//go:embed yaml/jwt-authn.tmpl.yaml
jwtAuthnTmpl string

//go:embed yaml/wasm-plugin.tmpl.yaml
wasmPluginTmpl string
)

func TestMain(m *testing.M) {
test.NewSuite(m).
Setup(ossm.BasicSetup).
Run()
}
120 changes: 120 additions & 0 deletions pkg/tests/tasks/extensions/threescale_wasm_plugin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package extensions

import (
"fmt"
"net/http"
"strings"

"github.com/maistra/maistra-test-tool/pkg/app"
"github.com/maistra/maistra-test-tool/pkg/util/check/assert"
"github.com/maistra/maistra-test-tool/pkg/util/check/require"
"github.com/maistra/maistra-test-tool/pkg/util/curl"
"github.com/maistra/maistra-test-tool/pkg/util/env"
"github.com/maistra/maistra-test-tool/pkg/util/istio"
"github.com/maistra/maistra-test-tool/pkg/util/ns"
"github.com/maistra/maistra-test-tool/pkg/util/oc"
"github.com/maistra/maistra-test-tool/pkg/util/pod"
"github.com/maistra/maistra-test-tool/pkg/util/request"
"github.com/maistra/maistra-test-tool/pkg/util/retry"
"github.com/maistra/maistra-test-tool/pkg/util/test"
"github.com/maistra/maistra-test-tool/pkg/util/version"

"testing"
)

const (
tokenURL = "https://raw.githubusercontent.com/istio/istio/release-1.19/security/tools/jwt/samples/demo.jwt"
)

func TestThreeScaleWasmPlugin(t *testing.T) {
test.NewTest(t).Groups(test.Full).Run(func(t test.TestHelper) {
t.Cleanup(func() {
oc.RecreateNamespace(t, ns.Foo)
oc.RecreateNamespace(t, meshNamespace)
oc.DeleteNamespace(t, threeScaleNs)
})

meshValues := map[string]string{
"Name": smcpName,
"Version": env.GetSMCPVersion().String(),
"Member": ns.Foo,
}

t.LogStep("Deploy SMCP")
oc.ApplyTemplate(t, meshNamespace, meshTmpl, meshValues)
oc.WaitSMCPReady(t, meshNamespace, smcpName)

t.LogStep("Deploy 3scale mocks")
oc.CreateNamespace(t, threeScaleNs)
oc.ApplyString(t, threeScaleNs, threeScaleBackend)
oc.ApplyString(t, meshNamespace, threeScaleBackendSvcEntry)
oc.ApplyString(t, threeScaleNs, threeScaleSystem)
oc.ApplyString(t, meshNamespace, threeScaleSystemSvcEntry)
oc.WaitAllPodsReady(t, threeScaleNs)

t.LogStep("Configure JWT authn")
oc.ApplyTemplate(t, meshNamespace, jwtAuthnTmpl, map[string]interface{}{
"AppLabel": "istio-ingressgateway",
"ForwardToken": true,
})

t.LogStep("Apply 3scale WASM plugin to the ingress gateway")
oc.ApplyTemplate(t, meshNamespace, wasmPluginTmpl, map[string]interface{}{"AppLabel": "istio-ingressgateway"})

t.LogStep("Deploy httpbin and configure its gateway and routing")
app.InstallAndWaitReady(t, app.Httpbin(ns.Foo))
oc.ApplyFile(t, ns.Foo, "https://raw.githubusercontent.com/maistra/istio/maistra-2.4/samples/httpbin/httpbin-gateway.yaml")

t.LogStep("Verify that a request to the ingress gateway with token returns 200")
ingressGatewayHost := istio.GetIngressGatewayHost(t, meshNamespace)
headersURL := fmt.Sprintf("http://%s/headers", ingressGatewayHost)
token := string(curl.Request(t, tokenURL, nil))
token = strings.Trim(token, "\n")
retry.UntilSuccess(t, func(t test.TestHelper) {
curl.Request(t, headersURL, request.WithHeader("Authorization", "Bearer "+token), require.ResponseStatus(http.StatusOK))
})

t.LogStep("Apply JWT config and 3scale plugin to httpbin")
oc.ApplyTemplate(t, ns.Foo, jwtAuthnTmpl, map[string]interface{}{"AppLabel": "httpbin"})
oc.ApplyTemplate(t, ns.Foo, wasmPluginTmpl, map[string]interface{}{"AppLabel": "httpbin"})

// This step would fail if the ingress gateway did not forward Authorization header to httpbin
t.LogStep("Verify that a request to the ingress gateway with token returns 200")
retry.UntilSuccess(t, func(t test.TestHelper) {
curl.Request(t, headersURL, request.WithHeader("Authorization", "Bearer "+token), require.ResponseStatus(http.StatusOK))
})

t.LogStep("Deploy sleep app")
app.InstallAndWaitReady(t, app.Sleep(ns.Foo))

t.LogStep("Verify that a request from sleep to httpbin with token returns 200")
retry.UntilSuccess(t, func(t test.TestHelper) {
oc.Exec(t, pod.MatchingSelector("app=sleep", ns.Foo), "sleep",
fmt.Sprintf(`curl http://httpbin:8000/headers -H "Authorization: Bearer %s" -s -o /dev/null -w "%%{http_code}"`, token),
assert.OutputContains("200", "Received 200 as expected", "Received unexpected status code"))
})

t.LogStep("Apply JWT config and 3scale plugin to sleep")
oc.ApplyTemplate(t, ns.Foo, jwtAuthnTmpl, map[string]interface{}{"AppLabel": "sleep"})
oc.ApplyTemplate(t, ns.Foo, wasmPluginTmpl, map[string]interface{}{"AppLabel": "sleep"})

if env.GetSMCPVersion().GreaterThanOrEqual(version.SMCP_2_3) {
// A request should fail, because in 2.3 and 2.4, WASM plugins are applied to inbound and outbound listeners.
// JWT authentication filter is applied only to inbound listeners, so 3scale plugin configured
// to use JWT filter metadata always fails on outbound.
t.LogStep("Verify that a request from sleep to httpbin returns 403")
retry.UntilSuccess(t, func(t test.TestHelper) {
oc.Exec(t, pod.MatchingSelector("app=sleep", ns.Foo), "sleep",
fmt.Sprintf(`curl http://httpbin:8000/headers -H "Authorization: Bearer %s" -s -o /dev/null -w "%%{http_code}"`, token),
assert.OutputContains("403", "Received 403 as expected", "Received unexpected status code"))
})
} else {
t.LogStep("Verify that a request from sleep to httpbin returns 200")
retry.UntilSuccess(t, func(t test.TestHelper) {
oc.Exec(t, pod.MatchingSelector("app=sleep", ns.Foo), "sleep",
fmt.Sprintf(`curl http://httpbin:8000/headers -H "Authorization: Bearer %s" -s -o /dev/null -w "%%{http_code}"`, token),
assert.OutputContains("200", "Received 200 as expected", "Received unexpected status code"))
})
}
})
}
13 changes: 13 additions & 0 deletions pkg/tests/tasks/extensions/yaml/3scale-backend-service-entry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
name: threescale-backend
spec:
hosts:
- backend.3scale.svc.cluster.local
ports:
- number: 80
name: http
protocol: HTTP
location: MESH_EXTERNAL
resolution: DNS
62 changes: 62 additions & 0 deletions pkg/tests/tasks/extensions/yaml/3scale-backend.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
namespace: 3scale
labels:
3scale: backend
spec:
selector:
matchLabels:
3scale: backend
template:
metadata:
labels:
3scale: backend
spec:
containers:
- name: wiremock
image: wiremock/wiremock:3.2.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
volumeMounts:
- name: wiremock-mapping
mountPath: /home/wiremock/mappings
volumes:
- name: wiremock-mapping
configMap:
name: wiremock-mapping-3scale-backend
---
apiVersion: v1
kind: Service
metadata:
name: backend
namespace: 3scale
labels:
3scale: backend
spec:
type: ClusterIP
selector:
3scale: backend
ports:
- port: 80
targetPort: 8080
---
# This is a mock response for 3scale backend API, which is called on plugin initialization to fetch a configuration for specified services.
apiVersion: v1
kind: ConfigMap
metadata:
name: wiremock-mapping-3scale-backend
namespace: 3scale
data:
static.json: |
{
"request": {
"method": "GET",
"url": "/transactions/authrep.xml?service_id=123&service_token=3d3bfe783a66ad7576c2389d4a8623ea613cc5146dce2e603b001ccac17e36f8&user_key=bar&usage[hits]=1"
},
"response": {
"status": 200
}
}
13 changes: 13 additions & 0 deletions pkg/tests/tasks/extensions/yaml/3scale-system-service-entry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
name: threescale-system
spec:
hosts:
- system.3scale.svc.cluster.local
ports:
- number: 80
name: http
protocol: HTTP
location: MESH_EXTERNAL
resolution: DNS
Loading

0 comments on commit 7934ac9

Please sign in to comment.