Skip to content

Commit

Permalink
lua support cluster specifier extension (envoyproxy#31034)
Browse files Browse the repository at this point in the history
* lua cluster specifier api

Signed-off-by: wangkai19 <[email protected]>

* fix api format

Signed-off-by: wangkai19 <[email protected]>

* add validate for api filed

Signed-off-by: wangkai19 <[email protected]>

* fix api format

Signed-off-by: wangkai19 <[email protected]>

* implement lua cluster specifier plugin

Signed-off-by: wangkai19 <[email protected]>

* fix format

Signed-off-by: wangkai19 <[email protected]>

* fix format

Signed-off-by: wangkai19 <[email protected]>

* fix format

Signed-off-by: wangkai19 <[email protected]>

* add example

Signed-off-by: wangkai19 <[email protected]>

* fix format

Signed-off-by: wangkai19 <[email protected]>

* fix format

Signed-off-by: wangkai19 <[email protected]>

* fix example

Signed-off-by: wangkai19 <[email protected]>

* fix review

Signed-off-by: wangkai19 <[email protected]>

* fix dep ci

Signed-off-by: wangkai19 <[email protected]>

* add changelog

Signed-off-by: wangkai19 <[email protected]>

* fix review

Signed-off-by: wangkai19 <[email protected]>

* fix ut

Signed-off-by: wangkai19 <[email protected]>

* add test

Signed-off-by: wangkai19 <[email protected]>

---------

Signed-off-by: wangkai19 <[email protected]>
  • Loading branch information
StarryVae authored Dec 28, 2023
1 parent 037f968 commit 57f23d3
Show file tree
Hide file tree
Showing 27 changed files with 883 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,9 @@ extensions/filters/http/oauth2 @derekargueta @mattklein123

/*/extensions/health_checkers/common @zuercher @botengyao

# Lua cluster specifier
/*/extensions/router/cluster_specifiers/lua @StarryVae @wbpcode

# Intentionally exempt (treated as core code)
/*/extensions/filters/common @UNOWNED @UNOWNED
/*/extensions/filters/http/common @UNOWNED @UNOWNED
Expand Down
1 change: 1 addition & 0 deletions api/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ proto_library(
"//envoy/extensions/retry/host/omit_host_metadata/v3:pkg",
"//envoy/extensions/retry/host/previous_hosts/v3:pkg",
"//envoy/extensions/retry/priority/previous_priorities/v3:pkg",
"//envoy/extensions/router/cluster_specifiers/lua/v3:pkg",
"//envoy/extensions/stat_sinks/graphite_statsd/v3:pkg",
"//envoy/extensions/stat_sinks/open_telemetry/v3:pkg",
"//envoy/extensions/stat_sinks/wasm/v3:pkg",
Expand Down
12 changes: 12 additions & 0 deletions api/envoy/extensions/router/cluster_specifiers/lua/v3/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py.

load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package")

licenses(["notice"]) # Apache 2

api_proto_package(
deps = [
"//envoy/config/core/v3:pkg",
"@com_github_cncf_xds//udpa/annotations:pkg",
],
)
27 changes: 27 additions & 0 deletions api/envoy/extensions/router/cluster_specifiers/lua/v3/lua.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
syntax = "proto3";

package envoy.extensions.router.cluster_specifiers.lua.v3;

import "envoy/config/core/v3/base.proto";

import "udpa/annotations/status.proto";
import "validate/validate.proto";

option java_package = "io.envoyproxy.envoy.extensions.router.cluster_specifiers.lua.v3";
option java_outer_classname = "LuaProto";
option java_multiple_files = true;
option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/router/cluster_specifiers/lua/v3;luav3";
option (udpa.annotations.file_status).package_version_status = ACTIVE;

// [#protodoc-title: Lua]
//
// Lua cluster specifier :ref:`configuration reference documentation <config_http_cluster_specifier_lua>`.
// [#extension: envoy.router.cluster_specifier_plugin.lua]

message LuaConfig {
// The lua code that Envoy will execute to select cluster.
config.core.v3.DataSource source_code = 1 [(validate.rules).message = {required: true}];

// Default cluster. It will be used when the lua code execute failure.
string default_cluster = 2;
}
1 change: 1 addition & 0 deletions api/versioning/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ proto_library(
"//envoy/extensions/retry/host/omit_host_metadata/v3:pkg",
"//envoy/extensions/retry/host/previous_hosts/v3:pkg",
"//envoy/extensions/retry/priority/previous_priorities/v3:pkg",
"//envoy/extensions/router/cluster_specifiers/lua/v3:pkg",
"//envoy/extensions/stat_sinks/graphite_statsd/v3:pkg",
"//envoy/extensions/stat_sinks/open_telemetry/v3:pkg",
"//envoy/extensions/stat_sinks/wasm/v3:pkg",
Expand Down
5 changes: 4 additions & 1 deletion bazel/repository_locations.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,10 @@ REPOSITORY_LOCATIONS_SPEC = dict(
urls = ["https://github.com/LuaJIT/LuaJIT/archive/{version}.tar.gz"],
release_date = "2023-04-16",
use_category = ["dataplane_ext"],
extensions = ["envoy.filters.http.lua"],
extensions = [
"envoy.filters.http.lua",
"envoy.router.cluster_specifier_plugin.lua",
],
cpe = "cpe:2.3:a:luajit:luajit:*",
license = "MIT",
license_url = "https://github.com/LuaJIT/LuaJIT/blob/{version}/COPYRIGHT",
Expand Down
3 changes: 3 additions & 0 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ new_features:
to add multiple namespaces with one filter and config to overwrite existing metadata is opt-in.
:ref:`untyped_metadata <envoy_v3_api_field_extensions.filters.http.set_metadata.v3.Config.metadata>`
may now be used to configure the ``set_metadata`` filter.
- area: lua
change: |
Added lua extension of router cluster specifier plugin to support selecting cluster dynamically by lua code.
deprecated:
- area: wasm
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Cluster specifier
=================

.. toctree::
:glob:
:maxdepth: 2

../../extensions/router/cluster_specifiers/*/v3/*
1 change: 1 addition & 0 deletions docs/root/api-v3/config/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@ Extensions
wasm/wasm
watchdog/watchdog
load_balancing_policies/load_balancing_policies
cluster_specifier/cluster_specifier
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ HTTP cluster specifier
:maxdepth: 2

golang
lua
92 changes: 92 additions & 0 deletions docs/root/configuration/http/cluster_specifier/lua.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
.. _config_http_cluster_specifier_lua:

Lua cluster specifier
=====================

Overview
--------

The HTTP Lua cluster specifier allows `Lua <https://www.lua.org/>`_ scripts to select router cluster
during the request flows. `LuaJIT <https://luajit.org/>`_ is used as the runtime. Because of this, the
supported Lua version is mostly 5.1 with some 5.2 features. See the `LuaJIT documentation
<https://luajit.org/extensions.html>`_ for more details.

Configuration
-------------

* This filter should be configured with the type URL ``type.googleapis.com/envoy.extensions.router.cluster_specifiers.lua.v3.LuaConfig``.
* :ref:`v3 API reference <envoy_v3_api_msg_extensions.router.cluster_specifiers.lua.v3.LuaConfig>`

A simple example of configuring Lua cluster specifier is as follow:

.. code-block:: yaml
routes:
- match:
prefix: "/"
route:
inline_cluster_specifier_plugin:
extension:
name: envoy.router.cluster_specifier_plugin.lua
typed_config:
"@type": type.googleapis.com/envoy.extensions.router.cluster_specifiers.lua.v3.LuaConfig
source_code:
inline_string: |
function envoy_on_route(route_handle)
local header_value = route_handle:headers():get("header_key")
if header_value == "fake" then
return "fake_service"
end
return "web_service"
end
default_cluster: web_service
Lua script defined in ``source_code`` will be executed to select router cluster, and just as cluster specifier
plugin in C++, Lua script can also select router cluster based on request headers. If Lua script execute failure,
``default_cluster`` will be used.

Complete example
----------------

A complete example using Docker is available in :repo:`/examples/lua-cluster-specifier`.

Route handle API
----------------

When Envoy loads the script in the configuration, it looks for a global function that the script defines:

.. code-block:: lua
function envoy_on_route(route_handle)
end
During the route path, Envoy will run *envoy_on_route* as a coroutine, passing a handle to the route API.

The following methods on the stream handle are supported:

headers()
^^^^^^^^^

.. code-block:: lua
local headers = handle:headers()
Returns the stream's headers. The headers can be used to match to select a specific cluster.

Returns a :ref:`header object <config_lua_cluster_specifier_header_wrapper>`.

.. _config_lua_cluster_specifier_header_wrapper:

Header object API
-----------------

get()
^^^^^

.. code-block:: lua
headers:get(key)
Gets a header. *key* is a string that supplies the header key. Returns a string that is the header
value or nil if there is no such header. If there are multiple headers in the same case-insensitive
key, their values will be combined with a *,* separator and returned as a string.
1 change: 1 addition & 0 deletions docs/root/start/sandboxes/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ The following sandboxes are available:
load_reporting_service
locality_load_balancing
local_ratelimit
lua-cluster-specifier
lua
mysql
opentelemetry
Expand Down
71 changes: 71 additions & 0 deletions docs/root/start/sandboxes/lua-cluster-specifier.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
.. _install_sandboxes_lua_cluster_specifier:

Lua Cluster Specifier
=====================

.. sidebar:: Requirements

.. include:: _include/docker-env-setup-link.rst

:ref:`curl <start_sandboxes_setup_curl>`
Used to make ``HTTP`` requests.

In this example, we show how the `Lua <https://www.lua.org/>`_ cluster specifier can be used with the
Envoy proxy.

The example Envoy proxy configuration includes a Lua cluster specifier plugin that contains a function:

- ``envoy_on_route(route_handle)``

:ref:`See here <config_http_cluster_specifier_lua>` for an overview of Envoy's Lua cluster specifier
and documentation regarding the function.

Step 1: Build the sandbox
*************************

Change to the ``examples/lua-cluster-specifier`` directory.

.. code-block:: console
$ pwd
envoy/examples/lua-cluster-specifier
$ docker compose pull
$ docker compose up --build -d
$ docker compose ps
Name Command State Ports
--------------------------------------------------------------------------------------------
lua-cluster-specifier-proxy-1 /docker-entrypoint.sh /usr ... Up 10000/tcp, 0.0.0.0:8000->8000/tcp
lua-cluster-specifier-web_service-1 /bin/echo-server Up 0.0.0.0:8080->8080/tcp
Step 2: Send a request to the normal service
********************************************

The output from the ``curl`` command below should return 200, since the lua code select the normal service.

Terminal 1

.. code-block:: console
$ curl -i localhost:8000/anything 2>&1 |grep 200
HTTP/1.1 200 OK
Step 3: Send a request to the fake service
******************************************

The output from the ``curl`` command below should return 503, since the lua code select the fake service.

Terminal 1

.. code-block:: console
$ curl -i localhost:8000/anything -H "header_key:fake" 2>&1 |grep 503
HTTP/1.1 503 Service Unavailable
.. seealso::

:ref:`Envoy Lua cluster specifier <config_http_cluster_specifier_lua>`
Learn more about the Envoy Lua cluster specifier.

`Lua <https://www.lua.org/>`_
The Lua programming language.
2 changes: 2 additions & 0 deletions examples/lua-cluster-specifier/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
To learn about this sandbox and for instructions on how to run it please head over
to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/lua-cluster-specifier.html)
14 changes: 14 additions & 0 deletions examples/lua-cluster-specifier/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
services:

proxy:
build:
context: .
dockerfile: ../shared/envoy/Dockerfile
ports:
- "${PORT_PROXY:-8000}:8000"

web_service:
build:
context: ../shared/echo
ports:
- "${PORT_WEB:-8080}:8080"
58 changes: 58 additions & 0 deletions examples/lua-cluster-specifier/envoy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
static_resources:
listeners:
- name: main
address:
socket_address:
address: 0.0.0.0
port_value: 8000
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
codec_type: AUTO
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains:
- "*"
routes:
- match:
prefix: "/"
route:
inline_cluster_specifier_plugin:
extension:
name: envoy.router.cluster_specifier_plugin.lua
typed_config:
"@type": type.googleapis.com/envoy.extensions.router.cluster_specifiers.lua.v3.LuaConfig
source_code:
inline_string: |
function envoy_on_route(route_handle)
local header_value = route_handle:headers():get("header_key")
if header_value == "fake" then
return "fake_service"
end
return "web_service"
end
default_cluster: web_service

http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

clusters:
- name: web_service
type: STRICT_DNS # static
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: web_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: httpbin.org
port_value: 80
19 changes: 19 additions & 0 deletions examples/lua-cluster-specifier/verify.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash -e

export NAME=lua-cluster-specifier
export PORT_PROXY="${LUA_CLUSTER_PORT_PROXY:-12620}"
export PORT_WEB="${LUA_CLUSTER_PORT_WEB:-12621}"

# shellcheck source=examples/verify-common.sh
. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh"

run_log "Test Lua cluster specifier with normal cluster"
responds_with_header \
"HTTP/1.1 200 OK" \
"http://localhost:${PORT_PROXY}/"

run_log "Test Lua cluster specifier with fake cluster"
responds_with_header \
"HTTP/1.1 503 Service Unavailable" \
"http://localhost:${PORT_PROXY}/" \
-H 'header_key: fake'
6 changes: 6 additions & 0 deletions source/extensions/extensions_build_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,12 @@ EXTENSIONS = {
# Geolocation Provider
#
"envoy.geoip_providers.maxmind": "//source/extensions/geoip_providers/maxmind:config",

#
# cluster specifier plugin
#

"envoy.router.cluster_specifier_plugin.lua": "//source/extensions/router/cluster_specifiers/lua:config",
}

# These can be changed to ["//visibility:public"], for downstream builds which
Expand Down
Loading

0 comments on commit 57f23d3

Please sign in to comment.