mirror of https://github.com/grpc/grpc-java.git
JWT token file call creds
This commit is contained in:
parent
6ff8ecac09
commit
a8f76b0b7b
|
@ -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.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import io.grpc.CallCredentials;
|
||||||
import io.grpc.ChannelCredentials;
|
import io.grpc.ChannelCredentials;
|
||||||
|
import io.grpc.CompositeCallCredentials;
|
||||||
|
import io.grpc.internal.GrpcUtil;
|
||||||
import io.grpc.internal.JsonUtil;
|
import io.grpc.internal.JsonUtil;
|
||||||
import io.grpc.xds.client.BootstrapperImpl;
|
import io.grpc.xds.client.BootstrapperImpl;
|
||||||
import io.grpc.xds.client.XdsInitializationException;
|
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_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_ENV_VAR = "GRPC_XDS_BOOTSTRAP_CONFIG";
|
||||||
private static final String BOOTSTRAP_CONFIG_SYS_PROPERTY = "io.grpc.xds.bootstrapConfig";
|
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
|
@VisibleForTesting
|
||||||
String bootstrapPathFromEnvVar = System.getenv(BOOTSTRAP_PATH_SYS_ENV_VAR);
|
String bootstrapPathFromEnvVar = System.getenv(BOOTSTRAP_PATH_SYS_ENV_VAR);
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
@ -41,6 +46,9 @@ class GrpcBootstrapperImpl extends BootstrapperImpl {
|
||||||
String bootstrapConfigFromEnvVar = System.getenv(BOOTSTRAP_CONFIG_SYS_ENV_VAR);
|
String bootstrapConfigFromEnvVar = System.getenv(BOOTSTRAP_CONFIG_SYS_ENV_VAR);
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
String bootstrapConfigFromSysProp = System.getProperty(BOOTSTRAP_CONFIG_SYS_PROPERTY);
|
String bootstrapConfigFromSysProp = System.getProperty(BOOTSTRAP_CONFIG_SYS_PROPERTY);
|
||||||
|
@VisibleForTesting
|
||||||
|
static boolean xdsBootstrapCallCredsEnabled = GrpcUtil.getFlag(
|
||||||
|
GRPC_EXPERIMENTAL_XDS_BOOTSTRAP_CALL_CREDS, false);
|
||||||
|
|
||||||
GrpcBootstrapperImpl() {
|
GrpcBootstrapperImpl() {
|
||||||
super();
|
super();
|
||||||
|
@ -90,7 +98,7 @@ class GrpcBootstrapperImpl extends BootstrapperImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object getImplSpecificConfig(Map<String, ?> serverConfig, String serverUri)
|
protected Object getImplSpecificChannelCredConfig(Map<String, ?> serverConfig, String serverUri)
|
||||||
throws XdsInitializationException {
|
throws XdsInitializationException {
|
||||||
return getChannelCredentials(serverConfig, serverUri);
|
return getChannelCredentials(serverConfig, serverUri);
|
||||||
}
|
}
|
||||||
|
@ -135,4 +143,58 @@ class GrpcBootstrapperImpl extends BootstrapperImpl {
|
||||||
}
|
}
|
||||||
return null;
|
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.CallOptions;
|
||||||
import io.grpc.ChannelCredentials;
|
import io.grpc.ChannelCredentials;
|
||||||
import io.grpc.ClientCall;
|
import io.grpc.ClientCall;
|
||||||
|
import io.grpc.CompositeCallCredentials;
|
||||||
|
import io.grpc.CompositeChannelCredentials;
|
||||||
import io.grpc.Context;
|
import io.grpc.Context;
|
||||||
import io.grpc.Grpc;
|
import io.grpc.Grpc;
|
||||||
import io.grpc.ManagedChannel;
|
import io.grpc.ManagedChannel;
|
||||||
|
@ -68,11 +70,26 @@ final class GrpcXdsTransportFactory implements XdsTransportFactory {
|
||||||
|
|
||||||
public GrpcXdsTransport(Bootstrapper.ServerInfo serverInfo, CallCredentials callCredentials) {
|
public GrpcXdsTransport(Bootstrapper.ServerInfo serverInfo, CallCredentials callCredentials) {
|
||||||
String target = serverInfo.target();
|
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)
|
this.channel = Grpc.newChannelBuilder(target, channelCredentials)
|
||||||
.keepAliveTime(5, TimeUnit.MINUTES)
|
.keepAliveTime(5, TimeUnit.MINUTES)
|
||||||
.build();
|
.build();
|
||||||
this.callCredentials = callCredentials;
|
|
||||||
|
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
|
@VisibleForTesting
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package io.grpc.xds;
|
package io.grpc.xds;
|
||||||
|
|
||||||
|
import io.grpc.CallCredentials;
|
||||||
import io.grpc.ChannelCredentials;
|
import io.grpc.ChannelCredentials;
|
||||||
import io.grpc.Internal;
|
import io.grpc.Internal;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -49,6 +50,17 @@ public abstract class XdsCredentialsProvider {
|
||||||
*/
|
*/
|
||||||
protected abstract ChannelCredentials newChannelCredentials(Map<String, ?> jsonConfig);
|
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
|
* 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.
|
* via {@link XdsCredentialsRegistry#getProvider}. This is called only when the class is loaded.
|
||||||
|
|
|
@ -114,7 +114,7 @@ final class XdsCredentialsRegistry {
|
||||||
new XdsCredentialsProviderPriorityAccessor());
|
new XdsCredentialsProviderPriorityAccessor());
|
||||||
if (providerList.isEmpty()) {
|
if (providerList.isEmpty()) {
|
||||||
logger.warning("No XdsCredsRegistry found via ServiceLoader, including for GoogleDefault, "
|
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();
|
instance = new XdsCredentialsRegistry();
|
||||||
for (XdsCredentialsProvider provider : providerList) {
|
for (XdsCredentialsProvider provider : providerList) {
|
||||||
|
@ -170,7 +170,13 @@ final class XdsCredentialsRegistry {
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
logger.log(Level.WARNING, "Unable to find TlsXdsCredentialsProvider", e);
|
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);
|
return Collections.unmodifiableList(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,22 +57,50 @@ public abstract class Bootstrapper {
|
||||||
public abstract static class ServerInfo {
|
public abstract static class ServerInfo {
|
||||||
public abstract String target();
|
public abstract String target();
|
||||||
|
|
||||||
public abstract Object implSpecificConfig();
|
public abstract Object implSpecificChannelCredConfig();
|
||||||
|
|
||||||
|
@Nullable public abstract Object implSpecificCallCredConfig();
|
||||||
|
|
||||||
public abstract boolean ignoreResourceDeletion();
|
public abstract boolean ignoreResourceDeletion();
|
||||||
|
|
||||||
public abstract boolean isTrustedXdsServer();
|
public abstract boolean isTrustedXdsServer();
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static ServerInfo create(String target, @Nullable Object implSpecificConfig) {
|
public static ServerInfo create(
|
||||||
return new AutoValue_Bootstrapper_ServerInfo(target, implSpecificConfig, false, false);
|
String target,
|
||||||
|
@Nullable Object implSpecificChannelCredConfig) {
|
||||||
|
return new AutoValue_Bootstrapper_ServerInfo(
|
||||||
|
target,
|
||||||
|
implSpecificChannelCredConfig,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public static ServerInfo create(
|
||||||
|
String target,
|
||||||
|
@Nullable Object implSpecificChannelCredConfig,
|
||||||
|
@Nullable Object implSpecificCallCredConfig) {
|
||||||
|
return new AutoValue_Bootstrapper_ServerInfo(
|
||||||
|
target,
|
||||||
|
implSpecificChannelCredConfig,
|
||||||
|
implSpecificCallCredConfig,
|
||||||
|
false,
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static ServerInfo create(
|
public static ServerInfo create(
|
||||||
String target, Object implSpecificConfig, boolean ignoreResourceDeletion,
|
String target,
|
||||||
|
Object implSpecificChannelCredConfig,
|
||||||
|
@Nullable Object implSpecificCallCredConfig,
|
||||||
|
boolean ignoreResourceDeletion,
|
||||||
boolean isTrustedXdsServer) {
|
boolean isTrustedXdsServer) {
|
||||||
return new AutoValue_Bootstrapper_ServerInfo(target, implSpecificConfig,
|
return new AutoValue_Bootstrapper_ServerInfo(
|
||||||
|
target,
|
||||||
|
implSpecificChannelCredConfig,
|
||||||
|
implSpecificCallCredConfig,
|
||||||
ignoreResourceDeletion, isTrustedXdsServer);
|
ignoreResourceDeletion, isTrustedXdsServer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,9 +68,13 @@ public abstract class BootstrapperImpl extends Bootstrapper {
|
||||||
|
|
||||||
protected abstract String getJsonContent() throws IOException, XdsInitializationException;
|
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;
|
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.
|
* 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);
|
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;
|
boolean ignoreResourceDeletion = false;
|
||||||
// "For forward compatibility reasons, the client will ignore any entry in the list that it
|
// "For forward compatibility reasons, the client will ignore any entry in the list that it
|
||||||
|
@ -256,9 +262,13 @@ public abstract class BootstrapperImpl extends Bootstrapper {
|
||||||
ignoreResourceDeletion = serverFeatures.contains(SERVER_FEATURE_IGNORE_RESOURCE_DELETION);
|
ignoreResourceDeletion = serverFeatures.contains(SERVER_FEATURE_IGNORE_RESOURCE_DELETION);
|
||||||
}
|
}
|
||||||
servers.add(
|
servers.add(
|
||||||
ServerInfo.create(serverUri, implSpecificConfig, ignoreResourceDeletion,
|
ServerInfo.create(
|
||||||
serverFeatures != null
|
serverUri,
|
||||||
&& serverFeatures.contains(SERVER_FEATURE_TRUSTED_XDS_SERVER)));
|
implSpecificChannelCredConfig,
|
||||||
|
implSpecificCallCredConfig,
|
||||||
|
ignoreResourceDeletion,
|
||||||
|
serverFeatures != null
|
||||||
|
&& serverFeatures.contains(SERVER_FEATURE_TRUSTED_XDS_SERVER)));
|
||||||
}
|
}
|
||||||
return servers.build();
|
return servers.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package io.grpc.xds.internal;
|
package io.grpc.xds.internal;
|
||||||
|
|
||||||
|
import io.grpc.CallCredentials;
|
||||||
import io.grpc.ChannelCredentials;
|
import io.grpc.ChannelCredentials;
|
||||||
import io.grpc.alts.GoogleDefaultChannelCredentials;
|
import io.grpc.alts.GoogleDefaultChannelCredentials;
|
||||||
import io.grpc.xds.XdsCredentialsProvider;
|
import io.grpc.xds.XdsCredentialsProvider;
|
||||||
|
@ -33,6 +34,11 @@ public final class GoogleDefaultXdsCredentialsProvider extends XdsCredentialsPro
|
||||||
return GoogleDefaultChannelCredentials.create();
|
return GoogleDefaultChannelCredentials.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CallCredentials newCallCredentials(Map<String, ?> jsonConfig) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getName() {
|
protected String getName() {
|
||||||
return CREDS_NAME;
|
return CREDS_NAME;
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package io.grpc.xds.internal;
|
package io.grpc.xds.internal;
|
||||||
|
|
||||||
|
import io.grpc.CallCredentials;
|
||||||
import io.grpc.ChannelCredentials;
|
import io.grpc.ChannelCredentials;
|
||||||
import io.grpc.InsecureChannelCredentials;
|
import io.grpc.InsecureChannelCredentials;
|
||||||
import io.grpc.xds.XdsCredentialsProvider;
|
import io.grpc.xds.XdsCredentialsProvider;
|
||||||
|
@ -33,6 +34,11 @@ public final class InsecureXdsCredentialsProvider extends XdsCredentialsProvider
|
||||||
return InsecureChannelCredentials.create();
|
return InsecureChannelCredentials.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CallCredentials newCallCredentials(Map<String, ?> jsonConfig) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getName() {
|
protected String getName() {
|
||||||
return CREDS_NAME;
|
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;
|
package io.grpc.xds.internal;
|
||||||
|
|
||||||
|
import io.grpc.CallCredentials;
|
||||||
import io.grpc.ChannelCredentials;
|
import io.grpc.ChannelCredentials;
|
||||||
import io.grpc.TlsChannelCredentials;
|
import io.grpc.TlsChannelCredentials;
|
||||||
import io.grpc.xds.XdsCredentialsProvider;
|
import io.grpc.xds.XdsCredentialsProvider;
|
||||||
|
@ -33,6 +34,11 @@ public final class TlsXdsCredentialsProvider extends XdsCredentialsProvider {
|
||||||
return TlsChannelCredentials.create();
|
return TlsChannelCredentials.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CallCredentials newCallCredentials(Map<String, ?> jsonConfig) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getName() {
|
protected String getName() {
|
||||||
return CREDS_NAME;
|
return CREDS_NAME;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
io.grpc.xds.internal.GoogleDefaultXdsCredentialsProvider
|
io.grpc.xds.internal.GoogleDefaultXdsCredentialsProvider
|
||||||
io.grpc.xds.internal.InsecureXdsCredentialsProvider
|
io.grpc.xds.internal.InsecureXdsCredentialsProvider
|
||||||
|
io.grpc.xds.internal.JwtTokenFileXdsCredentialsProvider
|
||||||
io.grpc.xds.internal.TlsXdsCredentialsProvider
|
io.grpc.xds.internal.TlsXdsCredentialsProvider
|
|
@ -17,6 +17,9 @@
|
||||||
package io.grpc.xds;
|
package io.grpc.xds;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
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.assertThrows;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
import static org.mockito.Mockito.mock;
|
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.ImmutableMap;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
import io.grpc.CompositeCallCredentials;
|
||||||
import io.grpc.InsecureChannelCredentials;
|
import io.grpc.InsecureChannelCredentials;
|
||||||
import io.grpc.TlsChannelCredentials;
|
import io.grpc.TlsChannelCredentials;
|
||||||
import io.grpc.internal.GrpcUtil;
|
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.EnvoyProtoData.Node;
|
||||||
import io.grpc.xds.client.Locality;
|
import io.grpc.xds.client.Locality;
|
||||||
import io.grpc.xds.client.XdsInitializationException;
|
import io.grpc.xds.client.XdsInitializationException;
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
@ -60,11 +67,16 @@ public class GrpcBootstrapperImplTest {
|
||||||
private String originalBootstrapConfigFromEnvVar;
|
private String originalBootstrapConfigFromEnvVar;
|
||||||
private String originalBootstrapConfigFromSysProp;
|
private String originalBootstrapConfigFromSysProp;
|
||||||
private boolean originalExperimentalXdsFallbackFlag;
|
private boolean originalExperimentalXdsFallbackFlag;
|
||||||
|
private boolean originalExperimentalXdsBootstrapCallCredsFlag;
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public TemporaryFolder tempFolder = new TemporaryFolder();
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
saveEnvironment();
|
saveEnvironment();
|
||||||
originalExperimentalXdsFallbackFlag = CommonBootstrapperTestUtils.setEnableXdsFallback(true);
|
originalExperimentalXdsFallbackFlag = CommonBootstrapperTestUtils.setEnableXdsFallback(true);
|
||||||
|
GrpcBootstrapperImpl.xdsBootstrapCallCredsEnabled = true;
|
||||||
bootstrapper.bootstrapPathFromEnvVar = BOOTSTRAP_FILE_PATH;
|
bootstrapper.bootstrapPathFromEnvVar = BOOTSTRAP_FILE_PATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +85,8 @@ public class GrpcBootstrapperImplTest {
|
||||||
originalBootstrapPathFromSysProp = bootstrapper.bootstrapPathFromSysProp;
|
originalBootstrapPathFromSysProp = bootstrapper.bootstrapPathFromSysProp;
|
||||||
originalBootstrapConfigFromEnvVar = bootstrapper.bootstrapConfigFromEnvVar;
|
originalBootstrapConfigFromEnvVar = bootstrapper.bootstrapConfigFromEnvVar;
|
||||||
originalBootstrapConfigFromSysProp = bootstrapper.bootstrapConfigFromSysProp;
|
originalBootstrapConfigFromSysProp = bootstrapper.bootstrapConfigFromSysProp;
|
||||||
|
originalExperimentalXdsBootstrapCallCredsFlag =
|
||||||
|
GrpcBootstrapperImpl.xdsBootstrapCallCredsEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -82,6 +96,8 @@ public class GrpcBootstrapperImplTest {
|
||||||
bootstrapper.bootstrapConfigFromEnvVar = originalBootstrapConfigFromEnvVar;
|
bootstrapper.bootstrapConfigFromEnvVar = originalBootstrapConfigFromEnvVar;
|
||||||
bootstrapper.bootstrapConfigFromSysProp = originalBootstrapConfigFromSysProp;
|
bootstrapper.bootstrapConfigFromSysProp = originalBootstrapConfigFromSysProp;
|
||||||
CommonBootstrapperTestUtils.setEnableXdsFallback(originalExperimentalXdsFallbackFlag);
|
CommonBootstrapperTestUtils.setEnableXdsFallback(originalExperimentalXdsFallbackFlag);
|
||||||
|
GrpcBootstrapperImpl.xdsBootstrapCallCredsEnabled =
|
||||||
|
originalExperimentalXdsBootstrapCallCredsFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -115,7 +131,9 @@ public class GrpcBootstrapperImplTest {
|
||||||
assertThat(info.servers()).hasSize(1);
|
assertThat(info.servers()).hasSize(1);
|
||||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||||
assertThat(serverInfo.target()).isEqualTo(SERVER_URI);
|
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(
|
assertThat(info.node()).isEqualTo(
|
||||||
getNodeBuilder()
|
getNodeBuilder()
|
||||||
.setId("ENVOY_NODE_ID")
|
.setId("ENVOY_NODE_ID")
|
||||||
|
@ -168,12 +186,14 @@ public class GrpcBootstrapperImplTest {
|
||||||
List<ServerInfo> serverInfoList = info.servers();
|
List<ServerInfo> serverInfoList = info.servers();
|
||||||
assertThat(serverInfoList.get(0).target())
|
assertThat(serverInfoList.get(0).target())
|
||||||
.isEqualTo("trafficdirector-foo.googleapis.com:443");
|
.isEqualTo("trafficdirector-foo.googleapis.com:443");
|
||||||
assertThat(serverInfoList.get(0).implSpecificConfig())
|
assertThat(serverInfoList.get(0).implSpecificChannelCredConfig())
|
||||||
.isInstanceOf(TlsChannelCredentials.class);
|
.isInstanceOf(TlsChannelCredentials.class);
|
||||||
|
assertNull(serverInfoList.get(0).implSpecificCallCredConfig());
|
||||||
assertThat(serverInfoList.get(1).target())
|
assertThat(serverInfoList.get(1).target())
|
||||||
.isEqualTo("trafficdirector-bar.googleapis.com:443");
|
.isEqualTo("trafficdirector-bar.googleapis.com:443");
|
||||||
assertThat(serverInfoList.get(1).implSpecificConfig())
|
assertThat(serverInfoList.get(1).implSpecificChannelCredConfig())
|
||||||
.isInstanceOf(InsecureChannelCredentials.class);
|
.isInstanceOf(InsecureChannelCredentials.class);
|
||||||
|
assertNull(serverInfoList.get(0).implSpecificCallCredConfig());
|
||||||
assertThat(info.node()).isEqualTo(
|
assertThat(info.node()).isEqualTo(
|
||||||
getNodeBuilder()
|
getNodeBuilder()
|
||||||
.setId("ENVOY_NODE_ID")
|
.setId("ENVOY_NODE_ID")
|
||||||
|
@ -217,7 +237,9 @@ public class GrpcBootstrapperImplTest {
|
||||||
assertThat(info.servers()).hasSize(1);
|
assertThat(info.servers()).hasSize(1);
|
||||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||||
assertThat(serverInfo.target()).isEqualTo(SERVER_URI);
|
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(
|
assertThat(info.node()).isEqualTo(
|
||||||
getNodeBuilder()
|
getNodeBuilder()
|
||||||
.setId("ENVOY_NODE_ID")
|
.setId("ENVOY_NODE_ID")
|
||||||
|
@ -288,7 +310,9 @@ public class GrpcBootstrapperImplTest {
|
||||||
assertThat(info.servers()).hasSize(1);
|
assertThat(info.servers()).hasSize(1);
|
||||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||||
assertThat(serverInfo.target()).isEqualTo(SERVER_URI);
|
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());
|
assertThat(info.node()).isEqualTo(getNodeBuilder().build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -583,7 +607,9 @@ public class GrpcBootstrapperImplTest {
|
||||||
BootstrapInfo info = bootstrapper.bootstrap();
|
BootstrapInfo info = bootstrapper.bootstrap();
|
||||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||||
assertThat(serverInfo.target()).isEqualTo(SERVER_URI);
|
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();
|
assertThat(serverInfo.ignoreResourceDeletion()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,7 +631,9 @@ public class GrpcBootstrapperImplTest {
|
||||||
BootstrapInfo info = bootstrapper.bootstrap();
|
BootstrapInfo info = bootstrapper.bootstrap();
|
||||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||||
assertThat(serverInfo.target()).isEqualTo(SERVER_URI);
|
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();
|
assertThat(serverInfo.ignoreResourceDeletion()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,7 +655,9 @@ public class GrpcBootstrapperImplTest {
|
||||||
BootstrapInfo info = bootstrapper.bootstrap();
|
BootstrapInfo info = bootstrapper.bootstrap();
|
||||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||||
assertThat(serverInfo.target()).isEqualTo(SERVER_URI);
|
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.
|
// Only ignore_resource_deletion feature enabled: confirm it's on, and xds_v3 is off.
|
||||||
assertThat(serverInfo.ignoreResourceDeletion()).isTrue();
|
assertThat(serverInfo.ignoreResourceDeletion()).isTrue();
|
||||||
}
|
}
|
||||||
|
@ -650,7 +680,9 @@ public class GrpcBootstrapperImplTest {
|
||||||
BootstrapInfo info = bootstrapper.bootstrap();
|
BootstrapInfo info = bootstrapper.bootstrap();
|
||||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||||
assertThat(serverInfo.target()).isEqualTo(SERVER_URI);
|
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();
|
assertThat(serverInfo.isTrustedXdsServer()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -672,7 +704,9 @@ public class GrpcBootstrapperImplTest {
|
||||||
BootstrapInfo info = bootstrapper.bootstrap();
|
BootstrapInfo info = bootstrapper.bootstrap();
|
||||||
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
ServerInfo serverInfo = Iterables.getOnlyElement(info.servers());
|
||||||
assertThat(serverInfo.target()).isEqualTo(SERVER_URI);
|
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.
|
// ignore_resource_deletion features enabled: confirm both are on.
|
||||||
assertThat(serverInfo.ignoreResourceDeletion()).isTrue();
|
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(
|
private static BootstrapperImpl.FileReader createFileReader(
|
||||||
final String expectedPath, final String rawData) {
|
final String expectedPath, final String rawData) {
|
||||||
return new BootstrapperImpl.FileReader() {
|
return new BootstrapperImpl.FileReader() {
|
||||||
|
|
|
@ -3549,7 +3549,11 @@ public class GrpcXdsClientImplDataTest {
|
||||||
|
|
||||||
private XdsResourceType.Args getXdsResourceTypeArgs(boolean isTrustedServer) {
|
private XdsResourceType.Args getXdsResourceTypeArgs(boolean isTrustedServer) {
|
||||||
return new XdsResourceType.Args(
|
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);
|
true);
|
||||||
BootstrapInfo bootstrapInfo =
|
BootstrapInfo bootstrapInfo =
|
||||||
Bootstrapper.BootstrapInfo.builder()
|
Bootstrapper.BootstrapInfo.builder()
|
||||||
|
@ -4219,8 +4223,12 @@ public abstract class GrpcXdsClientImplTestBase {
|
||||||
|
|
||||||
private BootstrapInfo buildBootStrap(String serverUri) {
|
private BootstrapInfo buildBootStrap(String serverUri) {
|
||||||
|
|
||||||
ServerInfo xdsServerInfo = ServerInfo.create(serverUri, CHANNEL_CREDENTIALS,
|
ServerInfo xdsServerInfo = ServerInfo.create(
|
||||||
ignoreResourceDeletion(), true);
|
serverUri,
|
||||||
|
CHANNEL_CREDENTIALS,
|
||||||
|
null,
|
||||||
|
ignoreResourceDeletion(),
|
||||||
|
true);
|
||||||
|
|
||||||
return Bootstrapper.BootstrapInfo.builder()
|
return Bootstrapper.BootstrapInfo.builder()
|
||||||
.servers(Collections.singletonList(xdsServerInfo))
|
.servers(Collections.singletonList(xdsServerInfo))
|
||||||
|
|
|
@ -347,7 +347,7 @@ public class XdsClientFallbackTest {
|
||||||
@Override
|
@Override
|
||||||
public XdsTransport create(Bootstrapper.ServerInfo serverInfo) {
|
public XdsTransport create(Bootstrapper.ServerInfo serverInfo) {
|
||||||
ChannelCredentials channelCredentials =
|
ChannelCredentials channelCredentials =
|
||||||
(ChannelCredentials) serverInfo.implSpecificConfig();
|
(ChannelCredentials) serverInfo.implSpecificChannelCredConfig();
|
||||||
return new GrpcXdsTransportFactory.GrpcXdsTransport(
|
return new GrpcXdsTransportFactory.GrpcXdsTransport(
|
||||||
Grpc.newChannelBuilder(serverInfo.target(), channelCredentials)
|
Grpc.newChannelBuilder(serverInfo.target(), channelCredentials)
|
||||||
.executor(executor)
|
.executor(executor)
|
||||||
|
|
|
@ -22,11 +22,13 @@ import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import io.grpc.CallCredentials;
|
||||||
import io.grpc.ChannelCredentials;
|
import io.grpc.ChannelCredentials;
|
||||||
import io.grpc.xds.XdsCredentialsProvider;
|
import io.grpc.xds.XdsCredentialsProvider;
|
||||||
import io.grpc.xds.XdsCredentialsRegistry;
|
import io.grpc.xds.XdsCredentialsRegistry;
|
||||||
import io.grpc.xds.internal.GoogleDefaultXdsCredentialsProvider;
|
import io.grpc.xds.internal.GoogleDefaultXdsCredentialsProvider;
|
||||||
import io.grpc.xds.internal.InsecureXdsCredentialsProvider;
|
import io.grpc.xds.internal.InsecureXdsCredentialsProvider;
|
||||||
|
import io.grpc.xds.internal.JwtTokenFileXdsCredentialsProvider;
|
||||||
import io.grpc.xds.internal.TlsXdsCredentialsProvider;
|
import io.grpc.xds.internal.TlsXdsCredentialsProvider;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -136,11 +138,13 @@ public class XdsCredentialsRegistryTest {
|
||||||
public void defaultRegistry_providers() {
|
public void defaultRegistry_providers() {
|
||||||
Map<String, XdsCredentialsProvider> providers =
|
Map<String, XdsCredentialsProvider> providers =
|
||||||
XdsCredentialsRegistry.getDefaultRegistry().providers();
|
XdsCredentialsRegistry.getDefaultRegistry().providers();
|
||||||
assertThat(providers).hasSize(3);
|
assertThat(providers).hasSize(4);
|
||||||
assertThat(providers.get("google_default").getClass())
|
assertThat(providers.get("google_default").getClass())
|
||||||
.isEqualTo(GoogleDefaultXdsCredentialsProvider.class);
|
.isEqualTo(GoogleDefaultXdsCredentialsProvider.class);
|
||||||
assertThat(providers.get("insecure").getClass())
|
assertThat(providers.get("insecure").getClass())
|
||||||
.isEqualTo(InsecureXdsCredentialsProvider.class);
|
.isEqualTo(InsecureXdsCredentialsProvider.class);
|
||||||
|
assertThat(providers.get("jwt_token_file").getClass())
|
||||||
|
.isEqualTo(JwtTokenFileXdsCredentialsProvider.class);
|
||||||
assertThat(providers.get("tls").getClass())
|
assertThat(providers.get("tls").getClass())
|
||||||
.isEqualTo(TlsXdsCredentialsProvider.class);
|
.isEqualTo(TlsXdsCredentialsProvider.class);
|
||||||
}
|
}
|
||||||
|
@ -151,6 +155,7 @@ public class XdsCredentialsRegistryTest {
|
||||||
assertThat(classes).containsExactly(
|
assertThat(classes).containsExactly(
|
||||||
GoogleDefaultXdsCredentialsProvider.class,
|
GoogleDefaultXdsCredentialsProvider.class,
|
||||||
InsecureXdsCredentialsProvider.class,
|
InsecureXdsCredentialsProvider.class,
|
||||||
|
JwtTokenFileXdsCredentialsProvider.class,
|
||||||
TlsXdsCredentialsProvider.class);
|
TlsXdsCredentialsProvider.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +200,11 @@ public class XdsCredentialsRegistryTest {
|
||||||
public ChannelCredentials newChannelCredentials(Map<String, ?> config) {
|
public ChannelCredentials newChannelCredentials(Map<String, ?> config) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CallCredentials newCallCredentials(Map<String, ?> config) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SampleChannelCredentials extends ChannelCredentials {
|
private static class SampleChannelCredentials extends ChannelCredentials {
|
||||||
|
|
|
@ -368,13 +368,13 @@ public class XdsNameResolverTest {
|
||||||
String serviceAuthority = "[::FFFF:129.144.52.38]:80";
|
String serviceAuthority = "[::FFFF:129.144.52.38]:80";
|
||||||
bootstrapInfo = BootstrapInfo.builder()
|
bootstrapInfo = BootstrapInfo.builder()
|
||||||
.servers(ImmutableList.of(ServerInfo.create(
|
.servers(ImmutableList.of(ServerInfo.create(
|
||||||
"td.googleapis.com", InsecureChannelCredentials.create(), true, true)))
|
"td.googleapis.com", InsecureChannelCredentials.create(), null, true, true)))
|
||||||
.node(Node.newBuilder().build())
|
.node(Node.newBuilder().build())
|
||||||
.authorities(
|
.authorities(
|
||||||
ImmutableMap.of(targetAuthority, AuthorityInfo.create(
|
ImmutableMap.of(targetAuthority, AuthorityInfo.create(
|
||||||
"xdstp://" + targetAuthority + "/envoy.config.listener.v3.Listener/%s?foo=1&bar=2",
|
"xdstp://" + targetAuthority + "/envoy.config.listener.v3.Listener/%s?foo=1&bar=2",
|
||||||
ImmutableList.of(ServerInfo.create(
|
ImmutableList.of(ServerInfo.create(
|
||||||
"td.googleapis.com", InsecureChannelCredentials.create(), true, true)))))
|
"td.googleapis.com", InsecureChannelCredentials.create(), null, true, true)))))
|
||||||
.build();
|
.build();
|
||||||
expectedLdsResourceName = "xdstp://xds.authority.com/envoy.config.listener.v3.Listener/"
|
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
|
+ "%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<>();
|
List<ServerInfo> serverInfos = new ArrayList<>();
|
||||||
for (String uri : serverUris) {
|
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();
|
EnvoyProtoData.Node node = EnvoyProtoData.Node.newBuilder().setId("node-id").build();
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package io.grpc.xds.internal;
|
package io.grpc.xds.internal;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertSame;
|
import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
@ -54,4 +55,9 @@ public class GoogleDefaultXdsCredentialsProviderTest {
|
||||||
assertSame(CompositeChannelCredentials.class,
|
assertSame(CompositeChannelCredentials.class,
|
||||||
provider.newChannelCredentials(null).getClass());
|
provider.newChannelCredentials(null).getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void callCredentials() {
|
||||||
|
assertNull(provider.newCallCredentials(null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package io.grpc.xds.internal;
|
package io.grpc.xds.internal;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertSame;
|
import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
@ -54,4 +55,9 @@ public class InsecureXdsCredentialsProviderTest {
|
||||||
assertSame(InsecureChannelCredentials.class,
|
assertSame(InsecureChannelCredentials.class,
|
||||||
provider.newChannelCredentials(null).getClass());
|
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;
|
package io.grpc.xds.internal;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertSame;
|
import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
@ -54,4 +55,9 @@ public class TlsXdsCredentialsProviderTest {
|
||||||
assertSame(TlsChannelCredentials.class,
|
assertSame(TlsChannelCredentials.class,
|
||||||
provider.newChannelCredentials(null).getClass());
|
provider.newChannelCredentials(null).getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void callCredentials() {
|
||||||
|
assertNull(provider.newCallCredentials(null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue