From 69bf18f458ce64441767ce1289c9d03cee486d06 Mon Sep 17 00:00:00 2001 From: minux Date: Fri, 16 Aug 2024 10:45:04 +0900 Subject: [PATCH] Register all envoy extension to MessageMarshaller (#1008) --- dependencies.toml | 12 +++++ xds/build.gradle | 2 + .../xds/internal/ControlPlanePlugin.java | 12 +---- .../xds/internal/XdsResourceManager.java | 48 +++++++++++++------ 4 files changed, 48 insertions(+), 26 deletions(-) diff --git a/dependencies.toml b/dependencies.toml index 1860b6dde..47601bd4a 100644 --- a/dependencies.toml +++ b/dependencies.toml @@ -57,6 +57,7 @@ proguard = "7.4.2" protobuf = "3.25.1" protobuf-gradle-plugin = "0.8.19" quartz = "2.3.2" +reflections = "0.9.11" shadow-gradle-plugin = "7.1.2" shiro = "1.3.2" slf4j1 = { strictly = "1.7.36" } @@ -298,6 +299,9 @@ module = "ch.qos.logback:logback-classic" version.ref = "logback15" javadocs = "https://www.javadoc.io/doc/ch.qos.logback/logback-classic/1.5.4/" +[libraries.controlplane-api] +module = "io.envoyproxy.controlplane:api" +version.ref = "controlplane" [libraries.controlplane-cache] module = "io.envoyproxy.controlplane:cache" version.ref = "controlplane" @@ -343,6 +347,14 @@ version.ref = "protobuf-gradle-plugin" module = "org.quartz-scheduler:quartz" version.ref = "quartz" +[libraries.reflections] +module = "org.reflections:reflections" +version.ref = "reflections" +exclusions = [ + "com.google.errorprone:error_prone_annotations", + "com.google.j2objc:j2objc-annotations", + "org.codehaus.mojo:animal-sniffer-annotations"] + [libraries.shadow-gradle-plugin] module = "gradle.plugin.com.github.johnrengelman:shadow" version.ref = "shadow-gradle-plugin" diff --git a/xds/build.gradle b/xds/build.gradle index ac6e3a1f7..f34c1be7d 100644 --- a/xds/build.gradle +++ b/xds/build.gradle @@ -2,8 +2,10 @@ dependencies { implementation project(':server') implementation libs.armeria.grpc + implementation libs.controlplane.api implementation libs.controlplane.cache implementation libs.controlplane.server + implementation libs.reflections testImplementation libs.armeria.junit5 testImplementation libs.armeria.xds diff --git a/xds/src/main/java/com/linecorp/centraldogma/xds/internal/ControlPlanePlugin.java b/xds/src/main/java/com/linecorp/centraldogma/xds/internal/ControlPlanePlugin.java index 1f6626bb1..04a5ec1ef 100644 --- a/xds/src/main/java/com/linecorp/centraldogma/xds/internal/ControlPlanePlugin.java +++ b/xds/src/main/java/com/linecorp/centraldogma/xds/internal/ControlPlanePlugin.java @@ -68,10 +68,6 @@ import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment; import io.envoyproxy.envoy.config.listener.v3.Listener; import io.envoyproxy.envoy.config.route.v3.RouteConfiguration; -import io.envoyproxy.envoy.extensions.filters.http.router.v3.Router; -import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager; -import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext; -import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext; import io.envoyproxy.envoy.service.discovery.v3.DeltaDiscoveryRequest; import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest; import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; @@ -174,13 +170,7 @@ private void init0(PluginInitContext pluginInitContext) { .jsonMarshallerFactory( serviceDescriptor -> GrpcJsonMarshaller .builder() - //TODO(minwoox): Automate the registration of the extension messages. - .jsonMarshallerCustomizer(builder -> { - builder.register(HttpConnectionManager.getDefaultInstance()) - .register(Router.getDefaultInstance()) - .register(UpstreamTlsContext.getDefaultInstance()) - .register(DownstreamTlsContext.getDefaultInstance()); - }) + .jsonMarshallerCustomizer(XdsResourceManager::registerEnvoyExtension) .build(serviceDescriptor)) .enableHttpJsonTranscoding(true).build(); sb.service(xdsApplicationService, pluginInitContext.authService()); diff --git a/xds/src/main/java/com/linecorp/centraldogma/xds/internal/XdsResourceManager.java b/xds/src/main/java/com/linecorp/centraldogma/xds/internal/XdsResourceManager.java index 8e6470ca4..b2f426530 100644 --- a/xds/src/main/java/com/linecorp/centraldogma/xds/internal/XdsResourceManager.java +++ b/xds/src/main/java/com/linecorp/centraldogma/xds/internal/XdsResourceManager.java @@ -21,13 +21,18 @@ import static java.util.Objects.requireNonNull; import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.regex.Pattern; import org.curioswitch.common.protobuf.json.MessageMarshaller; +import org.reflections.Reflections; +import org.reflections.scanners.SubTypesScanner; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.collect.ImmutableList; import com.google.protobuf.Empty; +import com.google.protobuf.GeneratedMessageV3; import com.google.protobuf.Message; import com.linecorp.centraldogma.common.Author; @@ -44,10 +49,7 @@ import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment; import io.envoyproxy.envoy.config.listener.v3.Listener; import io.envoyproxy.envoy.config.route.v3.RouteConfiguration; -import io.envoyproxy.envoy.extensions.filters.http.router.v3.Router; import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager; -import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext; -import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext; import io.grpc.Status; import io.grpc.stub.StreamObserver; @@ -56,19 +58,35 @@ public final class XdsResourceManager { public static final String RESOURCE_ID_PATTERN_STRING = "[a-z](?:[a-z0-9-_/]*[a-z0-9])?"; public static final Pattern RESOURCE_ID_PATTERN = Pattern.compile('^' + RESOURCE_ID_PATTERN_STRING + '$'); - //TODO(minwoox): Automate the registration of the extension message types. public static final MessageMarshaller JSON_MESSAGE_MARSHALLER = - MessageMarshaller.builder().omittingInsignificantWhitespace(true) - .register(Listener.getDefaultInstance()) - .register(Cluster.getDefaultInstance()) - .register(ClusterLoadAssignment.getDefaultInstance()) - .register(Router.getDefaultInstance()) - // extensions - .register(RouteConfiguration.getDefaultInstance()) - .register(HttpConnectionManager.getDefaultInstance()) - .register(UpstreamTlsContext.getDefaultInstance()) - .register(DownstreamTlsContext.getDefaultInstance()) - .build(); + registerEnvoyExtension( + MessageMarshaller.builder().omittingInsignificantWhitespace(true) + .register(Listener.getDefaultInstance()) + .register(Cluster.getDefaultInstance()) + .register(ClusterLoadAssignment.getDefaultInstance()) + .register(RouteConfiguration.getDefaultInstance())) + .build(); + + public static MessageMarshaller.Builder registerEnvoyExtension(MessageMarshaller.Builder builder) { + final Reflections reflections = new Reflections( + "io.envoyproxy.envoy.extensions", HttpConnectionManager.class.getClassLoader(), + new SubTypesScanner(true)); + reflections.getSubTypesOf(GeneratedMessageV3.class) + .stream() + .filter(c -> !c.getName().contains("$")) // exclude subclasses + .filter(XdsResourceManager::hasGetDefaultInstanceMethod) + .forEach(builder::register); + return builder; + } + + private static boolean hasGetDefaultInstanceMethod(Class clazz) { + try { + final Method method = clazz.getMethod("getDefaultInstance"); + return method.getParameterCount() == 0 && Modifier.isStatic(method.getModifiers()); + } catch (NoSuchMethodException ignored) { + return false; + } + } public static String removePrefix(String prefix, String name) { if (!name.startsWith(prefix)) {