diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 9366341..bbad389 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -9,6 +9,30 @@ on: branches: - '*' jobs: + basic: + name: Basic integration test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + target: wasm32-unknown-unknown + - uses: arduino/setup-protoc@v1 + with: + version: '3.x' + - uses: actions-rs/cargo@v1 + with: + command: build + args: --target wasm32-unknown-unknown + - name: Run docker compose + run: | + docker compose -f ./e2e/basic/docker-compose.yaml run start_services + - name: Execute tests in the running services + run: | + make -f ./e2e/basic/Makefile test remote_address: name: Remote address integration test runs-on: ubuntu-latest diff --git a/e2e/basic/Makefile b/e2e/basic/Makefile new file mode 100644 index 0000000..446c682 --- /dev/null +++ b/e2e/basic/Makefile @@ -0,0 +1,22 @@ +SHELL = /usr/bin/env bash -o pipefail +.SHELLFLAGS = -ec +.DEFAULT_GOAL := gateway +MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +WORKDIR := $(patsubst %/,%,$(dir $(MKFILE_PATH))) +DOCKER ?= $(shell which docker 2> /dev/null || echo "docker") + +run: + $(DOCKER) compose -f docker-compose.yaml run start_services + +test: + curl --silent --output /dev/null --fail --resolve test.example.com:18000:127.0.0.1 "http://test.example.com:18000" + $(eval TMP := $(shell mktemp -d)) + curl --silent --output $(TMP)/counters.json --fail "http://127.0.0.1:18080/counters/basic" + # only one counter + NUM_COUNTERS=$$(jq --exit-status 'length' $(TMP)/counters.json) && test $${NUM_COUNTERS} -eq 1 + # check counter value + COUNTER=$$(jq -r --exit-status '.[0].remaining' $(TMP)/counters.json) && [ "$${COUNTER}" == "28" ] + +clean: + $(DOCKER) compose down --volumes --remove-orphans + $(DOCKER) compose -f docker-compose.yaml down --volumes --remove-orphans diff --git a/e2e/basic/README.md b/e2e/basic/README.md new file mode 100644 index 0000000..972694a --- /dev/null +++ b/e2e/basic/README.md @@ -0,0 +1,94 @@ +## Basic integration test + +This is a integration test to validate basic happy path. + +This test is being added to the CI test suite + +### Description + +The Wasm configuration defines a set of rules for `*.example.com`. + +Two (rate limiting) services are being defined, namely `limitadorA` and `limitadorB`. + +One `actionSet` is defined that has two actions. +Each action should hit the same limitador instance, decrementing the counter twice. + +```yaml +"services": { + "limitadorA": { + "type": "ratelimit", + "endpoint": "limitador", + "failureMode": "deny" + }, + "limitadorB": { + "type": "ratelimit", + "endpoint": "limitador", + "failureMode": "deny" + } +}, +"actionSets": [ +{ + "actions": [ + { + "service": "limitadorA", + "scope": "a", + "data": [ + { + "expression": { + "key": "a", + "value": "1" + } + } + ] + }, + { + "service": "limitadorB", + "scope": "a", + "data": [ + { + "expression": { + "key": "a", + "value": "1" + } + } + ] + } + ] +} +] +``` + +And a new limit configuration + +```yaml +- namespace: basic + max_value: 30 + seconds: 60 + conditions: + - "a == '1'" + variables: [] +``` + +The test will run one request and expect the counter to be decremented by two. +The counter starts with `30`, so after the request, the counter should be `28`. + +### Run Manually + +It requires Wasm module being built at `target/wasm32-unknown-unknown/debug/wasm_shim.wasm`. +Check *Makefile* at the root of the project to build the module. + +``` +make run +``` + +Run the test + +``` +make test +``` + +### Clean up + +``` +make clean +``` diff --git a/e2e/basic/docker-compose.yaml b/e2e/basic/docker-compose.yaml new file mode 100644 index 0000000..bcecea7 --- /dev/null +++ b/e2e/basic/docker-compose.yaml @@ -0,0 +1,56 @@ +--- +services: + envoy: + image: envoyproxy/envoy:v1.31-latest + depends_on: + - limitador + - upstream + command: + - /usr/local/bin/envoy + - --config-path + - /etc/envoy.yaml + - --log-level + - info + - --component-log-level + - wasm:debug,http:debug,router:debug + - --service-cluster + - proxy + expose: + - "80" + - "8001" + ports: + - "18000:80" + - "18001:8001" + volumes: + - ./envoy.yaml:/etc/envoy.yaml + - ../../target/wasm32-unknown-unknown/debug/wasm_shim.wasm:/opt/kuadrant/wasm/wasm_shim.wasm + limitador: + image: quay.io/kuadrant/limitador:latest + command: ["limitador-server", "-vvv", "/opt/kuadrant/limits/limits.yaml"] + ports: + - "18080:8080" + - "18081:8081" + expose: + - "8080" + - "8081" + volumes: + - ./limits.yaml:/opt/kuadrant/limits/limits.yaml + upstream: + image: quay.io/kuadrant/authorino-examples:talker-api + environment: + PORT: 3000 + expose: + - "3000" + start_services: + image: alpine + depends_on: + - envoy + command: > + /bin/sh -c " + while ! nc -z envoy 80; + do + echo sleeping; + sleep 1; + done; + echo Connected! + " diff --git a/e2e/basic/envoy.yaml b/e2e/basic/envoy.yaml new file mode 100644 index 0000000..04dad50 --- /dev/null +++ b/e2e/basic/envoy.yaml @@ -0,0 +1,134 @@ +--- +static_resources: + listeners: + - name: main + address: + socket_address: + address: 0.0.0.0 + port_value: 80 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + use_remote_address: true + xff_num_trusted_hops: 1 + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: + - "*" + routes: + - match: + prefix: "/" + route: + cluster: upstream + http_filters: + - name: envoy.filters.http.wasm + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm + config: + name: kuadrant_wasm + root_id: kuadrant_wasm + vm_config: + vm_id: vm.sentinel.kuadrant_wasm + runtime: envoy.wasm.runtime.v8 + code: + local: + filename: /opt/kuadrant/wasm/wasm_shim.wasm + allow_precompiled: true + configuration: + "@type": "type.googleapis.com/google.protobuf.StringValue" + value: > + { + "services": { + "limitadorA": { + "type": "ratelimit", + "endpoint": "limitador", + "failureMode": "deny" + }, + "limitadorB": { + "type": "ratelimit", + "endpoint": "limitador", + "failureMode": "deny" + } + }, + "actionSets": [ + { + "name": "basic", + "routeRuleConditions": { + "hostnames": [ + "*.example.com" + ] + }, + "actions": [ + { + "service": "limitadorA", + "scope": "basic", + "data": [ + { + "expression": { + "key": "a", + "value": "1" + } + } + ] + }, + { + "service": "limitadorB", + "scope": "basic", + "data": [ + { + "expression": { + "key": "a", + "value": "1" + } + } + ] + } + ] + } + ] + } + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - name: upstream + connect_timeout: 0.25s + type: STRICT_DNS + lb_policy: round_robin + load_assignment: + cluster_name: upstream + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: upstream + port_value: 3000 + - name: limitador + connect_timeout: 0.25s + type: STRICT_DNS + lb_policy: round_robin + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http2_protocol_options: {} + load_assignment: + cluster_name: limitador + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: limitador + port_value: 8081 +admin: + address: + socket_address: + address: 0.0.0.0 + port_value: 8001 diff --git a/e2e/basic/limits.yaml b/e2e/basic/limits.yaml new file mode 100644 index 0000000..16a5c16 --- /dev/null +++ b/e2e/basic/limits.yaml @@ -0,0 +1,7 @@ +--- +- namespace: basic + max_value: 30 + seconds: 60 + conditions: + - "a == '1'" + variables: []