lds: Add HTTP API listener.#8170
Conversation
Signed-off-by: Mark D. Roth <roth@google.com>
Signed-off-by: Mark D. Roth <roth@google.com>
|
Looks like there's a BUILD visibility issue including the HTTP connection manager config from the LDS config. Is it okay to change the HCM config to be visible from there, or is there some better way to solve this layering issue? |
Signed-off-by: Mark D. Roth <roth@google.com>
|
I've also added a very basic client capabilities mechanism to the I'm quite certain that there are additional things we'll want in client capabilities, but I don't know what they all are yet. My goal here is just to propose something simple that will meet our immediate needs. We can do something different in v3 if we think of something better in the next month or so. Please let me know what you think. Thanks! |
|
@markdroth The visibility constraints exists to prevent core protos depends on extensions. Btw what is the whole context for adding this? Is this for v2 and not v3alpha (which already exists in the tree)? My speculation is this is for the gRPC client to receive LDS? |
This is how gRPC and Envoy Mobile (and similar things) will model an API listener. cc @goaway @junr03. (I have not reviewed yet, will do so soon) |
api/envoy/api/v2/BUILD
Outdated
| "//envoy/api/v2/core:base", | ||
| "//envoy/api/v2/listener", | ||
| "//envoy/api/v2/listener:udp_listener_config", | ||
| "//envoy/config/filter/network/http_connection_manager/v2:http_connection_manager", |
There was a problem hiding this comment.
I think this causes a circular build loop for proto packages as extensions depend on v2 transitively.
There was a problem hiding this comment.
Understood. But it's not obvious how to resolve this problem. Any suggestions...?
There was a problem hiding this comment.
We might need to make a decision whether HCM belongs to the core, or whether HTTP API listener belongs to an extension.
There was a problem hiding this comment.
@markdroth let's resolve the discussion below then thinks about this, if we get rid of the port to HCM map then we don't need it.
Or just use Filter message which is the opaque config supports HCM.
There was a problem hiding this comment.
I'm fine with waiting until some of the broader issues here are resolved. However, I don't think the discussion below will actually make this issue go away. Even if we remove the port map, we will still want an HttpConnectionManager field directly in the listener for the HTTP API listener, since the whole point of this new listener type is that it is always hard-coded to a single HttpConnectionManager.
Anyway, let's see how things shake out with the broader design discussion below, and then we can come back to this.
htuch
left a comment
There was a problem hiding this comment.
This is heading the right place directionally, thanks for getting this started, but lots of API concerns, we should have full consensus from @envoyproxy/api-shepherds before merging.
mattklein123
left a comment
There was a problem hiding this comment.
Thanks for getting this started. I think this is a really critical concept for client libraries consuming the APIs.
api/envoy/api/v2/lds.proto
Outdated
| enum Type { | ||
| // Default listener type. Binds to a port. | ||
| SOCKET = 0; | ||
| // HTTP API listener. This type of listener does not bind to a socket or support L3/L4 |
There was a problem hiding this comment.
For discussion, see the existing deprecated bind_to_port option in this message. IMO the API listener we are proposing is different, but it would be good to make sure that we are fully committed to removing that other option.
Also, while I think this is fine for v2 in which this message is a bit of a mess, this is one of the examples that I want to clean up in v3 in terms of making the various listener types polymorphic in terms of TCP listener, UDP listener, QUIC listener, API listener, etc. so I think we need to move this entire message into a set of common fields and then a oneof with specific messages for each listener type. I think what you are doing here is OK for v2, but can we add comments to remind us of what we immediately want to clean up for v3?
There was a problem hiding this comment.
Agreed. Added a comment about this.
Signed-off-by: Mark D. Roth <roth@google.com>
api/envoy/api/v2/lds.proto
Outdated
| enum Type { | ||
| // Default listener type. Binds to a port. | ||
| SOCKET = 0; | ||
| // HTTP API listener. This type of listener does not bind to a socket or support L3/L4 |
There was a problem hiding this comment.
Agreed. Added a comment about this.
api/envoy/api/v2/BUILD
Outdated
| "//envoy/api/v2/core:base", | ||
| "//envoy/api/v2/listener", | ||
| "//envoy/api/v2/listener:udp_listener_config", | ||
| "//envoy/config/filter/network/http_connection_manager/v2:http_connection_manager", |
There was a problem hiding this comment.
Understood. But it's not obvious how to resolve this problem. Any suggestions...?
|
I'm tagging this with no stale bot. Let's continue this conversation when @htuch is back from OOO. /wait-any |
With this PR any management server has to generate LDS response by understand which xds client is served. IMHO this is a huge regression. |
Signed-off-by: Mark D. Roth <roth@google.com>
|
I think the one significant remaining issue to resolve here is the build dependency problem introduced by including the HCM config in lds.proto. I'd welcome suggestions on how to address that. |
api/envoy/api/v2/lds.proto
Outdated
| // Used only when the :ref:`type<envoy_api_field_Listener.type>` field is set to | ||
| // :ref:`API_HTTP<envoy_api_field_Listener.Type.API_HTTP>`. | ||
| // The HttpConnectionManager configuration to be used by the HTTP listener. | ||
| envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager |
There was a problem hiding this comment.
WDYT about adding an ApiListener message, and then inside of that having a oneof with various types of API listeners, HCM being the only one today? I think @lizan already made this point, but in the future we might want to support TCP, Redis, etc.
There was a problem hiding this comment.
I've attempted to do this. Please let me know if this is not what you had in mind.
Signed-off-by: Mark D. Roth <roth@google.com>
mattklein123
left a comment
There was a problem hiding this comment.
Thanks, yeah this is what I had in mind. @htuch will probably need to advise on the compile issues though.
api/envoy/api/v2/lds.proto
Outdated
| // [#not-implemented-hide:] | ||
| // Describes a type of API listener, which is used in non-proxy clients. The type of API | ||
| // exposed to the non-proxy application depends on the type of API listener. | ||
| message ApiListener { |
There was a problem hiding this comment.
I think we should make ApiListener a envoy.config scoped type. We're going to retire the legacy envoy.api.vN namespace in v3.
Signed-off-by: Mark D. Roth <roth@google.com>
| // Describes a type of API listener, which is used in non-proxy clients. The type of API | ||
| // exposed to the non-proxy application depends on the type of API listener. | ||
| message ApiListener { | ||
| oneof api_type { |
There was a problem hiding this comment.
Can this just be a Filter? then you don't have to explicitly use HCM so build will pass, also we don't need to modify this to support other protocols.
There was a problem hiding this comment.
The goal here is that this message should explicitly allow only the specific type of configuration appropriate to the type of API listener. If we make it a Filter, we allow invalid configs like specifying a type of filter that is not related to a listener; this way, that kind of invalid config is structurally impossible, which makes validation easier.
There was a problem hiding this comment.
Don't we need to validate anyway (with PGV and additional checks to check if the config is valid HCM config), so just one more type check shouldn't be a deal breaker, no?
There was a problem hiding this comment.
I'll let @htuch and @mattklein123 weigh in on this.
From my perspective, the fact that the HCM config happens to be a filter config is basically a coincidence; in the future, we could have other types of API listeners whose fields are not existing filter messages. And the vast majority of filter messages are not suitable as configuration for a type of API listener. So it seems better to me to tailor this to what we actually need rather than providing unnecessary flexibility that IMHO makes the configuration harder to understand.
There was a problem hiding this comment.
The Filter message is just a name with Any or Struct, what do you mean by "the vast majority of filter messages are not suitable as configuration for a type of API listener"?
There was a problem hiding this comment.
What I mean is that a Filter field expects its Any field to be populated with one of the messages in the envoy/config/filter tree (or a message for a third-party filter). The vast majority of those messages will never be suitable for use as a type of API listener.
The concept here is that an API listener exposes a type-specific API to the application. The HTTP listener conceptually provides an API that exposes reading and writing headers, data, and trailers for an HTTP request; this happens to map to the functionality provided by the HCM, and the HCM happens to also be used as a filter in Envoy, but that's just a coincidence. In the future, we could also have (e.g.) an SQL API listener that exposes an API based around sending SQL queries and processing result records, or any other L7 protocol for which there is no existing Envoy filter. And conversely, most of the existing filters do not define L7 protocols and therefore can't act as API listeners; for example, it would be inherently non-sensical to say that we are going to expose an API for applications to access a service via (e.g.) fault injection or access logging.
There was a problem hiding this comment.
What I mean is that a
Filterfield expects itsAnyfield to be populated with one of the messages in the envoy/config/filter tree (or a message for a third-party filter). The vast majority of those messages will never be suitable for use as a type of API listener.
That's why I said, "do a type check and reject the config as DPLB", seems you just don't like the name "Filter"?
In the future, we could also have (e.g.) an SQL API listener that exposes an API based around sending SQL queries and processing result records, or any other L7 protocol for which there is no existing Envoy filter. And conversely, most of the existing filters do not define L7 protocols and therefore can't act as API listeners; for example, it would be inherently non-sensical to say that we are going to expose an API for applications to access a service via (e.g.) fault injection or access logging.
This doesn't seems conflicting with use of Any (or "Filter"), it is just a free form Any, the xDS protocol we're making here should guard that at reporting capabilities of which type of Any is supported, IMHO that should not be limited by the types of proto message level here.
There was a problem hiding this comment.
I do think that using Filter is worse than Any, because it implies to the reader that any filter configuration can be used here, which is not the case. API listeners really have nothing to do with filters, and IMHO it does not make sense to conflate two unrelated concepts.
With regard to the difference between a oneof and an Any, I think the trade-off is that an Any makes it easier to add new types, whereas a oneof provides more type safety and structurally prevents invalid configurations. I realize that we can also catch that kind of problem with validation, and I'm certainly not opposed to validation, but I think it makes everyone's lives easier if the underlying structure doesn't allow misconfiguration in the first place. If that weren't the case, every single field in every proto would be an Any. :)
IMHO, Any makes sense only in cases where we expect new types to be added frequently, especially by third parties. But I don't think that's the case here; I don't expect that adding new types of API listener will happen that often, so the additional flexibility of using Any isn't much of an offsetting advantage here.
In any case, as mentioned below, we have no choice but to go with an Any for now, due to the go proto circular dependency issue. But I've added a note that we should convert this to a oneof in the v3 API.
There was a problem hiding this comment.
I agree with Mark here that makes sense to be more specific, but given that we can't due to circular dependency issues, it seems fine to use Any for now.
There was a problem hiding this comment.
IMHO,
Anymakes sense only in cases where we expect new types to be added frequently, especially by third parties. But I don't think that's the case here; I don't expect that adding new types of API listener will happen that often, so the additional flexibility of usingAnyisn't much of an offsetting advantage here.
If we don't expect third parties add new types here then oneof is totally fine. If we don't expect any "I have my own API library want to implement in-house RPC protocol with UDPA" to be come up, this is fine. Historically we did convert oneof to Any or add Any to oneof in several places in Envoy (cluster, retry policies...), so if we're fine on that expectation I'm good with that.
|
|
||
| api_proto_package( | ||
| deps = [ | ||
| "//envoy/config/filter/network/http_connection_manager/v2:http_connection_manager", |
There was a problem hiding this comment.
I think this should be v2:pkg.
There was a problem hiding this comment.
I tried this, but I ran into a circular dependency problem with the go protos: http_connection_manager.proto depends on rds.proto, which is the same directory as lds.proto, so we cannot make lds.proto transitively depend on http_connection_manager.proto.
After chatting with @kyessenov, @htuch, and @dfawley about this, I've changed the ApiListener proto to contain an Any field instead of a oneof, and I've documented that the type of the Any field dictates the type of API listener. I've also added a large comment saying that in v3, we need to fix the circular dependency and replace the Any with a oneof.
I'm not terribly happy about this, but it seems like it's the best we can do in v2.
|
|
||
| import "gogoproto/gogo.proto"; | ||
|
|
||
| option (gogoproto.equal_all) = true; |
There was a problem hiding this comment.
Please remove this. We don't need to import gogoproto anymore.
Signed-off-by: Mark D. Roth <roth@google.com>
Signed-off-by: Mark D. Roth <roth@google.com>
htuch
left a comment
There was a problem hiding this comment.
LGTM, just some package namespace comment.
/wait
| @@ -0,0 +1,24 @@ | |||
| syntax = "proto3"; | |||
|
|
|||
| package envoy.config.api_listener.v2; | |||
There was a problem hiding this comment.
Do you want to make this v2alpha until you have verified this works?
There was a problem hiding this comment.
I don't think that would actually buy us anything, because we need to use it from the v2 LDS API. But I really don't expect any significant changes here, especially since we're using Any for now; if we do need something else, we can just change what message we're using in the Any.
| @@ -0,0 +1,24 @@ | |||
| syntax = "proto3"; | |||
|
|
|||
| package envoy.config.api_listener.v2; | |||
There was a problem hiding this comment.
I would suggest making this envoy.config.listener.v2 as a package name. The proto can still be api_listener.proto, but this can be a package where general listener config goes in the future.
Signed-off-by: Mark D. Roth <roth@google.com>
junr03
left a comment
There was a problem hiding this comment.
lgtm, I don't see any constraint here that would not work with future envoy mobile work given that in brief what is being added here is an Any. Thanks for the thoughtful discussion everyone :)
| // exposed to the non-proxy application depends on the type of API listener. | ||
| // When this field is set, no other field except for :ref:`name<envoy_api_field_Listener.name>` | ||
| // should be set. | ||
| // [#next-major-version: In the v3 API, instead of this messy approach where the socket |
There was a problem hiding this comment.
Agree with this. It would be nice to subsume socket-based listener fields to their own message.
Signed-off-by: Mark D. Roth <roth@google.com>
| @@ -0,0 +1,24 @@ | |||
| syntax = "proto3"; | |||
|
|
|||
| package envoy.config.listener.v2; | |||
There was a problem hiding this comment.
Nit: also change the directory to api/envoy/config/listener/v2. LGTM once that is done.
Signed-off-by: Mark D. Roth <roth@google.com>
Signed-off-by: Mark D. Roth <roth@google.com>
Signed-off-by: Mark D. Roth <roth@google.com>
Signed-off-by: Mark D. Roth <roth@google.com>
Description: this PR introduces the initial implementation of an Api Listener based on the proto configuration merged in #8170. Notably, this PR introduces the ability to add only _one_ api listener via _bootstrap config only_. This decision was made in order to iterate into more complex setups (multiple listeners, LDS supplied listeners) in subsequent PRs. Moreover, the API listener is created in the context of Envoy's main thread not worker threads. A first use of this Api Listener can be seen in envoyproxy/envoy-mobile#616. Risk Level: low, only used in Envoy Mobile. The risk here is about building something generally useful and flexible. Note however that a couple of things were rejiggered in the HCM. Testing: unit and integration tests. Additional testing in https://github.com/lyft/envoy-mobile. Docs Changes: Added inline comments and TODOs. Proto documentation is up-to-date. Release Notes: similar to doc changes. Signed-off-by: Jose Nino <jnino@lyft.com>
Description: this PR introduces the initial implementation of an Api Listener based on the proto configuration merged in envoyproxy/envoy#8170. Notably, this PR introduces the ability to add only _one_ api listener via _bootstrap config only_. This decision was made in order to iterate into more complex setups (multiple listeners, LDS supplied listeners) in subsequent PRs. Moreover, the API listener is created in the context of Envoy's main thread not worker threads. A first use of this Api Listener can be seen in envoyproxy/envoy-mobile#616. Risk Level: low, only used in Envoy Mobile. The risk here is about building something generally useful and flexible. Note however that a couple of things were rejiggered in the HCM. Testing: unit and integration tests. Additional testing in https://github.com/lyft/envoy-mobile. Docs Changes: Added inline comments and TODOs. Proto documentation is up-to-date. Release Notes: similar to doc changes. Signed-off-by: Jose Nino <jnino@lyft.com> Mirrored from https://github.com/envoyproxy/envoy @ 9b6260fcf6ee1299744b8e5c76c1e6d9d36f7c89
Signed-off-by: Mark D. Roth roth@google.com
Description: Add HTTP API listener.
Risk Level: Low
Testing: None
Docs Changes: Inline with API protos
Release Notes: N/A
@mattklein123 and @htuch, this is an initial stab at the HTTP API listener structure we talked about. Please let me know what you think. Thanks!