Skip to content

Commit 0734e8e

Browse files
committed
HBASE-25051 DIGEST based auth broken for rpc based ConnectionRegistry (#5631)
Signed-off-by: Bryan Beaudreault <bbeaudreault@apache.org> (cherry picked from commit 16de74c)
1 parent ee6332a commit 0734e8e

File tree

63 files changed

+1031
-483
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1031
-483
lines changed

hbase-client/src/main/java/org/apache/hadoop/hbase/client/AbstractRpcBasedConnectionRegistry.java

Lines changed: 20 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,17 @@
3333
import java.util.concurrent.atomic.AtomicInteger;
3434
import java.util.function.Predicate;
3535
import org.apache.hadoop.conf.Configuration;
36-
import org.apache.hadoop.hbase.HConstants;
3736
import org.apache.hadoop.hbase.HRegionLocation;
3837
import org.apache.hadoop.hbase.RegionLocations;
3938
import org.apache.hadoop.hbase.ServerName;
4039
import org.apache.hadoop.hbase.exceptions.ClientExceptionsUtil;
4140
import org.apache.hadoop.hbase.exceptions.MasterRegistryFetchException;
4241
import org.apache.hadoop.hbase.ipc.HBaseRpcController;
43-
import org.apache.hadoop.hbase.ipc.RpcClient;
44-
import org.apache.hadoop.hbase.ipc.RpcClientFactory;
4542
import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
4643
import org.apache.hadoop.hbase.security.User;
4744
import org.apache.hadoop.hbase.util.FutureUtils;
4845
import org.apache.yetus.audience.InterfaceAudience;
4946

50-
import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
51-
import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableMap;
5247
import org.apache.hbase.thirdparty.com.google.protobuf.Message;
5348
import org.apache.hbase.thirdparty.com.google.protobuf.RpcCallback;
5449

@@ -79,30 +74,21 @@ abstract class AbstractRpcBasedConnectionRegistry implements ConnectionRegistry
7974

8075
private final int hedgedReadFanOut;
8176

82-
// Configured list of end points to probe the meta information from.
83-
private volatile ImmutableMap<ServerName, ClientMetaService.Interface> addr2Stub;
84-
8577
// RPC client used to talk to the masters.
86-
private final RpcClient rpcClient;
78+
private final ConnectionRegistryRpcStubHolder rpcStubHolder;
8779
private final RpcControllerFactory rpcControllerFactory;
88-
private final int rpcTimeoutMs;
8980

9081
private final RegistryEndpointsRefresher registryEndpointRefresher;
9182

92-
protected AbstractRpcBasedConnectionRegistry(Configuration conf,
83+
protected AbstractRpcBasedConnectionRegistry(Configuration conf, User user,
9384
String hedgedReqsFanoutConfigName, String initialRefreshDelaySecsConfigName,
9485
String refreshIntervalSecsConfigName, String minRefreshIntervalSecsConfigName)
9586
throws IOException {
9687
this.hedgedReadFanOut =
9788
Math.max(1, conf.getInt(hedgedReqsFanoutConfigName, HEDGED_REQS_FANOUT_DEFAULT));
98-
rpcTimeoutMs = (int) Math.min(Integer.MAX_VALUE,
99-
conf.getLong(HConstants.HBASE_RPC_TIMEOUT_KEY, HConstants.DEFAULT_HBASE_RPC_TIMEOUT));
100-
// XXX: we pass cluster id as null here since we do not have a cluster id yet, we have to fetch
101-
// this through the master registry...
102-
// This is a problem as we will use the cluster id to determine the authentication method
103-
rpcClient = RpcClientFactory.createClient(conf, null);
10489
rpcControllerFactory = RpcControllerFactory.instantiate(conf);
105-
populateStubs(getBootstrapNodes(conf));
90+
rpcStubHolder = new ConnectionRegistryRpcStubHolder(conf, user, rpcControllerFactory,
91+
getBootstrapNodes(conf));
10692
// could return null here is refresh interval is less than zero
10793
registryEndpointRefresher =
10894
RegistryEndpointsRefresher.create(conf, initialRefreshDelaySecsConfigName,
@@ -114,19 +100,7 @@ protected AbstractRpcBasedConnectionRegistry(Configuration conf,
114100
protected abstract CompletableFuture<Set<ServerName>> fetchEndpoints();
115101

116102
private void refreshStubs() throws IOException {
117-
populateStubs(FutureUtils.get(fetchEndpoints()));
118-
}
119-
120-
private void populateStubs(Set<ServerName> addrs) throws IOException {
121-
Preconditions.checkNotNull(addrs);
122-
ImmutableMap.Builder<ServerName, ClientMetaService.Interface> builder =
123-
ImmutableMap.builderWithExpectedSize(addrs.size());
124-
User user = User.getCurrent();
125-
for (ServerName masterAddr : addrs) {
126-
builder.put(masterAddr,
127-
ClientMetaService.newStub(rpcClient.createRpcChannel(masterAddr, user, rpcTimeoutMs)));
128-
}
129-
addr2Stub = builder.build();
103+
rpcStubHolder.refreshStubs(() -> FutureUtils.get(fetchEndpoints()));
130104
}
131105

132106
/**
@@ -211,20 +185,25 @@ private <T extends Message> void groupCall(CompletableFuture<T> future, Set<Serv
211185

212186
protected final <T extends Message> CompletableFuture<T> call(Callable<T> callable,
213187
Predicate<T> isValidResp, String debug) {
214-
ImmutableMap<ServerName, ClientMetaService.Interface> addr2StubRef = addr2Stub;
215-
Set<ServerName> servers = addr2StubRef.keySet();
216-
List<ClientMetaService.Interface> stubs = new ArrayList<>(addr2StubRef.values());
217-
Collections.shuffle(stubs, ThreadLocalRandom.current());
218188
CompletableFuture<T> future = new CompletableFuture<>();
219-
groupCall(future, servers, stubs, 0, callable, isValidResp, debug,
220-
new ConcurrentLinkedQueue<>());
189+
FutureUtils.addListener(rpcStubHolder.getStubs(), (addr2Stub, error) -> {
190+
if (error != null) {
191+
future.completeExceptionally(error);
192+
return;
193+
}
194+
Set<ServerName> servers = addr2Stub.keySet();
195+
List<ClientMetaService.Interface> stubs = new ArrayList<>(addr2Stub.values());
196+
Collections.shuffle(stubs, ThreadLocalRandom.current());
197+
groupCall(future, servers, stubs, 0, callable, isValidResp, debug,
198+
new ConcurrentLinkedQueue<>());
199+
});
221200
return future;
222201
}
223202

224203
@RestrictedApi(explanation = "Should only be called in tests", link = "",
225204
allowedOnPath = ".*/src/test/.*")
226-
Set<ServerName> getParsedServers() {
227-
return addr2Stub.keySet();
205+
Set<ServerName> getParsedServers() throws IOException {
206+
return FutureUtils.get(rpcStubHolder.getStubs()).keySet();
228207
}
229208

230209
/**
@@ -277,8 +256,8 @@ public void close() {
277256
if (registryEndpointRefresher != null) {
278257
registryEndpointRefresher.stop();
279258
}
280-
if (rpcClient != null) {
281-
rpcClient.close();
259+
if (rpcStubHolder != null) {
260+
rpcStubHolder.close();
282261
}
283262
}, getClass().getSimpleName() + ".close");
284263
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hbase.client;
19+
20+
import java.util.ArrayList;
21+
import java.util.Collections;
22+
import java.util.List;
23+
import java.util.Set;
24+
import java.util.concurrent.CompletableFuture;
25+
import org.apache.hadoop.conf.Configuration;
26+
import org.apache.hadoop.hbase.ServerName;
27+
import org.apache.hadoop.hbase.ipc.HBaseRpcController;
28+
import org.apache.hadoop.hbase.ipc.RpcClient;
29+
import org.apache.hadoop.hbase.ipc.RpcClientFactory;
30+
import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
31+
import org.apache.hadoop.hbase.security.User;
32+
import org.apache.hadoop.hbase.util.FutureUtils;
33+
import org.apache.yetus.audience.InterfaceAudience;
34+
import org.slf4j.Logger;
35+
import org.slf4j.LoggerFactory;
36+
37+
import org.apache.hbase.thirdparty.com.google.protobuf.RpcCallback;
38+
import org.apache.hbase.thirdparty.com.google.protobuf.RpcChannel;
39+
40+
import org.apache.hadoop.hbase.shaded.protobuf.generated.RegistryProtos.ConnectionRegistryService;
41+
import org.apache.hadoop.hbase.shaded.protobuf.generated.RegistryProtos.GetConnectionRegistryRequest;
42+
import org.apache.hadoop.hbase.shaded.protobuf.generated.RegistryProtos.GetConnectionRegistryResponse;
43+
44+
/**
45+
* Fetch cluster id through special preamble header.
46+
* <p>
47+
* An instance of this class should only be used once, like:
48+
*
49+
* <pre>
50+
* new ClusterIdFetcher().fetchClusterId()
51+
* </pre>
52+
*
53+
* Calling the fetchClusterId multiple times will lead unexpected behavior.
54+
* <p>
55+
* See HBASE-25051 for more details.
56+
*/
57+
@InterfaceAudience.Private
58+
class ClusterIdFetcher {
59+
60+
private static final Logger LOG = LoggerFactory.getLogger(ClusterIdFetcher.class);
61+
62+
private final List<ServerName> bootstrapServers;
63+
64+
private final User user;
65+
66+
private final RpcClient rpcClient;
67+
68+
private final RpcControllerFactory rpcControllerFactory;
69+
70+
private final CompletableFuture<String> future;
71+
72+
ClusterIdFetcher(Configuration conf, User user, RpcControllerFactory rpcControllerFactory,
73+
Set<ServerName> bootstrapServers) {
74+
this.user = user;
75+
// use null cluster id here as we do not know the cluster id yet, we will fetch it through this
76+
// rpc client
77+
this.rpcClient = RpcClientFactory.createClient(conf, null);
78+
this.rpcControllerFactory = rpcControllerFactory;
79+
this.bootstrapServers = new ArrayList<ServerName>(bootstrapServers);
80+
// shuffle the bootstrap servers so we will not always fetch from the same one
81+
Collections.shuffle(this.bootstrapServers);
82+
future = new CompletableFuture<String>();
83+
}
84+
85+
/**
86+
* Try get cluster id from the server with the given {@code index} in {@link #bootstrapServers}.
87+
*/
88+
private void getClusterId(int index) {
89+
ServerName server = bootstrapServers.get(index);
90+
LOG.debug("Going to request {} for getting cluster id", server);
91+
// user and rpcTimeout are both not important here, as we will not actually send any rpc calls
92+
// out, only a preamble connection header, but if we pass null as user, there will be NPE in
93+
// some code paths...
94+
RpcChannel channel = rpcClient.createRpcChannel(server, user, 0);
95+
ConnectionRegistryService.Interface stub = ConnectionRegistryService.newStub(channel);
96+
HBaseRpcController controller = rpcControllerFactory.newController();
97+
stub.getConnectionRegistry(controller, GetConnectionRegistryRequest.getDefaultInstance(),
98+
new RpcCallback<GetConnectionRegistryResponse>() {
99+
100+
@Override
101+
public void run(GetConnectionRegistryResponse resp) {
102+
if (!controller.failed()) {
103+
LOG.debug("Got connection registry info: {}", resp);
104+
future.complete(resp.getClusterId());
105+
return;
106+
}
107+
if (ConnectionUtils.isUnexpectedPreambleHeaderException(controller.getFailed())) {
108+
// this means we have connected to an old server where it does not support passing
109+
// cluster id through preamble connnection header, so we fallback to use null
110+
// cluster id, which is the old behavior
111+
LOG.debug("Failed to get connection registry info, should be an old server,"
112+
+ " fallback to use null cluster id", controller.getFailed());
113+
future.complete(null);
114+
} else {
115+
LOG.debug("Failed to get connection registry info", controller.getFailed());
116+
if (index == bootstrapServers.size() - 1) {
117+
future.completeExceptionally(controller.getFailed());
118+
} else {
119+
// try next bootstrap server
120+
getClusterId(index + 1);
121+
}
122+
}
123+
}
124+
});
125+
126+
}
127+
128+
CompletableFuture<String> fetchClusterId() {
129+
getClusterId(0);
130+
// close the rpc client after we finish the request
131+
FutureUtils.addListener(future, (r, e) -> rpcClient.close());
132+
return future;
133+
}
134+
}

hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ public static CompletableFuture<AsyncConnection> createAsyncConnection(Configura
338338
final User user, Map<String, byte[]> connectionAttributes) {
339339
return TraceUtil.tracedFuture(() -> {
340340
CompletableFuture<AsyncConnection> future = new CompletableFuture<>();
341-
ConnectionRegistry registry = ConnectionRegistryFactory.getRegistry(conf);
341+
ConnectionRegistry registry = ConnectionRegistryFactory.getRegistry(conf, user);
342342
addListener(registry.getClusterId(), (clusterId, error) -> {
343343
if (error != null) {
344344
registry.close();

hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionOverAsyncConnection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
import org.apache.hadoop.hbase.ServerName;
3030
import org.apache.hadoop.hbase.TableName;
3131
import org.apache.hadoop.hbase.log.HBaseMarkers;
32-
import org.apache.hadoop.hbase.util.ConcurrentMapUtils.IOExceptionSupplier;
3332
import org.apache.hadoop.hbase.util.FutureUtils;
33+
import org.apache.hadoop.hbase.util.IOExceptionSupplier;
3434
import org.apache.yetus.audience.InterfaceAudience;
3535
import org.slf4j.Logger;
3636
import org.slf4j.LoggerFactory;

hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionRegistryFactory.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static org.apache.hadoop.hbase.HConstants.CLIENT_CONNECTION_REGISTRY_IMPL_CONF_KEY;
2121

2222
import org.apache.hadoop.conf.Configuration;
23+
import org.apache.hadoop.hbase.security.User;
2324
import org.apache.hadoop.hbase.util.ReflectionUtils;
2425
import org.apache.yetus.audience.InterfaceAudience;
2526

@@ -33,10 +34,10 @@ private ConnectionRegistryFactory() {
3334
}
3435

3536
/** Returns The connection registry implementation to use. */
36-
static ConnectionRegistry getRegistry(Configuration conf) {
37+
static ConnectionRegistry getRegistry(Configuration conf, User user) {
3738
Class<? extends ConnectionRegistry> clazz =
3839
conf.getClass(CLIENT_CONNECTION_REGISTRY_IMPL_CONF_KEY, RpcConnectionRegistry.class,
3940
ConnectionRegistry.class);
40-
return ReflectionUtils.newInstance(clazz, conf);
41+
return ReflectionUtils.newInstance(clazz, conf, user);
4142
}
4243
}

0 commit comments

Comments
 (0)