Skip to content
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

Merged
merged 16 commits into from
Oct 13, 2017

Conversation

akonradi
Copy link
Contributor

Add Address::ResolverFactory and Address::Resolver interfaces for instantiating Address::Instances 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

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]>
Copy link
Member

@htuch htuch left a 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.

* 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
Copy link
Member

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.

* @param supplies the port on the address
* @return an appropriate Address::Instance
*/
virtual Network::Address::InstanceConstSharedPtr resolve(const std::string& address,
Copy link
Member

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).

public:
virtual ~ResolverFactory() {}

virtual ResolverPtr create() const PURE;
Copy link
Member

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.

/**
* Destructor that removes an instance of the factory from the FactoryRegistry
*/
~RegisterFactory() { FactoryRegistry<Base>::unregisterFactory(instance_); }
Copy link
Member

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
Copy link
Member

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");
Copy link
Member

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);
Copy link
Member

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);
Copy link
Member

Choose a reason for hiding this comment

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

Nit: const

htuch
htuch previously approved these changes Oct 12, 2017

TEST(ResolverTest, UninitializedAddress) {
envoy::api::v2::Address address;
EXPECT_THROW(resolveProtoAddress(address), EnvoyException);
Copy link
Member

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());
Copy link
Member

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?

Copy link
Contributor Author

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.

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]>
Copy link
Member

@htuch htuch left a 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);
Copy link
Member

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.

Copy link
Contributor Author

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_); }
Copy link
Member

@mrice32 mrice32 Oct 12, 2017

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?

Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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?

Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Member

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.

@@ -103,6 +103,7 @@ class TestResolver : public Resolver {
};

TEST(ResolverTest, NonStandardResolver) {
// TODO(akonradi) Use singleton override for this test once #1808 is resolved.
Copy link
Member

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]>
@htuch
Copy link
Member

htuch commented Oct 13, 2017

TSAN was stuck, kicked it.

@htuch htuch merged commit c85a140 into envoyproxy:master Oct 13, 2017
@akonradi akonradi deleted the v2-address-resolution branch October 16, 2017 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants