Skip to content

lua: Add per route config#9279

Closed
dio wants to merge 6 commits intoenvoyproxy:masterfrom
dio:per-route-lua-filter
Closed

lua: Add per route config#9279
dio wants to merge 6 commits intoenvoyproxy:masterfrom
dio:per-route-lua-filter

Conversation

@dio
Copy link
Copy Markdown
Member

@dio dio commented Dec 9, 2019

Description: This patch adds Lua HTTP filter per route config.
Risk Level: Low
Testing: unit test, integration test
Docs Changes: Added
Release Notes: Added
Fixes #3124

Signed-off-by: Dhi Aurrahman dio@tetrate.io

@repokitteh-read-only
Copy link
Copy Markdown

CC @envoyproxy/api-shepherds: Your approval is needed for changes made to api/.

🐱

Caused by: #9279 was opened by dio.

see: more, trace.

dio added 5 commits December 9, 2019 21:29
This patch adds Lua HTTP filter per route config.

Signed-off-by: Dhi Aurrahman <dio@tetrate.io>
Signed-off-by: Dhi Aurrahman <dio@tetrate.io>
Signed-off-by: Dhi Aurrahman <dio@tetrate.io>
Signed-off-by: Dhi Aurrahman <dio@tetrate.io>
Signed-off-by: Dhi Aurrahman <dio@tetrate.io>
@dio dio marked this pull request as ready for review December 10, 2019 21:56
@dio dio changed the title WIP: lua: Add per route config lua: Add per route config Dec 10, 2019
@dio
Copy link
Copy Markdown
Member Author

dio commented Dec 11, 2019

@mattklein123 I'm not sure if this is the right approach to implement this. WDYT?

@mattklein123 mattklein123 self-assigned this Dec 11, 2019
Copy link
Copy Markdown
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this is cool. A few API comments to get started.

/wait

// strings so complex scripts can be easily expressed inline in the configuration.
string inline_code = 1 [(validate.rules).string = {min_bytes: 1}];

// The Lua source codes that Envoy will execute based on name. Key is Lua source code name, e.g.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: s/source codes/source code

// The Lua source codes that Envoy will execute based on name. Key is Lua source code name, e.g.
// ``foo.lua``, this name can be referenced by
// :ref:`LuaPerRoute <envoy_api_msg_config.filter.http.lua.v2.LuaPerRoute>`.
map<string, api.v2.core.DataSource> source_codes = 2;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: s/source_codes/source_code

@@ -18,4 +20,23 @@ message Lua {
// be properly escaped. YAML configuration may be easier to read since YAML supports multi-line
// strings so complex scripts can be easily expressed inline in the configuration.
string inline_code = 1 [(validate.rules).string = {min_bytes: 1}];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From an API standpoint, it's kind of strange that we still require inline code if we now support named code. Should we now potentially relax requiring inline_code and deprecate it, and optionally allow a root/global code by name?


// The name of a Lua source code defined in
// :ref:`Lua.source_codes <envoy_api_field_config.filter.http.lua.v2.Lua.source_codes>`.
string name = 2 [(validate.rules).string = {min_bytes: 1}];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get that you might want to lookup the code by name for performance/de-dup reasons, but wouldn't you also potentially want to allow the code to be delivered via RDS? I think this would block that? Should this be a oneof that allows either lookup by name or direct inline code?

Copy link
Copy Markdown
Member Author

@dio dio Dec 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, "direct inline code" here is to open the possibility to "compile" code on the fly (when the route is being called)?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's what I meant, or at least have some type of facility to see if we already have a thread local Lua context ready for a per-route code? I'm not sure if we would then need some type of callback to clean this up. I would need to look into all of the ownership stuff in more detail to advise. But I think this is optimally how it would work and we should optimally make sure the API works for that? WDYT?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM. I'll update the PR to accommodate that first then.

@erikbos
Copy link
Copy Markdown
Contributor

erikbos commented Dec 15, 2019

@dio Thanks for implementing this! I just gave docker.io/dio123/envoy-lua a try with the following configuration:

          - match:
              prefix: "/route1"
            route:
              cluster: test
              prefix_rewrite: "/people"
            per_filter_config:
              envoy.lua:
                name: route1.lua
          - match:
              prefix: "/route2"
            route:
              cluster: test
              prefix_rewrite: "/people"
            per_filter_config:
              envoy.lua:
                name: route2.lua

      - name: envoy.lua
        typed_config:
          "@type": type.googleapis.com/envoy.config.filter.http.lua.v2.Lua
          inline_code: |
            function envoy_on_response(request_handle)
              request_handle:headers():add("Global catchall","Delivery catch all")
            end

          source_codes:
            route1.lua:
              inline_string: |
                function envoy_on_request(request_handle)
                  request_handle:headers():add("Requested","route 1")
                end
                function envoy_on_response(response_handle)
                  response_handle:headers():add("Handled","route 1")
                end

            route3.lua:
              inline_string: |
                function envoy_on_request(request_handle)
                  request_handle:headers():add("Requested","route 2")
                end
                function envoy_on_response(response_handle)
                  response_handle:headers():add("Handled","route 2")
                end

A few observations:

  1. Requesting /route1 works fine: upstream receives additional header, but I don't see the downstream header "Handled: route1". Is my configuration correct?

  2. Requesting /route2 (referring "route3.lua" which does not exist) results in Envoy logging 200 status code logged, with RESPONSE_FLAGS = "-", client gets disconnected and no upstream cluster was contacted. Looks wrong? Shouldn't this result in a clean http 503 response with a corresponding response flag?

  3. The global envoy_on_response() does not get called next to the route specific lua.. (perhaps related to 1) Is that functionally actually still supposed to work? (Would be nice :-)

  4. Envoy does not complain about route2.lua not existing, I assume because of it's eventual consistency model? (I'm an envoy beginner)

