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

xds: define base interfaces for filter factories #9391

Merged
merged 18 commits into from
Jan 7, 2020

Conversation

kyessenov
Copy link
Contributor

@kyessenov kyessenov commented Dec 18, 2019

Signed-off-by: Kuat Yessenov [email protected]

Introduces a typed factory and an untyped factory as base types for all extension factories.
Code refactor to facilitate using the config type as the primary selection mechanism for an extension.
This change should not cause any behavioral change. The actual switch is in a follow-up PR.

Contributes to: #9358

Risk Level: low
Testing: existing tests pass
Docs Changes:
Release Notes:
[Optional Fixes #Issue]
[Optional Deprecated:]

@jmarantz
Copy link
Contributor

@kyessenov can you look at CI? Thanks!

@kyessenov
Copy link
Contributor Author

Sorry, I meant to click draft button. Working on tests.

Signed-off-by: Kuat Yessenov <[email protected]>
@kyessenov
Copy link
Contributor Author

/cc @htuch
since this overlaps with some API migration work.
A few observations after going through the code that might need attention:

  • some filters don't use typed_config field to host Any (e.g. dubbo)
  • some factories are married to using names to select a factory (UDP, protocol options, quic transport sockets)
  • we would need an API oracle that doesn't need extension name to migrate protobufs

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.

@kyessenov we already have an API oracle for this, take a look at https://github.com/envoyproxy/envoy/blob/master/source/common/config/api_type_oracle.h. We leave breadcrumb links across different versions of messages. There is also a more complete API type database, which we don't link in for size reasons.

I think it would be nice to have a solution that works consistently across all forms of extension. I think that what you have uncovered is that we haven't been doing a great job at ensuring factories and extensions adopt consistent conventions, which is something that should be improved going forward.

Do you need to land this before the v3 API migration? I'm thinking it could be better to do after 1.13.0 if there are no dependencies. If there's something that you think we will need in v3 to make this tractable, would be good to know soon.

CC @envoyproxy/api-shepherds @yanavlasov (for factory naming conventions)

class TypedConfig {
public:
/**
* @return ProtobufTypes::MessagePtr create empty config proto message for v2. The config, which
Copy link
Member

Choose a reason for hiding this comment

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

Given that folks are going to be starting to move to v3 in early 2020, do we want to do a lot of work to make this supported in v2?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We need some bridge to associate a protobuf type with a factory. We probably could invert this, and just use type to instantiate a proto. Either way works, I'm aiming for minimal disruption in this PR.

Copy link
Member

Choose a reason for hiding this comment

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

What do you think about getting #8933 landed? In this world, all extensions are typed..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that would be wonderful. I was going to special case empty config but would be better without that.

Signed-off-by: Kuat Yessenov <[email protected]>
@kyessenov
Copy link
Contributor Author

kyessenov commented Dec 19, 2019

I don't have a strong need to land this before v3 migration. I think it would be good to make the transition smooth though, so it might be a good idea to relax the spec without breaking compatibility first. I find API-first method of detecting extensions is much better than using extension names. We could probably start a UDPA spec for a generic extension config in parallel:

message ExtensionConfig {
  // Resource name for the config (to be referred in the filter config discovery service)
  string name = 1;
  // Opaque config. The type URL is used to select an extension and upgrade the config if necessary.
  google.protobuf.Any typed_config = 2;
  // Transparent metadata for control plane annotations.
  core.Metadata metadata = 3;
}

@yanavlasov
Copy link
Contributor

To make sure I understand. The type() method of extension factory provides URL string of corresponding proto message for configuring the extensions. In this way cofig consumer can find and instantiate filters of correct type, right?
Also, is this related to #9358 in any way?

@htuch
Copy link
Member

htuch commented Dec 20, 2019

@yanavlasov I tagged you for this comment of @kyessenov:

some factories are married to using names to select a factory (UDP, protocol options, quic transport sockets)

I'm generally in agreement with @kyessenov on API native conventions for conveying type. I'm hoping this naturally is a thing in v3 once we get rid of empty proto extensions.

@@ -74,6 +74,7 @@ class TypedMetadataFactory {
parse(const ProtobufWkt::Struct& data) const PURE;

static std::string category() { return "typed_metadata"; }
static std::string type() { return ""; }
Copy link
Member

Choose a reason for hiding this comment

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

qq from a skim: what does type() mean and why is it set to empty string everywhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's set to "" for factories that don't have protos defined.
type() means the protobuf message name for the config of a factory.

Copy link
Member

Choose a reason for hiding this comment

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

Is there some way to make this more obvious and less boilerplate either via templating, base class with a default, etc.?

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 happy case is handled by TypedConfig base class. I was thinking we may want to revisit the unhappy cases and fix them, so wasn't optimizing for that. we can provide a default base class UntypedConfig for that.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah something as simple as UntypedConfig IMO would make it a lot more clear, but up to you.

Copy link
Contributor

Choose a reason for hiding this comment

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

Would it make sense to have a common base class for all factories at this point? I think there might be a critical mass of attributes common to all factories.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, category, name, and type are sufficient for a base class.

/**
* @return config proto full name
*/
virtual std::string type() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it make sense to name it config_type_url()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe config_type? I didn't want to call it URL since it's not really a type URL as in Any. Calling message name is also confusing (unless you're used to protobuf lingua).

Copy link
Member

Choose a reason for hiding this comment

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

If we're going to bike shed, what about we replace the whole category and config type thing with kind, as in https://en.wikipedia.org/wiki/Kind_(type_theory).

Copy link
Contributor Author

@kyessenov kyessenov Dec 23, 2019

Choose a reason for hiding this comment

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

Kind might confuse a k8s user, since in k8s the API group and version are separated from the kind. Kind means an "object kind" in that object-oriented world.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why not use the type URL as in Any?

Copy link
Contributor Author

@kyessenov kyessenov Dec 26, 2019

Choose a reason for hiding this comment

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

Type URL is ambiguous. It looks like proto_registry/proto_name where proto_registry is usually type.googleapis.com but it doesn't have to be. It's some sort of indicator about where to fetch the descriptor, although I haven't seen anyone using it that way.

Copy link
Contributor

@yanavlasov yanavlasov Dec 26, 2019

Choose a reason for hiding this comment

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

Ok, if proto descriptor is less ambiguous than type URL then I should change corresponding field name in the Extension proto in PR #9301

Signed-off-by: Kuat Yessenov <[email protected]>
@kyessenov
Copy link
Contributor Author

Updated the change to remove any behavioral change, since refactoring got quite large. Please take a look.

Signed-off-by: Kuat Yessenov <[email protected]>
@kyessenov
Copy link
Contributor Author

kyessenov commented Dec 26, 2019

There is an issue with registering factories based on proto objects. The factory registration call races ahead of the protobuf registration, which causes an issue with incomplete proto objects. There are two possible way to solve this:

  1. make type to factory index be lazily constructed.
  2. change the implementation to return a textual proto string; this would require some macro to convert a parametric C++ proto type to a proto type.

I moved the code that causes it out of this PR, so it'd be easier to tackle. Let me know if this PR is good enough.

@kyessenov
Copy link
Contributor Author

Sent envoyproxy/envoy-filter-example#109 to fix the build error in a sister repo.

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

In general this approach LGTM.

Comment on lines 52 to 54
if (ptr == nullptr) {
return "";
}
Copy link
Member

Choose a reason for hiding this comment

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

Should this ever return nullptr? Should this be an ASSERT/RELEASE_ASSERT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated to use ASSERT. Seems reasonable since this interface requires a proto config.

* arrives in an opaque google.protobuf.Struct message, will be converted to JSON and then parsed
* into this empty proto.
*/
virtual ProtobufTypes::MessagePtr createEmptyConfigProto() PURE;
Copy link
Member

Choose a reason for hiding this comment

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

One thing we could do, and it doesn't have to be this PR, is move away from having each individual filter return empty protos. Instead, they could either return a type name or descriptor, and we could have more general machinery for assembling the empty proto. Might not be a big win, but just thought I'd throw it out there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agree. It also helps with the registration initialization fiasco, since protobuf initializer may run after factory initializers.

@kyessenov
Copy link
Contributor Author

@htuch can you help merging the linked filter example PR to make tests pass here: envoyproxy/envoy-filter-example#109

@kyessenov kyessenov changed the title xds: dis-associate filter name from filter factory xds: define base interfaces for filter factories Jan 3, 2020
htuch
htuch previously approved these changes Jan 3, 2020
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, thanks!

@htuch
Copy link
Member

htuch commented Jan 3, 2020

@mattklein123 any final thoughts on this one?

@mattklein123
Copy link
Member

Nope ship it! Nice work.

@lizan
Copy link
Member

lizan commented Jan 4, 2020

@kyessenov the CI failure is real because envoy-filter-example doesn't build with this change. Can you raise a PR there to accommodate this?

@kyessenov
Copy link
Contributor Author

@lizan I already did that and it got merged. How are you supposed to update both repos in tandem?

@lizan
Copy link
Member

lizan commented Jan 4, 2020

Signed-off-by: Kuat Yessenov <[email protected]>
Signed-off-by: Kuat Yessenov <[email protected]>
Signed-off-by: Kuat Yessenov <[email protected]>
Signed-off-by: Kuat Yessenov <[email protected]>
Signed-off-by: Kuat Yessenov <[email protected]>
@lizan lizan merged commit 154207e into envoyproxy:master Jan 7, 2020
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.

7 participants