Skip to content

Commit

Permalink
Allow users outside of io.grpc.xds package to create custom xDS resou…
Browse files Browse the repository at this point in the history
…rces (#10834)

Currently few of the interfaces needed to define and start a watch for a xDS resource are package private, which can't be used externally outside of io.grpc.xds. Exposing them outside allows users to define their own custom resources and start a watch along with the default supported resources.

Also as part of this change, move an Exception defined in the XdsClientImpl into XdsResourceType. As XdsClientImpl is an implementation package, it makes more sense to expose it via the XdsResourceType class.
  • Loading branch information
anicr7 committed Jan 25, 2024
1 parent 3e8e56f commit 6d96e65
Show file tree
Hide file tree
Showing 11 changed files with 61 additions and 61 deletions.
Expand Up @@ -39,8 +39,8 @@
import io.grpc.LoadBalancerRegistry;
import io.grpc.internal.JsonParser;
import io.grpc.xds.LoadBalancerConfigFactory.LoadBalancingPolicyConverter.MaxRecursionReachedException;
import io.grpc.xds.XdsClientImpl.ResourceInvalidException;
import io.grpc.xds.XdsLogger.XdsLogLevel;
import io.grpc.xds.XdsResourceType.ResourceInvalidException;
import java.io.IOException;
import java.util.Map;

Expand Down
5 changes: 2 additions & 3 deletions xds/src/main/java/io/grpc/xds/XdsClient.java
Expand Up @@ -47,7 +47,7 @@
* protocols (e.g., LDS, RDS, VHDS, CDS and EDS) over a single channel. Watch-based interfaces
* are provided for each set of data needed by gRPC.
*/
abstract class XdsClient {
public abstract class XdsClient {

static boolean isResourceNameValid(String resourceName, String typeUrl) {
checkNotNull(resourceName, "resourceName");
Expand Down Expand Up @@ -110,8 +110,7 @@ static String percentEncodePath(String input) {
return Joiner.on('/').join(encodedSegs);
}

interface ResourceUpdate {
}
public interface ResourceUpdate {}

/**
* Watcher interface for a single requested xDS resource.
Expand Down
12 changes: 0 additions & 12 deletions xds/src/main/java/io/grpc/xds/XdsClientImpl.java
Expand Up @@ -748,18 +748,6 @@ private void notifyWatcher(ResourceWatcher<T> watcher, T update) {
}
}

static final class ResourceInvalidException extends Exception {
private static final long serialVersionUID = 0L;

ResourceInvalidException(String message) {
super(message, null, false, false);
}

ResourceInvalidException(String message, Throwable cause) {
super(cause != null ? message + ": " + cause.getMessage() : message, cause, false, false);
}
}

abstract static class XdsChannelFactory {
static final XdsChannelFactory DEFAULT_XDS_CHANNEL_FACTORY = new XdsChannelFactory() {
@Override
Expand Down
15 changes: 7 additions & 8 deletions xds/src/main/java/io/grpc/xds/XdsClusterResource.java
Expand Up @@ -42,8 +42,8 @@
import io.grpc.xds.EnvoyServerProtoData.OutlierDetection;
import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext;
import io.grpc.xds.XdsClient.ResourceUpdate;
import io.grpc.xds.XdsClientImpl.ResourceInvalidException;
import io.grpc.xds.XdsClusterResource.CdsUpdate;
import io.grpc.xds.XdsResourceType.ResourceInvalidException;
import java.util.List;
import java.util.Locale;
import java.util.Set;
Expand All @@ -65,37 +65,36 @@ public static XdsClusterResource getInstance() {

@Override
@Nullable
String extractResourceName(Message unpackedResource) {
protected String extractResourceName(Message unpackedResource) {
if (!(unpackedResource instanceof Cluster)) {
return null;
}
return ((Cluster) unpackedResource).getName();
}

@Override
String typeName() {
protected String typeName() {
return "CDS";
}

@Override
String typeUrl() {
protected String typeUrl() {
return ADS_TYPE_URL_CDS;
}

@Override
boolean isFullStateOfTheWorld() {
protected boolean isFullStateOfTheWorld() {
return true;
}

@Override
@SuppressWarnings("unchecked")
Class<Cluster> unpackedClassName() {
protected Class<Cluster> unpackedClassName() {
return Cluster.class;
}

@Override
CdsUpdate doParse(Args args, Message unpackedMessage)
throws ResourceInvalidException {
protected CdsUpdate doParse(Args args, Message unpackedMessage) throws ResourceInvalidException {
if (!(unpackedMessage instanceof Cluster)) {
throw new ResourceInvalidException("Invalid message type: " + unpackedMessage.getClass());
}
Expand Down
15 changes: 7 additions & 8 deletions xds/src/main/java/io/grpc/xds/XdsEndpointResource.java
Expand Up @@ -28,8 +28,8 @@
import io.grpc.xds.Endpoints.DropOverload;
import io.grpc.xds.Endpoints.LocalityLbEndpoints;
import io.grpc.xds.XdsClient.ResourceUpdate;
import io.grpc.xds.XdsClientImpl.ResourceInvalidException;
import io.grpc.xds.XdsEndpointResource.EdsUpdate;
import io.grpc.xds.XdsResourceType.ResourceInvalidException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -54,36 +54,35 @@ public static XdsEndpointResource getInstance() {

@Override
@Nullable
String extractResourceName(Message unpackedResource) {
protected String extractResourceName(Message unpackedResource) {
if (!(unpackedResource instanceof ClusterLoadAssignment)) {
return null;
}
return ((ClusterLoadAssignment) unpackedResource).getClusterName();
}

@Override
String typeName() {
protected String typeName() {
return "EDS";
}

@Override
String typeUrl() {
protected String typeUrl() {
return ADS_TYPE_URL_EDS;
}

@Override
boolean isFullStateOfTheWorld() {
protected boolean isFullStateOfTheWorld() {
return false;
}

@Override
Class<ClusterLoadAssignment> unpackedClassName() {
protected Class<ClusterLoadAssignment> unpackedClassName() {
return ClusterLoadAssignment.class;
}

@Override
EdsUpdate doParse(Args args, Message unpackedMessage)
throws ResourceInvalidException {
protected EdsUpdate doParse(Args args, Message unpackedMessage) throws ResourceInvalidException {
if (!(unpackedMessage instanceof ClusterLoadAssignment)) {
throw new ResourceInvalidException("Invalid message type: " + unpackedMessage.getClass());
}
Expand Down
14 changes: 7 additions & 7 deletions xds/src/main/java/io/grpc/xds/XdsListenerResource.java
Expand Up @@ -18,8 +18,8 @@

import static com.google.common.base.Preconditions.checkNotNull;
import static io.grpc.xds.XdsClient.ResourceUpdate;
import static io.grpc.xds.XdsClientImpl.ResourceInvalidException;
import static io.grpc.xds.XdsClusterResource.validateCommonTlsContext;
import static io.grpc.xds.XdsResourceType.ResourceInvalidException;
import static io.grpc.xds.XdsRouteConfigureResource.extractVirtualHosts;

import com.github.udpa.udpa.type.v1.TypedStruct;
Expand Down Expand Up @@ -66,35 +66,35 @@ public static XdsListenerResource getInstance() {

@Override
@Nullable
String extractResourceName(Message unpackedResource) {
protected String extractResourceName(Message unpackedResource) {
if (!(unpackedResource instanceof Listener)) {
return null;
}
return ((Listener) unpackedResource).getName();
}

@Override
String typeName() {
protected String typeName() {
return "LDS";
}

@Override
Class<Listener> unpackedClassName() {
protected Class<Listener> unpackedClassName() {
return Listener.class;
}

@Override
String typeUrl() {
protected String typeUrl() {
return ADS_TYPE_URL_LDS;
}

@Override
boolean isFullStateOfTheWorld() {
protected boolean isFullStateOfTheWorld() {
return true;
}

@Override
LdsUpdate doParse(Args args, Message unpackedMessage)
protected LdsUpdate doParse(Args args, Message unpackedMessage)
throws ResourceInvalidException {
if (!(unpackedMessage instanceof Listener)) {
throw new ResourceInvalidException("Invalid message type: " + unpackedMessage.getClass());
Expand Down
35 changes: 25 additions & 10 deletions xds/src/main/java/io/grpc/xds/XdsResourceType.java
Expand Up @@ -21,15 +21,17 @@
import static io.grpc.xds.XdsClient.ResourceUpdate;
import static io.grpc.xds.XdsClient.canonifyResourceName;
import static io.grpc.xds.XdsClient.isResourceNameValid;
import static io.grpc.xds.XdsClientImpl.ResourceInvalidException;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.protobuf.Any;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import io.envoyproxy.envoy.service.discovery.v3.Resource;
import io.grpc.ExperimentalApi;
import io.grpc.LoadBalancerRegistry;
import io.grpc.xds.Bootstrapper.ServerInfo;
import io.grpc.xds.XdsClient.ResourceUpdate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -38,7 +40,8 @@
import java.util.Set;
import javax.annotation.Nullable;

abstract class XdsResourceType<T extends ResourceUpdate> {
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/10847")
public abstract class XdsResourceType<T extends ResourceUpdate> {
static final String TYPE_URL_RESOURCE =
"type.googleapis.com/envoy.service.discovery.v3.Resource";
static final String TRANSPORT_SOCKET_NAME_TLS = "envoy.transport_sockets.tls";
Expand Down Expand Up @@ -68,22 +71,22 @@ abstract class XdsResourceType<T extends ResourceUpdate> {
"type.googleapis.com/xds.type.v3.TypedStruct";

@Nullable
abstract String extractResourceName(Message unpackedResource);
protected abstract String extractResourceName(Message unpackedResource);

abstract Class<? extends com.google.protobuf.Message> unpackedClassName();
protected abstract Class<? extends com.google.protobuf.Message> unpackedClassName();

abstract String typeName();
protected abstract String typeName();

abstract String typeUrl();
protected abstract String typeUrl();

// Do not confuse with the SotW approach: it is the mechanism in which the client must specify all
// resource names it is interested in with each request. Different resource types may behave
// differently in this approach. For LDS and CDS resources, the server must return all resources
// that the client has subscribed to in each request. For RDS and EDS, the server may only return
// the resources that need an update.
abstract boolean isFullStateOfTheWorld();
protected abstract boolean isFullStateOfTheWorld();

static class Args {
public static class Args {
final ServerInfo serverInfo;
final String versionInfo;
final String nonce;
Expand Down Expand Up @@ -114,6 +117,18 @@ public Args(ServerInfo serverInfo, String versionInfo, String nonce,
}
}

public static final class ResourceInvalidException extends Exception {
private static final long serialVersionUID = 0L;

public ResourceInvalidException(String message) {
super(message, null, false, false);
}

public ResourceInvalidException(String message, Throwable cause) {
super(cause != null ? message + ": " + cause.getMessage() : message, cause, false, false);
}
}

ValidatedResourceUpdate<T> parse(Args args, List<Any> resources) {
Map<String, ParsedResource<T>> parsedResources = new HashMap<>(resources.size());
Set<String> unpackedResources = new HashSet<>(resources.size());
Expand Down Expand Up @@ -147,7 +162,7 @@ ValidatedResourceUpdate<T> parse(Args args, List<Any> resources) {
T resourceUpdate;
try {
resourceUpdate = doParse(args, unpackedMessage);
} catch (XdsClientImpl.ResourceInvalidException e) {
} catch (ResourceInvalidException e) {
errors.add(String.format("%s response %s '%s' validation error: %s",
typeName(), unpackedClassName().getSimpleName(), cname, e.getMessage()));
invalidResources.add(cname);
Expand All @@ -162,7 +177,7 @@ ValidatedResourceUpdate<T> parse(Args args, List<Any> resources) {

}

abstract T doParse(Args args, Message unpackedMessage) throws ResourceInvalidException;
protected abstract T doParse(Args args, Message unpackedMessage) throws ResourceInvalidException;

/**
* Helper method to unpack serialized {@link com.google.protobuf.Any} message, while replacing
Expand Down
14 changes: 7 additions & 7 deletions xds/src/main/java/io/grpc/xds/XdsRouteConfigureResource.java
Expand Up @@ -49,7 +49,7 @@
import io.grpc.xds.VirtualHost.Route.RouteMatch;
import io.grpc.xds.VirtualHost.Route.RouteMatch.PathMatcher;
import io.grpc.xds.XdsClient.ResourceUpdate;
import io.grpc.xds.XdsClientImpl.ResourceInvalidException;
import io.grpc.xds.XdsResourceType.ResourceInvalidException;
import io.grpc.xds.XdsRouteConfigureResource.RdsUpdate;
import io.grpc.xds.internal.MatcherParser;
import io.grpc.xds.internal.Matchers;
Expand Down Expand Up @@ -85,35 +85,35 @@ public static XdsRouteConfigureResource getInstance() {

@Override
@Nullable
String extractResourceName(Message unpackedResource) {
protected String extractResourceName(Message unpackedResource) {
if (!(unpackedResource instanceof RouteConfiguration)) {
return null;
}
return ((RouteConfiguration) unpackedResource).getName();
}

@Override
String typeName() {
protected String typeName() {
return "RDS";
}

@Override
String typeUrl() {
protected String typeUrl() {
return ADS_TYPE_URL_RDS;
}

@Override
boolean isFullStateOfTheWorld() {
protected boolean isFullStateOfTheWorld() {
return false;
}

@Override
Class<RouteConfiguration> unpackedClassName() {
protected Class<RouteConfiguration> unpackedClassName() {
return RouteConfiguration.class;
}

@Override
RdsUpdate doParse(XdsResourceType.Args args, Message unpackedMessage)
protected RdsUpdate doParse(XdsResourceType.Args args, Message unpackedMessage)
throws ResourceInvalidException {
if (!(unpackedMessage instanceof RouteConfiguration)) {
throw new ResourceInvalidException("Invalid message type: " + unpackedMessage.getClass());
Expand Down
Expand Up @@ -52,7 +52,7 @@
import io.grpc.internal.JsonUtil;
import io.grpc.internal.ServiceConfigUtil;
import io.grpc.internal.ServiceConfigUtil.LbConfig;
import io.grpc.xds.XdsClientImpl.ResourceInvalidException;
import io.grpc.xds.XdsResourceType.ResourceInvalidException;
import java.util.List;
import org.junit.After;
import org.junit.Test;
Expand Down
2 changes: 1 addition & 1 deletion xds/src/test/java/io/grpc/xds/XdsClientImplDataTest.java
Expand Up @@ -134,8 +134,8 @@
import io.grpc.xds.VirtualHost.Route.RouteMatch;
import io.grpc.xds.VirtualHost.Route.RouteMatch.PathMatcher;
import io.grpc.xds.WeightedRoundRobinLoadBalancer.WeightedRoundRobinLoadBalancerConfig;
import io.grpc.xds.XdsClientImpl.ResourceInvalidException;
import io.grpc.xds.XdsClusterResource.CdsUpdate;
import io.grpc.xds.XdsResourceType.ResourceInvalidException;
import io.grpc.xds.XdsResourceType.StructOrError;
import io.grpc.xds.internal.Matchers;
import io.grpc.xds.internal.Matchers.FractionMatcher;
Expand Down

0 comments on commit 6d96e65

Please sign in to comment.