Closed mattklein123 closed 6 years ago
OK finally coming back to this. Here is what I concretely propose:
/source/extensions/<maturity_level>/access_loggers
/source/extensions/<maturity_level>/http_filters
/source/extensions/<maturity_level>/http_tracers
/source/extensions/<maturity_level>/listener_filters
/source/extensions/<maturity_level>/network_filters
/source/extensions/<maturity_level>/resolvers
/source/extensions/<maturity_level>/stat_sinks
experimental
or production
. /test
. /source/extensions/production/network_filters/tcp_proxy
The benefits of this approach are:
I considered having a different tree outside of /source
and /test
but I'm not sure it's really worth the extra effort of fixing all the resultant build issues that assume this is where code is.
@envoyproxy/maintainers any thoughts?
I like the overall plan. How do you propose dealing with filters that have libraries with mockable components? We've tended to use include/envoy
and test/mock
here, how do you envisage this mapping into the new filter space?
Is there anything special we want to do to make it globally easy to condition in/out individual filters from the build in the general OSS distribution based on this organization? What about avoiding taking certain external dependencies that are irrelevant (LuaJIT, I'm looking at you!)?
I have a personal preference, which might not be universally shared, for shorter, more concise names where possible. Would a filters/listener
, filters/network
, etc. hierarchy make sense?
I like the overall plan. How do you propose dealing with filters that have libraries with mockable components? We've tended to use include/envoy and test/mock here, how do you envisage this mapping into the new filter space?
My thinking was to break this for extensions and just put abstract interfaces and mocks (if any) directly with the test code of the extension so that it is self contained. The one thing to think about is what if extensions end up having common code. We might need some type of common
directory, allow extensions to refer to each other, or potentially require common code to go on "core" Envoy. Thoughts?
Is there anything special we want to do to make it globally easy to condition in/out individual filters from the build in the general OSS distribution based on this organization? What about avoiding taking certain external dependencies that are irrelevant (LuaJIT, I'm looking at you!)?
My thinking here is to work towards a place where every extension in the repo is governed by a bazel define that can turn it off. Thus, if no Lua filter is used, there doesn't need to be any Lua code. However, Lua already exposed the issue with common code. Does all Lua code go in extensions? Or just the filter parts?
I have a personal preference, which might not be universally shared, for shorter, more concise names where possible. Would a filters/listener, filters/network, etc. hierarchy make sense?
What do you mean exactly?
/source/extensions/<maturity_level>/access_loggers
/source/extensions/<maturity_level>/http_tracers
/source/extensions/<maturity_level>/filters/http
/source/extensions/<maturity_level>/filters/listener
/source/extensions/<maturity_level>/filters/network
/source/extensions/<maturity_level>/resolvers
/source/extensions/<maturity_level>/stat_sinks
? If so that sounds fine to me.
Things I don't love but have no good solution for - I feel like it's going to be harder/more-annoying to find code, and I dislike files moving around when we promote filters. I don't see a good way around either so shrug
Mainly I have a bunch of questions :-) Do we have definitions of what gets you over the bar to a production extension? Significant time in production by a heavy user? Active maintainer support? Do things move back out of production if they end up abandonware? I assume we're not lowering the bar for code / test for extensions directory but maybe we are if we allow a wider variety of OWNERS. Do we ever remove extensions from experimental if they don't get promoted? Maintenance cost is low if we don't extend APIs but if we have a policy anyone willing to own things can stick it in extensions, cost in subtle things like CI build time could end up being non-trivial.
My thinking here is to work towards a place where every extension in the repo is governed by a bazel define that can turn it off. Thus, if no Lua filter is used, there doesn't need to be any Lua code. However, Lua already exposed the issue with common code. Does all Lua code go in extensions? Or just the filter parts?
One thing to keep in mind is the growth of the binary itself. Do we want to keep Envoy suitable for very tight, resource constrained environments? E.g. in tiny containers or embedded applications? If so, it might be a good idea to have a plan to avoid unchecked growth in binary size. The model I'm thinking of is something closer to how the Linux kernel deals with drivers, with its config files. There are sensible minimal defaults which you can then opt-in on various features from.
I realize that if folks really want to strip it down they can today and in the future (just by creating distinct BUILD files), but making this convenient would be nice and could be accomplished somewhat structurally (e.g. by avoiding Lua code leaking into common) while we work on this specific issue.
Things I don't love but have no good solution for - I feel like it's going to be harder/more-annoying to find code, and I dislike files moving around when we promote filters. I don't see a good way around either so shrug
We can just drop
Do we have definitions of what gets you over the bar to a production extension? Significant time in production by a heavy user? Active maintainer support? Do things move back out of production if they end up abandonware? I assume we're not lowering the bar for code / test for extensions directory but maybe we are if we allow a wider variety of OWNERS. Do we ever remove extensions from experimental if they don't get promoted? Maintenance cost is low if we don't extend APIs but if we have a policy anyone willing to own things can stick it in extensions, cost in subtle things like CI build time could end up being non-trivial.
I agree these are all good questions which I don't really have answers for. My suggestion would be to just iterate what we have today into a new directory structure, and then go from there. We can initially do nothing different other than directory structure. If we decide to drop maturity level from the directory structure entirely, some of this becomes moot.
One thing to keep in mind is the growth of the binary itself. Do we want to keep Envoy suitable for very tight, resource constrained environments? E.g. in tiny containers or embedded applications? If so, it might be a good idea to have a plan to avoid unchecked growth in binary size. The model I'm thinking of is something closer to how the Linux kernel deals with drivers, with its config files. There are sensible minimal defaults which you can then opt-in on various features from.
I agree. Basically, as I said above IMO every extension in the future should have a bazel define. Some can even default to disabled if we want. I think the best course of action is to get the extensions into the new directory layout, and then start to figure out a general way to handle these types of defines. I'm guessing we can have some common Skylark that can help us here.
Hi, I will try to play around with the proposal i.e. using the following pattern
/source/extensions/filters/http
/source/extensions/filters/network
...
and reflect that to the envoyproxy/envoy-filter-example
, and will share the experience 😁
@dio thanks. FYI I'm planning on updating this issue this coming week with a final proposed plan.
@mattklein123 thanks for the heads up. I saw that as one of the agenda for the next community call. Cool!
Would this notion of "pluggable extensions" be extended to TransportSockets? (thinking about fd.io VPP integration, and could be applicable to SslTransportSocket?)
+1 on pluggable transport sockets
Yes definitely re: pluggable transport sockets. I will update the proposal to account for that also.
OK sorry for the radio silence here. The new proposal after chatting with various people is:
We will drop
There may still be a common directory inside extensions for code that is shared between extensions but is not used by core.
All config static registration will be colocated for easier inspection of how everything works.
Final directory layout:
/source/extensions/access_loggers
/source/extensions/http_tracers
/source/extensions/filters/http
/source/extensions/filters/listener
/source/extensions/filters/network
/source/extensions/resolvers
/source/extensions/stat_sinks
/source/extensions/transport_sockets
Any objections to this proposal? My plan is to get started on this once we release 1.6.0 early next week. I discussed offline with @jmarantz and he might also be interested in helping. I will coordinate with him.
@mattklein123 can you elaborate on (3)? Are you saying the factories for modules will still lives outside /source/extensions
or are you only referring to the core factory code?
IMHO dropping maturity level from the dir structure is a good idea. Less churn. Could be handled by build time defines I guess. i.e. "give me all the mature extensions" with still having per extension granularity. I'm happy to help if there is a particular task you want to farm out.
@htuch re: (3) my intention is to remove https://github.com/envoyproxy/envoy/tree/master/source/server/config and colocate the static registration with each extension. I think this will be a very nice improvement for readability.
@alagalah I had the same idea about build tagging. I think we should explore this in the future.
+1 on colocating registration with the extension.
+1 on proposal so far, but we need to also figure out what happens with test code. @dio has done some example implementation of this layout in https://github.com/envoyproxy/envoy-filter-example/pull/43/files. There are two features there of interest:
test/extensions
and test/integration/extensions
. The structuring of the integration tests is the first time we've imposed structure there. This is a nice cleanup IMHO.I think this could work, and reflects existing Envoy source/test split. We could have OWNERS in the parallel trees. Call this the "core Envoy structure for extensions".
An alternative would be to have root level extensions/filters/http/foo/source
, extensions/filters/http/foo/test, extensions/filters/http/foo/test/integration
, etc. Call this "single root structure for extensions".
I think the single root structure has a nice property, where all code is colocated and under a single OWNERS. But, it's a weird inversion of the existing order.
I'm leaning towards existing structure as per https://github.com/envoyproxy/envoy-filter-example/pull/43, but I wanted to throw it out there that we should clarify the test structure and consider the alternatives before locking the structure in.
In general, I have a preference for a "single root," but my concern was that it was going to break too many things which assume the code is in //source and //test. So I was planning on doing the former. Yes the integration cleanup sounds good to me. If people feel very strongly about "single root" we can look into what it would take to make that happen.
FWIW I'm not averse to experimental / production, I really just didn't have a feel for what went there. Most of the documented reasons for doing this were around the experimental / production split, with the last being around owners/reviewers. For that last issue, Would we have subdirectory-per-various filters and have /source/extensions//http_filters/lua owned by lua-savvy people or do more of per-file syntax?
I'd love if as a part of this move or a close follow-up we could have build rules / tools to make opting in and out of individual pluggable components easier, to head dependency bloat before we get there :-)
I'm not hearing any strong argument for single root. @mattklein123 @alyssawilk @ggreenway can you folks all take a look at https://github.com/envoyproxy/envoy-filter-example/pull/43 and see if this matches what you envisage in the new layout? If so, we can merge that and we can move to applying this to the main repo.
Yes envoyproxy/envoy-filter-example#43 is essentially what I was planning on doing. I was planning on moving the echo filter first, since it's so simple. Probably on Friday. So perhaps I can do that and then we can look at that PR and envoyproxy/envoy-filter-example#43 together and then make a final call?
I have a mild preference for single root, with test code located near the thing it is testing, instead of a parallel directory structure for tests. But I don't feel at all strongly about it, so if everyone else is happy with this, I say go for it.
I think the encoder/decoder split makes sense for the example code, but are we planning the same split for non-example code?
I'm with Greg that I mildly prefer code and tests to be colocated (test code is easy to flag in build rules with testonly) but will go with what folks prefer.
Otherwise 'tis fine by me
I think the encoder/decoder split makes sense for the example code, but are we planning the same split for non-example code?
I wasn't planning on it.
I'm with Greg that I mildly prefer code and tests to be colocated (test code is easy to flag in build rules with testonly) but will go with what folks prefer.
I don't have a strong preference on this one way or the other. The main issue I see with colocating test code with source code is that we don't do it anywhere else, so it's a bit strange. One middle ground would be to do:
/source/extensions/...
/test/extensions/...
But just put the integration tests colocated with the unit tests. That seems fine to me and would reduce one aspect of the split? Thoughts?
Agree, if we wouldn't move the rest of the tests to be colocated it'd be sort of weird. I figured if we were moving around 80% of the code it was worth discussing the other 20% but no need to make this larger than it is :-P
I prefer keeping unit tests and integration tests separate, since integration tests often cover more than one thing, so of the two choices I'd lean towards keeping things as they're done in this PR.
Yeah I think this more like 20% code movement, vs. the other way around. OK SGTM. I will do the echo PR on Friday.
First stab at reorg done for echo filter here https://github.com/envoyproxy/envoy/pull/2894. PTAL.
@alyssawilk @jmillikin-stripe and I just had our regular sync with the Bazel team and we covered this topic. The best suggestion so far is to continue our approach of using --define
, but to create custom bazel.rc
files that get fed into the Bazel CLI invocation to allow this to scale. The idea is that they would in turn be generated by a user facing configuration system or language (e.g. kernel config system, some YAML thing, whatever else is trendy) that generates the bazel.rc
.
Making this work when Envoy is composed into something like envoy-filter-example
and unifying the bazel.rc
approach with explicit cc_binary
deps for configuration is something that we should explore further.
I'm going to fill this in with an actual plan, but the rough goal is to do the following:
Move all statically registered components (filters, access loggers, stats, etc.) into an "extensions" directory. Under this directory we would have "experimental" and "stable." This would also more easily allow us to compile extensions in and out more easily.
The goal here is to be able to more clearly separate out extensions from core, and have discrete OWNERS for extensions.
cc @envoyproxy/maintainers