-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
config: v2 Address resolution using named resolver #1835
Conversation
This will be used to support resolved addresses, where the resolved (physical) name is different from the unresolved (logical) name. Signed-off-by: Alex Konradi <[email protected]>
Used for address translation. This supports a completely synchronous interface, so all translation must be done inline without any network access. DNS resolution is thus explicitly forbidden, and must be done through the already-existing DNS machinery. Signed-off-by: Alex Konradi <[email protected]>
Does exactly what it says; resolves string IP addresses into Address::Instance objects Signed-off-by: Alex Konradi <[email protected]>
This will make it possible to use RegisterFactory in tests without leaking state between tests. Signed-off-by: Alex Konradi <[email protected]>
Now implemented by the Network::Address::ResolverFactory registry and appropriate interfaces. Signed-off-by: Alex Konradi <[email protected]>
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.
Great, looks mostly good, comments are basically nits.
include/envoy/network/resolver.h
Outdated
* Resolve a custom address string and port to an Address::Instance | ||
* @param address the address to resolve | ||
* @param supplies the port on the address | ||
* @return an appropriate Address::Instance |
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. Envoy convention is to do @return Network::Address::InstanceConstSharedPtr an appropriate ...
. TBH, it's a bit inconsistent in practice, but see https://github.com/envoyproxy/envoy/blob/master/STYLE.md#deviations-from-google-c-style-guidelines.
include/envoy/network/resolver.h
Outdated
* @param supplies the port on the address | ||
* @return an appropriate Address::Instance | ||
*/ | ||
virtual Network::Address::InstanceConstSharedPtr resolve(const std::string& address, |
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.
Why can't we just resolve
on a envoy::api::v2::SocketAddress
https://github.com/envoyproxy/data-plane-api/blob/master/api/address.proto#L11? This would allow a single resolve
method and might make the interface more extensible should we chose to grow the SocketAddress
in the future (not that likely, but you never know).
include/envoy/network/resolver.h
Outdated
public: | ||
virtual ~ResolverFactory() {} | ||
|
||
virtual ResolverPtr create() const PURE; |
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: createResolver
for consistency with other factory interfaces.
include/envoy/registry/registry.h
Outdated
/** | ||
* Destructor that removes an instance of the factory from the FactoryRegistry | ||
*/ | ||
~RegisterFactory() { FactoryRegistry<Base>::unregisterFactory(instance_); } |
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 whole static registration facility is (already) violating the static initialization order fiasco rules in the style guide. We're adding another way that things can go wrong in terms of destructor ordering here, but since its only effect is to unregister I would argue it's safe.
namespace Address { | ||
|
||
/** | ||
* Implementation of a resolver for IP addresses |
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.
Very tiny nit. Prefer sentences to end with .
(here and everywhere).
|
||
Network::Address::InstanceConstSharedPtr resolve(const std::string&, | ||
const std::string&) override { | ||
throw EnvoyException("named ports are not supported by this resolver"); |
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: not support by IP resolver")
(or something to that effect).
|
||
TEST_F(IpResolverTest, DisallowsNamedPort) { | ||
auto resolver = factory_->create(); | ||
EXPECT_THROW(resolver->resolve("1.2.3.4", "http"), EnvoyException); |
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.
Could be good to validate it's the exception we're after with EXPECT_THROW_WITH_MESSAGE
. Do we have full coverage of all the throw
cases in the PR?
TestResolver(const std::map<std::string, std::string> name_mappings) | ||
: name_mappings_(name_mappings) {} | ||
InstanceConstSharedPtr resolve(const std::string& logical, uint32_t port) { | ||
std::string physical = getPhysicalName(logical); |
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: const
Signed-off-by: Alex Konradi <[email protected]>
Signed-off-by: Alex Konradi <[email protected]>
Signed-off-by: Alex Konradi <[email protected]>
Signed-off-by: Alex Konradi <[email protected]>
Signed-off-by: Alex Konradi <[email protected]>
|
||
TEST(ResolverTest, UninitializedAddress) { | ||
envoy::api::v2::Address address; | ||
EXPECT_THROW(resolveProtoAddress(address), EnvoyException); |
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: would also prefer EXPECT_THROW_WITH_MESSAGE
here.
if (resolver_factory == nullptr) { | ||
throw EnvoyException(fmt::format("Unknown address resolver: {}", resolver_name)); | ||
} | ||
ResolverPtr resolver(resolver_factory->createResolver()); |
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's the reasoning behind having a resolver factory which then creates a resolver for each resolved address? Why not just have the resolver in the registry do the job?
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 reasoning was that this would allow each resolver to keep state, such as for future asynchronous requests, and is more consistent generally with the rest of the codebase. That being said, it incurs some overhead and is probably unnecessary.
Signed-off-by: Alex Konradi <[email protected]>
It might be helpful to have separate Resolvers and ResolverFactories if addresses were begin resolved asynchronously. Until then, though, there is no need to have both. Signed-off-by: Alex Konradi <[email protected]>
Signed-off-by: Alex Konradi <[email protected]>
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 modulo nits, TSAN/ASAN look like they are failing, can you take a look?
resolver_factory = Registry::FactoryRegistry<ResolverFactory>::getFactory( | ||
Config::AddressResolverNames::get().IP); | ||
resolver = | ||
Registry::FactoryRegistry<Resolver>::getFactory(Config::AddressResolverNames::get().IP); |
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.
Is getFactory
now a misnomer? It's not returning a factory.. I thought it was fine before for the reasons you stated, but it's OK this way, just needs some changes to nomenclature.
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.
A Resolver is not a literal factory, but it does produce Address::Instances. Alternatively, we can rename FactoryRegistry and its methods to something more generic like NamedRegistry, since that's the only method classes are required to implement.
/** | ||
* Destructor that removes an instance of the factory from the FactoryRegistry. | ||
*/ | ||
~RegisterFactory() { FactoryRegistry<Base>::unregisterFactory(instance_); } |
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 new setup creates a slightly bigger violation of the static ordering fiasco - we now have destructors being called in some unknown ordering (in addition to the constructors). @akonradi mentioned this was done so that tests don't leak state. Maybe it would be preferable to create an unregister
method on the FactoryRegistry
that manually removes the pointer (and allows the user to delete, maybe by returning as a std::unique_ptr
) rather than doing it by default on destruction of the RegisterFactory
class since we'd only need this in tests? @htuch thoughts?
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.
@akonradi can you point at the specific test where the leak was an issue?
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.
https://github.com/akonradi/envoy/blob/915c7d3b432ae9a4abbc4f69da5bf19694feb59a/test/common/network/resolver_impl_test.cc#L145 is where there could have been one if the resolver from https://github.com/akonradi/envoy/blob/915c7d3b432ae9a4abbc4f69da5bf19694feb59a/test/common/network/resolver_impl_test.cc#L106 was allowed to leak. FWIW it seems like we've avoided this in the rest of the tests because we don't register test factories and stick to testing the included ones.
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 where a singleton override for test would be useful. @alyssawilk any thoughts on how far along the work is to provide 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.
Hasn't started (not assigned) but it looked like a pretty simple implementation. If it's as few lines of code as I think it could be folded in 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.
You're talking about #1808? I'd rather make that a separate pull request and either wait to merge it once it lands in master or add a TODO 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.
OK, TODO sounds good to me, and update the issue to reference, thanks.
Fixes broken CI builds Signed-off-by: Alex Konradi <[email protected]>
Signed-off-by: Alex Konradi <[email protected]>
@@ -103,6 +103,7 @@ class TestResolver : public Resolver { | |||
}; | |||
|
|||
TEST(ResolverTest, NonStandardResolver) { | |||
// TODO(akonradi) Use singleton override for this test once #1808 is resolved. |
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.
... and get rid of the need to unregister factories...
Signed-off-by: Alex Konradi <[email protected]>
TSAN was stuck, kicked it. |
Add
Address::ResolverFactory
andAddress::Resolver
interfaces for instantiatingAddress::Instance
s from strings in protos. The interface is completely synchronous, so all translation must be done inline without any network access, meaning this can't be used to resolve hostnames via DNS. Doing so would require making more things asynchronous; this can be done, but let's not do it until it's necessary.This is progress towards #1477