Skip to content

Commit 8031a29

Browse files
authored
feat: migrate to google-auth-library for pre-emptive oauth refresh (#3228)
Now that auth library bundles [pre-emptive refresh](googleapis/google-auth-library-java#646), RefreshingOAuth2CredentialsInterceptor can be removed. Also, remove retry on UNAUTHENTICATED
1 parent 38925e8 commit 8031a29

File tree

9 files changed

+58
-1160
lines changed

9 files changed

+58
-1160
lines changed

bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/config/RetryOptions.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,7 @@ public class RetryOptions implements Serializable, Cloneable {
4747
/** For internal use only - public for technical reasons. */
4848
@InternalApi("For internal usage only")
4949
public static final Set<Status.Code> DEFAULT_ENABLE_GRPC_RETRIES_SET =
50-
ImmutableSet.of(
51-
Status.Code.DEADLINE_EXCEEDED,
52-
Status.Code.UNAVAILABLE,
53-
Status.Code.ABORTED,
54-
Status.Code.UNAUTHENTICATED);
50+
ImmutableSet.of(Status.Code.DEADLINE_EXCEEDED, Status.Code.UNAVAILABLE, Status.Code.ABORTED);
5551

5652
/**
5753
* We can timeout when reading large cells with a low value here.

bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/BigtableSession.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import com.google.cloud.bigtable.config.BulkOptions;
3030
import com.google.cloud.bigtable.config.CredentialOptions;
3131
import com.google.cloud.bigtable.config.Logger;
32-
import com.google.cloud.bigtable.config.RetryOptions;
3332
import com.google.cloud.bigtable.core.IBigtableDataClient;
3433
import com.google.cloud.bigtable.core.IBigtableTableAdminClient;
3534
import com.google.cloud.bigtable.core.IBulkMutation;
@@ -392,10 +391,9 @@ private WatchdogInterceptor setupWatchdog() {
392391
private static ClientInterceptor createAuthInterceptor(BigtableOptions options)
393392
throws IOException {
394393
CredentialInterceptorCache credentialsCache = CredentialInterceptorCache.getInstance();
395-
RetryOptions retryOptions = options.getRetryOptions();
396394
CredentialOptions credentialOptions = options.getCredentialOptions();
397395
try {
398-
return credentialsCache.getCredentialsInterceptor(credentialOptions, retryOptions);
396+
return credentialsCache.getCredentialsInterceptor(credentialOptions);
399397
} catch (GeneralSecurityException e) {
400398
throw new IOException("Could not initialize credentials.", e);
401399
}

bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/io/CredentialInterceptorCache.java

Lines changed: 12 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,13 @@
2121
import com.google.cloud.bigtable.config.CredentialFactory;
2222
import com.google.cloud.bigtable.config.CredentialOptions;
2323
import com.google.cloud.bigtable.config.CredentialOptions.CredentialType;
24-
import com.google.cloud.bigtable.config.RetryOptions;
25-
import com.google.cloud.bigtable.util.ThreadUtil;
26-
import com.google.common.util.concurrent.MoreExecutors;
2724
import io.grpc.ClientInterceptor;
28-
import io.grpc.auth.ClientAuthInterceptor;
2925
import java.io.IOException;
3026
import java.security.GeneralSecurityException;
31-
import java.util.concurrent.ExecutorService;
32-
import java.util.concurrent.Executors;
3327

3428
/**
35-
* Caches {@link com.google.cloud.bigtable.grpc.io.RefreshingOAuth2CredentialsInterceptor} for
36-
* default authorization cases. In other types of authorization, such as file based Credentials, it
37-
* will create a new one.
29+
* Caches {@link CredentialsInterceptor} for default authorization cases. In other types of
30+
* authorization, such as file based Credentials, it will create a new one.
3831
*
3932
* <p>For internal use only - public for technical reasons.
4033
*/
@@ -51,9 +44,6 @@ public static CredentialInterceptorCache getInstance() {
5144
return instance;
5245
}
5346

54-
private final ExecutorService executor =
55-
Executors.newCachedThreadPool(ThreadUtil.getThreadFactory("Credentials-Refresh-%d", true));
56-
5747
private ClientInterceptor defaultCredentialInterceptor;
5848

5949
private CredentialInterceptorCache() {}
@@ -64,26 +54,24 @@ private CredentialInterceptorCache() {}
6454
*
6555
* <ol>
6656
* <li>Look up the credentials
67-
* <li>If there are credentials, create a gRPC interceptor that gets OAuth2 security tokens and
68-
* add that token as a header on all calls. <br>
69-
* NOTE: {@link com.google.cloud.bigtable.grpc.io.RefreshingOAuth2CredentialsInterceptor}
70-
* ensures that the token stays fresh. It does token lookups asynchronously so that the
71-
* calls themselves take as little performance penalty as possible.
57+
* <li>If there are credentials, create a gRPC interceptor that adds the credentials to {@link
58+
* io.grpc.CallOptions}. <br>
59+
* NOTE: {@link OAuth2Credentials} ensures that the token stays fresh. It does token lookups
60+
* asynchronously so that the calls themselves take as little performance penalty as
61+
* possible.
7262
* <li>Cache the interceptor in step #2 if the {@link
7363
* com.google.cloud.bigtable.config.CredentialOptions} uses <a
7464
* href="https://developers.google.com/identity/protocols/application-default-credentials">
7565
* default application credentials </a>
7666
* </ol>
7767
*
7868
* @param credentialOptions Defines how credentials should be achieved
79-
* @param retryOptions a {@link com.google.cloud.bigtable.config.RetryOptions} object.
80-
* @return a HeaderInterceptor
69+
* @return a ClientInterceptor
8170
* @throws java.io.IOException if any.
8271
* @throws java.security.GeneralSecurityException if any.
8372
*/
8473
public synchronized ClientInterceptor getCredentialsInterceptor(
85-
CredentialOptions credentialOptions, RetryOptions retryOptions)
86-
throws IOException, GeneralSecurityException {
74+
CredentialOptions credentialOptions) throws IOException, GeneralSecurityException {
8775
// Default credentials is the most likely CredentialType. It's also the only CredentialType
8876
// that can be safely cached.
8977
boolean isDefaultCredentials =
@@ -99,34 +87,11 @@ public synchronized ClientInterceptor getCredentialsInterceptor(
9987
return null;
10088
}
10189

102-
// Optimization for asyncOAuth refresh
103-
if (credentials instanceof OAuth2Credentials) {
104-
RefreshingOAuth2CredentialsInterceptor oauth2Interceptor =
105-
new RefreshingOAuth2CredentialsInterceptor(executor, (OAuth2Credentials) credentials);
106-
107-
// The RefreshingOAuth2CredentialsInterceptor uses the credentials to get a security token
108-
// that
109-
// will live for a short time. That token is added on all calls by the gRPC interceptor to
110-
// allow users to access secure resources.
111-
//
112-
// Perform that token lookup asynchronously. This permits other work to be done in
113-
// parallel. The RefreshingOAuth2CredentialsInterceptor has internal locking that assures that
114-
// the oauth2 token is loaded before the interceptor proceeds with any calls.
115-
oauth2Interceptor.asyncRefresh();
116-
if (isDefaultCredentials) {
117-
defaultCredentialInterceptor = oauth2Interceptor;
118-
}
119-
return oauth2Interceptor;
120-
}
121-
122-
// Normal path
123-
ClientInterceptor jwtAuthInterceptor =
124-
new ClientAuthInterceptor(credentials, MoreExecutors.directExecutor());
90+
CredentialsInterceptor credentialsInterceptor = new CredentialsInterceptor(credentials);
12591

12692
if (isDefaultCredentials) {
127-
defaultCredentialInterceptor = jwtAuthInterceptor;
93+
defaultCredentialInterceptor = credentialsInterceptor;
12894
}
129-
130-
return jwtAuthInterceptor;
95+
return credentialsInterceptor;
13196
}
13297
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2021 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.bigtable.grpc.io;
18+
19+
import com.google.auth.Credentials;
20+
import io.grpc.CallCredentials;
21+
import io.grpc.CallOptions;
22+
import io.grpc.Channel;
23+
import io.grpc.ClientCall;
24+
import io.grpc.ClientInterceptor;
25+
import io.grpc.ForwardingClientCall;
26+
import io.grpc.MethodDescriptor;
27+
import io.grpc.auth.MoreCallCredentials;
28+
29+
public class CredentialsInterceptor implements ClientInterceptor {
30+
31+
private CallCredentials callCredentials;
32+
33+
public CredentialsInterceptor(Credentials credentials) {
34+
this.callCredentials = MoreCallCredentials.from(credentials);
35+
}
36+
37+
@Override
38+
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
39+
MethodDescriptor<ReqT, RespT> methodDescriptor, CallOptions callOptions, Channel channel) {
40+
return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(
41+
channel.newCall(methodDescriptor, callOptions.withCallCredentials(callCredentials))) {};
42+
}
43+
}

0 commit comments

Comments
 (0)