-
Notifications
You must be signed in to change notification settings - Fork 3.9k
alts: add Google Default Channel implementation #4742
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
Conversation
ec18806 to
32fad37
Compare
ejona86
left a comment
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 few drive-by comments.
| NettyChannelBuilder.forTarget(target) | ||
| .keepAliveTime(20, TimeUnit.SECONDS) | ||
| .keepAliveTimeout(10, TimeUnit.SECONDS) | ||
| .keepAliveWithoutCalls(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.
What's going on 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.
It is the same as ALTS channel builder and internal version of channel builder. Is there a problem here to set keepAliveWithoutCalls(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.
All three of these settings are inappropriate when using TLS. The keepalive time will cause failures when going through GFE.
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 see Can I just delete these 3 lines (lines 77-79)? Or you have better suggestion?
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.
@jiangtaoli2016, if you don't need them, removing them will work.
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.
| @Override | ||
| public TsiHandshaker newHandshaker() { | ||
| // Used the shared grpc channel to connecting to the ALTS handshaker service. | ||
| ManagedChannel channel = HandshakerServiceChannel.get(); |
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 needs to be referenced counted. We can't have resources/threads live forever. That'll cause memory leaks in environments with custom ClassLoaders. You can create a SharedResourceHolder.Resource for it.
Many environments are quite temperamental when it comes to creating new threads, so it looks like we'll also want it to be lazily initialized, the first time ALTS is used in this channel.
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 created #4755. Let fix this quarter in a separate PR. It would be fairly easy to unref the channel if nobody is using it. I like to keep the thread open until application closes. Otherwise keep creating/closing thread could also be expensive. Is there concept of fiber (i.e., lightweight thread) in Java?
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 like to keep the thread open until application closes.
No. The threads are actually the primary problem.
Otherwise keep creating/closing thread could also be expensive.
This is what our SharedResourceHolder deals with, to a degree. It waits a second before actually freeing, in case there will be another use soon. In general though, we would expect users to have a long-lived Channel, so there shouldn't be rampant thread creation.
Is there concept of fiber (i.e., lightweight thread) in Java?
Nope. I wish.
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.
Let me take a look at SharedResourceHolder. I may schedule a mtg with you and @carl-mastrangelo. I remember old time ago, @carl-mastrangelo and I thought about SharedResourceHolder, but we did not use it. I don't remember the exact reason.
32fad37 to
8a787a0
Compare
|
@ejona86 Code revised with unit tests. Could you please take a look? |
|
:-/ You squashed the changes all together. Please don't do that next time, as it means I can't see any changes you've made since last time I looked. |
|
Sorry about squashing. In grpc c core PRs, reviewable is often used, thus squashing is not an issue there. The difference between first version and a few days back is that I added unit tests. I also revised GoogleDefaultProtocolNegotiator.java to add a testing-only constructor. PTAL. I will squash in the end before merge. |
| def com_google_auth_google_auth_library_oauth2_http(): | ||
| native.maven_jar( | ||
| name = "com_google_auth_google_auth_library_oauth2_http", | ||
| artifact = "com.google.auth:google-auth-library-oauth2-http:0.9.0", |
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 requires dependencies on google-http-client and google-http-client-jackson2, transitively. So it will also pull in jackson-core-asl, org.apache.httpcomponents:httpclient, and maybe j2objc-annotations.
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 there a better library to depends on? DefaultChannel needs to call GoogleCredentials.getApplicationDefault().
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.
To my knowledge... 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.
Shall we add a comment on ALTS that alts artifact will depend on all these?
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.
No, it would just be a dependency in the BUILD.bzl file. Note that if we were doing this "right" we would have a third_party/ directory that would pull in the maven_jar and include all the proper dependencies.
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.
Ack. Thanks for clarification.
| } | ||
|
|
||
| /** "Overrides" the static method in {@link ManagedChannelBuilder}. */ | ||
| public static GoogleDefaultChannelBuilder forAddress(String name, int port) { |
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 this version necessary/helpful? It seems just a target string would be more than enough. People wouldn't need to pass in ipv6 addresses here, nor would it even be common to specify a port.
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.
Okay, let remove this forAddress()
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.
Oops, if I remove this forAddress(), tests will fail on https://github.com/grpc/grpc-java/blob/master/interop-testing/src/test/java/io/grpc/ChannelAndServerBuilderTest.java#L91, because GoogleDefaultChannelBuilder extends ForwardingChannelBuilder and it supposes to define forAddress().
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.
Oh, that's right, ManagedChannelBuilder.forAddress exists. Yeah, we will want this function to exist here.
|
|
||
| @Override | ||
| public ManagedChannel build() { | ||
| InternalNettyChannelBuilder.setDynamicTransportParamsFactory(delegate(), tcpfFactory); |
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'd prefer this to be called in the constructor, to avoid mutating the builder during build().
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.
| } | ||
| } | ||
|
|
||
| static final class GoogleDefaultChannel extends ManagedChannel { |
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 isn't this an interceptor? It could be added to the underlying channel with delegate().intercept() (probably from the builder's constructor).
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 there benefit to use interceptor 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 would only implement newCall (actually interceptCall, but it's basically the same thing). All the other methods aren't necessary.
| try { | ||
| return new GoogleDefaultChannel( | ||
| delegate().build(), GoogleCredentials.getApplicationDefault()); | ||
| } catch (IOException e) { |
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 a pretty common exception. I'm not wild about hiding it behind a RuntimeException. Options:
- Throw from
forTarget() - Throw from
build() - If it throws, package that up into a
CallCredentialsor anInterceptorto fail calls on this Channel.
Maybe there's some other options.
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 can throw IOException from build(). But we need to declare build() can throws IOException in ManagedChannelBuilder and ForwardingChannelBuilder. Revise.
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.
But we need to declare build() can throws IOException in ManagedChannelBuilder and ForwardingChannelBuilder.
Oh, yeah, we would need to change those. Changing those would break API though. That means (2) is not possible.
| + "\n --use_tls=true|false Whether to use TLS. Default " + c.useTls | ||
| + "\n --use_alts=true|false Whether to use ALTS. Enable ALTS will disable TLS." | ||
| + "\n Default " + c.useAlts | ||
| + "\n --custom_credentials_type Custom credentials type to use. Default " |
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 this the argument that's being used cross-language?
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 naming of "custom_credentials_type" come from grpc c core. Although I don't like this argument name, it is better to keep it so that interop tests can work in the future.
67863a5 to
3dfa45a
Compare
|
@ejona86 code revised. PTAL |
| * @since 1.0.0 | ||
| */ | ||
| public abstract ManagedChannel build(); | ||
| public abstract ManagedChannel build() throws IOException; |
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.
As I said before, this change is not backward compatible and thus not acceptable. Please revert, along with all the other exception handling you added elsewhere to make this possible. One of the other options is still possible, but we can't add new exceptions to pre-existing stable methods.
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 reason I throw RuntimeException in the first place. We cannot throw at forTarget(), at build(). We cannot throw at ClientInterceptor.
It looks throwing RumtimeException is the best option, IMO.
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 throw in forTarget()? While the ClientInterceptor can't throw, it can fail the RPC.
|
In ALTS, we check and fail (throws runtime exception) when we detect app is not running on GCP. Similarly, when a user tries to use GoogleDefaultChannel and if it does not run on GCP and he/she does not provide google credentials, we shall fail early (e.g., through runtime exception) rather than waiting SSL handshake succeed and then fail during rpc call (e.g., through per call interceptor). What do you think @ejona86 ? |
That sounds wrong. Can you point me to that code?
I don't much problem with failing early. I have a problem with using a RuntimeException for a common case that the user should really handle. |
|
Code revised. If we fail to at getApplicationDefault(), instead of throws exception, we fail at each RPC call. @ejona86 If you are happy with the change, I can do similar trick on ALTS in the next PR. |
| } catch (IOException e) { | ||
| throw new RuntimeException(e); | ||
| String message = "Failed to get Google default credentials: " + e.getMessage(); | ||
| logger.log(Level.WARNING, message); |
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.
As a general rule, log an error or propagate the error, but not both. Remove this log statement.
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.
Got it! log statement removed.
b4267eb to
f9d3459
Compare
f9d3459 to
cbf3f7b
Compare
| credentials = MoreCallCredentials.from(GoogleCredentials.getApplicationDefault()); | ||
| } catch (IOException e) { | ||
| String message = "Failed to get Google default credentials: " + e.getMessage(); | ||
| status = Status.FAILED_PRECONDITION.withDescription(message); |
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 throw away the exception. Instead, pass it via withCause(e). At that point you shouldn't include e.getMessage() in the description.
460f1cc to
8195994
Compare
| status = Status.FAILED_PRECONDITION.withDescription(message); | ||
| status = | ||
| Status.FAILED_PRECONDITION | ||
| .withDescription("Failed to get Google default credentials.") |
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: we don't tend to end in punctuation.
8195994 to
6f7ee6c
Compare
|
Thanks @ejona86 for review! Let me squash and merge. Two more PRs coming. |
experimenting