workflow
The workflow plugin supports the conditional execution of user-defined actions to client traffic based on a given set of rules, defined using APISIX expressions. This provides a granular approach to traffic management.
If you would like to apply more complex matching conditions and actions, see the traffic-label plugin.
Examples
The examples below demonstrates how you can use the workflow plugin for different scenarios.
Return Response HTTP Status Code Conditionally
The following example demonstrates a simple rule with one matching condition and one associated action to return HTTP status code conditionally.
Create a route with the workflow plugin as such:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "workflow-route",
"uri": "/anything/*",
"plugins": {
"workflow":{
"rules":[
{
"case":[
["uri", "==", "/anything/rejected"]
],
"actions":[
[
"return",
{"code": 403}
]
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org": 1
}
}
}'
services:
- name: httpbin
routes:
- uris:
- /anything/*
name: workflow-route
plugins:
workflow:
rules:
- case:
- ["uri", "==", "/anything/rejected"]
actions:
- - return
- code: 403
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: workflow-plugin-config
spec:
plugins:
- name: workflow
config:
rules:
- case:
- ["uri", "==", "/anything/rejected"]
actions:
- - return
- code: 403
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: workflow-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /anything/
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: workflow-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: workflow-route
spec:
ingressClassName: apisix
http:
- name: workflow-route
match:
paths:
- /anything/*
upstreams:
- name: httpbin-external-domain
plugins:
- name: workflow
enable: true
config:
rules:
- case:
- ["uri", "==", "/anything/rejected"]
actions:
- - return
- code: 403
Apply the configuration to your cluster:
kubectl apply -f workflow-ic.yaml
❶ Trigger the action only when the request's URI path is /anything/rejected.
❷ Return HTTP status code 403 when the rule is matched.
Send a request that matches none of the rules:
curl -i "http://127.0.0.1:9080/anything/anything"
You should receive an HTTP/1.1 200 OK response.
Send a request that matches the configured rule:
curl -i "http://127.0.0.1:9080/anything/rejected"
You should receive an HTTP/1.1 403 Forbidden response of following:
{"error_msg":"rejected by workflow"}
Apply Rate Limiting Conditionally by URI and Query Parameter
The following example demonstrates a rule with two matching conditions and one associated action to rate limit requests conditionally.
Create a route with the workflow plugin as such:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "workflow-route",
"uri": "/anything/*",
"plugins":{
"workflow":{
"rules":[
{
"case":[
["uri", "==", "/anything/rate-limit"],
["arg_env", "==", "v1"]
],
"actions":[
[
"limit-count",
{
"count":1,
"time_window":60,
"rejected_code":429
}
]
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org": 1
}
}
}'
services:
- name: httpbin
routes:
- uris:
- /anything/*
name: workflow-route
plugins:
workflow:
rules:
- case:
- ["uri", "==", "/anything/rate-limit"]
- ["arg_env", "==", "v1"]
actions:
- - limit-count
- count: 1
time_window: 60
rejected_code: 429
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: workflow-plugin-config
spec:
plugins:
- name: workflow
config:
rules:
- case:
- ["uri", "==", "/anything/rate-limit"]
- ["arg_env", "==", "v1"]
actions:
- - limit-count
- count: 1
time_window: 60
rejected_code: 429
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: workflow-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /anything/
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: workflow-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: workflow-route
spec:
ingressClassName: apisix
http:
- name: workflow-route
match:
paths:
- /anything/*
upstreams:
- name: httpbin-external-domain
plugins:
- name: workflow
enable: true
config:
rules:
- case:
- ["uri", "==", "/anything/rate-limit"]
- ["arg_env", "==", "v1"]
actions:
- - limit-count
- count: 1
time_window: 60
rejected_code: 429
Apply the configuration to your cluster:
kubectl apply -f workflow-ic.yaml
❶ Match URI path /anything/rate-limit.
❷ Match query parameter env whose value being v1. See built-in variables for more variables to help construct conditions.
❸ Apply rate limiting when both of the conditions are matched.
Generate two consecutive requests that matches the second rule:
curl -i "http://127.0.0.1:9080/anything/rate-limit?env=v1"
You should receive an HTTP/1.1 200 OK response and an HTTP 429 Too Many Requests response.
Generate requests that do not match the condition:
curl -i "http://127.0.0.1:9080/anything/anything?env=v1"
You should receive HTTP/1.1 200 OK responses for all requests, as they are not rate limited.
Apply Rate Limiting Conditionally by Consumers
The following example demonstrates how to configure the plugin to perform rate limiting based on the following specifications:
- consumer
johnshould have a quota of 5 requests within a 30-second window - consumer
janeshould have a quota of 3 requests within a 30-second window - all other consumers should have a quota of 2 requests within a 30-second window
While this example will be using key-auth, you can easily replace it with other authentication plugins.
- Admin API
- ADC
- Ingress Controller
Create a consumer john:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "john"
}'
Create key-auth credential for the consumer:
curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "cred-john-key-auth",
"plugins": {
"key-auth": {
"key": "john-key"
}
}
}'
Create a second consumer jane:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "jane"
}'
Create key-auth credential for the consumer:
curl "http://127.0.0.1:9180/apisix/admin/consumers/jane/credentials" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "cred-jane-key-auth",
"plugins": {
"key-auth": {
"key": "jane-key"
}
}
}'
Create a third consumer jimmy:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "jimmy"
}'
Create key-auth credential for the consumer:
curl "http://127.0.0.1:9180/apisix/admin/consumers/jimmy/credentials" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "cred-jimmy-key-auth",
"plugins": {
"key-auth": {
"key": "jimmy-key"
}
}
}'
Create a route with the workflow plugin as such:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "workflow-route",
"uri": "/anything",
"plugins":{
"key-auth": {},
"workflow":{
"rules":[
{
"actions": [
[
"limit-count",
{
"count": 5,
"key": "consumer_john",
"key_type": "constant",
"rejected_code": 429,
"time_window": 30,
"policy": "local"
}
]
],
"case": [
[
"consumer_name",
"==",
"john"
]
]
},
{
"actions": [
[
"limit-count",
{
"count": 3,
"key": "consumer_jane",
"key_type": "constant",
"rejected_code": 429,
"time_window": 30,
"policy": "local"
}
]
],
"case": [
[
"consumer_name",
"==",
"jane"
]
]
},
{
"actions": [
[
"limit-count",
{
"count": 2,
"key": "$consumer_name",
"key_type": "var",
"rejected_code": 429,
"time_window": 30,
"policy": "local"
}
]
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org": 1
}
}
}'
Create three consumers and a route that enables per-consumer rate limiting:
consumers:
- username: john
credentials:
- name: key-auth
type: key-auth
config:
key: john-key
- username: jane
credentials:
- name: key-auth
type: key-auth
config:
key: jane-key
- username: jimmy
credentials:
- name: key-auth
type: key-auth
config:
key: jimmy-key
services:
- name: httpbin
routes:
- uris:
- /anything
name: workflow-route
plugins:
key-auth: {}
workflow:
rules:
- case:
- ["consumer_name", "==", "john"]
actions:
- - limit-count
- count: 5
key: consumer_john
key_type: constant
rejected_code: 429
time_window: 30
policy: local
- case:
- ["consumer_name", "==", "jane"]
actions:
- - limit-count
- count: 3
key: consumer_jane
key_type: constant
rejected_code: 429
time_window: 30
policy: local
- actions:
- - limit-count
- count: 2
key: "$consumer_name"
key_type: var
rejected_code: 429
time_window: 30
policy: local
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
Create three consumers and a route that enables per-consumer rate limiting:
When consumers are configured using the Ingress Controller, the consumer name is generated in the format namespace_consumername. As a result, the consumer_name logic in the workflow plugin should match the consumer name in this format.
- Gateway API
- APISIX CRD
apiVersion: apisix.apache.org/v1alpha1
kind: Consumer
metadata:
namespace: aic
name: john
spec:
gatewayRef:
name: apisix
credentials:
- type: key-auth
name: primary-key
config:
key: john-key
---
apiVersion: apisix.apache.org/v1alpha1
kind: Consumer
metadata:
namespace: aic
name: jane
spec:
gatewayRef:
name: apisix
credentials:
- type: key-auth
name: primary-key
config:
key: jane-key
---
apiVersion: apisix.apache.org/v1alpha1
kind: Consumer
metadata:
namespace: aic
name: jimmy
spec:
gatewayRef:
name: apisix
credentials:
- type: key-auth
name: primary-key
config:
key: jimmy-key
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: workflow-plugin-config
spec:
plugins:
- name: key-auth
config:
_meta:
disable: false
- name: workflow
config:
rules:
- case:
- ["consumer_name", "==", "aic_john"]
actions:
- - limit-count
- count: 5
key: consumer_john
key_type: constant
rejected_code: 429
time_window: 30
policy: local
- case:
- ["consumer_name", "==", "aic_jane"]
actions:
- - limit-count
- count: 3
key: consumer_jane
key_type: constant
rejected_code: 429
time_window: 30
policy: local
- actions:
- - limit-count
- count: 2
key: "$consumer_name"
key_type: var
rejected_code: 429
time_window: 30
policy: local
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: workflow-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: workflow-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
apiVersion: apisix.apache.org/v2
kind: ApisixConsumer
metadata:
namespace: aic
name: john
spec:
ingressClassName: apisix
authParameter:
keyAuth:
value:
key: john-key
---
apiVersion: apisix.apache.org/v2
kind: ApisixConsumer
metadata:
namespace: aic
name: jane
spec:
ingressClassName: apisix
authParameter:
keyAuth:
value:
key: jane-key
---
apiVersion: apisix.apache.org/v2
kind: ApisixConsumer
metadata:
namespace: aic
name: jimmy
spec:
ingressClassName: apisix
authParameter:
keyAuth:
value:
key: jimmy-key
---
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: workflow-route
spec:
ingressClassName: apisix
http:
- name: workflow-route
match:
paths:
- /anything
upstreams:
- name: httpbin-external-domain
plugins:
- name: key-auth
enable: true
- name: workflow
enable: true
config:
rules:
- case:
- ["consumer_name", "==", "aic_john"]
actions:
- - limit-count
- count: 5
key: consumer_john
key_type: constant
rejected_code: 429
time_window: 30
policy: local
- case:
- ["consumer_name", "==", "aic_jane"]
actions:
- - limit-count
- count: 3
key: consumer_jane
key_type: constant
rejected_code: 429
time_window: 30
policy: local
- actions:
- - limit-count
- count: 2
key: "$consumer_name"
key_type: var
rejected_code: 429
time_window: 30
policy: local
Apply the configuration to your cluster:
kubectl apply -f workflow-ic.yaml
❶ Enable key-auth on the route.
❷ Match consumer john and apply a rate limiting quota of 5 requests within a 30-second window.
❸ Match consumer jane and apply a rate limiting quota of 3 requests within a 30-second window.
❹ Match all other consumers and apply a rate limiting quota of 2 requests within a 30-second window, per consumer.
To verify, send 6 consecutive requests with john's key:
resp=$(seq 6 | xargs -I{} curl "http://127.0.0.1:9080/anything" -H 'apikey: john-key' -o /dev/null -s -w "%{http_code}\n") && \
count_200=$(echo "$resp" | grep "200" | wc -l) && \
count_429=$(echo "$resp" | grep "429" | wc -l) && \
echo "200": $count_200, "429": $count_429
You should see the following response, showing that out of the 6 requests, 5 requests were successful (status code 200) while the others were rejected (status code 429).
200: 5, 429: 1
Send 6 consecutive requests with jane's key:
resp=$(seq 6 | xargs -I{} curl "http://127.0.0.1:9080/anything" -H 'apikey: jane-key' -o /dev/null -s -w "%{http_code}\n") && \
count_200=$(echo "$resp" | grep "200" | wc -l) && \
count_429=$(echo "$resp" | grep "429" | wc -l) && \
echo "200": $count_200, "429": $count_429
You should see the following response, showing that out of the 6 requests, 3 requests were successful (status code 200) while the others were rejected (status code 429).
200: 3, 429: 3
Send 3 consecutive requests with jimmy's key:
resp=$(seq 3 | xargs -I{} curl "http://127.0.0.1:9080/anything" -H 'apikey: jimmy-key' -o /dev/null -s -w "%{http_code}\n") && \
count_200=$(echo "$resp" | grep "200" | wc -l) && \
count_429=$(echo "$resp" | grep "429" | wc -l) && \
echo "200": $count_200, "429": $count_429
You should see the following response, showing that out of the 3 requests, 2 requests were successful (status code 200) while the others were rejected (status code 429).
200: 2, 429: 1
Apply Advanced Rate Limiting with Sliding Window
The following example demonstrates how to configure workflow with the Enterprise limit-count-advanced plugin to perform rate limiting conditionally, using the sliding window algorithm.
Create a route with the workflow plugin as such:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "workflow-route",
"uri": "/anything/*",
"plugins":{
"workflow":{
"rules":[
{
"case": [
["uri", "==", "/anything/rate-limit-advanced"]
],
"actions": [
[
"limit-count-advanced",
{
"count": 5,
"time_window": 10,
"rejected_code": 429,
"policy": "local",
"key_type": "var",
"key": "remote_addr",
"window_type": "sliding"
}
]
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org": 1
}
}
}'
services:
- name: httpbin
routes:
- uris:
- /anything/*
name: workflow-route
plugins:
workflow:
rules:
- case:
- ["uri", "==", "/anything/rate-limit-advanced"]
actions:
- - limit-count-advanced
- count: 5
time_window: 10
rejected_code: 429
policy: local
key_type: var
key: remote_addr
window_type: sliding
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: workflow-plugin-config
spec:
plugins:
- name: workflow
config:
rules:
- case:
- ["uri", "==", "/anything/rate-limit-advanced"]
actions:
- - limit-count-advanced
- count: 5
time_window: 10
rejected_code: 429
policy: local
key_type: var
key: remote_addr
window_type: sliding
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: workflow-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /anything/
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: workflow-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: workflow-route
spec:
ingressClassName: apisix
http:
- name: workflow-route
match:
paths:
- /anything/*
upstreams:
- name: httpbin-external-domain
plugins:
- name: workflow
enable: true
config:
rules:
- case:
- ["uri", "==", "/anything/rate-limit-advanced"]
actions:
- - limit-count-advanced
- count: 5
time_window: 10
rejected_code: 429
policy: local
key_type: var
key: remote_addr
window_type: sliding
Apply the configuration to your cluster:
kubectl apply -f workflow-ic.yaml
❶ Match URI path /anything/rate-limit-advanced.
❷ Apply rate limiting when the condition is matched.
❸ Set the rate limiting algorithm to sliding window.
Generate 7 requests to the route that matches the condition every other second:
for i in $(seq 7); do
(curl -I "http://127.0.0.1:9080/anything/rate-limit-advanced" &)
sleep 1
done
You should receive HTTP/1.1 200 OK responses for most requests, with the remainder being HTTP 429 Too Many Requests responses.
If you send requests to the route with other paths, such as:
curl -i "http://127.0.0.1:9080/anything/else"
You should not observe any rate limiting in effect as the condition is not matched.