(To comment on Matt's remark: yes I'd love to deliver all of this via xDS)

@erikbos
Copy link
Copy Markdown
Contributor

erikbos commented Dec 15, 2019

Played with it a bit more:

  1. Requesting /route1 works fine: upstream receives additional header, but I don't see the downstream header "Handled: route1". Is my configuration correct?

Adding an empty non-route specific envoy_on_request() ensures route1.lua's envoy_on_response() does run.. (hm.. how is possible that these two influence each other?)

  1. Requesting /route2 (referring "route3.lua" which does not exist) results in Envoy logging 200 status code logged, with RESPONSE_FLAGS = "-", client gets disconnected and no upstream cluster was contacted. Looks wrong? Shouldn't this result in a clean http 503 response with a corresponding response flag?

Found the reason: this was caused by setting a non-valid header "Global catchall" with a space in it, my bad.

  1. The global envoy_on_response() does not get called next to the route specific lua.. (perhaps related to 1) Is that functionally actually still supposed to work? (Would be nice :-)

With 2 fixed it appears it falls back to running the non-route specific lua in case the per-route function does not exist.

And I just noticed a per-route lua function followed by a for-all-routes lua function can be defined by having multiple "envoy.lua" filters in sequence. Nice 👍

@dio
Copy link
Copy Markdown
Member Author

dio commented Dec 15, 2019

@erikbos Thanks for testing the sketch.

(To comment on Matt's remark: yes I'd love to deliver all of this via xDS)

I'll try to add this one.

@dio
Copy link
Copy Markdown
Member Author

dio commented Dec 15, 2019

Re your issue on 1, I'll try to check it out and do some tests.

@stale
Copy link
Copy Markdown

stale bot commented Dec 27, 2019

This pull request has been automatically marked as stale because it has not had activity in the last 7 days. It will be closed in 7 days if no further activity occurs. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions!

@stale stale bot added the stale stalebot believes this issue/PR has not been touched recently label Dec 27, 2019
@stale
Copy link
Copy Markdown

stale bot commented Jan 3, 2020

This pull request has been automatically closed because it has not had activity in the last 14 days. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions!

@stale stale bot closed this Jan 3, 2020
@wbpcode
Copy link
Copy Markdown
Member

wbpcode commented May 11, 2020

Is there any progress in this work?

@dio
Copy link
Copy Markdown
Member Author

dio commented May 11, 2020

@wbpcode yes, I've been working on something else. I'll try to update this week.

ThreadLocal::SlotPtr tls_slot_;
uint64_t current_global_slot_{};
absl::flat_hash_map<std::string, ThreadLocal::SlotPtr> tls_slot_map_;
absl::flat_hash_map<std::string, uint64_t> current_global_slot_map_;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not keep ThreadLocalState unchanged, but maintain a map in FilterConfig to manage different Lua scripts?

@wbpcode
Copy link
Copy Markdown
Member

wbpcode commented May 11, 2020

Thanks. I am very much looking forward to the completion of this function, because I just need this function recently.

@wbpcode
Copy link
Copy Markdown
Member

wbpcode commented May 14, 2020

@dio I implemented a new version based on your commit and it seems to work fine. If you are too busy recently, maybe I can continue to complete this function based on your work.

@erikbos
Copy link
Copy Markdown
Contributor

erikbos commented May 14, 2020

I'd like to see this functionality included as well :-) I am available for testing if you need a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api stale stalebot believes this issue/PR has not been touched recently waiting

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support per-route config for Lua

4 participants