mirror of https://github.com/grpc/grpc-java.git
Merge a8f76b0b7b
into 42e1829b37
This commit is contained in:
commit
bd3d8e51fa
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2025 The gRPC Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.grpc.alts;
|
||||
|
||||
import com.google.api.client.json.gson.GsonFactory;
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature;
|
||||
import com.google.auth.oauth2.AccessToken;
|
||||
import com.google.auth.oauth2.OAuth2Credentials;
|
||||
import com.google.common.io.Files;
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.auth.MoreCallCredentials;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* JWT token file call credentials.
|
||||
* See gRFC A97 (https://github.com/grpc/proposal/pull/492).
|
||||
*/
|
||||
public final class JwtTokenFileCallCredentials extends OAuth2Credentials {
|
||||
private String path = null;
|
||||
|
||||
private JwtTokenFileCallCredentials(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessToken refreshAccessToken() throws IOException {
|
||||
String tokenString = new String(Files.toByteArray(new File(path)), StandardCharsets.UTF_8);
|
||||
Long expTime = JsonWebSignature.parse(new GsonFactory(), tokenString)
|
||||
.getPayload()
|
||||
.getExpirationTimeSeconds();
|
||||
if (expTime == null) {
|
||||
throw new IOException("No expiration time found for JWT token");
|
||||
}
|
||||
|
||||
return AccessToken.newBuilder()
|
||||
.setTokenValue(tokenString)
|
||||
.setExpirationTime(new Date(expTime * 1000L))
|
||||
.build();
|
||||
}
|
||||
|
||||
// using {@link MoreCallCredentials} adapter to be compatible with {@link CallCredentials} iface
|
||||
public static CallCredentials create(String path) {
|
||||
JwtTokenFileCallCredentials jwtTokenFileCallCredentials = new JwtTokenFileCallCredentials(path);
|
||||
return MoreCallCredentials.from(jwtTokenFileCallCredentials);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright 2025 The gRPC Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.grpc.alts;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import com.google.auth.oauth2.AccessToken;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.google.common.truth.Truth;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
/** Unit tests for {@link JwtTokenFileCallCredentials}. */
|
||||
@RunWith(JUnit4.class)
|
||||
public class JwtTokenFileCallCredentialsTest {
|
||||
@Rule
|
||||
public TemporaryFolder tempFolder = new TemporaryFolder();
|
||||
|
||||
private File jwtTokenFile;
|
||||
private JwtTokenFileCallCredentials unit;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
jwtTokenFile = tempFolder.newFile(new String("jwt.json"));
|
||||
|
||||
Constructor<JwtTokenFileCallCredentials> ctor =
|
||||
JwtTokenFileCallCredentials.class.getDeclaredConstructor(String.class);
|
||||
ctor.setAccessible(true);
|
||||
unit = ctor.newInstance(jwtTokenFile.toString());
|
||||
}
|
||||
|
||||
private void fillJwtTokenWithoutExpiration(File jwtFile) throws Exception {
|
||||
FileOutputStream outputStream = new FileOutputStream(jwtFile);
|
||||
String content =
|
||||
BaseEncoding.base64().encode(
|
||||
new String("{\"typ\": \"JWT\", \"alg\": \"HS256\"}").getBytes(StandardCharsets.UTF_8))
|
||||
+ "."
|
||||
+ BaseEncoding.base64().encode(
|
||||
new String("{\"name\": \"Google\"}").getBytes(StandardCharsets.UTF_8))
|
||||
+ "."
|
||||
+ BaseEncoding.base64().encode(new String("signature").getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.write(content.getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.close();
|
||||
}
|
||||
|
||||
private String fillValidJwtToken(File jwtFile, Long expTime) throws Exception {
|
||||
FileOutputStream outputStream = new FileOutputStream(jwtFile);
|
||||
String content =
|
||||
BaseEncoding.base64().encode(
|
||||
new String("{\"typ\": \"JWT\", \"alg\": \"HS256\"}").getBytes(StandardCharsets.UTF_8))
|
||||
+ "."
|
||||
+ BaseEncoding.base64().encode(
|
||||
String.format("{\"exp\": %d}", expTime).getBytes(StandardCharsets.UTF_8))
|
||||
+ "."
|
||||
+ BaseEncoding.base64().encode(new String("signature").getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.write(content.getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.close();
|
||||
return content;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenJwtTokenFileEmpty_WhenTokenRefreshed_ExpectException() {
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
unit.refreshAccessToken();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenJwtTokenFileWithoutExpiration_WhenTokenRefreshed_ExpectException()
|
||||
throws Exception {
|
||||
|
||||
fillJwtTokenWithoutExpiration(jwtTokenFile);
|
||||
|
||||
Exception ex = assertThrows(IOException.class, () -> {
|
||||
unit.refreshAccessToken();
|
||||
});
|
||||
|
||||
String expectedMsg = "No expiration time found for JWT token";
|
||||
String actualMsg = ex.getMessage();
|
||||
|
||||
assertEquals(expectedMsg, actualMsg);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenValidJwtTokenFile_WhenTokenRefreshed_ExpectAccessTokenInstance()
|
||||
throws Exception {
|
||||
final Long givenExpTimeInSeconds = 1753364000L;
|
||||
final Date givenExpTimeDate = new Date(givenExpTimeInSeconds * 1000L);
|
||||
|
||||
String givenTokenValue = fillValidJwtToken(jwtTokenFile, givenExpTimeInSeconds);
|
||||
|
||||
AccessToken token = unit.refreshAccessToken();
|
||||
|
||||
Truth.assertThat(token.getExpirationTime()).isEquivalentAccordingToCompareTo(givenExpTimeDate);
|
||||
assertEquals(token.getTokenValue(), givenTokenValue);
|
||||
}
|
||||
}
|
|
@ -18,7 +18,10 @@ package io.grpc.xds;
|
|||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.ChannelCredentials;
|
||||
import io.grpc.CompositeCallCredentials;
|
||||
import io.grpc.internal.GrpcUtil;
|
||||
import io.grpc.internal.JsonUtil;
|
||||
import io.grpc.xds.client.BootstrapperImpl;
|
||||
import io.grpc.xds.client.XdsInitializationException;
|
||||
|
@ -33,6 +36,8 @@ class GrpcBootstrapperImpl extends BootstrapperImpl {
|
|||
private static final String BOOTSTRAP_PATH_SYS_PROPERTY = "io.grpc.xds.bootstrap";
|
||||
private static final String BOOTSTRAP_CONFIG_SYS_ENV_VAR = "GRPC_XDS_BOOTSTRAP_CONFIG";
|
||||
private static final String BOOTSTRAP_CONFIG_SYS_PROPERTY = "io.grpc.xds.bootstrapConfig";
|
||||
private static final String GRPC_EXPERIMENTAL_XDS_BOOTSTRAP_CALL_CREDS =
|
||||
"GRPC_EXPERIMENTAL_XDS_BOOTSTRAP_CALL_CREDS";
|
||||
@VisibleForTesting
|
||||
String bootstrapPathFromEnvVar = System.getenv(BOOTSTRAP_PATH_SYS_ENV_VAR);
|
||||
@VisibleForTesting
|
||||
|
@ -41,6 +46,9 @@ class GrpcBootstrapperImpl extends BootstrapperImpl {
|
|||
String bootstrapConfigFromEnvVar = System.getenv(BOOTSTRAP_CONFIG_SYS_ENV_VAR);
|
||||
@VisibleForTesting
|
||||
String bootstrapConfigFromSysProp = System.getProperty(BOOTSTRAP_CONFIG_SYS_PROPERTY);
|
||||
@VisibleForTesting
|
||||
static boolean xdsBootstrapCallCredsEnabled = GrpcUtil.getFlag(
|
||||
GRPC_EXPERIMENTAL_XDS_BOOTSTRAP_CALL_CREDS, false);
|
||||
|
||||
GrpcBootstrapperImpl() {
|
||||
super();
|
||||
|
@ -90,7 +98,7 @@ class GrpcBootstrapperImpl extends BootstrapperImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Object getImplSpecificConfig(Map<String, ?> serverConfig, String serverUri)
|
||||
protected Object getImplSpecificChannelCredConfig(Map<String, ?> serverConfig, String serverUri)
|
||||
throws XdsInitializationException {
|
||||
return getChannelCredentials(serverConfig, serverUri);
|
||||
}
|
||||
|
@ -135,4 +143,58 @@ class GrpcBootstrapperImpl extends BootstrapperImpl {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getImplSpecificCallCredConfig(Map<String, ?> serverConfig, String serverUri)
|
||||
throws XdsInitializationException {
|
||||
return getCallCredentials(serverConfig, serverUri);
|
||||
}
|
||||
|
||||
private static CallCredentials getCallCredentials(Map<String, ?> serverConfig,
|
||||
String serverUri)
|
||||
throws XdsInitializationException {
|
||||
List<?> rawCallCredsList = JsonUtil.getList(serverConfig, "call_creds");
|
||||
if (rawCallCredsList == null || rawCallCredsList.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
CallCredentials callCredentials =
|
||||
parseCallCredentials(JsonUtil.checkObjectList(rawCallCredsList), serverUri);
|
||||
return callCredentials;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static CallCredentials parseCallCredentials(List<Map<String, ?>> jsonList,
|
||||
String serverUri)
|
||||
throws XdsInitializationException {
|
||||
CallCredentials callCredentials = null;
|
||||
if (xdsBootstrapCallCredsEnabled) {
|
||||
for (Map<String, ?> callCreds : jsonList) {
|
||||
String type = JsonUtil.getString(callCreds, "type");
|
||||
if (type != null) {
|
||||
XdsCredentialsProvider provider = XdsCredentialsRegistry.getDefaultRegistry()
|
||||
.getProvider(type);
|
||||
if (provider != null) {
|
||||
Map<String, ?> config = JsonUtil.getObject(callCreds, "config");
|
||||
if (config == null) {
|
||||
config = ImmutableMap.of();
|
||||
}
|
||||
CallCredentials parsedCallCredentials = provider.newCallCredentials(config);
|
||||
if (parsedCallCredentials == null) {
|
||||
throw new XdsInitializationException(
|
||||
"Invalid bootstrap: server " + serverUri + " with invalid 'config' for " + type
|
||||
+ " 'call_creds'");
|
||||
}
|
||||
|
||||
if (callCredentials == null) {
|
||||
callCredentials = parsedCallCredentials;
|
||||
} else {
|
||||
callCredentials = new CompositeCallCredentials(
|
||||
callCredentials, parsedCallCredentials);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return callCredentials;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ import io.grpc.CallCredentials;
|
|||
import io.grpc.CallOptions;
|
||||
import io.grpc.ChannelCredentials;
|
||||
import io.grpc.ClientCall;
|
||||
import io.grpc.CompositeCallCredentials;
|
||||
import io.grpc.CompositeChannelCredentials;
|
||||
import io.grpc.Context;
|
||||
import io.grpc.Grpc;
|
||||
import io.grpc.ManagedChannel;
|
||||
|
@ -68,12 +70,27 @@ final class GrpcXdsTransportFactory implements XdsTransportFactory {
|
|||
|
||||
public GrpcXdsTransport(Bootstrapper.ServerInfo serverInfo, CallCredentials callCredentials) {
|
||||
String target = serverInfo.target();
|
||||
ChannelCredentials channelCredentials = (ChannelCredentials) serverInfo.implSpecificConfig();
|
||||
ChannelCredentials channelCredentials =
|
||||
(ChannelCredentials) serverInfo.implSpecificChannelCredConfig();
|
||||
Object callCredConfig = serverInfo.implSpecificCallCredConfig();
|
||||
if (callCredConfig != null) {
|
||||
channelCredentials = CompositeChannelCredentials.create(
|
||||
channelCredentials, (CallCredentials) callCredConfig);
|
||||
}
|
||||
|
||||
this.channel = Grpc.newChannelBuilder(target, channelCredentials)
|
||||
.keepAliveTime(5, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
if (callCredentials != null && callCredConfig != null) {
|
||||
this.callCredentials =
|
||||
new CompositeCallCredentials(callCredentials, (CallCredentials) callCredConfig);
|
||||
} else if (callCredConfig != null) {
|
||||
this.callCredentials = (CallCredentials) callCredConfig;
|
||||
} else {
|
||||
this.callCredentials = callCredentials;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public GrpcXdsTransport(ManagedChannel channel, CallCredentials callCredentials) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package io.grpc.xds;
|
||||
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.ChannelCredentials;
|
||||
import io.grpc.Internal;
|
||||
import java.util.Map;
|
||||
|
@ -49,6 +50,17 @@ public abstract class XdsCredentialsProvider {
|
|||
*/
|
||||
protected abstract ChannelCredentials newChannelCredentials(Map<String, ?> jsonConfig);
|
||||
|
||||
/**
|
||||
* Creates a {@link CallCredentials} from the given jsonConfig, or
|
||||
* {@code null} if the given config is invalid. The provider is free to ignore
|
||||
* the config if it's not needed for producing the channel credentials.
|
||||
*
|
||||
* @param jsonConfig json config that can be consumed by the provider to create
|
||||
* the channel credentials
|
||||
*
|
||||
*/
|
||||
protected abstract CallCredentials newCallCredentials(Map<String, ?> jsonConfig);
|
||||
|
||||
/**
|
||||
* Returns the xDS credential name associated with this provider which makes it selectable
|
||||
* via {@link XdsCredentialsRegistry#getProvider}. This is called only when the class is loaded.
|
||||
|
|
|
@ -114,7 +114,7 @@ final class XdsCredentialsRegistry {
|
|||
new XdsCredentialsProviderPriorityAccessor());
|
||||
if (providerList.isEmpty()) {
|
||||
logger.warning("No XdsCredsRegistry found via ServiceLoader, including for GoogleDefault, "
|
||||
+ "TLS and Insecure. This is probably due to a broken build.");
|
||||
+ "TLS, Insecure and JWT token file. This is probably due to a broken build.");
|
||||
}
|
||||
instance = new XdsCredentialsRegistry();
|
||||
for (XdsCredentialsProvider provider : providerList) {
|
||||
|
@ -171,6 +171,12 @@ final class XdsCredentialsRegistry {
|
|||
logger.log(Level.WARNING, "Unable to find TlsXdsCredentialsProvider", e);
|
||||
}
|
||||
|
||||
try {
|
||||
list.add(Class.forName("io.grpc.xds.internal.JwtTokenFileXdsCredentialsProvider"));
|
||||
} catch (ClassNotFoundException e) {
|
||||
logger.log(Level.WARNING, "Unable to find JwtTokenFileXdsCredentialsProvider", e);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
|
|
|
@ -57,22 +57,50 @@ public abstract class Bootstrapper {
|
|||
public abstract static class ServerInfo {
|
||||
public abstract String target();
|
||||
|
||||
public abstract Object implSpecificConfig();
|
||||
public abstract Object implSpecificChannelCredConfig();
|
||||
|
||||
@Nullable public abstract Object implSpecificCallCredConfig();
|
||||
|
||||
public abstract boolean ignoreResourceDeletion();
|
||||
|
||||
public abstract boolean isTrustedXdsServer();
|
||||
|
||||
@VisibleForTesting
|
||||
public static ServerInfo create(String target, @Nullable Object implSpecificConfig) {
|
||||
return new AutoValue_Bootstrapper_ServerInfo(target, implSpecificConfig, false, false);
|
||||
public static ServerInfo create(
|
||||
String target,
|
||||
@Nullable Object implSpecificChannelCredConfig) {
|
||||
return new AutoValue_Bootstrapper_ServerInfo(
|
||||
target,
|
||||
implSpecificChannelCredConfig,
|
||||
null,
|
||||
false,
|
||||
false);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static ServerInfo create(
|
||||
String target, Object implSpecificConfig, boolean ignoreResourceDeletion,
|
||||
String target,
|
||||
@Nullable Object implSpecificChannelCredConfig,
|
||||
@Nullable Object implSpecificCallCredConfig) {
|
||||
return new AutoValue_Bootstrapper_ServerInfo(
|
||||
target,
|
||||
implSpecificChannelCredConfig,
|
||||
implSpecificCallCredConfig,
|
||||
false,
|
||||
false);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static ServerInfo create(
|
||||
String target,
|
||||
Object implSpecificChannelCredConfig,
|
||||
@Nullable Object implSpecificCallCredConfig,
|
||||
boolean ignoreResourceDeletion,
|
||||
boolean isTrustedXdsServer) {
|
||||
return new AutoValue_Bootstrapper_ServerInfo(target, implSpecificConfig,
|
||||
return new AutoValue_Bootstrapper_ServerInfo(
|
||||
target,
|
||||
implSpecificChannelCredConfig,
|
||||
implSpecificCallCredConfig,
|
||||
ignoreResourceDeletion, isTrustedXdsServer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,9 +68,13 @@ public abstract class BootstrapperImpl extends Bootstrapper {
|
|||
|
||||
protected abstract String getJsonContent() throws IOException, XdsInitializationException;
|
||||
|
||||
protected abstract Object getImplSpecificConfig(Map<String, ?> serverConfig, String serverUri)
|
||||
protected abstract Object getImplSpecificChannelCredConfig(
|
||||
Map<String, ?> serverConfig, String serverUri)
|
||||
throws XdsInitializationException;
|
||||
|
||||
protected abstract Object getImplSpecificCallCredConfig(
|
||||
Map<String, ?> serverConfig, String serverUri)
|
||||
throws XdsInitializationException;
|
||||
|
||||
/**
|
||||
* Reads and parses bootstrap config. The config is expected to be in JSON format.
|
||||
|
@ -245,7 +249,9 @@ public abstract class BootstrapperImpl extends Bootstrapper {
|
|||
}
|
||||
logger.log(XdsLogLevel.INFO, "xDS server URI: {0}", serverUri);
|
||||
|
||||
Object implSpecificConfig = getImplSpecificConfig(serverConfig, serverUri);
|
||||
Object implSpecificChannelCredConfig =
|
||||
getImplSpecificChannelCredConfig(serverConfig, serverUri);
|
||||
Object implSpecificCallCredConfig = getImplSpecificCallCredConfig(serverConfig, serverUri);
|
||||
|
||||
boolean ignoreResourceDeletion = false;
|
||||
// "For forward compatibility reasons, the client will ignore any entry in the list that it
|
||||
|
@ -256,7 +262,11 @@ public abstract class BootstrapperImpl extends Bootstrapper {
|
|||
ignoreResourceDeletion = serverFeatures.contains(SERVER_FEATURE_IGNORE_RESOURCE_DELETION);
|
||||
}
|
||||
servers.add(
|
||||
ServerInfo.create(serverUri, implSpecificConfig, ignoreResourceDeletion,
|
||||
ServerInfo.create(
|
||||
serverUri,
|
||||
implSpecificChannelCredConfig,
|
||||
implSpecificCallCredConfig,
|
||||
ignoreResourceDeletion,
|
||||
serverFeatures != null
|
||||
&& serverFeatures.contains(SERVER_FEATURE_TRUSTED_XDS_SERVER)));
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package io.grpc.xds.internal;
|
||||
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.ChannelCredentials;
|
||||
import io.grpc.alts.GoogleDefaultChannelCredentials;
|
||||
import io.grpc.xds.XdsCredentialsProvider;
|
||||
|
@ -33,6 +34,11 @@ public final class GoogleDefaultXdsCredentialsProvider extends XdsCredentialsPro
|
|||
return GoogleDefaultChannelCredentials.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CallCredentials newCallCredentials(Map<String, ?> jsonConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getName() {
|
||||
return CREDS_NAME;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package io.grpc.xds.internal;
|
||||
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.ChannelCredentials;
|
||||
import io.grpc.InsecureChannelCredentials;
|
||||
import io.grpc.xds.XdsCredentialsProvider;
|
||||
|
@ -33,6 +34,11 @@ public final class InsecureXdsCredentialsProvider extends XdsCredentialsProvider
|
|||
return InsecureChannelCredentials.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CallCredentials newCallCredentials(Map<String, ?> jsonConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getName() {
|
||||
return CREDS_NAME;
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2025 The gRPC Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.grpc.xds.internal;
|
||||
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.ChannelCredentials;
|
||||
import io.grpc.alts.JwtTokenFileCallCredentials;
|
||||
import io.grpc.internal.JsonUtil;
|
||||
import io.grpc.xds.XdsCredentialsProvider;
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A wrapper class that supports {@link JwtTokenFileXdsCredentialsProvider} for
|
||||
* Xds by implementing {@link XdsCredentialsProvider}.
|
||||
*/
|
||||
public class JwtTokenFileXdsCredentialsProvider extends XdsCredentialsProvider {
|
||||
private static final String CREDS_NAME = "jwt_token_file";
|
||||
|
||||
@Override
|
||||
protected ChannelCredentials newChannelCredentials(Map<String, ?> jsonConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CallCredentials newCallCredentials(Map<String, ?> jsonConfig) {
|
||||
if (jsonConfig == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String jwtTokenPath = JsonUtil.getString(jsonConfig, getName());
|
||||
if (jwtTokenPath == null || !new File(jwtTokenPath).isFile()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return JwtTokenFileCallCredentials.create(jwtTokenPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getName() {
|
||||
return CREDS_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int priority() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package io.grpc.xds.internal;
|
||||
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.ChannelCredentials;
|
||||
import io.grpc.TlsChannelCredentials;
|
||||
import io.grpc.xds.XdsCredentialsProvider;
|
||||
|
@ -33,6 +34,11 @@ public final class TlsXdsCredentialsProvider extends XdsCredentialsProvider {
|
|||
return TlsChannelCredentials.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CallCredentials newCallCredentials(Map<String, ?> jsonConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getName() {
|
||||
return CREDS_NAME;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
io.grpc.xds.internal.GoogleDefaultXdsCredentialsProvider
|
||||
io.grpc.xds.internal.InsecureXdsCredentialsProvider
|
||||
io.grpc.xds.internal.JwtTokenFileXdsCredentialsProvider
|
||||
io.grpc.xds.internal.TlsXdsCredentialsProvider
|
|
@ -17,6 +17,9 @@
|
|||
package io.grpc.xds;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
@ -24,6 +27,7 @@ import static org.mockito.Mockito.verifyNoInteractions;
|
|||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import io.grpc.CompositeCallCredentials;
|
||||
import io.grpc.InsecureChannelCredentials;
|
||||
import io.grpc.TlsChannelCredentials;
|
||||
import io.grpc.internal.GrpcUtil;
|
||||
|
@ -37,13 +41,16 @@ import io.grpc.xds.client.CommonBootstrapperTestUtils;
|
|||
import io.grpc.xds.client.EnvoyProtoData.Node;
|
||||
import io.grpc.xds.client.Locality;
|
||||
import io.grpc.xds.client.XdsInitializationException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
|
@ -60,11 +67,16 @@ public class GrpcBootstrapperImplTest {
|
|||
private String originalBootstrapConfigFromEnvVar;
|
||||
private String originalBootstrapConfigFromSysProp;
|
||||
private boolean originalExperimentalXdsFallbackFlag;
|
||||
private boolean originalExperimentalXdsBootstrapCallCredsFlag;
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder tempFolder = new TemporaryFolder();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
saveEnvironment();
|
||||
originalExperimentalXdsFallbackFlag = CommonBootstrapperTestUtils.setEnableXdsFallback(true);
|
||||
GrpcBootstrapperImpl.xdsBootstrapCallCredsEnabled = true;
|
||||
bootstrapper.bootstrapPathFromEnvVar = BOOTSTRAP_FILE_PATH;
|
||||
}
|
||||
|
||||
|
@ -73,6 +85,8 @@ public class GrpcBootstrapperImplTest {
|
|||
originalBootstrapPathFromSysProp = bootstrapper.bootstrapPathFromSysProp;
|
||||
originalBootstrapConfigFromEnvVar = bootstrapper.bootstrapConfigFromEnvVar;
|
||||
originalBootstrapConfigFromSysProp = bootstrapper.bootstrapConfigFromSysProp;
|
||||
originalExperimentalXdsBootstrapCallCredsFlag =
|
||||
GrpcBootstrapperImpl.xdsBootstrapCallCredsEnabled;
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -82,6 +96,8 @@ public class GrpcBootstrapperImplTest {
|
|||
bootstrapper.bootstrapConfigFromEnvVar = originalBootstrapConfigFromEnvVar;
|
||||
bootstrapper.bootstrapConfigFromSysProp = originalBootstrapConfigFromSysProp;
|
||||
CommonBootstrapperTestUtils.setEnableXdsFallback(originalExperimentalXdsFallbackFlag);
|
||||
GrpcBootstrapperImpl.xdsBootstrapCallCredsEnabled =
|
||||
originalExperimentalXdsBootstrapCallCredsFlag;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -115,7 +131,9 @@ public class GrpcBootstrapperImplTest {
|
|||
assertThat(info.servers()).hasSize(1);
|
||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||
assertThat(serverInfo.target()).isEqualTo(SERVER_URI);
|
||||
assertThat(serverInfo.implSpecificConfig()).isInstanceOf(InsecureChannelCredentials.class);
|
||||
assertThat(serverInfo.implSpecificChannelCredConfig()).isInstanceOf(
|
||||
InsecureChannelCredentials.class);
|
||||
assertNull(serverInfo.implSpecificCallCredConfig());
|
||||
assertThat(info.node()).isEqualTo(
|
||||
getNodeBuilder()
|
||||
.setId("ENVOY_NODE_ID")
|
||||
|
@ -168,12 +186,14 @@ public class GrpcBootstrapperImplTest {
|
|||
List<ServerInfo> serverInfoList = info.servers();
|
||||
assertThat(serverInfoList.get(0).target())
|
||||
.isEqualTo("trafficdirector-foo.googleapis.com:443");
|
||||
assertThat(serverInfoList.get(0).implSpecificConfig())
|
||||
assertThat(serverInfoList.get(0).implSpecificChannelCredConfig())
|
||||
.isInstanceOf(TlsChannelCredentials.class);
|
||||
assertNull(serverInfoList.get(0).implSpecificCallCredConfig());
|
||||
assertThat(serverInfoList.get(1).target())
|
||||
.isEqualTo("trafficdirector-bar.googleapis.com:443");
|
||||
assertThat(serverInfoList.get(1).implSpecificConfig())
|
||||
assertThat(serverInfoList.get(1).implSpecificChannelCredConfig())
|
||||
.isInstanceOf(InsecureChannelCredentials.class);
|
||||
assertNull(serverInfoList.get(0).implSpecificCallCredConfig());
|
||||
assertThat(info.node()).isEqualTo(
|
||||
getNodeBuilder()
|
||||
.setId("ENVOY_NODE_ID")
|
||||
|
@ -217,7 +237,9 @@ public class GrpcBootstrapperImplTest {
|
|||
assertThat(info.servers()).hasSize(1);
|
||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||
assertThat(serverInfo.target()).isEqualTo(SERVER_URI);
|
||||
assertThat(serverInfo.implSpecificConfig()).isInstanceOf(InsecureChannelCredentials.class);
|
||||
assertThat(serverInfo.implSpecificChannelCredConfig()).isInstanceOf(
|
||||
InsecureChannelCredentials.class);
|
||||
assertNull(serverInfo.implSpecificCallCredConfig());
|
||||
assertThat(info.node()).isEqualTo(
|
||||
getNodeBuilder()
|
||||
.setId("ENVOY_NODE_ID")
|
||||
|
@ -288,7 +310,9 @@ public class GrpcBootstrapperImplTest {
|
|||
assertThat(info.servers()).hasSize(1);
|
||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||
assertThat(serverInfo.target()).isEqualTo(SERVER_URI);
|
||||
assertThat(serverInfo.implSpecificConfig()).isInstanceOf(InsecureChannelCredentials.class);
|
||||
assertThat(serverInfo.implSpecificChannelCredConfig()).isInstanceOf(
|
||||
InsecureChannelCredentials.class);
|
||||
assertNull(serverInfo.implSpecificCallCredConfig());
|
||||
assertThat(info.node()).isEqualTo(getNodeBuilder().build());
|
||||
}
|
||||
|
||||
|
@ -583,7 +607,9 @@ public class GrpcBootstrapperImplTest {
|
|||
BootstrapInfo info = bootstrapper.bootstrap();
|
||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||
assertThat(serverInfo.target()).isEqualTo(SERVER_URI);
|
||||
assertThat(serverInfo.implSpecificConfig()).isInstanceOf(InsecureChannelCredentials.class);
|
||||
assertThat(serverInfo.implSpecificChannelCredConfig()).isInstanceOf(
|
||||
InsecureChannelCredentials.class);
|
||||
assertNull(serverInfo.implSpecificCallCredConfig());
|
||||
assertThat(serverInfo.ignoreResourceDeletion()).isFalse();
|
||||
}
|
||||
|
||||
|
@ -605,7 +631,9 @@ public class GrpcBootstrapperImplTest {
|
|||
BootstrapInfo info = bootstrapper.bootstrap();
|
||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||
assertThat(serverInfo.target()).isEqualTo(SERVER_URI);
|
||||
assertThat(serverInfo.implSpecificConfig()).isInstanceOf(InsecureChannelCredentials.class);
|
||||
assertThat(serverInfo.implSpecificChannelCredConfig()).isInstanceOf(
|
||||
InsecureChannelCredentials.class);
|
||||
assertNull(serverInfo.implSpecificCallCredConfig());
|
||||
assertThat(serverInfo.ignoreResourceDeletion()).isFalse();
|
||||
}
|
||||
|
||||
|
@ -627,7 +655,9 @@ public class GrpcBootstrapperImplTest {
|
|||
BootstrapInfo info = bootstrapper.bootstrap();
|
||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||
assertThat(serverInfo.target()).isEqualTo(SERVER_URI);
|
||||
assertThat(serverInfo.implSpecificConfig()).isInstanceOf(InsecureChannelCredentials.class);
|
||||
assertThat(serverInfo.implSpecificChannelCredConfig()).isInstanceOf(
|
||||
InsecureChannelCredentials.class);
|
||||
assertNull(serverInfo.implSpecificCallCredConfig());
|
||||
// Only ignore_resource_deletion feature enabled: confirm it's on, and xds_v3 is off.
|
||||
assertThat(serverInfo.ignoreResourceDeletion()).isTrue();
|
||||
}
|
||||
|
@ -650,7 +680,9 @@ public class GrpcBootstrapperImplTest {
|
|||
BootstrapInfo info = bootstrapper.bootstrap();
|
||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||
assertThat(serverInfo.target()).isEqualTo(SERVER_URI);
|
||||
assertThat(serverInfo.implSpecificConfig()).isInstanceOf(InsecureChannelCredentials.class);
|
||||
assertThat(serverInfo.implSpecificChannelCredConfig()).isInstanceOf(
|
||||
InsecureChannelCredentials.class);
|
||||
assertNull(serverInfo.implSpecificCallCredConfig());
|
||||
assertThat(serverInfo.isTrustedXdsServer()).isTrue();
|
||||
}
|
||||
|
||||
|
@ -672,7 +704,9 @@ public class GrpcBootstrapperImplTest {
|
|||
BootstrapInfo info = bootstrapper.bootstrap();
|
||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||
assertThat(serverInfo.target()).isEqualTo(SERVER_URI);
|
||||
assertThat(serverInfo.implSpecificConfig()).isInstanceOf(InsecureChannelCredentials.class);
|
||||
assertThat(serverInfo.implSpecificChannelCredConfig()).isInstanceOf(
|
||||
InsecureChannelCredentials.class);
|
||||
assertNull(serverInfo.implSpecificCallCredConfig());
|
||||
// ignore_resource_deletion features enabled: confirm both are on.
|
||||
assertThat(serverInfo.ignoreResourceDeletion()).isTrue();
|
||||
}
|
||||
|
@ -898,6 +932,131 @@ public class GrpcBootstrapperImplTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseNotSupportedCallCredentials() throws Exception {
|
||||
String rawData = "{\n"
|
||||
+ " \"xds_servers\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"server_uri\": \"" + SERVER_URI + "\",\n"
|
||||
+ " \"channel_creds\": [\n"
|
||||
+ " {\"type\": \"insecure\"}\n"
|
||||
+ " ],\n"
|
||||
+ " \"call_creds\": [\n"
|
||||
+ " {\"type\": \"unknown\"}\n"
|
||||
+ " ]\n"
|
||||
+ " }\n"
|
||||
+ " ]\n"
|
||||
+ "}";
|
||||
bootstrapper.setFileReader(createFileReader(BOOTSTRAP_FILE_PATH, rawData));
|
||||
BootstrapInfo info = bootstrapper.bootstrap();
|
||||
assertThat(info.servers()).hasSize(1);
|
||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||
assertNull(serverInfo.implSpecificCallCredConfig());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseSupportedCallCredentialsWithInvalidConfig() throws Exception {
|
||||
String rawData = "{\n"
|
||||
+ " \"xds_servers\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"server_uri\": \"" + SERVER_URI + "\",\n"
|
||||
+ " \"channel_creds\": [\n"
|
||||
+ " {\"type\": \"insecure\"}\n"
|
||||
+ " ],\n"
|
||||
+ " \"call_creds\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"type\": \"jwt_token_file\",\n"
|
||||
+ " \"config\": {}\n"
|
||||
+ " }\n"
|
||||
+ " ]\n"
|
||||
+ " }\n"
|
||||
+ " ]\n"
|
||||
+ "}";
|
||||
bootstrapper.setFileReader(createFileReader(BOOTSTRAP_FILE_PATH, rawData));
|
||||
Exception ex = assertThrows(XdsInitializationException.class, () -> {
|
||||
bootstrapper.bootstrap();
|
||||
});
|
||||
|
||||
String expectedMsg = "Invalid bootstrap: server "
|
||||
+ SERVER_URI + " with invalid 'config' for jwt_token_file 'call_creds'";
|
||||
String actualMsg = ex.getMessage();
|
||||
|
||||
assertEquals(expectedMsg, actualMsg);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseSupportedCallCredentialsWithJwtFileMissingConfig() throws Exception {
|
||||
String rawData = "{\n"
|
||||
+ " \"xds_servers\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"server_uri\": \"" + SERVER_URI + "\",\n"
|
||||
+ " \"channel_creds\": [\n"
|
||||
+ " {\"type\": \"insecure\"}\n"
|
||||
+ " ],\n"
|
||||
+ " \"call_creds\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"type\": \"jwt_token_file\",\n"
|
||||
+ " \"config\": {\n"
|
||||
+ " \"jwt_token_file\": \"/path/to/file\"\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ " ]\n"
|
||||
+ " }\n"
|
||||
+ " ]\n"
|
||||
+ "}";
|
||||
bootstrapper.setFileReader(createFileReader(BOOTSTRAP_FILE_PATH, rawData));
|
||||
Exception ex = assertThrows(XdsInitializationException.class, () -> {
|
||||
bootstrapper.bootstrap();
|
||||
});
|
||||
|
||||
String expectedMsg = "Invalid bootstrap: server "
|
||||
+ SERVER_URI + " with invalid 'config' for jwt_token_file 'call_creds'";
|
||||
String actualMsg = ex.getMessage();
|
||||
|
||||
assertEquals(expectedMsg, actualMsg);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseTwoSupportedCallCredentialsWithValidConfig() throws Exception {
|
||||
File jwtToken_1 = tempFolder.newFile(new String("jwt-token-1.txt"));
|
||||
File jwtToken_2 = tempFolder.newFile(new String("jwt-token-2.txt"));
|
||||
|
||||
String rawData = "{\n"
|
||||
+ " \"xds_servers\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"server_uri\": \"" + SERVER_URI + "\",\n"
|
||||
+ " \"channel_creds\": [\n"
|
||||
+ " {\"type\": \"insecure\"}\n"
|
||||
+ " ],\n"
|
||||
+ " \"call_creds\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"type\": \"jwt_token_file\",\n"
|
||||
+ " \"config\": {\n"
|
||||
+ " \"jwt_token_file\": \"" + jwtToken_1.getAbsolutePath() + "\"\n"
|
||||
+ " }\n"
|
||||
+ " },\n"
|
||||
+ " {\n"
|
||||
+ " \"type\": \"jwt_token_file\",\n"
|
||||
+ " \"config\": {\n"
|
||||
+ " \"jwt_token_file\": \"" + jwtToken_2.getAbsolutePath() + "\"\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ " ]\n"
|
||||
+ " }\n"
|
||||
+ " ]\n"
|
||||
+ "}";
|
||||
|
||||
bootstrapper.setFileReader(createFileReader(BOOTSTRAP_FILE_PATH, rawData));
|
||||
BootstrapInfo info = bootstrapper.bootstrap();
|
||||
assertThat(info.servers()).hasSize(1);
|
||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||
assertSame(CompositeCallCredentials.class,
|
||||
serverInfo.implSpecificCallCredConfig().getClass());
|
||||
|
||||
jwtToken_1.delete();
|
||||
jwtToken_2.delete();
|
||||
}
|
||||
|
||||
private static BootstrapperImpl.FileReader createFileReader(
|
||||
final String expectedPath, final String rawData) {
|
||||
return new BootstrapperImpl.FileReader() {
|
||||
|
|
|
@ -3549,7 +3549,11 @@ public class GrpcXdsClientImplDataTest {
|
|||
|
||||
private XdsResourceType.Args getXdsResourceTypeArgs(boolean isTrustedServer) {
|
||||
return new XdsResourceType.Args(
|
||||
ServerInfo.create("http://td", "", false, isTrustedServer), "1.0", null, null, null, null
|
||||
);
|
||||
ServerInfo.create("http://td", "", "", false, isTrustedServer),
|
||||
"1.0",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -346,7 +346,11 @@ public abstract class GrpcXdsClientImplTestBase {
|
|||
}
|
||||
};
|
||||
|
||||
xdsServerInfo = ServerInfo.create(SERVER_URI, CHANNEL_CREDENTIALS, ignoreResourceDeletion(),
|
||||
xdsServerInfo = ServerInfo.create(
|
||||
SERVER_URI,
|
||||
CHANNEL_CREDENTIALS,
|
||||
null,
|
||||
ignoreResourceDeletion(),
|
||||
true);
|
||||
BootstrapInfo bootstrapInfo =
|
||||
Bootstrapper.BootstrapInfo.builder()
|
||||
|
@ -4219,8 +4223,12 @@ public abstract class GrpcXdsClientImplTestBase {
|
|||
|
||||
private BootstrapInfo buildBootStrap(String serverUri) {
|
||||
|
||||
ServerInfo xdsServerInfo = ServerInfo.create(serverUri, CHANNEL_CREDENTIALS,
|
||||
ignoreResourceDeletion(), true);
|
||||
ServerInfo xdsServerInfo = ServerInfo.create(
|
||||
serverUri,
|
||||
CHANNEL_CREDENTIALS,
|
||||
null,
|
||||
ignoreResourceDeletion(),
|
||||
true);
|
||||
|
||||
return Bootstrapper.BootstrapInfo.builder()
|
||||
.servers(Collections.singletonList(xdsServerInfo))
|
||||
|
|
|
@ -347,7 +347,7 @@ public class XdsClientFallbackTest {
|
|||
@Override
|
||||
public XdsTransport create(Bootstrapper.ServerInfo serverInfo) {
|
||||
ChannelCredentials channelCredentials =
|
||||
(ChannelCredentials) serverInfo.implSpecificConfig();
|
||||
(ChannelCredentials) serverInfo.implSpecificChannelCredConfig();
|
||||
return new GrpcXdsTransportFactory.GrpcXdsTransport(
|
||||
Grpc.newChannelBuilder(serverInfo.target(), channelCredentials)
|
||||
.executor(executor)
|
||||
|
|
|
@ -22,11 +22,13 @@ import static org.junit.Assert.assertSame;
|
|||
import static org.junit.Assert.fail;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.ChannelCredentials;
|
||||
import io.grpc.xds.XdsCredentialsProvider;
|
||||
import io.grpc.xds.XdsCredentialsRegistry;
|
||||
import io.grpc.xds.internal.GoogleDefaultXdsCredentialsProvider;
|
||||
import io.grpc.xds.internal.InsecureXdsCredentialsProvider;
|
||||
import io.grpc.xds.internal.JwtTokenFileXdsCredentialsProvider;
|
||||
import io.grpc.xds.internal.TlsXdsCredentialsProvider;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -136,11 +138,13 @@ public class XdsCredentialsRegistryTest {
|
|||
public void defaultRegistry_providers() {
|
||||
Map<String, XdsCredentialsProvider> providers =
|
||||
XdsCredentialsRegistry.getDefaultRegistry().providers();
|
||||
assertThat(providers).hasSize(3);
|
||||
assertThat(providers).hasSize(4);
|
||||
assertThat(providers.get("google_default").getClass())
|
||||
.isEqualTo(GoogleDefaultXdsCredentialsProvider.class);
|
||||
assertThat(providers.get("insecure").getClass())
|
||||
.isEqualTo(InsecureXdsCredentialsProvider.class);
|
||||
assertThat(providers.get("jwt_token_file").getClass())
|
||||
.isEqualTo(JwtTokenFileXdsCredentialsProvider.class);
|
||||
assertThat(providers.get("tls").getClass())
|
||||
.isEqualTo(TlsXdsCredentialsProvider.class);
|
||||
}
|
||||
|
@ -151,6 +155,7 @@ public class XdsCredentialsRegistryTest {
|
|||
assertThat(classes).containsExactly(
|
||||
GoogleDefaultXdsCredentialsProvider.class,
|
||||
InsecureXdsCredentialsProvider.class,
|
||||
JwtTokenFileXdsCredentialsProvider.class,
|
||||
TlsXdsCredentialsProvider.class);
|
||||
}
|
||||
|
||||
|
@ -195,6 +200,11 @@ public class XdsCredentialsRegistryTest {
|
|||
public ChannelCredentials newChannelCredentials(Map<String, ?> config) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallCredentials newCallCredentials(Map<String, ?> config) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private static class SampleChannelCredentials extends ChannelCredentials {
|
||||
|
|
|
@ -368,13 +368,13 @@ public class XdsNameResolverTest {
|
|||
String serviceAuthority = "[::FFFF:129.144.52.38]:80";
|
||||
bootstrapInfo = BootstrapInfo.builder()
|
||||
.servers(ImmutableList.of(ServerInfo.create(
|
||||
"td.googleapis.com", InsecureChannelCredentials.create(), true, true)))
|
||||
"td.googleapis.com", InsecureChannelCredentials.create(), null, true, true)))
|
||||
.node(Node.newBuilder().build())
|
||||
.authorities(
|
||||
ImmutableMap.of(targetAuthority, AuthorityInfo.create(
|
||||
"xdstp://" + targetAuthority + "/envoy.config.listener.v3.Listener/%s?foo=1&bar=2",
|
||||
ImmutableList.of(ServerInfo.create(
|
||||
"td.googleapis.com", InsecureChannelCredentials.create(), true, true)))))
|
||||
"td.googleapis.com", InsecureChannelCredentials.create(), null, true, true)))))
|
||||
.build();
|
||||
expectedLdsResourceName = "xdstp://xds.authority.com/envoy.config.listener.v3.Listener/"
|
||||
+ "%5B::FFFF:129.144.52.38%5D:80?bar=2&foo=1"; // query param canonified
|
||||
|
|
|
@ -203,7 +203,7 @@ public class CommonBootstrapperTestUtils {
|
|||
|
||||
List<ServerInfo> serverInfos = new ArrayList<>();
|
||||
for (String uri : serverUris) {
|
||||
serverInfos.add(ServerInfo.create(uri, CHANNEL_CREDENTIALS, false, true));
|
||||
serverInfos.add(ServerInfo.create(uri, CHANNEL_CREDENTIALS, null, false, true));
|
||||
}
|
||||
EnvoyProtoData.Node node = EnvoyProtoData.Node.newBuilder().setId("node-id").build();
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package io.grpc.xds.internal;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
@ -54,4 +55,9 @@ public class GoogleDefaultXdsCredentialsProviderTest {
|
|||
assertSame(CompositeChannelCredentials.class,
|
||||
provider.newChannelCredentials(null).getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callCredentials() {
|
||||
assertNull(provider.newCallCredentials(null));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package io.grpc.xds.internal;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
@ -54,4 +55,9 @@ public class InsecureXdsCredentialsProviderTest {
|
|||
assertSame(InsecureChannelCredentials.class,
|
||||
provider.newChannelCredentials(null).getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callCredentials() {
|
||||
assertNull(provider.newCallCredentials(null));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright 2025 The gRPC Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.grpc.xds.internal;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.grpc.InternalServiceProviders;
|
||||
import io.grpc.xds.XdsCredentialsProvider;
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
|
||||
/** Unit tests for {@link JwtTokenFileXdsCredentialsProvider}. */
|
||||
@RunWith(JUnit4.class)
|
||||
public class JwtTokenFileXdsCredentialsProviderTest {
|
||||
private JwtTokenFileXdsCredentialsProvider provider = new JwtTokenFileXdsCredentialsProvider();
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder tempFolder = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void provided() {
|
||||
for (XdsCredentialsProvider current
|
||||
: InternalServiceProviders.getCandidatesViaServiceLoader(
|
||||
XdsCredentialsProvider.class, getClass().getClassLoader())) {
|
||||
if (current instanceof JwtTokenFileXdsCredentialsProvider) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail("ServiceLoader unable to load JwtTokenFileXdsCredentialsProvider");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable() {
|
||||
assertTrue(provider.isAvailable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void channelCredentials() {
|
||||
assertNull(provider.newChannelCredentials(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callCredentialsWhenNullConfig() {
|
||||
assertNull(provider.newCallCredentials(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callCredentialsWhenWrongConfig() {
|
||||
Map<String, ?> jsonConfig = ImmutableMap.of("jwt_token_file", "/tmp/not-exisiting-file.txt");
|
||||
assertNull(provider.newCallCredentials(jsonConfig));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callCredentialsWhenExpectedConfig() throws Exception {
|
||||
File createdFile = tempFolder.newFile(new String("existing-file.txt"));
|
||||
Map<String, ?> jsonConfig = ImmutableMap.of("jwt_token_file", createdFile.toString());
|
||||
assertEquals("io.grpc.auth.GoogleAuthLibraryCallCredentials",
|
||||
provider.newCallCredentials(jsonConfig).getClass().getName());
|
||||
createdFile.delete();
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package io.grpc.xds.internal;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
@ -54,4 +55,9 @@ public class TlsXdsCredentialsProviderTest {
|
|||
assertSame(TlsChannelCredentials.class,
|
||||
provider.newChannelCredentials(null).getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callCredentials() {
|
||||
assertNull(provider.newCallCredentials(null));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue