Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OSSM-3647: Add tests for 3scale plugin #633

Merged
merged 9 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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