Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* 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;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.io.Closeable;

/**
* {@code ChannelCredentials} which holds allocated resources (e.g. file watchers) upon
* instantiation of a given {@code ChannelCredentials} object, which must be closed once
* mentioned {@code ChannelCredentials} are no longer in use.
*/
public final class ResourceAllocatingChannelCredentials extends ChannelCredentials {
public static ChannelCredentials create(
ChannelCredentials channelCreds, ImmutableList<Closeable> resources) {
return new ResourceAllocatingChannelCredentials(channelCreds, resources);
}

private final ChannelCredentials channelCreds;
private final ImmutableList<Closeable> resources;

private ResourceAllocatingChannelCredentials(
ChannelCredentials channelCreds, ImmutableList<Closeable> resources) {
this.channelCreds = Preconditions.checkNotNull(channelCreds, "channelCreds");
this.resources = Preconditions.checkNotNull(resources, "resources");
}

public ChannelCredentials getChannelCredentials() {
return channelCreds;
}

public ImmutableList<Closeable> getAllocatedResources() {
return resources;
}

@Override
public ChannelCredentials withoutBearerTokens() {
return channelCreds.withoutBearerTokens();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* 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;

import static com.google.common.truth.Truth.assertThat;

import com.google.common.collect.ImmutableList;
import java.io.Closeable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/** Tests for {@link ResourceAllocatingChannelCredentials}. */
@RunWith(JUnit4.class)
public class ResourceAllocatingChannelCredentialsTest {
@Test
public void withoutBearerTokenDelegatesCall() {
ChannelCredentials channelChreds = new ChannelCredentials() {
@Override
public ChannelCredentials withoutBearerTokens() {
return this;
}
};
ImmutableList<Closeable> resources = ImmutableList.<Closeable>of();
ChannelCredentials creds =
ResourceAllocatingChannelCredentials.create(channelChreds, resources);
assertThat(creds.withoutBearerTokens()).isEqualTo(channelChreds);
}
}
34 changes: 15 additions & 19 deletions xds/src/main/java/io/grpc/xds/GrpcBootstrapperImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import io.grpc.ChannelCredentials;
import com.google.common.collect.ImmutableSet;
import io.grpc.internal.JsonUtil;
import io.grpc.xds.client.BootstrapperImpl;
import io.grpc.xds.client.XdsInitializationException;
Expand Down Expand Up @@ -90,47 +90,43 @@ protected String getJsonContent() throws XdsInitializationException, IOException
}

@Override
protected Object getImplSpecificConfig(Map<String, ?> serverConfig, String serverUri)
protected ImmutableMap<String, ?> getImplSpecificConfig(Map<String, ?> serverConfig,
String serverUri)
throws XdsInitializationException {
return getChannelCredentials(serverConfig, serverUri);
return getChannelCredentialsConfig(serverConfig, serverUri);
}

private static ChannelCredentials getChannelCredentials(Map<String, ?> serverConfig,
String serverUri)
private static ImmutableMap<String, ?> getChannelCredentialsConfig(Map<String, ?> serverConfig,
String serverUri)
throws XdsInitializationException {
List<?> rawChannelCredsList = JsonUtil.getList(serverConfig, "channel_creds");
if (rawChannelCredsList == null || rawChannelCredsList.isEmpty()) {
throw new XdsInitializationException(
"Invalid bootstrap: server " + serverUri + " 'channel_creds' required");
}
ChannelCredentials channelCredentials =
ImmutableMap<String, ?> channelCredentialsConfig =
parseChannelCredentials(JsonUtil.checkObjectList(rawChannelCredsList), serverUri);
if (channelCredentials == null) {
if (channelCredentialsConfig == null) {
throw new XdsInitializationException(
"Server " + serverUri + ": no supported channel credentials found");
}
return channelCredentials;
return channelCredentialsConfig;
}

@Nullable
private static ChannelCredentials parseChannelCredentials(List<Map<String, ?>> jsonList,
String serverUri)
private static ImmutableMap<String, ?> parseChannelCredentials(List<Map<String, ?>> jsonList,
String serverUri)
throws XdsInitializationException {
for (Map<String, ?> channelCreds : jsonList) {
String type = JsonUtil.getString(channelCreds, "type");
if (type == null) {
throw new XdsInitializationException(
"Invalid bootstrap: server " + serverUri + " with 'channel_creds' type unspecified");
}
XdsCredentialsProvider provider = XdsCredentialsRegistry.getDefaultRegistry()
.getProvider(type);
if (provider != null) {
Map<String, ?> config = JsonUtil.getObject(channelCreds, "config");
if (config == null) {
config = ImmutableMap.of();
}

return provider.newChannelCredentials(config);
ImmutableSet<String> supportedNames = XdsCredentialsRegistry.getDefaultRegistry()
.getSupportedCredentialNames();
if (supportedNames.contains(type)) {
return ImmutableMap.copyOf(channelCreds);
}
}
return null;
Expand Down
39 changes: 35 additions & 4 deletions xds/src/main/java/io/grpc/xds/GrpcXdsTransportFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.grpc.CallCredentials;
import io.grpc.CallOptions;
import io.grpc.ChannelCredentials;
Expand All @@ -28,9 +30,15 @@
import io.grpc.ManagedChannel;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import io.grpc.ResourceAllocatingChannelCredentials;
import io.grpc.Status;
import io.grpc.internal.GrpcUtil;
import io.grpc.internal.JsonUtil;
import io.grpc.xds.client.Bootstrapper;
import io.grpc.xds.client.XdsInitializationException;
import io.grpc.xds.client.XdsTransportFactory;
import java.io.Closeable;
import java.util.Map;
import java.util.concurrent.TimeUnit;

final class GrpcXdsTransportFactory implements XdsTransportFactory {
Expand All @@ -42,7 +50,7 @@ final class GrpcXdsTransportFactory implements XdsTransportFactory {
}

@Override
public XdsTransport create(Bootstrapper.ServerInfo serverInfo) {
public XdsTransport create(Bootstrapper.ServerInfo serverInfo) throws XdsInitializationException {
return new GrpcXdsTransport(serverInfo, callCredentials);
}

Expand All @@ -56,8 +64,9 @@ static class GrpcXdsTransport implements XdsTransport {

private final ManagedChannel channel;
private final CallCredentials callCredentials;
private final ImmutableList<Closeable> resources;

public GrpcXdsTransport(Bootstrapper.ServerInfo serverInfo) {
public GrpcXdsTransport(Bootstrapper.ServerInfo serverInfo) throws XdsInitializationException {
this(serverInfo, null);
}

Expand All @@ -66,9 +75,27 @@ public GrpcXdsTransport(ManagedChannel channel) {
this(channel, null);
}

public GrpcXdsTransport(Bootstrapper.ServerInfo serverInfo, CallCredentials callCredentials) {
public GrpcXdsTransport(Bootstrapper.ServerInfo serverInfo, CallCredentials callCredentials)
throws XdsInitializationException {
String target = serverInfo.target();
ChannelCredentials channelCredentials = (ChannelCredentials) serverInfo.implSpecificConfig();
Map<String, ?> implSpecificConfig = serverInfo.implSpecificConfig();
String type = JsonUtil.getString(implSpecificConfig, "type");
XdsCredentialsProvider provider = XdsCredentialsRegistry.getDefaultRegistry()
.getProvider(type);
Map<String, ?> config = JsonUtil.getObject(implSpecificConfig, "config");
if (config == null) {
config = ImmutableMap.of();
}
ChannelCredentials channelCredentials = provider.newChannelCredentials(config);
if (channelCredentials == null) {
throw new XdsInitializationException(
"Cannot create channel credentials of type " + type + " for target " + target);
}
// if {@code ChannelCredentials} instance has allocated resource of any type, save them to be
// released once the channel is shutdown
this.resources = (channelCredentials instanceof ResourceAllocatingChannelCredentials)
? ((ResourceAllocatingChannelCredentials) channelCredentials).getAllocatedResources()
: ImmutableList.<Closeable>of();
this.channel = Grpc.newChannelBuilder(target, channelCredentials)
.keepAliveTime(5, TimeUnit.MINUTES)
.build();
Expand All @@ -79,6 +106,7 @@ public GrpcXdsTransport(Bootstrapper.ServerInfo serverInfo, CallCredentials call
public GrpcXdsTransport(ManagedChannel channel, CallCredentials callCredentials) {
this.channel = checkNotNull(channel, "channel");
this.callCredentials = callCredentials;
this.resources = ImmutableList.<Closeable>of();
}

@Override
Expand All @@ -99,6 +127,9 @@ public <ReqT, RespT> StreamingCall<ReqT, RespT> createStreamingCall(
@Override
public void shutdown() {
channel.shutdown();
for (Closeable resource : resources) {
GrpcUtil.closeQuietly(resource);
}
}

private class XdsStreamingCall<ReqT, RespT> implements
Expand Down
9 changes: 9 additions & 0 deletions xds/src/main/java/io/grpc/xds/XdsCredentialsRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.concurrent.GuardedBy;
import io.grpc.InternalServiceProviders;
import java.util.ArrayList;
Expand Down Expand Up @@ -147,6 +148,14 @@ public synchronized XdsCredentialsProvider getProvider(String name) {
return effectiveProviders.get(checkNotNull(name, "name"));
}

/**
* Returns list of registered xds credential names. Each provider declares its name via
* {@link XdsCredentialsProvider#getName}.
*/
public synchronized ImmutableSet<String> getSupportedCredentialNames() {
return effectiveProviders.keySet();
}

@VisibleForTesting
static List<Class<?>> getHardCodedClasses() {
// Class.forName(String) is used to remove the need for ProGuard configuration. Note that
Expand Down
7 changes: 4 additions & 3 deletions xds/src/main/java/io/grpc/xds/client/Bootstrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public BootstrapInfo bootstrap(Map<String, ?> rawData) throws XdsInitializationE
public abstract static class ServerInfo {
public abstract String target();

public abstract Object implSpecificConfig();
public abstract ImmutableMap<String, ?> implSpecificConfig();

public abstract boolean ignoreResourceDeletion();

Expand All @@ -66,14 +66,15 @@ public abstract static class ServerInfo {
public abstract boolean resourceTimerIsTransientError();

@VisibleForTesting
public static ServerInfo create(String target, @Nullable Object implSpecificConfig) {
public static ServerInfo create(
String target, @Nullable ImmutableMap<String, ?> implSpecificConfig) {
return new AutoValue_Bootstrapper_ServerInfo(target, implSpecificConfig,
false, false, false);
}

@VisibleForTesting
public static ServerInfo create(
String target, Object implSpecificConfig, boolean ignoreResourceDeletion,
String target, ImmutableMap<String, ?> implSpecificConfig, boolean ignoreResourceDeletion,
boolean isTrustedXdsServer, boolean resourceTimerIsTransientError) {
return new AutoValue_Bootstrapper_ServerInfo(target, implSpecificConfig,
ignoreResourceDeletion, isTrustedXdsServer, resourceTimerIsTransientError);
Expand Down
6 changes: 3 additions & 3 deletions xds/src/main/java/io/grpc/xds/client/BootstrapperImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ protected BootstrapperImpl() {

protected abstract String getJsonContent() throws IOException, XdsInitializationException;

protected abstract Object getImplSpecificConfig(Map<String, ?> serverConfig, String serverUri)
throws XdsInitializationException;
protected abstract ImmutableMap<String, ?> getImplSpecificConfig(
Map<String, ?> serverConfig, String serverUri) throws XdsInitializationException;


/**
Expand Down Expand Up @@ -253,7 +253,7 @@ private List<ServerInfo> parseServerInfos(List<?> rawServerConfigs, XdsLogger lo
}
logger.log(XdsLogLevel.INFO, "xDS server URI: {0}", serverUri);

Object implSpecificConfig = getImplSpecificConfig(serverConfig, serverUri);
ImmutableMap<String, ?> implSpecificConfig = getImplSpecificConfig(serverConfig, serverUri);

boolean resourceTimerIsTransientError = false;
boolean ignoreResourceDeletion = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/10823")
public interface XdsTransportFactory {
XdsTransport create(Bootstrapper.ServerInfo serverInfo);
XdsTransport create(Bootstrapper.ServerInfo serverInfo) throws XdsInitializationException;

/**
* Represents transport for xDS communication (e.g., a gRPC channel).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import io.grpc.ConnectivityState;
import io.grpc.ConnectivityStateInfo;
import io.grpc.EquivalentAddressGroup;
import io.grpc.InsecureChannelCredentials;
import io.grpc.LoadBalancer;
import io.grpc.LoadBalancer.CreateSubchannelArgs;
import io.grpc.LoadBalancer.FixedResultPicker;
Expand Down Expand Up @@ -116,7 +115,7 @@ public class ClusterImplLoadBalancerTest {
private static final String CLUSTER = "cluster-foo.googleapis.com";
private static final String EDS_SERVICE_NAME = "service.googleapis.com";
private static final ServerInfo LRS_SERVER_INFO =
ServerInfo.create("api.google.com", InsecureChannelCredentials.create());
ServerInfo.create("api.google.com", ImmutableMap.of("type", "insecure"));
private static final Metadata.Key<OrcaLoadReport> ORCA_ENDPOINT_LOAD_METRICS_KEY =
Metadata.Key.of(
"endpoint-load-metrics-bin",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import io.grpc.ConnectivityState;
import io.grpc.EquivalentAddressGroup;
import io.grpc.HttpConnectProxiedSocketAddress;
import io.grpc.InsecureChannelCredentials;
import io.grpc.LoadBalancer;
import io.grpc.LoadBalancer.Helper;
import io.grpc.LoadBalancer.PickResult;
Expand Down Expand Up @@ -126,7 +125,7 @@ public class ClusterResolverLoadBalancerTest {
private static final String EDS_SERVICE_NAME2 = "backend-service-bar.googleapis.com";
private static final String DNS_HOST_NAME = "dns-service.googleapis.com";
private static final ServerInfo LRS_SERVER_INFO =
ServerInfo.create("lrs.googleapis.com", InsecureChannelCredentials.create());
ServerInfo.create("lrs.googleapis.com", ImmutableMap.of("type", "insecure"));
private final Locality locality1 =
Locality.create("test-region-1", "test-zone-1", "test-subzone-1");
private final Locality locality2 =
Expand Down
3 changes: 1 addition & 2 deletions xds/src/test/java/io/grpc/xds/CsdsServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import io.envoyproxy.envoy.service.status.v3.ClientStatusResponse;
import io.envoyproxy.envoy.type.matcher.v3.NodeMatcher;
import io.grpc.Deadline;
import io.grpc.InsecureChannelCredentials;
import io.grpc.MetricRecorder;
import io.grpc.Status;
import io.grpc.Status.Code;
Expand Down Expand Up @@ -79,7 +78,7 @@ public class CsdsServiceTest {
EnvoyProtoData.Node.newBuilder().setId(NODE_ID).build();
private static final BootstrapInfo BOOTSTRAP_INFO = BootstrapInfo.builder()
.servers(ImmutableList.of(
ServerInfo.create(SERVER_URI, InsecureChannelCredentials.create())))
ServerInfo.create(SERVER_URI, ImmutableMap.of("type", "insecure"))))
.node(BOOTSTRAP_NODE)
.build();
private static final FakeXdsClient XDS_CLIENT_NO_RESOURCES = new FakeXdsClient();
Expand Down
Loading
Loading