-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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
@@ -86,6 +86,7 @@ api_proto_library_internal( | |||
"//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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this causes a circular build loop for proto packages as extensions depend on v2 transitively.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood. But it's not obvious how to resolve this problem. Any suggestions...?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. Added a comment about this.
Signed-off-by: Mark D. Roth <roth@google.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for all the feedback!
@ejona86 may want to weigh in on some of this discussion.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. Added a comment about this.
api/envoy/api/v2/BUILD
Outdated
@@ -86,6 +86,7 @@ api_proto_library_internal( | |||
"//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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should make ApiListener
a envoy.config
scoped type. We're going to retire the legacy envoy.api.vN
namespace in v3.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I mean is that a
Filter
field expects itsAny
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 usingAny
isn'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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be v2:pkg
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove this. We don't need to import gogoproto anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Signed-off-by: Mark D. Roth <roth@google.com>
Signed-off-by: Mark D. Roth <roth@google.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want to make this v2alpha
until you have verified this works?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Signed-off-by: Mark D. Roth <roth@google.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: also change the directory to api/envoy/config/listener/v2
. LGTM once that is done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Signed-off-by: Mark D. Roth <roth@google.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
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!