mirror of https://github.com/grpc/grpc-java.git
xds: fix parsing RouteLookupClusterSpecifier mistake (#8641)
- Partially revert the change of RlsProtoData.java in #8612 by removing `public` accessor - Have grpc-xds no longer strongly depend on grpc-rls. The application will need grpc-rls as runtime dependencies if they need route lookup feature in xds. - Parse RouteLookupServiceClusterSpecifierPlugin config to the Json/Map representation of `io.grpc.lookup.v1.RouteLookupClusterSpecifier` instead of `io.grpc.rls.RlsProtoData.RouteLookupConfig`
This commit is contained in:
parent
b3579db574
commit
ad0971ef5f
|
|
@ -25,7 +25,6 @@ import com.google.common.base.MoreObjects;
|
|||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.grpc.Internal;
|
||||
import io.grpc.rls.RlsProtoData.GrpcKeyBuilder.Name;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
|
@ -36,8 +35,7 @@ import javax.annotation.Nullable;
|
|||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/** RlsProtoData is a collection of internal representation of RouteLookupService proto messages. */
|
||||
@Internal
|
||||
public final class RlsProtoData {
|
||||
final class RlsProtoData {
|
||||
|
||||
private RlsProtoData() {}
|
||||
|
||||
|
|
@ -142,7 +140,7 @@ public final class RlsProtoData {
|
|||
|
||||
/** A config object for gRPC RouteLookupService. */
|
||||
@Immutable
|
||||
public static final class RouteLookupConfig {
|
||||
static final class RouteLookupConfig {
|
||||
|
||||
private static final long MAX_AGE_MILLIS = TimeUnit.MINUTES.toMillis(5);
|
||||
private static final long MAX_CACHE_SIZE = 5 * 1024 * 1024;
|
||||
|
|
@ -164,8 +162,7 @@ public final class RlsProtoData {
|
|||
@Nullable
|
||||
private final String defaultTarget;
|
||||
|
||||
/** Constructor. */
|
||||
public RouteLookupConfig(
|
||||
RouteLookupConfig(
|
||||
List<GrpcKeyBuilder> grpcKeyBuilders,
|
||||
String lookupService,
|
||||
long lookupServiceTimeoutInMillis,
|
||||
|
|
@ -336,7 +333,7 @@ public final class RlsProtoData {
|
|||
* is true, one of the specified names must be present for the keybuilder to match.
|
||||
*/
|
||||
@Immutable
|
||||
public static final class NameMatcher {
|
||||
static final class NameMatcher {
|
||||
|
||||
private final String key;
|
||||
|
||||
|
|
@ -344,8 +341,7 @@ public final class RlsProtoData {
|
|||
|
||||
private final boolean optional;
|
||||
|
||||
/** Constructor. */
|
||||
public NameMatcher(String key, List<String> names, @Nullable Boolean optional) {
|
||||
NameMatcher(String key, List<String> names, @Nullable Boolean optional) {
|
||||
this.key = checkNotNull(key, "key");
|
||||
this.names = ImmutableList.copyOf(checkNotNull(names, "names"));
|
||||
this.optional = optional != null ? optional : true;
|
||||
|
|
@ -398,7 +394,7 @@ public final class RlsProtoData {
|
|||
}
|
||||
|
||||
/** GrpcKeyBuilder is a configuration to construct headers consumed by route lookup service. */
|
||||
public static final class GrpcKeyBuilder {
|
||||
static final class GrpcKeyBuilder {
|
||||
|
||||
private final ImmutableList<Name> names;
|
||||
|
||||
|
|
@ -407,7 +403,7 @@ public final class RlsProtoData {
|
|||
private final ImmutableMap<String, String> constantKeys;
|
||||
|
||||
/** Constructor. All args should be nonnull. Headers should head unique keys. */
|
||||
public GrpcKeyBuilder(
|
||||
GrpcKeyBuilder(
|
||||
List<Name> names, List<NameMatcher> headers, ExtraKeys extraKeys,
|
||||
Map<String, String> constantKeys) {
|
||||
checkState(names != null && !names.isEmpty(), "names cannot be empty");
|
||||
|
|
@ -486,7 +482,7 @@ public final class RlsProtoData {
|
|||
* required and includes the proto package name. The method name may be omitted, in which case
|
||||
* any method on the given service is matched.
|
||||
*/
|
||||
public static final class Name {
|
||||
static final class Name {
|
||||
|
||||
private final String service;
|
||||
|
||||
|
|
@ -497,7 +493,7 @@ public final class RlsProtoData {
|
|||
}
|
||||
|
||||
/** The primary constructor. */
|
||||
public Name(String service, String method) {
|
||||
Name(String service, String method) {
|
||||
checkState(
|
||||
!checkNotNull(service, "service").isEmpty(),
|
||||
"service must not be empty or null");
|
||||
|
|
@ -542,7 +538,7 @@ public final class RlsProtoData {
|
|||
}
|
||||
|
||||
@AutoValue
|
||||
public abstract static class ExtraKeys {
|
||||
abstract static class ExtraKeys {
|
||||
static final ExtraKeys DEFAULT = create(null, null, null);
|
||||
|
||||
@Nullable abstract String host();
|
||||
|
|
@ -551,7 +547,7 @@ public final class RlsProtoData {
|
|||
|
||||
@Nullable abstract String method();
|
||||
|
||||
public static ExtraKeys create(
|
||||
static ExtraKeys create(
|
||||
@Nullable String host, @Nullable String service, @Nullable String method) {
|
||||
return new AutoValue_RlsProtoData_ExtraKeys(host, service, method);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@ dependencies {
|
|||
libraries.autovalue_annotation,
|
||||
libraries.opencensus_proto,
|
||||
libraries.protobuf_util
|
||||
implementation project(path: ':grpc-rls')
|
||||
def nettyDependency = implementation project(':grpc-netty')
|
||||
|
||||
testImplementation project(':grpc-rls')
|
||||
testImplementation project(':grpc-core').sourceSets.test.output
|
||||
|
||||
annotationProcessor libraries.autovalue
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@
|
|||
|
||||
package io.grpc.xds;
|
||||
|
||||
import com.google.protobuf.Descriptors.Descriptor;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.google.protobuf.Message;
|
||||
import com.google.protobuf.MessageOrBuilder;
|
||||
import com.google.protobuf.TypeRegistry;
|
||||
import com.google.protobuf.util.JsonFormat;
|
||||
|
|
@ -46,7 +48,7 @@ final class MessagePrinter {
|
|||
static final JsonFormat.Printer printer = newPrinter();
|
||||
|
||||
private static JsonFormat.Printer newPrinter() {
|
||||
TypeRegistry registry =
|
||||
TypeRegistry.Builder registry =
|
||||
TypeRegistry.newBuilder()
|
||||
.add(Listener.getDescriptor())
|
||||
.add(io.envoyproxy.envoy.api.v2.Listener.getDescriptor())
|
||||
|
|
@ -71,9 +73,20 @@ final class MessagePrinter {
|
|||
.add(io.envoyproxy.envoy.config.cluster.aggregate.v2alpha.ClusterConfig
|
||||
.getDescriptor())
|
||||
.add(ClusterLoadAssignment.getDescriptor())
|
||||
.add(io.envoyproxy.envoy.api.v2.ClusterLoadAssignment.getDescriptor())
|
||||
.build();
|
||||
return JsonFormat.printer().usingTypeRegistry(registry);
|
||||
.add(io.envoyproxy.envoy.api.v2.ClusterLoadAssignment.getDescriptor());
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends Message> routeLookupClusterSpecifierClass =
|
||||
(Class<? extends Message>)
|
||||
Class.forName("io.grpc.lookup.v1.RouteLookupClusterSpecifier");
|
||||
Descriptor descriptor =
|
||||
(Descriptor)
|
||||
routeLookupClusterSpecifierClass.getDeclaredMethod("getDescriptor").invoke(null);
|
||||
registry.add(descriptor);
|
||||
} catch (Exception e) {
|
||||
// Ignore. In most cases RouteLookup is not required.
|
||||
}
|
||||
return JsonFormat.printer().usingTypeRegistry(registry.build());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,18 +17,14 @@
|
|||
package io.grpc.xds;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.protobuf.Any;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.google.protobuf.Message;
|
||||
import com.google.protobuf.util.Durations;
|
||||
import io.grpc.lookup.v1.GrpcKeyBuilder;
|
||||
import io.grpc.lookup.v1.GrpcKeyBuilder.ExtraKeys;
|
||||
import io.grpc.lookup.v1.GrpcKeyBuilder.Name;
|
||||
import io.grpc.lookup.v1.NameMatcher;
|
||||
import io.grpc.lookup.v1.RouteLookupConfig;
|
||||
import io.grpc.rls.RlsProtoData;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import io.grpc.internal.JsonParser;
|
||||
import io.grpc.internal.JsonUtil;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/** The ClusterSpecifierPlugin for RouteLookup policy. */
|
||||
final class RouteLookupServiceClusterSpecifierPlugin implements ClusterSpecifierPlugin {
|
||||
|
|
@ -49,84 +45,49 @@ final class RouteLookupServiceClusterSpecifierPlugin implements ClusterSpecifier
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public ConfigOrError<RlsPluginConfig> parsePlugin(Message rawProtoMessage) {
|
||||
if (!(rawProtoMessage instanceof Any)) {
|
||||
return ConfigOrError.fromError("Invalid config type: " + rawProtoMessage.getClass());
|
||||
}
|
||||
|
||||
Any anyMessage = (Any) rawProtoMessage;
|
||||
RouteLookupConfig configProto;
|
||||
try {
|
||||
configProto = anyMessage.unpack(RouteLookupConfig.class);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
return ConfigOrError.fromError("Invalid proto: " + e);
|
||||
}
|
||||
try {
|
||||
List<GrpcKeyBuilder> keyBuildersProto = configProto.getGrpcKeybuildersList();
|
||||
List<RlsProtoData.GrpcKeyBuilder> keyBuilders =
|
||||
new ArrayList<>(keyBuildersProto.size());
|
||||
for (GrpcKeyBuilder keyBuilderProto : keyBuildersProto) {
|
||||
List<Name> namesProto = keyBuilderProto.getNamesList();
|
||||
List<RlsProtoData.GrpcKeyBuilder.Name> names = new ArrayList<>(namesProto.size());
|
||||
for (Name nameProto : namesProto) {
|
||||
if (nameProto.getMethod().isEmpty()) {
|
||||
names.add(new RlsProtoData.GrpcKeyBuilder.Name(nameProto.getService()));
|
||||
} else {
|
||||
names.add(
|
||||
new RlsProtoData.GrpcKeyBuilder.Name(
|
||||
nameProto.getService(), nameProto.getMethod()));
|
||||
}
|
||||
}
|
||||
|
||||
List<NameMatcher> headersProto = keyBuilderProto.getHeadersList();
|
||||
List<RlsProtoData.NameMatcher> headers = new ArrayList<>(headersProto.size());
|
||||
for (NameMatcher headerProto : headersProto) {
|
||||
headers.add(
|
||||
new RlsProtoData.NameMatcher(
|
||||
headerProto.getKey(), headerProto.getNamesList(),
|
||||
headerProto.getRequiredMatch()));
|
||||
}
|
||||
|
||||
String host = null;
|
||||
String service = null;
|
||||
String method = null;
|
||||
if (keyBuilderProto.hasExtraKeys()) {
|
||||
ExtraKeys extraKeysProto = keyBuilderProto.getExtraKeys();
|
||||
host = extraKeysProto.getHost();
|
||||
service = extraKeysProto.getService();
|
||||
method = extraKeysProto.getMethod();
|
||||
}
|
||||
RlsProtoData.ExtraKeys extraKeys =
|
||||
RlsProtoData.ExtraKeys.create(host, service, method);
|
||||
|
||||
RlsProtoData.GrpcKeyBuilder keyBuilder =
|
||||
new RlsProtoData.GrpcKeyBuilder(
|
||||
names, headers, extraKeys, keyBuilderProto.getConstantKeysMap());
|
||||
keyBuilders.add(keyBuilder);
|
||||
Any anyMessage = (Any) rawProtoMessage;
|
||||
Class<? extends Message> protoClass;
|
||||
try {
|
||||
protoClass =
|
||||
(Class<? extends Message>)
|
||||
Class.forName("io.grpc.lookup.v1.RouteLookupClusterSpecifier");
|
||||
} catch (ClassNotFoundException e) {
|
||||
return ConfigOrError.fromError("Dependency for 'io.grpc:grpc-rls' is missing: " + e);
|
||||
}
|
||||
Message configProto;
|
||||
try {
|
||||
configProto = anyMessage.unpack(protoClass);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
return ConfigOrError.fromError("Invalid proto: " + e);
|
||||
}
|
||||
String jsonString = MessagePrinter.print(configProto);
|
||||
try {
|
||||
Map<String, ?> jsonMap = (Map<String, ?>) JsonParser.parse(jsonString);
|
||||
Map<String, ?> config = JsonUtil.getObject(jsonMap, "routeLookupConfig");
|
||||
return ConfigOrError.fromConfig(RlsPluginConfig.create(config));
|
||||
} catch (IOException e) {
|
||||
return ConfigOrError.fromError(
|
||||
"Unable to parse RouteLookupClusterSpecifier: " + jsonString);
|
||||
}
|
||||
RlsProtoData.RouteLookupConfig config = new RlsProtoData.RouteLookupConfig(
|
||||
keyBuilders,
|
||||
configProto.getLookupService(),
|
||||
Durations.toMillis(configProto.getLookupServiceTimeout()),
|
||||
configProto.hasMaxAge() ? Durations.toMillis(configProto.getMaxAge()) : null,
|
||||
configProto.hasStaleAge() ? Durations.toMillis(configProto.getStaleAge()) : null,
|
||||
configProto.getCacheSizeBytes(),
|
||||
configProto.getValidTargetsList(),
|
||||
configProto.getDefaultTarget());
|
||||
return ConfigOrError.fromConfig(RlsPluginConfig.create(config));
|
||||
} catch (RuntimeException e) {
|
||||
return ConfigOrError.fromError(
|
||||
"Error parsing RouteLookupConfig: \n" + configProto + "\n reason: " + e);
|
||||
return ConfigOrError.fromError("Error parsing RouteLookupConfig: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
abstract static class RlsPluginConfig implements PluginConfig {
|
||||
|
||||
abstract RlsProtoData.RouteLookupConfig config();
|
||||
abstract ImmutableMap<String, ?> config();
|
||||
|
||||
static RlsPluginConfig create(RlsProtoData.RouteLookupConfig config) {
|
||||
return new AutoValue_RouteLookupServiceClusterSpecifierPlugin_RlsPluginConfig(config);
|
||||
static RlsPluginConfig create(Map<String, ?> config) {
|
||||
return new AutoValue_RouteLookupServiceClusterSpecifierPlugin_RlsPluginConfig(
|
||||
ImmutableMap.copyOf(config));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ import io.grpc.lookup.v1.GrpcKeyBuilder;
|
|||
import io.grpc.lookup.v1.GrpcKeyBuilder.ExtraKeys;
|
||||
import io.grpc.lookup.v1.GrpcKeyBuilder.Name;
|
||||
import io.grpc.lookup.v1.NameMatcher;
|
||||
import io.grpc.lookup.v1.RouteLookupClusterSpecifier;
|
||||
import io.grpc.lookup.v1.RouteLookupConfig;
|
||||
import io.grpc.rls.RlsProtoData;
|
||||
import io.grpc.xds.RouteLookupServiceClusterSpecifierPlugin.RlsPluginConfig;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
|
@ -57,29 +57,38 @@ public class RouteLookupServiceClusterSpecifierPluginTest {
|
|||
.addValidTargets("valid-target")
|
||||
.setDefaultTarget("default-target")
|
||||
.build();
|
||||
RouteLookupClusterSpecifier specifier =
|
||||
RouteLookupClusterSpecifier.newBuilder().setRouteLookupConfig(routeLookupConfig).build();
|
||||
RlsPluginConfig config =
|
||||
RouteLookupServiceClusterSpecifierPlugin.INSTANCE.parsePlugin(Any.pack(routeLookupConfig))
|
||||
RouteLookupServiceClusterSpecifierPlugin.INSTANCE.parsePlugin(Any.pack(specifier))
|
||||
.config;
|
||||
assertThat(config.typeUrl()).isEqualTo("type.googleapis.com/grpc.lookup.v1.RouteLookupConfig");
|
||||
assertThat(config.config()).isEqualTo(
|
||||
new RlsProtoData.RouteLookupConfig(
|
||||
ImmutableList.of(
|
||||
new RlsProtoData.GrpcKeyBuilder(
|
||||
ImmutableMap.builder()
|
||||
.put(
|
||||
"grpcKeybuilders",
|
||||
ImmutableList.of(ImmutableMap.of(
|
||||
"names",
|
||||
ImmutableList.of(
|
||||
new RlsProtoData.GrpcKeyBuilder.Name("service1", "method1"),
|
||||
new RlsProtoData.GrpcKeyBuilder.Name("service2", "method2")),
|
||||
ImmutableMap.of("service", "service1", "method", "method1"),
|
||||
ImmutableMap.of("service", "service2", "method", "method2")),
|
||||
"headers",
|
||||
ImmutableList.of(
|
||||
new RlsProtoData.NameMatcher("key1", ImmutableList.of("v1"), true)),
|
||||
RlsProtoData.ExtraKeys.create("host1", "service1", "method1"),
|
||||
ImmutableMap.of("key2", "value2")
|
||||
)),
|
||||
"rls-cbt.googleapis.com",
|
||||
1234,
|
||||
56789L,
|
||||
1000L,
|
||||
5000,
|
||||
ImmutableList.of("valid-target"),
|
||||
"default-target"));
|
||||
ImmutableMap.of(
|
||||
"key", "key1", "names", ImmutableList.of("v1"),
|
||||
"requiredMatch", true)),
|
||||
"extraKeys",
|
||||
ImmutableMap.of("host", "host1", "service", "service1", "method", "method1"),
|
||||
"constantKeys",
|
||||
ImmutableMap.of("key2", "value2"))))
|
||||
.put("lookupService", "rls-cbt.googleapis.com")
|
||||
.put("lookupServiceTimeout", "1.234s")
|
||||
.put("maxAge", "56.789s")
|
||||
.put("staleAge", "1s")
|
||||
.put("cacheSizeBytes", "5000")
|
||||
.put("validTargets", ImmutableList.of("valid-target"))
|
||||
.put("defaultTarget","default-target")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -96,47 +105,30 @@ public class RouteLookupServiceClusterSpecifierPluginTest {
|
|||
.setCacheSizeBytes(5000)
|
||||
.addValidTargets("valid-target")
|
||||
.build();
|
||||
RouteLookupClusterSpecifier specifier =
|
||||
RouteLookupClusterSpecifier.newBuilder().setRouteLookupConfig(routeLookupConfig).build();
|
||||
RlsPluginConfig config =
|
||||
RouteLookupServiceClusterSpecifierPlugin.INSTANCE.parsePlugin(Any.pack(routeLookupConfig))
|
||||
RouteLookupServiceClusterSpecifierPlugin.INSTANCE.parsePlugin(Any.pack(specifier))
|
||||
.config;
|
||||
assertThat(config.typeUrl()).isEqualTo("type.googleapis.com/grpc.lookup.v1.RouteLookupConfig");
|
||||
assertThat(config.config()).isEqualTo(
|
||||
new RlsProtoData.RouteLookupConfig(
|
||||
ImmutableList.of(
|
||||
new RlsProtoData.GrpcKeyBuilder(
|
||||
ImmutableMap.builder()
|
||||
.put(
|
||||
"grpcKeybuilders",
|
||||
ImmutableList.of(ImmutableMap.of(
|
||||
"names",
|
||||
ImmutableList.of(
|
||||
new RlsProtoData.GrpcKeyBuilder.Name("service1"),
|
||||
new RlsProtoData.GrpcKeyBuilder.Name("service2")),
|
||||
ImmutableMap.of("service", "service1"),
|
||||
ImmutableMap.of("service", "service2")),
|
||||
"headers",
|
||||
ImmutableList.of(
|
||||
new RlsProtoData.NameMatcher("key1", ImmutableList.of("v1"), true)),
|
||||
RlsProtoData.ExtraKeys.create(null, null, null),
|
||||
ImmutableMap.<String, String>of()
|
||||
)),
|
||||
"rls-cbt.googleapis.com",
|
||||
1234,
|
||||
null,
|
||||
null,
|
||||
5000,
|
||||
ImmutableList.of("valid-target"),
|
||||
null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseInvalidConfig() {
|
||||
RouteLookupConfig routeLookupConfig = RouteLookupConfig.newBuilder()
|
||||
.addGrpcKeybuilders(
|
||||
GrpcKeyBuilder.newBuilder()
|
||||
.addNames(Name.newBuilder().setService("service1"))
|
||||
.addNames(Name.newBuilder().setService("service2"))
|
||||
.addHeaders(
|
||||
NameMatcher.newBuilder().setKey("key1").addNames("v1").setRequiredMatch(true)))
|
||||
.setLookupService("rls-cbt.googleapis.com")
|
||||
.setLookupServiceTimeout(Durations.fromMillis(1234))
|
||||
.setCacheSizeBytes(-5000) // negative
|
||||
.addValidTargets("valid-target")
|
||||
.build();
|
||||
ConfigOrError<RlsPluginConfig> configOrError =
|
||||
RouteLookupServiceClusterSpecifierPlugin.INSTANCE.parsePlugin(Any.pack(routeLookupConfig));
|
||||
assertThat(configOrError.errorDetail).contains("cacheSize must be positive");
|
||||
ImmutableMap.of(
|
||||
"key", "key1", "names", ImmutableList.of("v1"),
|
||||
"requiredMatch", true)))))
|
||||
.put("lookupService", "rls-cbt.googleapis.com")
|
||||
.put("lookupServiceTimeout", "1.234s")
|
||||
.put("cacheSizeBytes", "5000")
|
||||
.put("validTargets", ImmutableList.of("valid-target"))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue